add participant to group chat

This commit is contained in:
michaelr 2016-03-10 16:29:48 +02:00
parent d5d781410f
commit 89c85025db
9 changed files with 146 additions and 44 deletions

View File

@ -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))

View File

@ -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)

View File

@ -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)))

View File

@ -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))

View File

@ -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"))))

View File

@ -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]

View File

@ -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)))

View File

@ -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>

View File

@ -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"