NodeJS: Делаем кнопки в Telegram API (inline-keyboards)

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

Последнее время, я заметил, что на мой блог все чаще переходят по запросам связанными с Telegram API. Да и сам я в последнее время увлекся разработкой Telegram-ботов и NodeJS. В данной статье, я хочу рассказать о том, как добавлять inline-keybord к сообщениям. Разница между ReplyKeyboardMarkup, незначительная. Однако, в этой статьи я опишу пример использования первого варианта с inline-keyboards.

Пример данной клавиатуры, вы можете увидеть на скриншоте ниже. Данный бот публикует записи в мою группу Вконтакте с разных IT-сообществ.

Ничего сверхъестественного использовать мы не будем. Хочу отметить, что я работаю с пакетом [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);
});