ProHoster > Blog > Administration > Writing a telegram bot in R (part 3): How to add keyboard support to a bot
Writing a telegram bot in R (part 3): How to add keyboard support to a bot
This is the third article in the "Writing a telegram bot in R" series. In previous publications, we learned how to create a telegram bot, send messages through it, added commands and message filters to the bot. Therefore, before reading this article, I highly recommend that you familiarize yourself with previous, because here I will not stop on the previously described basics of bot building.
In this article, we will improve the usability of our bot by adding a keyboard that will make the bot's interface intuitive and easy to use.
All articles from the series "Writing a telegram bot in R"
If you are interested in data analysis, you might be interested in my telegram ΠΈ youtube channels. Most of the content of which is devoted to the R language.
What types of keyboards does the telegram bot support?
At the time of writing the article telegram.bot allows you to create two types of keyboards:
Reply - The main, regular keyboard, which is located under the message text input panel. Such a keyboard simply sends a text message to the bot, and as text it will send the text that is written on the button itself.
Inline - Keyboard tied to a specific bot message. This keyboard sends to the bot data associated with the pressed button, this data may differ from the text written on the button itself. And such buttons are processed through CallbackQueryHandler.
In order for the bot to open the keyboard, it is necessary when sending a message through the method sendMessage(), pass the previously created keyboard to the argument reply_markup.
Below we will look at a few examples.
Reply keyboard
As I wrote above, this is the main bot control keyboard.
An example of creating a Reply keyboard from official help
The above is an example from the official package help. telegram.bot. To create a keyboard, use the function ReplyKeyboardMarkup(), which in turn accepts a list of button lists that are created by the function KeyboardButton().
Why in ReplyKeyboardMarkup() it is necessary to transfer not just a list, but a list of lists? The thing is that you are passing the main list, and in it you set each row of buttons as separate lists, because Several buttons can be placed in one row.
Argument resize_keyboard allows you to automatically select the optimal size of the keyboard buttons, and the argument one_time_keyboard allows you to hide the keyboard after each button press.
Let's write a simple bot that will have 3 buttons:
Chat ID - Request the chat ID of the conversation with the bot
Run the code example above, after replacing 'YOUR BOT TOKEN' with the real token you received when you created the bot via Bot Father (I talked about creating a bot in first article).
After launch, give the bot the command /start, because that is what we defined to launch the keyboard.
If at the moment it is difficult for you to parse the given code example, with the creation of methods, filters and handlers, then you should return to the previous one. article, in which I described all this in detail.
We have created 4 methods:
start β Keyboard start
chat_id β Query chat id
my_name - Request your name
my_username - Request your login
To object MessageFilters added 3 message filters, by their text:
chat_id β Messages with text "Π§Π°Ρ ID"
name β Messages with text "ΠΠΎΡ ΠΈΠΌΡ"
username β Messages with text "ΠΠΎΠΉ Π»ΠΎΠ³ΠΈΠ½"
And we created 4 handlers that, according to the given commands and filters, will execute the specified methods.
In our case, we placed all the buttons under each other, but we can arrange them in one row by making changes to the list of button lists. Because one row inside the keyboard is created through a nested list of buttons, then in order to bring our buttons into one row, we need to rewrite part of the code for building the keyboard like this:
The keyboard is sent to the chat by the method sendMessage(), in the argument reply_markup.
bot$sendMessage(update$message$chat_id,
text = 'ΠΡΠ±Π΅ΡΠΈΡΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ',
reply_markup = RKM)
Inline keyboard
As I wrote above, the Inline keyboard is tied to a specific message. Working with it is somewhat more difficult than with the main keyboard.
Initially, you need to add a method to the bot to call the Inline keyboard.
To respond to an Inline button press, you can also use the bot method answerCallbackQuery(), which can display a notification in the telegram interface, to the user who clicked the Inline button.
The data sent from the Inline button is not text, so to process it, you need to create a special handler using the command CallbackQueryHandler().
The code for building an Inline keyboard, which is given in the official help of the package telegram.bot.
Code for building an Inline keyboard from the official help
It is necessary to build an Inline keyboard using the command InlineKeyboardMarkup(), in the same way as the Reply keyboard. IN InlineKeyboardMarkup() it is necessary to pass a list, Inline lists of buttons, each individual button is created by a function InlineKeyboardButton().
An inline button can either pass some data to the bot using an argument callback_data, or open any HTML page specified with the argument url.
The result will be a list in which each element is also a list of Inline buttons that need to be combined into one row.
Next, we will look at several examples of bots with Inline buttons.
An example of a simple bot with support for InLine buttons
To begin with, we will write a bot for express testing for covid-19. On command /test, it will send you a keyboard with two buttons, depending on the button pressed, it will send you a message with the results of your testing.
Run the code example above, after replacing 'YOUR BOT TOKEN' with the real token you received when you created the bot via Bot Father (I talked about creating a bot in first article).
Result:
We have created two methods:
test β To send chat to Inline keyboard
answer_cb β To process data sent from the keyboard.
The data that will be sent from each button is specified in the argument callback_data, when creating the button. You can get the data sent from the button using the construction update$callback_query$data, inside the method answer_cb.
To make the bot react to the Inline keyboard, the method answer_cb processed by a special handler: CallbackQueryHandler(answer_cb). Which launches the specified method on pressing the Inline button. Handler CallbackQueryHandler takes two arguments:
callback - Method to run
pattern - Filter by data that is attached to the button using an argument callback_data.
Accordingly, using the argument pattern we can write a separate method for pressing each button:
Run the code example above, after replacing 'YOUR BOT TOKEN' with the real token you received when you created the bot via Bot Father (I talked about creating a bot in first article).
Now we have written 2 separate methods i.e. one method at a time, under the click of each button, and used the argument pattern, when creating their handlers:
Method code ends answer_cb the team bot$answerCallbackQuery(callback_query_id = update$callback_query$id), which tells the bot that data has been received from the inline keyboard.
An example of a bot that reports the current weather for a selected city
Let's try to write a bot that requests weather data.
The logic of its work will be as follows. Initially by the team /start you call the main keyboard, in which there is only one button "Weather". By clicking on this button you get a message with an Inline keyboard to select the city for which you want to know the current weather. Choose one of the cities and get the current weather.
In this code example, we will be using a few additional packages:
httr - a package for working with HTTP requests, on the basis of which work with any API is built. In our case, we will use the free API openweathermap.org.
stringr - a package for working with text, in our case we will use it to generate a message about the weather in the selected city.
Code 4: Bot that reports the current weather for the selected city
Run the code example above, after replacing 'YOUR BOT TOKEN' with the real token you received when you created the bot via Bot Father (I talked about creating a bot in first article).
As a result, our bot will work like this:
Schematically, this bot can be drawn like this:
We have created 3 methods available inside our weather bot:
start β Launching the main keyboard of the bot
weather - Launch Inline keyboard to select a city
answer_cb - The main method that requests the weather in the API for a given city and sends it to the chat.
Method start we are running a team /start, which is implemented by the handler CommandHandler('start', start).
To run a method weather we have created a filter of the same name:
And we call this method with the following message handler: MessageHandler(weather, filters = MessageFilters$weather).
And finally, our main method answer_cb responds to pressing Inline buttons, which is implemented by a special handler: CallbackQueryHandler(answer_cb).
Inside a Method answer_cb, we read the data sent from the keyboard and write it to a variable city: city <- update$callback_query$data. After that, we request weather data from the API, form and send a message, and finally use the method answerCallbackQuery in order to inform the bot that we have processed the Inline button click.
An example of a bot that displays a list of the most recent articles with links according to the specified Habu from habr.com.
I bring this bot in order to show you how to display Inline buttons that lead to web pages.
The logic of this bot is similar to the previous one, initially we launch the main keyboard with the command /start. Next, the bot gives us a list of 6 hubs to choose from, we select the hub we are interested in, and get the 5 most recent publications from the selected Hub.
As you understand, in this case we need to get a list of articles, and for this we will use a special package habR, which allows you to request articles from habra and some statistics on them in R.
Install package habR you can only from github, for which you will need an additional package devtools. Use the code below to install.
Run the code example above, after replacing 'YOUR BOT TOKEN' with the real token you received when you created the bot via Bot Father (I talked about creating a bot in first article).
As a result, we will get the following result:
We entered the list of Hubs available for selection with a hardcode, in the method habs:
We get the list of articles from the specified Hub with the command habr_hub_posts(), from the package habR. At the same time, we indicate that we do not need a list of articles for all time, but only the first page on which 20 articles are located. From the resulting table using the command head() we leave only the top 5, which are the most recent articles.
The logic is very similar to the previous bot, but in this case we dynamically generate the Inline keyboard with a list of articles using the function lapply().
We substitute the title of the article in the button text posts$title[x], and in the argument url link to article: url = posts$link[x].
Next, we create a filter, handlers and run our bot.
Conclusion
Now the bots you write will be much more convenient to use, due to the fact that they will be controlled from the keyboard, and not by entering commands. At least when interacting with the bot through a smartphone, the keyboard will significantly simplify the process of using it.
In the next article, we will figure out how to build a logical dialogue with a bot and work with databases.