Skip to content

Commit

Permalink
Added support for global commands, available at any state.
Browse files Browse the repository at this point in the history
  • Loading branch information
svetlyak40wt committed Nov 10, 2024
1 parent 1063572 commit a5c3745
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 46 deletions.
10 changes: 10 additions & 0 deletions cl-telegram-bot2.asd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
:in-order-to ((test-op (test-op "cl-telegram-bot2-tests"))))


(defsystem "cl-telegram-bot2/deps"
:description "Utility system to load non-package-inferred systems using package-inferred imports."
:author "Alexander Artemenko <[email protected]>"
:license "MIT"
:homepage "https://40ants.com/cl-telegram-bot/"
:source-control (:git "https://github.com/40ants/cl-telegram-bot")
:bug-tracker "https://github.com/40ants/cl-telegram-bot/issues"
:depends-on ("njson/jzon"))


(asdf:register-system-packages "log4cl" '("LOG"))
(asdf:register-system-packages "utilities.print-items" '("PRINT-ITEMS"))
(asdf:register-system-packages "dexador" '("DEX"))
Expand Down
8 changes: 5 additions & 3 deletions examples/commands.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
(:import-from #:cl-telegram-bot2/api
#:message-message-id)
(:import-from #:cl-telegram-bot2/state-with-commands
#:global-command
#:command
#:state-with-commands-mixin)
(:import-from #:cl-telegram-bot2/generics
Expand Down Expand Up @@ -53,7 +54,8 @@ additional command /reverse, which will reverse any given text.")
(declare (ignore update))
(let ((trimmed (trim arg)))
(cond
((string= trimmed "")
((or (null trimmed)
(string= trimmed ""))
(reply "This command requires an argument."))
(t
(reply (reverse arg)))))
Expand All @@ -79,8 +81,8 @@ additional command /reverse, which will reverse any given text.")
(command "/reverse" 'on-reverse-command
:description "Switch to the prev state")))
:description "Switch to the next state")
(command "/help" 'on-help-command
:description "Show information about bot's commands.")))))
(global-command "/help" 'on-help-command
:description "Show information about bot's commands.")))))


(defvar *bot* nil)
Expand Down
6 changes: 3 additions & 3 deletions v2/bot.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
(:import-from #:sento.actor-system)
(:import-from #:cl-telegram-bot2/vars
#:*current-bot*)
(:import-from #:cl-telegram-bot2/state
#:state)
(:import-from #:cl-telegram-bot2/states/base
#:base-state)
(:export
#:defbot))
(in-package #:cl-telegram-bot2/bot)
Expand Down Expand Up @@ -58,7 +58,7 @@
(initial-state-class :initarg :initial-state-class
:initform (required-argument "Initial state is required argument.")
:type (or symbol
state)
base-state)
:reader initial-state-class)))


Expand Down
1 change: 1 addition & 0 deletions v2/generics.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
(:use #:cl)
(:import-from #:cl-telegram-bot2/api
#:pre-checkout-query)
(:import-from #:log)
(:export
#:process
#:on-state-activation
Expand Down
5 changes: 3 additions & 2 deletions v2/spec.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
#:curry
#:make-keyword
#:hash-table-alist)
(:import-from #:njson/jzon)
(:import-from #:cl-telegram-bot2/deps)
(:import-from #:njson
#:jget)
(:import-from #:cl-json
#:simplified-camel-case-to-lisp)
(:import-from #:str
#:param-case)
(:import-from #:cl-telegram-bot2/errors
#:telegram-error))
#:telegram-error)
(:import-from #:quri))
(in-package #:cl-telegram-bot2/spec)


Expand Down
113 changes: 86 additions & 27 deletions v2/state-with-commands.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#:process
#:on-state-activation)
(:import-from #:alexandria
#:flatten
#:length=
#:required-argument)
(:import-from #:serapeum
#:->
#:length<
#:soft-list-of)
(:import-from #:cl-telegram-bot2/api
Expand All @@ -24,21 +26,37 @@
#:*current-bot*
#:*current-chat*
#:*current-user*)
(:import-from #:cl-telegram-bot2/workflow
#:workflow-blocks
#:workflow-block)
(:import-from #:sento.actor
#:*state*)
(:import-from #:cl-telegram-bot2/bot
#:bot-name)
(:export #:state-with-commands-mixin
#:state-commands
#:command))
#:command
#:global-command))
(in-package #:cl-telegram-bot2/state-with-commands)


(defclass command ()
(deftype command-handler ()
'(or
symbol
workflow-block
workflow-blocks))


(defclass base-command ()
((name :initarg :name
:initform (required-argument ":COMMAND is required argument.")
:type string
:documentation "A command name like \"/start\" or \"/help\"."
:reader command-name)
(handler :initarg :handler
:initform (required-argument ":HANDLER is required argument.")
:documentation "A callable object of one argument or an object to return from PROCESS generic-function."
:type command-handler
:documentation "An fbound symbol of two arguments (command-argument update-obj) or a workflow object to return from PROCESS generic-function."
:reader command-handler)
(description :initarg :description
:initform nil
Expand All @@ -47,12 +65,36 @@
:reader command-description)))


(defclass command (base-command)
()
(:documentation "This type of command is available only in the state where it is defined."))


(defclass global-command (command)
()
(:documentation "This command will be available during in all bot states."))


(-> command (string command-handler
&key (:description (or null string)))
(values command &optional))

(defun command (name handler &key description)
(make-instance 'command
:name name
:handler handler
:description description))

(-> global-command (string command-handler
&key (:description (or null string)))
(values global-command &optional))

(defun global-command (name handler &key description)
(make-instance 'global-command
:name name
:handler handler
:description description))


(defclass state-with-commands-mixin ()
((commands :initarg :commands
Expand All @@ -61,6 +103,12 @@
:reader state-commands)))


(defun state-global-commands (state)
(loop for command in (state-commands state)
when (typep command 'global-command)
collect command))


(defun send-commands (state)
(let ((chat *current-chat*)
(user *current-user*))
Expand All @@ -69,26 +117,32 @@
;; It returns error: can't change commands in channel chats
(not (string= (chat-type chat)
"channel")))
(set-my-commands
(loop for command in (state-commands state)
collect (make-instance 'bot-command
:command (command-name command)
:description (or (command-description command)
(command-name command))))
:scope (cond
((and chat
(string-equal
(chat-type chat)
"private"))
(make-instance 'bot-command-scope-chat
:type "chat"
:chat-id (chat-id chat)))
((and chat
user)
(make-instance 'bot-command-scope-chat-member
:type "chat_member"
:chat-id (chat-id chat)
:user-id (user-id user))))))))

(loop for command in (append (state-commands state)
(flatten (mapcar #'state-global-commands
(rest *state*))))
for bot-command = (make-instance 'bot-command
:command (command-name command)
:description (or (command-description command)
(command-name command)))
collect bot-command into bot-commands
finally
(when bot-commands
(set-my-commands bot-commands
:scope (cond
((and chat
(string-equal
(chat-type chat)
"private"))
(make-instance 'bot-command-scope-chat
:type "chat"
:chat-id (chat-id chat)))
((and chat
user)
(make-instance 'bot-command-scope-chat-member
:type "chat_member"
:chat-id (chat-id chat)
:user-id (user-id user))))))))))


(defmethod on-state-activation :before ((state state-with-commands-mixin))
Expand Down Expand Up @@ -141,18 +195,23 @@
(cond
((and command-name
(or (null bot-name)
(string-equal (cl-telegram-bot2/bot::bot-name)
(string-equal (bot-name)
bot-name)))
(loop for command in (state-commands state)
(loop for command in (append (state-commands state)
;; Here we need to search previos states
;; on a stack to support their global
;; commands in the current state:
(flatten (mapcar #'state-global-commands
(rest *state*))))
when (string-equal command-name
(command-name command))
do (return
(let ((handler (command-handler command)))
(typecase handler
((or symbol function)
(funcall handler rest-text update))
(funcall handler rest-text update))
(t
handler))))
handler))))
finally (return
(call-next-method))))
(t
Expand Down
12 changes: 1 addition & 11 deletions v2/states/base.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,12 @@
#:pretty-print-hash-table
#:dict
#:soft-list-of)
(:import-from #:cl-telegram-bot2/action
#:action)
(:import-from #:cl-telegram-bot2/generics
#:process
#:on-state-activation)
(:import-from #:cl-telegram-bot2/term/back
#:back)
(:import-from #:sento.actor
#:*state*)
(:import-from #:print-items
#:print-items
#:print-items-mixin)
(:import-from #:cl-telegram-bot2/state-with-commands
#:state-with-commands-mixin)
(:export #:state
#:result-var
(:export #:result-var
#:state-result-var
#:clear-state-result-vars
#:base-state
Expand Down

0 comments on commit a5c3745

Please sign in to comment.