Как в Телеграме можно сделать кнопку для бота на канале, пошаговая инструкция

В этой главе мы познакомимся с такой замечательной фичей Telegram-ботов, как кнопки. Прежде всего, чтобы избежать путаницы, определимся с названиями. То, что цепляется к низу экрана вашего устройства, будем называть обычными кнопками, а то, что цепляется непосредственно к сообщениям, назовём инлайн-кнопками. Ещё раз картинкой:

Обычные кнопки¶

Кнопки как шаблоны¶

Этот вид кнопок появился вместе с Bot API в далёком 2015 году и представляет собой не что иное, как шаблоны сообщений (за исключением нескольких особых случаев, но о них позже). Принцип простой: что написано на кнопке, то и будет отправлено в текущий чат. Соответственно, чтобы обработать нажатие такой кнопки, бот должен распознавать входящие текстовые сообщения.

Напишем хэндлер, который будет при нажатии на команду /start отправлять сообщение с двумя кнопками:

 # from aiogram import types @dp.message_handler(commands="start") async def cmd_start(message: types.Message):     keyboard = types.ReplyKeyboardMarkup()     button_1 = types.KeyboardButton(text="С пюрешкой")     keyboard.add(button_1)     button_2 = "Без пюрешки"     keyboard.add(button_2)     await message.answer("Как подавать котлеты?", reply_markup=keyboard) 

Обратите внимание, что т.к. обычные кнопки суть шаблоны сообщений, то их можно создавать не только как объекты KeyboardButton, но и как обычные строки. Что ж, запустим бота и обалдеем от громадных кнопок:

Как-то некрасиво. Во-первых, хочется сделать кнопки поменьше, а во-вторых, расположить их горизонтально. Почему вообще они такие большие? Дело в том, что по умолчанию «кнопочная» клавиатура должна занимать на смартфонах столько же места, сколько и обычная буквенная. Для уменьшения кнопок к объекту клавиатуры надо указать дополнительный параметр resize_keyboard=True. Но как заменить вертикальные кнопки на горизонтальные? С точки зрения Bot API, клавиатура — это массив массивов кнопок, а если говорить проще, массив строк. Метод add() при каждом вызове создаёт новую строку (ряд) и принимает произвольное число аргументов по количеству желаемых кнопок в строке. Перепишем наш код, чтобы было красиво:

 @dp.message_handler(commands="start") async def cmd_start(message: types.Message):     keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True)     buttons = ["С пюрешкой", "Без пюрешки"]     keyboard.add(*buttons)     await message.answer("Как подавать котлеты?", reply_markup=keyboard) 

Обратите внимание на конструкцию *buttons. Здесь вам не C++ и звёздочка используется для распаковки списка. Подробнее об операторах * и ** можно прочитать здесь.

Смотрим — действительно красиво:

Осталось научить бота реагировать на нажатие таких кнопок. Как уже было сказано выше, необходимо делать проверку на полное совпадение текста. Сделаем это двумя способами: через специальный фильтр Text и обычной лямбдой:

 # from aiogram.dispatcher.filters import Text @dp.message_handler(Text(equals="С пюрешкой")) async def with_puree(message: types.Message):     await message.reply("Отличный выбор!")   @dp.message_handler(lambda message: message.text == "Без пюрешки") async def without_puree(message: types.Message):     await message.reply("Так невкусно!") 

Чтобы удалить кнопки, необходимо отправить новое сообщение со специальной «удаляющей» клавиатурой типа ReplyKeyboardRemove. Например: await message.reply("Отличный выбор!", reply_markup=types.ReplyKeyboardRemove())

У объекта обычной клавиатуры есть ещё две полезных опции: one_time_keyboard для скрытия кнопок после нажатия и selective для показа клавиатуры лишь некоторым участникам группы. Их использование остаётся для самостоятельного изучения.

Помимо стандартных опций, описанных выше, aiogram немного расширяет функциональность клавиатур параметром row_width. При его использовании, фреймворк автоматически разобьёт массив кнопок на строки по N элементов в каждой, где N — значение row_width, например, row_width=2. Попробуйте!

Специальные обычные кнопки¶

По состоянию на конец ужасного 2020 года в Telegram существует три специальных вида обычных кнопок, не являющихся шаблонами: для отправки текущей геолокации, для отправки своего номера телефона и ярлык для создания опроса/викторины. Для первых двух типов достаточно установить булевый флаг, а для опросов и викторин нужно передать специальный тип KeyboardButtonPollType и, по желанию, указать тип создаваемого объекта.

Впрочем, проще один раз увидеть код:

 @dp.message_handler(commands="special_buttons") async def cmd_special_buttons(message: types.Message):     keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True)     keyboard.add(types.KeyboardButton(text="Запросить геолокацию", request_location=True))     keyboard.add(types.KeyboardButton(text="Запросить контакт", request_contact=True))     keyboard.add(types.KeyboardButton(text="Создать викторину",                                       request_poll=types.KeyboardButtonPollType(type=types.PollType.QUIZ)))     await message.answer("Выберите действие:", reply_markup=keyboard) 

Инлайн-кнопки¶

URL-кнопки и колбэки¶

В отличие от обычных кнопок, инлайновые цепляются не к низу экрана, а к сообщению, с которым были отправлены. В этой главе мы рассмотрим два типа таких кнопок: URL и Callback. Ещё один — Switch — будет рассмотрен в главе про инлайн-режим.

Login- и Pay-кнопки в книге рассматриваться не будут вообще. Если у кого-то есть желание помочь хотя бы с рабочим кодом для авторизации или оплаты, пожалуйста, создайте Pull Request на GitHub. Спасибо!

Самые простые инлайн-кнопки относятся к типу URL, т.е. «ссылка». Поддерживаются только протоколы HTTP(S) и tg://

 @dp.message_handler(commands="inline_url") async def cmd_inline_url(message: types.Message):     buttons = [         types.InlineKeyboardButton(text="GitHub", url="https://github.com"),         types.InlineKeyboardButton(text="Оф. канал Telegram", url="tg://resolve?domain=telegram")     ]     keyboard = types.InlineKeyboardMarkup(row_width=1)     keyboard.add(*buttons)     await message.answer("Кнопки-ссылки", reply_markup=keyboard) 

А если хотите обе кнопки в ряд, то уберите row_width=1 (тогда будет использоваться значение по умолчанию 3).

С URL-кнопками больше обсуждать, по сути, нечего, поэтому перейдём к гвоздю сегодняшней программы — Callback-кнопкам. Это очень мощная штука, которую вы можете встретить практически везде. Кнопки-реакции у постов (лайки), меню у @BotFather и т.д. Суть в чём: у колбэк-кнопок есть специальное значение (data), по которому ваше приложение опознаёт, что нажато и что надо сделать. И выбор правильного data очень важен! Стоит также отметить, что, в отличие от обычных кнопок, нажатие на колбэк-кнопку позволяет сделать практически что угодно, от заказа пиццы до перезагрузки сервера.

Напишем хэндлер, который по команде /random будет отправлять сообщение с колбэк-кнопкой:

 @dp.message_handler(commands="random") async def cmd_random(message: types.Message):     keyboard = types.InlineKeyboardMarkup()     keyboard.add(types.InlineKeyboardButton(text="Нажми меня", callback_data="random_value"))     await message.answer("Нажмите на кнопку, чтобы бот отправил число от 1 до 10", reply_markup=keyboard) 

Но как же обработать нажатие? Если раньше мы использовали message_handler для обработки входящих сообщений, то теперь будем использовать callback_query_handler для обработки колбэков. Ориентироваться будем на «значение» кнопки, т.е. на её data:

 @dp.callback_query_handler(text="random_value") async def send_random_value(call: types.CallbackQuery):     await call.message.answer(str(randint(1, 10))) 

Важно

Несмотря на то, что параметр кнопки callback_data, а значение data лежит в одноимённом поле data объекта CallbackQuery, собственный фильтр aiogram называется text.

Ой, а что это за часики? Оказывается, сервер Telegram ждёт от нас подтверждения о доставке колбэка, иначе в течение 30 секунд будет показывать специальную иконку. Чтобы скрыть часики, нужно вызвать метод answer() у колбэка (или использовать метод API answer_callback_query()). В общем случае, в метод answer() можно ничего не передавать, но можно вызвать специальное окошко (всплывающее сверху или поверх экрана):

 @dp.callback_query_handler(text="random_value") async def send_random_value(call: types.CallbackQuery):     await call.message.answer(str(randint(1, 10)))     await call.answer(text="Спасибо, что воспользовались ботом!", show_alert=True)     # или просто await call.answer() 

Обратите внимание

В функции send_random_value мы вызывали метод answer() не у message, а у call.message. Это связано с тем, что колбэк-хэндлеры работают не с сообщениями (тип Message), а с колбэками (тип CallbackQuery), у которого другие поля, и само сообщение — всего лишь его часть. Учтите также, что message — это сообщение, к которому была прицеплена кнопка (т.е. отправитель такого сообщения — сам бот). Если хотите узнать, кто нажал на кнопку, смотрите поле from (в вашем коде это будет call.from_user, т.к. слово from зарезервировано в Python)

Когда вызывать answer()?

В общем случае, главное — просто не забыть сообщить Telegram о получении колбэк-запроса, но я рекомендую ставить вызов answer() в самом конце, и вот почему: если вдруг в процессе обработки колбэка случится какая-то ошибка и бот нарвётся на необработанное исключение, пользователь увидит неубирающиеся полминуты часики и поймёт, что что-то не так. В противном случае, часики исчезнут, а пользователь останется в неведении, выполнился его запрос успешно или нет.

Перейдём к примеру посложнее. Пусть пользователю предлагается сообщение с числом 0, а внизу три кнопки: +1, -1 и Подтвердить. Первыми двумя он может редактировать число, а последняя удаляет всю клавиатуру, фиксируя изменения. Хранить значения будем в памяти в словаре (про конечные автоматы поговорим как-нибудь в другой раз).

 # Здесь хранятся пользовательские данные. # Т.к. это словарь в памяти, то при перезапуске он очистится user_data = {}  def get_keyboard():     # Генерация клавиатуры.     buttons = [         types.InlineKeyboardButton(text="-1", callback_data="num_decr"),         types.InlineKeyboardButton(text="+1", callback_data="num_incr"),         types.InlineKeyboardButton(text="Подтвердить", callback_data="num_finish")     ]     # Благодаря row_width=2, в первом ряду будет две кнопки, а оставшаяся одна     # уйдёт на следующую строку     keyboard = types.InlineKeyboardMarkup(row_width=2)     keyboard.add(*buttons)     return keyboard  async def update_num_text(message: types.Message, new_value: int):     # Общая функция для обновления текста с отправкой той же клавиатуры     await message.edit_text(f"Укажите число: {new_value}", reply_markup=get_keyboard())  @dp.message_handler(commands="numbers") async def cmd_numbers(message: types.Message):     user_data[message.from_user.id] = 0     await message.answer("Укажите число: 0", reply_markup=get_keyboard())  @dp.callback_query_handler(Text(startswith="num_")) async def callbacks_num(call: types.CallbackQuery):     # Получаем текущее значение для пользователя, либо считаем его равным 0     user_value = user_data.get(call.from_user.id, 0)     # Парсим строку и извлекаем действие, например `num_incr` -> `incr`     action = call.data.split("_")[1]     if action == "incr":         user_data[call.from_user.id] = user_value+1         await update_num_text(call.message, user_value+1)     elif action == "decr":         user_data[call.from_user.id] = user_value-1         await update_num_text(call.message, user_value-1)     elif action == "finish":         # Если бы мы не меняли сообщение, то можно было бы просто удалить клавиатуру         # вызовом await call.message.delete_reply_markup().         # Но т.к. мы редактируем сообщение и не отправляем новую клавиатуру,          # то она будет удалена и так.         await call.message.edit_text(f"Итого: {user_value}")     # Не забываем отчитаться о получении колбэка     await call.answer() 

И, казалось бы, всё работает:

Но теперь представим, что ушлый пользователь сделал следующее: вызвал команду /numbers (значение 0), увеличил значение до 1, снова вызвал /numbers (значение сбросилось до 0) и отредактировал нажал кнопку “+1” на первом сообщении. Что произойдёт? Бот по-честному отправит запрос на редактирование текста со значением 1, но т.к. на том сообщении уже стоит цифра 1, то Bot API вернёт ошибку, что старый и новый тексты совпадают, а бот словит исключение: aiogram.utils.exceptions.MessageNotModified: Message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message

С этой ошибкой вы, скорее всего, будете поначалу часто сталкиваться, пытаясь редактировать сообщения. Но, в действительности, решается проблема очень просто: мы проигнорируем исключение MessageNotModified. Из первой главы вы уже знаете о такой прекрасной штуке, как errors_handler, но в этот раз мы поступим чуть иначе и перепишем функцию update_num_text() следующим образом:

 # from aiogram.utils.exceptions import MessageNotModified # from contextlib import suppress  async def update_num_text(message: types.Message, new_value: int):     with suppress(MessageNotModified):         await message.edit_text(f"Укажите число: {new_value}", reply_markup=get_keyboard()) 

Если теперь вы попробуете повторить пример выше, то указанное исключение в этом блоке кода бот просто-напросто проигнорирует.

Фабрика колбэков¶

В aiogram существует т.н. фабрика колбэков. Вы создаёте объект CallbackData, указываете ему префикс и произвольное количество доп. аргументов, которые в дальнейшем указываете при создании колбэка для кнопки. Например, рассмотрим следующий объект:

 # from aiogram.utils.callback_data import CallbackData cb= CallbackData("post", "id", "action") 

Тогда при создании кнопки вам надо указать её параметры так:

 button =  types.InlineKeyboardButton(     text="Лайкнуть",      callback_data=cb.new(id=5, action="like") ) 

В примере выше в кнопку запишется callback_data, равный post:5:like, а хэндлер на префикс post будет выглядеть так:

 @dp.callback_query_handler(cb.filter()) async def callbacks(call: types.CallbackQuery, callback_data: dict):     post_id = callback_data["id"]     action = callback_data["action"] 

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

 # fabnum - префикс, action - название аргумента, которым будем передавать значение callback_numbers = CallbackData("fabnum", "action")   def get_keyboard_fab():     buttons = [         types.InlineKeyboardButton(text="-1", callback_data=callback_numbers.new(action="decr")),         types.InlineKeyboardButton(text="+1", callback_data=callback_numbers.new(action="incr")),         types.InlineKeyboardButton(text="Подтвердить", callback_data=callback_numbers.new(action="finish"))     ]     keyboard = types.InlineKeyboardMarkup(row_width=2)     keyboard.add(*buttons)     return keyboard   async def update_num_text_fab(message: types.Message, new_value: int):     with suppress(MessageNotModified):         await message.edit_text(f"Укажите число: {new_value}", reply_markup=get_keyboard_fab())   @dp.message_handler(commands="numbers_fab") async def cmd_numbers(message: types.Message):     user_data[message.from_user.id] = 0     await message.answer("Укажите число: 0", reply_markup=get_keyboard_fab())   @dp.callback_query_handler(callback_numbers.filter(action=["incr", "decr"])) async def callbacks_num_change_fab(call: types.CallbackQuery, callback_data: dict):     user_value = user_data.get(call.from_user.id, 0)     action = callback_data["action"]     if action == "incr":         user_data[call.from_user.id] = user_value + 1         await update_num_text_fab(call.message, user_value + 1)     elif action == "decr":         user_data[call.from_user.id] = user_value - 1         await update_num_text_fab(call.message, user_value - 1)     await call.answer()   @dp.callback_query_handler(callback_numbers.filter(action=["finish"])) async def callbacks_num_finish_fab(call: types.CallbackQuery):     user_value = user_data.get(call.from_user.id, 0)     await call.message.edit_text(f"Итого: {user_value}")     await call.answer() 

На этом глава про кнопки окончена, но про некоторые других их виды мы поговорим в следующих главах.

Многие пользователи мессенджера обращают внимание на кнопки в Телеграм под постами. Благодаря им получается выразить отношение к определенной записи. Но в стандартном наборе функций такого нет. Поэтому востребованным остается вопрос – как их сделать и вставить в свое послание.

Содержание

Типы встроенных кнопок в Telegram

Клавиатура Telegram, расположенная в ряд в строке под сообщением, подвязывается к каждой конкретной новости и позволяет ее динамически видоизменять. Чтобы их добавлять, используется специальный бот. На сегодняшний день существует 3 типа кнопок. Рассмотрим подробно каждый из них.

URL-кнопки

URL-кнопки в Телеграм.

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

У них есть соответствующий ярлык, расположенный справа экрана в углу.

Switch-кнопки

Такие кнопки в телеграм-боте используют с целью перехода на другой чат с непосредственной активацией общения с программой. Чтобы подключить его вручную, можно просто прописать «@название бота». Но применение данного вида клавиш делает всю работу в автоматическом режиме, позволяя существенно сэкономить время.

Для создания переключателя указывается «switch_inline_query». Аргумент может оставаться пустым или же иметь какой-то текст.

Callback-кнопки

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

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

Работа с ControllerBot и BotFather

Эти боты позволяют создавать управление собственной записью и ее форматирование.

Юзеры, которые решат их поставить, смогут:

  • добавлять всевозможные реакции к посту (лайки и т.д.);
  • работать с отложенными записями – редактировать их, планировать время размещения;
  • проводить форматирование текста и выставлять анкорные ссылки;
  • изучать статистику по количеству просмотров или новых читателей группы;
  • размещать активные  клавиши, при нажатии на которые происходит подписка, обсуждение с друзьями, предложение поделиться информацией или открытие сторонних ресурсов.

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

Подключение к каналу и настройка

Рассмотрим порядок работы с ботом на примере Controllerbot. Для начала его нужно создать. Зайдя в @BotFather, необходимо кликнуть на Старт и написать команду /newbot.

Далее алгоритм следующий:

  1. Придумать имя и ввести его.
  2. Скопировать робота, так как он пригодится для подключения или добавления на канал в качестве администратора.
  3. Для настроек в поиске следует отыскать «КонтролерБота».
  4. Прописать  /add и кликнуть по иконке «Подключить».
  5. Осталось только скопировать токен, отправив его роботу. Дополнительно нужно указать @username канала и город, что позволит корректно отражать настройки постинга.

Добавление реакций

Кнопки реакций в Телеграме.

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

Чтобы сделать такую кнопку в телеграм-канале, нужно:

  1. Открыть пункт «Добавить реакции». Допускается присоединять до 6 различных смайликов к одному посту. Впоследствии пользователь сможет отслеживать, сколько человек кликнуло по иконке.
  2. Чтобы они автоматически прикреплялись к каждой записи, используется «Реакции по умолчанию», а список доступных эмоций отправляется роботу.

Добавление URL-кнопки

Это специальные клавиши, в которые прячут ссылки, позволяющие переходить к опросу, голосованию, другому каналу или сайту. Чтобы их разместить, после написания сообщения, выбирается пункт «Добавить урл» и вводится имя по формату «название-ссылка».

Виджет обратной связи с Telegram

Существует несколько вариантов:

  1. Использование сервиса Telegram.im. Для этого надо выбрать пункт «Сделать кнопку в Телеграме», а на открытой вкладке прописать имя канала. По желанию здесь можно произвести и другие настройки (определить фон, поэкспериментировать с размером текста). По завершению кликнуть по «Получить код», скопировать его и поставить на нужное место.
  2. Tgwidget.com. Тут представлено два варианта иконок и один виджет с трансляцией новостей ресурса, которые отображаются у владельцев подписки. На сайте необходимо зарегистрироваться и получить код для личного кабинета, который добавляется непосредственно в мессенджер.

Выводы

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

Детальная инструкция видна на видео:

Сегодняшняя статья будет посвящена тому, каким образом можно создать для бота пользовательскую клавиатуру, что это даёт и как со всем этим работать. Все приводимые в статье примеры кода будут на php.

Какие вообще бывают пользовательские клавиатуры

Пользовательские клавиатуры в Telegram бывают двух видов: Inline и Reply.

Inline (встроенные)-клавиатуры просто добавляют кнопки внизу сообщения, вот так:

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

Интересной фишкой подобных клавиатур является возможность правки «на лету» сообщений, к которым они привязаны.

Reply-клавиатуры (клавиатуры для ответа) напротив, не привязаны к конкретному сообщению и подменяют (или дополняют) в чате с ботом традиционную клавиатуру. Эти клавиатуры вызывают отправку сообщений в чат и выглядят вот так:

Как сгенерировать и удалить клавиатуру

Для того, чтобы сгенерировать какую-либо пользовательскую клавиатуру — нужно при обращении к одному из предназначенных для отправки данных API-методов (например, при к SendMessage) задать параметр reply_markup, описывающий объект типа InlineKeyboardMarkup — для создания inline-клавиатуры, или объект типа ReplyKeyboardMarkup — для создания reply-клавиатуры. Подробнее о том, к каким методам можно прицепить reply_markup, а к каким — нет, можно прочитать в документации.

Описывающие клавиатуры объекты представляют собой многомерные массивы, содержащие наборы записей о каждой из кнопок (подпись, данные колбэка, открываемый при нажатии url и так далее).

Таким образом, для генерирования клавиатуры нам нужно сформировать в коде соответствующего типа объект, описывающий создаваемую клавиатуру (назовём этот объект, например, $keyboard), закодировать этот объект в виде json-строки и добавить эту json-строку в качестве параметра reply_markup при отправке запроса (GET или POST) telegram-боту.

Пример создания inline-клавиатуры (php)

// создаём $keyboard - объект, описывающий клавиатуру $keyboard = [ "inline_keyboard" => [ /* первый ряд кнопок - массив из наборов {подпись кнопки, даные для колбэка} */ [ /* первые две кнопки вызывают колбэк, а третья - открытие url-а */ [ "text" => "button 1", "callback_data" => "data 1" ], [ "text" => "button 2", "callback_data" => "data 2" ], [ "text" => "button 3", "url" => $any_url ] ] /* здесь мог бы быть второй массив для второго ряда кнопок и так далее */ ] ]; ... ... // далее нужно просто добавить этот параметр в запрос (GET или POST) в качестве параметра reply_markup // например, при использовании в GET-запросе: $keyboard_json = json_encode($keyboard); // перекодируем в json // формируем url для get-запроса и добавляем к этому url-у параметр reply_markup с описанием нашей клавиатуры $url=$telegram_api_url.$bot_token.'/sendMessage?chat_id='.$chat_id.'&text='.urlencode($text_message).'&parse_mode=HTML'.'&reply_markup='.$keyboard_json; // осталось только выполнить GET-запрос, например с помощью file_get_contents (ну или curl :)) file_get_contents($url); // отправляем get-запрос на сформированный url

[свернуть] Пример создания reply-клавиатуры (php)

// создаём $keyboard - объект, описывающий клавиатуру $keyboard = [ "keyboard" => [ /* первый ряд кнопок - массив из наборов {подпись кнопки} */ [ /* перва кнопка отправляет команду /any_command в виде текстового сообщения, вторая - данные геолокации */ [ "text" => "/any_command" ], [ "text" => "my location", "request_location" => true ] ] /* здесь мог бы быть второй массив для второго ряда кнопок и так далее */ ] ]; ... ... // далее нужно просто добавить этот параметр в запрос (GET или POST) в качестве параметра reply_markup // например, при использовании в GET-запросе: $keyboard_json = json_encode($keyboard); // перекодируем в json // формируем url для get-запроса и добавляем к этому url-у параметр reply_markup с описанием нашей клавиатуры $url=$telegram_api_url.$bot_token.'/sendMessage?chat_id='.$chat_id.'&text='.urlencode($text_message).'&parse_mode=HTML'.'&reply_markup='.$keyboard_json; // осталось только выполнить GET-запрос, например с помощью file_get_contents (ну или curl :)) file_get_contents($url); // отправляем get-запрос на сформированный url

[свернуть]

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

Для удаления reply-клавиатуры нужно в параметре reply_markup послать объект, типа ReplyKeyboardRemove.

Пример удаления reply-клавиатуры (php)

// создаём $remove_keyboard - объект, описывающий удаление reply-клавиатуры $remove_keyboard = [ "remove_keyboard" => true ]; ... ... // далее нужно просто добавить этот объект в запрос (GET или POST) в качестве параметра reply_markup // например, при использовании в GET-запросе: $remove_keyboard_json = json_encode($remove_keyboard); // перекодируем в json // формируем url для get-запроса и добавляем к этому url-у параметр reply_markup с описывающим удаление клавиатуры объектом $url=$telegram_api_url.$bot_token.'/sendMessage?chat_id='.$chat_id.'&text='.urlencode($text_message).'&parse_mode=HTML'.'&reply_markup='.$remove_keyboard_json; // осталось только выполнить GET-запрос, например с помощью file_get_contents (ну или curl :)) file_get_contents($url); // отправляем get-запрос на сформированный url

[свернуть]

Правка сообщений «на лету»

Одна из прикольных фишек inline-клавиатур — возможность «на лету» править сообщения, к которым эти клавиатуры привязаны. Как я уже писал выше, нажатие на кнопки inline-клавиатур не вызывает отправку сообщений в чат, но то же время, получив callback мы всегда можем понять, к какому сообщению была привязана нажатая кнопка. Остаётся только исправить это сообщение при помощи одного из методов Updating messages.

Как получить callback? Для этого просто нужно запросить, получить и обработать очередной update, если бот работает через longpolling, или выполнить только два последних пункта, если бот работает через webhook (в этом случае, как вы помните, ничего запрашивать не нужно, так как телега сама знает по какому адресу отправлять апдейты). Данные колбэка будут содержаться в поле «callback_query» полученного апдейта.

Пример апдейта, полученного после нажатия на одну из кнопок inline-клавиатуры

"{"ok":true,"result": [ { "update_id":xxxxxxxxxx, "callback_query": { "id":"xxxxxxxxxxx", "from": { "id":xxxxxxxxx, "first_name":"xxxxxxx", "last_name":"xxxxxxx", "username":"xxxxxxxxx", "language_code":"ru" }, "message": { "message_id":xxxx, "from": { "id":xxxxxxxxx, "first_name":"xxxxxxxx", "username":"xxxxxxx" }, "chat": { "id":xxxxxxxx, "first_name":"xxxxxxx", "last_name":"xxxxxxxx", "username":"xxxxxxx", "type":"private" }, "date":1499854111, "text":"test" }, "chat_instance":"xxxxxxxxxxxx", "data":"1" } } ] }"

[свернуть] Пример редактирования сообщения налету

// если мы определили, что нам прилетел именно колбэк (по наличию поля "callback_query"), то: // выделяем из этого колбэка необходимые для редактирования сообщения данные $chat_id = $input_array['callback_query']['message']['chat']['id']; // идентификатор чата $message_id = $input_array['callback_query']['message']['message_id']; //идентификатор сообщения $data = $input_array['callback_query']['data']; // данные, которые были отправлены при нажатии на кнопку ... ... // далее нужно просто выполнить запрос (GET или POST), обратившись, например, к методу editMessageText $url=$telegram_api_url.$bot_token.'/editMessageText?chat_id='.$chat_id.'&message_id='.$message_id.'&text='.urlencode($edited_message).'&reply_markup='.$keyboard_json; // $edited_message - отредактированный текст сообщения // $keyboard_json - клавиатура, которая будет содержаться в отредактированном сообщении file_get_contents($url); // отправляем get-запрос на сформированный url

[свернуть]

Вот и всё. Как видите, всё достаточно просто и понятно. В telegram вообще всегда всё просто, понятно и подробно описано. Вообще не понимаю, зачем нужны всякие библиотеки и фреймворки. Все они выглядят гораздо страшнее и монструознее, чем исходные описания из API. Это при том, что все эти фреймворки добавляют коду веса, ошибок и нестабильности. Уф, надо вступить в какое-нибудь движение за чистоту кода. Ну ладно, это я уже отвлёкся. Пока!

В общем-то, эта статья является дополнением этой — Пишем бота для Telegram на php, так-что если вы попали сюда впервые — советую, для начала, прочитать ее.

Как, используя бота, публиковать новости в свой канал

Поясню для читателя, что такое канал. Канал, это что-то вроде паблика вк, на нем нельзя общаться, но можно читать, что пишет автор этого самого канала. Как пример — Красные Котлеты — это мой личный канал, подписывайтесь, к стати. Ну, если мои взгляды (анархо-коммунист) разделяете, разумеется.

Для решения этой, с виду не такой уж простой, задачи надо добавить нашего бота в канал, как администратора. Чтобы сделать это, я, на своей текущей версии телеграма, перехожу в админку канала и добавляю туда своего бота из вышеуказанной статьи. Переходим в управление группой -> administrators

Тыкаем туда, после чего указываем ник нашего бота и добавляем.

Отлично!

Код

А тут, на самом деле, ничего сложно нет. Фича именно что не очевидная, для многих. Сейчас поясню. По идее нам надо отправлять в канал сообщения через метод $bot->sendMessage(), однако, первый аргумент этого метода требует наличия некоего ID чата, в который отсылается сообщение. В случае с чатами, конфами и прочей фигней эти самые ID — цифровые, как у групп в VK, а вот в случае с каналами такие ID соответствуют названию самих каналов. Поэтому чтобы отослать в канал «Красные котлеты» сообщение «Тест» сгодится такой код:

$bot->sendMessage("@burgercaputt", "Тест");

Куда прописывать этот код? Да прямо между $bot = new TelegramBotApiClient($token,null); и $bot->run(); впишите. В смысле примерно так, как на картинке.

Как запустить? Да легко, переходим на страничку с вашим ботом и руками нажимаем там F5(обновить).

Ну и, собственно, вот результат работы данной команды —

Теперь ничего не стоит добавить вызов данной страницы в, например, CURL и получить отложенный постинг. Разберетесь. Так-же был вопрос — как сделать, чтобы бот публиковал в канал по команде, или публиковал в несколько каналов. — Запросто — используйте банальные GET параметры, вот как примерно в этом наброске кода:

 if($_GET["bname"] == "revcombot"){ $bot->sendMessage("@burgercaputt", "Тест"); } if($_GET["bname"] == "someoother"){ $bot->sendMessage("@someoother", "Тест"); }

Теперь достаточно обратиться к файлу index.php с get параметрами вида index.php?bname=revcombot. Дальше, думаю, сами разберетесь.

Для начала о каких кнопках идет речь. Вот на картинке вы видите их:

Как вывести такие кнопки используя telegram-bot/api? Вообще это весьма обширная тема, я чуть ли не два часа потратил на эти гребаные кнопки и попытки их осилить. Выводятся они следующим кодом:

// Кнопки у сообщений $bot->command("ibutton", function ($message) use ($bot) { $keyboard = new TelegramBotApiTypesInlineInlineKeyboardMarkup( [ [ ['callback_data' => 'data_test', 'text' => 'Answer'], ['callback_data' => 'data_test2', 'text' => 'ОтветЪ'] ] ] );  $bot->sendMessage($message->getChat()->getId(), "тест", false, null,null,$keyboard); });

То есть по команде /ibutton вылезут к нам две кнопки Answer и ОтветЪ. С выводом разобрались, теперь нам надо их обработать. Это очень сложная часть всей свистопляски, которая отняла у меня изрядно времени. Делается все так:

// Обработка кнопок у сообщений $bot->on(function($update) use ($bot, $callback_loc, $find_command){ $callback = $update->getCallbackQuery(); $message = $callback->getMessage(); $chatId = $message->getChat()->getId(); $data = $callback->getData();  if($data == "data_test"){ $bot->answerCallbackQuery( $callback->getId(), "This is Ansver!",true); } if($data == "data_test2"){ $bot->sendMessage($chatId, "Это ответ!"); $bot->answerCallbackQuery($callback->getId()); // можно отослать пустое, чтобы просто убрать "часики" на кнопке }  }, function($update){ $callback = $update->getCallbackQuery(); if (is_null($callback) || !strlen($callback->getData())) return false; return true; });

Вот и все. Собственно, вот результат работы этой функции:

Как вы видите — все отлично работает. Ну и вторая кнопка для большей… полноты материала.

 

Как вы видите так — тоже можно.

Если вы работали с botfater’ом то видели там такую панельку из кнопок, примерно:

То есть этот вид кнопок появляется не под сообщением, а под вашим текстовым полем. Как вывести эти кнопки? Вот код —

// Reply-Кнопки $bot->command("buttons", function ($message) use ($bot) { $keyboard = new TelegramBotApiTypesReplyKeyboardMarkup([[["text" => "Власть советам!"], ["text" => "Сиськи!"]]], true, true);  $bot->sendMessage($message->getChat()->getId(), "тест", false, null,null, $keyboard); });

Тут задается команда /buttons после которой и появляются эти команды. Как обработать? На самом деле никакой особой обработки эти кнопки не требуют, в смысле их надо обрабатывать как обычные текстовые сообщения. В данном случае, я сделал это через метод on()

// Отлов любых сообщений + обрабтка reply-кнопок $bot->on(function($Update) use ($bot){  $message = $Update->getMessage(); $mtext = $message->getText(); $cid = $message->getChat()->getId();  if(mb_stripos($mtext,"Сиськи") !== false){ $pic = "http://aftamat4ik.ru/wp-content/uploads/2017/05/14277366494961.jpg";  $bot->sendPhoto($message->getChat()->getId(), $pic); } if(mb_stripos($mtext,"власть советам") !== false){ $bot->sendMessage($message->getChat()->getId(), "Смерть богатым!"); } }, function($message) use ($name){ return true; // когда тут true - команда проходит });

Ну и, собственно, если на любую из этих кнопок ткнуть, получим результат.

Обработка inline-запросов

Что такое inline запрос у telegram? Хм, на словах будет сложно объяснить, лучше покажу. Есть у нас бот, очень популярный — @vkmusic_bot, он выполняет поиск музыки по вконтакте. И его можно вызывать в любом месте телеграма. Просто прописываем его название и следом — название трека, который надо найти. Это и есть inline запрос.

Как сделать в своем боте такую-же фичу?

На самом деле эта задачка заставила меня попотеть изрядно. Я перелопатил половину буржунета и весь код долбаной библиотеки telebram-bot/api, отдебажил там все ключевые функции и только потом смог найти нормальный ответ. И знаете что? Вам, таки, повезло что я его нашел!

Подготовка бота

Не только лишь все боты могут обрабатывать inline запросы, а только те, в которых автор включил эту фичу. Идем на botfather’а  и выполняем там команду /setinline, и выбираем бота. Вылезет что-то типа того:

Думаете все, режим включился? А вот фиг там было! Нам надо передать в botfather’а форму инлайн команды, примерно так: @revcom_bot тестовый inline ответ

Вот ТЕПЕРЬ можно начинать написание кода.

Код

Собственно в библиотеке бота есть метод $bot->inlineQuery(), именно он отвечает за обработку контекстных запросов. Но как заставить его работать? Оказывается у каждого поля, отправляемого в результат запроса свой формат + для некоторых полей надо данные готовить заранее. Однако я все прокомментировал, так что разобраться с этим функционалом вы сможете в два счета. А вот мне пришлось чуть ли не в слепую угадывать что и куда ставить из-за отсутствия вменяемой документации к библиотеке.

// обработка инлайнов $bot->inlineQuery(function ($inlineQuery) use ($bot) { mb_internal_encoding("UTF-8"); $qid = $inlineQuery->getId(); $text = $inlineQuery->getQuery();  // Это - базовое содержимое сообщения, оно выводится, когда тыкаем на выбранный нами инлайн $str = "Что другие? Свора голодных нищих. Им все равно... В этом мире немытом Душу человеческую Ухорашивают рублем, И если преступно здесь быть бандитом, То не более преступно, Чем быть королем... Я слышал, как этот прохвост Говорил тебе о Гамлете. Что он в нем смыслит? Гамлет восстал против лжи, В которой варился королевский двор. Но если б теперь он жил, То был бы бандит и вор."; $base = new TelegramBotApiTypesInlineInputMessageContentText($str,"Html");  // Это список инлайнов // инлайн для стихотворения $msg = new TelegramBotApiTypesInlineQueryResultArticle("1","С. Есенин","Отрывок из поэмы `Страна негодяев`"); $msg->setInputMessageContent($base); // указываем, что в ответ к этому сообщению надо показать стихотворение  // инлайн для картинки $full = "http://aftamat4ik.ru/wp-content/uploads/2017/05/14277366494961.jpg"; // собственно урл на картинку  $thumb = "http://aftamat4ik.ru/wp-content/uploads/2017/05/14277366494961-150x150.jpg"; // и миниятюра  $photo = new TelegramBotApiTypesInlineQueryResultPhoto("2",$full,$thumb);  // инлайн для музыки $url = "http://aftamat4ik.ru/wp-content/uploads/2017/05/mongol-shuudan_-_kozyr-nash-mandat.mp3"; $mp3 = new TelegramBotApiTypesInlineQueryResultAudio("3",$url,"Монгол Шуудан - Козырь наш Мандат!");  // инлайн для видео $vurl = "http://aftamat4ik.ru/wp-content/uploads/2017/05/bb.mp4"; $thumb = "http://aftamat4ik.ru/wp-content/uploads/2017/05/joker_5-150x150.jpg"; $video = new TelegramBotApiTypesInlineQueryResultVideo("4",$vurl,$thumb, "video/mp4","коммунальные службы","тут тоже может быть описание");  // отправка try{ $result = $bot->answerInlineQuery( $qid, [$msg,$photo,$mp3,$video],100,false); }catch(Exception $e){ file_put_contents("rdata",print_r($e,true)); } }); 

Как это работает в боте? Смотрим.

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

Надеюсь я смог решить вашу проблему.

Исходники

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

RevcomBot исходники Скачано: 0, размер: 578.6 KB, дата: 24.Май.2017

Желающие могут поиграться с демонстрационным ботом тут — revcom_bot

Запись Бот для telegram на php. Отложенный постинг, кнопки, инлайн-запросы. впервые появилась Личный блог Гарри.

Источник

Комментарии (28):

Войдите или зарегистрируйтесь чтобы оставить комментарий

О блоге

Оцените статью
Рейтинг автора
4,8
Материал подготовил
Егор Новиков
Наш эксперт
Написано статей
127
А как считаете Вы?
Напишите в комментариях, что вы думаете – согласны
ли со статьей или есть что добавить?
Добавить комментарий