summaryrefslogtreecommitdiff
path: root/src/tg
diff options
context:
space:
mode:
Diffstat (limited to 'src/tg')
-rw-r--r--src/tg/delete-message.lisp7
-rw-r--r--src/tg/get-me.lisp4
-rw-r--r--src/tg/message-entity.lisp3
-rw-r--r--src/tg/message.lisp13
-rw-r--r--src/tg/method-macros.lisp23
-rw-r--r--src/tg/send-animation.lisp9
-rw-r--r--src/tg/send-message.lisp6
-rw-r--r--src/tg/set-my-name.lisp4
-rw-r--r--src/tg/type-macros.lisp28
-rw-r--r--src/tg/user.lisp37
10 files changed, 96 insertions, 38 deletions
diff --git a/src/tg/delete-message.lisp b/src/tg/delete-message.lisp
index 2b332df..44fccd2 100644
--- a/src/tg/delete-message.lisp
+++ b/src/tg/delete-message.lisp
@@ -3,6 +3,7 @@
3(defpackage :ukkoclot/src/tg/delete-message 3(defpackage :ukkoclot/src/tg/delete-message
4 (:documentation "deleteMessage Telegram method") 4 (:documentation "deleteMessage Telegram method")
5 (:use :c2cl :ukkoclot/src/tg/message :ukkoclot/src/tg/method-macros :ukkoclot/src/tg/send-animation) 5 (:use :c2cl :ukkoclot/src/tg/message :ukkoclot/src/tg/method-macros :ukkoclot/src/tg/send-animation)
6 (:import-from :serapeum :->)
6 (:export :delete-message :try-delete-message)) 7 (:export :delete-message :try-delete-message))
7(in-package :ukkoclot/src/tg/delete-message) 8(in-package :ukkoclot/src/tg/delete-message)
8 9
@@ -10,6 +11,7 @@
10 (chat-id (or integer string)) 11 (chat-id (or integer string))
11 (message-id integer)) 12 (message-id integer))
12 13
14(-> try-delete-message (message) boolean)
13(defun try-delete-message (msg) 15(defun try-delete-message (msg)
14 "Attempt to delete the MSG, on failure reply with a GIF of the DO NOT cat." 16 "Attempt to delete the MSG, on failure reply with a GIF of the DO NOT cat."
15 (handler-case 17 (handler-case
@@ -17,6 +19,7 @@
17 :message-id (message-id msg)) 19 :message-id (message-id msg))
18 (error () 20 (error ()
19 (handler-case 21 (handler-case
20 (reply-animation msg #P"blob/do-not.mp4" 22 (prog1 nil
21 :allow-sending-without-reply nil) 23 (reply-animation msg #P"blob/do-not.mp4"
24 :allow-sending-without-reply nil))
22 (error () nil))))) 25 (error () nil)))))
diff --git a/src/tg/get-me.lisp b/src/tg/get-me.lisp
index e7d41a1..5360f16 100644
--- a/src/tg/get-me.lisp
+++ b/src/tg/get-me.lisp
@@ -3,12 +3,14 @@
3(defpackage :ukkoclot/src/tg/get-me 3(defpackage :ukkoclot/src/tg/get-me
4 (:documentation "getMe Telegram method") 4 (:documentation "getMe Telegram method")
5 (:use :c2cl :ukkoclot/src/tg/method-macros :ukkoclot/src/tg/user) 5 (:use :c2cl :ukkoclot/src/tg/method-macros :ukkoclot/src/tg/user)
6 (:import-from :serapeum :->)
6 (:import-from :state) 7 (:import-from :state)
7 (:export :bot-id :bot-username :get-me)) 8 (:export :bot-id :bot-username :get-me))
8(in-package :ukkoclot/src/tg/get-me) 9(in-package :ukkoclot/src/tg/get-me)
9 10
10(define-tg-method (get-me% user :GET)) 11(define-tg-method (get-me% user :GET))
11 12
13(-> get-me () user)
12(defun get-me () 14(defun get-me ()
13 "getMe Telegram method" 15 "getMe Telegram method"
14 (let ((me (get-me%))) 16 (let ((me (get-me%)))
@@ -16,6 +18,7 @@
16 (setf (state:username%) (user-username me)) 18 (setf (state:username%) (user-username me))
17 me)) 19 me))
18 20
21(-> bot-id () integer)
19(defun bot-id () 22(defun bot-id ()
20 "Get the bot's ID, this memoizes the result" 23 "Get the bot's ID, this memoizes the result"
21 (or (state:id%) 24 (or (state:id%)
@@ -23,6 +26,7 @@
23 (get-me) 26 (get-me)
24 (state:id%)))) 27 (state:id%))))
25 28
29(-> bot-username () string)
26(defun bot-username () 30(defun bot-username ()
27 "Get the bot's username, this memoizes the result" 31 "Get the bot's username, this memoizes the result"
28 (or (state:username%) 32 (or (state:username%)
diff --git a/src/tg/message-entity.lisp b/src/tg/message-entity.lisp
index 1a8cd27..c87dca0 100644
--- a/src/tg/message-entity.lisp
+++ b/src/tg/message-entity.lisp
@@ -3,6 +3,7 @@
3(defpackage :ukkoclot/src/tg/message-entity 3(defpackage :ukkoclot/src/tg/message-entity
4 (:documentation "MessageEntity Telegram type") 4 (:documentation "MessageEntity Telegram type")
5 (:use :c2cl :iterate :ukkoclot/src/enum :ukkoclot/src/tg/type-macros :ukkoclot/src/tg/user) 5 (:use :c2cl :iterate :ukkoclot/src/enum :ukkoclot/src/tg/type-macros :ukkoclot/src/tg/user)
6 (:import-from :serapeum :->)
6 (:export 7 (:export
7 #:message-entity-type 8 #:message-entity-type
8 #:mention 9 #:mention
@@ -72,6 +73,7 @@
72(unless (= char-code-limit #x110000) 73(unless (= char-code-limit #x110000)
73 (error "Some UTF-16 fuckery assumes that system chars are UTF-32")) 74 (error "Some UTF-16 fuckery assumes that system chars are UTF-32"))
74 75
76(-> utf16-width (character) (member 1 2))
75(defun utf16-width (ch) 77(defun utf16-width (ch)
76 "Calculate the size of char in UTF-16 units." 78 "Calculate the size of char in UTF-16 units."
77 (declare (type character ch)) 79 (declare (type character ch))
@@ -79,6 +81,7 @@
79 1 81 1
80 2)) 82 2))
81 83
84(-> message-entity-extract (message-entity string) string)
82(defun message-entity-extract (entity text) 85(defun message-entity-extract (entity text)
83 "Extract the text corresponding to the ENTITY from the message text (in TEXT)." 86 "Extract the text corresponding to the ENTITY from the message text (in TEXT)."
84 (check-type entity message-entity) 87 (check-type entity message-entity)
diff --git a/src/tg/message.lisp b/src/tg/message.lisp
index 13162a5..70155ab 100644
--- a/src/tg/message.lisp
+++ b/src/tg/message.lisp
@@ -10,6 +10,7 @@
10 :ukkoclot/src/tg/photo-size 10 :ukkoclot/src/tg/photo-size
11 :ukkoclot/src/tg/type-macros 11 :ukkoclot/src/tg/type-macros
12 :ukkoclot/src/tg/user) 12 :ukkoclot/src/tg/user)
13 (:import-from :serapeum :-> :defsubst)
13 (:export 14 (:export
14 #:message-chat-id 15 #:message-chat-id
15 #:message-thread-id 16 #:message-thread-id
@@ -163,17 +164,17 @@
163 ;; (reply-markup (or inline-keyboard-markup null) nil) 164 ;; (reply-markup (or inline-keyboard-markup null) nil)
164 ) ; lint:suppress closing-parens-same-line whitespace-before-close-paren 165 ) ; lint:suppress closing-parens-same-line whitespace-before-close-paren
165 166
166(declaim (inline message-id)) 167(-> message-id (message) integer)
167(defun message-id (msg) 168(defsubst message-id (msg)
168 "Better named version of `message-message-id'." 169 "Better named version of `message-message-id'."
169 (message-message-id msg)) 170 (message-message-id msg))
170 171
171(declaim (inline message-chat-id)) 172(-> message-chat-id (message) integer)
172(defun message-chat-id (msg) 173(defsubst message-chat-id (msg)
173 "Shortcut for (CHAT-ID (MESSAGE-CHAT MSG))." 174 "Shortcut for (CHAT-ID (MESSAGE-CHAT MSG))."
174 (chat-id (message-chat msg))) 175 (chat-id (message-chat msg)))
175 176
176(declaim (inline message-thread-id)) 177(-> message-thread-id (message) (or integer null))
177(defun message-thread-id (msg) 178(defsubst message-thread-id (msg)
178 "Better named version of `message-message-thread-id'." 179 "Better named version of `message-message-thread-id'."
179 (message-message-thread-id msg)) 180 (message-message-thread-id msg))
diff --git a/src/tg/method-macros.lisp b/src/tg/method-macros.lisp
index 0d33ffb..9ab9e89 100644
--- a/src/tg/method-macros.lisp
+++ b/src/tg/method-macros.lisp
@@ -5,10 +5,10 @@
5 (:use :c2cl :iterate) 5 (:use :c2cl :iterate)
6 (:import-from :alexandria :make-keyword :with-gensyms) 6 (:import-from :alexandria :make-keyword :with-gensyms)
7 (:import-from :com.dieggsy.f-string :enable-f-strings) 7 (:import-from :com.dieggsy.f-string :enable-f-strings)
8 (:import-from :serapeum :take) 8 (:import-from :serapeum :-> :take)
9 (:import-from :state) 9 (:import-from :state)
10 (:import-from :str) 10 (:import-from :str)
11 (:import-from :ukkoclot/src/transport :do-call) 11 (:import-from :ukkoclot/src/transport :do-call :http-method)
12 (:export :define-tg-method)) 12 (:export :define-tg-method))
13(in-package :ukkoclot/src/tg/method-macros) 13(in-package :ukkoclot/src/tg/method-macros)
14 14
@@ -21,6 +21,7 @@
21 (defparameter +unique+ (gensym)) 21 (defparameter +unique+ (gensym))
22 22
23 ;; TODO: Fix optional-and-key ! 23 ;; TODO: Fix optional-and-key !
24 (-> make-param (symbol t &optional t &key (:skip-if-default boolean)) param)
24 (defun make-param (name type ; lint:suppress avoid-optional-and-key 25 (defun make-param (name type ; lint:suppress avoid-optional-and-key
25 &optional (default +unique+) 26 &optional (default +unique+)
26 &key (skip-if-default (not (eq default +unique+)))) 27 &key (skip-if-default (not (eq default +unique+))))
@@ -32,26 +33,34 @@
32 :default default 33 :default default
33 :skip-if-default skip-if-default))) 34 :skip-if-default skip-if-default)))
34 35
36 ;; TODO: list-of-params, list-of-param-specs
37 (-> parse-param-specs (list) list)
35 (defun parse-param-specs (param-specs) 38 (defun parse-param-specs (param-specs)
36 (iter (for param-spec in param-specs) 39 (iter (for param-spec in param-specs)
37 (collect (apply #'make-param param-spec)))) 40 (collect (apply #'make-param param-spec))))
38 41
42 (-> path-from-name (symbol) string)
39 (defun path-from-name (name) 43 (defun path-from-name (name)
40 (let ((str (str:camel-case name))) 44 (let ((str (str:camel-case name)))
41 (if (str:ends-with-p "%" str :ignore-case nil) 45 (if (str:ends-with-p "%" str :ignore-case nil)
42 (take (- (length str) 1) str) 46 (take (- (length str) 1) str)
43 str))) 47 str)))
44 48
49 (-> emit-append-to-args (param symbol) list)
45 (defun emit-append-to-args (param args) 50 (defun emit-append-to-args (param args)
46 `(setf ,args (acons ',(param-name param) ,(param-name param) ,args))) 51 `(setf ,args (acons ',(param-name param) ,(param-name param) ,args)))
47 52
53 (-> emit-arg-type (param) list)
48 (defun emit-arg-type (param) 54 (defun emit-arg-type (param)
49 `(,(make-keyword (param-name param)) 55 `(,(make-keyword (param-name param))
50 ,(param-type param))) 56 ,(param-type param)))
51 57
58 (-> emit-defun-arg (param) list)
52 (defun emit-defun-arg (param) 59 (defun emit-defun-arg (param)
53 `(,(param-name param) ,(param-default param))) 60 `(,(param-name param) ,(param-default param)))
54 61
62 ;; TODO: list-of-params
63 (-> emit-defun (symbol t list http-method) list)
55 (defun emit-defun (name return-type params method) 64 (defun emit-defun (name return-type params method)
56 (with-gensyms (args) 65 (with-gensyms (args)
57 `(defun ,name (&key ,@(iter (for param in params) ; lint:suppress lambda-list-invalid 66 `(defun ,name (&key ,@(iter (for param in params) ; lint:suppress lambda-list-invalid
@@ -65,11 +74,13 @@
65 (emit-append-to-args param args)))) 74 (emit-append-to-args param args))))
66 (do-call ,method ,(path-from-name name) ',return-type ,args))))) 75 (do-call ,method ,(path-from-name name) ',return-type ,args)))))
67 76
77 ;; TODO: list-of-params
78 (-> emit-ftype (symbol t list) list)
68 (defun emit-ftype (name return-type params) 79 (defun emit-ftype (name return-type params)
69 `(declaim (ftype (function (&key ,@(iter (for param in params) 80 `(-> ,name
70 (collect (emit-arg-type param)))) 81 (&key ,@(iter (for param in params)
71 ,return-type) 82 (collect (emit-arg-type param))))
72 ,name)))) 83 ,return-type)))
73 84
74(defmacro define-tg-method ((name type &optional (method :POST)) 85(defmacro define-tg-method ((name type &optional (method :POST))
75 &body param-specs) 86 &body param-specs)
diff --git a/src/tg/send-animation.lisp b/src/tg/send-animation.lisp
index 560b331..acddb21 100644
--- a/src/tg/send-animation.lisp
+++ b/src/tg/send-animation.lisp
@@ -2,6 +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/src/tg/send-animation 3(defpackage :ukkoclot/src/tg/send-animation
4 (:documentation "sendAnimation Telegram method") 4 (:documentation "sendAnimation Telegram method")
5 (:import-from :serapeum :->)
5 (:use 6 (:use
6 :c2cl 7 :c2cl
7 :ukkoclot/src/tg/force-reply 8 :ukkoclot/src/tg/force-reply
@@ -41,6 +42,14 @@
41 (reply-markup (or inline-keyboard-markup reply-keyboard-markup reply-keyboard-remove force-reply null) nil)) 42 (reply-markup (or inline-keyboard-markup reply-keyboard-markup reply-keyboard-remove force-reply null) nil))
42 43
43;; TODO: Some kind of caching for files? 44;; TODO: Some kind of caching for files?
45(-> reply-animation (message
46 pathname
47 &key
48 (:allow-sending-without-reply boolean)
49 (:text (or string null))
50 (:parse-mode (or parse-mode null))
51 (:caption-above boolean))
52 message)
44(defun reply-animation (msg animation &key allow-sending-without-reply text parse-mode caption-above) 53(defun reply-animation (msg animation &key allow-sending-without-reply text parse-mode caption-above)
45 "Shortcut to reply to a given MSG with an animation." 54 "Shortcut to reply to a given MSG with an animation."
46 (send-animation :chat-id (message-chat-id msg) 55 (send-animation :chat-id (message-chat-id msg)
diff --git a/src/tg/send-message.lisp b/src/tg/send-message.lisp
index befecbe..7c24f87 100644
--- a/src/tg/send-message.lisp
+++ b/src/tg/send-message.lisp
@@ -2,6 +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/src/tg/send-message 3(defpackage :ukkoclot/src/tg/send-message
4 (:documentation "sendMessage Telegram method") 4 (:documentation "sendMessage Telegram method")
5 (:import-from :serapeum :->)
5 (:use 6 (:use
6 :c2cl 7 :c2cl
7 :ukkoclot/src/tg/force-reply 8 :ukkoclot/src/tg/force-reply
@@ -31,6 +32,11 @@
31 (reply-parameters (or reply-parameters null) nil) 32 (reply-parameters (or reply-parameters null) nil)
32 (reply-markup (or inline-keyboard-markup reply-keyboard-markup reply-keyboard-remove force-reply null) nil)) 33 (reply-markup (or inline-keyboard-markup reply-keyboard-markup reply-keyboard-remove force-reply null) nil))
33 34
35(-> reply-message (message
36 string
37 &key
38 (:parse-mode (or parse-mode null))
39 (:allow-sending-without-reply boolean)))
34(defun reply-message (msg text &key parse-mode allow-sending-without-reply) 40(defun reply-message (msg text &key parse-mode allow-sending-without-reply)
35 "Shortcut to reply to a given MSG." 41 "Shortcut to reply to a given MSG."
36 (send-message :chat-id (message-chat-id msg) 42 (send-message :chat-id (message-chat-id msg)
diff --git a/src/tg/set-my-name.lisp b/src/tg/set-my-name.lisp
index 2b3869a..f0b5c5f 100644
--- a/src/tg/set-my-name.lisp
+++ b/src/tg/set-my-name.lisp
@@ -2,6 +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/src/tg/set-my-name 3(defpackage :ukkoclot/src/tg/set-my-name
4 (:documentation "setMyName Telegram method.") 4 (:documentation "setMyName Telegram method.")
5 (:import-from :serapeum :->)
5 (:use :c2cl :ukkoclot/src/tg/bot-name :ukkoclot/src/tg/get-my-name :ukkoclot/src/tg/method-macros) 6 (:use :c2cl :ukkoclot/src/tg/bot-name :ukkoclot/src/tg/get-my-name :ukkoclot/src/tg/method-macros)
6 (:export :set-my-name)) 7 (:export :set-my-name))
7(in-package :ukkoclot/src/tg/set-my-name) 8(in-package :ukkoclot/src/tg/set-my-name)
@@ -10,6 +11,9 @@
10 (name (or string null) nil) 11 (name (or string null) nil)
11 (language-code (or string null) nil)) 12 (language-code (or string null) nil))
12 13
14(-> set-my-name
15 (&key (:name (or string null)) (:language-code (or string null)))
16 boolean)
13(defun set-my-name (&key (name nil) (language-code nil)) 17(defun set-my-name (&key (name nil) (language-code nil))
14 "setMyName Telegram method. 18 "setMyName Telegram method.
15 19
diff --git a/src/tg/type-macros.lisp b/src/tg/type-macros.lisp
index ea35f48..02437ec 100644
--- a/src/tg/type-macros.lisp
+++ b/src/tg/type-macros.lisp
@@ -5,6 +5,7 @@
5 (:use :c2cl :iterate) 5 (:use :c2cl :iterate)
6 (:import-from :alexandria :make-keyword :symbolicate :with-gensyms) 6 (:import-from :alexandria :make-keyword :symbolicate :with-gensyms)
7 (:import-from :com.dieggsy.f-string :enable-f-strings) 7 (:import-from :com.dieggsy.f-string :enable-f-strings)
8 (:import-from :serapeum :->)
8 (:import-from :str) 9 (:import-from :str)
9 (:import-from :ukkoclot/src/serializing :parse-value) 10 (:import-from :ukkoclot/src/serializing :parse-value)
10 (:import-from :ukkoclot/src/hash-tables :gethash-lazy) 11 (:import-from :ukkoclot/src/hash-tables :gethash-lazy)
@@ -22,6 +23,7 @@
22 (defparameter +unique+ (gensym)) 23 (defparameter +unique+ (gensym))
23 24
24 ;; TODO: Fix optional-and-key ! 25 ;; TODO: Fix optional-and-key !
26 (-> make-field (symbol t &optional t &key (:skip-if-default boolean)) field)
25 (defun make-field (name type ; lint:suppress avoid-optional-and-key 27 (defun make-field (name type ; lint:suppress avoid-optional-and-key
26 &optional (default +unique+) 28 &optional (default +unique+)
27 &key (skip-if-default (not (eq default +unique+)))) 29 &key (skip-if-default (not (eq default +unique+))))
@@ -33,28 +35,36 @@
33 :default default 35 :default default
34 :skip-if-default skip-if-default))) 36 :skip-if-default skip-if-default)))
35 37
38 (-> type-constructor (symbol) symbol)
36 (defun type-constructor (name) 39 (defun type-constructor (name)
37 (symbolicate "MAKE-" name)) 40 (symbolicate "MAKE-" name))
38 41
42 (-> field-accessor (symbol field) symbol)
39 (defun field-accessor (name field) 43 (defun field-accessor (name field)
40 (symbolicate name "-" (field-name field))) 44 (symbolicate name "-" (field-name field)))
41 45
46 (-> field-hash-key (field) string)
42 (defun field-hash-key (field) 47 (defun field-hash-key (field)
43 (str:snake-case (field-name field))) 48 (str:snake-case (field-name field)))
44 49
50 (-> field-keyword (field) keyword)
45 (defun field-keyword (field) 51 (defun field-keyword (field)
46 (make-keyword (field-name field))) 52 (make-keyword (field-name field)))
47 53
54 ;; TODO: list-of-fields, list-of-field-specs
55 (-> parse-field-specs (list) list)
48 (defun parse-field-specs (field-specs) 56 (defun parse-field-specs (field-specs)
49 (iter (for field-spec in field-specs) 57 (iter (for field-spec in field-specs)
50 (collect (apply #'make-field field-spec)))) 58 (collect (apply #'make-field field-spec))))
51 59
52 (defun emit-append-to-pprint-args (field value pprint-args) 60 (-> emit-coerced-field (field (or symbol list)) list)
53 `(setf ,pprint-args (list* ',(field-name field) ,value ,pprint-args)))
54
55 (defun emit-coerced-field (field value) 61 (defun emit-coerced-field (field value)
56 `(list ,(field-hash-key field) ,value ',(field-type field))) 62 `(list ,(field-hash-key field) ,value ',(field-type field)))
57 63
64 ;; TODO: list-of-fields
65 (-> emit-collect-nondefault-fields
66 (symbol list symbol (function (field (or symbol list)) list))
67 list)
58 (defun emit-collect-nondefault-fields (name fields obj collector) 68 (defun emit-collect-nondefault-fields (name fields obj collector)
59 (with-gensyms (value) 69 (with-gensyms (value)
60 (iter (for field in (reverse fields)) 70 (iter (for field in (reverse fields))
@@ -65,12 +75,16 @@
65 ,(funcall collector field value))) 75 ,(funcall collector field value)))
66 (funcall collector field (list (field-accessor name field) obj))))))) 76 (funcall collector field (list (field-accessor name field) obj)))))))
67 77
78 (-> emit-constructor-args (field) list)
68 (defun emit-constructor-args (field) 79 (defun emit-constructor-args (field)
69 `(,(field-keyword field) ,(field-name field))) 80 `(,(field-keyword field) ,(field-name field)))
70 81
82 (-> emit-gethash (field symbol) list)
71 (defun emit-gethash (field source) 83 (defun emit-gethash (field source)
72 `(gethash-lazy ,(field-hash-key field) ,source ,(field-default field))) 84 `(gethash-lazy ,(field-hash-key field) ,source ,(field-default field)))
73 85
86 ;; TODO: list-of-fields
87 (-> emit-jzon-coerced-fields (symbol list) list)
74 (defun emit-jzon-coerced-fields (name fields) 88 (defun emit-jzon-coerced-fields (name fields)
75 (with-gensyms (obj result) 89 (with-gensyms (obj result)
76 `(defmethod jzon:coerced-fields ((,obj ,name)) 90 `(defmethod jzon:coerced-fields ((,obj ,name))
@@ -81,10 +95,13 @@
81 `(push ,(emit-coerced-field field value) ,result))) 95 `(push ,(emit-coerced-field field value) ,result)))
82 ,result)))) 96 ,result))))
83 97
98 (-> emit-let-gethash (field symbol) list)
84 (defun emit-let-gethash (field source) 99 (defun emit-let-gethash (field source)
85 `(,(field-name field) 100 `(,(field-name field)
86 (parse-value ',(field-type field) ,(emit-gethash field source)))) 101 (parse-value ',(field-type field) ,(emit-gethash field source))))
87 102
103 ;; TODO: list-of-fields
104 (-> emit-parse-value (symbol list) list)
88 (defun emit-parse-value (name fields) 105 (defun emit-parse-value (name fields)
89 (with-gensyms (source type) 106 (with-gensyms (source type)
90 `(defmethod parse-value ((,type (eql ',name)) ,source) 107 `(defmethod parse-value ((,type (eql ',name)) ,source)
@@ -94,6 +111,8 @@
94 ,@(iter (for field in fields) 111 ,@(iter (for field in fields)
95 (appending (emit-constructor-args field)))))))) 112 (appending (emit-constructor-args field))))))))
96 113
114 ;; TODO: list-of-fields
115 (-> emit-printer (symbol symbol list) list)
97 (defun emit-printer (name printer-name fields) 116 (defun emit-printer (name printer-name fields)
98 (with-gensyms (depth obj pprint-args stream) 117 (with-gensyms (depth obj pprint-args stream)
99 `(defun ,printer-name (,obj ,stream ,depth) ; lint:suppress lambda-list-invalid 118 `(defun ,printer-name (,obj ,stream ,depth) ; lint:suppress lambda-list-invalid
@@ -105,11 +124,14 @@
105 `(setf ,pprint-args (list* ',(field-name field) ,value ,pprint-args)))) 124 `(setf ,pprint-args (list* ',(field-name field) ,value ,pprint-args))))
106 (format ,stream "~A~<[~;~@{~_~1I~W = ~W~^, ~}~;]~:>" ',name ,pprint-args))))) 125 (format ,stream "~A~<[~;~@{~_~1I~W = ~W~^, ~}~;]~:>" ',name ,pprint-args)))))
107 126
127 ;; TODO: list-of-fields
128 (-> emit-struct (symbol symbol list) list)
108 (defun emit-struct (name printer-name fields) 129 (defun emit-struct (name printer-name fields)
109 `(defstruct (,name (:print-function ,printer-name)) 130 `(defstruct (,name (:print-function ,printer-name))
110 ,@(iter (for field in fields) 131 ,@(iter (for field in fields)
111 (collect (emit-struct-field field))))) 132 (collect (emit-struct-field field)))))
112 133
134 (-> emit-struct-field (field) list)
113 (defun emit-struct-field (field) 135 (defun emit-struct-field (field)
114 `(,(field-name field) ,(field-default field) :type ,(field-type field)))) 136 `(,(field-name field) ,(field-default field) :type ,(field-type field))))
115 137
diff --git a/src/tg/user.lisp b/src/tg/user.lisp
index 0768d12..aefdeeb 100644
--- a/src/tg/user.lisp
+++ b/src/tg/user.lisp
@@ -3,6 +3,8 @@
3(defpackage :ukkoclot/src/tg/user 3(defpackage :ukkoclot/src/tg/user
4 (:documentation "User Telegram type") 4 (:documentation "User Telegram type")
5 (:use :c2cl :ukkoclot/src/tg/type-macros) 5 (:use :c2cl :ukkoclot/src/tg/type-macros)
6 (:import-from :serapeum :->)
7 (:import-from :ukkoclot/src/streams :with-format-like-stream)
6 (:import-from :ukkoclot/src/strings :escape-xml) 8 (:import-from :ukkoclot/src/strings :escape-xml)
7 (:export 9 (:export
8 #:user 10 #:user
@@ -39,26 +41,19 @@
39 (supports-inline-queries boolean nil) 41 (supports-inline-queries boolean nil)
40 (can-connect-to-business boolean nil)) 42 (can-connect-to-business boolean nil))
41 43
42(defun user-format-name% (user out) 44(-> user-format-name (user &optional (or stream boolean)) (or string null))
43 "Format the USER's name in a nice way to stream OUT." 45(defun user-format-name (user &optional out-spec)
44 (format out "<a href=\"tg://user?id=~A\"><i>" (user-id user)) 46 "Format the `user''s name in a nice way."
45 (escape-xml (user-first-name user) out) 47 (with-format-like-stream (out out-spec)
46 (when (user-last-name user) 48 (format out "<a href=\"tg://user?id=~A\"><i>" (user-id user))
47 (write-char #\Space out) 49 (escape-xml (user-first-name user) out)
48 (escape-xml (user-last-name user) out)) 50 (when (user-last-name user)
49 (write-string "</i>" out) 51 (write-char #\Space out)
52 (escape-xml (user-last-name user) out))
53 (write-string "</i>" out)
50 54
51 (when (user-username user) 55 (when (user-username user)
52 (write-string " @" out) 56 (write-string " @" out)
53 (escape-xml (user-username user) out)) 57 (escape-xml (user-username user) out))
54 58
55 (format out "</a> [<code>~A</code>]" (user-id user))) 59 (format out "</a> [<code>~A</code>]" (user-id user))))
56
57(defun user-format-name (user &optional out)
58 "Format the USER's name in a nice way to stream OUT.
59
60If OUT is `nil', return the formatted name as a string instead."
61 (if out
62 (user-format-name% user out)
63 (with-output-to-string (stream)
64 (user-format-name% user stream))))