Приветствую всех! В этой статье мы продолжим исследовать возможности библиотеки Aiogram 3 и рассмотрим тему инлайн кнопок и CallBack данных. На данный момент мы уже:
Определились со структурой бота;
Настроили нашего Telegram-бота на базе Aiogram 3;
Разобрались с командами, включая аргументы, командное меню и фильтры Command и CommandStart;
Освоили работу с текстовыми кнопками (в предыдущей публикации я максимально подробно разобрал эту тему);
Коснулись магических фильтров и обсудили прочие аспекты взаимодействия с ботом.
После тщательных размышлений я пришёл к выводу, что логичным продолжением будет изучение CallBack хендлеров и CallBack данных. Эти технологии открывают безграничные возможности для создания интерактивных и сложных сценариев взаимодействия с пользователями.
В данной статье мы рассмотрим:
Что такое CallBack хендлеры;
Разновидности CallBack хендлеров (ссылки, веб-приложения, обычные CallBack данные);
Научимся создавать более сложные конструкции через магические фильтры в контексте CallBack.
Поработаем с библиотекой Faker
Напишем функцию по генерации случайного пользователя и коснемся темы форматирования сообщений
Я покажу вам как работает имитация действий в боте (будем имитировать набор текста ботом)
Приступим к углубленному изучению этих аспектов и постараемся максимально использовать возможности, которые предоставляет нам библиотека Aiogram 3.
Что такое CallBack в Aiogram 3
CallBack в Aiogram 3 — это способ обработки взаимодействий пользователей с ботом, когда они нажимают на инлайн кнопки. Когда пользователь нажимает на такую кнопку, бот получает специальное сообщение — CallBack, с информацией о том, какую кнопку нажали. Эта информация называется CallBack дата.
Проще говоря, CallBack позволяет боту реагировать на нажатия инлайн кнопок, выполняя определённые действия в ответ на это. Это очень удобно для создания интерактивных и динамических ботов, которые могут менять своё поведение в зависимости от выбора пользователя.
CallBack кнопки могут работать, как в формате текстовых кнопок. Это когда текст на кнопке равняется той информации (CallBack дате), которую пользователь передает боту. Бывает полезным, например, если в вашем боте нет текстовых клавиатур (в рамках общего стиля например), а есть только InlineKeyboard. В таком случае можно, к примеру, в инлайн кнопку Home передать CallData home, но обычно в практике обычно CallBack дата кардинально отличается от надписи на кнопке.
Сейчас я дам пару текстовых примеров из моей реальной практики, чтобы стало более понятно, а после приступим к написанию кода.
Например, у вас бот службы поддержки (порядка 5-6 проектов разной сложности делал в формате «support bot»). Пользователь отправляет некое сообщение в бота, бот перехватывает это сообщение, параллельно захватив телеграмм айди пользователя (в прошлой статье я показывал как достать телеграмм айди из объекта message).
Далее он отправляет это сообщение менеджерам службы поддержки с инлайн кнопкой «Ответить». Менеджер нажимает на эту кнопку, и бот ждёт ответного сообщения (подробнее рассмотрим в теме FSM). И тут основная фишка в том, что кликнув на кнопку «Ответить», менеджер не просто запускает сценарий ответа на сообщение, но и сразу указывает боту, что этот ответ должен полететь конкретному пользователю (хотите статью о том, как написать простую службу поддержки?). В данном случае бот достает телеграмм айди пользователя из CallData (сегодня мы сделаем нечто похожее).
Другой пример (тоже из службы поддержки):
В ботах периодически бывают так называемые FAQ (разделы с часто задаваемыми вопросами). Был опыт, когда я в админ панели прописывал функционал, позволяющий захватить вопрос и ответ на него (FSM), далее это всё записывалось в базу данных под определённым айдишником.
После, когда пользователь заходит в раздел FAQ, бот отправляет запрос в базу данных и при помощи генератора инлайн кнопок (InlineKeyboardBuilder по типу такого же как для текстовых кнопок) происходит генерация клавиатуры с вопросом и ответом.
Далее боту достаточно всего одного хендлера для того чтобы массово обрабатывать сразу все ответы на любые вопросы. Ниже пример реализации:
@qst_router.callback_query(F.data.startswith('qst_'))
async def cmd_start(call: CallbackQuery, state: FSMContext):
await state.clear()
await call.answer()
qst_id = int(call.data.replace('qst_', ''))
async with ChatActionSender(bot=bot, chat_id=call.from_user.id, action="typing"):
info = await pg_db.select_data('questions', {'where_conditions': [{'id': qst_id}]})
await call.message.answer(info.get('answer'), reply_markup=main_kb(call.from_user.id))
Сильно не вдавайтесь сейчас в подробности кода, в будущих статьях я вас научу делать каждую реализацию. Тут просто смысл в демонстрации мощи CallBackData — буквально пару строк кода способны закрыть огромный блок FAQ (масштабирование, по сути, неограниченное).
Ну ладно, я могу очень долго говорить про CallBack, так как технология, по моему мнению, шикарная. Если сейчас пока не понятно что к чему – не переживайте. Дочитав эту статью, вы точно разберётесь с темой CallBack.
Приступаем к коду.
Код я буду писать в том же проекте, что и писал в прошлых статьях (если хотите такой же шаблон как у меня – читайте первую статью по теме aiogram – там я подробно расписал свой каркас бота стартового).
Давайте в нашем пакете keyboards создадим новый файл под Inline клавиатуры и дадим ему название inline_kbs.py
:
В него сразу импортируем следующие модули:
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
from aiogram.utils.keyboard import InlineKeyboardBuilder
Импортируемые модули в файле inline_kbs.py
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
InlineKeyboardMarkup
: Этот класс используется для создания разметки инлайн клавиатуры. Разметка определяет, как будут располагаться кнопки на клавиатуре и как они будут взаимодействовать с пользователем.InlineKeyboardButton
: Этот класс представляет собой отдельную кнопку на инлайн клавиатуре. С помощью него мы можем задавать текст кнопки и действие, которое произойдет при нажатии на неё, например, отправку CallBack данных.WebAppInfo
: Этот класс используется для создания кнопок, которые открывают веб-приложения внутри Telegram (тема заслуживает отдельного большого обсуждения, так что далее просто покажу, что оно существует). С его помощью можно определить URL веб-приложения, которое будет открыто при нажатии на кнопку. Это полезно для интеграции внешних веб-сервисов и приложений с ботом.
from aiogram.utils.keyboard import InlineKeyboardBuilder
InlineKeyboardBuilder
: Это удобный инструмент для построения инлайн клавиатур. С его помощью можно легко и быстро создавать клавиатуры, добавляя кнопки и определяя их расположение. Этот класс помогает упрощать процесс создания сложных разметок клавиатур, делая код более читаемым и удобным для поддержки. Работает похожим образом сReplyKeyboardBuilder
, но со своими особенностями, о которых мы сегодня поговорим.
Инлайн клавиатура со ссылками
def ease_link_kb():
inline_kb_list = [
[InlineKeyboardButton(text="Мой хабр", url='https://habr.com/ru/users/yakvenalex/')],
[InlineKeyboardButton(text="Мой Telegram", url='tg://resolve?domain=yakvenalexx')],
[InlineKeyboardButton(text="Веб приложение", web_app=WebAppInfo(url="https://tg-promo-bot.ru/questions"))]
]
return InlineKeyboardMarkup(inline_keyboard=inline_kb_list)
Объяснение кода:
Определение функции
ease_link_kb()
:Функция
ease_link_kb
предназначена для создания и возвращения инлайн клавиатуры с кнопками, которые ведут к различным ссылкам различных типов.
Создание списка кнопок
inline_kb_list
:Внутри функции создаётся список
inline_kb_list
, который содержит вложенные списки с объектамиInlineKeyboardButton
. Каждая вложенная структура представляет собой отдельную строку кнопок на инлайн клавиатуре.
Кнопка с ссылкой на мой аккаунт в Хабре:
[InlineKeyboardButton(text="Мой хабр", url='https://habr.com/ru/users/yakvenalex/')]
:Создаётся кнопка с текстом "Мой хабр", которая при нажатии перенаправляет пользователя на страницу Хабра.
Кнопка с ссылкой на мой Telegram аккаунт:
[InlineKeyboardButton(text="Мой Telegram", url='tg://resolve?domain=yakvenalexx')]
:Создаётся кнопка с текстом "Мой Telegram", которая при нажатии открывает Telegram и переходит к моему аккаунту.
Кнопка для открытия веб-приложения:
[InlineKeyboardButton(text="Веб приложение", web_app=WebAppInfo(url="https://tg-promo-bot.ru/questions"))]
:Создаётся кнопка с текстом "Веб приложение", которая при нажатии открывает веб-приложение по указанному URL.
Возвращение инлайн клавиатуры:
return InlineKeyboardMarkup(inline_keyboard=inline_kb_list)
:Функция возвращает объект
InlineKeyboardMarkup
, который содержит разметку инлайн клавиатуры с указанными кнопками.
Этот пример демонстрирует, как создавать инлайн клавиатуру с различными типами ссылок, включая обычные URL, ссылки на аккаунты в Telegram и веб-приложения. Давайте тестировать.
Для тестов я предлагаю создать новый message handler, который будет вызываться текстом «Давай инлайн!». К нему прикрутим нашу инлайн клавиатуру и поклацаем ее.
@start_router.message(F.text == 'Давай инлайн!')
async def get_inline_btn_link(message: Message):
await message.answer('Вот тебе инлайн клавиатура со ссылками!', reply_markup=ease_link_kb())
Думаю, что к настоящему моменту вы уже понимаете, что тут мы использовали магический фильтр F.text, который будет срабатывать на отправку текста 'Давай инлайн!', а чтоб было ещё интересней давайте мы создадим текстовую кнопку с текстом 'Давай инлайн!', а саму кнопку привяжем к главной клавиатуре (это вы уже умеете делать, если нет, то читайте прошлую статью).
Давайте теперь изучать каждую кнопку-ссылку:
После клика на обычную кнопку-ссылку появляется окно которое спрашивает хотим ли мы перейти по ссылке.
После клика на ссылку с моим профилем Telegram происходит переход без окна в мой профиль. До недавнего времени только ссылки формата «tg://resolve?domain=yakvenalexx» позволяли переходить в профиль без окна, но, при написании этого текста обнаружил что и при формате ссылки «https://t.me/yakvenalexx» окно не появлялось.
Теперь к интересному моменту ВЕБ-ПРИЛОЖЕНИЕ:
После клика по инлайн кнопке с «квадратиком» телеграмм отправит нам такое сообщение:
А после откроется то ВЕБ-ПРИЛОЖЕНИЕ, которое я создал. С ПК версии, возможно не так наглядно как с телефона. Сейчас продемонстрирую.
Обратите внимание на интерактивность. Веб-приложение становится, как бы, частью телеграмм бота, тем самым открывая неограниченные возможности. Вот так, например, я прописывал специальную анкету в одном из своих ботов:
Как я говорил выше – все это тема отдельной статьи. Может как-то обсудим.
Итак, к промежуточным выводам. Мы разобрали все варианты инлайн-клавиатур ссылок, а это значит, что можем переходить к более интересной части – CallBack Data!
Начнем с простой клавиатуры. Пускай в ней будет 2 инлайн-кнопки. Одна кнопка должна переводить пользователя на стартовый экран, а вторая запускает некое действие, например, выводит информацию о случайном пользователе (первое, что пришло в голову).
Для этого нам нужно подготовиться.
Сначала напишем функцию, которая будет генерировать информацию о случайном пользователе. Для этого воспользуемся интересной библиотекой Faker (возьмите её на заметку, часто пригождается).
Устанавливаем библиотеку:
pip install faker
Пишем функцию, которая будет генерировать случайного пользователя (как раз наш пакет с утилитами тут будет кстати).
Пишем код:
from faker import Faker
def get_random_person():
# Создаем объект Faker с русской локализацией
fake = Faker('ru_RU')
# Генерируем случайные данные пользователя
user = {
'name': fake.name(),
'address': fake.address(),
'email': fake.email(),
'phone_number': fake.phone_number(),
'birth_date': fake.date_of_birth(),
'company': fake.company(),
'job': fake.job()
}
return user
Тут все достаточно просто. Импортируем модуль. При инициализации объекта fake
укажем, что нас интересуют русские данные. Далее создадим простой словарь и захватим в него следующие данные: имя, адрес, email, телефон, дата рождения, компания и работа.
Далее мы напишем специальный хендлер, который при получении CallData «get_person» будет возвращать хорошо оформленное сообщение с информацией о пользователе (как раз немного углубим свои знания в теме форматирования текста в aiogram 3).
Сначала импортируем функцию для генерации случайного пользователя из пакета utils и CallbackQuery для удобства аннотаций.
from utils.utils import get_random_person
from aiogram.types import CallbackQuery
Теперь напишем сам хендлер. Я его пропишу полностью, а дальше дам объяснения.
@start_router.callback_query(F.data == 'get_person')
async def send_random_person(call: CallbackQuery):
# await call.answer('Генерирую случайного пользователя')
user = get_random_person()
formatted_message = (
f"