add participant to group chat
This commit is contained in:
parent
d5d781410f
commit
89c85025db
|
@ -10,20 +10,22 @@
|
|||
[syng-im.protocol.state.delivery :refer [add-pending-message]]
|
||||
[syng-im.protocol.state.group-chat :refer [save-keypair
|
||||
get-keypair
|
||||
get-peer-identities
|
||||
get-identities
|
||||
save-identities]]
|
||||
[syng-im.protocol.delivery :refer [start-delivery-loop]]
|
||||
[syng-im.protocol.web3 :refer [listen make-msg
|
||||
[syng-im.protocol.web3 :refer [listen
|
||||
make-msg
|
||||
post-msg
|
||||
make-web3
|
||||
new-identity]]
|
||||
[syng-im.protocol.handler :refer [handle-incoming-whisper-msg]]
|
||||
[syng-im.protocol.user-handler :refer [invoke-user-handler]]
|
||||
[syng-im.utils.encryption :refer [new-keypair]])
|
||||
[syng-im.utils.encryption :refer [new-keypair]]
|
||||
[syng-im.protocol.group-chat :refer [send-group-msg]]
|
||||
[syng-im.protocol.defaults :refer [default-content-type]])
|
||||
(:require-macros [cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(def default-content-type "text/plain")
|
||||
|
||||
(defn create-connection [ethereum-rpc-url]
|
||||
(make-web3 ethereum-rpc-url))
|
||||
|
||||
|
@ -49,9 +51,10 @@
|
|||
:delivery-failed [msg-id]
|
||||
:new-group-chat [from group-id]
|
||||
:group-chat-invite-acked [from group-id]
|
||||
:group-new-participant [identity group-id]
|
||||
:initialized [identity]
|
||||
|
||||
:new-msg, msg-acked should be handled idempotently (may be called multiple times for the same msg-id)
|
||||
:new-msg, new-group-msg, msg-acked should be handled idempotently (may be called multiple times for the same msg-id)
|
||||
"
|
||||
[{:keys [handler ethereum-rpc-url storage identity active-group-ids]}]
|
||||
(set-storage storage)
|
||||
|
@ -78,31 +81,25 @@
|
|||
(post-msg (connection) msg)
|
||||
new-msg))
|
||||
|
||||
(defn send-group-msg [{:keys [group-id content]}]
|
||||
(let [store (storage)
|
||||
{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
|
||||
:public-key public-key
|
||||
(defn send-group-user-msg [{:keys [group-id content]}]
|
||||
(send-group-msg {:group-id group-id
|
||||
:type :group-user-msg
|
||||
:payload {:content content
|
||||
: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))
|
||||
:content-type default-content-type}}))
|
||||
|
||||
(defn start-group-chat [identities]
|
||||
(let [group-topic (random/id)
|
||||
keypair (new-keypair)
|
||||
store (storage)
|
||||
connection (connection)]
|
||||
connection (connection)
|
||||
my-identity (state/my-identity)
|
||||
identities (-> (set identities)
|
||||
(conj my-identity))]
|
||||
(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)
|
||||
(doseq [ident identities :when (not (= ident my-identity))]
|
||||
(let [{:keys [msg-id msg]} (make-msg {:from my-identity
|
||||
:to ident
|
||||
:payload {:type :init-group-chat
|
||||
:group-topic group-topic
|
||||
|
@ -112,5 +109,25 @@
|
|||
(post-msg connection msg)))
|
||||
group-topic))
|
||||
|
||||
(defn group-add-participant [group-id new-peer-identity]
|
||||
(let [store (storage)
|
||||
connection (connection)
|
||||
identities (-> (get-identities store group-id)
|
||||
(conj new-peer-identity))
|
||||
keypair (get-keypair store group-id)]
|
||||
(save-identities store group-id identities)
|
||||
(let [{:keys [msg-id msg]} (make-msg {:from (state/my-identity)
|
||||
:to new-peer-identity
|
||||
:payload {:type :init-group-chat
|
||||
:group-topic group-id
|
||||
:identities identities
|
||||
:keypair keypair}})]
|
||||
(add-pending-message msg-id msg {:internal? true})
|
||||
(post-msg connection msg))
|
||||
(send-group-msg {:group-id group-id
|
||||
:type :group-new-participant
|
||||
:payload {:identity new-peer-identity}
|
||||
:internal? true})))
|
||||
|
||||
(defn current-connection []
|
||||
(connection))
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
(ns syng-im.protocol.defaults
|
||||
(:require [cljs-time.core :as t]))
|
||||
|
||||
(def default-content-type "text/plain")
|
||||
|
||||
(def max-retry-send-count 5)
|
||||
(def ack-wait-timeout-ms (t/millis 5000))
|
||||
(def check-delivery-interval-msg 100)
|
|
@ -5,13 +5,12 @@
|
|||
[syng-im.protocol.state.delivery :as state]
|
||||
[syng-im.protocol.state.state :as s]
|
||||
[syng-im.protocol.web3 :as whisper]
|
||||
[syng-im.protocol.user-handler :refer [invoke-user-handler]])
|
||||
[syng-im.protocol.user-handler :refer [invoke-user-handler]]
|
||||
[syng-im.protocol.defaults :refer [max-retry-send-count
|
||||
ack-wait-timeout-ms
|
||||
check-delivery-interval-msg]])
|
||||
(:require-macros [cljs.core.async.macros :refer [go]]))
|
||||
|
||||
(def max-retry-send-count 5)
|
||||
(def ack-wait-timeout-ms (t/millis 5000))
|
||||
(def check-delivery-interval-msg 100)
|
||||
|
||||
(defn expired? [timestamp]
|
||||
(t/before? (t/plus timestamp ack-wait-timeout-ms) (t/now)))
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
(ns syng-im.protocol.group-chat
|
||||
(:require [syng-im.protocol.state.state :as state :refer [connection
|
||||
storage]]
|
||||
[syng-im.protocol.state.delivery :refer [add-pending-message]]
|
||||
[syng-im.protocol.state.group-chat :refer [get-keypair
|
||||
get-peer-identities]]
|
||||
[syng-im.protocol.web3 :refer [make-msg
|
||||
post-msg]]))
|
||||
|
||||
(defn send-group-msg [{:keys [group-id payload type internal?] :or {internal? false}}]
|
||||
(let [store (storage)
|
||||
{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
|
||||
:public-key public-key
|
||||
:payload payload
|
||||
:clear-info {:group-id group-id
|
||||
:type type}})]
|
||||
(add-pending-message msg-id msg {:identities (get-peer-identities store group-id)
|
||||
:internal? internal?})
|
||||
(post-msg (connection) msg)
|
||||
new-msg))
|
|
@ -7,13 +7,16 @@
|
|||
update-pending-message]]
|
||||
[syng-im.protocol.state.group-chat :refer [save-keypair
|
||||
save-identities
|
||||
get-identities
|
||||
chat-exists?
|
||||
get-keypair]]
|
||||
get-keypair
|
||||
add-identity]]
|
||||
[syng-im.protocol.web3 :refer [to-ascii
|
||||
make-msg
|
||||
post-msg
|
||||
listen]]
|
||||
[syng-im.protocol.user-handler :refer [invoke-user-handler]]))
|
||||
[syng-im.protocol.user-handler :refer [invoke-user-handler]]
|
||||
[syng-im.protocol.defaults :refer [default-content-type]]))
|
||||
|
||||
(defn handle-ack [from {:keys [ack-msg-id] :as payload}]
|
||||
(log/info "Got ack for message:" ack-msg-id "from:" from)
|
||||
|
@ -56,16 +59,28 @@
|
|||
:identities identities
|
||||
:group-id group-topic}))))
|
||||
|
||||
(defn handle-group-user-msg [web3 from {:keys [enc-payload group-id]}]
|
||||
(defn decrypt-group-msg [group-id encrypted-payload]
|
||||
(let [store (storage)
|
||||
{private-key :private} (get-keypair store group-id)
|
||||
{:keys [msg-id] :as payload} (-> (decrypt private-key enc-payload)
|
||||
{private-key :private} (get-keypair store group-id)]
|
||||
(-> (decrypt private-key encrypted-payload)
|
||||
(read-string)
|
||||
(assoc :group-id group-id))]
|
||||
(assoc :group-id group-id))))
|
||||
|
||||
(defn handle-group-user-msg [web3 from {:keys [enc-payload group-id]}]
|
||||
(let [{:keys [msg-id] :as payload} (decrypt-group-msg group-id enc-payload)]
|
||||
(send-ack web3 from msg-id)
|
||||
(invoke-user-handler :new-group-msg {:from from
|
||||
:payload payload})))
|
||||
|
||||
(defn handle-group-new-participant [web3 from {:keys [enc-payload group-id]}]
|
||||
(let [store (storage)
|
||||
{:keys [msg-id identity]} (decrypt-group-msg group-id enc-payload)]
|
||||
(send-ack web3 from msg-id)
|
||||
(add-identity store group-id identity)
|
||||
(invoke-user-handler :group-new-participant {:identity identity
|
||||
:group-id group-id
|
||||
:from from})))
|
||||
|
||||
(defn handle-incoming-whisper-msg [web3 msg]
|
||||
(log/info "Got whisper message:" msg)
|
||||
(let [{from :from
|
||||
|
@ -81,5 +96,6 @@
|
|||
:ack (handle-ack from payload)
|
||||
:user-msg (handle-user-msg web3 from payload)
|
||||
:init-group-chat (handle-new-group-chat web3 from payload)
|
||||
:group-user-msg (handle-group-user-msg web3 from payload)))
|
||||
:group-user-msg (handle-group-user-msg web3 from payload)
|
||||
:group-new-participant (handle-group-new-participant web3 from payload)))
|
||||
(log/warn "My identity:" (state/my-identity) "Message To:" to "Message is encrypted for someone else, ignoring"))))
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
(swap! state (fn [state]
|
||||
(-> (assoc-in state [:pending-messages msg-id] {:msg msg
|
||||
:retry-count 0
|
||||
:identities (when identities
|
||||
(set identities))
|
||||
:identities identities
|
||||
:internal? internal?})
|
||||
(push-msg-to-delivery-queue msg-id)))))
|
||||
([msg-id msg]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns syng-im.protocol.state.group-chat
|
||||
(:require [syng-im.protocol.state.storage :as s]))
|
||||
(:require [syng-im.protocol.state.storage :as s]
|
||||
[syng-im.protocol.state.state :as state]))
|
||||
|
||||
(defn topic-keypair-key [topic]
|
||||
(str "group-chat.topic-keypair." topic))
|
||||
|
@ -15,10 +16,20 @@
|
|||
(let [key (topic-identities-key topic)]
|
||||
(s/put storage key identities)))
|
||||
|
||||
(defn add-identity [storage topic identity]
|
||||
(let [identities (get-identities storage topic)]
|
||||
(when-not (contains? identities identity)
|
||||
(->> (conj identities identity)
|
||||
(save-identities storage group-id)))))
|
||||
|
||||
(defn get-identities [storage topic]
|
||||
(let [key (topic-identities-key topic)]
|
||||
(s/get storage key)))
|
||||
|
||||
(defn get-peer-identities [storage topic]
|
||||
(-> (get-identities storage topic)
|
||||
(disj (state/my-identity))))
|
||||
|
||||
(defn chat-exists? [storage topic]
|
||||
(let [key (topic-keypair-key topic)]
|
||||
(s/contains-key? storage key)))
|
||||
|
|
|
@ -69,6 +69,11 @@
|
|||
<div>
|
||||
<button id="start-group-chat-button">Start Group Chat</button>
|
||||
</div>
|
||||
<div>
|
||||
<label for="new-peer">Add Participant</label>
|
||||
<input id="new-peer" type="text"/>
|
||||
<button id="add-peer-button">Add Participant</button>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/compiled/syng_im.js" type="text/javascript"></script>
|
||||
</body>
|
||||
|
|
|
@ -34,13 +34,16 @@
|
|||
:group-identities nil
|
||||
:storage (map->MapStore {:m (atom {})})}))
|
||||
|
||||
(defn shorten [s]
|
||||
(subs s 0 6))
|
||||
|
||||
(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)
|
||||
chat (f/getValue chat-area)
|
||||
chat (str chat (subs from 0 6) ": " content "\n")]
|
||||
chat (str chat (shorten from) ": " content "\n")]
|
||||
(f/setValue chat-area chat)))
|
||||
|
||||
(defn set-group-identities [identities]
|
||||
|
@ -48,6 +51,11 @@
|
|||
(-> (g/getElement "to-identities")
|
||||
(f/setValue ids))))
|
||||
|
||||
(defn get-group-identities []
|
||||
(-> (g/getElement "to-identities")
|
||||
(f/getValue)
|
||||
(s/split "\n")))
|
||||
|
||||
(defn start []
|
||||
(let [rpc-url (-> (g/getElement "rpc-url")
|
||||
(f/getValue))]
|
||||
|
@ -77,6 +85,12 @@
|
|||
:new-group-msg (let [{from :from
|
||||
{content :content} :payload} event]
|
||||
(add-to-chat "group-chat" from content))
|
||||
:group-new-participant (let [{:keys [group-id identity from]} event]
|
||||
(add-to-chat "group-chat" ":" (str (shorten from) " added " (shorten identity) " to group chat"))
|
||||
(-> (get-group-identities)
|
||||
(set)
|
||||
(conj identity)
|
||||
(set-group-identities)))
|
||||
(add-to-chat "chat" ":" (str "Don't know how to handle " event-type))))})
|
||||
(e/listen (-> (g/getElement "msg")
|
||||
(goog.events.KeyHandler.))
|
||||
|
@ -98,18 +112,23 @@
|
|||
(let [msg (-> (g/getElement "group-msg")
|
||||
(f/getValue))
|
||||
group-id (:group-id @state)]
|
||||
(p/send-group-msg {:group-id group-id
|
||||
(p/send-group-user-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")
|
||||
(f/getValue)
|
||||
(s/split "\n"))]
|
||||
(let [identities (get-group-identities)]
|
||||
(add-to-chat "group-chat" ":" (str "Starting group chat with " identities))
|
||||
(let [group-id (p/start-group-chat identities)]
|
||||
(set-group-id! group-id))))
|
||||
|
||||
(defn add-new-peer-to-group []
|
||||
(let [input (g/getElement "new-peer")
|
||||
new-identity (-> input (f/getValue))
|
||||
group-id (:group-id @state)]
|
||||
(f/setValue input "")
|
||||
(p/group-add-participant group-id new-identity)))
|
||||
|
||||
(let [button (g/getElement "connect-button")]
|
||||
(e/listen button EventType/CLICK
|
||||
(fn [e]
|
||||
|
@ -123,6 +142,11 @@
|
|||
(g/setProperties (g/getElement "to-identities") #js {:disabled "disabled"})
|
||||
(start-group-chat))))
|
||||
|
||||
(let [button (g/getElement "add-peer-button")]
|
||||
(e/listen button EventType/CLICK
|
||||
(fn [e]
|
||||
(add-new-peer-to-group))))
|
||||
|
||||
(comment
|
||||
|
||||
(p/init-protocol {:ethereum-rpc-url "http://localhost:4546"
|
||||
|
|
Loading…
Reference in New Issue