summaryrefslogtreecommitdiff
path: root/src/bot/impl.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bot/impl.lisp')
-rw-r--r--src/bot/impl.lisp91
1 files changed, 54 insertions, 37 deletions
diff --git a/src/bot/impl.lisp b/src/bot/impl.lisp
index b57e2d3..57a9572 100644
--- a/src/bot/impl.lisp
+++ b/src/bot/impl.lisp
@@ -8,41 +8,62 @@
8 (:local-nicknames 8 (:local-nicknames
9 (:jzon :com.inuoe.jzon)) 9 (:jzon :com.inuoe.jzon))
10 (:export 10 (:export
11 :arg-encode :bot :bot-p :make-bot :do-call 11 :bot :bot-p :make-bot :fixup-value :do-call :parse-value
12 12
13 :bot-config :bot-db :bot-base-uri :bot-power-on :bot-username% :bot-id%)) 13 :bot-config :bot-db :bot-base-uri :bot-power-on :bot-username% :bot-id%))
14(in-package :ukkoclot/bot/impl) 14(in-package :ukkoclot/bot/impl)
15 15
16(defgeneric will-arg-encode (object) 16(defgeneric parse-value (type json)
17 (:documentation "Whether the OBJECT has any transformation done to it by arg-encode") 17 (:documentation "Parse value of TYPE from the parsed JSON")
18 (:method (obj) 18 (:method (type json)
19 nil) 19 (log-error "I don't know how to parse simple type ~A!" type)
20 (:method ((obj cons)) 20 (error "I don't know how to parse simple type ~A!" type))
21 (or (will-arg-encode (car obj)) 21 (:method ((type (eql 'boolean)) json)
22 (will-arg-encode (cdr obj))))) 22 (check-type json boolean)
23 json)
24 (:method ((type (eql 'integer)) json)
25 (check-type json integer)
26 json)
27 (:method ((type (eql 'null)) json)
28 (check-type json null)
29 json)
30 (:method ((type (eql 'string)) json)
31 (check-type json string)
32 json))
23 33
24(defgeneric arg-encode (object) 34(defun try-parse-value (type json)
25 (:documentation "Encode the OBJECT to a form that our HTTP code is happy about.") 35 (handler-case (values t (parse-value type json))
26 (:method (obj) 36 (error () (values nil nil))))
27 obj)
28 (:method ((obj cons))
29 (if (not (will-arg-encode obj))
30 obj
31 (cons (arg-encode (car obj))
32 (arg-encode (cdr obj))))))
33 37
34(defgeneric fixup-arg (value) 38(defmethod parse-value ((type cons) json)
35 (:documentation "Make sure Telegram & QURI & whatever like the arg") 39 (cond ((and (eq (car type) 'array)
40 (null (cddr type)))
41 (when json
42 (let ((element-type (cadr type)))
43 (iter (for element in-vector json)
44 (collect (parse-value element-type element) result-type vector)))))
45 ((eq (car type) 'or)
46 (iter (for el-type in (cdr type))
47 (multiple-value-bind (success res) (try-parse-value el-type json)
48 (when success
49 (return res)))
50 (finally
51 (error "Failed to parse ~S as ~A!" json type))))
52 (t
53 (error "I don't know how to parse complex type ~A!" type))))
54
55(defgeneric fixup-value (value)
56 (:documentation "Fixup top-level VALUE before passing it onto telegram")
36 (:method (value) 57 (:method (value)
37 (error "Unsupported argument type ~S (~S)" (class-name (class-of value)) value)) 58 (jzon:stringify value))
38 (:method ((value null)) 59 (:method ((value null))
39 value) 60 value)
40 (:method ((value number)) 61 (:method ((value number))
41 value) 62 value)
42 (:method ((value string)) 63 (:method ((value pathname))
43 value) 64 value)
44 (:method ((value hash-table)) 65 (:method ((value string))
45 (jzon:stringify value))) 66 value))
46 67
47(defstruct (bot (:constructor make-bot%)) 68(defstruct (bot (:constructor make-bot%))
48 (config (error "No value given for config") :read-only t) 69 (config (error "No value given for config") :read-only t)
@@ -58,39 +79,35 @@
58 (config-bot-token config) "/"))) 79 (config-bot-token config) "/")))
59 (make-bot% :config config :db db :base-uri base-uri))) 80 (make-bot% :config config :db db :base-uri base-uri)))
60 81
61(defun args-plist->alist (args-plist) 82(defun fixup-args (args)
62 (iter (for (old-key value) on args-plist by #'cddr) 83 (iter (for (key . value) in args)
63 (collect 84 (collect
64 (let ((new-key (string-downcase (lisp->snake-case (symbol-name old-key))))) 85 (cons (string-downcase (lisp->snake-case (symbol-name key)))
65 (cons new-key value))))) 86 (fixup-value value)))))
66
67(defun fixup-args (args-alist)
68 (iter (for (name . value) in args-alist)
69 (collecting (cons name (fixup-arg (arg-encode value))))))
70 87
71(defun req (uri method content) 88(defun req (uri method content)
72 ;; We deal with this manually 89 ;; We deal with this manually
73 (handler-bind ((dex:http-request-too-many-requests #'dex:ignore-and-continue)) 90 (handler-bind ((dex:http-request-too-many-requests #'dex:ignore-and-continue))
74 (dex:request uri :method method :content content))) 91 (dex:request uri :method method :content content)))
75 92
76(defun do-call% (bot method uri mapfn args-encoded) 93(defun do-call% (bot method uri type args-encoded)
77 (let ((body (req uri method args-encoded))) 94 (let ((body (req uri method args-encoded)))
78 (let ((hash (jzon:parse body))) 95 (let ((hash (jzon:parse body)))
79 (acond 96 (acond
80 ((gethash "ok" hash) (funcall mapfn (gethash "result" hash))) 97 ((gethash "ok" hash) (parse-value type (gethash "result" hash)))
81 ((aand (gethash "parameters" hash) 98 ((aand (gethash "parameters" hash)
82 (gethash "retry_after" it)) 99 (gethash "retry_after" it))
83 (log-info "Should sleep for ~A seconds" it) 100 (log-info "Should sleep for ~A seconds" it)
84 (sleep it) 101 (sleep it)
85 (log-info "Good morning!") 102 (log-info "Good morning!")
86 (do-call% bot method uri mapfn args-encoded)) 103 (do-call% bot method uri type args-encoded))
87 (t (error "TG error ~A: ~A ~:A" 104 (t (error "TG error ~A: ~A ~:A"
88 (gethash "error_code" hash) 105 (gethash "error_code" hash)
89 (gethash "description" hash) 106 (gethash "description" hash)
90 (gethash "parameters" hash))))))) 107 (gethash "parameters" hash)))))))
91 108
92(defun do-call (bot method path mapfn args-plist) 109(defun do-call (bot method path type args)
93 (let ((uri (concatenate 'string (bot-base-uri bot) path)) 110 (let ((uri (concatenate 'string (bot-base-uri bot) path))
94 (args-encoded (fixup-args (args-plist->alist args-plist)))) 111 (args-encoded (fixup-args args)))
95 (log-debug "~A .../~A ~S" method path args-encoded) 112 (log-debug "~A .../~A ~S" method path args-encoded)
96 (do-call% bot method uri mapfn args-encoded))) 113 (do-call% bot method uri type args-encoded)))