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.

Writing a telegram bot in R (part 3): How to add keyboard support to a bot

All articles from the series "Writing a telegram bot in R"

  1. Create a bot and use it to send messages to telegram
  2. Add command support and message filters to the bot
  3. How to add keyboard support to a bot

Content

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.

  1. What types of keyboards does the telegram bot support?
  2. Reply keyboard
  3. Inline keyboard
    3.1. An example of a simple bot with support for InLine buttons
    3.2. An example of a bot that reports the current weather for a selected city
    3.3. An example of a bot that displays a list of the most recent articles with links according to the specified Habu from habr.com
  4. Conclusion

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

bot <- Bot(token = "TOKEN")
chat_id <- "CHAT_ID"

# Create Custom Keyboard
text <- "Aren't those custom keyboards cool?"
RKM <- ReplyKeyboardMarkup(
  keyboard = list(
    list(KeyboardButton("Yes, they certainly are!")),
    list(KeyboardButton("I'm not quite sure")),
    list(KeyboardButton("No..."))
  ),
  resize_keyboard = FALSE,
  one_time_keyboard = TRUE
)

# Send Custom Keyboard
bot$sendMessage(chat_id, text, reply_markup = RKM)

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
  • My name - Request your name
  • My login - Request your username in telegram

Code 1: Simple Bot with Reply Keyboard

library(telegram.bot)

# создаём экзСмпляр класса Updater
updater <- Updater('Π’ΠžΠšΠ•Π Π’ΠΠ¨Π•Π“Πž Π‘ΠžΠ’Π')

# создаём ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹
## ΠΌΠ΅Ρ‚ΠΎΠ΄ для запуска ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
start <- function(bot, update) {

  # создаём ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  RKM <- ReplyKeyboardMarkup(
    keyboard = list(
      list(KeyboardButton("Π§Π°Ρ‚ ID")),
      list(KeyboardButton("ΠœΠΎΡ‘ имя")),
      list(KeyboardButton("Мой логин"))
    ),
    resize_keyboard = FALSE,
    one_time_keyboard = TRUE
  )

  # отправляСм ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  bot$sendMessage(update$message$chat_id,
                  text = 'Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ', 
                  reply_markup = RKM)

}

## ΠΌΠ΅Ρ‚ΠΎΠ΄ Π²ΠΎΠ·Π²Ρ€Π°Π·Π°ΡŽΡ‰ΠΈΠΉ id Ρ‡Π°Ρ‚Π°
chat_id <- function(bot, update) {

  bot$sendMessage(update$message$chat_id, 
                  text = paste0("Π§Π°Ρ‚ id этого Π΄ΠΈΠ°Π»ΠΎΠ³Π°: ", update$message$chat_id),
                  parse_mode = "Markdown")

}

## ΠΌΠ΅Ρ‚ΠΎΠ΄ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‰ΠΈΠΉ имя
my_name <- function(bot, update) {

  bot$sendMessage(update$message$chat_id, 
                  text = paste0("Вас Π·ΠΎΠ²ΡƒΡ‚ ", update$message$from$first_name),
                  parse_mode = "Markdown")

}

## ΠΌΠ΅Ρ‚ΠΎΠ΄ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‰ΠΈΠΉ Π»ΠΎΠ³ΠΈΠ½
my_username <- function(bot, update) {

  bot$sendMessage(update$message$chat_id, 
                  text = paste0("Π’Π°Ρˆ Π»ΠΎΠ³ΠΈΠ½ ", update$message$from$username),
                  parse_mode = "Markdown")

}

# создаём Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹
## сообщСния с тСкстом Π§Π°Ρ‚ ID
MessageFilters$chat_id <- BaseFilter(function(message) {

  # провСряСм тСкст сообщСния
  message$text == "Π§Π°Ρ‚ ID"

}
)

## сообщСния с тСкстом ΠœΠΎΡ‘ имя
MessageFilters$name <- BaseFilter(function(message) {

  # провСряСм тСкст сообщСния
  message$text == "ΠœΠΎΡ‘ имя"

}
)

## сообщСния с тСкстом Мой Π»ΠΎΠ³ΠΈΠ½
MessageFilters$username <- BaseFilter(function(message) {

  # провСряСм тСкст сообщСния
  message$text == "Мой логин"
)

# создаём ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ
h_start    <- CommandHandler('start', start)
h_chat_id  <- MessageHandler(chat_id, filters = MessageFilters$chat_id)
h_name     <- MessageHandler(my_name, filters = MessageFilters$name)
h_username <- MessageHandler(my_username, filters = MessageFilters$username)

# добавляСм ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π² диспСтчСр
updater <- updater + 
            h_start +
            h_chat_id +
            h_name +
            h_username

# запускаСм Π±ΠΎΡ‚Π° 
updater$start_polling()

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.

Writing a telegram bot in R (part 3): How to add keyboard support to a bot

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.

# создаём ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ
h_start    <- CommandHandler('start', start)
h_chat_id  <- MessageHandler(chat_id, filters = MessageFilters$chat_id)
h_name     <- MessageHandler(my_name, filters = MessageFilters$name)
h_username <- MessageHandler(my_username, filters = MessageFilters$username)

The keyboard itself is created inside the method start() the team ReplyKeyboardMarkup().

RKM <- ReplyKeyboardMarkup(
    keyboard = list(
      list(KeyboardButton("Π§Π°Ρ‚ ID")),
      list(KeyboardButton("ΠœΠΎΡ‘ имя")),
      list(KeyboardButton("Мой логин"))
    ),
    resize_keyboard = FALSE,
    one_time_keyboard = TRUE
)

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:

RKM <- ReplyKeyboardMarkup(
    keyboard = list(
      list(
          KeyboardButton("Π§Π°Ρ‚ ID"),
          KeyboardButton("ΠœΠΎΡ‘ имя"),
          KeyboardButton("Мой логин")
     )
    ),
    resize_keyboard = FALSE,
    one_time_keyboard = TRUE
)

Writing a telegram bot in R (part 3): How to add keyboard support to a bot

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

# Initialize bot
bot <- Bot(token = "TOKEN")
chat_id <- "CHAT_ID"

# Create Inline Keyboard
text <- "Could you type their phone number, please?"
IKM <- InlineKeyboardMarkup(
  inline_keyboard = list(
    list(
      InlineKeyboardButton(1),
      InlineKeyboardButton(2),
      InlineKeyboardButton(3)
    ),
    list(
      InlineKeyboardButton(4),
      InlineKeyboardButton(5),
      InlineKeyboardButton(6)
    ),
    list(
      InlineKeyboardButton(7),
      InlineKeyboardButton(8),
      InlineKeyboardButton(9)
    ),
    list(
      InlineKeyboardButton("*"),
      InlineKeyboardButton(0),
      InlineKeyboardButton("#")
    )
  )
)

# Send Inline Keyboard
bot$sendMessage(chat_id, text, reply_markup = IKM)

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.

Code 2: The simplest bot with Inline keyboard

library(telegram.bot)

# создаём экзСмпляр класса Updater
updater <- Updater('Π’ΠžΠšΠ•Π Π’ΠΠ¨Π•Π“Πž Π‘ΠžΠ’Π')

# ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ InLine ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
test <- function(bot, update) {

  # создаём InLine ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  IKM <- InlineKeyboardMarkup(
    inline_keyboard = list(
      list(
        InlineKeyboardButton("Π”Π°", callback_data = 'yes'),
        InlineKeyboardButton("НСт", callback_data = 'no')
      )
    )
  )

  # ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ Π² Ρ‡Π°Ρ‚
  bot$sendMessage(update$message$chat_id, 
                  text = "Π’Ρ‹ Π±ΠΎΠ»Π΅Ρ‚Π΅ коронавирусом?", 
                  reply_markup = IKM)
}

# ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ
answer_cb <- function(bot, update) {

  # ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ с ΠΊΠ½ΠΎΠΏΠΊΠΈ
  data <- update$callback_query$data

  # ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ, наТавшСго ΠΊΠ½ΠΎΠΏΠΊΡƒ
  uname <- update$effective_user()$first_name

  # ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°
  if ( data == 'no' ) {

    msg <- paste0(uname, ", ΠΏΠΎΠ·Π΄Ρ€Π°Π²Π»ΡΡŽ, ваш тСст Π½Π° covid-19 ΠΎΡ‚Ρ€ΠΈΡ†Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ.")

  } else {

    msg <- paste0(uname, ", ΠΊ соТалСни ваш тСст Π½Π° covid-19 ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ.")

  }

  # ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° сообщСния
  bot$sendMessage(chat_id = update$from_chat_id(),
                  text = msg)

  # сообщаСм Π±ΠΎΡ‚Ρƒ, Ρ‡Ρ‚ΠΎ запрос с ΠΊΠ½ΠΎΠΏΠΊΠΈ принят
  bot$answerCallbackQuery(callback_query_id = update$callback_query$id) 
}

# создаём ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ
inline_h      <- CommandHandler('test', test)
query_handler <- CallbackQueryHandler(answer_cb)

# добавляСм ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π² диспСтчСр
updater <- updater + inline_h + query_handler

# запускаСм Π±ΠΎΡ‚Π°
updater$start_polling()

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:
Writing a telegram bot in R (part 3): How to add keyboard support to a bot

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:

Code 3: Separate methods for each Inline button

library(telegram.bot)

# создаём экзСмпляр класса Updater
updater <- Updater('Π’ΠžΠšΠ•Π Π’ΠΠ¨Π•Π“Πž Π‘ΠžΠ’Π')

# ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ InLine ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
test <- function(bot, update) {  

  # создаём InLine ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  IKM <- InlineKeyboardMarkup(
    inline_keyboard = list(
      list(
        InlineKeyboardButton("Π”Π°", callback_data = 'yes'),
        InlineKeyboardButton("НСт", callback_data = 'no')
      )
    )
  )

  # ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ Π² Ρ‡Π°Ρ‚
  bot$sendMessage(update$message$chat_id, 
                  text = "Π’Ρ‹ Π±ΠΎΠ»Π΅Ρ‚Π΅ коронавирусом?", 
                  reply_markup = IKM)
}

# ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ Π”Π°
answer_cb_yes <- function(bot, update) {

  # ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ, наТавшСго ΠΊΠ½ΠΎΠΏΠΊΡƒ
  uname <- update$effective_user()$first_name

  # ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°
  msg <- paste0(uname, ", ΠΊ соТалСни ваш тСкст Π½Π° covid-19 ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ.")

  # ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° сообщСния
  bot$sendMessage(chat_id = update$from_chat_id(),
                  text = msg)

  # сообщаСм Π±ΠΎΡ‚Ρƒ, Ρ‡Ρ‚ΠΎ запрос с ΠΊΠ½ΠΎΠΏΠΊΠΈ принят
  bot$answerCallbackQuery(callback_query_id = update$callback_query$id) 
}

# ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ НСт
answer_cb_no <- function(bot, update) {

  # ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ, наТавшСго ΠΊΠ½ΠΎΠΏΠΊΡƒ
  uname <- update$effective_user()$first_name

  msg <- paste0(uname, ", ΠΏΠΎΠ·Π΄Ρ€Π°Π²Π»ΡΡŽ, ваш тСкст Π½Π° covid-19 ΠΎΡ‚Ρ€ΠΈΡ†Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ.")

  # ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° сообщСния
  bot$sendMessage(chat_id = update$from_chat_id(),
                  text = msg)

  # сообщаСм Π±ΠΎΡ‚Ρƒ, Ρ‡Ρ‚ΠΎ запрос с ΠΊΠ½ΠΎΠΏΠΊΠΈ принят
  bot$answerCallbackQuery(callback_query_id = update$callback_query$id) 
}

# создаём ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ
inline_h          <- CommandHandler('test', test)
query_handler_yes <- CallbackQueryHandler(answer_cb_yes, pattern = 'yes')
query_handler_no  <- CallbackQueryHandler(answer_cb_no, pattern = 'no')

# добавляСм ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π² диспСтчСр
updater <- updater + 
            inline_h + 
            query_handler_yes +
            query_handler_no

# запускаСм Π±ΠΎΡ‚Π°
updater$start_polling()

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:

query_handler_yes <- CallbackQueryHandler(answer_cb_yes, pattern = 'yes')
query_handler_no  <- CallbackQueryHandler(answer_cb_no, pattern = 'no')

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

library(telegram.bot)
library(httr)
library(stringr)

# создаём экзСмпляр класса Updater
updater <- Updater('Π’ΠžΠšΠ•Π Π’ΠΠ¨Π•Π“Πž Π‘ΠžΠ’Π')

# создаём ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹
## ΠΌΠ΅Ρ‚ΠΎΠ΄ для запуска основной ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
start <- function(bot, update) {

  # создаём ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  RKM <- ReplyKeyboardMarkup(
    keyboard = list(
      list(
        KeyboardButton("Погода")
      )
    ),
    resize_keyboard = TRUE,
    one_time_keyboard = TRUE
  )

  # отправляСм ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  bot$sendMessage(update$message$chat_id,
                  text = 'Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ', 
                  reply_markup = RKM)

}

## ΠœΠ΅Ρ‚ΠΎΠ΄ Π²Ρ‹Π·ΠΎΠ²Π° Inine ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
weather <- function(bot, update) {

  IKM <- InlineKeyboardMarkup(
    inline_keyboard = list(
      list(
        InlineKeyboardButton(text = 'Москва', callback_data = 'New York,us'),
        InlineKeyboardButton(text = 'Π‘Π°Π½ΠΊΡ‚-ΠŸΠ΅Ρ‚Π΅Ρ€Π±ΡƒΡ€Π³', callback_data = 'Saint Petersburg'),
        InlineKeyboardButton(text = 'Нью-Π™ΠΎΡ€ΠΊ', callback_data = 'New York')
      ),
      list(
        InlineKeyboardButton(text = 'Π•ΠΊΠ°Ρ‚Π΅Ρ€ΠΈΠ½Π±ΡƒΡ€Π³', callback_data = 'Yekaterinburg,ru'),
        InlineKeyboardButton(text = 'Π‘Π΅Ρ€Π»ΠΈΠ½', callback_data = 'Berlin,de'),
        InlineKeyboardButton(text = 'ΠŸΠ°Ρ€ΠΈΠΆ', callback_data = 'Paris,fr')
      ),
      list(
        InlineKeyboardButton(text = 'Π ΠΈΠΌ', callback_data = 'Rome,it'),
        InlineKeyboardButton(text = 'ОдСсса', callback_data = 'Odessa,ua'),
        InlineKeyboardButton(text = 'КиСв', callback_data = 'Kyiv,fr')
      ),
      list(
        InlineKeyboardButton(text = 'Π’ΠΎΠΊΠΈΠΎ', callback_data = 'Tokyo'),
        InlineKeyboardButton(text = 'АмстСрдам', callback_data = 'Amsterdam,nl'),
        InlineKeyboardButton(text = 'Π’Π°ΡˆΠΈΠ½Π³Ρ‚ΠΎΠ½', callback_data = 'Washington,us')
      )
    )
  )

  # Send Inline Keyboard
  bot$sendMessage(chat_id = update$message$chat_id, 
                  text = "Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ Π³ΠΎΡ€ΠΎΠ΄", 
                  reply_markup = IKM)
}

# ΠΌΠ΅Ρ‚ΠΎΠ΄ для сообщСния ΠΏΠΎΠ³ΠΎΠ΄Ρ‹
answer_cb <- function(bot, update) {

  # ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΈΠ· сообщСния Π³ΠΎΡ€ΠΎΠ΄
  city <- update$callback_query$data

  # отправляСм запрос
  ans <- GET('https://api.openweathermap.org/data/2.5/weather', 
             query = list(q     = city,
                          lang  = 'ru',
                          units = 'metric',
                          appid = '4776568ccea136ffe4cda9f1969af340')) 

  # парсим ΠΎΡ‚Π²Π΅Ρ‚
  result <- content(ans)

  # Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅ΠΌ сообщСниС
  msg <- str_glue("{result$name} ΠΏΠΎΠ³ΠΎΠ΄Π°:n",
                  "ВСкущая Ρ‚Π΅ΠΌΠΏΠ΅Ρ€Π°Ρ‚ΡƒΡ€Π°: {result$main$temp}n",
                  "Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ Π²Π΅Ρ‚Ρ€Π°: {result$wind$speed}n",
                  "ОписаниС: {result$weather[[1]]$description}")

  # отправляСм ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ ΠΏΠΎΠ³ΠΎΠ΄Π΅
  bot$sendMessage(chat_id = update$from_chat_id(),
                  text    = msg)

  bot$answerCallbackQuery(callback_query_id = update$callback_query$id) 
}

# создаём Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹
## сообщСния с тСкстом Погода
MessageFilters$weather <- BaseFilter(function(message) {

  # провСряСм тСкст сообщСния
  message$text == "Погода"

}
)

# создаём ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ
h_start         <- CommandHandler('start', start)
h_weather       <- MessageHandler(weather, filters = MessageFilters$weather)
h_query_handler <- CallbackQueryHandler(answer_cb)

# добавляСм ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π² диспСтчСр
updater <- updater + 
              h_start +
              h_weather +
              h_query_handler

# запускаСм Π±ΠΎΡ‚Π°
updater$start_polling()

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:
Writing a telegram bot in R (part 3): How to add keyboard support to a bot

Schematically, this bot can be drawn like this:
Writing a telegram bot in R (part 3): How to add keyboard support to a bot

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:

# создаём Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹
## сообщСния с тСкстом Погода
MessageFilters$weather <- BaseFilter(function(message) {

  # провСряСм тСкст сообщСния
  message$text == "Погода"

}
)

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.

install.packages('devtools')
devtools::install_github('selesnow/habR')

Now consider the code for building the bot described above:

Code 5: Bot that displays a list of the most recent articles on the selected Hub

library(telegram.bot)
library(habR)

# создаём экзСмпляр класса Updater
updater <- Updater('Π’ΠžΠšΠ•Π Π’ΠΠ¨Π•Π“Πž Π‘ΠžΠ’Π')

# создаём ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹
## ΠΌΠ΅Ρ‚ΠΎΠ΄ для запуска основной ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
start <- function(bot, update) {

  # создаём ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  RKM <- ReplyKeyboardMarkup(
    keyboard = list(
      list(
        KeyboardButton("Бписок статСй")
      )
    ),
    resize_keyboard = TRUE,
    one_time_keyboard = TRUE
  )

  # отправляСм ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  bot$sendMessage(update$message$chat_id,
                  text = 'Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ', 
                  reply_markup = RKM)

}

## ΠœΠ΅Ρ‚ΠΎΠ΄ Π²Ρ‹Π·ΠΎΠ²Π° Inine ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
habs <- function(bot, update) {

  IKM <- InlineKeyboardMarkup(
    inline_keyboard = list(
      list(
        InlineKeyboardButton(text = 'R', callback_data = 'R'),
        InlineKeyboardButton(text = 'Data Mining', callback_data = 'data_mining'),
        InlineKeyboardButton(text = 'Data Engineering', callback_data = 'data_engineering')
      ),
      list(
        InlineKeyboardButton(text = 'Big Data', callback_data = 'bigdata'),
        InlineKeyboardButton(text = 'Python', callback_data = 'python'),
        InlineKeyboardButton(text = 'Визуализация Π΄Π°Π½Π½Ρ‹Ρ…', callback_data = 'data_visualization')
      )
    )
  )

  # Send Inline Keyboard
  bot$sendMessage(chat_id = update$message$chat_id, 
                  text = "Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ Π₯Π°Π±", 
                  reply_markup = IKM)
}

# ΠΌΠ΅Ρ‚ΠΎΠ΄ для сообщСния ΠΏΠΎΠ³ΠΎΠ΄Ρ‹
answer_cb <- function(bot, update) {

  # ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΈΠ· сообщСния Π³ΠΎΡ€ΠΎΠ΄
  hub <- update$callback_query$data

  # сообщСниС ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎ ΠΊΠ½ΠΎΠΏΠΊΠ΅ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Ρ‹
  bot$answerCallbackQuery(callback_query_id = update$callback_query$id, 
                          text = 'ΠŸΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ нСсколько ΠΌΠΈΠ½ΡƒΡ‚, запрос обрабатываСтся') 

  # сообщСниС ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π½Π°Π΄ΠΎ ΠΏΠΎΠ΄ΠΎΠΆΠ΄Π°Ρ‚ΡŒ ΠΏΠΎΠΊΠ° Π±ΠΎΡ‚ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ Π΄Π°Π½Π½Ρ‹Π΅
  mid <- bot$sendMessage(chat_id = update$from_chat_id(),
                         text    = "ΠŸΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ нСсколько ΠΌΠΈΠ½ΡƒΡ‚ ΠΏΠΎΠΊΠ°, я собСру Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎ Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠΌΡƒ Π₯Π°Π±Ρƒ")

  # парсим Π₯Π°Π±Ρ€
  posts <- head(habr_hub_posts(hub, 1), 5)

  # удаляСм сообщСниС ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π½Π°Π΄ΠΎ ΠΏΠΎΠ΄ΠΎΠΆΠ΄Π°Ρ‚ΡŒ
  bot$deleteMessage(update$from_chat_id(), mid$message_id) 

  # Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅ΠΌ список ΠΊΠ½ΠΎΠΏΠΎΠΊ
  keys <- lapply(1:5, function(x) list(InlineKeyboardButton(posts$title[x], url = posts$link[x])))

  # Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅ΠΌ ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  IKM <- InlineKeyboardMarkup(
    inline_keyboard =  keys 
    )

  # отправляСм ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ ΠΏΠΎΠ³ΠΎΠ΄Π΅
  bot$sendMessage(chat_id = update$from_chat_id(),
                  text    = paste0("5 Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ свСТих статСй ΠΈΠ· Π₯Π°Π±Π° ", hub),
                  reply_markup = IKM)

}

# создаём Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹
## сообщСния с тСкстом Погода
MessageFilters$hubs <- BaseFilter(function(message) {

  # провСряСм тСкст сообщСния
  message$text == "Бписок статСй"

}
)

# создаём ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ
h_start         <- CommandHandler('start', start)
h_hubs          <- MessageHandler(habs, filters = MessageFilters$hubs)
h_query_handler <- CallbackQueryHandler(answer_cb)

# добавляСм ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π² диспСтчСр
updater <- updater + 
  h_start +
  h_hubs  +
  h_query_handler

# запускаСм Π±ΠΎΡ‚Π°
updater$start_polling()

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:
Writing a telegram bot in R (part 3): How to add keyboard support to a bot

We entered the list of Hubs available for selection with a hardcode, in the method habs:

## ΠœΠ΅Ρ‚ΠΎΠ΄ Π²Ρ‹Π·ΠΎΠ²Π° Inine ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρ‹
habs <- function(bot, update) {

  IKM <- InlineKeyboardMarkup(
    inline_keyboard = list(
      list(
        InlineKeyboardButton(text = 'R', callback_data = 'r'),
        InlineKeyboardButton(text = 'Data Mining', callback_data = 'data_mining'),
        InlineKeyboardButton(text = 'Data Engineering', callback_data = 'data_engineering')
      ),
      list(
        InlineKeyboardButton(text = 'Big Data', callback_data = 'bigdata'),
        InlineKeyboardButton(text = 'Python', callback_data = 'python'),
        InlineKeyboardButton(text = 'Визуализация Π΄Π°Π½Π½Ρ‹Ρ…', callback_data = 'data_visualization')
      )
    )
  )

  # Send Inline Keyboard
  bot$sendMessage(chat_id = update$message$chat_id, 
                  text = "Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ Π₯Π°Π±", 
                  reply_markup = IKM)
}

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.

  # парсим Π₯Π°Π±Ρ€
  posts <- head(habr_hub_posts(hub, 1), 5)

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().

  # Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅ΠΌ список ΠΊΠ½ΠΎΠΏΠΎΠΊ
  keys <- lapply(1:5, function(x) list(InlineKeyboardButton(posts$title[x], url = posts$link[x])))

  # Ρ„ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅ΠΌ ΠΊΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Ρƒ
  IKM <- InlineKeyboardMarkup(
    inline_keyboard =  keys 
    )

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.

Source: habr.com

Add a comment