diff --git a/bots/mailman/bot.js b/bots/mailman/bot.js index a1ef5625a6..75f9df04cc 100644 --- a/bots/mailman/bot.js +++ b/bots/mailman/bot.js @@ -5,6 +5,12 @@ status.command({ description: I18n.t('location_description'), color: "#a187d5", sequentialParams: true, + registeredOnly: true, + params: [{ + name: "address", + type: status.types.TEXT, + placeholder: I18n.t('location_address') + }], preview: function (params) { var text = status.components.text( { @@ -42,8 +48,4 @@ status.command({ ) }; } -}).param({ - name: "address", - type: status.types.TEXT, - placeholder: I18n.t('location_address') }); diff --git a/src/status_im/bots/constants.cljs b/src/status_im/bots/constants.cljs new file mode 100644 index 0000000000..1743ba9d44 --- /dev/null +++ b/src/status_im/bots/constants.cljs @@ -0,0 +1,7 @@ +(ns status-im.bots.constants) + +(def mailman-bot "mailman") +(defn mailman-bot? [bot-name] + (= mailman-bot bot-name)) + +(def hidden-bots #{mailman-bot}) diff --git a/src/status_im/chat/handlers/input.cljs b/src/status_im/chat/handlers/input.cljs index c874886a64..a1037ee94f 100644 --- a/src/status_im/chat/handlers/input.cljs +++ b/src/status_im/chat/handlers/input.cljs @@ -122,7 +122,7 @@ :params parameter-index :suggestions] - args (-> (get-in db [:chats jail-id :input-text]) + args (-> (get-in db [:chats current-chat-id :input-text]) (input-model/split-command-args) (rest)) params {:parameters {:args args} @@ -221,6 +221,7 @@ (dispatch [::request-command-data {:command command :chat-id chat-id + :jail-id (get-in command [:command :bot]) :data-type :preview :after #(dispatch [::send-message % chat-id])}]))))) @@ -278,7 +279,7 @@ (handlers/register-handler ::check-dapp-suggestions (handlers/side-effect! - (fn [{:keys [current-account-id] :as db} [_ chat-id text]] + (fn [{:keys [current-account-id] :as db} [_ chat-id text]] (let [data (get-in db [:local-storage chat-id])] (status/call-function! {:chat-id chat-id diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs index 9ec140e753..8d8d58e8d2 100644 --- a/src/status_im/chat/handlers/send_message.cljs +++ b/src/status_im/chat/handlers/send_message.cljs @@ -31,7 +31,8 @@ :timestamp (time/now-ms) :content (assoc content :handler-data handler-data :type (name (:type command)) - :content-command (:name command)) + :content-command (:name command) + :bot (:bot command)) :content-type (or content-type (if request content-type-command-request diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index d3fecbf487..74553b939b 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -6,7 +6,8 @@ [status-im.i18n :as i18n] [status-im.utils.phone-number :as phone-number] [taoensso.timbre :as log] - [status-im.chat.utils :as chat-utils])) + [status-im.chat.utils :as chat-utils] + [status-im.bots.constants :as bots-constants])) (defn text-ends-with-space? [text] (when text @@ -19,8 +20,8 @@ commands' (into {} (map (fn [[k v]] [k [v :any]]) (merge global-commands commands))) responses' (into {} (map (fn [{:keys [message-id type]}] - [type [(get responses type) message-id]]) - requests))] + [type [(get responses type) message-id]]) + requests))] (vals (merge commands' responses')))) (defn split-command-args [command-text] @@ -63,10 +64,12 @@ command-args (split-command-args input-text) command-name (first command-args)] (when (chat-utils/starts-as-command? (or command-name "")) - (when-let [[command to-message-id] (-> (filter (fn [[{:keys [name bot]} message-id]] - (= (or bot name) (subs command-name 1))) - possible-actions) - (first))] + (when-let [[command to-message-id] + (-> (filter (fn [[{:keys [name bot]} message-id]] + (= (or (when-not (bots-constants/mailman-bot? bot) bot) name) + (subs command-name 1))) + possible-actions) + (first))] {:command command :metadata (if (not= :any to-message-id) (assoc input-metadata :to-message-id to-message-id) @@ -75,7 +78,7 @@ (rest command-args) seq-arguments)})))) ([{:keys [current-chat-id] :as db} chat-id] - (selected-chat-command db chat-id (get-in db [:chats chat-id :input-text])))) + (selected-chat-command db chat-id (get-in db [:chats chat-id :input-text])))) (defn current-chat-argument-position [{:keys [args] :as command} input-text seq-arguments] @@ -93,17 +96,17 @@ -1)) (defn argument-position [{:keys [current-chat-id] :as db} chat-id] - (let [chat-id (or chat-id current-chat-id) - input-text (get-in db [:chats chat-id :input-text]) - seq-arguments (get-in db [:chats chat-id :seq-arguments]) - chat-command (selected-chat-command db chat-id)] + (let [chat-id (or chat-id current-chat-id) + input-text (get-in db [:chats chat-id :input-text]) + seq-arguments (get-in db [:chats chat-id :seq-arguments]) + chat-command (selected-chat-command db chat-id)] (current-chat-argument-position chat-command input-text seq-arguments))) (defn command-completion ([{:keys [current-chat-id] :as db} chat-id] - (let [chat-id (or chat-id current-chat-id) - input-text (get-in db [:chats chat-id :input-text]) - chat-command (selected-chat-command db chat-id)] + (let [chat-id (or chat-id current-chat-id) + input-text (get-in db [:chats chat-id :input-text]) + chat-command (selected-chat-command db chat-id)] (command-completion chat-command))) ([{:keys [args] :as chat-command}] (let [args (remove str/blank? args) diff --git a/src/status_im/chat/utils.cljs b/src/status_im/chat/utils.cljs index e6eb9a5296..4b1a6dfc72 100644 --- a/src/status_im/chat/utils.cljs +++ b/src/status_im/chat/utils.cljs @@ -2,7 +2,8 @@ (:require [status-im.constants :refer [console-chat-id wallet-chat-id]] [clojure.string :as str] - [status-im.chat.constants :as const])) + [status-im.chat.constants :as const] + [status-im.bots.constants :as bots-constants])) (defn console? [s] (= console-chat-id s)) @@ -48,9 +49,13 @@ (pos? (count message)))) (defn command-name [{:keys [bot name]}] - (if bot - (str const/bot-char bot) - (str const/command-char name))) + (cond + (bots-constants/mailman-bot? bot) + (str const/command-char name) + + bot (str const/bot-char bot) + + :else (str const/command-char name))) (defn starts-as-command? [text] (and (not (nil? text)) diff --git a/src/status_im/chat/views/message/message.cljs b/src/status_im/chat/views/message/message.cljs index 7b8f446e32..d1a0e04fe3 100644 --- a/src/status_im/chat/views/message/message.cljs +++ b/src/status_im/chat/views/message/message.cljs @@ -127,10 +127,12 @@ :get-responses :get-commands) chat-id] + global-commands [:get :global-commands] current-chat-id [:get-current-chat-id] contact-chat [:get-in [:chats (if outgoing to from)]] preview [:get-in [:message-data :preview message-id :markup]]] - (let [{:keys [command params]} (parse-command-message-content commands content) + (let [{:keys [command params]} + (parse-command-message-content commands global-commands content) {:keys [name type] icon-path :icon} command] [view st/content-command-view @@ -370,15 +372,16 @@ (into [view] children))) (defn chat-message [{:keys [outgoing message-id chat-id user-statuses from] :as message}] - (let [my-identity (subscribe [:get :current-public-key]) - status (subscribe [:get-in [:message-data :user-statuses message-id my-identity]]) - preview (subscribe [:get-in [:message-data :preview message-id :markup]])] + (let [my-identity (subscribe [:get :current-public-key]) + status (subscribe [:get-in [:message-data :user-statuses message-id my-identity]]) + preview (subscribe [:get-in [:message-data :preview message-id :markup]])] (r/create-class {:component-will-mount (fn [] - (when (and (get-in message [:content :command]) - (not @preview)) - (dispatch [:request-command-data message :preview]))) + (let [{:keys [bot] :as command} (get-in message [:content]) + message' (assoc message :jail-id bot)] + (when (and command (not @preview)) + (dispatch [:request-command-data message' :preview])))) :component-did-mount (fn [] diff --git a/src/status_im/chats_list/views/inner_item.cljs b/src/status_im/chats_list/views/inner_item.cljs index fb7e9742db..cd6a51dbf6 100644 --- a/src/status_im/chats_list/views/inner_item.cljs +++ b/src/status_im/chats_list/views/inner_item.cljs @@ -5,7 +5,6 @@ [status-im.components.react :refer [view image icon text]] [status-im.components.chat-icon.screen :refer [chat-icon-view-chat-list]] [status-im.components.context-menu :refer [context-menu]] - [status-im.models.commands :refer [parse-command-message-content]] [status-im.chats-list.styles :as st] [status-im.utils.utils :refer [truncate-str]] [status-im.i18n :refer [get-contact-translated label label-pluralize]] diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs index f657efd7e5..da67a32dd3 100644 --- a/src/status_im/commands/handlers/loading.cljs +++ b/src/status_im/commands/handlers/loading.cljs @@ -14,22 +14,27 @@ [status-im.utils.homoglyph :as h] [status-im.utils.js-resources :as js-res] [status-im.utils.random :as random] - [status-im.chat.sign-up :as sign-up])) + [status-im.chat.sign-up :as sign-up] + [status-im.bots.constants :as bots-constants])) (def commands-js "commands.js") (defn load-commands! - [{:keys [current-chat-id contacts]} [identity callback]] - (let [identity' (or identity current-chat-id) - contact (or (get contacts identity') - sign-up/console-contact)] + [{:keys [current-chat-id contacts]} [contact callback]] + (let [whole-contact? (map? contact) + {:keys [whisper-identity]} contact + identity' (or whisper-identity contact current-chat-id) + contact' (if whole-contact? + contact + (or (get contacts identity') + sign-up/console-contact))] (when identity' - (dispatch [::fetch-commands! {:contact contact + (dispatch [::fetch-commands! {:contact contact' :callback callback}]))) ;; todo uncomment - #_(if-let [{:keys [file]} (commands/get-by-chat-id identity)] - (dispatch [::parse-commands! identity file]) - (dispatch [::fetch-commands! identity]))) + #_(if-let [{:keys [file]} (commands/get-by-chat-id contact)] + (dispatch [::parse-commands! contact file]) + (dispatch [::fetch-commands! contact]))) (defn http-get-commands [params url] @@ -103,8 +108,9 @@ (defn filter-forbidden-names [account id commands] (->> commands - (remove (fn [[_ {:keys [registered-only]}]] + (remove (fn [[_ {:keys [registered-only name]}]] (and (not (:address account)) + (not= name "global") registered-only))) (remove (fn [[n]] (and @@ -112,37 +118,63 @@ (h/matches (name n) "password")))) (into {}))) +(defn get-mailmans-commands [db] + (->> (get-in db [:contacts bots-constants/mailman-bot :commands]) + (map + (fn [[k v :as com]] + [k (-> v + (update :params (fn [p] + (if (map? p) + ((comp vec vals) p) + p))) + (assoc :bot bots-constants/mailman-bot + :type :command))])) + (into {}))) + (defn add-commands [db [id _ {:keys [commands responses subscriptions]}]] - (let [account @(subscribe [:get-current-account]) - commands' (filter-forbidden-names account id commands) - global-command (:global commands') - commands'' (apply dissoc commands' [:init :global]) - responses' (filter-forbidden-names account id responses)] + (let [account @(subscribe [:get-current-account]) + commands' (filter-forbidden-names account id commands) + global-command (:global commands') + commands'' (apply dissoc commands' [:init :global]) + responses' (filter-forbidden-names account id responses) + mailman-commands (get-mailmans-commands db)] (cond-> db true (update-in [:contacts id] assoc :commands-loaded true - :commands (mark-as :command commands'') + :commands (mark-as :command (merge mailman-commands commands'')) :responses (mark-as :response responses') :subscriptions subscriptions) global-command (update :global-commands assoc (keyword id) (assoc global-command :bot id - :type :command))))) + :type :command)) + + (= id bots-constants/mailman-bot) + (update db :contacts (fn [contacts] + (reduce (fn [contacts [k _]] + (update-in contacts [k :commands] + (fn [c] + (merge mailman-commands c)))) + contacts + contacts)))))) (defn save-commands-js! [_ [id file]] #_(commands/save {:chat-id id :file file})) -(defn save-global-command! - [{:keys [global-commands]} [id]] - (let [command (get global-commands (keyword id))] - (when command - (contacts/save {:whisper-identity id - :global-command command})))) +(defn save-commands! + [{:keys [global-commands contacts]} [id]] + (let [command (get global-commands (keyword id)) + commands (get-in contacts [id :commands]) + responses (get-in contacts [id :commands])] + (contacts/save {:whisper-identity id + :global-command command + :commands (vals commands) + :responses (vals responses)}))) (defn loading-failed! [db [id reason details]] @@ -173,7 +205,7 @@ (reg-handler ::add-commands [(after save-commands-js!) - (after save-global-command!) + (after save-commands!) (after #(dispatch [:check-and-open-dapp!])) (after #(dispatch [:update-suggestions])) (after (fn [_ [id]] diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index 99a3049e05..f1127a5ca0 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -223,7 +223,7 @@ (register-handler :load-default-contacts! (u/side-effect! - (fn [{:keys [chats groups]}] + (fn [{:keys [contacts groups]}] (let [default-contacts js-res/default-contacts default-groups js-res/default-contact-groups] (dispatch [:add-groups (mapv @@ -237,21 +237,23 @@ (doseq [[id {:keys [name photo-path public-key add-chat? has-global-command? dapp? dapp-url dapp-hash bot-url]}] default-contacts] (let [id' (clojure.core/name id)] - (when-not (chats id') + (when-not (get contacts id') (when add-chat? (dispatch [:add-chat id' {:name (:en name)}])) - (dispatch [:add-contacts [{:whisper-identity id' - :address (public-key->address id') - :name (:en name) - :photo-path photo-path - :public-key public-key - :dapp? dapp? - :dapp-url (:en dapp-url) - :bot-url bot-url - :has-global-command? has-global-command? - :dapp-hash dapp-hash}]]) - (when bot-url - (dispatch [:load-commands! id']))))))))) + (let [contact + {:whisper-identity id' + :address (public-key->address id') + :name (:en name) + :photo-path photo-path + :public-key public-key + :dapp? dapp? + :dapp-url (:en dapp-url) + :bot-url bot-url + :has-global-command? has-global-command? + :dapp-hash dapp-hash}] + (dispatch [:add-contacts [contact]]) + (when bot-url + (dispatch [:load-commands! contact])))))))))) (register-handler :add-contacts diff --git a/src/status_im/contacts/subs.cljs b/src/status_im/contacts/subs.cljs index 565b23483e..e53b298277 100644 --- a/src/status_im/contacts/subs.cljs +++ b/src/status_im/contacts/subs.cljs @@ -2,7 +2,8 @@ (:require-macros [reagent.ratom :refer [reaction]]) (:require [re-frame.core :refer [register-sub subscribe]] [status-im.utils.identicon :refer [identicon]] - [clojure.string :as str])) + [clojure.string :as str] + [status-im.bots.constants :as bots-constants])) (register-sub :current-contact (fn [db [_ k]] @@ -23,10 +24,13 @@ (clojure.string/lower-case name2)))) (vals contacts))) + (register-sub :all-added-contacts (fn [db _] (let [contacts (reaction (:contacts @db))] - (->> (remove #(true? (:pending? (second %))) @contacts) + (->> (remove (fn [[_ {:keys [pending? whisper-identity]}]] + (or (true? pending?) + (bots-constants/hidden-bots whisper-identity))) @contacts) (sort-contacts) (reaction))))) diff --git a/src/status_im/data_store/contacts.cljs b/src/status_im/data_store/contacts.cljs index 2a43a4e17e..5a7964a9e8 100644 --- a/src/status_im/data_store/contacts.cljs +++ b/src/status_im/data_store/contacts.cljs @@ -2,9 +2,18 @@ (:require [status-im.data-store.realm.contacts :as data-store]) (:refer-clojure :exclude [exists?])) +(defn- command->map-item + [[_ {:keys [name] :as command}]] + [(keyword name) command]) + (defn get-all [] - (data-store/get-all-as-list)) + (map + (fn [{:keys [commands responses] :as contact}] + (assoc contact + :commands (into {} (map command->map-item commands)) + :responses (into {} (map command->map-item responses)))) + (data-store/get-all-as-list))) (defn get-by-id [whisper-identity] @@ -15,9 +24,9 @@ (let [{pending-db? :pending? :as contact-db} (data-store/get-by-id whisper-identity) contact (assoc contact :pending? - (boolean (if contact-db - (if (nil? pending?) pending-db? pending?) - pending?)))] + (boolean (if contact-db + (if (nil? pending?) pending-db? pending?) + pending?)))] (data-store/save contact (if contact-db true false)))) (defn save-all diff --git a/src/status_im/data_store/realm/schemas/account/v6/command.cljs b/src/status_im/data_store/realm/schemas/account/v6/command.cljs index 6cf95d16dc..c1f89a0776 100644 --- a/src/status_im/data_store/realm/schemas/account/v6/command.cljs +++ b/src/status_im/data_store/realm/schemas/account/v6/command.cljs @@ -7,6 +7,8 @@ :color {:type :string :optional true} :name {:type :string} + :icon {:type :string + :optional true} :params {:type :list :objectType :command-parameter} :title {:type :string @@ -16,7 +18,9 @@ :fullscreen {:type :bool :default true} :suggestions-trigger {:type :string - :default "on-change"}}}) + :default "on-change"} + :sequential-params {:type :bool + :default false}}}) (defn migration [old-realm new-realm] (log/debug "migrating chat-contact schema v6")) diff --git a/src/status_im/data_store/realm/schemas/account/v6/contact.cljs b/src/status_im/data_store/realm/schemas/account/v6/contact.cljs index 543e2c15ac..3ff6cbc1d4 100644 --- a/src/status_im/data_store/realm/schemas/account/v6/contact.cljs +++ b/src/status_im/data_store/realm/schemas/account/v6/contact.cljs @@ -23,6 +23,10 @@ :optional true} :global-command {:type :command :optional true} + :commands {:type :list + :objectType :command} + :responses {:type :list + :objectType :command} :dapp-hash {:type :int :optional true} :debug? {:type :bool @@ -33,10 +37,10 @@ (let [new-contacts (.objects new-realm "contact")] (dotimes [i (.-length new-contacts)] (let [contact (aget new-contacts i) - id (aget contact "whisper-identity")] + id (aget contact "whisper-identity")] (when (= id "console") (log/debug (js->clj contact)) (aset contact "dapp-url" nil) - (aset contact "bot-url" "local://console-bot")) + (aset contact "bot-url" "local://console-bot")) (when (= id "wallet") (aset contact "dapp-url" "https://status.im/dapps/wallet/")))))) diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index ef303951ab..8710a74b8f 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -49,6 +49,7 @@ network-status network first-run]} _] (data-store/init) (assoc app-db :current-account-id nil + :contacts {} :network-status network-status :status-module-initialized? (or p/ios? js/goog.DEBUG status-module-initialized?) :status-node-started? status-node-started? @@ -97,7 +98,6 @@ (dispatch [:set :first-run false])) (when (or (not first-run) (empty? accounts)) (dispatch [:init-console-chat]) - (dispatch [:load-default-contacts!]) (dispatch [:load-commands!]) (when callback (callback)))))) diff --git a/src/status_im/models/commands.cljs b/src/status_im/models/commands.cljs index 2f3887e373..c76647ccf6 100644 --- a/src/status_im/models/commands.cljs +++ b/src/status_im/models/commands.cljs @@ -1,10 +1,15 @@ (ns status-im.models.commands (:require [status-im.db :as db] - [tailrecursion.priority-map :refer [priority-map-by]])) + [tailrecursion.priority-map :refer [priority-map-by]] + [status-im.bots.constants :as bots-constants])) -(defn parse-command-message-content [commands content] +(defn parse-command-message-content + [commands global-commands content] (if (map? content) - (update content :command #((keyword %) commands)) + (let [{:keys [command bot]} content] + (if (and bot (not (bots-constants/mailman-bot? bot))) + (update content :command #((keyword bot) global-commands)) + (update content :command #((keyword command) commands)))) content)) (defn parse-command-request [commands content]