An open API service indexing awesome lists of open source software.

https://github.com/aartaka/cl-telegram-bot-auto-api

Auto-generated Telegram Bot API bindings for Common Lisp.
https://github.com/aartaka/cl-telegram-bot-auto-api

Last synced: about 1 year ago
JSON representation

Auto-generated Telegram Bot API bindings for Common Lisp.

Awesome Lists containing this project

README

          

#+TITLE:cl-telegram-bot-auto-api — automatically-generated Telegam Bot API bindings for Common Lisp

This library aims to make Telegram bots writing easy on the part of making sure all the bindings are available and up-to-date. It stems from the problem one has with (otherwise perfect) [[https://github.com/40ants/cl-telegram-bot][cl-telegram-bot]]—one has to add lots of methods to even make one's bot ideas tested. This library solves the problem of having to write Telegram Bot API bindings by hand—all the classes and methods are generated automatically at load-time from [[https://github.com/rockneurotiko/telegram_api_json][telegram_api_json]] JSON files.

* Getting Started
** Installing the library
Git-cloning (recursively)
#+begin_src sh
git clone --recursive https://github.com/aartaka/cl-telegram-bot-auto-api.git
# update the telegram_api_json submodule in case it's outdated
# (don't forget to open an issue if that's the case)
git submodule update -- telegram_api_json
#+end_src
and ASDF-loading
#+begin_src lisp
(asdf:load-system :cl-telegram-bot-auto-api)
#+end_src
should be enough to get started with this library, given that you have
- Dexador,
- Quri,
- NJSON,
- Bordeaux Threads,
- Alexandria,
- and Serapeum installed.

** Dispatching ~on~ events
Let's try making a simple echo bot, as in [[https://github.com/40ants/cl-telegram-bot][cl-telegram-bot README]]. Having a chat with BotFather and getting an access token is implied. Echo bot should respond to incoming messages with the same text they contain. We can use the ~tga:copy-mesage~ for that just fine:
#+begin_src lisp
(defmethod tga:on ((message tga:message))
"Respond to the message with the same text it contains (actually using `tga:copy-message')."
(tga:copy-message (tga:id (tga:chat message)) (tga:id (tga:chat message)) (tga:message-id message)))
#+end_src
or we can rely on a lower-level things, like ~message~ slots (note that you'd better use the accessors instead of ~slot-value~ when dealing with ~cl-telegram-bot-auto-api~ objects, because the accessors do lots of intuitive parsing of otherwise raw-ish objects these classes contain):
#+begin_src lisp
(defmethod tga:on ((message tga:message))
"Respond to the MESSAGE with the same text it contains."
(tga:send-message (tga:id (tga:chat message)) (tga:text message)))
#+end_src

As you can see, ~tga:on~ is the main entry point for the (inherently event-driven) Telegram API (as implemented in this library). It's on only restricted to ~message~ processing, the ~update~ slots (and eponymous classes) it processes are:
- ~edited-message~,
- ~channel-post~,
- ~edited-channel-post~,
- ~inline-query~,
- ~chosen-inline-result~,
- ~callback-query~,
- ~shipping-query~,
- ~pre-checkout-query~,
- ~poll~,
- ~poll-answer~,
- ~chat-member~,
- ~my-chat-member~,
- ~chat-join-request~,
- ~bot-command~,
- and others that ~update~ may contain in the future you're loading this library in.

An additional use for ~on~ that you may be interested in is... error handling. In case something goes wrong (~error~-level wrong!), ~on~ is called with the error instance in the ~handler-bind~ context of this error. So, if you want to invoke some restarts or do something fancy with the error, ~on~ is the place to do so! For example, if something goes wrong in our echo bot and is continuable (all the errors cl-telegram-auto-api generates are continuable, just in case), we can rest assured things will be fine with this method:

#+begin_src lisp
(defmethod tga:on ((object error))
"Invoke CONTINUE restart of OBJECT, if found."
(continue))
#+end_src

** Finally, starting the bot
~tga:start~ is the ultimate entry point to launch your bot with. Simply pass the token and rock! (you can also pass the token and a ~:name~, so that the newly created bot thread is easier to find by name...)
#+begin_src lisp
(tga:start "YOUR-TOKEN" :name "My echo bot")
#+end_src

This:
- Creates a new thread.
- Binds ~tga:*token*~ to the one you provided for all the methods in this thread.
- Calls ~tga:get-updates~ repetitively.
- And then invokes either ~:update-callback~ or ~tga:on~ with each of the fetched updates.
- In case of errors, calls either ~:error-callback~ or ~tga:on~ as the error handler.

* Design Decisions
The biggest goals/ideals for this library were:
- Not bothering with API bindings :: getting to writing the actual bot sooner, not having to care about up-to-date API bindings and contributing to someone else's library.
- Being flexible to API changes :: no matter when you load the library (even if Telegram API has a version 103 by then), it should load just fine with all the available API methods, given that the JSON it's parsed from is the same. I mean, that's a lot of "if"-s, but much less that with the hand-written bindings that tend to go obsolete the moment they are published.
- Being flexible to one's style :: This library is a terribly thin wrapper, so it is more likely to fit with your programming style than bigger and more opinionated libraries.
- In particular, ~tga:on~, this universal processor for everything, may be totally ignored, if you provide ~tga:start~ with ~:update-callback~ and ~:error-callback~ arguments and do your work there.
- You don't need to define a class for every bot: simply call ~tga:start~ with different tokens, and it will spawn separate threads with bot-specific data. Then simply ~bt:destroy-thread~ the ones you no longer need, and you're done!
- Being image-based and lispy :: this library source code is /not/ good for understanding what it does, because all the matter is hidden behind code-generating macros. ~asdf:load-system~ it, ~describe~ the symbols you see, read the ~documentation~ of the classes and functions it exposes. Use the facilities Lisp provides to interact with this library and understand what is there inside it.
- While this library is implied for interactive REPL use, no one forbids you from compiling a binary calling ~tga:start~ in its entry point. See the "Being flexible to one's style" point :)

* Helpers
Even though providing the full-blown library for immediate bot writing is explicitly not a goal, here are some small helpers that can ease your bot writing and are not likely to ever break, even with automated API generation:
- Passing objects to method by value :: It's not cool to do ~tga:id~, ~tga:update-id~, ~tga:message-id~ every time you want to ~tga:send-message~ or do something else with several objects that you need to pass by ID. No more! Objects that have an ~tga:id~ method will be automatically turned into respective IDs when passed to methods that accept string/integer IDs instead of objects. So you can easily do:
#+begin_src lisp
(defmethod tga:on ((message tga:message))
(tga:copy-message (tga:chat message) ; No ID here.
(tga:chat message) ; And here!
message)) ; And here too!!!
#+end_src
- ~tga:id~ :: This enables the previous point: ~tga:id~ applies to every object semantically having an ID (be it ~update-id~, ~message-id~ etc. in Telegram) and returns the most sensible ID for it. No need to scour the docs for this-exact-slot-name-for-ID, just use ~tga:id~!
- ~tga:command~ :: Command parsing can be hard, especially when there are bot-mentioned commands and some complex text following them. ~tga:command~ (initially just a slot reader for ~bot-command~ class) allows you to get the command name and the remaining text for ~update~ or ~message~ objects, just as a convenience for easy command parsing/dispatch. Shamelessly stolen from [[https://github.com/40ants/cl-telegram-bot][cl-telegram-bot]] as a feature worth having in every Telegram Bot API library!