Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Q-codes #56

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,34 @@ Sends an answer to an callback query sent from inline keyboards.
show-alert)
```

## Q-Code

[Q-Codes](https://en.wikipedia.org/wiki/Q_code) in Morse are shorthands for common patterns.

### Direct Reply
This Q-code expects your handler to return a string which it will respond to the sender automatically.

```clojure
(require '[morse.qcodes :as q]
'[morse.handlers :as h])

(defn just-say-hi [name]
(str "Hi " name "!"))

(h/defhandler bot-api
(h/message {:keys [text]} (just-say-hi text)))

;; direct-reply wraps your handler, it needs the token to send a response
(def wrapped-handler (q/direct-reply token bot-api))
```

### Request to Morse
This Q-code wraps your handler and returns a ring compatible handler. When you use webhooks this should enable you to use the same handler in your server as you've used for long-polling. It will take care of routing to the path and converting the JSON body to EDN.

```clojure
(def ring-handler (q/req->morse "/any-route" bot-api))
```

## License

Copyright © 2017 Anton Chebotaev
Expand Down
4 changes: 3 additions & 1 deletion src/morse/api.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

(def base-url "https://api.telegram.org/bot")

(defn extract-data [resp]
(-> resp :body (json/parse-string true)))

(defn get-updates-async
"Receive updates from Bot via long-polling endpoint"
Expand All @@ -21,7 +23,7 @@
:async? true}
result (a/chan)
on-success (fn [resp]
(if-let [data (-> resp :body (json/parse-string true) :result)]
(if-let [data (-> resp extract-data :result)]
(a/put! result data)
(a/put! result ::error))
(a/close! result))
Expand Down
14 changes: 14 additions & 0 deletions src/morse/qcodes.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(ns morse.qcodes
(:require [morse.api :as api]))

(defn req->morse [handler]
(comp handler api/extract-data))

(defn direct-reply [token handler]
(fn [{{{chatid :id} :chat} :message :as req}]
(when-let [reply (handler req)]
(if (string? reply)
(with-meta
{:status 200}
{:direct-reply (api/send-text token chatid reply)})
{:status 200}))))
5 changes: 5 additions & 0 deletions test/morse/api_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
(def inline-query-id 1337)
(def callback-query-id 1338)

(deftest extract-data-from-update
(let [update-obj {:body "{\"key-num\": 1, \"key-ar-str\": [\"astring\"]}"}
result {:key-num 1 :key-ar-str ["astring"]}]
(is (= (api/extract-data update-obj) result))))

(deftest get-file-request
(let [req (-> (api/get-file token 116)
(u/capture-request))
Expand Down
44 changes: 44 additions & 0 deletions test/morse/qcodes_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
(ns morse.qcodes-test
(:require [cheshire.core :as json]
[clojure.test :refer :all]
[morse.handlers :as h]
[morse.qcodes :as q]
[morse.test-utils :as u]))

(defn command-message [command]
{:text (str "/" command)
:chat {:id "fake-chat-id"}})

(deftest ring-req->morse
(let [handler (h/command-fn "start" :text)
webhandler (q/req->morse handler)
start-request {:body "{\"message\":{\"text\": \"/start\"}}"}
start-command {:message (command-message "start")}]
(is (= (handler start-command) (webhandler start-request)))))

(deftest direct-reply-handler
(let [handler (q/direct-reply "fake-token" (h/command-fn "start" (constantly "change")))
handler2 (q/direct-reply "fake-token" (h/command-fn "start" (constantly {})))
non-string-resp (handler2 {:message (command-message "start")})
nil-resp (handler {:message (command-message "stop")})
direct-reply-request (-> {:message (command-message "start")} handler u/capture-request)
body (json/decode (slurp (:body direct-reply-request)) true)]

; check direct reply does not mess with the message when it shouldn't
(is (= nil-resp nil))

; check that it returns a status 200, optionally with meta
(u/capture-request
(let [string-resp (handler {:message (command-message "start")})]
(is (= string-resp {:status 200}))
(is (-> string-resp meta (contains? :direct-reply)))))
(is (= non-string-resp {:status 200}))

; check a non string responding handler does not generate a text
(is (-> non-string-resp meta (contains? :direct-reply) not))

; check that direct-reply is now post request
(is (= :post (:request-method direct-reply-request)))

; check that default params are presented
(is (u/has-subset? {:chat_id "fake-chat-id" :text "change"} [body]))))