Telegram BOT API: добавляем кнопки к сообщениям и обрабатываем их на стороне сервера

Введение

В начале апреля 2016 года вышло первое по-настоящему крупное обновление API для ботов. Изменений довольно много, поэтому материал я разобью на несколько частей. Сегодня поговорим об inline-кнопках и редактировании сообщений, а затем обсудим новые инлайн-режимы вместе со специальными кнопками для отправки геолокации и номера телефона.

Новые возможности

Начнём с двух важных изменений:

  1. Каждая кнопка, будь то обычная или инлайн, это теперь самостоятельный объект KeyboardButton или InlineKeyboardButton, не забудьте обновить своих ботов!
  2. В Inline-режиме все текстовые поля теперь представлены отдельными объектами InputMessageContent, которые, в свою очередь могут быть аж 4-х типов (подробности тут).

URL-кнопки

Итак, инлайн-кнопки. Что это такое? Это специальные объекты, которые “цепляются” к конкретным сообщениям и распространяют своё действие, в общем случае, только на них. Делятся такие кнопки на три типа: URL-кнопки, Callback-кнопки и Switch-кнопки. Самыми простыми являются кнопки-ссылки (URL). Как видно из названия, их цель — просто перекидывать пользователей по определенным веб-адресам. Давайте сразу напишем обработчик, который будет на любое сообщение отвечать каким-либо текстом и предложением перейти, например, на Яндекс.

@bot.message_handler(content_types=["text"]) def default_test(message):     keyboard = types.InlineKeyboardMarkup()     url_button = types.InlineKeyboardButton(text="Перейти на Яндекс", url="https://ya.ru")     keyboard.add(url_button)     bot.send_message(message.chat.id, "Привет! Нажми на кнопку и перейди в поисковик.", reply_markup=keyboard) 

Инлайн-клавиатура представляет собой объект InlineKeyboardMarkup, а каждая инлайн-кнопка – это объект InlineKeyboardButton. Чтобы получилась URL-кнопка, нужно указать значения параметров text (текст на кнопке) и url (валидный веб-адрес). В результате бот пришлет нам такое сообщение (см. рис.). В целях обеспечения безопасности, перед переходом по URL-кнопкам появляется всплывающее окно, в котором видна ссылка целиком.

l8_1.png

URL-кнопка

Callback-кнопки и редактирование сообщений

Прежде, чем мы перейдем к другим кнопкам, давайте познакомимся с функциями редактирования сообщений, коих тоже три: editMessageText (редактирование текста), editMessageCaption (редактирование подписи к медиа) и editMessageReplyMarkup (редактирование инлайн-клавиатуры). В рамках этого урока рассмотрим только первую функцию, остальные работают аналогично и предлагаются для самостоятельного изучения.Чтобы отредактировать сообщение, нам надо знать, про какое именно идёт речь. В случае, если оно было отправлено самим ботом, идентификаторами служит связка chat_id + message_id. Но если сообщение было отправлено в инлайн-режиме, то ориентироваться надо по параметру inline_message_id.

И вот теперь вернемся к нашим баранам кнопкам. На очереди – Callback. Это, на мой взгляд, самая крутая фича нового обновления. Колбэк-кнопки позволяют выполнять произвольные действия по их нажатию. Всё зависит от того, какие параметры каждая кнопка в себе несёт. Соответственно, все нажатия будут приводить к отправке боту объекта CallbackQuery, содержащему поле data, в котором написана некоторая строка, заложенная в кнопку, а также либо объект Message, если сообщение отправлено ботом в обычном режиме, либо поле inline_message_id, если сообщение отправлено в инлайн-режиме.

Приведу пример, после которого все вопросы должны отпасть: пусть, например, если сообщение отправлено ботом в обычном режиме, то нажатие на кнопку заменит текст сообщения на “Пыщь”, если в инлайн – то “Бдыщь”. При этом в обоих случаях значение callback_data будет равно test. Что для этого нужно сделать: во-первых, написать простейший хэндлер для всех входящих сообщений, во-вторых, написать простейший хэндлер для инлайн-сообщений, в-третьих, написать простейший хэндлер для колбэка, который определит, из какого режима пришло сообщение.

# Обычный режим @bot.message_handler(content_types=["text"]) def any_msg(message):     keyboard = types.InlineKeyboardMarkup()     callback_button = types.InlineKeyboardButton(text="Нажми меня", callback_data="test")     keyboard.add(callback_button)     bot.send_message(message.chat.id, "Я – сообщение из обычного режима", reply_markup=keyboard)   # Инлайн-режим с непустым запросом @bot.inline_handler(lambda query: len(query.query) > 0) def query_text(query):     kb = types.InlineKeyboardMarkup()     # Добавляем колбэк-кнопку с содержимым "test"     kb.add(types.InlineKeyboardButton(text="Нажми меня", callback_data="test"))     results = []     single_msg = types.InlineQueryResultArticle(         id="1", title="Press me",         input_message_content=types.InputTextMessageContent(message_text="Я – сообщение из инлайн-режима"),         reply_markup=kb     )     results.append(single_msg)     bot.answer_inline_query(query.id, results)   # В большинстве случаев целесообразно разбить этот хэндлер на несколько маленьких @bot.callback_query_handler(func=lambda call: True) def callback_inline(call):     # Если сообщение из чата с ботом     if call.message:         if call.data == "test":             bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="Пыщь")     # Если сообщение из инлайн-режима     elif call.inline_message_id:         if call.data == "test":             bot.edit_message_text(inline_message_id=call.inline_message_id, text="Бдыщь")  if __name__ == '__main__':     bot.infinity_polling() 

Запускаем бота, отправляем инлайн-сообщение, которое, в свою очередь, вызовет обычное:

l8_2.png

Нажмем на обе кнопки, результат правильный:

l8_3.png

После проверки

Таким образом, callback-кнопки – это очень мощный инструмент для взаимодействия пользователей с ботом, а редактирование сообщений дополнительно помогает в этом. Более того, нажатие на колбэк-кнопку может дополнительно тригернуть либо уведомление в верхней части экрана, либо всплывающее окно. Покажу первый вариант. Пускай помимо изменения сообщения на “Пыщь”, аналогичное слово показывается уведомлением. Для этого перепишем первое if-условие в хендлере колбэков:

if call.message:     if call.data == "test":         bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="Пыщь")         bot.answer_callback_query(callback_query_id=call.id, show_alert=False, text="Пыщь!") 

Результат – на скриншоте. Попробуйте, кстати, изменить аргумент show_alert на True и посмотрите, что получится.

l8_4.png

Всплывающее уведомление

Switch-кнопки

Наконец, остался последний тип кнопок — Switch (переключатель). Они нужны, чаще всего, для обучения пользователей работе с ботом в инлайн-режиме. Чтобы активировать сделать кнопку такого типа, нужно указать аргумент switch_inline_query либо пустой, либо с каким-либо текстом. В последнем случае этот текст будет сразу подставлен в поле ввода, например, для показа демонстрации инлайна. Как вообще работает такая кнопка? При нажатии на неё Telegram предложит выбрать чат, после чего подставит в поле ввода ник вашего бота и (если есть), текст, указанный вами в аргументе switch_inline_query. Давайте попробуем так сделать. Добавим кнопку, которая будет перенаправлять пользователя в какой-либо чат и предлагать в инлайн-режиме запрос “Telegram”. Код всего хендлера выглядит вот так:

@bot.message_handler(content_types=["text"]) def any_msg(message):     keyboard = types.InlineKeyboardMarkup()     switch_button = types.InlineKeyboardButton(text="Нажми меня", switch_inline_query="Telegram")     keyboard.add(switch_button)     bot.send_message(message.chat.id, "Я – сообщение из обычного режима", reply_markup=keyboard) 

Теперь, если мы нажмем на кнопку и выберем чат, вот что получится:

l8_5.png

Результат

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

← Урок №7Урок №9 →

Привет, HABR! В этой статье постараюсь показать маленький пример, использования клавиатуры под сообщением в Telegram. То есть мы с помощью бота будем отправлять пост в наш канал с использованием языка программирования PHP. Telegram BOT API неплохо документирован, но всё же остаются вопросы у новичков, как это всё собрать чтобы работало.InlineKeyboardMarkup — этот объект представляет встроенную клавиатуру, которая появляется под соответствующим сообщением.

Что мы хотим сделать?

При публикации сообщение с использованием бота под сами сообщением вывести 5 кнопок (Нравиться, Ха-ха, Ух ты!, Сочувствую, Возмутительно) в виде смайликов. И цель их отображать количество людей, которые нажали на определённую кнопку. В сети уже много готовых библиотек на PHP по работе с Telegram BOT API. Для нашей задачи они не понадобятся. Итак приступаем.

Нам понадобится

  1. Создать telegram канал;
  2. Создать бота;
  3. Добавить бота в telegram канал;
  4. Хостинг на котором будет лежать файл обработки логики кнопок;
  5. База данных в которой будем хранить id пользователей, которые нажал на кнопку.

Логика

При нажатии на определённую кнопку добавить счётчик в неё. Пользователь может нажать только на одну из 5-кнопок. В БД мы будем хранить ID сообщения и ID пользователей, которые уже отреагировали на сообщение.

Структура файлов

  1. send.php — отправляем сообщение в Telegram канал
  2. callback.php — обрабатываем клики кнопок получены из Telegram канала

Содержание файла send.php

    define( 'TOKEN', '' );    define( 'CHAT_ID', '' @name_chat    define( 'API_URL', 'https://api.telegram.org/bot' . TOKEN . '/' );        function request($method, $params = array()) {       if ( (!empty($params) ) {          $url = API_URL . $method . "?" . http_build_query($params);       } else {          $url = API_URL . $method;       }        return json_decode(file_get_contents($url), JSON_OBJECT_AS_ARRAY);    }     $keyboard = array(       array(          array('text'=>':like:','callback_data'=>'{"action":"like","count":0,"text":":like:"}'),          array('text'=>':joy:','callback_data'=>'{"action":"joy","count":0,"text":":joy:"}'),          array('text'=>':hushed:','callback_data'=>'{"action":"hushed","count":0,"text":":hushed:"}'),          array('text'=>':cry:','callback_data'=>'{"action":"cry","count":0,"text":":cry:"}'),          array('text'=>':rage:','callback_data'=>'{"action":"rage","count":0,"text":":rage:"}')       )    );     request("sendMessage", array(       'chat_id' => CHAT_ID,       'text' => "hello world!",       'disable_web_page_preview' => false,       'reply_markup' => json_encode(array('inline_keyboard' => $keyboard))    )); 

Как видим по коду он очень просто и мы тут просто отправляем «Hello World!» сообщение и к нему добавляем 5 кнопок. Нам также нужно установить Webhook для бота. Это нужно для того, чтобы указать боту куда (http://site.ru/callback.php) отправлять результат обработки кнопок. Это очень просто сделать, в браузер соберите ссылку такого формата:

https://api.telegram.org/bot{my_bot_token}/setWebhook?url={url_to_send_updates_to}

В ответ получите json примерно такого содержания:

{"ok":true,"result":true,"description":"Webhook was set"}

Содержание файла callback.php

    define( 'TOKEN', '' );    define( 'CHAT_ID', '' ); // chat    define( 'API_URL', 'https://api.telegram.org/bot' . TOKEN . '/' );     function request($method, $params = array()) {       if ( (!empty($params) ) {          $url = API_URL . $method . "?" . http_build_query($params);       } else {          $url = API_URL . $method;       }        return json_decode(file_get_contents($url), JSON_OBJECT_AS_ARRAY);    }     function editMessageReplyMarkup($params){       //В этом цикле мы изменяем сами кнопки, а именно текст кнопки и значение параметра callback_data       foreach ( $params['inline_keyboard'][0] as $key => $value ) {          $data_for = json_decode($value->callback_data, true); // изначально у нас callback_data храниться в виде json-а, декатируем в массив          if ( $params['data']['action'] == $data_for['action'] ) { // определяем, на какую именно кнопку нажал пользователь под сообщением             $data_for['count']++; //плюсуем единичку             $value->text = $data_for['text'] . " " . $data_for['count']; // Изменяем текст кнопки смайлик + количество лайков          }          $value->callback_data = json_encode($data_for); // callback_data кнопки кодируем в json          $params['inline_keyboard'][0][$key] = (array)$value; // изменяем кнопку на новую       }        //Изменяем кнопки к сообщению       request("editMessageReplyMarkup", array(          'chat_id' => CHAT_ID,          'message_id' => $params['message_id'],          'reply_markup' => json_encode(array('inline_keyboard' => $params['inline_keyboard'])),       ));        //Выводим сообщение в чат       request("answerCallbackQuery", array(          'callback_query_id' => $params['callback_query_id'],          'text' => "Спасибо! Вы поставили " . $params['data']['text'],       )); }     $result = json_decode(file_get_contents('php://input')); // получаем результат нажатия кнопки    $inline_keyboard = $result->callback_query->message->reply_markup->inline_keyboard; // текущее состояние кнопок при нажатии на одну из 5 кнопок    $data = json_decode($result->callback_query->data, true); // получаем значение с кнопки, а именно с параметра callback_data нажатой кнопки    $message_id = $result->callback_query->message->message_id; // ID сообщения в чате    $callback_query_id = $result->callback_query->id; //ID полученного результата    $user_id = $result->callback_query->from->id; // ID пользователя     $db_message = $db->super_query("SELECT * FROM bot_like WHERE message_id={$message_id}"); //Ищем в БД ID сообщения     /*    Я использую библиотеку ($db = new db;) от CMS DLE для работы с БД.    super_query - этот метод возвращает первую найденную запись в виде массива    */     if ( $db_message === null ) {       // Если не нашли в БД ID сообщения, записываем в БД текущий ID сообщения и ID пользователя, который отреагировал (нажал на одну из 5 кнопок) на сообщение.        $db->query("INSERT INTO " . PREFIX . "_posting_tg_bot_like (message_id, users) VALUES ('{$message_id}', '{$user_id}')");        editMessageReplyMarkup(array(          'inline_keyboard' => $inline_keyboard,          'data' => $data,          'message_id' => $message_id,          'callback_query_id' => $callback_query_id       ));    } else {       //Если в БД нашли сообщение       $users = explode(",", $db_message['users']);              // Если нашли в БД пользователя, выводим сообщение "Вы уже нажали на одну из 5 кнопок"       if( in_array($user_id, $users) ) {          request("answerCallbackQuery", array(             'callback_query_id' => $callback_query_id,             'text' => "Вы уже отреагировали на новость",          ));       }         // Если не нашли ID в БД изменяем одну из 5 кнопок и добавляем ID пользователя       else {          editMessageReplyMarkup(array(             'inline_keyboard' => $inline_keyboard,             'data' => $data,             'message_id' => $message_id,             'callback_query_id' => $callback_query_id          ));           array_push($users, $user_id);          $users = implode(',', $users);          $db->query("UPDATE bot_like SET users='{$users}' WHERE message_id='{$message_id}'");     } } 

Вот и всё, такой маленький пример получился, за ранние извините, если код написан безобразно. Можно расширить этот код до нужной функциональности, это уже зависит от вашей задачи. Моя задача была показать маленький пример, и я надеюсь у меня это получилось. Спасибо за внимание, встретимся в комментариях. Используемые источники:

  • https://mastergroosha.github.io/telegram-tutorial/docs/lesson_08/
  • https://habr.com/ru/sandbox/133464/

Оцените статью
Рейтинг автора
5
Материал подготовил
Илья Коршунов
Наш эксперт
Написано статей
134
Для этой статьи работает наш еженедельный конкурс комментаторов.
Оставьте ваш комментарий в форме ниже и автору лучшего комментария за неделю (самого полезного по нашему мнению) мы подарим приз - iPhone XR 128Gb.
Чтобы мы могли связаться с вами обязательно при отправке комментария укажите ваш e-mail.
Результаты вы сможете узнать на этой странице через неделю.
Добавить комментарий