Merge pull request #7 from status-im/discover-rework

Discovery and status/profile data exchange rework
This commit is contained in:
Roman Volosovskyi 2016-08-18 20:09:20 +03:00 committed by GitHub
commit 281a052804
9 changed files with 199 additions and 162 deletions

View File

@ -30,7 +30,7 @@
:cljsbuild {:builds
[{:id "dev"
:source-paths ["src/cljs" "protocol/src/cljs"]
:source-paths ["src/cljs" "../../src/cljs"]
;; If no code is to be run, set :figwheel true for continued automagical reloading
:figwheel {:on-jsload "status-im.core/on-js-reload"}

View File

@ -125,8 +125,8 @@
(add-to-chat "chat" ":" (str "Don't know how to handle " event-type))))})
(e/listen (-> (g/getElement "msg")
(goog.events.KeyHandler.))
key-handler-events/KEY
(fn [e]
key-handler-events/KEY
(fn [e]
(when (= (.-keyCode e) KeyCodes/ENTER)
(let [msg (-> (g/getElement "msg")
(f/getValue))
@ -137,8 +137,8 @@
(add-to-chat "chat" (p/my-identity) msg)))))
(e/listen (-> (g/getElement "group-msg")
(goog.events.KeyHandler.))
key-handler-events/KEY
(fn [e]
key-handler-events/KEY
(fn [e]
(when (= (.-keyCode e) KeyCodes/ENTER)
(let [msg (-> (g/getElement "group-msg")
(f/getValue))

View File

@ -1,4 +1,4 @@
(defproject status-im/protocol "0.1.1"
(defproject status-im/protocol "0.1.3"
:description "FIXME: write this!"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"

View File

@ -4,7 +4,7 @@
[status-im.protocol.state.state :as state :refer [set-storage
set-handler
set-connection
set-identity
set-account
connection
storage]]
[status-im.protocol.state.delivery :refer [add-pending-message]]
@ -22,7 +22,9 @@
save-hashtags
get-topics
save-status
save-name]]
save-name
save-photo-path
discovery-topic]]
[status-im.protocol.delivery :refer [start-delivery-loop]]
[status-im.protocol.web3 :refer [listen
make-msg
@ -32,8 +34,7 @@
add-identity
stop-listener
stop-watching-filters]]
[status-im.protocol.handler :refer [handle-incoming-whisper-msg]
:as handler]
[status-im.protocol.handler :refer [handle-incoming-whisper-msg] :as handler]
[status-im.protocol.user-handler :refer [invoke-user-handler]]
[status-im.utils.encryption :refer [new-keypair]]
[status-im.protocol.group-chat :refer [send-group-msg
@ -41,12 +42,14 @@
group-add-participant-msg
group-remove-participant-msg
removed-from-group-msg]]
[status-im.protocol.discovery :refer [init-discovery
get-hashtag-topics
discovery-response-topic
discovery-search-topic
[status-im.protocol.discovery :refer [hashtags->topics
user-topic
discovery-topic
discovery-search-message
broadcast-status]]
broadcast-status
broadcast-account-update
broadcast-online
do-periodically]]
[status-im.protocol.defaults :refer [default-content-type]]
[status-im.utils.logging :as log])
(:require-macros [cljs.core.async.macros :refer [go]]))
@ -54,9 +57,16 @@
(defn create-connection [ethereum-rpc-url]
(make-web3 ethereum-rpc-url))
(defn my-account []
(state/my-account))
(defn my-identity []
(state/my-identity))
(defn send-online []
(let [topics [[(user-topic (my-identity)) discovery-topic]]]
(broadcast-online topics)))
(defn init-protocol
"Required [handler ethereum-rpc-url storage]
Optional [identity - if not passed a new identity is created automatically
@ -83,23 +93,29 @@
"
([parameters] (init-protocol {:public-key "no-identity"
:address "no-address"} parameters))
([{:keys [public-key] :as account} {:keys [handler ethereum-rpc-url storage identity active-group-ids]}]
([account {:keys [handler ethereum-rpc-url storage active-group-ids]}]
(when (seq (state/get-all-filters))
(stop-watching-filters))
(set-storage storage)
(set-handler handler)
(go
(let [connection (create-connection ethereum-rpc-url)]
(let [connection (create-connection ethereum-rpc-url)
topics (get-topics)]
(set-connection connection)
(set-identity public-key)
(set-account account)
(listen connection handle-incoming-whisper-msg)
(start-delivery-loop)
(doseq [group-id active-group-ids]
(listen connection handle-incoming-whisper-msg {:topics [group-id]}))
(init-discovery)
(listen connection handle-incoming-whisper-msg {:topics [discovery-response-topic]})
(listen connection handle-incoming-whisper-msg {:topic [group-id]}))
(doseq [topic topics]
(listen connection handle-incoming-whisper-msg {:topic topic}))
(do-periodically (* 60 10 1000) send-online)
(invoke-user-handler :initialized {:identity account})))))
(defn watch-user [{:keys [whisper-identity]}]
(let [topic [(user-topic whisper-identity) discovery-topic]]
(listen (connection) handle-incoming-whisper-msg {:topic topic})))
(defn send-user-msg [{:keys [to content msg-id]}]
(let [{:keys [msg-id msg] :as new-msg}
(make-msg {:from (state/my-identity)
@ -123,17 +139,17 @@
(start-group-chat identities nil))
([identities group-name]
(let [group-topic (random/id)
keypair (new-keypair)
store (storage)
connection (connection)
keypair (new-keypair)
store (storage)
connection (connection)
my-identity (state/my-identity)
identities (-> (set identities)
(conj my-identity))]
identities (-> (set identities)
(conj my-identity))]
(save-keypair store group-topic keypair)
(save-identities store group-topic identities)
(save-group-admin store group-topic my-identity)
(save-group-name store group-topic group-name)
(listen connection handle-incoming-whisper-msg {:topics [group-topic]})
(listen connection handle-incoming-whisper-msg {:topic [group-topic]})
(doseq [ident identities :when (not (= ident my-identity))]
(let [{:keys [msg-id msg]} (init-group-chat-msg ident group-topic identities keypair group-name)]
(add-pending-message msg-id msg {:internal? true})
@ -143,14 +159,14 @@
(defn group-add-participant
"Only call if you are the group-admin"
[group-id new-peer-identity]
(let [store (storage)
(let [store (storage)
my-identity (my-identity)]
(if-not (group-admin? store group-id my-identity)
(log/error "Called group-add-participant but not group admin, group-id:" group-id "my-identity:" my-identity)
(let [connection (connection)
identities (-> (get-identities store group-id)
(conj new-peer-identity))
keypair (get-keypair store group-id)
keypair (get-keypair store group-id)
group-name (group-name store group-id)]
(save-identities store group-id identities)
(let [{:keys [msg-id msg]} (group-add-participant-msg new-peer-identity group-id group-name identities keypair)]
@ -164,14 +180,14 @@
(defn group-remove-participant
"Only call if you are the group-admin"
[group-id identity-to-remove]
(let [store (storage)
(let [store (storage)
my-identity (my-identity)]
(if-not (group-admin? store group-id my-identity)
(log/error "Called group-remove-participant but not group admin, group-id:" group-id "my-identity:" my-identity)
(let [connection (connection)
identities (-> (get-identities store group-id)
(disj identity-to-remove))
keypair (new-keypair)]
keypair (new-keypair)]
(save-identities store group-id identities)
(save-keypair store group-id keypair)
(doseq [ident identities :when (not (= ident my-identity))]
@ -183,31 +199,32 @@
(post-msg connection msg))))))
(defn leave-group-chat [group-id]
(let [store (storage)
(let [store (storage)
my-identity (my-identity)]
(send-group-msg {:group-id group-id
:type :left-group
:payload {:identity my-identity}
:internal? true})
(remove-group-data store group-id)
(stop-listener group-id)))
(stop-listener [group-id])))
(defn stop-broadcasting-discover []
(let [topics (get-topics)]
(doseq [topic topics]
(stop-listener topic))
(save-hashtags [])))
(doseq [topic (get-topics)]
(stop-listener topic)))
(defn broadcast-discover-status [name status hashtags]
(defn broadcast-discover-status [{:keys [name photo-path]} status hashtags]
(log/debug "Broadcasting status: " name status hashtags)
(let [topics (get-hashtag-topics hashtags)]
(let [topics (hashtags->topics hashtags)]
(stop-broadcasting-discover)
(listen (connection) handle-incoming-whisper-msg {:topics topics})
(doseq [topic topics]
(listen (connection) handle-incoming-whisper-msg {:topic topic}))
(save-name name)
(save-photo-path photo-path)
(save-topics topics)
(save-hashtags hashtags)
(save-status status)
(broadcast-status)))
(broadcast-status topics)
(broadcast-status [[(user-topic (my-identity)) discovery-topic]])))
(defn search-discover [hashtags]
(let [{:keys [msg-id msg]} (discovery-search-message hashtags)]
@ -219,3 +236,7 @@
(defn send-seen [to message-id]
(handler/send-seen (connection) to message-id))
(defn send-account-update [account]
(let [topics [[(user-topic (my-identity)) discovery-topic]]]
(broadcast-account-update topics account)))

View File

@ -1,85 +1,106 @@
(ns status-im.protocol.discovery
(:require [status-im.protocol.state.state :as state :refer [connection
storage]]
storage]]
[status-im.protocol.state.delivery :refer [add-pending-message]]
[status-im.protocol.state.discovery :refer [save-status
get-name
get-status
get-hashtags]]
get-name
get-photo-path
get-status
get-hashtags]]
[status-im.protocol.user-handler :refer [invoke-user-handler]]
[status-im.utils.logging :as log]
[status-im.utils.logging :as log]
[status-im.protocol.web3 :refer [make-msg
post-msg]]))
post-msg]]
[cljs-time.core :refer [now]]
[cljs-time.coerce :refer [to-long]]
[status-im.utils.random :as random]))
(def discovery-response-topic "status-discovery-responses")
(def discovery-search-topic "status-discovery-searches")
(def discovery-hashtag-topic "status-search-")
(def broadcast-interval 1800000)
(def discovery-topic "status-discovery")
(def discovery-user-topic "status-user-")
(def discovery-hashtag-topic "status-hashtag-")
(def daily-broadcast-ttl (* 60 60 24))
(def weekly-broadcast-ttl (* daily-broadcast-ttl 7))
(def monthly-broadcast-ttl (* daily-broadcast-ttl 30))
(defn set-interval
"Invoke the given function after and every delay milliseconds."
[delay f]
(js/setInterval f delay))
(defn get-hashtag-topics
"Create listen topic from hastags."
(defn hashtags->topics
"Create listen topic from hashtags."
[hashtags]
(map #(str discovery-hashtag-topic %) hashtags))
(->> (distinct hashtags)
(sort)
(mapv #(str discovery-hashtag-topic %))
(mapv #(vector % discovery-topic))))
(defn discover-response-message
"Create discover response message."
([payload]
(discover-response-message payload nil))
([payload to]
(let [data {:from (state/my-identity)
:topics [discovery-response-topic]
:payload payload}
_ (log/debug "Creating discover message using: " data)]
(->> (cond-> data
to (assoc :to to))
(make-msg)))))
(defn user-topic
"Create listen topic for user identity"
[identity]
(str discovery-user-topic identity))
(defn send-discover-message
"Send discover message to network."
[{:keys [payload to] :or {to nil}}]
(log/debug "Sending discover status: " payload to)
(let [{:keys [msg-id msg] :as new-msg} (discover-response-message payload to)]
(post-msg (connection) msg)
new-msg))
(defn create-discover-message
"Create discovery message"
[topic payload ttl to msg-id]
(let [data {:msg-id msg-id
:from (state/my-identity)
:topics topic
:ttl ttl
:payload payload}]
(->> (cond-> data
to (assoc :to to))
(make-msg))))
(defn send-broadcast-status
"Broadcast discover message."
[to hashtags location]
(let [name (get-name)
status (get-status)]
(send-discover-message {:payload {:name name
:status status
:hashtags hashtags
:type :discover-response
:location location}
:to to})))
(defn send-discover-messages
"Send discover messages for each topic"
[{:keys [topics payload ttl to] :as msg :or {ttl weekly-broadcast-ttl
to nil}}]
(let [msg-id (random/id)]
(log/debug (str "Sending discover status messages with msg-id " msg-id " for each topic: ") msg)
(doseq [topic topics]
(let [{:keys [msg]} (create-discover-message topic payload ttl to msg-id)]
(post-msg (connection) msg)))))
(defn broadcast-status
"Broadcast discover message if we have hashtags."
([]
(broadcast-status nil))
([to]
(let [hashtags (get-hashtags)]
(when (pos? (count hashtags))
(.getCurrentPosition (.-geolocation js/navigator)
#(send-broadcast-status to hashtags %)
#(send-broadcast-status to hashtags nil)
{:enableHighAccuracy false
:timeout 20000
:maximumAge 1000})))))
[topics]
(let [name (get-name)
photo-path (get-photo-path)
status (get-status)
hashtags (get-hashtags)]
(send-discover-messages {:topics topics
:payload {:name name
:photo-path photo-path
:status status
:hashtags hashtags
:type :discover-response}})))
(defn init-discovery
"Initialize broadcasting discover message."
[]
(set-interval broadcast-interval broadcast-status))
(defn broadcast-account-update
"Broadcast discover message if we have hashtags."
[topics account]
(send-discover-messages {:topics topics
:ttl monthly-broadcast-ttl
:payload {:account account
:type :contact-update}}))
(defn broadcast-online
"Broadcast user's online presence"
[topics]
(send-discover-messages {:topics topics
:ttl daily-broadcast-ttl
:payload {:at (to-long (now))
:type :contact-online}}))
(defn do-periodically
"Do something periodically"
[interval func]
(func)
(set-interval interval func))
(defn discovery-search-message [hashtags]
(let [topics (get-hashtag-topics hashtags)
(let [topics (hashtags->topics hashtags)
payload {:type :discovery-search
:hashtags hashtags}]
make-msg {:from (state/my-identity)
@ -92,50 +113,26 @@
(log/debug "Received discover-response message: " payload)
(when (not (= (state/my-identity) from))
(invoke-user-handler :discover-response {:from from
:payload payload})))
:payload payload})))
(defn handle-discovery-search
"Handle discover-search messages."
[web3 from payload]
(log/debug "Received discover-search message: " payload)
;;TODO (task #188):
(broadcast-status from))
(defn get-next-latitude [latitude]
(let [base-latitude (int latitude)]
(if (< base-latitude 90)
(inc base-latitude)
(dec base-latitude))))
#_(comment
(defn get-next-longitude [longitude]
(let [base-longitude (int longitude)]
(if (< base-longitude 180) (inc base-longitude) -180)))
(get-next-latitude 40.5)
(defn discover-in-proximity [{:keys [latitude longitude]} hashtags]
(let [base-latitude (int latitude)
base-longitude (int longitude)
next-latitude (get-next-latitude latitude)
next-longitude (get-next-longitude longitude)
topics [(clojure.string/join "," [base-latitude base-longitude])
(clojure.string/join "," [next-latitude base-longitude])
(clojure.string/join "," [base-latitude next-longitude])
(clojure.string/join "," [next-latitude next-longitude])]
payload {:type :discover
:hashtags hashtags}]
(make-msg {:from (state/my-identity)
:topics topics
:payload payload})))
(get-next-latitude 90)
(comment
(get-next-longitude 40.5)
(get-next-latitude 40.5)
(get-next-longitude 180)
(get-next-latitude 90)
(discover-in-proximity {:latitude 90 :longitude 180} [])
(get-next-longitude 40.5)
(get-next-longitude 180)
(discover-in-proximity {:latitude 90 :longitude 180} [])
(.getCurrentPosition (.-geolocation js/navigator) #(.log js/console %) #(.log js/console %) {:enableHighAccuracy true, :timeout 20000, :maximumAge 1000})
)
(.getCurrentPosition (.-geolocation js/navigator) #(.log js/console %) #(.log js/console %) {:enableHighAccuracy true, :timeout 20000, :maximumAge 1000})
)

View File

@ -79,7 +79,7 @@
(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]})
(listen web3 handle-incoming-whisper-msg {:topic [group-topic]})
(save-keypair store group-topic keypair)
(save-identities store group-topic identities)
(save-group-admin store group-topic from)
@ -138,7 +138,7 @@
(send-ack web3 from msg-id)
(when (group-member? store group-topic (state/my-identity))
(remove-group-data store group-topic)
(stop-listener group-topic)
(stop-listener [group-topic])
(invoke-user-handler :removed-from-group {:group-id group-topic
:from from
:msg-id msg-id})))
@ -161,6 +161,15 @@
:left-group (handle-participant-left-group web3 from payload))
(log/debug "Could not decrypt group msg, possibly you've left the group.")))
(defn handle-contact-update [from payload]
(log/debug "Received contact-update message: " payload)
(invoke-user-handler :contact-update {:from from
:payload payload}))
(defn handle-contact-online [from payload]
(invoke-user-handler :contact-online {:from from
:payload payload}))
(defn handle-incoming-whisper-msg [web3 msg]
(log/info "Got whisper message:" msg)
(let [{from :from
@ -183,6 +192,8 @@
:left-group (handle-group-msg web3 msg-type from payload)
:discovery-search (handle-discovery-search web3 from payload)
:discover-response (handle-discover-response web3 from payload)
:contact-update (handle-contact-update from payload)
:contact-online (handle-contact-online from payload)
(if msg-type
(log/debug "Undefined message type: " (name msg-type))
(log/debug "Nil message type"))))

View File

@ -1,17 +1,16 @@
(ns status-im.protocol.state.discovery
(:require [status-im.protocol.state.storage :as s]
[status-im.protocol.state.state :as state]
[status-im.protocol.state.state :as state :refer [set-storage
set-handler
set-connection
set-identity
connection
storage]]))
set-handler
set-connection
connection
storage]]))
(def discovery-status "discovery-status")
(def discovery-topics "discovery-topics")
(def discovery-hashtags "discovery-hashtags")
(def discovery-name "discovery-name")
(def discovery-photo-path "discovery-photo-path")
(defn save-status [status]
(let [store (storage)]
@ -45,3 +44,11 @@
(let [store (storage)]
(s/get store discovery-name)))
(defn save-photo-path [photo-path]
(let [store (storage)]
(s/put store discovery-photo-path photo-path)))
(defn get-photo-path []
(let [store (storage)]
(s/get store discovery-photo-path)))

View File

@ -5,7 +5,7 @@
:filters {}
:delivery-queue #queue []
:external-handler nil
:identity nil
:account nil
:connection nil
:storage nil}))
@ -30,8 +30,8 @@
(defn set-handler [handler]
(swap! state assoc :external-handler handler))
(defn set-identity [identity]
(swap! state assoc :identity identity))
(defn set-account [identity]
(swap! state assoc :account identity))
(defn set-connection [connection]
(swap! state assoc :connection connection))
@ -39,8 +39,11 @@
(defn connection []
(:connection @state))
(defn my-account []
(:account @state))
(defn my-identity []
(:identity @state))
(get-in @state [:account :public-key]))
(defn external-handler []
(:external-handler @state))

View File

@ -8,7 +8,7 @@
[status-im.protocol.user-handler :refer [invoke-user-handler]])
(:require-macros [cljs.core.async.macros :refer [go]]))
(def status-app-topic "STATUS-APP-CHAT-TOPIC")
(def status-app-topic "status-app")
(def status-msg-ttl 100)
(defn from-utf8 [s]
@ -112,28 +112,26 @@
from (assoc :from from)
to (assoc :to to))}))
(defn listen
"Returns a filter which can be stopped with (stop-whisper-listener)"
([web3 msg-handler]
(listen web3 msg-handler {}))
([web3 msg-handler {:keys [topics] :as opts :or {topics []}}]
(let [topics (conj topics status-app-topic)
([web3 msg-handler {:keys [topic] :or {topic []}}]
(let [topic (conj topic status-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))))
filter (.filter shh (make-topics topic) (fn [error msg]
(if error
(invoke-user-handler :error {:error-msg error})
(msg-handler web3 msg))))]
(log/debug "Listening to: " topic)
(state/add-filter topic filter))))
(defn stop-listener [group-topic]
(let [topics (conj [group-topic] status-app-topic)
filter (state/get-filter topics)]
(defn stop-listener [topic]
(let [topic (conj topic status-app-topic)
filter (state/get-filter topic)]
(when filter
(do
(.stopWatching filter)
(state/remove-filter topics)))))
(.stopWatching filter)
(state/remove-filter topic))))
(defn stop-watching-filters []
(doseq [filter (state/get-all-filters)]