summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSES/LicenseRef-RandomGIFs.txt2
-rw-r--r--REUSE.toml6
-rw-r--r--blob/do-not.mp4bin0 -> 58316 bytes
-rw-r--r--blob/rule-10.mp4bin0 -> 122589 bytes
-rw-r--r--blob/rule-11.mp4bin0 -> 136999 bytes
-rw-r--r--src/bot/advanced.lisp27
-rw-r--r--src/bot/impl.lisp91
-rw-r--r--src/bot/methods.lisp46
-rw-r--r--src/inline-bots.lisp31
-rw-r--r--src/main.lisp26
-rw-r--r--src/tg-types.lisp10
-rw-r--r--src/tg-types/bot-name.lisp10
-rw-r--r--src/tg-types/callback-query.lisp17
-rw-r--r--src/tg-types/chat-administrator-rights.lisp44
-rw-r--r--src/tg-types/chat.lisp31
-rw-r--r--src/tg-types/force-reply.lisp17
-rw-r--r--src/tg-types/inline-keyboard-button.lisp28
-rw-r--r--src/tg-types/inline-keyboard-markup.lisp13
-rw-r--r--src/tg-types/keyboard-button-poll-type.lisp14
-rw-r--r--src/tg-types/keyboard-button-request-chat.lisp32
-rw-r--r--src/tg-types/keyboard-button-request-users.lisp24
-rw-r--r--src/tg-types/keyboard-button.lisp32
-rw-r--r--src/tg-types/link-preview-options.lisp21
-rw-r--r--src/tg-types/macros.lisp82
-rw-r--r--src/tg-types/message-entity.lisp30
-rw-r--r--src/tg-types/message.lisp104
-rw-r--r--src/tg-types/parsers.lisp9
-rw-r--r--src/tg-types/reply-keyboard-markup.lisp24
-rw-r--r--src/tg-types/reply-keyboard-remove.lisp16
-rw-r--r--src/tg-types/reply-parameters.lisp27
-rw-r--r--src/tg-types/suggested-post-parameters.lisp16
-rw-r--r--src/tg-types/suggested-post-price.lisp16
-rw-r--r--src/tg-types/update.lisp39
-rw-r--r--src/tg-types/user.lisp23
-rw-r--r--src/tg-types/web-app-info.lisp14
35 files changed, 613 insertions, 309 deletions
diff --git a/LICENSES/LicenseRef-RandomGIFs.txt b/LICENSES/LicenseRef-RandomGIFs.txt
new file mode 100644
index 0000000..408378d
--- /dev/null
+++ b/LICENSES/LicenseRef-RandomGIFs.txt
@@ -0,0 +1,2 @@
1These are random gifs I've gathered on Telegram. I don't own them, and who the fuck knows who made them, the videos
2could probably be sourced if needed. These are used under the implied "meme license": just don't make money off of it.
diff --git a/REUSE.toml b/REUSE.toml
index 5798adc..f87ab00 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -12,3 +12,9 @@ version = 1
12 path = ["README.md"] 12 path = ["README.md"]
13 SPDX-License-Identifier = "EUPL-1.2" 13 SPDX-License-Identifier = "EUPL-1.2"
14 SPDX-FileCopyrightText = " 2025 Uko Kokņevičs <perkontevs@gmail.com>" 14 SPDX-FileCopyrightText = " 2025 Uko Kokņevičs <perkontevs@gmail.com>"
15
16[[annotations]]
17 label = "Random \"gifs\""
18 path = ["blob/do-not.mp4", "blob/rule-10.mp4", "blob/rule-11.mp4"]
19 SPDX-License-Identifier = "LicenseRef-RandomGIFs"
20 SPDX-FileCopyrightText = "Unknown"
diff --git a/blob/do-not.mp4 b/blob/do-not.mp4
new file mode 100644
index 0000000..408e627
--- /dev/null
+++ b/blob/do-not.mp4
Binary files differ
diff --git a/blob/rule-10.mp4 b/blob/rule-10.mp4
new file mode 100644
index 0000000..3dcc60e
--- /dev/null
+++ b/blob/rule-10.mp4
Binary files differ
diff --git a/blob/rule-11.mp4 b/blob/rule-11.mp4
new file mode 100644
index 0000000..ec90c87
--- /dev/null
+++ b/blob/rule-11.mp4
Binary files differ
diff --git a/src/bot/advanced.lisp b/src/bot/advanced.lisp
index a6ad9ba..241b04d 100644
--- a/src/bot/advanced.lisp
+++ b/src/bot/advanced.lisp
@@ -2,7 +2,7 @@
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> 2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/bot/advanced 3(defpackage :ukkoclot/bot/advanced
4 (:use :c2cl :ukkoclot/bot/impl :ukkoclot/bot/methods :ukkoclot/tg-types) 4 (:use :c2cl :ukkoclot/bot/impl :ukkoclot/bot/methods :ukkoclot/tg-types)
5 (:export :bot-id :bot-username :reply-message)) 5 (:export :bot-id :bot-username :reply-animation :reply-message :try-delete-message))
6(in-package :ukkoclot/bot/advanced) 6(in-package :ukkoclot/bot/advanced)
7 7
8(defun bot-id (bot) 8(defun bot-id (bot)
@@ -17,6 +17,20 @@
17 (get-me bot) 17 (get-me bot)
18 (bot-username% bot)))) 18 (bot-username% bot))))
19 19
20;; TODO: Some kind of caching for files?
21(defun reply-animation (bot msg animation &key allow-sending-without-reply text parse-mode caption-above)
22 (send-animation bot
23 :chat-id (message-chat-id msg)
24 :animation animation
25 :caption text
26 :parse-mode parse-mode
27 :show-caption-above-media caption-above
28 :reply-parameters
29 (make-reply-parameters
30 :allow-sending-without-reply allow-sending-without-reply
31 :message-id (message-id msg)
32 :chat-id (message-chat-id msg))))
33
20(defun reply-message (bot msg text &key parse-mode allow-sending-without-reply) 34(defun reply-message (bot msg text &key parse-mode allow-sending-without-reply)
21 (send-message bot 35 (send-message bot
22 :chat-id (message-chat-id msg) 36 :chat-id (message-chat-id msg)
@@ -27,3 +41,14 @@
27 :allow-sending-without-reply allow-sending-without-reply 41 :allow-sending-without-reply allow-sending-without-reply
28 :message-id (message-id msg) 42 :message-id (message-id msg)
29 :chat-id (message-chat-id msg)))) 43 :chat-id (message-chat-id msg))))
44
45(defun try-delete-message (bot msg)
46 (handler-case
47 (delete-message bot
48 :chat-id (message-chat-id msg)
49 :message-id (message-id msg))
50 (error ()
51 (handler-case
52 (reply-animation bot msg #P"blob/do-not.mp4"
53 :allow-sending-without-reply nil)
54 (error () nil)))))
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)))
diff --git a/src/bot/methods.lisp b/src/bot/methods.lisp
index 99b6411..7ccc4ad 100644
--- a/src/bot/methods.lisp
+++ b/src/bot/methods.lisp
@@ -2,22 +2,22 @@
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> 2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/bot/methods 3(defpackage :ukkoclot/bot/methods
4 (:use :c2cl :ukkoclot/bot/impl :ukkoclot/tg-types :ukkoclot/tg-types/macros) 4 (:use :c2cl :ukkoclot/bot/impl :ukkoclot/tg-types :ukkoclot/tg-types/macros)
5 (:export :answer-callback-query :delete-message :edit-message-text :get-me :get-updates :send-message :set-my-name)) 5 (:export :answer-callback-query :delete-message :send-animation :edit-message-text :get-me :get-updates :send-message :set-my-name))
6(in-package :ukkoclot/bot/methods) 6(in-package :ukkoclot/bot/methods)
7 7
8(define-tg-method (answer-callback-query boolean "answerCallbackQuery" #'identity) 8(define-tg-method (answer-callback-query boolean "answerCallbackQuery")
9 (callback-query-id string) 9 (callback-query-id string)
10 (text (or string null) nil) 10 (text (or string null) nil)
11 (show-alert boolean nil) 11 (show-alert boolean nil)
12 (url (or string null) nil) 12 (url (or string null) nil)
13 (cache-time (or integer null) nil)) 13 (cache-time (or integer null) nil))
14 14
15(define-tg-method (delete-message boolean "deleteMessage" #'identity) 15(define-tg-method (delete-message boolean "deleteMessage")
16 (chat-id (or integer string)) 16 (chat-id (or integer string))
17 (message-id integer)) 17 (message-id integer))
18 18
19;; TODO: Add a way to simply specify :message msg :) 19;; TODO: Add a way to simply specify :message msg :)
20(define-tg-method (edit-message-text message "editMessageText" #'hash->message) 20(define-tg-method (edit-message-text message "editMessageText")
21 (business-connection-id (or string null) nil) 21 (business-connection-id (or string null) nil)
22 (chat-id (or integer string null) nil) 22 (chat-id (or integer string null) nil)
23 (message-id (or integer null) nil) 23 (message-id (or integer null) nil)
@@ -28,7 +28,7 @@
28 (link-preview-options (or link-preview-options null) nil) 28 (link-preview-options (or link-preview-options null) nil)
29 (reply-markup (or inline-keyboard-markup null) nil)) 29 (reply-markup (or inline-keyboard-markup null) nil))
30 30
31(define-tg-method (get-me% user "getMe" #'hash->user :GET)) 31(define-tg-method (get-me% user "getMe" :GET))
32 32
33(defun get-me (bot) 33(defun get-me (bot)
34 (let ((res (get-me% bot))) 34 (let ((res (get-me% bot)))
@@ -36,16 +36,39 @@
36 (setf (bot-username% bot) (user-username res)) 36 (setf (bot-username% bot) (user-username res))
37 res)) 37 res))
38 38
39(define-tg-method (get-my-name bot-name "getMyName" #'hash->bot-name :GET) 39(define-tg-method (get-my-name bot-name "getMyName" :GET)
40 (language-code (or string null) nil)) 40 (language-code (or string null) nil))
41 41
42(define-tg-method (get-updates (array update) "getUpdates" #'parse-update-array) 42(define-tg-method (get-updates (array update) "getUpdates")
43 (offset (or integer null) nil) 43 (offset (or integer null) nil)
44 (limit (or integer null) nil) 44 (limit (or integer null) nil)
45 (timeout (or integer null) nil) 45 (timeout (or integer null) nil)
46 (allowed-updates (or string null) nil)) 46 (allowed-updates (or string null) nil))
47 47
48(define-tg-method (send-message message "sendMessage" #'hash->message) 48(define-tg-method (send-animation message "sendAnimation")
49 (business-connection-id (or string null) nil)
50 (chat-id (or integer string))
51 (message-thread-id (or integer null) nil)
52 (direct-messages-topic-id (or integer null) nil)
53 (animation (or pathname string))
54 (duration (or integer null) nil)
55 (width (or integer null) nil)
56 (height (or integer null) nil)
57 (thumbnail (or pathname string null) nil)
58 (caption (or string null) nil)
59 (parse-mode (or string null) nil)
60 (caption-entities (or (array message-entity) null) nil)
61 (show-caption-above-media boolean nil)
62 (has-spoiler boolean nil)
63 (disable-notification boolean nil)
64 (protect-content boolean nil)
65 (allow-paid-broadcast boolean nil)
66 (message-effect-id (or string null) nil)
67 (suggested-post-parameters (or suggested-post-parameters null) nil)
68 (reply-parameters (or reply-parameters null) nil)
69 (reply-markup (or inline-keyboard-markup reply-keyboard-markup reply-keyboard-remove force-reply null) nil))
70
71(define-tg-method (send-message message "sendMessage")
49 (business-connection-id (or string null) nil) 72 (business-connection-id (or string null) nil)
50 (chat-id (or integer string)) 73 (chat-id (or integer string))
51 (message-thread-id (or integer null) nil) 74 (message-thread-id (or integer null) nil)
@@ -58,12 +81,9 @@
58 (protect-content (or boolean null) nil) 81 (protect-content (or boolean null) nil)
59 (message-effect-id (or string null) nil) 82 (message-effect-id (or string null) nil)
60 (reply-parameters (or reply-parameters null) nil) 83 (reply-parameters (or reply-parameters null) nil)
61 (reply-markup (or inline-keyboard-markup 84 (reply-markup (or inline-keyboard-markup reply-keyboard-markup reply-keyboard-remove force-reply null) nil))
62 ;; TODO: reply-keyboard-markup
63 ;; TODO: reply-keyboard-remove
64 force-reply null) nil))
65 85
66(define-tg-method (set-my-name% boolean "setMyName" #'identity) 86(define-tg-method (set-my-name% boolean "setMyName")
67 (name (or string null) nil) 87 (name (or string null) nil)
68 (language-code (or string null) nil)) 88 (language-code (or string null) nil))
69 89
diff --git a/src/inline-bots.lisp b/src/inline-bots.lisp
index 5945084..eb20f21 100644
--- a/src/inline-bots.lisp
+++ b/src/inline-bots.lisp
@@ -20,23 +20,20 @@
20 (log-info "Deleting an unallowed inline bot message from ~A ~A" 20 (log-info "Deleting an unallowed inline bot message from ~A ~A"
21 (user-username via) 21 (user-username via)
22 (user-id via)) 22 (user-id via))
23 (delete-message bot 23 (try-delete-message bot msg)
24 :chat-id (message-chat-id msg)
25 :message-id (message-id msg))
26 (unless (eq ty :blacklisted) 24 (unless (eq ty :blacklisted)
27 ;; Not explicitly blacklisted, notify dev group 25 ;; Not explicitly blacklisted, notify dev group
28 (send-message bot 26 (let ((whitelist (make-inline-keyboard-button :text "Whitelist"
29 :chat-id (config-dev-group (bot-config bot)) 27 :callback-data (format nil "bwl:~A" (user-id via))))
30 :text (format nil "Deleted a message sent via inline bot @~A <code>~A</code>" 28 (blacklist (make-inline-keyboard-button :text "Blacklist"
31 (user-username via) 29 :callback-data (format nil "bbl:~A" (user-id via)))))
32 (user-id via)) 30 (send-message bot
33 :parse-mode "HTML" 31 :chat-id (config-dev-group (bot-config bot))
34 :reply-markup (make-inline-keyboard-markup 32 :text (format nil "Deleted a message sent via inline bot @~A <code>~A</code>"
35 :inline-keyboard 33 (user-username via)
36 #(#((make-inline-keyboard-button 34 (user-id via))
37 :text "Whitelist" 35 :parse-mode "HTML"
38 :callback-data (format nil "bwl:~A" (user-id via))) 36 :reply-markup (make-inline-keyboard-markup
39 (make-inline-keyboard-button 37 :inline-keyboard
40 :text "Blacklist" 38 (make-array '(1 2) :initial-contents (list (list whitelist blacklist)))))))
41 :callback-data (format nil "bbl:~A" (user-id via))))))))
42 nil)))) 39 nil))))
diff --git a/src/main.lisp b/src/main.lisp
index 419bb67..6d83ff4 100644
--- a/src/main.lisp
+++ b/src/main.lisp
@@ -4,7 +4,7 @@
4 (:nicknames :ukkoclot) 4 (:nicknames :ukkoclot)
5 (:use :c2cl :ukkoclot/bot :ukkoclot/config :ukkoclot/inline-bots :ukkoclot/log :ukkoclot/tg-types) 5 (:use :c2cl :ukkoclot/bot :ukkoclot/config :ukkoclot/inline-bots :ukkoclot/log :ukkoclot/tg-types)
6 (:import-from :anaphora :acond :awhen :it) 6 (:import-from :anaphora :acond :awhen :it)
7 (:import-from :ukkoclot/bot :make-bot :bot-power-on) 7 (:import-from :ukkoclot/bot :make-bot :bot-power-on :fixup-value)
8 (:import-from :ukkoclot/db :with-db) 8 (:import-from :ukkoclot/db :with-db)
9 (:import-from :ukkoclot/strings :escape-xml :is-tg-whitespace-str :starts-with :starts-with-ignore-case) 9 (:import-from :ukkoclot/strings :escape-xml :is-tg-whitespace-str :starts-with :starts-with-ignore-case)
10 (:local-nicknames 10 (:local-nicknames
@@ -32,7 +32,8 @@
32 (log-info "We're done!"))) 32 (log-info "We're done!")))
33 33
34(defun wrapped-main (bot config) 34(defun wrapped-main (bot config)
35 (send-message bot :chat-id (config-dev-group config) :text "Initializing...") 35 (when *in-prod*
36 (send-message bot :chat-id (config-dev-group config) :text "Initializing..."))
36 (set-my-name bot :name (config-bot-name config)) 37 (set-my-name bot :name (config-bot-name config))
37 (let ((gup-offset 0)) 38 (let ((gup-offset 0))
38 (loop while (bot-power-on bot) do 39 (loop while (bot-power-on bot) do
@@ -101,14 +102,17 @@
101 (on-new-member bot msg new-chat-member))))) 102 (on-new-member bot msg new-chat-member)))))
102 103
103(defun on-new-member (bot msg new-member) 104(defun on-new-member (bot msg new-member)
104 ;; TODO: Rule 11 no hating on cats on bot entry
105 ;; TODO: Rule 10 have fun and enjoy your time on user entry 105 ;; TODO: Rule 10 have fun and enjoy your time on user entry
106 (if (= (user-id new-member) (bot-id bot)) 106 (if (= (user-id new-member) (bot-id bot))
107 nil 107 (reply-animation bot msg #P"blob/rule-11.mp4"
108 (reply-message bot msg 108 :allow-sending-without-reply t)
109 (concatenate 'string "Hello there, " (user-format-name new-member) "! Be on your bestest behaviour now!!") 109 (reply-animation bot msg #P"blob/rule-10.mp4"
110 :parse-mode "HTML" 110 :text (concatenate 'string "Hello there, "
111 :allow-sending-without-reply t))) 111 (user-format-name new-member)
112 "! Be on your bestest behaviour now!!")
113 :parse-mode "HTML"
114 :caption-above t
115 :allow-sending-without-reply t)))
112 116
113(defun is-bad-text (text) 117(defun is-bad-text (text)
114 ;; TODO: 118 ;; TODO:
@@ -130,7 +134,7 @@
130 134
131 (awhen (message-entities msg) 135 (awhen (message-entities msg)
132 (loop for entity across it 136 (loop for entity across it
133 when (and (eq (message-entity-type entity) :bot-command) 137 when (and (equal (message-entity-type entity) "bot_command")
134 (= (message-entity-offset entity) 0)) 138 (= (message-entity-offset entity) 0))
135 do (on-text-command bot msg text (message-entity-extract entity text)))) 139 do (on-text-command bot msg text (message-entity-extract entity text))))
136 140
@@ -218,9 +222,7 @@
218 222
219 ((and (equal simple-cmd "msginfo") 223 ((and (equal simple-cmd "msginfo")
220 (message-reply-to-message msg)) 224 (message-reply-to-message msg))
221 (reply-message bot it 225 (reply-message bot it (fixup-value it)))
222 ;; TODO: Text needs lot more massaging lol
223 (jzon:stringify (arg-encode it))))
224 226
225 ((equal simple-cmd "ping") 227 ((equal simple-cmd "ping")
226 (let* ((start-time (get-internal-real-time)) 228 (let* ((start-time (get-internal-real-time))
diff --git a/src/tg-types.lisp b/src/tg-types.lisp
index 1243773..6a830b6 100644
--- a/src/tg-types.lisp
+++ b/src/tg-types.lisp
@@ -6,13 +6,23 @@
6 :ukkoclot/tg-types/bot-name 6 :ukkoclot/tg-types/bot-name
7 :ukkoclot/tg-types/callback-query 7 :ukkoclot/tg-types/callback-query
8 :ukkoclot/tg-types/chat 8 :ukkoclot/tg-types/chat
9 :ukkoclot/tg-types/chat-administrator-rights
9 :ukkoclot/tg-types/force-reply 10 :ukkoclot/tg-types/force-reply
10 :ukkoclot/tg-types/inline-keyboard-button 11 :ukkoclot/tg-types/inline-keyboard-button
11 :ukkoclot/tg-types/inline-keyboard-markup 12 :ukkoclot/tg-types/inline-keyboard-markup
13 :ukkoclot/tg-types/keyboard-button
14 :ukkoclot/tg-types/keyboard-button-poll-type
15 :ukkoclot/tg-types/keyboard-button-request-chat
16 :ukkoclot/tg-types/keyboard-button-request-users
12 :ukkoclot/tg-types/link-preview-options 17 :ukkoclot/tg-types/link-preview-options
13 :ukkoclot/tg-types/message 18 :ukkoclot/tg-types/message
14 :ukkoclot/tg-types/message-entity 19 :ukkoclot/tg-types/message-entity
20 :ukkoclot/tg-types/reply-keyboard-markup
21 :ukkoclot/tg-types/reply-keyboard-remove
15 :ukkoclot/tg-types/reply-parameters 22 :ukkoclot/tg-types/reply-parameters
23 :ukkoclot/tg-types/suggested-post-parameters
24 :ukkoclot/tg-types/suggested-post-price
16 :ukkoclot/tg-types/update 25 :ukkoclot/tg-types/update
17 :ukkoclot/tg-types/user 26 :ukkoclot/tg-types/user
27 :ukkoclot/tg-types/web-app-info
18 )) 28 ))
diff --git a/src/tg-types/bot-name.lisp b/src/tg-types/bot-name.lisp
index 385b91c..b42765c 100644
--- a/src/tg-types/bot-name.lisp
+++ b/src/tg-types/bot-name.lisp
@@ -3,11 +3,11 @@
3(defpackage :ukkoclot/tg-types/bot-name 3(defpackage :ukkoclot/tg-types/bot-name
4 (:use :c2cl :ukkoclot/tg-types/macros) 4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export 5 (:export
6 bot-name bot-name-p 6 #:bot-name
7 7 #:make-bot-name
8 hash->bot-name make-bot-name parse-bot-name-array 8 #:bot-name-p
9 9 #:copy-bot-name
10 bot-name-name)) 10 #:bot-name-name))
11(in-package :ukkoclot/tg-types/bot-name) 11(in-package :ukkoclot/tg-types/bot-name)
12 12
13(define-tg-type bot-name 13(define-tg-type bot-name
diff --git a/src/tg-types/callback-query.lisp b/src/tg-types/callback-query.lisp
index bb1b4e7..875ff25 100644
--- a/src/tg-types/callback-query.lisp
+++ b/src/tg-types/callback-query.lisp
@@ -6,12 +6,17 @@
6 :ukkoclot/tg-types/message 6 :ukkoclot/tg-types/message
7 :ukkoclot/tg-types/user) 7 :ukkoclot/tg-types/user)
8 (:export 8 (:export
9 callback-query callback-query-p 9 #:callback-query
10 10 #:make-callback-query
11 hash->callback-query make-callback-query parse-callback-query-array 11 #:callback-query-p
12 12 #:copy-callback-query
13 callback-query-id callback-query-from callback-query-message callback-query-inline-message-id 13 #:callback-query-id
14 callback-query-chat-instance callback-query-data callback-query-game-short-name)) 14 #:callback-query-from
15 #:callback-query-message
16 #:callback-query-inline-message-id
17 #:callback-query-chat-instance
18 #:callback-query-data
19 #:callback-query-game-short-name))
15(in-package :ukkoclot/tg-types/callback-query) 20(in-package :ukkoclot/tg-types/callback-query)
16 21
17(define-tg-type callback-query 22(define-tg-type callback-query
diff --git a/src/tg-types/chat-administrator-rights.lisp b/src/tg-types/chat-administrator-rights.lisp
new file mode 100644
index 0000000..37c1ce0
--- /dev/null
+++ b/src/tg-types/chat-administrator-rights.lisp
@@ -0,0 +1,44 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/chat-administrator-rights
4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export
6 #:chat-administrator-rights
7 #:make-chat-administrator-rights
8 #:chat-administrator-rights-p
9 #:copy-chat-administrator-rights
10 #:chat-administrator-rights-is-anonymous
11 #:chat-administrator-rights-can-manage-chat
12 #:chat-administrator-rights-can-delete-messages
13 #:chat-administrator-rights-can-manage-video-chats
14 #:chat-administrator-rights-can-restrict-members
15 #:chat-administrator-rights-can-promote-members
16 #:chat-administrator-rights-can-change-info
17 #:chat-administrator-rights-can-invite-users
18 #:chat-administrator-rights-can-post-stories
19 #:chat-administrator-rights-can-edit-stories
20 #:chat-administrator-rights-can-delete-stories
21 #:chat-administrator-rights-can-post-messages
22 #:chat-administrator-rights-can-edit-messages
23 #:chat-administrator-rights-can-pin-messages
24 #:chat-administrator-rights-can-manage-topics
25 #:chat-administrator-rights-can-manage-direct-messages))
26(in-package :ukkoclot/tg-types/chat-administrator-rights)
27
28(define-tg-type chat-administrator-rights
29 (is-anonymous boolean)
30 (can-manage-chat boolean)
31 (can-delete-messages boolean)
32 (can-manage-video-chats boolean)
33 (can-restrict-members boolean)
34 (can-promote-members boolean)
35 (can-change-info boolean)
36 (can-invite-users boolean)
37 (can-post-stories boolean)
38 (can-edit-stories boolean)
39 (can-delete-stories boolean)
40 (can-post-messages boolean nil)
41 (can-edit-messages boolean nil)
42 (can-pin-messages boolean nil)
43 (can-manage-topics boolean nil)
44 (can-manage-direct-messages boolean nil))
diff --git a/src/tg-types/chat.lisp b/src/tg-types/chat.lisp
index 4010f7b..3ad42ef 100644
--- a/src/tg-types/chat.lisp
+++ b/src/tg-types/chat.lisp
@@ -1,28 +1,25 @@
1;; SPDX-License-Identifier: EUPL-1.2 1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> 2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/chat 3(defpackage :ukkoclot/tg-types/chat
4 (:use :c2cl :ukkoclot/tg-types/macros :ukkoclot/tg-types/parsers) 4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export 5 (:export
6 chat 6 #:chat
7 make-chat 7 #:make-chat
8 chat-p 8 #:chat-p
9 copy-chat 9 #:copy-chat
10 chat-id 10 #:chat-id
11 chat-type 11 #:chat-type
12 chat-title 12 #:chat-title
13 chat-username 13 #:chat-username
14 chat-first-name 14 #:chat-first-name
15 chat-last-name 15 #:chat-last-name
16 chat-is-forum 16 #:chat-is-forum
17 chat-is-direct-messages 17 #:chat-is-direct-messages))
18
19 hash->chat
20 parse-chat-array))
21(in-package :ukkoclot/tg-types/chat) 18(in-package :ukkoclot/tg-types/chat)
22 19
23(define-tg-type chat 20(define-tg-type chat
24 (id integer) 21 (id integer)
25 (type keyword nil :parser tg-string->keyword) 22 (type string nil) ;TODO: member of keywords
26 (title (or string null) nil) 23 (title (or string null) nil)
27 (username (or string null) nil) 24 (username (or string null) nil)
28 (first-name (or string null) nil) 25 (first-name (or string null) nil)
diff --git a/src/tg-types/force-reply.lisp b/src/tg-types/force-reply.lisp
index ad9d2a0..5dc49fb 100644
--- a/src/tg-types/force-reply.lisp
+++ b/src/tg-types/force-reply.lisp
@@ -3,16 +3,13 @@
3(defpackage :ukkoclot/tg-types/force-reply 3(defpackage :ukkoclot/tg-types/force-reply
4 (:use :c2cl :ukkoclot/tg-types/macros) 4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export 5 (:export
6 force-reply 6 #:force-reply
7 make-force-reply 7 #:make-force-reply
8 force-reply-p 8 #:force-reply-p
9 copy-force-reply 9 #:copy-force-reply
10 force-reply-force-reply 10 #:force-reply-force-reply
11 force-reply-input-field-placeholder 11 #:force-reply-input-field-placeholder
12 force-reply-selective 12 #:force-reply-selective))
13
14 hash->force-reply
15 parse-force-reply-array))
16(in-package :ukkoclot/tg-types/force-reply) 13(in-package :ukkoclot/tg-types/force-reply)
17 14
18(define-tg-type force-reply 15(define-tg-type force-reply
diff --git a/src/tg-types/inline-keyboard-button.lisp b/src/tg-types/inline-keyboard-button.lisp
index 3b76ade..e3b3533 100644
--- a/src/tg-types/inline-keyboard-button.lisp
+++ b/src/tg-types/inline-keyboard-button.lisp
@@ -1,28 +1,26 @@
1;; SPDX-License-Identifier: EUPL-1.2 1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> 2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/inline-keyboard-button 3(defpackage :ukkoclot/tg-types/inline-keyboard-button
4 (:use :c2cl :ukkoclot/tg-types/macros) 4 (:use :c2cl :ukkoclot/tg-types/macros :ukkoclot/tg-types/web-app-info)
5 (:export 5 (:export
6 inline-keyboard-button 6 #:inline-keyboard-button
7 make-inline-keyboard-button 7 #:make-inline-keyboard-button
8 inline-keyboard-button-p 8 #:inline-keyboard-button-p
9 copy-inline-keyboard-button 9 #:copy-inline-keyboard-button
10 inline-keyboard-button-text 10 #:inline-keyboard-button-text
11 inline-keyboard-button-url 11 #:inline-keyboard-button-url
12 inline-keyboard-button-callback-data 12 #:inline-keyboard-button-callback-data
13 inline-keyboard-button-switch-inline-query 13 #:inline-keyboard-button-web-app
14 inline-keyboard-button-switch-inline-query-current-chat 14 #:inline-keyboard-button-switch-inline-query
15 inline-keyboard-button-pay 15 #:inline-keyboard-button-switch-inline-query-current-chat
16 16 #:inline-keyboard-button-pay))
17 hash->inline-keyboard-button
18 parse-inline-keyboard-button-array))
19(in-package :ukkoclot/tg-types/inline-keyboard-button) 17(in-package :ukkoclot/tg-types/inline-keyboard-button)
20 18
21(define-tg-type inline-keyboard-button 19(define-tg-type inline-keyboard-button
22 (text string) 20 (text string)
23 (url (or string null) nil) 21 (url (or string null) nil)
24 (callback-data string) 22 (callback-data string)
25 ;; TODO: (web-app (or web-app-info null) nil) 23 (web-app (or web-app-info null) nil)
26 ;; TODO: (login-url (or login-url null) nil) 24 ;; TODO: (login-url (or login-url null) nil)
27 (switch-inline-query (or string null) nil) 25 (switch-inline-query (or string null) nil)
28 (switch-inline-query-current-chat (or string null) nil) 26 (switch-inline-query-current-chat (or string null) nil)
diff --git a/src/tg-types/inline-keyboard-markup.lisp b/src/tg-types/inline-keyboard-markup.lisp
index 1f17f6c..bb7b9c1 100644
--- a/src/tg-types/inline-keyboard-markup.lisp
+++ b/src/tg-types/inline-keyboard-markup.lisp
@@ -3,14 +3,11 @@
3(defpackage :ukkoclot/tg-types/inline-keyboard-markup 3(defpackage :ukkoclot/tg-types/inline-keyboard-markup
4 (:use :c2cl :ukkoclot/tg-types/inline-keyboard-button :ukkoclot/tg-types/macros) 4 (:use :c2cl :ukkoclot/tg-types/inline-keyboard-button :ukkoclot/tg-types/macros)
5 (:export 5 (:export
6 inline-keyboard-markup 6 #:inline-keyboard-markup
7 make-inline-keyboard-markup 7 #:make-inline-keyboard-markup
8 inline-keyboard-markup-p 8 #:inline-keyboard-markup-p
9 copy-inline-keyboard-markup 9 #:copy-inline-keyboard-markup
10 inline-keyboard-markup-inline-keyboard 10 #:inline-keyboard-markup-inline-keyboard))
11
12 hash->inline-keyboard-markup
13 parse-inline-keyboard-markup-array))
14(in-package :ukkoclot/tg-types/inline-keyboard-markup) 11(in-package :ukkoclot/tg-types/inline-keyboard-markup)
15 12
16(define-tg-type inline-keyboard-markup 13(define-tg-type inline-keyboard-markup
diff --git a/src/tg-types/keyboard-button-poll-type.lisp b/src/tg-types/keyboard-button-poll-type.lisp
new file mode 100644
index 0000000..fdd7b92
--- /dev/null
+++ b/src/tg-types/keyboard-button-poll-type.lisp
@@ -0,0 +1,14 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/keyboard-button-poll-type
4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export
6 #:keyboard-button-poll-type
7 #:make-keyboard-button-poll-type
8 #:keyboard-button-poll-type-p
9 #:copy-keyboard-button-poll-type
10 #:keyboard-button-poll-type-type))
11(in-package :ukkoclot/tg-types/keyboard-button-poll-type)
12
13(define-tg-type keyboard-button-poll-type
14 (type (or string null) nil)) ;(member "quiz" "regular") or null
diff --git a/src/tg-types/keyboard-button-request-chat.lisp b/src/tg-types/keyboard-button-request-chat.lisp
new file mode 100644
index 0000000..aab8512
--- /dev/null
+++ b/src/tg-types/keyboard-button-request-chat.lisp
@@ -0,0 +1,32 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/keyboard-button-request-chat
4 (:use :c2cl :ukkoclot/tg-types/chat-administrator-rights :ukkoclot/tg-types/macros)
5 (:export
6 #:keyboard-button-request-chat
7 #:make-keyboard-button-request-chat
8 #:keyboard-button-request-chat-p
9 #:copy-keyboard-button-request-chat
10 #:keyboard-button-request-chat-request-id
11 #:keyboard-button-request-chat-chat-is-channel
12 #:keyboard-button-request-chat-chat-is-created
13 #:keyboard-button-request-chat-user-administrator-rights
14 #:keyboard-button-request-chat-bot-administrator-rights
15 #:keyboard-button-request-chat-bot-is-member
16 #:keyboard-button-request-chat-request-title
17 #:keyboard-button-request-chat-request-username
18 #:keyboard-button-request-chat-request-photo))
19(in-package :ukkoclot/tg-types/keyboard-button-request-chat)
20
21(define-tg-type keyboard-button-request-chat
22 (request-id integer)
23 (chat-is-channel boolean)
24 ;; TODO: (chat-is-forum ternary nil)
25 ;; TODO: (chat-has-username ternary nil)
26 (chat-is-created boolean nil)
27 (user-administrator-rights (or chat-administrator-rights null) nil)
28 (bot-administrator-rights (or chat-administrator-rights null) nil)
29 (bot-is-member boolean nil)
30 (request-title boolean nil)
31 (request-username boolean nil)
32 (request-photo boolean nil))
diff --git a/src/tg-types/keyboard-button-request-users.lisp b/src/tg-types/keyboard-button-request-users.lisp
new file mode 100644
index 0000000..9b20c3d
--- /dev/null
+++ b/src/tg-types/keyboard-button-request-users.lisp
@@ -0,0 +1,24 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/keyboard-button-request-users
4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export
6 #:keyboard-button-request-users
7 #:make-keyboard-button-request-users
8 #:keyboard-button-request-users-p
9 #:copy-keyboard-button-request-users
10 #:keyboard-button-request-users-request-id
11 #:keyboard-button-request-users-max-quantity
12 #:keyboard-button-request-users-request-name
13 #:keyboard-button-request-users-request-username
14 #:keyboard-button-request-users-request-photo))
15(in-package :ukkoclot/tg-types/keyboard-button-request-users)
16
17(define-tg-type keyboard-button-request-users
18 (request-id integer)
19 ;; TODO: (user-is-bot ternary nil)
20 ;; TODO: (user-is-premium ternary nil)
21 (max-quantity integer 1)
22 (request-name boolean nil)
23 (request-username boolean nil)
24 (request-photo boolean nil))
diff --git a/src/tg-types/keyboard-button.lisp b/src/tg-types/keyboard-button.lisp
new file mode 100644
index 0000000..fac64e6
--- /dev/null
+++ b/src/tg-types/keyboard-button.lisp
@@ -0,0 +1,32 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/keyboard-button
4 (:use
5 :c2cl
6 :ukkoclot/tg-types/keyboard-button-poll-type
7 :ukkoclot/tg-types/keyboard-button-request-chat
8 :ukkoclot/tg-types/keyboard-button-request-users
9 :ukkoclot/tg-types/macros
10 :ukkoclot/tg-types/web-app-info)
11 (:export
12 #:keyboard-button
13 #:make-keyboard-button
14 #:keyboard-button-p
15 #:copy-keyboard-button
16 #:keyboard-button-text
17 #:keyboard-button-request-users
18 #:keyboard-button-request-chat
19 #:keyboard-button-request-contact
20 #:keyboard-button-request-location
21 #:keyboard-button-request-poll
22 #:keyboard-button-web-app))
23(in-package :ukkoclot/tg-types/keyboard-button)
24
25(define-tg-type keyboard-button
26 (text string)
27 (request-users (or keyboard-button-request-users null) nil)
28 (request-chat (or keyboard-button-request-chat null) nil)
29 (request-contact boolean nil)
30 (request-location boolean nil)
31 (request-poll (or keyboard-button-poll-type null) nil)
32 (web-app (or web-app-info null) nil))
diff --git a/src/tg-types/link-preview-options.lisp b/src/tg-types/link-preview-options.lisp
index 66b7d83..450a9ea 100644
--- a/src/tg-types/link-preview-options.lisp
+++ b/src/tg-types/link-preview-options.lisp
@@ -3,18 +3,15 @@
3(defpackage :ukkoclot/tg-types/link-preview-options 3(defpackage :ukkoclot/tg-types/link-preview-options
4 (:use :c2cl :ukkoclot/tg-types/macros) 4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export 5 (:export
6 link-preview-options 6 #:link-preview-options
7 make-link-preview-options 7 #:make-link-preview-options
8 link-preview-options-p 8 #:link-preview-options-p
9 copy-link-preview-options 9 #:copy-link-preview-options
10 link-preview-options-is-disabled 10 #:link-preview-options-is-disabled
11 link-preview-options-url 11 #:link-preview-options-url
12 link-preview-options-prefer-small-media 12 #:link-preview-options-prefer-small-media
13 link-preview-options-prefer-large-media 13 #:link-preview-options-prefer-large-media
14 link-preview-options-show-above-text 14 #:link-preview-options-show-above-text))
15
16 hash->link-preview-options
17 parse-link-preview-options-array))
18(in-package :ukkoclot/tg-types/link-preview-options) 15(in-package :ukkoclot/tg-types/link-preview-options)
19 16
20(define-tg-type link-preview-options 17(define-tg-type link-preview-options
diff --git a/src/tg-types/macros.lisp b/src/tg-types/macros.lisp
index 668df17..a8a3d96 100644
--- a/src/tg-types/macros.lisp
+++ b/src/tg-types/macros.lisp
@@ -2,26 +2,27 @@
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> 2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/macros 3(defpackage :ukkoclot/tg-types/macros
4 (:use :c2cl) 4 (:use :c2cl)
5 (:import-from :ukkoclot/bot/impl :arg-encode :bot :do-call :will-arg-encode) 5 (:import-from :ukkoclot/bot/impl :bot :do-call :parse-value)
6 (:import-from :ukkoclot/hash-tables :gethash-lazy) 6 (:import-from :ukkoclot/hash-tables :gethash-lazy)
7 (:import-from :ukkoclot/strings :lisp->snake-case) 7 (:import-from :ukkoclot/strings :lisp->snake-case)
8 (:local-nicknames
9 (:jzon :com.inuoe.jzon))
8 (:export :define-tg-method :define-tg-type)) 10 (:export :define-tg-method :define-tg-type))
9(in-package :ukkoclot/tg-types/macros) 11(in-package :ukkoclot/tg-types/macros)
10 12
11(eval-when (:compile-toplevel :load-toplevel :execute) 13(eval-when (:compile-toplevel :load-toplevel :execute)
12 (defstruct (field (:constructor make-field%)) name type default skip-if-default (parser 'identity)) 14 (defstruct (field (:constructor make-field%)) name type default skip-if-default)
13 15
14 (defparameter +unique+ (gensym)) 16 (defparameter +unique+ (gensym))
15 17
16 (defun make-field (name type &optional (default +unique+) &key (parser 'identity) (skip-if-default (not (eq default +unique+)))) 18 (defun make-field (name type &optional (default +unique+) &key (skip-if-default (not (eq default +unique+))))
17 (let ((default (if (eq default +unique+) 19 (let ((default (if (eq default +unique+)
18 (list 'error (format nil "No value given for ~A" name)) 20 (list 'error (format nil "No value given for ~A" name))
19 default))) 21 default)))
20 (make-field% :name name 22 (make-field% :name name
21 :type type 23 :type type
22 :default default 24 :default default
23 :skip-if-default skip-if-default 25 :skip-if-default skip-if-default)))
24 :parser parser)))
25 26
26 (defun parse-field-specs (field-specs) 27 (defun parse-field-specs (field-specs)
27 (loop for field-spec in field-specs 28 (loop for field-spec in field-specs
@@ -33,6 +34,11 @@
33 (defun field-accessor (struc-name field) 34 (defun field-accessor (struc-name field)
34 (intern (concatenate 'string (symbol-name struc-name) "-" (symbol-name (field-name field))))) 35 (intern (concatenate 'string (symbol-name struc-name) "-" (symbol-name (field-name field)))))
35 36
37 (defun field->coerced-field-spec (field struc-name obj-name)
38 `(list ,(string-downcase (lisp->snake-case (symbol-name (field-name field))))
39 (,(field-accessor struc-name field) ,obj-name)
40 ',(field-type field)))
41
36 (defun field->defun-spec (field) 42 (defun field->defun-spec (field)
37 (list (field-name field) (field-default field))) 43 (list (field-name field) (field-default field)))
38 44
@@ -58,10 +64,8 @@
58 `(setf (gethash ,hash-key ,hash-table-sym) (,(field-accessor name field) ,struc))))) 64 `(setf (gethash ,hash-key ,hash-table-sym) (,(field-accessor name field) ,struc)))))
59 65
60 (defun field->let-gethash-spec (field hash-table-sym) 66 (defun field->let-gethash-spec (field hash-table-sym)
61 (list (field-name field) 67 `(,(field-name field)
62 (list 'funcall 68 (parse-value ',(field-type field) ,(field->gethash-spec field hash-table-sym))))
63 (list 'function (field-parser field))
64 (field->gethash-spec field hash-table-sym))))
65 69
66 (defun field->make-spec (field) 70 (defun field->make-spec (field)
67 (list (intern (symbol-name (field-name field)) :keyword) 71 (list (intern (symbol-name (field-name field)) :keyword)
@@ -72,35 +76,40 @@
72 76
73;; TODO: Automatically derive path from name 77;; TODO: Automatically derive path from name
74;; TODO: Automatically derive mapfn from type 78;; TODO: Automatically derive mapfn from type
75;; TODO: Skip values that are already their defaults
76(defmacro define-tg-method ( 79(defmacro define-tg-method (
77 (name type path mapfn &optional (method :POST)) 80 (name type path &optional (method :POST))
78 &body field-specs) 81 &body field-specs)
79 (let ((fields (parse-field-specs field-specs)) 82 (let* ((fields (parse-field-specs field-specs))
80 (args-plist (gensym "ARGS-PLIST-")) 83 (revfields (reverse fields))
81 (bot (gensym "BOT-"))) 84 (args (gensym "ARGS"))
85 (bot (gensym "BOT-")))
82 `(progn 86 `(progn
83 (declaim (ftype (function (bot &key ,@(loop for field in fields 87 (declaim (ftype (function (bot &key ,@(loop for field in fields
84 collect (field->ftype-spec field))) 88 collect (field->ftype-spec field)))
85 ,type) 89 ,type)
86 ,name)) 90 ,name))
87 (defun ,name (,bot &rest ,args-plist &key ,@(loop for field in fields collect (field->defun-spec field))) 91 (defun ,name (,bot &key ,@(loop for field in fields collect (field->defun-spec field)))
88 (declare ,@(loop for field in fields collect (list 'ignore (field-name field)))) 92 (let (,args)
89 (do-call ,bot ,method ,path ,mapfn ,args-plist))))) 93 ,@(loop for field in revfields
94 collecting
95 (if (field-skip-if-default field)
96 `(unless (equal ,(field-name field) ,(field-default field))
97 (setf ,args (acons ',(field-name field) ,(field-name field) ,args)))
98 `(setf ,args (acons ',(field-name field) ,(field-name field) ,args))))
99 (do-call ,bot ,method ,path ',type ,args))))))
90 100
91(defmacro define-tg-type (name &body field-specs) 101(defmacro define-tg-type (name &body field-specs)
92 (let* ((fields (parse-field-specs field-specs)) 102 (let* ((fields (parse-field-specs field-specs))
93 (revfields (reverse fields)) 103 (revfields (reverse fields))
94 (make-name (intern (concatenate 'string "MAKE-" (symbol-name name)))) 104 (make-name (intern (concatenate 'string "MAKE-" (symbol-name name))))
95 (hash->name (intern (concatenate 'string "HASH->" (symbol-name name))))
96 (parse-name-array (intern (concatenate 'string "PARSE-" (symbol-name name) "-ARRAY")))
97 (printer (gensym (concatenate 'string "PRINT-" (symbol-name name)))) 105 (printer (gensym (concatenate 'string "PRINT-" (symbol-name name))))
98 (hash (gensym "HASH-")) 106 (hash (gensym "HASH-"))
99 (array (gensym "ARRAY-"))
100 (struc (gensym (symbol-name name))) 107 (struc (gensym (symbol-name name)))
101 (stream (gensym "STREAM")) 108 (stream (gensym "STREAM"))
102 (depth (gensym "DEPTH")) 109 (depth (gensym "DEPTH"))
103 (pprint-args (gensym "PPRINT-ARGS"))) 110 (pprint-args (gensym "PPRINT-ARGS"))
111 (res (gensym "RES"))
112 (type (gensym "TYPE")))
104 `(progn 113 `(progn
105 (defstruct (,name (:print-function ,printer)) 114 (defstruct (,name (:print-function ,printer))
106 ,@(loop for field in fields 115 ,@(loop for field in fields
@@ -116,19 +125,18 @@
116 (setf ,pprint-args (list* ',(field-name field) value ,pprint-args)))) 125 (setf ,pprint-args (list* ',(field-name field) value ,pprint-args))))
117 `(setf ,pprint-args (list* ',(field-name field) (,(field-accessor name field) ,struc) ,pprint-args)))) 126 `(setf ,pprint-args (list* ',(field-name field) (,(field-accessor name field) ,struc) ,pprint-args))))
118 (format ,stream "~A~<[~;~@{~_~1I~W = ~W~^, ~}~;]~:>" ',name ,pprint-args))) 127 (format ,stream "~A~<[~;~@{~_~1I~W = ~W~^, ~}~;]~:>" ',name ,pprint-args)))
119 (defun ,hash->name (,hash) 128 (defmethod parse-value ((,type (eql ',name)) ,hash)
120 (when ,hash 129 (let ,(loop for field in fields
121 (let ,(loop for field in fields 130 collect (field->let-gethash-spec field hash))
122 collect (field->let-gethash-spec field hash)) 131 (,make-name ,@(loop for field in fields
123 (,make-name ,@(loop for field in fields 132 append (field->make-spec field)))))
124 append (field->make-spec field)))))) 133 (defmethod jzon:coerced-fields ((,struc ,name))
125 (defmethod arg-encode ((,struc ,name)) 134 (let (,res)
126 (let ((,hash (make-hash-table))) 135 ,@(loop for field in revfields
127 ,@(loop for field in fields 136 collecting
128 collect (field->sethash-spec field name struc hash)) 137 (if (field-skip-if-default field)
129 ,hash)) 138 `(let ((value (,(field-accessor name field) ,struc)))
130 (defmethod will-arg-encode ((,struc ,name)) 139 (unless (equal value ,(field-default field))
131 t) 140 (setf ,res (cons ,(field->coerced-field-spec field name struc) ,res))))
132 (defun ,parse-name-array (,array) 141 `(setf ,res (cons ,(field->coerced-field-spec field name struc) ,res))))
133 (when ,array 142 ,res)))))
134 (map 'vector #',hash->name ,array))))))
diff --git a/src/tg-types/message-entity.lisp b/src/tg-types/message-entity.lisp
index fcabcce..a605f23 100644
--- a/src/tg-types/message-entity.lisp
+++ b/src/tg-types/message-entity.lisp
@@ -1,27 +1,25 @@
1;; SPDX-License-Identifier: EUPL-1.2 1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> 2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/message-entity 3(defpackage :ukkoclot/tg-types/message-entity
4 (:use :c2cl :iterate :ukkoclot/tg-types/macros :ukkoclot/tg-types/parsers :ukkoclot/tg-types/user) 4 (:use :c2cl :iterate :ukkoclot/tg-types/macros :ukkoclot/tg-types/user)
5 (:export 5 (:export
6 message-entity 6 #:message-entity
7 make-message-entity 7 #:make-message-entity
8 message-entity-p 8 #:message-entity-p
9 copy-message-entity 9 #:copy-message-entity
10 message-entity-type 10 #:message-entity-type
11 message-entity-offset 11 #:message-entity-offset
12 message-entity-length 12 #:message-entity-length
13 message-entity-url 13 #:message-entity-url
14 message-entity-user 14 #:message-entity-user
15 message-entity-language 15 #:message-entity-language
16 message-entity-custom-emoji-id 16 #:message-entity-custom-emoji-id
17 17
18 hash->message-entity 18 #:message-entity-extract))
19 message-entity-extract
20 parse-message-entity-array))
21(in-package :ukkoclot/tg-types/message-entity) 19(in-package :ukkoclot/tg-types/message-entity)
22 20
23(define-tg-type message-entity 21(define-tg-type message-entity
24 (type keyword nil :parser tg-string->keyword) 22 (type string nil) ;TODO: keywords
25 (offset integer) 23 (offset integer)
26 (length integer) 24 (length integer)
27 (url (or string null) nil) 25 (url (or string null) nil)
diff --git a/src/tg-types/message.lisp b/src/tg-types/message.lisp
index fee0734..028379f 100644
--- a/src/tg-types/message.lisp
+++ b/src/tg-types/message.lisp
@@ -7,52 +7,50 @@
7 :ukkoclot/tg-types/message-entity 7 :ukkoclot/tg-types/message-entity
8 :ukkoclot/tg-types/user) 8 :ukkoclot/tg-types/user)
9 (:export 9 (:export
10 message 10 #:message
11 make-message 11 #:make-message
12 message-p 12 #:message-p
13 copy-message 13 #:copy-message
14 message-message-id 14 #:message-message-id
15 message-message-thread-id 15 #:message-message-thread-id
16 message-from 16 #:message-from
17 message-sender-boost-count 17 #:message-sender-boost-count
18 message-sender-business-bot 18 #:message-sender-business-bot
19 message-date 19 #:message-date
20 message-business-connection-id 20 #:message-business-connection-id
21 message-chat 21 #:message-chat
22 message-is-topic-message 22 #:message-is-topic-message
23 message-is-automatic-forward 23 #:message-is-automatic-forward
24 message-reply-to-message 24 #:message-reply-to-message
25 message-reply-to-checklist-task-id 25 #:message-reply-to-checklist-task-id
26 message-via-bot 26 #:message-via-bot
27 message-edit-date 27 #:message-edit-date
28 message-has-protected-content 28 #:message-has-protected-content
29 message-is-from-offline 29 #:message-is-from-offline
30 message-is-paid-post 30 #:message-is-paid-post
31 message-media-group-id 31 #:message-media-group-id
32 message-author-signature 32 #:message-author-signature
33 message-paid-star-count 33 #:message-paid-star-count
34 message-text 34 #:message-text
35 message-entities 35 #:message-entities
36 message-effect-id 36 #:message-effect-id
37 message-caption 37 #:message-caption
38 message-show-caption-above-media 38 #:message-show-caption-above-media
39 message-has-media-spoiler 39 #:message-has-media-spoiler
40 message-new-chat-members 40 #:message-new-chat-members
41 message-new-chat-title 41 #:message-new-chat-title
42 message-delete-chat-photo 42 #:message-delete-chat-photo
43 message-group-chat-created 43 #:message-group-chat-created
44 message-supergroup-chat-created 44 #:message-supergroup-chat-created
45 message-channel-chat-created 45 #:message-channel-chat-created
46 message-migrate-to-chat-id 46 #:message-migrate-to-chat-id
47 message-migrate-from-chat-id 47 #:message-migrate-from-chat-id
48 message-pinned-message 48 #:message-pinned-message
49 message-connected-website 49 #:message-connected-website
50 50
51 hash->message 51 #:message-chat-id
52 message-id 52 #:message-thread-id
53 message-chat-id 53 #:message-id))
54 message-thread-id
55 parse-message-array))
56(in-package :ukkoclot/tg-types/message) 54(in-package :ukkoclot/tg-types/message)
57 55
58;; If this is a MaybeInaccessibleMessage date will be 0 if this is inaccessible 56;; If this is a MaybeInaccessibleMessage date will be 0 if this is inaccessible
@@ -60,22 +58,22 @@
60 (message-id integer) 58 (message-id integer)
61 (message-thread-id (or integer null) nil) 59 (message-thread-id (or integer null) nil)
62 ;; (direct-messages-topic (or direct-messages-topic null) nil) 60 ;; (direct-messages-topic (or direct-messages-topic null) nil)
63 (from (or user null) nil :parser hash->user) 61 (from (or user null) nil)
64 ;; (sender-chat (or chat null) nil) 62 ;; (sender-chat (or chat null) nil)
65 (sender-boost-count (or integer null) nil) 63 (sender-boost-count (or integer null) nil)
66 (sender-business-bot (or user null) nil :parser hash->user) 64 (sender-business-bot (or user null) nil)
67 (date integer) 65 (date integer)
68 (business-connection-id (or string null) nil) 66 (business-connection-id (or string null) nil)
69 (chat chat nil :parser hash->chat) 67 (chat chat nil)
70 ;; (forward-origin (or message-origin null) nil) 68 ;; (forward-origin (or message-origin null) nil)
71 (is-topic-message boolean nil) 69 (is-topic-message boolean nil)
72 (is-automatic-forward boolean nil) 70 (is-automatic-forward boolean nil)
73 (reply-to-message (or message null) nil :parser hash->message) 71 (reply-to-message (or message null) nil)
74 ;; (external-reply (or external-reply-info null) nil) 72 ;; (external-reply (or external-reply-info null) nil)
75 ;; (quote (or text-quote null) nil) 73 ;; (quote (or text-quote null) nil)
76 ;; (reply-to-story (or story null) nil) 74 ;; (reply-to-story (or story null) nil)
77 (reply-to-checklist-task-id (or integer null) nil) 75 (reply-to-checklist-task-id (or integer null) nil)
78 (via-bot (or user null) nil :parser hash->user) 76 (via-bot (or user null) nil)
79 (edit-date (or integer null) nil) 77 (edit-date (or integer null) nil)
80 (has-protected-content boolean nil) 78 (has-protected-content boolean nil)
81 (is-from-offline boolean nil) 79 (is-from-offline boolean nil)
@@ -84,7 +82,7 @@
84 (author-signature (or string null) nil) 82 (author-signature (or string null) nil)
85 (paid-star-count (or string null) nil) 83 (paid-star-count (or string null) nil)
86 (text (or string null) nil) 84 (text (or string null) nil)
87 (entities (or (array message-entity) null) nil :parser parse-message-entity-array) 85 (entities (or (array message-entity) null) nil)
88 ;; (link-preview-options (or link-preview-options null) nil) 86 ;; (link-preview-options (or link-preview-options null) nil)
89 ;; (suggested-post-info (or suggested-post-info null) nil) 87 ;; (suggested-post-info (or suggested-post-info null) nil)
90 (effect-id (or string null) nil) 88 (effect-id (or string null) nil)
@@ -108,7 +106,7 @@
108 ;; (poll (or poll null) nil) 106 ;; (poll (or poll null) nil)
109 ;; (venue (or venue null) nil) 107 ;; (venue (or venue null) nil)
110 ;; (location (or location null) nil) 108 ;; (location (or location null) nil)
111 (new-chat-members (or (array user) null) nil :parser parse-user-array) 109 (new-chat-members (or (array user) null) nil)
112 ;; (left-chat-member (or user null) nil) 110 ;; (left-chat-member (or user null) nil)
113 (new-chat-title (or string null) nil) 111 (new-chat-title (or string null) nil)
114 ;; (new-chat-photo (or (array photo-size) null) nil) 112 ;; (new-chat-photo (or (array photo-size) null) nil)
@@ -119,7 +117,7 @@
119 ;; (message-auto-delete-timer-changed (orp message-auto-delete-timer-changed null) nil) 117 ;; (message-auto-delete-timer-changed (orp message-auto-delete-timer-changed null) nil)
120 (migrate-to-chat-id (or integer null) nil) 118 (migrate-to-chat-id (or integer null) nil)
121 (migrate-from-chat-id (or integer null) nil) 119 (migrate-from-chat-id (or integer null) nil)
122 (pinned-message (or message null) nil :parser hash->message) 120 (pinned-message (or message null) nil)
123 ;; (invoice (or invoice null) nil) 121 ;; (invoice (or invoice null) nil)
124 ;; (successful-payment (or successful-payment null) nil) 122 ;; (successful-payment (or successful-payment null) nil)
125 ;; (refunded-payment (or refunded-payment null) nil) 123 ;; (refunded-payment (or refunded-payment null) nil)
diff --git a/src/tg-types/parsers.lisp b/src/tg-types/parsers.lisp
deleted file mode 100644
index 0b6c4ae..0000000
--- a/src/tg-types/parsers.lisp
+++ /dev/null
@@ -1,9 +0,0 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/parsers
4 (:use :c2cl :ukkoclot/strings)
5 (:export tg-string->keyword))
6(in-package :ukkoclot/tg-types/parsers)
7
8(defun tg-string->keyword (str)
9 (intern (string-upcase (snake->lisp-case str)) :keyword))
diff --git a/src/tg-types/reply-keyboard-markup.lisp b/src/tg-types/reply-keyboard-markup.lisp
new file mode 100644
index 0000000..f4b5d45
--- /dev/null
+++ b/src/tg-types/reply-keyboard-markup.lisp
@@ -0,0 +1,24 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/reply-keyboard-markup
4 (:use :c2cl :ukkoclot/tg-types/keyboard-button :ukkoclot/tg-types/macros)
5 (:export
6 #:reply-keyboard-markup
7 #:make-reply-keyboard-markup
8 #:reply-keyboard-markup-p
9 #:copy-reply-keyboard-markup
10 #:reply-keyboard-markup-keyboard
11 #:reply-keyboard-markup-is-persistent
12 #:reply-keyboard-markup-resize-keyboard
13 #:reply-keyboard-markup-one-time-keyboard
14 #:reply-keyboard-markup-input-field-placeholder
15 #:reply-keyboard-markup-selective))
16(in-package :ukkoclot/tg-types/reply-keyboard-markup)
17
18(define-tg-type reply-keyboard-markup
19 (keyboard (array (array keyboard-button)))
20 (is-persistent boolean nil)
21 (resize-keyboard boolean nil)
22 (one-time-keyboard boolean nil)
23 (input-field-placeholder (or string null) nil)
24 (selective boolean nil))
diff --git a/src/tg-types/reply-keyboard-remove.lisp b/src/tg-types/reply-keyboard-remove.lisp
new file mode 100644
index 0000000..9925d82
--- /dev/null
+++ b/src/tg-types/reply-keyboard-remove.lisp
@@ -0,0 +1,16 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/reply-keyboard-remove
4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export
6 #:reply-keyboard-remove
7 #:make-reply-keyboard-remove
8 #:reply-keyboard-remove-p
9 #:copy-reply-keyboard-remove
10 #:reply-keyboard-remove-remove-keyboard
11 #:reply-keyboard-remove-selective))
12(in-package :ukkoclot/tg-types/reply-keyboard-remove)
13
14(define-tg-type reply-keyboard-remove
15 (remove-keyboard boolean t :skip-if-default nil)
16 (selective boolean nil))
diff --git a/src/tg-types/reply-parameters.lisp b/src/tg-types/reply-parameters.lisp
index 29d21f7..ac38db7 100644
--- a/src/tg-types/reply-parameters.lisp
+++ b/src/tg-types/reply-parameters.lisp
@@ -3,21 +3,18 @@
3(defpackage :ukkoclot/tg-types/reply-parameters 3(defpackage :ukkoclot/tg-types/reply-parameters
4 (:use :c2cl :ukkoclot/tg-types/macros :ukkoclot/tg-types/message-entity) 4 (:use :c2cl :ukkoclot/tg-types/macros :ukkoclot/tg-types/message-entity)
5 (:export 5 (:export
6 reply-parameters 6 #:reply-parameters
7 make-reply-parameters 7 #:make-reply-parameters
8 reply-parameters-p 8 #:reply-parameters-p
9 copy-reply-parameters 9 #:copy-reply-parameters
10 reply-parameters-message-id 10 #:reply-parameters-message-id
11 reply-parameters-chat-id 11 #:reply-parameters-chat-id
12 reply-parameters-allow-sending-without-reply 12 #:reply-parameters-allow-sending-without-reply
13 reply-parameters-quote 13 #:reply-parameters-quote
14 reply-parameters-quote-parse-mode 14 #:reply-parameters-quote-parse-mode
15 reply-parameters-quote-entities 15 #:reply-parameters-quote-entities
16 reply-parameters-quote-position 16 #:reply-parameters-quote-position
17 reply-parameters-checklist-task-id 17 #:reply-parameters-checklist-task-id))
18
19 hash->reply-parameters
20 parse-reply-parameters-array))
21(in-package :ukkoclot/tg-types/reply-parameters) 18(in-package :ukkoclot/tg-types/reply-parameters)
22 19
23(define-tg-type reply-parameters 20(define-tg-type reply-parameters
diff --git a/src/tg-types/suggested-post-parameters.lisp b/src/tg-types/suggested-post-parameters.lisp
new file mode 100644
index 0000000..bf781fc
--- /dev/null
+++ b/src/tg-types/suggested-post-parameters.lisp
@@ -0,0 +1,16 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/suggested-post-parameters
4 (:use :c2cl :ukkoclot/tg-types/macros :ukkoclot/tg-types/suggested-post-price)
5 (:export
6 #:suggested-post-parameters
7 #:make-suggested-post-parameters
8 #:suggested-post-parameters-p
9 #:copy-suggested-post-parameters
10 #:suggested-post-parameters-price
11 #:suggested-post-parameters-send-date))
12(in-package :ukkoclot/tg-types/suggested-post-parameters)
13
14(define-tg-type suggested-post-parameters
15 (price (or suggested-post-price null) nil)
16 (send-date (or integer null) nil))
diff --git a/src/tg-types/suggested-post-price.lisp b/src/tg-types/suggested-post-price.lisp
new file mode 100644
index 0000000..e5b6cb3
--- /dev/null
+++ b/src/tg-types/suggested-post-price.lisp
@@ -0,0 +1,16 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/suggested-post-price
4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export
6 #:suggested-post-price
7 #:make-suggested-post-price
8 #:suggested-post-price-p
9 #:copy-suggested-post-price
10 #:suggested-post-price-currency
11 #:suggested-post-price-amount))
12(in-package :ukkoclot/tg-types/suggested-post-price)
13
14(define-tg-type suggested-post-price
15 (currency string) ;(member "XTR" "TON")
16 (amount integer))
diff --git a/src/tg-types/update.lisp b/src/tg-types/update.lisp
index 9043d54..c259553 100644
--- a/src/tg-types/update.lisp
+++ b/src/tg-types/update.lisp
@@ -5,36 +5,35 @@
5 :ukkoclot/tg-types/callback-query 5 :ukkoclot/tg-types/callback-query
6 :ukkoclot/tg-types/message) 6 :ukkoclot/tg-types/message)
7 (:export 7 (:export
8 update update-p 8 #:update
9 9 #:make-update
10 hash->update make-update parse-update-array 10 #:update-p
11 11 #:copy-update
12 update-update-id update-message update-edited-message update-channel-post update-edited-channel-post 12 #:update-update-id
13 ;; update-business-connection 13 #:update-message
14 update-business-message update-edited-business-message 14 #:update-edited-message
15 ;; update-deleted-business-messages update-message-reaction update-message-reaction-count update-inline-query 15 #:update-channel-post
16 ;; update-chosen-inline-result 16 #:update-edited-channel-post
17 update-callback-query 17 #:update-business-message
18 ;; update-shipping-query update-pre-checkout-query update-poll update-poll-answer update-my-chat-member 18 #:update-edited-business-message
19 ;; update-chat-member update-chat-join-request update-chat-boost update-removed-chat-boost 19 #:update-callback-query))
20 ))
21(in-package :ukkoclot/tg-types/update) 20(in-package :ukkoclot/tg-types/update)
22 21
23(define-tg-type update 22(define-tg-type update
24 (update-id integer) 23 (update-id integer)
25 (message (or message null) nil :parser hash->message) 24 (message (or message null) nil)
26 (edited-message (or message null) nil :parser hash->message) 25 (edited-message (or message null) nil)
27 (channel-post (or message null) nil :parser hash->message) 26 (channel-post (or message null) nil)
28 (edited-channel-post (or message null) nil :parser hash->message) 27 (edited-channel-post (or message null) nil)
29 ;; (business-connection (or business-connection null) nil) 28 ;; (business-connection (or business-connection null) nil)
30 (business-message (or message null) nil :parser hash->message) 29 (business-message (or message null) nil)
31 (edited-business-message (or message null) nil :parser hash->message) 30 (edited-business-message (or message null) nil)
32 ;; (deleted-business-messages (or business-messages-deleted null) nil) 31 ;; (deleted-business-messages (or business-messages-deleted null) nil)
33 ;; (message-reaction (or message-reaction-updated null) nil) 32 ;; (message-reaction (or message-reaction-updated null) nil)
34 ;; (message-reaction-count (or message-reaction-count-updated null) nil) 33 ;; (message-reaction-count (or message-reaction-count-updated null) nil)
35 ;; (inline-query (or inline-query null) nil) 34 ;; (inline-query (or inline-query null) nil)
36 ;; (chosen-inline-result (or chosen-inline-result null) nil) 35 ;; (chosen-inline-result (or chosen-inline-result null) nil)
37 (callback-query (or callback-query null) nil :parser hash->callback-query) 36 (callback-query (or callback-query null) nil)
38 ;; (shipping-query (or shipping-query null) nil) 37 ;; (shipping-query (or shipping-query null) nil)
39 ;; (pre-checkout-query (or pre-checkout-query null) nil) 38 ;; (pre-checkout-query (or pre-checkout-query null) nil)
40 ;; (poll (or poll null) nil) 39 ;; (poll (or poll null) nil)
diff --git a/src/tg-types/user.lisp b/src/tg-types/user.lisp
index c5ed499..b5be417 100644
--- a/src/tg-types/user.lisp
+++ b/src/tg-types/user.lisp
@@ -4,13 +4,24 @@
4 (:use :c2cl :ukkoclot/tg-types/macros) 4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:import-from :ukkoclot/strings :escape-xml) 5 (:import-from :ukkoclot/strings :escape-xml)
6 (:export 6 (:export
7 user user-p 7 #:user
8 #:make-user
9 #:user-p
10 #:copy-user
11 #:user-id
12 #:user-is-bot
13 #:user-first-name
14 #:user-last-name
15 #:user-username
16 #:user-language-code
17 #:user-is-premium
18 #:user-added-to-attachment-menu
19 #:user-can-join-groups
20 #:user-can-read-all-group-messages
21 #:user-supports-inline-queries
22 #:user-can-connect-to-business
8 23
9 hash->user make-user parse-user-array user-format-name 24 #:user-format-name))
10
11 user-id user-is-bot user-first-name user-last-name user-username user-language-code user-is-premium
12 user-added-to-attachment-menu user-can-join-groups user-can-read-all-group-messages user-supports-inline-queries
13 user-can-connect-to-business))
14(in-package :ukkoclot/tg-types/user) 25(in-package :ukkoclot/tg-types/user)
15 26
16(define-tg-type user 27(define-tg-type user
diff --git a/src/tg-types/web-app-info.lisp b/src/tg-types/web-app-info.lisp
new file mode 100644
index 0000000..e239830
--- /dev/null
+++ b/src/tg-types/web-app-info.lisp
@@ -0,0 +1,14 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg-types/web-app-info
4 (:use :c2cl :ukkoclot/tg-types/macros)
5 (:export
6 #:web-app-info
7 #:make-web-app-info
8 #:web-app-info-p
9 #:copy-web-app-info
10 #:web-app-info-url))
11(in-package :ukkoclot/tg-types/web-app-info)
12
13(define-tg-type web-app-info
14 (url string))