Для решения данной задачи нам понадобятся следующие компоненты aiogram:
- InlineKeyboardButton и InlineKeyboardMarkup для создания инлайн кнопок
- CallbackQuery и chat_data для обработки нажатий на кнопки
- InlineQuery и InputTextMessageContent для создания и отправки сообщений с фото и текстом
В начале нужно импортировать все нужные компоненты из aiogram:
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
bot = Bot(token="TOKEN")
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
Далее мы можем создать список с пиццами для демонстрации:
pizzas = [
{"name": "Пицца 1", "price": 10, "description": "Описание пиццы 1", "photo": "photo1"},
{"name": "Пицца 2", "price": 20, "description": "Описание пиццы 2", "photo": "photo2"},
{"name": "Пицца 3", "price": 30, "description": "Описание пиццы 3", "photo": "photo3"},
{"name": "Пицца 4", "price": 40, "description": "Описание пиццы 4", "photo": "photo4"},
]
Затем создадим хэндлер для обработки нажатия на инлайн кнопку "категория":
@dp.callback_query_handler(text="category")
async def show_pizzas(call: types.CallbackQuery):
page = 0 # текущая страница
per_page = 2 # количество элементов на странице
await show_pizza_page(call.message.chat.id, page, per_page)
В функции `show_pizzas()` мы вызываем функцию `show_pizza_page()`, которая будет отображать страницу с пиццами.
async def show_pizza_page(chat_id: int, page: int, per_page: int):
start_index = page * per_page
end_index = start_index + per_page
markup = await generate_pizzas_markup(start_index, end_index)
await bot.send_message(chat_id, "Выберите пиццу:", reply_markup=markup)
Функция `generate_pizzas_markup()` создает InlineKeyboardMarkup с инлайн кнопками для выбора пиццы:
async def generate_pizzas_markup(start_index: int, end_index: int) -> types.InlineKeyboardMarkup:
markup = types.InlineKeyboardMarkup()
for i in range(start_index, end_index):
if i >= len(pizzas): # если индекс выходит за пределы списка пицц
break
pizza = pizzas[i]
button_text = f"{pizza['name']} - {pizza['price']} руб."
callback_data = f"pizza:{i}"
markup.add(
types.InlineKeyboardButton(button_text, callback_data=callback_data)
)
pagination_row = types.InlineKeyboardRow()
if start_index > 0:
pagination_row.add(
types.InlineKeyboardButton("Назад", callback_data="prev")
)
if end_index < len(pizzas):
pagination_row.add(
types.InlineKeyboardButton("Далее", callback_data="next")
)
markup.add(pagination_row)
return markup
Теперь нужно обработать нажатия на кнопки "Назад", "Далее" и пиццы:
@dp.callback_query_handler(lambda c: c.data in ["prev", "next"])
async def handle_pagination(call: types.CallbackQuery):
page = int(call.message.text.split(":")[-1]) # получаем текущую страницу из текста сообщения
if call.data == "prev":
page -= 1
elif call.data == "next":
page += 1
await show_pizza_page(call.message.chat.id, page, per_page)
@dp.callback_query_handler(lambda c: c.data.startswith("pizza:"))
async def handle_pizza(call: types.CallbackQuery):
pizza_index = int(call.data.split(":")[-1])
pizza = pizzas[pizza_index]
photo = pizza["photo"]
caption = f"{pizza['name']}nn{pizza['description']}nnЦена: {pizza['price']} руб."
await bot.send_photo(call.message.chat.id, photo, caption)
Теперь код для работы с aiogram 3 генерирует сообщения с пиццами и инлайн кнопками пагинации. При нажатии на кнопку пиццы выводится фото пиццы и ее описание. Если была последняя страница, то кнопка "Далее" больше не появляется в меню пагинации.
Код полностью:
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
bot = Bot(token="TOKEN")
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
pizzas = [
{"name": "Пицца 1", "price": 10, "description": "Описание пиццы 1", "photo": "photo1"},
{"name": "Пицца 2", "price": 20, "description": "Описание пиццы 2", "photo": "photo2"},
{"name": "Пицца 3", "price": 30, "description": "Описание пиццы 3", "photo": "photo3"},
{"name": "Пицца 4", "price": 40, "description": "Описание пиццы 4", "photo": "photo4"},
]
@dp.callback_query_handler(text="category")
async def show_pizzas(call: types.CallbackQuery):
page = 0 # текущая страница
per_page = 2 # количество элементов на странице
await show_pizza_page(call.message.chat.id, page, per_page)
async def show_pizza_page(chat_id: int, page: int, per_page: int):
start_index = page * per_page
end_index = start_index + per_page
markup = await generate_pizzas_markup(start_index, end_index)
await bot.send_message(chat_id, "Выберите пиццу:", reply_markup=markup)
async def generate_pizzas_markup(start_index: int, end_index: int) -> types.InlineKeyboardMarkup:
markup = types.InlineKeyboardMarkup()
for i in range(start_index, end_index):
if i >= len(pizzas): # если индекс выходит за пределы списка пицц
break
pizza = pizzas[i]
button_text = f"{pizza['name']} - {pizza['price']} руб."
callback_data = f"pizza:{i}"
markup.add(
types.InlineKeyboardButton(button_text, callback_data=callback_data)
)
pagination_row = types.InlineKeyboardRow()
if start_index > 0:
pagination_row.add(
types.InlineKeyboardButton("Назад", callback_data="prev")
)
if end_index < len(pizzas):
pagination_row.add(
types.InlineKeyboardButton("Далее", callback_data="next")
)
markup.add(pagination_row)
return markup
@dp.callback_query_handler(lambda c: c.data in ["prev", "next"])
async def handle_pagination(call: types.CallbackQuery):
page = int(call.message.text.split(":")[-1]) # получаем текущую страницу из текста сообщения
if call.data == "prev":
page -= 1
elif call.data == "next":
page += 1
await show_pizza_page(call.message.chat.id, page, per_page)
@dp.callback_query_handler(lambda c: c.data.startswith("pizza:"))
async def handle_pizza(call: types.CallbackQuery):
pizza_index = int(call.data.split(":")[-1])
pizza = pizzas[pizza_index]
photo = pizza["photo"]
caption = f"{pizza['name']}nn{pizza['description']}nnЦена: {pizza['price']} руб."
await bot.send_photo(call.message.chat.id, photo, caption)
Не забудьте заменить "TOKEN" на ваш токен бота.