diff options
Diffstat (limited to 'src/tg')
| -rw-r--r-- | src/tg/delete-message.lisp | 7 | ||||
| -rw-r--r-- | src/tg/get-me.lisp | 4 | ||||
| -rw-r--r-- | src/tg/message-entity.lisp | 3 | ||||
| -rw-r--r-- | src/tg/message.lisp | 13 | ||||
| -rw-r--r-- | src/tg/method-macros.lisp | 23 | ||||
| -rw-r--r-- | src/tg/send-animation.lisp | 9 | ||||
| -rw-r--r-- | src/tg/send-message.lisp | 6 | ||||
| -rw-r--r-- | src/tg/set-my-name.lisp | 4 | ||||
| -rw-r--r-- | src/tg/type-macros.lisp | 28 | ||||
| -rw-r--r-- | src/tg/user.lisp | 37 |
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 | |||
| 60 | If 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)))) | ||