ProHoster > блог > адміністраванне > Пішам telegram бота на мове R (частка 3): Як дадаць боту падтрымку клавіятуры
Пішам telegram бота на мове R (частка 3): Як дадаць боту падтрымку клавіятуры
Гэта трэці артыкул з серыі "Пішам telegram бота на мове R". У папярэдніх публікацыях мы навучыліся ствараць тэлеграм робата, адпраўляць праз яго паведамленні, дадалі робату каманды і фільтры паведамленняў. Таму перад тым як прыступіць да чытання дадзенага артыкула я вельмі рэкамендую азнаёміцца з папярэднімі, т.я. тут я ўжо не буду спыняць на апісаных раней асновах ботабудавання.
У гэтым артыкуле мы павысім юзабіліці нашага робата за кошт дадання клавіятуры, якая зробіць інтэрфейс робата інтуітыўна зразумелым, і простым ў выкарыстанні.
Усе артыкулы з серыі "Пішам telegram бота на мове R"
На момант напісання артыкула telegram.bot дазваляе вам стварыць клавіятуры двух тыпаў:
Reply - Асноўная, звычайная клавіятура, якая знаходзіцца пад панэллю ўводу тэксту паведамлення. Такая клавіятура проста адпраўляе робату тэкставае паведамленне, і ў якасці тэксту адправіць той тэкст, які напісаны на самой кнопцы.
Inline – Клавіятура прывязаная да канкрэтнага паведамлення робата. Дадзеная клавіятура адпраўляе робату дадзеныя, прывязаныя да націснутай кнопкі, гэтыя дадзеныя могуць адрознівацца ад тэксту, напісанага на самой кнопцы. І апрацоўваюцца такія кнопкі праз CallbackQueryHandler.
Для таго, каб бот адкрыў клавіятуру неабходна пры адпраўцы паведамлення праз метад sendMessage(), перадаць створаную раней клавіятуру ў аргумент reply_markup.
Ніжэй мы разбяром некалькі прыкладаў.
Reply клавіятура
Як я ўжо пісаў вышэй, гэта асноўная клавіятура кіравання робатам.
Прыклад стварэння Reply клавіятуры з афіцыйнай даведкі
Вышэй прыведзены прыклад з афіцыйнай даведкі пакета telegram.bot. Для стварэння клавіятуры выкарыстоўваецца функцыя ReplyKeyboardMarkup(), якая ў сваю чаргу прымае спіс спісаў кнопак, якія ствараюцца функцыяй KeyboardButton().
чаму ў ReplyKeyboardMarkup() неабходна перадаваць не проста спіс, а спіс спісаў? Справа ў тым, што вы перадаеце асноўны спіс, і ў ім асобнымі спісамі вы задаеце кожны шэраг кнопак, т.я. у адзін шэраг можна размясціць некалькі кнопак.
аргумент resize_keyboard дазваляе аўтаматычна падбіраць аптымальны памер кнопак клавіятуры, а аргумент one_time_keyboard дазваляе хаваць клавіятуру пасля кожнага націску на кнопку.
Давайце напішам найпростага робата, у якога будзе 3 кнопкі:
Чат ID - Запытаць чат ID дыялогу з ботам
Маё імя - Запытаць сваё імя
Мой лагін - Запытаць сваё імя карыстальніка ў тэлеграм
Код 1: Просты бот з Reply клавіятурай
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()
Запусціце прыведзены вышэй прыклад кода, папярэдне замяніўшы 'ТОКЕН ВАШАГА БОТА' на рэальны токен, які вы атрымалі пры стварэнні бота праз БотБацька (аб стварэнні бота я распавядаў у першым артыкуле).
Пасля запуску задайце боту каманду /start, т.я. менавіта яе мы вызначылі для запуску клавіятуры.
Калі на дадзены момант вам складана разабраць прыведзены прыклад кода, са стварэннем метадаў, фільтраў і апрацоўшчыкаў, тое варта вярнуцца да папярэдняй артыкуле, у якой я падрабязна ўсё гэта апісаў.
Мы стварылі 4 метады:
start - Запуск клавіятуры
chat_id - Запыт ідэнтыфікатара чата
my_name - Запыт свайго імя
my_username - Запыт свайго лагіна
У аб'ект MessageFilters дадалі 3 фільтры паведамленняў, па іх тэксце:
chat_id — Допісы з тэкстам "Чат ID"
name — Допісы з тэкстам "Моё имя"
username — Паведамленні з тэкстам "Мой логин"
І стварылі 4 апрацоўшчыкі, якія па зададзеным камандам і фільтрам будуць выконваць названыя метады.
У нашым выпадку ўсе кнопкі мы размясцілі сябар пад сябрам, але мы можам размясціць іх у адзін шэраг, занясучы змены ў спіс спісаў кнопак. Т.к. адзін шэраг усярэдзіне клавіятуры ствараецца праз укладзены спіс кнопак, то для таго, каб вывесці нашы кнопкі ў адзін шэраг трэба перапісаць частку кода па пабудове клавіятуры вось так:
Адпраўляецца клавіятура ў чат метадам sendMessage(), у аргуменце reply_markup.
bot$sendMessage(update$message$chat_id,
text = 'Выберите команду',
reply_markup = RKM)
Inline клавіятура
Як я ўжо пісаў вышэй, Inline клавіятура прывязана да канкрэтнага паведамлення. З ёй працаваць некалькі складаней, чым з асноўнай клавіятурай.
Першапачаткова вам неабходна дадаць робату метад, для выкліку Inline клавіятуры.
Для адказу на націск Inline кнопкі таксама можна выкарыстоўваць метад бота answerCallbackQuery(), які можа вывесці апавяшчэнне ў інтэрфейсе telegram, карыстачу націснуў Inline кнопку.
Дадзеныя адпраўленыя з Inline кнопкі не з'яўляюцца тэкстам, таму для іх апрацоўкі неабходна стварыць спецыяльны апрацоўшчык з дапамогай каманды CallbackQueryHandler().
Код пабудовы Inline клавіятуры які прыводзіцца ў афіцыйнай даведцы пакета telegram.bot.
Код пабудовы Inline клавіятуры з афіцыйнай даведкі
Будаваць Inline клавіятуру неабходна з дапамогай каманды InlineKeyboardMarkup(), па такім жа прынцыпе, як і Reply клавіятуру. У InlineKeyboardMarkup() неабходна перадаць спіс, спісаў Inline кнопак, кожная асобная кнопка ствараецца функцыяй InlineKeyboardButton().
Inline кнопка можа альбо перадаваць робату нейкія дадзеныя з дапамогай аргументу callback_data, альбо адкрываць якую-небудзь HTML старонку, зададзеную з дапамогай аргументу url.
У выніку будзе спіс, у якім кожны элемент таксама з'яўляецца спісам Inline кнопак, якія неабходна аб'яднаць у адзін шэраг.
Далей мы разгледзім некалькі прыкладаў робатаў з Inline кнопкамі.
Прыклад найпростага робата з падтрымкай InLine кнопак
Для пачатку мы напішам робата для экспрэс тэсціравання на covid-19. Па камандзе /test, ён будзе адпраўляць вам клавіятуру з двума кнопкамі, у залежнасці ад націснутай кнопкі ён будзе дасылаць вам паведамленне з вынікамі вашага тэсціравання.
Код 2: Найпросты бот з Inline клавіятурай
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()
Запусціце прыведзены вышэй прыклад кода, папярэдне замяніўшы 'ТОКЕН ВАШАГА БОТА' на рэальны токен, які вы атрымалі пры стварэнні бота праз БотБацька (аб стварэнні бота я распавядаў у першым артыкуле).
Вынік:
Мы стварылі два метады:
тэст - Для адпраўкі ў чат Inline клавіятуры
answer_cb - Для апрацоўкі адпраўленых з клавіятуры дадзеных.
Дадзеныя, якія будуць адпраўлены з кожнай кнопкі задаюцца ў аргуменце callback_data, пры стварэнні кнопкі. Атрымаць адпраўленыя з кнопкі даныя можна з дапамогай канструкцыі update$callback_query$data, ўнутры метаду answer_cb.
Што б бот рэагаваў на Inline клавіятуру, метад answer_cb апрацоўваецца спецыяльным апрацоўшчыкам: CallbackQueryHandler(answer_cb). Які запускае ўказаны метад па націску Inline кнопкі. Апрацоўшчык CallbackQueryHandler прымае два аргументы:
callback - Метад які неабходна запусціць
pattern - Фільтр па дадзеных, якія прывязаныя да кнопкі з дапамогай аргументу callback_data.
Адпаведна з дапамогай аргумента pattern мы можам пад націск кожнай кнопкі напісаць асобны метад:
Код 3: Падзяляем метады пад кожную Inline кнопку
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()
Запусціце прыведзены вышэй прыклад кода, папярэдне замяніўшы 'ТОКЕН ВАШАГА БОТА' на рэальны токен, які вы атрымалі пры стварэнні бота праз БотБацька (аб стварэнні бота я распавядаў у першым артыкуле).
Цяпер мы напісалі 2 асобныя метады, г.зн. па адным метадзе, пад націск кожнай кнопкі, і выкарыстоўвалі аргумент pattern, пры стварэнні іх апрацоўшчыкаў:
Сканчаецца код метаду answer_cb камандай bot$answerCallbackQuery(callback_query_id = update$callback_query$id), Якая паведамляе робату, што дадзеныя з inline клавіятуры атрыманы.
Прыклад бота, які паведамляе бягучае надвор'е па выбраным горадзе
Давайце паспрабуем напісаць робата, які запытвае дадзеныя аб надвор'і.
Логіка яго працы будзе наступная. Першапачаткова камандай /start вы выклікаеце асноўную клавіятуру, у якой прысутнічае ўсяго адна кнопка "Надвор'е". Націснуўшы на гэтую кнопку вы атрымліваеце паведамленне з Inline клавіятурай, для выбару горада, па якім патрабуецца пазнаць бягучае надвор'е. Выбіраеце адзін з гарадоў і атрымліваеце бягучае надвор'е.
У гэтым прыкладзе кода мы будзем выкарыстоўваць некалькі дадатковых пакетаў:
httr - пакет для працы з HTTP запытамі, на аснове якіх пабудавана праца з любым API. У нашым выпадку мы будзем выкарыстоўваць бясплатны API openweathermap.org.
stringr - пакет для працы з тэкстам, у нашым выпадку мы будзем яго выкарыстоўваць для фарміравання паведамлення аб надвор'і ў абраным горадзе.
Код 4: Бот, які паведамляе бягучае надвор'е па выбраным горадзе
Запусціце прыведзены вышэй прыклад кода, папярэдне замяніўшы 'ТОКЕН ВАШАГА БОТА' на рэальны токен, які вы атрымалі пры стварэнні бота праз БотБацька (аб стварэнні бота я распавядаў у першым артыкуле).
У выніку наш бот будзе працаваць прыкладна так:
Схематычна дадзенага робата можна выбраць вось так:
Мы стварылі 3 метаду, даступныя ўнутры нашага пагоднага бота:
Пачатак - Запуск асноўнай клавіятуры бота
надвор'е - Запуск Inline клавіятуры для выбару горада
answer_cb - Асноўны метад, які па зададзеным горадзе запытвае ў API надвор'е, і адпраўляе яе ў чат.
метад Пачатак у нас запускаецца камандай /start, што рэалізавана апрацоўшчыкам CommandHandler('start', start).
Для запуску метаду надвор'е мы стварылі аднайменны фільтр:
# создаём фильтры
## сообщения с текстом Погода
MessageFilters$weather <- BaseFilter(function(message) {
# проверяем текст сообщения
message$text == "Погода"
}
)
І выклікаем гэты метад наступным апрацоўшчыкам паведамленняў: MessageHandler(weather, filters = MessageFilters$weather).
І ў рэшце рэшт, асноўны наш метад answer_cb рэагуе на націск Inline кнопак, што рэалізавана адмысловым апрацоўшчыкам: CallbackQueryHandler(answer_cb).
Унутры метаду answer_cb, мы счытваем адпраўленыя з клавіятуры дадзеныя і запісваем іх у зменную city: city <- update$callback_query$data. Пасля чаго запытваем з API дадзеныя аб надвор'і, фармуем і адпраўляем паведамленне, і ўрэшце выкарыстоўваем метад answerCallbackQuery для таго, што б паведаміць боту, аб тым, што мы апрацавалі націск Inline кнопкі.
Прыклад робата, які выводзіць спіс самых свежых артыкулаў са спасылкамі па-паказаным Хабу з habr.com.
Дадзенага робата я прыводжу для таго, каб паказаць вам, як вывесці Inline кнопкі якія вядуць на вэб старонкі.
Логіка дадзенага робата падобная з папярэднім, першапачаткова мы запускаем асноўную клавіятуру камандай /start. Далей бот дае нам на выбар спіс з 6 хабаў, мы выбіраем які цікавіць нас хаб, і атрымліваем 5 самых свежых публікацый з абранага Хаба.
Як вы разумееце, у дадзеным выпадку нам неабходна атрымаць спіс артыкулаў, і для гэтага мы будзем выкарыстоўваць спецыяльны пакет habR, які дазваляе запытваць з хабры артыкула і некаторую статыстыку па іх у R.
Усталяваць пакет habR можна толькі з github, для чаго вам спатрэбіцца дадатковы пакет devtools. Для ўстаноўкі скарыстайцеся прыведзеным ніжэй кодам.
Цяпер разгледзім код пабудовы апісанага вышэй робата:
Код 5: Бот які выводзіць спіс найбольш свежых артыкулаў па абраным Хабу
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()
Запусціце прыведзены вышэй прыклад кода, папярэдне замяніўшы 'ТОКЕН ВАШАГА БОТА' на рэальны токен, які вы атрымалі пры стварэнні бота праз БотБацька (аб стварэнні бота я распавядаў у першым артыкуле).
У выніку мы атрымаем вось такі вынік:
Спіс даступных для выбару Хабаў мы ўбілі хардкодам, у метадзе habs:
Спіс артыкулаў з указанага Хаба мы атрымліваем камандай habr_hub_posts(), з пакета habR. Пры гэтым паказваем, што нам не патрабуецца спіс артыкулаў за ўвесь час, а толькі першая старонка на якой размяшчаюцца 20 артыкулаў. З атрыманай табліцы з дапамогай каманды head() пакідаем толькі 5 самых верхніх, якія і з;яўляюцца самымі свежымі артыкуламі.
Логіка вельмі падобная з папярэднім ботам, але ў дадзеным выпадку Inline клавіятуру са спісам артыкулаў мы генеруемы дынамічна з дапамогай функцыі lapply().
У тэкст кнопкі мы падстаўляем назву артыкула posts$title[x], а ў аргумент url спасылку на артыкул: url = posts$link[x].
Далей, ствараем фільтр, апрацоўшчыкі і запускаем нашага робата.
Заключэнне
Цяпер напісаныя вамі робаты будуць значна зручней у працы, за кошт таго, што кіраванне імі будзе ажыццяўляцца з клавіятуры, а не ўводам каманд. Прынамсі пры ўзаемадзеянні з ботам праз смартфон клавіятура адчувальна спросціць працэс яго выкарыстання.
У наступным артыкуле мы разбярэмся як будаваць лагічны дыялог з ботам, і працаваць з базамі дадзеных.