-
Notifications
You must be signed in to change notification settings - Fork 985
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[mentions] Replace all mentions with public keys before sending message
- Loading branch information
Showing
4 changed files
with
214 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
(ns status-im.chat.models.mentions | ||
(:require [clojure.string :as string] | ||
[status-im.contact.db :as contact.db])) | ||
|
||
(defn get-mentionable-users | ||
[{{:keys [current-chat-id messages] | ||
:contacts/keys [contacts] :as db} :db}] | ||
(let [{:keys [group-chat public?] :as chat} | ||
(get-in db [:chats current-chat-id]) | ||
chat-specific-suggestions | ||
(cond | ||
(and group-chat (not public?)) | ||
(let [{:keys [public-key]} (:multiaccount db) | ||
all-contacts (:contacts/contacts db) | ||
group-contacts | ||
(contact.db/get-all-contacts-in-group-chat | ||
(disj (:contacts chat) public-key) | ||
nil | ||
all-contacts | ||
nil)] | ||
(reduce | ||
(fn [acc {:keys [alias public-key identicon name]}] | ||
(assoc acc alias | ||
{:alias alias | ||
:identicon identicon | ||
:public-key public-key | ||
:name name})) | ||
{} | ||
group-contacts)) | ||
|
||
(and group-chat public?) | ||
(reduce | ||
(fn [acc [_ {:keys [alias name identicon from]}]] | ||
(assoc acc alias {:alias alias | ||
:name (or name alias) | ||
:identicon identicon | ||
:public-key from})) | ||
nil | ||
(get messages current-chat-id)) | ||
|
||
:else {})] | ||
(reduce | ||
(fn [acc [key {:keys [alias name identicon]}]] | ||
(let [name (string/replace name ".stateofus.eth" "")] | ||
(assoc acc alias {:alias alias | ||
:name (or name alias) | ||
:identicon identicon | ||
:public-key key}))) | ||
chat-specific-suggestions | ||
contacts))) | ||
|
||
(def word-regex #"^[\S]*\s|^[\S]*$") | ||
|
||
(defn mentioned? [{:keys [alias name]} text] | ||
(let [lcase-name (string/lower-case name) | ||
lcase-alias (string/lower-case alias) | ||
regex (re-pattern | ||
(string/join | ||
"|" | ||
[(str "^" lcase-name "\\s") | ||
(str "^" lcase-name "$") | ||
(str "^" lcase-alias "\\s") | ||
(str "^" lcase-alias "$")])) | ||
lcase-text (string/lower-case text)] | ||
(re-find regex lcase-text))) | ||
|
||
(defn match-mention | ||
([text users mention-key-idx] | ||
(match-mention text users mention-key-idx (inc mention-key-idx) [])) | ||
([text users mention-key-idx next-word-idx words] | ||
(let [trimmed-text (subs text next-word-idx)] | ||
(when-let [word (string/trim (re-find word-regex trimmed-text))] | ||
(let [words (conj words word) | ||
searched-text (string/lower-case (string/join " " words)) | ||
suggestions (filter | ||
(fn [[_ {:keys [alias name]}]] | ||
(let [names (set [alias name])] | ||
(some | ||
(fn [username] | ||
(string/starts-with? | ||
(string/lower-case username) | ||
searched-text)) | ||
names))) | ||
users) | ||
suggestions-cnt (count suggestions)] | ||
(cond (zero? suggestions-cnt) | ||
nil | ||
|
||
(and (= 1 suggestions-cnt) | ||
(mentioned? (second (first suggestions)) | ||
(subs text (inc mention-key-idx)))) | ||
(second (first suggestions)) | ||
|
||
(> suggestions-cnt 1) | ||
(let [word-len (count word) | ||
text-len (count text) | ||
next-word-start (+ next-word-idx (inc word-len))] | ||
(when (> text-len next-word-start) | ||
(match-mention text users mention-key-idx | ||
next-word-start words))))))))) | ||
|
||
(defn replace-mentions | ||
([text users-fn] | ||
(replace-mentions text users-fn 0)) | ||
([text users-fn idx] | ||
(let [mention-key-idx (string/index-of text "@" idx)] | ||
(if-not mention-key-idx | ||
text | ||
(let [users (users-fn) | ||
{:keys [public-key alias]} | ||
(match-mention text users mention-key-idx)] | ||
(if-not alias | ||
(recur text (fn [] users) (inc mention-key-idx)) | ||
(let [new-text (string/join | ||
[(subs text 0 (inc mention-key-idx)) | ||
public-key | ||
(subs text (+ (inc mention-key-idx) | ||
(count alias)))]) | ||
mention-end (+ (inc mention-key-idx) (count public-key))] | ||
(recur new-text (fn [] users) mention-end)))))))) | ||
|
||
(defn check-mentions [cofx text] | ||
(replace-mentions text #(get-mentionable-users cofx))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
(ns status-im.chat.models.mentions-test | ||
(:require [status-im.chat.models.mentions :as mentions] | ||
[clojure.string :as string] | ||
[cljs.test :as test :include-macros true])) | ||
|
||
(test/deftest test-replace-mentions | ||
(let [users (fn [] | ||
{"User Number One" | ||
{:name "User Number One" | ||
:alias "User Number One" | ||
:public-key "0xpk1"} | ||
"User Number Two" | ||
{:name "user2" | ||
:alias "User Number Two" | ||
:public-key "0xpk2"} | ||
"User Number Three" | ||
{:name "user3" | ||
:alias "User Number Three" | ||
:public-key "0xpk3"}})] | ||
(test/testing "no mentions" | ||
(let [text "foo bar @buzz kek @foo" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result text) (pr-str text)))) | ||
|
||
(test/testing "starts with mention" | ||
(let [text "@User Number One" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "@0xpk1") (pr-str text)))) | ||
|
||
(test/testing "starts with mention but no space after" | ||
(let [text "@User Number Onefoo" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result text) (pr-str text)))) | ||
|
||
(test/testing "starts with mention, some text after mention" | ||
(let [text "@User Number One foo" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "@0xpk1 foo") (pr-str text)))) | ||
|
||
(test/testing "starts with some text, then mention" | ||
(let [text "text @User Number One" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "text @0xpk1") (pr-str text)))) | ||
|
||
(test/testing "starts with some text, then mention, then more text" | ||
(let [text "text @User Number One foo" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "text @0xpk1 foo") (pr-str text)))) | ||
|
||
(test/testing "no space before mention" | ||
(let [text "text@User Number One" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "text@0xpk1") (pr-str text)))) | ||
|
||
(test/testing "two different mentions" | ||
(let [text "@User Number One @User Number two" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "@0xpk1 @0xpk2") (pr-str text)))) | ||
|
||
(test/testing "two different mentions inside text" | ||
(let [text "foo@User Number One bar @User Number two baz" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "foo@0xpk1 bar @0xpk2 baz") (pr-str text)))) | ||
|
||
(test/testing "ens mention" | ||
(let [text "@user2" | ||
result (mentions/replace-mentions text users)] | ||
(test/is (= result "@0xpk2") (pr-str text)))) | ||
|
||
(test/testing "multiple mentions" | ||
(let [text (string/join | ||
" " | ||
(repeat 1000 "@User Number One @User Number two")) | ||
result (mentions/replace-mentions text users) | ||
exprected-result (string/join | ||
" " | ||
(repeat 1000 "@0xpk1 @0xpk2"))] | ||
(test/is (= exprected-result result)))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters