Встроенные кнопки в Telegram Bot API — pyTelegramBotAPI

Добрый день уважаемые читатели, давайте рассмотрим, какие основные типы встроенных кнопок предлагают чат-боты telegram и в чем их особенности. Статья будет полезна всем, кто хочет разобраться в возможностях взаимодействия с пользователями telegram в версии bot API 2.0. Для обзора возможностей нам понадобится установить 3 целых 2 десятых Python`a и пару ложек pyTelegramBotAPI. Особенности настройки и регистрации чат-бота мы рассматривать не будем, т.к. есть множество статей на эту тему. И так, что же такое встроенные кнопки(клавиатура) в мессенджере Telegram? Это кнопки которые выводятся во внутренней области чата и привязываются к конкретному сообщению. Они жестко связаны с сообщением(если удалить сообщение, внутренние кнопки так же удаляются вместе с ним.). Они дают возможность динамически видоизменять его. В данный момент есть три типа встроенных кнопок:

URL-кнопки

Для создания кнопки используется тип InlineKeyboardMarkup, давайте создадим кнопку «Наш сайт»:

@bot.message_handler(commands = ['url']) def url(message):     markup = types.InlineKeyboardMarkup()     btn_my_site= types.InlineKeyboardButton(text='Наш сайт', url='https://habrahabr.ru')     markup.add(btn_my_site)     bot.send_message(message.chat.id, "Нажми на кнопку и перейди на наш сайт.", reply_markup = markup) 

Тут название говорит само за себя, это тип кнопок предназначен для перенаправления пользователя по ссылке, с соответствующим предупреждением. Кнопка имеет соответствующий ярлычок в правом верхнем углу, чтобы дать понять пользователю, что это ссылка.

Switch-кнопки

Этот тип кнопок предназначен для перенаправления пользователя в какой либо чат, с последующей активацией (встроенного) inline-режима общения с ботом. Данный режим можно активировать вручную: в чате, вводим: «@название бота», но switch-кнопки позволяют это сделать автоматически (помогая знакомиться с inline-режимом новичкам). Для того что-бы создать подобный переключатель, необходимо указать аргумент switch_inline_query
либо пустой, либо с каким-либо текстом.

@bot.message_handler(commands = ['switch']) def switch(message):     markup = types.InlineKeyboardMarkup()     switch_button = types.InlineKeyboardButton(text='Try', switch_inline_query="Telegram")     markup.add(switch_button)     bot.send_message(message.chat.id, "Выбрать чат", reply_markup = markup) 

Теперь, если мы нажмем на кнопку и выберем чат, вот что получится:Шаг 1:
Нажимаем на кнопку.
Шаг 2
:Выбираем чат.
Шаг 3:
Активировался встроенный inline-режим.

Callback-кнопки

Ну и наконец самое интересное — это кнопки с обратной связью: позволяют динамически обновлять сообщение/встроенные кнопки (не засоряя при этом ленту), а так же отображать уведомление в верху чат-бота или модальном окне. Например, их можно использовать для просмотра длинного сообщения, аналогично пагинации страниц на сайтах, или например сделать календарь. Я не стану изобретать велосипед, а через поиск по GitHub, найду готовую библиотеку calendar-telegram. Выполнив указанные инструкции, получаем готовый календарь, который можно динамически изменять по нажатию на соответствующие кнопки:

@bot.message_handler(commands=['calendar']) def get_calendar(message):     now = datetime.datetime.now() #Текущая дата     chat_id = message.chat.id     date = (now.year,now.month)     current_shown_dates[chat_id] = date #Сохраним текущую дату в словарь     markup = create_calendar(now.year,now.month)     bot.send_message(message.chat.id, "Пожалйста, выберите дату", reply_markup=markup)

Так же можно добавить уведомление по нажатию на дату, для этого достаточно указать сообщение в ответе:

bot.answer_callback_query(call.id, text="Дата выбрана")

(Пример в десктопной версии)
(Пример в мобильной версии)
Если изменить show_alert
на True
, то мы получим модальное окно:

bot.answer_callback_query(call.id, show_alert=True, text="Дата выбрана")

Заключение

По последним данным, в нашумевшем мессенджере Telegram регистрируются больше 600к пользователей ежедневно. Именно поэтому важно подхватить тренд и разобраться с его основными особенностями, т.к. различные методы взаимодействия с ботами существенно облегчает жизнь разработчиков и пользователей. Cпасибо за Ваш интерес к данной теме.

124

103.4k


124

InlineKeyboard
— клавиатура привязанная к сообщению, изпользующая обратный вызов (CallbackQuery), вместо отправки сообщения с обыкновенной клавиатуры.
Пример

Создание каркаса бота

Для начала создадим проект на Maven и добавим репозиторий «Telegram Bots»:

     org.telegram
     telegrambots
     4.0.0
 
 

При помощью BotFather регистрируем бота и получаем token:

Скрин

Далее создаем класс Bot, наследуемся от TelegramLongPollingBot и Овверайдим методы:

Исходный код
import org.telegram.telegrambots.bots.TelegramLongPollingBot; import org.telegram.telegrambots.meta.api.objects.Update;  public class Bot extends TelegramLongPollingBot {      @Override     public void onUpdateReceived(Update update) {      }      @Override     public String getBotUsername() {         return null;     }      @Override     public String getBotToken() {         return null;     } } 

Создаем final переменные с именем бота и токеном, добавляем в метод getBotUsername() — botUserName, в getBotToken() — token. В методе main регистрируем бота:

Исходный код
import org.telegram.telegrambots.bots.TelegramLongPollingBot; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;  public class Bot extends TelegramLongPollingBot {     private static final String botUserName = "Habr_Inlinebot";     private static final String token = "632072575:AAG5YNl9tM9MJbnP5HwLB22rVzNCmY05MQI";      public static void main(String[] args) {         ApiContextInitializer.init();         TelegramBotsApi telegramBotsApi = new TelegramBotsApi();         try {             telegramBotsApi.registerBot(new Bot());         } catch (TelegramApiRequestException e) {             e.printStackTrace();         }     }     @Override     public void onUpdateReceived(Update update) {      }      @Override     public String getBotUsername() {         return botUserName;     }      @Override     public String getBotToken() {         return token;     } }

Каркас бота готов! Теперь напишем метод с InlineKeyboard.

Работа с InlineKeyboard

Создаем обьект разметки клавиатуры:

 InlineKeyboardMarkup inlineKeyboardMarkup =new InlineKeyboardMarkup();

Теперь выстраиваем положение кнопок. Создаем обьект InlineKeyboardButton, у которой есть 2 параметка: Текст (Что будет написано на самой кнопке) и CallBackData (Что будет отсылатся серверу при нажатии на кнопку).

 InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();  inlineKeyboardButton.setText("Тык");  inlineKeyboardButton.setCallbackData("Button "Тык" has been pressed"); 

Добавляем его в список, таким образом создавая ряд.

 List keyboardButtonsRow1 = new ArrayList();  keyboardButtons.add(inlineKeyboardButton);

Если желаете создать еще один ряд, просто сделайте еще один список и добавляйте в него новые кнопки.

 List keyboardButtonsRow2 = new ArrayList();  keyboardButtons.add(inlineKeyboardButton2);

После этого нам нужно обьеденить ряды, поэтому создаем список рядов.

List> rowList= new ArrayList();  rowListeyboardButtonsRow1);  rowList.add(keyboardButtonsRow2);

Фича

Разработчик позаботился о нас и мы можем сразу записывать кнопки в список не создавая переменную.

keyboardButtonsRow1.add(new InlineKeyboardButton().setText("Fi4a")                                     .setCallbackData("CallFi4a"));

Теперь мы можем установить кнопки в обьект разметки клавиатуры.

 inlineKeyboardMarkup.setKeyboard(rowList);

Если немного не понятно описание работы с созданием клавиатуры, вот вам схема: Вот и всё! Теперь добавляем разметку в сообщение:

 SendMessage message = new SendMessage().setChatId(chatId).setText("Пример")     .setReplyMarkup(inlineKeyboardMarkup);

Теперь можем отправлять, вот вам готовый метод:

Исходный код
public static SendMessage sendInlineKeyBoardMessage(long chatId) {      InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();      InlineKeyboardButton inlineKeyboardButton1 = new InlineKeyboardButton();      InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();      inlineKeyboardButton1.setText("Тык");      inlineKeyboardButton1.setCallbackData("Button "Тык" has been pressed");      inlineKeyboardButton2.setText("Тык2");      inlineKeyboardButton2.setCallbackData("Button "Тык2" has been pressed");      List keyboardButtonsRow1 = new ArrayList();      List keyboardButtonsRow2 = new ArrayList();      keyboardButtonsRow1.add(inlineKeyboardButton1);     keyboardButtonsRow1.add(new InlineKeyboardButton().setText("Fi4a").setCallbackData("CallFi4a"));      keyboardButtonsRow2.add(inlineKeyboardButton2);      List> rowList = new ArrayList();      rowList.add(keyboardButtonsRow1);      rowListeyboardButtonsRow2);      inlineKeyboardMarkup.setKeyboard(rowList);      return new SendMessage().setChatId(chatId).setText("Пример").setReplyMarkup(inlineKeyboardMarkup);     }

Делаем вариант когда будет вызывается метод в обработчике запросов onUpdateReceived:

Исходный код
 @Override     public void onUpdateReceived(Update update) {         if(update.hasMessage()){             if(update.getMessage().hasText()){                 if(update.getMessage().getText().equals("Hello")){                     try {                         execute(sendInlineKeyBoardMessage(update.getMessage().getChatId()));                     } catch (TelegramApiException e) {                         e.printStackTrace();                     }                 }             }         }     }

Пробуем! Теперь нам нужно обработать делаем новое ветвление в if и обрабатываем CallbackQuery:

Исходный код
 @Override     public void onUpdateReceived(Update update) {         if(update.hasMessage()){             if(update.getMessage().hasText()){                 if(update.getMessage().getText().equals("Hello")){                     try {                         execute(sendInlineKeyBoardMessage(update.getMessage().getChatId()));                     } catch (TelegramApiException e) {                         e.printStackTrace();                     }                 }             }         }else if(update.hasCallbackQuery()){             try {                 execute(new SendMessage().setText(                         update.getCallbackQuery().getData())                         .setChatId(update.getCallbackQuery().getMessage().getChatId()));             } catch (TelegramApiException e) {                 e.printStackTrace();             }         }     }

Проверяем!

На этом, пожалуй всё, спасибо за внимание!

Весь исходный код:

Весь код
import org.telegram.telegrambots.ApiContextInitializer; import org.telegram.telegrambots.bots.TelegramLongPollingBot; import org.telegram.telegrambots.meta.ApiContext; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardButton; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;  import java.util.ArrayList; import java.util.List;  public class Bot extends TelegramLongPollingBot {     private static final String botUserName = "Habr_Inlinebot";     private static final String token = "632072575:AAG5YNl9tM9MJbnP5HwLB22rVzNCmY05MQI";      public static void main(String[] args) {         ApiContextInitializer.init();         TelegramBotsApi telegramBotsApi = new TelegramBotsApi();         try {             telegramBotsApi.registerBot(new Bot());         } catch (TelegramApiRequestException e) {             e.printStackTrace();         }     }      @Override     public void onUpdateReceived(Update update) {         if(update.hasMessage()){             if(update.getMessage().hasText()){                 if(update.getMessage().getText().equals("Hello")){                     try {                         execute(sendInlineKeyBoardMessage(update.getMessage().getChatId()));                     } catch (TelegramApiException e) {                         e.printStackTrace();                     }                 }             }         }else if(update.hasCallbackQuery()){             try {                 execute(new SendMessage().setText(                         update.getCallbackQuery().getData())                         .setChatId(update.getCallbackQuery().getMessage().getChatId()));             } catch (TelegramApiException e) {                 e.printStackTrace();             }         }     }      @Override     public String getBotUsername() {         return botUserName;     }      @Override     public String getBotToken() {         return token;     }      public static SendMessage sendInlineKeyBoardMessage(long chatId) {         InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();         InlineKeyboardButton inlineKeyboardButton1 = new InlineKeyboardButton();         InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();         inlineKeyboardButton1.setText("Тык");         inlineKeyboardButton1.setCallbackData("Button "Тык" has been pressed");         inlineKeyboardButton2.setText("Тык2");         inlineKeyboardButton2.setCallbackData("Button "Тык2" has been pressed");         List keyboardButtonsRow1 = new ArrayList();         List keyboardButtonsRow2 = new ArrayList();         keyboardButtonsRow1.add(inlineKeyboardButton1);         keyboardButtonsRow1.add(new InlineKeyboardButton().setText("Fi4a").setCallbackData("CallFi4a"));         keyboardButtonsRow2.add(inlineKeyboardButton2);         List> rowList = new ArrayList();         rowList.add(keyboardButtonsRow1);         rowList.add(keyboardButtonsRow2);         inlboardMarkup.setKeyboard(rowList);         return new SendMessage().setChatId(chatId).setText("Пример").setReplyMarkup(inlineKeyboardMarkup);     } } 
46

11.7k


46

  • Главная
  • »

  • JavaScript
  • »

  • NodeJS: Делаем кнопки в Telegram API (inline-keyboards)
Дата: 13.11.2016 в 21:44, Категория:
JavaScript
  • 30909
  • 36
  • —>

Ничего сверхъестественного использовать мы не будем. Хочу отметить, что я работаю с пакетом [node-telegram-bot-api](https://github.com/yagop/node-telegram-bot-api)
. И в следующих статьях, как и в предыдущей про то, как создать своего телеграм бота на node.js, мы будем использовать только этот пакет. Советую прочесть данную статью, чтобы вы могли работать с командой npm
.

Первым делом, мы создаем файл server.js
и устанавливаем пакет node-telegram-bot-api
.

После чего, подключаем данный пакет и создаем первую команду. Если у вас еще нету своего бота, заранее создайте его.

Использовать inline-keyboards
можно следующим способом:

var options = {   reply_markup: JSON.stringify({     inline_keyboard: [       [{ text: 'Кнопка 1', callback_data: '1' }],       [{ text: 'Кнопка 2', callback_data: 'data 2' }],       [{ text: 'Кнопка 3', callback_data: 'text 3' }]     ]   }) };  bot.onText(//start_test/, function (msg, match) {   bot.sendMessage(msg.chat.id, 'Выберите любую кнопку:', options); });

Я не стал париться над тем, какого бота мы будем делать. В качестве примера решил написать бота для теста знаний JavaScript.

Вопросы я поместил в отдельный массив, который вы можете отредактировать сами. Отмечу, что это простой пример реализации подобного бота. Если вам понравилась идея, вы с уверенностью можете его доделать.

var questions = [   {     title:'Сколько параметров можно передать функции ?',     buttons: [         [{ text: 'Ровно столько, сколько указано в определении функции.', callback_data: '0_1' }],         [{ text: 'Сколько указано в определении функции или меньше.', callback_data: '0_2' }],         [{ text: 'Сколько указано в определении функции или больше.', callback_data: '0_3' }],         [{ text: 'Любое количество.', callback_data: '0_4' }]       ],     right_answer: 4   },   {     title:'Чему равна переменная name?nvar name = "пупкин".replace("п", "д")',     buttons: [         [{ text: 'дудкин', callback_data: '1_1' }],         [{ text: 'дупкин', callback_data: '1_2' }],         [{ text: 'пупкин', callback_data: '1_3' }],         [{ text: 'ляпкин-тяпкин', callback_data: '1_4' }]       ],     right_answer: 2   },   {     title:'Чему равно 0 || "" || 2 || true ?',     buttons: [         [{ text: '0', callback_data: '2_1' }],         [{ text: '""', callback_data: '2_2' }],         [{ text: '2', callback_data: '2_3' }],         [{ text: 'true', callback_data: '2_4' }]       ],     right_answer: 3   }, ];
  • title
    — текст вопроса
  • buttons
    — варианты ответов (кнопки). В каждом ответе хранится свое значение, которое будет отправляться на сервер.
  • right_answer
    — номер верного ответа.

Разъясню еще один код.

[{ text: 'ТЕКСТ_КНОПКИ', callback_data: 'НОМЕР-ВОПРОСА_ВАРИАНТ-ОТВЕТА' }]

К примеру, если в вопрос по счету является 2-ым и у него 3 ответа, то варианты нужно прописывать так: 2_1
, 2_2
, 2_3
. Можете сделать свой вариант ответа, но я решил разделять через нижнее подчеркивание.

Так же, я разработал две функции:

  • getRandomQuestion()
    — случайный вопрос. Возвращает массив.
  • newQuestion
    — отправляет вопрос пользователю.
function getRandomQuestion(){   return questions[Math.floor(Math.random()*questions.length)]; }  function newQuestion(msg){   var arr = getRandomQuestion(); // Получаем случайный вопрос   var text = arr.title; // Вытаскиваем оттуда текст вопроса (Пример: title:'Чему равно 0 || "" || 2 || true ?')   var options = {     reply_markup: JSON.stringify({       inline_keyboard: arr.buttons, // Добавляем кнопки, которые есть в вопросе.     })   };   chat = msg.hasOwnProperty('chat') ? msg.chat.id : msg.from.id; // Если сообщение отправлял пользователь, то свойство msg.chat.id, если же он кликал на кнопку, то msg.from.id   bot.sendMessage(chat, text, options); // Отправляем пользователю сообщение: id пользователя, текст вопроса, кнопки. }

Главные функции у меня уже готовы, теперь осталось добавить событие на сообщение /start_test
. И еще одно событие, на получение ответа от кнопок.

// Отправив сообщение боту "/start_test", выполнится функция newQuestion(msg); bot.onText(//start_test/, function (msg, match) {   newQuestion(msg); });  // Ответ от кнопок bot.on('callback_query', function (msg) {   var answer = msg.data.split('_'); // Делим ответ на две части, превратив в массив. Первый элемент номер вопроса, второй будет вариант ответа.   var index = answer[0]; // Получаем номер вопроса   var button = answer[1]; // И вариант ответа    // Если присланный вариант совпадает с вариантом из массива   if (questions[index].right_answer==button) {     bot.sendMessage(msg.from.id, 'Ответ верный ✅');   } else {     bot.sendMessage(msg.from.id, 'Ответ неверный ❌');   }    // Отправляем еще один вопрос пользователю   newQuestion(msg); });

И вроде все. Теперь бот будет присылать вопросы пользователю, отправив команду /start_test
. При клике на кнопку, проверяется правильность варианта и отправляется новый вопрос.

Кстати, вы можете спокойно задавать свои команды. Неважно на русском или английском и не имеет разницы, имеется ли в команде слэш. Можно прописать хоть /дайка быстро мне тест/
и если такое сочетание найдено в сообщении, то выполнится функция генерации вопроса. Эти два слэша, нкжно ставить обязательно, так как это регулярное выражение

Пример нашего бота, вы можете увидеть ниже:

#Полный код

var TelegramBot = require('node-telegram-bot-api');  // Устанавливаем токен, который выдавал нам бот. var token = 'TELEGRAM_TOKEN';  // Включить опрос сервера var bot = new TelegramBot(token, {polling: true});  var questions = [   {     title:'Сколько параметров можно передать функции ?',     buttons: [         [{ text: 'Ровно столько, сколько указано в определении функции.', callback_data: '0_1' }],         [{ text: 'Сколько указано в определении функции или меньше.', callback_data: '0_2' }],         [{ text: 'Сколько указано в определении функции или больше.', callback_data: '0_3' }],         [{ text: 'Любое количество.', callback_data: '0_4' }]       ],     right_answer: 4   },   {     title:'Чему равна переменная name?nvar name = "пупкин".replace("п", "д")',     buttons: [         [{ text: 'дудкин', callback_data: '1_1' }],         [{ text: 'дупкин', callback_data: '1_2' }],         [{ text: 'пупкин', callback_data: '1_3' }],         [{ text: 'ляпкин-тяпкин', callback_data: '1_4' }]       ],     right_answer: 2   },   {     title:'Чему равно 0 || "" || 2 || true ?',     buttons: [         [{ text: '0', callback_data: '2_1' }],         [{ text: '""', callback_data: '2_2' }],         [{ text: '2', callback_data: '2_3' }],         [{ text: 'true', callback_data: '2_4' }]       ],     right_answer: 3   }, ];  function getRandomQuestion(){   return questions[Math.floor(Math.random()*questions.length)]; }  function newQuestion(msg){   var arr = getRandomQuestion();   var text = arr.title;   var options = {     reply_markup: JSON.stringify({       inline_keyboard: arr.buttons,       parse_mode: 'Markdown'     })   };   chat = msg.hasOwnProperty('chat') ? msg.chat.id : msg.from.id;   bot.sendMessage(chat, text, options); }  bot.onText(//start_test/, function (msg, match) {   newQuestion(msg); });  bot.on('callback_query', function (msg) {   var answer = msg.data.split('_');   var index = answer[0];   var button = answer[1];    if (questions[index].right_answer==button) {     bot.sendMessage(msg.from.id, 'Ответ верный ✅');   } else {     bot.sendMessage(msg.from.id, 'Ответ неверный ❌');   }    bot.answerCallbackQuery(msg.id, 'Вы выбрали: '+ msg.data, true);   newQuestion(msg); });

ПОДПИШИСЬ НА ОБНОВЛЕНИЯ

Только новые публикации и никакого мусора.

Рейтинг автора
5
Подборку подготовил
Андрей Ульянов
Наш эксперт
Написано статей
168
Ссылка на основную публикацию
Похожие публикации