Создание inline-кнопок, callback-обработчики, inline-режим для ботов и юзерботов.
Ключевая идея: Inline-кнопки и callback-запросы позволяют создавать интерактивные интерфейсы прямо в сообщениях Telegram. Это мощный способ взаимодействия с пользователем без ввода команд.
Inline-кнопки добавляются к сообщению через параметр buttons:
from telethon.tl.types import KeyboardButtonUrl, KeyboardButtonCallback
from telethon.tl.custom import Button
# Через готовый класс Button
await client.send_message(
chat,
'Выберите действие:',
buttons=[
[Button.inline('Начать', b'start')],
[Button.url('Сайт', 'https://example.com')],
[Button.inline('Стоп', b'stop')]
]
)Ключевая идея: Inline-кнопки не отправляют текст в чат. Вместо этого они передают скрытые данные (callback data), которые обрабатываются вашим кодом.
Button.inline('Текст на кнопке', b'callback_data')Callback-кнопки отправляют данные обратно вашему обработчику:
@client.on(events.CallbackQuery)
async def handler(event):
print(f'Получены данные: {event.data}')
await event.answer('Обработано!')Button.url('Открыть сайт', 'https://example.com')URL-кнопки открывают ссылку в браузере:
await client.send_message(
chat,
'Полезные ссылки:',
buttons=[
[Button.url('Документация', 'https://docs.telethon.dev')],
[Button.url('GitHub', 'https://github.com/LonamiWebs/Telethon')]
]
)Button.text('Поделиться контактом', request_contact=True)Важно: кнопки с запросом контакта/геолокации работают только в ботах. Для юзерботов используйте callback и URL.
@client.on(events.CallbackQuery)
async def callback_handler(event):
data = event.data.decode() # bytes → string
if data == 'start':
await event.edit('Процесс запущен ✓')
await event.answer('Запущено')
elif data == 'stop':
await event.edit('Процесс остановлен ✓')
await event.answer('Остановлено')
else:
await event.answer('Неизвестная кнопка')@client.on(events.CallbackQuery(data=b'start'))
async def start_handler(event):
await event.edit('Запущено!')
await event.answer()
@client.on(events.CallbackQuery(data=b'stop'))
async def stop_handler(event):
await event.edit('Остановлено!')
await event.answer()buttons = [
# Ряд 1
[Button.inline('A', b'a'), Button.inline('B', b'b')],
# Ряд 2
[Button.inline('C', b'c'), Button.inline('D', b'd')],
# Ряд 3
[Button.url('Сайт', 'https://example.com')]
]
await client.send_message(chat, 'Клавиатура:', buttons=buttons)Правило: каждый вложенный список — это отдельный ряд кнопок. Кнопки в ряду располагаются горизонтально.
# Отправка с кнопками
msg = await client.send_message(
chat,
'Выберите:',
buttons=[
[Button.inline('Да', b'yes'), Button.inline('Нет', b'no')]
]
)
# После нажатия — редактируем текст и кнопки
@client.on(events.CallbackQuery(data=b'yes'))
async def yes_handler(event):
await event.edit(
'Вы выбрали Да ✓',
buttons=[Button.inline('Вернуться', b'reset')]
)
@client.on(events.CallbackQuery(data=b'no'))
async def no_handler(event):
await event.edit(
'Вы выбрали Нет ✗',
buttons=[Button.inline('Вернуться', b'reset')]
)Inline-режим позволяет вызывать бота из любого чата через @username бота:
from telethon.tl.types import (
InputBotInlineMessageText,
InputBotInlineResult
)
@client.on(events.InlineQuery)
async def inline_handler(event):
builder = event.builder
# Создаём результаты
results = [
builder.article(
title='Первый результат',
text='Текст первого результата',
buttons=[Button.inline('Кнопка', b'click')]
),
builder.article(
title='Второй результат',
text='Текст второго результата'
)
]
await event.answer(results)Важно: inline-режим требует активации через BotFather. Для юзерботов inline-режим недоступен.
async def create_pagination_buttons(page, total_pages):
buttons = []
row = []
if page > 1:
row.append(Button.inline('◀️', f'prev_{page-1}'.encode()))
row.append(Button.inline(f'{page}/{total_pages}', b'noop'))
if page < total_pages:
row.append(Button.inline('▶️', f'next_{page+1}'.encode()))
buttons.append(row)
return buttons
# Использование
buttons = await create_pagination_buttons(2, 5)
await client.send_message(chat, 'Страница 2', buttons=buttons)import json
# Кодирование данных
data = json.dumps({'action': 'delete', 'msg_id': 12345}).encode()
await client.send_message(
chat,
'Удалить сообщение?',
buttons=[
[Button.inline('Удалить', data), Button.inline('Отмена', b'cancel')]
]
)
# Декодирование
@client.on(events.CallbackQuery)
async def handler(event):
data = json.loads(event.data.decode())
if data['action'] == 'delete':
msg_id = data['msg_id']
await client.delete_messages(chat, [msg_id])
await event.answer('Удалено')Правило: callback data ограничен 64 байтами. Для больших данных используйте внешнее хранилище (Redis, SQLite) с ключом в callback.
# Простое подтверждение (без текста)
await event.answer()
# С текстом (показывается 3 секунды)
await event.answer('Сохранено!')
# С оповещением (всплывающее окно)
await event.answer('Ошибка!', alert=True)@client.on(events.CallbackQuery(data=b'confirm'))
async def confirm_handler(event):
# Редактируем сообщение, убирая кнопки
await event.edit('Действие выполнено ✓')
# Кнопки исчезнут, так как не указаны в edit| Ошибка | Причина | Решение |
|---|---|---|
ButtonDataInvalidError | Callback data > 64 байт | Сократите данные или используйте ключ |
ButtonUrlInvalidError | Недопустимый URL | Проверьте схему (http/https) |
ButtonTypeInvalidError | Неверный тип кнопки | Используйте Button.inline/url/text |
# Проверка размера callback data
data = json.dumps({'action': 'delete', 'id': 12345}).encode()
if len(data) > 64:
# Сохраняем в хранилище и передаём ключ
key = save_to_cache({'action': 'delete', 'id': 12345})
data = f'act:{key}'.encode()Inline-кнопки делают юзербот интерактивным. Но для production-приложений критично правильное хранение сессий. Следующая тема — хранение сессий — расскажет о типах сессий и их безопасном хранении.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.