From 89c85025db58a9a39812d6015b863335228973a5 Mon Sep 17 00:00:00 2001 From: michaelr Date: Thu, 10 Mar 2016 16:29:48 +0200 Subject: [PATCH] add participant to group chat --- protocol/src/cljs/syng_im/protocol/api.cljs | 61 ++++++++++++------- .../src/cljs/syng_im/protocol/defaults.cljs | 8 +++ .../src/cljs/syng_im/protocol/delivery.cljs | 9 ++- .../src/cljs/syng_im/protocol/group_chat.cljs | 23 +++++++ .../src/cljs/syng_im/protocol/handler.cljs | 32 +++++++--- .../cljs/syng_im/protocol/state/delivery.cljs | 3 +- .../syng_im/protocol/state/group_chat.cljs | 13 +++- .../resources/public/index.html | 5 ++ .../src/cljs/syng_im/core.cljs | 36 +++++++++-- 9 files changed, 146 insertions(+), 44 deletions(-) create mode 100644 protocol/src/cljs/syng_im/protocol/defaults.cljs create mode 100644 protocol/src/cljs/syng_im/protocol/group_chat.cljs diff --git a/protocol/src/cljs/syng_im/protocol/api.cljs b/protocol/src/cljs/syng_im/protocol/api.cljs index cf4ad6a..d4781b3 100644 --- a/protocol/src/cljs/syng_im/protocol/api.cljs +++ b/protocol/src/cljs/syng_im/protocol/api.cljs @@ -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 - :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)) +(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}})) (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)) diff --git a/protocol/src/cljs/syng_im/protocol/defaults.cljs b/protocol/src/cljs/syng_im/protocol/defaults.cljs new file mode 100644 index 0000000..ace3957 --- /dev/null +++ b/protocol/src/cljs/syng_im/protocol/defaults.cljs @@ -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) diff --git a/protocol/src/cljs/syng_im/protocol/delivery.cljs b/protocol/src/cljs/syng_im/protocol/delivery.cljs index ba247ba..bbb66ed 100644 --- a/protocol/src/cljs/syng_im/protocol/delivery.cljs +++ b/protocol/src/cljs/syng_im/protocol/delivery.cljs @@ -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))) diff --git a/protocol/src/cljs/syng_im/protocol/group_chat.cljs b/protocol/src/cljs/syng_im/protocol/group_chat.cljs new file mode 100644 index 0000000..65d4ea5 --- /dev/null +++ b/protocol/src/cljs/syng_im/protocol/group_chat.cljs @@ -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)) \ No newline at end of file diff --git a/protocol/src/cljs/syng_im/protocol/handler.cljs b/protocol/src/cljs/syng_im/protocol/handler.cljs index d8072d7..101213d 100644 --- a/protocol/src/cljs/syng_im/protocol/handler.cljs +++ b/protocol/src/cljs/syng_im/protocol/handler.cljs @@ -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) - (read-string) - (assoc :group-id group-id))] + {private-key :private} (get-keypair store group-id)] + (-> (decrypt private-key encrypted-payload) + (read-string) + (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")))) diff --git a/protocol/src/cljs/syng_im/protocol/state/delivery.cljs b/protocol/src/cljs/syng_im/protocol/state/delivery.cljs index c4b3923..6f95026 100644 --- a/protocol/src/cljs/syng_im/protocol/state/delivery.cljs +++ b/protocol/src/cljs/syng_im/protocol/state/delivery.cljs @@ -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] diff --git a/protocol/src/cljs/syng_im/protocol/state/group_chat.cljs b/protocol/src/cljs/syng_im/protocol/state/group_chat.cljs index 3f430ec..12ad389 100644 --- a/protocol/src/cljs/syng_im/protocol/state/group_chat.cljs +++ b/protocol/src/cljs/syng_im/protocol/state/group_chat.cljs @@ -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))) diff --git a/simple-whisper-chat/resources/public/index.html b/simple-whisper-chat/resources/public/index.html index 2dd55c7..1728d77 100644 --- a/simple-whisper-chat/resources/public/index.html +++ b/simple-whisper-chat/resources/public/index.html @@ -69,6 +69,11 @@
+
+ + + +
diff --git a/simple-whisper-chat/src/cljs/syng_im/core.cljs b/simple-whisper-chat/src/cljs/syng_im/core.cljs index fc2fbeb..e9d09f6 100644 --- a/simple-whisper-chat/src/cljs/syng_im/core.cljs +++ b/simple-whisper-chat/src/cljs/syng_im/core.cljs @@ -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 - :content msg}) + (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"