Найти в Дзене
Записки сисадмина

Python. MAX бот. Кнопки и клавиатуры ч2. Inline кнопки и callback'и

Я попробовал вывести на одном из мониторов видео с солнечного пляжа, но почему-то от этого в организме не стало больше витамина D. С этим мы разберемся позже, а пока что с лицом, больше похожим на лицо панды, продолжаем ковыряться в api MAX. В прошлой статье мы уже разобрались с текстовыми кнопками и импортировали необходимые нам модули. Сегодня будем работать с Inline кнопками, поэтому нам понадобятся: from maxapi.types import CallbackButton, MessageCallback
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder В чем разница между CallbackButton и MessageButton: MessageButton имитирует ввод текста. При нажатии на кнопку, у пользователя формируется сообщение с текстом данной кнопки и отправляется боту. Соответственно, обработка событий от MessageButton ничем не отличается от обработки других текстовых сообщений. CallbackButton формирует callback - внутреннюю команду для бота с данными, которые прописаны у нее в payload. Имитировать вызов callback'а вручную невозможно, поэтом
Оглавление

Я попробовал вывести на одном из мониторов видео с солнечного пляжа, но почему-то от этого в организме не стало больше витамина D. С этим мы разберемся позже, а пока что с лицом, больше похожим на лицо панды, продолжаем ковыряться в api MAX.

В прошлой статье мы уже разобрались с текстовыми кнопками и импортировали необходимые нам модули.

Сегодня будем работать с Inline кнопками, поэтому нам понадобятся:

from maxapi.types import CallbackButton, MessageCallback
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder

В чем разница между CallbackButton и MessageButton:

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

CallbackButton формирует callback - внутреннюю команду для бота с данными, которые прописаны у нее в payload. Имитировать вызов callback'а вручную невозможно, поэтому это дает нам уверенность, что у пользователя не будет ложных срабатываний. Обработка callback'ов всегда осуществляется отдельной функцией.

Функционал CallbackButton довольно прост:

CallbackButton(text='Отображаемый текст кнопки', payload='Текст, передаваемый в обработчик')

Создание кнопок

Создадим обработчик команды /kb, который будет формировать сообщение с двумя Callback кнопками:

-2
@dp.message_created(Command('kb'))
async def keyboard_message(event: MessageCreated):
photo = InputMedia('blank_user.jpg')
kb = InlineKeyboardBuilder()
kb.row(CallbackButton(text='Callback button 1', payload='test1'),
CallbackButton(text='Callback button 2', payload='test2'))
await event.bot.send_message(user_id=event.from_user.user_id, text='test message',
attachments=[photo, kb.as_markup()])

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

-3

При этом, в логах будет видно, что события поступают, но не обрабатываются:

-4

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

Для обработки событий нажатия на CallbackButton, напишем отдельный обработчик:

-5

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

-6

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

Ограничение нажатий на кнопку

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

-7

Как это работает:

  • Когда пользователь нажимает на кнопку, формируется callback.
  • Обработчик message_callback(callback: MessageCallback) принимает в себя все сообщение вместе с информацией о том, какая кнопка была нажата.
  • Метод message.edit() позволяет изменять сообщение, поэтому мы можем переписать текст самого сообщения.
  • Поскольку клавиатура хранится у сообщения в attachments, необходимо передать туда пустой список (иначе данная часть сообщения не изменится)
-8

Теперь пользователь не сможет нажать на кнопки больше одного раза.

Также мы можем обрабатывать разные callback'и в разных функциях, передавая в декоратор фильтр:

-9
@dp.message_callback(F.callback.payload == 'test1')
async def message_callback(callback: MessageCallback):
await callback.message.answer(f'Вы нажали на Callback! Payload: {callback.callback.payload}')
@dp.message_callback(F.callback.payload == 'test2')
async def message_callback(callback: MessageCallback):
await callback.message.edit(text='pressed callback', attachments=[])
await callback.message.answer(f'Вы нажали на Callback! Payload: {callback.callback.payload}')

При нажатии на "Callback button 1", бот отправит нам сообщение, не редактируя оригинальное, а при нажатии на "Callback button 2" текст сообщения бота изменится:

-10
-11

Заметьте: изменяться всегда будет то сообщение, из которого пришел callback.

Код из статьи выложил здесь