basic group chat
This commit is contained in:
parent
1fd85efc7d
commit
7d1c1fa5c1
|
@ -35,13 +35,15 @@
|
|||
|
||||
(defn init-protocol
|
||||
"Required [handler ethereum-rpc-url storage]
|
||||
Optional [whisper-identity] - if not passed a new identity is created automatically
|
||||
Optional [whisper-identity - if not passed a new identity is created automatically
|
||||
active-group-ids - list of active group ids]
|
||||
|
||||
(fn handler [{:keys [event-type...}])
|
||||
|
||||
:event-type can be:
|
||||
|
||||
:new-msg - [from payload]
|
||||
:new-group-msg [from payload]
|
||||
:error - [error-msg details]
|
||||
:msg-acked [msg-id from]
|
||||
:delivery-failed [msg-id]
|
||||
|
@ -51,7 +53,7 @@
|
|||
|
||||
:new-msg, msg-acked should be handled idempotently (may be called multiple times for the same msg-id)
|
||||
"
|
||||
[{:keys [handler ethereum-rpc-url storage identity]}]
|
||||
[{:keys [handler ethereum-rpc-url storage identity active-group-ids]}]
|
||||
(set-storage storage)
|
||||
(set-handler handler)
|
||||
(go
|
||||
|
@ -62,6 +64,8 @@
|
|||
(set-identity identity)
|
||||
(listen connection handle-incoming-whisper-msg)
|
||||
(start-delivery-loop)
|
||||
(doseq [group-id active-group-ids]
|
||||
(listen connection handle-incoming-whisper-msg {:topics [group-id]}))
|
||||
(invoke-user-handler :initialized {:identity identity}))))
|
||||
|
||||
(defn send-user-msg [{:keys [to content]}]
|
||||
|
@ -79,21 +83,24 @@
|
|||
{public-key :public} (get-keypair store group-id)
|
||||
{:keys [msg-id msg] :as new-msg} (make-msg {:from (state/my-identity)
|
||||
:topics [group-id]
|
||||
:encrypt true
|
||||
:encrypt? true
|
||||
:public-key public-key
|
||||
:payload {:content content
|
||||
:content-type default-content-type
|
||||
:type :user-msg}})]
|
||||
:content-type default-content-type}
|
||||
:clear-info {:group-id group-id
|
||||
:type :group-user-msg}})]
|
||||
(add-pending-message msg-id msg {:identities (get-identities store group-id)})
|
||||
(post-msg (connection) msg)
|
||||
new-msg))
|
||||
|
||||
(defn start-group-chat [identities]
|
||||
(let [group-topic (random/id)
|
||||
keypair (new-keypair)]
|
||||
(let [store (storage)]
|
||||
(save-keypair store group-topic keypair)
|
||||
(save-identities store group-topic identities))
|
||||
keypair (new-keypair)
|
||||
store (storage)
|
||||
connection (connection)]
|
||||
(save-keypair store group-topic keypair)
|
||||
(save-identities store group-topic identities)
|
||||
(listen connection handle-incoming-whisper-msg {:topics [group-topic]})
|
||||
(doseq [ident identities]
|
||||
(let [{:keys [msg-id msg]} (make-msg {:from (state/my-identity)
|
||||
:to ident
|
||||
|
@ -102,28 +109,8 @@
|
|||
:identities identities
|
||||
:keypair keypair}})]
|
||||
(add-pending-message msg-id msg {:internal? true})
|
||||
(post-msg (connection) msg)))
|
||||
(post-msg connection msg)))
|
||||
group-topic))
|
||||
|
||||
(defn current-connection []
|
||||
(connection))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
(ns syng-im.protocol.group-chat
|
||||
(:require [syng-im.utils.random :as random]
|
||||
[syng-im.utils.encryption :refer [new-keypair]]
|
||||
[syng-im.protocol.state.group-chat :refer [save-keypair]]
|
||||
[syng-im.protocol.state.state :refer [connection my-identity storage]]
|
||||
[syng-im.protocol.web3 :refer [make-msg]]
|
||||
[syng-im.protocol.state.delivery :refer [add-pending-message]]))
|
||||
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
(ns syng-im.protocol.handler
|
||||
(:require [cljs.reader :refer [read-string]]
|
||||
[syng-im.utils.logging :as log]
|
||||
[syng-im.utils.encryption :refer [decrypt]]
|
||||
[syng-im.protocol.state.state :as state :refer [storage]]
|
||||
[syng-im.protocol.state.delivery :refer [internal?
|
||||
update-pending-message]]
|
||||
[syng-im.protocol.state.group-chat :refer [save-keypair
|
||||
save-identities
|
||||
chat-exists?]]
|
||||
chat-exists?
|
||||
get-keypair]]
|
||||
[syng-im.protocol.web3 :refer [to-ascii
|
||||
make-msg
|
||||
post-msg
|
||||
]]
|
||||
listen]]
|
||||
[syng-im.protocol.user-handler :refer [invoke-user-handler]]))
|
||||
|
||||
(defn handle-ack [from {:keys [ack-msg-id] :as payload}]
|
||||
|
@ -20,7 +22,7 @@
|
|||
(when-not internal-message?
|
||||
(invoke-user-handler :msg-acked {:msg-id ack-msg-id
|
||||
:from from}))
|
||||
(when-let [group-topic (payload :group-topic)]
|
||||
(when-let [group-topic (payload :group-invite)]
|
||||
(invoke-user-handler :group-chat-invite-acked {:from from
|
||||
:group-id group-topic}))))
|
||||
|
||||
|
@ -41,16 +43,29 @@
|
|||
(invoke-user-handler :new-msg {:from from
|
||||
:payload payload}))
|
||||
|
||||
(declare handle-incoming-whisper-msg)
|
||||
|
||||
(defn handle-new-group-chat [web3 from {:keys [group-topic keypair identities msg-id]}]
|
||||
(send-ack web3 from msg-id {:group-topic group-topic})
|
||||
(send-ack web3 from msg-id {:group-invite group-topic})
|
||||
(let [store (storage)]
|
||||
(when-not (chat-exists? store group-topic)
|
||||
(listen web3 handle-incoming-whisper-msg {:topics [group-topic]})
|
||||
(save-keypair store group-topic keypair)
|
||||
(save-identities store group-topic identities)
|
||||
(invoke-user-handler :new-group-chat {:from from
|
||||
:identities identities
|
||||
:group-id group-topic}))))
|
||||
|
||||
(defn handle-group-user-msg [web3 from {:keys [enc-payload group-id]}]
|
||||
(let [store (storage)
|
||||
{private-key :private} (get-keypair store group-id)
|
||||
{:keys [msg-id] :as payload} (-> (decrypt private-key enc-payload)
|
||||
(read-string)
|
||||
(assoc :group-id group-id))]
|
||||
(send-ack web3 from msg-id)
|
||||
(invoke-user-handler :new-group-msg {:from from
|
||||
:payload payload})))
|
||||
|
||||
(defn handle-incoming-whisper-msg [web3 msg]
|
||||
(log/info "Got whisper message:" msg)
|
||||
(let [{from :from
|
||||
|
@ -58,13 +73,13 @@
|
|||
topics :topics ;; always empty (bug in go-ethereum?)
|
||||
payload :payload
|
||||
:as msg} (js->clj msg :keywordize-keys true)]
|
||||
(if (= to (state/my-identity))
|
||||
(let [{msg-type :type
|
||||
msg-id :msg-id
|
||||
:as payload} (->> (to-ascii payload)
|
||||
(read-string))]
|
||||
(if (or (= to "0x0")
|
||||
(= to (state/my-identity)))
|
||||
(let [{msg-type :type :as payload} (->> (to-ascii payload)
|
||||
(read-string))]
|
||||
(case msg-type
|
||||
:ack (handle-ack from payload)
|
||||
:user-msg (handle-user-msg web3 from payload)
|
||||
:init-group-chat (handle-new-group-chat web3 from payload)))
|
||||
:init-group-chat (handle-new-group-chat web3 from payload)
|
||||
:group-user-msg (handle-group-user-msg web3 from payload)))
|
||||
(log/warn "My identity:" (state/my-identity) "Message To:" to "Message is encrypted for someone else, ignoring"))))
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
(do
|
||||
(log/error (str error-msg ":") error)
|
||||
(invoke-user-handler :error {:error-msg error-msg
|
||||
:details error}))
|
||||
:details error}))
|
||||
(put! result-channel result))
|
||||
(close! result-channel)))
|
||||
|
||||
|
@ -54,11 +54,16 @@
|
|||
(let [error-msg "Call to shh.post() failed"]
|
||||
(log/error (str error-msg ":") error)
|
||||
(invoke-user-handler :error {:error-msg error-msg
|
||||
:details error}))))))))
|
||||
:details error}))))))))
|
||||
|
||||
(defn encrypt-payload [public-key clear-info payload-str]
|
||||
(->> (merge {:enc-payload (encrypt public-key payload-str)}
|
||||
clear-info)
|
||||
(str)))
|
||||
|
||||
(defn make-msg
|
||||
"Returns [msg-id msg], `msg` is formed for Web3.shh.post()"
|
||||
[{:keys [from to ttl topics payload encrypt? public-key]
|
||||
[{:keys [from to ttl topics payload encrypt? public-key clear-info]
|
||||
:or {ttl syng-msg-ttl
|
||||
topics []}}]
|
||||
(let [msg-id (random/id)]
|
||||
|
@ -68,7 +73,7 @@
|
|||
(mapv from-ascii))
|
||||
:payload (cond->> (merge payload {:msg-id msg-id})
|
||||
true (str)
|
||||
encrypt? (encrypt public-key)
|
||||
encrypt? (encrypt-payload public-key clear-info)
|
||||
true (from-ascii))}
|
||||
from (assoc :from from)
|
||||
to (assoc :to to))}))
|
||||
|
@ -77,14 +82,16 @@
|
|||
|
||||
(defn listen
|
||||
"Returns a filter which can be stopped with (stop-whisper-listener)"
|
||||
[web3 msg-handler]
|
||||
(let [topics [syng-app-topic]
|
||||
shh (whisper web3)
|
||||
filter (.filter shh (make-topics topics) (fn [error msg]
|
||||
(if error
|
||||
(invoke-user-handler :error {:error-msg error})
|
||||
(msg-handler web3 msg))))]
|
||||
(state/add-filter topics filter)))
|
||||
([web3 msg-handler]
|
||||
(listen web3 msg-handler {}))
|
||||
([web3 msg-handler {:keys [topics] :as opts :or {topics []}}]
|
||||
(let [topics (conj topics syng-app-topic)
|
||||
shh (whisper web3)
|
||||
filter (.filter shh (make-topics topics) (fn [error msg]
|
||||
(if error
|
||||
(invoke-user-handler :error {:error-msg error})
|
||||
(msg-handler web3 msg))))]
|
||||
(state/add-filter topics filter))))
|
||||
|
||||
(defn stop-listener [filter]
|
||||
(.stopWatching filter))
|
||||
|
|
|
@ -30,8 +30,12 @@
|
|||
(contains-key? [{:keys [m]} key]
|
||||
(contains? @m key)))
|
||||
|
||||
(defonce state (atom {:group-id nil
|
||||
:storage (map->MapStore {:m (atom {})})}))
|
||||
(defonce state (atom {:group-id nil
|
||||
:group-identities nil
|
||||
:storage (map->MapStore {:m (atom {})})}))
|
||||
|
||||
(defn set-group-id! [group-id]
|
||||
(swap! state assoc :group-id group-id))
|
||||
|
||||
(defn add-to-chat [element-id from content]
|
||||
(let [chat-area (g/getElement element-id)
|
||||
|
@ -39,6 +43,11 @@
|
|||
chat (str chat (subs from 0 6) ": " content "\n")]
|
||||
(f/setValue chat-area chat)))
|
||||
|
||||
(defn set-group-identities [identities]
|
||||
(let [ids (s/join "\n" identities)]
|
||||
(-> (g/getElement "to-identities")
|
||||
(f/setValue ids))))
|
||||
|
||||
(defn start []
|
||||
(let [rpc-url (-> (g/getElement "rpc-url")
|
||||
(f/getValue))]
|
||||
|
@ -48,8 +57,8 @@
|
|||
:handler (fn [{:keys [event-type] :as event}]
|
||||
(log/info "Event:" (clj->js event))
|
||||
(case event-type
|
||||
:new-msg (let [{:keys [from payload]} event
|
||||
{content :content} payload]
|
||||
:new-msg (let [{from :from
|
||||
{content :content} :payload} event]
|
||||
(add-to-chat "chat" from content))
|
||||
:msg-acked (let [{:keys [msg-id]} event]
|
||||
(add-to-chat "chat" ":" (str "Message " msg-id " was acked")))
|
||||
|
@ -59,10 +68,15 @@
|
|||
(f/setValue identity)))
|
||||
:delivery-failed (let [{:keys [msg-id]} event]
|
||||
(add-to-chat "chat" ":" (str "Delivery of message " msg-id " failed")))
|
||||
:new-group-chat (let [{:keys [from group-id]} event]
|
||||
:new-group-chat (let [{:keys [from group-id identities]} event]
|
||||
(set-group-id! group-id)
|
||||
(set-group-identities identities)
|
||||
(add-to-chat "group-chat" ":" (str "Received group chat invitation from " from " for group-id: " group-id)))
|
||||
:group-chat-invite-acked (let [{:keys [from group-id]} event]
|
||||
(add-to-chat "group-chat" ":" (str "Received ACK for group chat invitation from " from " for group-id: " group-id)))
|
||||
:new-group-msg (let [{from :from
|
||||
{content :content} :payload} event]
|
||||
(add-to-chat "group-chat" from content))
|
||||
(add-to-chat "chat" ":" (str "Don't know how to handle " event-type))))})
|
||||
(e/listen (-> (g/getElement "msg")
|
||||
(goog.events.KeyHandler.))
|
||||
|
@ -75,7 +89,18 @@
|
|||
(f/getValue))]
|
||||
(p/send-user-msg {:to to-identity
|
||||
:content msg})
|
||||
(add-to-chat "chat" (p/my-identity) msg)))))))
|
||||
(add-to-chat "chat" (p/my-identity) msg)))))
|
||||
(e/listen (-> (g/getElement "group-msg")
|
||||
(goog.events.KeyHandler.))
|
||||
key-handler-events/KEY
|
||||
(fn [e]
|
||||
(when (= (.-keyCode e) KeyCodes/ENTER)
|
||||
(let [msg (-> (g/getElement "group-msg")
|
||||
(f/getValue))
|
||||
group-id (:group-id @state)]
|
||||
(p/send-group-msg {:group-id group-id
|
||||
:content msg})
|
||||
(add-to-chat "group-chat" (p/my-identity) msg)))))))
|
||||
|
||||
(defn start-group-chat []
|
||||
(let [identities (-> (g/getElement "to-identities")
|
||||
|
@ -83,7 +108,7 @@
|
|||
(s/split "\n"))]
|
||||
(add-to-chat "group-chat" ":" (str "Starting group chat with " identities))
|
||||
(let [group-id (p/start-group-chat identities)]
|
||||
(swap! state assoc :group-id group-id))))
|
||||
(set-group-id! group-id))))
|
||||
|
||||
(let [button (g/getElement "connect-button")]
|
||||
(e/listen button EventType/CLICK
|
||||
|
|
Loading…
Reference in New Issue