diff options
Diffstat (limited to 'src/main.lisp')
| -rw-r--r-- | src/main.lisp | 126 |
1 files changed, 58 insertions, 68 deletions
diff --git a/src/main.lisp b/src/main.lisp index caef651..fa7fab0 100644 --- a/src/main.lisp +++ b/src/main.lisp | |||
| @@ -8,10 +8,10 @@ | |||
| 8 | (:import-from :conf) | 8 | (:import-from :conf) |
| 9 | (:import-from :log) | 9 | (:import-from :log) |
| 10 | (:import-from :serapeum :drop) | 10 | (:import-from :serapeum :drop) |
| 11 | (:import-from :state :*state* :make-state) | ||
| 11 | (:import-from :str) | 12 | (:import-from :str) |
| 12 | (:import-from :ukkoclot/src/db :with-db) | 13 | (:import-from :ukkoclot/src/db :with-db) |
| 13 | (:import-from :ukkoclot/src/serializing :fixup-value) | 14 | (:import-from :ukkoclot/src/serializing :fixup-value) |
| 14 | (:import-from :ukkoclot/src/state :make-bot :bot-power-on) | ||
| 15 | (:import-from :ukkoclot/src/strings :escape-xml :is-tg-whitespace :is-tg-whitespace-str) | 15 | (:import-from :ukkoclot/src/strings :escape-xml :is-tg-whitespace :is-tg-whitespace-str) |
| 16 | (:local-nicknames | 16 | (:local-nicknames |
| 17 | (:jzon :com.inuoe.jzon)) | 17 | (:jzon :com.inuoe.jzon)) |
| @@ -26,7 +26,7 @@ | |||
| 26 | `(cond | 26 | `(cond |
| 27 | (*in-prod* | 27 | (*in-prod* |
| 28 | (handler-case (progn ,@body) ; lint:suppress redundant-progn | 28 | (handler-case (progn ,@body) ; lint:suppress redundant-progn |
| 29 | (error (err) (report-error bot ,evt err)))) | 29 | (error (err) (report-error ,evt err)))) |
| 30 | (t ,@body))) | 30 | (t ,@body))) |
| 31 | 31 | ||
| 32 | (defun main () | 32 | (defun main () |
| @@ -43,87 +43,82 @@ | |||
| 43 | (defun main-with-config () | 43 | (defun main-with-config () |
| 44 | (unwind-protect | 44 | (unwind-protect |
| 45 | (with-db (db (conf:db-path)) | 45 | (with-db (db (conf:db-path)) |
| 46 | (let ((bot (make-bot db))) | 46 | (setf *state* (make-state db)) |
| 47 | ;; TODO: Catch fatal errors & report them | 47 | ;; TODO: Catch fatal errors & report them |
| 48 | (wrapped-main bot))) | 48 | (wrapped-main)) |
| 49 | (log:info "We're done!"))) | 49 | (log:info "We're done!"))) |
| 50 | 50 | ||
| 51 | (defun wrapped-main (bot) | 51 | (defun wrapped-main () |
| 52 | (when *in-prod* | 52 | (when *in-prod* |
| 53 | (send-message bot :chat-id (conf:dev-group) :text "Initializing...")) | 53 | (send-message :chat-id (conf:dev-group) :text "Initializing...")) |
| 54 | (set-my-name bot :name (conf:bot-name)) | 54 | (set-my-name :name (conf:bot-name)) |
| 55 | (let ((gup-offset 0)) | 55 | (let ((gup-offset 0)) |
| 56 | (loop while (bot-power-on bot) do | 56 | (loop while (state:power-on) do |
| 57 | (let ((updates (get-updates bot :timeout 60 :offset gup-offset))) | 57 | (let ((updates (get-updates :timeout 60 :offset gup-offset))) |
| 58 | (loop for update across updates do | 58 | (loop for update across updates do |
| 59 | (unwind-protect | 59 | (unwind-protect |
| 60 | (progn | 60 | (progn |
| 61 | (when-let (msg (update-message update)) | 61 | (when-let (msg (update-message update)) |
| 62 | (reporty (msg) | 62 | (reporty (msg) |
| 63 | (on-message bot msg))) | 63 | (on-message msg))) |
| 64 | (when-let (cbq (update-callback-query update)) | 64 | (when-let (cbq (update-callback-query update)) |
| 65 | (reporty (cbq) | 65 | (reporty (cbq) |
| 66 | (on-callback-query bot cbq)))) | 66 | (on-callback-query cbq)))) |
| 67 | (setf gup-offset (1+ (update-update-id update))))))) | 67 | (setf gup-offset (1+ (update-update-id update))))))) |
| 68 | ;; One last getUpdates to make sure offset is stored on server | 68 | ;; One last getUpdates to make sure offset is stored on server |
| 69 | (get-updates bot :timeout 0 :limit 1 :offset gup-offset)) | 69 | (get-updates :timeout 0 :limit 1 :offset gup-offset)) |
| 70 | (send-message bot :chat-id (conf:dev-group) :text "Shutting down...")) | 70 | (send-message :chat-id (conf:dev-group) :text "Shutting down...")) |
| 71 | 71 | ||
| 72 | (defun on-callback-query (bot cb) | 72 | (defun on-callback-query (cb) |
| 73 | (let ((data (callback-query-data cb))) | 73 | (let ((data (callback-query-data cb))) |
| 74 | (cond ((and data | 74 | (cond ((and data |
| 75 | (str:starts-with-p "bbl:" data :ignore-case nil) | 75 | (str:starts-with-p "bbl:" data :ignore-case nil) |
| 76 | (= (user-id (callback-query-from cb)) | 76 | (= (user-id (callback-query-from cb)) |
| 77 | (conf:owner))) | 77 | (conf:owner))) |
| 78 | (let ((bot-id (read-from-string data t nil :start 4))) | 78 | (let ((bot-id (read-from-string data t nil :start 4))) |
| 79 | (blacklist-inline-bot bot bot-id)) | 79 | (blacklist-inline-bot bot-id)) |
| 80 | (when-let (msg (callback-query-message cb)) | 80 | (when-let (msg (callback-query-message cb)) |
| 81 | (delete-message bot | 81 | (delete-message :chat-id (message-chat-id msg) |
| 82 | :chat-id (message-chat-id msg) | ||
| 83 | :message-id (message-id msg))) | 82 | :message-id (message-id msg))) |
| 84 | (answer-callback-query bot | 83 | (answer-callback-query :callback-query-id (callback-query-id cb) |
| 85 | :callback-query-id (callback-query-id cb) | ||
| 86 | :text "OK")) | 84 | :text "OK")) |
| 87 | ((and data | 85 | ((and data |
| 88 | (str:starts-with-p "bwl:" data :ignore-case nil) | 86 | (str:starts-with-p "bwl:" data :ignore-case nil) |
| 89 | (= (user-id (callback-query-from cb)) | 87 | (= (user-id (callback-query-from cb)) |
| 90 | (conf:owner))) | 88 | (conf:owner))) |
| 91 | (let ((bot-id (read-from-string data t nil :start 4))) | 89 | (let ((bot-id (read-from-string data t nil :start 4))) |
| 92 | (whitelist-inline-bot bot bot-id)) | 90 | (whitelist-inline-bot bot-id)) |
| 93 | (when-let (msg (callback-query-message cb)) | 91 | (when-let (msg (callback-query-message cb)) |
| 94 | (delete-message bot | 92 | (delete-message :chat-id (message-chat-id msg) |
| 95 | :chat-id (message-chat-id msg) | ||
| 96 | :message-id (message-id msg))) | 93 | :message-id (message-id msg))) |
| 97 | (answer-callback-query bot | 94 | (answer-callback-query :callback-query-id (callback-query-id cb) |
| 98 | :callback-query-id (callback-query-id cb) | ||
| 99 | :text "OK")) | 95 | :text "OK")) |
| 100 | (t | 96 | (t |
| 101 | (log:info "Unrecognised callback query data: ~A" data) | 97 | (log:info "Unrecognised callback query data: ~A" data) |
| 102 | (answer-callback-query bot | 98 | (answer-callback-query :callback-query-id (callback-query-id cb) |
| 103 | :callback-query-id (callback-query-id cb) | ||
| 104 | :text "Unallowed callback query, don't press the button again" | 99 | :text "Unallowed callback query, don't press the button again" |
| 105 | :show-alert t))))) | 100 | :show-alert t))))) |
| 106 | 101 | ||
| 107 | 102 | ||
| 108 | (defun on-message (bot msg) | 103 | (defun on-message (msg) |
| 109 | (block nil | 104 | (block nil |
| 110 | (when-let (inline-bot (message-via-bot msg)) | 105 | (when-let (inline-bot (message-via-bot msg)) |
| 111 | (unless (on-inline-bot bot msg inline-bot) | 106 | (unless (on-inline-bot msg inline-bot) |
| 112 | (return))) | 107 | (return))) |
| 113 | 108 | ||
| 114 | (when-let (text (message-text msg)) | 109 | (when-let (text (message-text msg)) |
| 115 | (on-text-message bot msg text)) | 110 | (on-text-message msg text)) |
| 116 | 111 | ||
| 117 | (when-let (new-chat-members (message-new-chat-members msg)) | 112 | (when-let (new-chat-members (message-new-chat-members msg)) |
| 118 | (iter | 113 | (iter |
| 119 | (for new-chat-member in-vector new-chat-members) | 114 | (for new-chat-member in-vector new-chat-members) |
| 120 | (on-new-member bot msg new-chat-member))))) | 115 | (on-new-member msg new-chat-member))))) |
| 121 | 116 | ||
| 122 | (defun on-new-member (bot msg new-member) | 117 | (defun on-new-member (msg new-member) |
| 123 | (if (= (user-id new-member) (bot-id bot)) | 118 | (if (= (user-id new-member) (bot-id)) |
| 124 | (reply-animation bot msg #P"blob/rule-11.mp4" | 119 | (reply-animation msg #P"blob/rule-11.mp4" |
| 125 | :allow-sending-without-reply t) | 120 | :allow-sending-without-reply t) |
| 126 | (reply-animation bot msg #P"blob/rule-10.mp4" | 121 | (reply-animation msg #P"blob/rule-10.mp4" |
| 127 | :text (concatenate 'string "Hello there, " | 122 | :text (concatenate 'string "Hello there, " |
| 128 | (user-format-name new-member) | 123 | (user-format-name new-member) |
| 129 | "! Be on your bestest behaviour now!!") | 124 | "! Be on your bestest behaviour now!!") |
| @@ -136,7 +131,7 @@ | |||
| 136 | ;; TODO: | 131 | ;; TODO: |
| 137 | nil) | 132 | nil) |
| 138 | 133 | ||
| 139 | (defun on-text-message (bot msg text) | 134 | (defun on-text-message (msg text) |
| 140 | (block nil | 135 | (block nil |
| 141 | (when (is-bad-text text) | 136 | (when (is-bad-text text) |
| 142 | ;; TODO: Delete message, mute & warn user | 137 | ;; TODO: Delete message, mute & warn user |
| @@ -155,19 +150,19 @@ | |||
| 155 | (for entity in-vector entities) | 150 | (for entity in-vector entities) |
| 156 | (when (and (eql (message-entity-type entity) bot-command) | 151 | (when (and (eql (message-entity-type entity) bot-command) |
| 157 | (zerop (message-entity-offset entity))) | 152 | (zerop (message-entity-offset entity))) |
| 158 | (on-text-command bot msg text (message-entity-extract entity text))))) | 153 | (on-text-command msg text (message-entity-extract entity text))))) |
| 159 | 154 | ||
| 160 | ;; TODO: Replace this cond with a nicer dispatch. Something like string-case? | 155 | ;; TODO: Replace this cond with a nicer dispatch. Something like string-case? |
| 161 | (cond ((equal text ":3") | 156 | (cond ((equal text ":3") |
| 162 | (reply-message bot msg ">:3")) | 157 | (reply-message msg ">:3")) |
| 163 | 158 | ||
| 164 | ((equal text ">:3") | 159 | ((equal text ">:3") |
| 165 | (reply-message bot msg "<b>>:3</b>" :parse-mode html)) | 160 | (reply-message msg "<b>>:3</b>" :parse-mode html)) |
| 166 | 161 | ||
| 167 | ((str:starts-with-p "big " text) | 162 | ((str:starts-with-p "big " text) |
| 168 | (let ((the-text (drop 4 text))) | 163 | (let ((the-text (drop 4 text))) |
| 169 | (unless (is-tg-whitespace-str the-text) | 164 | (unless (is-tg-whitespace-str the-text) |
| 170 | (reply-message bot msg | 165 | (reply-message msg |
| 171 | (concatenate 'string | 166 | (concatenate 'string |
| 172 | "<b>" | 167 | "<b>" |
| 173 | (escape-xml (string-upcase the-text)) | 168 | (escape-xml (string-upcase the-text)) |
| @@ -175,33 +170,31 @@ | |||
| 175 | :parse-mode html)))) | 170 | :parse-mode html)))) |
| 176 | 171 | ||
| 177 | ((string-equal text "dio cane") | 172 | ((string-equal text "dio cane") |
| 178 | (reply-message bot | 173 | (reply-message (or (message-reply-to-message msg) msg) |
| 179 | (or (message-reply-to-message msg) msg) | ||
| 180 | "porco dio")) | 174 | "porco dio")) |
| 181 | 175 | ||
| 182 | ((string-equal text "forgor") | 176 | ((string-equal text "forgor") |
| 183 | (reply-message bot msg "💀")) | 177 | (reply-message msg "💀")) |
| 184 | 178 | ||
| 185 | ((string-equal text "huh") | 179 | ((string-equal text "huh") |
| 186 | (reply-message bot msg "idgi")) | 180 | (reply-message msg "idgi")) |
| 187 | 181 | ||
| 188 | ((string= text "H") | 182 | ((string= text "H") |
| 189 | (reply-message bot msg "<code>Randomly selected reminder that h > H.</code>" :parse-mode html)) | 183 | (reply-message msg "<code>Randomly selected reminder that h > H.</code>" :parse-mode html)) |
| 190 | 184 | ||
| 191 | ((string-equal text "porco dio") | 185 | ((string-equal text "porco dio") |
| 192 | (reply-message bot | 186 | (reply-message (or (message-reply-to-message msg) msg) |
| 193 | (or (message-reply-to-message msg) msg) | ||
| 194 | "dio cane")) | 187 | "dio cane")) |
| 195 | 188 | ||
| 196 | ((str:starts-with-p "say " text) | 189 | ((str:starts-with-p "say " text) |
| 197 | (let ((the-text (drop 4 text))) | 190 | (let ((the-text (drop 4 text))) |
| 198 | (unless (is-tg-whitespace-str the-text) | 191 | (unless (is-tg-whitespace-str the-text) |
| 199 | (reply-message bot msg the-text)))) | 192 | (reply-message msg the-text)))) |
| 200 | 193 | ||
| 201 | ((str:starts-with-p "tiny " text) | 194 | ((str:starts-with-p "tiny " text) |
| 202 | (let ((the-text (drop 5 text))) | 195 | (let ((the-text (drop 5 text))) |
| 203 | (unless (is-tg-whitespace-str the-text) | 196 | (unless (is-tg-whitespace-str the-text) |
| 204 | (reply-message bot msg | 197 | (reply-message msg |
| 205 | (map 'string #'(lambda (ch) | 198 | (map 'string #'(lambda (ch) |
| 206 | (if (is-tg-whitespace ch) | 199 | (if (is-tg-whitespace ch) |
| 207 | ch | 200 | ch |
| @@ -209,16 +202,15 @@ | |||
| 209 | the-text))))) | 202 | the-text))))) |
| 210 | 203 | ||
| 211 | ((string-equal text "uwu") | 204 | ((string-equal text "uwu") |
| 212 | (reply-message bot msg "OwO")) | 205 | (reply-message msg "OwO")) |
| 213 | 206 | ||
| 214 | ((string-equal text "waow") | 207 | ((string-equal text "waow") |
| 215 | (reply-message | 208 | (reply-message |
| 216 | bot | ||
| 217 | (or (message-reply-to-message msg) msg) | 209 | (or (message-reply-to-message msg) msg) |
| 218 | "BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED")) | 210 | "BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED BASED")) |
| 219 | 211 | ||
| 220 | ((string-equal text "what") | 212 | ((string-equal text "what") |
| 221 | (reply-message bot msg | 213 | (reply-message msg |
| 222 | (with-output-to-string (s) | 214 | (with-output-to-string (s) |
| 223 | (if (char= (elt text 0) #\w) | 215 | (if (char= (elt text 0) #\w) |
| 224 | (write-char #\g s) | 216 | (write-char #\g s) |
| @@ -235,55 +227,53 @@ | |||
| 235 | 227 | ||
| 236 | (t nil)))) | 228 | (t nil)))) |
| 237 | 229 | ||
| 238 | (defun simplify-cmd (bot cmd) | 230 | (defun simplify-cmd (cmd) |
| 239 | (let ((at-idx (position #\@ cmd))) | 231 | (let ((at-idx (position #\@ cmd))) |
| 240 | (if (null at-idx) | 232 | (if (null at-idx) |
| 241 | (drop 1 cmd) | 233 | (drop 1 cmd) |
| 242 | (let ((username (drop (1+ at-idx) cmd)) | 234 | (let ((username (drop (1+ at-idx) cmd)) |
| 243 | (my-username (bot-username bot))) | 235 | (my-username (bot-username))) |
| 244 | (if (equal username my-username) | 236 | (if (equal username my-username) |
| 245 | (subseq cmd 1 at-idx) | 237 | (subseq cmd 1 at-idx) |
| 246 | nil))))) | 238 | nil))))) |
| 247 | 239 | ||
| 248 | (defun on-text-command (bot msg text cmd) | 240 | (defun on-text-command (msg text cmd) |
| 249 | (declare (ignore text)) | 241 | (declare (ignore text)) |
| 250 | (let ((simple-cmd (simplify-cmd bot cmd))) | 242 | (let ((simple-cmd (simplify-cmd cmd))) |
| 251 | (log:debug "text-command: ~A AKA ~A" cmd simple-cmd) | 243 | (log:debug "text-command: ~A AKA ~A" cmd simple-cmd) |
| 252 | ;; TODO: Replace this cond with a nicer dispatch. Something like string-case? | 244 | ;; TODO: Replace this cond with a nicer dispatch. Something like string-case? |
| 253 | (cond | 245 | (cond |
| 254 | ((equal simple-cmd "chatid") | 246 | ((equal simple-cmd "chatid") |
| 255 | (reply-message bot msg | 247 | (reply-message msg |
| 256 | #f"<code>{(message-chat-id msg)}</code>" | 248 | #f"<code>{(message-chat-id msg)}</code>" |
| 257 | :parse-mode html)) | 249 | :parse-mode html)) |
| 258 | 250 | ||
| 259 | ((equal simple-cmd "msginfo") | 251 | ((equal simple-cmd "msginfo") |
| 260 | (when-let (replied (message-reply-to-message msg)) | 252 | (when-let (replied (message-reply-to-message msg)) |
| 261 | (reply-message bot replied (let ((*print-pretty* t)) (fixup-value replied))))) | 253 | (reply-message replied (let ((*print-pretty* t)) (fixup-value replied))))) |
| 262 | 254 | ||
| 263 | ((equal simple-cmd "ping") | 255 | ((equal simple-cmd "ping") |
| 264 | (let* ((start-time (get-internal-real-time)) | 256 | (let* ((start-time (get-internal-real-time)) |
| 265 | (reply (reply-message bot msg #f"Pong!{;~2%}Send time: ...")) | 257 | (reply (reply-message msg #f"Pong!{;~2%}Send time: ...")) |
| 266 | (end-time (get-internal-real-time)) | 258 | (end-time (get-internal-real-time)) |
| 267 | (time-elapsed (* (/ (- end-time start-time) internal-time-units-per-second) | 259 | (time-elapsed (* (/ (- end-time start-time) internal-time-units-per-second) |
| 268 | 1000))) | 260 | 1000))) |
| 269 | (edit-message-text bot | 261 | (edit-message-text :chat-id (message-chat-id reply) |
| 270 | :chat-id (message-chat-id reply) | ||
| 271 | :message-id (message-id reply) | 262 | :message-id (message-id reply) |
| 272 | :text #f"Pong!{;~2%}Send time: {time-elapsed;~G}ms"))) | 263 | :text #f"Pong!{;~2%}Send time: {time-elapsed;~G}ms"))) |
| 273 | 264 | ||
| 274 | ((and (equal simple-cmd "shutdown") | 265 | ((and (equal simple-cmd "shutdown") |
| 275 | (message-from msg) | 266 | (message-from msg) |
| 276 | (= (user-id (message-from msg)) (conf:owner))) | 267 | (= (user-id (message-from msg)) (conf:owner))) |
| 277 | (setf (bot-power-on bot) nil) | 268 | (setf (state:power-on) nil) |
| 278 | (reply-message bot msg "Initialising shutdown..." :allow-sending-without-reply t))))) | 269 | (reply-message msg "Initialising shutdown..." :allow-sending-without-reply t))))) |
| 279 | 270 | ||
| 280 | (defun escape-xml-obj (obj) | 271 | (defun escape-xml-obj (obj) |
| 281 | (escape-xml #f"{obj}")) | 272 | (escape-xml #f"{obj}")) |
| 282 | 273 | ||
| 283 | (defun report-error (bot evt err) | 274 | (defun report-error (evt err) |
| 284 | (log:error "While handling ~A: ~A" evt err) | 275 | (log:error "While handling ~A: ~A" evt err) |
| 285 | (let ((msg #f"<code>{(escape-xml-obj err)}</code> while handling{;~%}<pre>{(escape-xml-obj evt)}</pre>")) | 276 | (let ((msg #f"<code>{(escape-xml-obj err)}</code> while handling{;~%}<pre>{(escape-xml-obj evt)}</pre>")) |
| 286 | (send-message bot | 277 | (send-message :chat-id (conf:dev-group) |
| 287 | :chat-id (conf:dev-group) | 278 | :text msg |
| 288 | :text msg | 279 | :parse-mode html))) |
| 289 | :parse-mode html))) | ||