Skip to content

Commit

Permalink
Merge pull request #24 from 40ants/auto-api
Browse files Browse the repository at this point in the history
Declarative framework version + Automatic API spec parser
  • Loading branch information
svetlyak40wt authored Dec 14, 2024
2 parents 68a2f17 + 2a47daa commit 28e516b
Show file tree
Hide file tree
Showing 61 changed files with 26,271 additions and 587 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
"uses": "40ants/setup-lisp@v4",
"with": {
"asdf-system": "cl-telegram-bot-docs",
"cache": "true"
"dynamic-space-size": "4gb",
"cache": "true",
"cache-suffix": "dyn-space"
}
},
{
Expand All @@ -45,4 +47,4 @@
]
}
}
}
}
1 change: 1 addition & 0 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"linter": {
"runs-on": "ubuntu-latest",
"env": {
"DYNAMIC_SPACE_SIZE": "4Gb",
"OS": "ubuntu-latest",
"QUICKLISP_DIST": "quicklisp",
"LISP": "sbcl-bin"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
.*.~undo-tree~
.DS_Store
*.fasl
.env
1 change: 1 addition & 0 deletions .local.el
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(with-temp-file "/proc/self/comm" (insert "emacs tel-bot"))
42 changes: 42 additions & 0 deletions Lakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#|-*- mode:lisp -*-|#

(push "~/projects/40ants-lake/" asdf:*central-registry*)
(push "~/projects/cl-mustache/" asdf:*central-registry*)

(ql:quickload :40ants-lake
:silent t)

(defpackage :lake.user
(:use :cl :lake)
(:import-from #:40ants-lake/utils
#:alias)
(:import-from #:40ants-lake/environment
#:load-env-file)
(:import-from #:40ants-lake/app
#:defapps
#:app)
(:import-from #:40ants-lake/component/webservice
#:webservice
#:proxy)
(:import-from #:40ants-lake/component/daemon
#:daemon)
(:import-from #:40ants-lake/env-val
#:env-val)
(:shadowing-import-from :lake
:directory))
(in-package :lake.user)


(when (probe-file ".local-config.lisp")
(load ".local-config.lisp"))


(load-env-file)


(let ((backend-port (env-val :dev 10120)))
(defapps
(app "cl-echo-bot"
:components (list (webservice (env-val :dev "cl-echo-bot.dev.40ants.com")
:routes (proxy "/"
backend-port))))))
2,895 changes: 2,341 additions & 554 deletions README.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions cl-telegram-bot-docs.asd
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
:bug-tracker "https://github.com/40ants/cl-telegram-bot/issues"
:pathname "docs"
:depends-on ("cl-telegram-bot"
"cl-telegram-bot2"
"cl-telegram-bot-media"
"cl-telegram-bot-docs/index"))
18 changes: 18 additions & 0 deletions cl-telegram-bot2-examples.asd
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#-asdf3.1 (error "cl-telegram-bot requires ASDF 3.1 because for lower versions pathname does not work for package-inferred systems.")
(defsystem "cl-telegram-bot2-examples"
:description "Telegram Bot API, based on sovietspaceship's work but mostly rewritten."
: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"
:class :40ants-asdf-system
:defsystem-depends-on ("40ants-asdf-system")
:pathname "examples"
:depends-on ("clack-handler-hunchentoot"
"cl-telegram-bot2-examples/calc"
"cl-telegram-bot2-examples/commands"
"cl-telegram-bot2-examples/gallery"
"cl-telegram-bot2-examples/payments"
"cl-telegram-bot2-examples/mini-app")
:in-order-to ((test-op (test-op "cl-telegram-bot2-tests"))))
34 changes: 34 additions & 0 deletions cl-telegram-bot2.asd
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#-asdf3.1 (error "cl-telegram-bot requires ASDF 3.1 because for lower versions pathname does not work for package-inferred systems.")
(defsystem "cl-telegram-bot2"
:description "Telegram Bot API, completely rewritten. Autogenerates code from JSON spec and adds high-level declarative DSL on top."
: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"
:class :40ants-asdf-system
:defsystem-depends-on ("40ants-asdf-system")
:pathname "v2"
:depends-on ("cl-telegram-bot2/api"
"cl-telegram-bot2/pipeline"
"cl-telegram-bot2/server")
: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 "bordeaux-threads" '("BORDEAUX-THREADS-2"))
(asdf:register-system-packages "log4cl" '("LOG"))
(asdf:register-system-packages "utilities.print-items" '("PRINT-ITEMS"))
(asdf:register-system-packages "dexador" '("DEX"))
(asdf:register-system-packages "sento" '("SENTO.ACTOR-SYSTEM"
"SENTO.ACTOR-CONTEXT"
"SENTO.ACTOR"))
16 changes: 16 additions & 0 deletions docs/changelog.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@
"REPL"
"CL-TELEGRAM-BOT/MESSAGE:REPLY"
"HTTP"))
(0.7.0 2024-12-14
"
Huge Changes
============
A new ASDF system, cl-telegram-bot2, has been added with a completely rewritten codebase.
This new approach separates the code into low-level and high-level components.
The low-level code is generated from the Telegram API specification and encompasses all the objects and methods available in the API.
In contrast, the high-level API allows you to define the bot's behavior in a declarative manner,
somewhat similar to how today's popular \"no-code\" bot builders operate.
However, we do it better because we have Common Lisp at our fingertips.
You can find more examples in the `examples` directory and in the CL-TELEGRAM-BOT-DOCS/TUTORIAL::@FIRST-BOT section.
")
(0.6.0 2024-10-15
"
Changed
Expand Down
79 changes: 60 additions & 19 deletions docs/index.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
#:defsection-copy)
(:import-from #:cl-telegram-bot-docs/changelog
#:@changelog)
(:import-from #:cl-telegram-bot-docs/states
#:@states-and-actions)
(:import-from #:docs-config
#:docs-config)
(:import-from #:40ants-doc/autodoc
#:defautodoc)
(:import-from #:cl-telegram-bot-docs/tutorial
#:@first-bot)
(:export #:@index
#:@readme
#:@changelog))
Expand All @@ -35,36 +39,53 @@
(list :theme
(find-symbol "40ANTS-THEME"
(find-package "40ANTS-DOC-THEME-40ANTS"))
:full-package-names nil))
:full-package-names nil
:root-sections '(@index
@first-bot
@states-and-actions
@api-v2
@v1)))


(defsection @index (:title "cl-telegram-bot - Telegram Bot API"
:ignore-words ("JSON"
"HTTP"
"HTTPS"
"MIME"
"CL"
"TODO"
(defsection @intro (:title "cl-telegram-bot - Telegram Bot API"
:ignore-words ("API"
"MIT"
"API"
"CLOS"
"REPL"
"GIT"))
"JSON"
"DSL"
"CLOS"))
(cl-telegram-bot system)
(cl-telegram-bot2 system)
"
[![](https://github-actions.40ants.com/40ants/cl-telegram-bot/matrix.svg?only=ci.run-tests)](https://github.com/40ants/cl-telegram-bot/actions)
![Quicklisp](http://quickdocs.org/badge/cl-telegram-bot.svg)
"

(@note section)
(@installation section)
(@quickstart section)
(@api section)
(@credits section))


(defsection-copy @readme @index)
(defsection-copy @index @intro)

(defsection-copy @readme @intro)


(defsection @note (:title "Important Note"
:ignore-words ("JSON"
"ASDF"
"DSL"
"API"))
"There are two different ASDF systems:
- cl-telegram-bot: the legacy system that is no longer supported.
- cl-telegram-bot2: the new version that generates classes and methods from the JSON specification and adds a declarative DSL on top.
The new version is still incomplete, and the high-level API may change in the future.
If you encounter any issues, please refer to ChangeLog.md.
Pull requests with features and fixes are welcome!
")

(defsection @installation (:title "Installation")
"""
You can install this library from Quicklisp, but you want to receive updates quickly, then install it from Ultralisp.org:
Expand Down Expand Up @@ -119,7 +140,7 @@ THE-BOT> (defmethod on-command ((bot echo-bot)
Now, stop for the minute, open your Telegram client, and create a new bot
using the BotFather bot:
![](images/create-a-bot.png)
![](asdf:cl-telegram-bot-media:images/create-a-bot.png)
When you've got token, return to the REPL and start our bot:
Expand All @@ -135,11 +156,11 @@ This will start a new thread for processing incoming messages.
Now, find your bot in the Telegram client:
![](images/choose-the-bot.png)
![](asdf:cl-telegram-bot-media:images/choose-the-bot.png)
And start communicating with him:
![](images/write-to-the-bot.png)
![](asdf:cl-telegram-bot-media:images/write-to-the-bot.png)
")

Expand All @@ -151,4 +172,24 @@ And start communicating with him:
")


(defautodoc @api (:system :cl-telegram-bot))
(defsection @v1 (:title "v1 (old API)"
:ignore-words ("CLOS"
"REPL"
"HTTPS"
"HTTP"
"JSON"
"MIME"
"CL"
"API"))
(@quickstart section)
(@api section))


(defautodoc @api (:title "API v1 (old version)"
:system :cl-telegram-bot))

(defautodoc @api-v2 (:title "API v2"
:system :cl-telegram-bot2
:ignore-words ("CL-TELEGRAM-BOT2/API:PRE-CHECKOUT-QUERY-INVOICE-PAYLOAD"
"API")
:ignore-packages ("cl-telegram-bot2/api")))
86 changes: 86 additions & 0 deletions docs/states.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
(uiop:define-package #:cl-telegram-bot-docs/states
(:use #:cl)
(:import-from #:40ants-doc
#:defsection)
(:import-from #:cl-telegram-bot2/state
#:state)
(:import-from #:cl-telegram-bot2/actions/send-text
#:send-text)
(:import-from #:cl-telegram-bot2/actions/send-photo
#:send-photo)
(:import-from #:cl-telegram-bot2/actions/send-invoice
#:send-invoice)
(:import-from #:cl-telegram-bot2/actions/edit-message-media
#:edit-message-media))
(in-package #:cl-telegram-bot-docs/states)


(defsection @states-and-actions (:title "States and Actions")
"
This framework makes it possible to define bot with all allowed state.
The state defines behaviour of the bot, the way it should respond to commands, updates and other events.
"
(@states section)
(@actions section)
(@event-processing section))


(defsection @states (:title "States")
"
There can be more than one handler for the event. We call these handlers \"Actions\".
An action should return a NIL or a new state. In latter case, the current bot's state will be changed to the new one and handlers for `on-activation` event will be called.
State is constructed using STATE function, which accepts handlers for different kinds of events. Here is simples state which greets a user when it start the chat and then reply with the same text:
```
(defun reply-with-same-text (update)
(reply (message-text
(update-message update)))
(values))
(state (send-text \"Hello, I'm the echo bot.\")
:on-update 'reply-with-same-text)
```
The first argument to STATE function is a handler for `on-activation` event. If you don't want to react on activation, you can pass NIL instead. The SEND-TEXT function returns an action instance. This way, we tell what bot should do, we use a declarative way to describe bot's behaviour.
The :ON-UPDATE argument specifies a handler for `on-update` event. This is the most generic event which occur when bot receives an update which wasn't processed by other event handlers. For this handler we are using a custom function bound to the symbol `reply-with-same-text`. The function accepts a single argument - update object. Use generic functions from `cl-telegram-bot2/api` package to work with this update object.
The reason why we only accept a special action object or a symbol but not a lambda function is because this way we'll be able to generate schemas of all states and transitions between them. Another reason is that it will be possible to redefine fbound function and use interactive approach to changing bot's behaviour.
See other support events in STATE function documentation.
")


(defsection @actions (:title "Actions")
"
Actions in cl-telegra-bot are small objects holding an information about what should be done on some event. Typically, you will want to reply with some text or send a photo.
Usually, actions are created using a function having the same name as action's class. Here are which actions are available:
- [SEND-TEXT][function]
- [SEND-PHOTO][function]
- [SEND-INVOICE][function]
- [EDIT-MESSAGE-MEDIA][function]
More actions will be added in future and you can create your own.
Also, a function bound symbol can be used instead an action object. Why do we require a symbol but not a function object? Because symbol has a name and it can be useful when we want to save bot's state or to render states graph.
")


(defsection @event-processing (:title "Event processing")
"
When some event occur, a corresponding generic function is called first on state object then on an action specified for this kind.
For example, if new update was received, then CL-TELEGRAM-BOT2/GENERICS:PROCESS generic-function will be called with current state as the first argument
and update object as the second argument. Then the method specified on state class will call the same CL-TELEGRAM-BOT2/GENERICS:PROCESS generic-function
on the object specified as :ON-UPDATE argument for the action. If action is a symbol, then it's function will be called with update object as a single argument in case if this function accepts one argument and without any arguments otherwise.
Action's method should return should return a new state object if it wants to change the current bot's state or NIL otherwise. If new state was returned, then `on-activate` event will be processed afterwards.
Instead of one action a list of actions can be specified as an event handler. In this case processing will stop on an action which returns a new state.
")
Loading

0 comments on commit 28e516b

Please sign in to comment.