callback in inline Keyboard

This commit is contained in:
vasilytray 2025-03-12 18:37:26 +07:00
parent 8b18cc444a
commit 621b3725d4
2 changed files with 105 additions and 0 deletions

View File

@ -558,3 +558,35 @@ async def on_user_shared(message: types.Message):
У колбэк-кнопок есть специальное значение **(data)**, по которому ваше приложение опознаёт, что нажато и что надо сделать. И выбор правильного data очень важен! Стоит также отметить, что, в отличие от обычных кнопок, нажатие на колбэк-кнопку позволяет сделать практически что угодно, от заказа пиццы до запуска вычислений на кластере суперкомпьютеров.
Сервер Telegram отправив нам кнопку callback ждет от нас подтверждения о доставке колбэка. Нам необходимо вызвать метод **answer()** в общем случае в него можем ничего не передавать, но можно вызвать специальное окошко (всплывающее сверху или поверх экрана):
```py
@dp.callback_query(F.data == "random_value")
async def send_random_value(callback: types.CallbackQuery):
await callback.message.answer(str(randint(1, 10)))
await callback.answer(
text="Спасибо, что воспользовались ботом!",
show_alert=True
)
# или просто await callback.answer()
```
> В функции send_random_value мы вызывали метод answer() не у message, а у callback.message. Это связано с тем, что колбэк-хэндлеры работают не с сообщениями (тип Message), а с колбэками (тип CallbackQuery), у которого другие поля, и само сообщение — всего лишь его часть. Учтите также, что message — это сообщение, к которому была прицеплена кнопка (т.е. отправитель такого сообщения — сам бот). Если хотите узнать, кто нажал на кнопку, смотрите поле from (в вашем коде это будет callback.from_user, т.к. слово from зарезервировано в Python)
Иногда пользователь, вызвав несколько раз сообщение с колбэками, может нажимать кнопки новых и старых сообщений и тогда может возникнуть ошибка, которую мы получим от Bot API, что старый и новый тексты совпадают, а бот словит исключение: ``Bad Request: 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 относится к категории Bad Request, поэтому у нас есть выбор: проигнорировать весь подобный класс ошибок, либо отловить весь класс BadRequest и попытаться по тексту ошибки опознать конкретную причину.
Чтобы игнорировать весь подобный класс обновим функцию ``update_num_text()``
```py
# Новые импорты!
from contextlib import suppress
from aiogram.exceptions import TelegramBadRequest
async def update_num_text(message: types.Message, new_value: int):
with suppress(TelegramBadRequest):
await message.edit_text(
f"Укажите число: {new_value}",
reply_markup=get_keyboard()
)
```

73
bot2.py
View File

@ -1,3 +1,4 @@
from random import randint
import re
import asyncio
import logging
@ -10,6 +11,9 @@ from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
# новый импорт!
from aiogram.utils.keyboard import ReplyKeyboardBuilder
# Новые импорты!
from contextlib import suppress
from aiogram.exceptions import TelegramBadRequest
from config_reader import config
@ -128,6 +132,75 @@ async def handle_contact(message: types.Message):
# новый импорт
from aiogram.utils.keyboard import InlineKeyboardBuilder
# Запускаем Callback - кнопка
@dp.message(Command("random"))
async def cmd_random(message: types.Message):
builder = InlineKeyboardBuilder()
builder.add(types.InlineKeyboardButton(
text="Нажми меня",
callback_data="random_value")
)
await message.answer(
"Нажмите на кнопку, чтобы бот отправил число от 1 до 10",
reply_markup=builder.as_markup()
)
# хэндлер обработки callback кнопки
@dp.callback_query(F.data == "random_value")
async def send_random_value(callback: types.CallbackQuery):
await callback.message.answer(str(randint(1, 10)))
# отправим всплывающее окно после результата
await callback.answer(
text="Спасибо, что воспользовались ботом!",
show_alert=True
)
# или просто await callback.answer()
# Продолжим с колбэками
# Здесь хранятся пользовательские данные.
# Т.к. это словарь в памяти, то при перезапуске он очистится
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")]
]
keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons)
return keyboard
# формируем сообщение с переменным аргументом
async def update_num_text(message: types.Message, new_value: int):
with suppress(TelegramBadRequest):
await message.edit_text(
f"Укажите число: {new_value}",
reply_markup=get_keyboard()
)
# запуск клавиатуры по команде /numbers
@dp.message(Command("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(F.data.startswith("num_"))
async def callbacks_num(callback: types.CallbackQuery):
user_value = user_data.get(callback.from_user.id, 0)
action = callback.data.split("_")[1]
if action == "incr":
user_data[callback.from_user.id] = user_value+1
await update_num_text(callback.message, user_value+1)
elif action == "decr":
user_data[callback.from_user.id] = user_value-1
await update_num_text(callback.message, user_value-1)
elif action == "finish":
await callback.message.edit_text(f"Итого: {user_value}")
await callback.answer()
@dp.message(Command("inline_url"))
async def cmd_inline_url(message: types.Message, bot: Bot):