aiogram и python. Подскажите пожалуйста как правильнее написать код
Всем привет, подскажите как мне лучше написать код. Я пишу телеграм бот магазин, который в основном состоит из InlineKeyboard`ов и вот я подумал как мне проще написать клавиатуры и подумал, что занесу данные которые нужны для написания функции я занесу в словарь и создам цикл в котором будет вызываться эта функция с данными о продукте, и таким образом создам keyboard'ы. Но очевидно, что я не гуру и не знаю как это правильно реализовать функция не должна вызываться в цикле ведь она даже применять только когда callback_data будет соответствовать фильтру.Если возможно сделать что то приближенно к тому как я хочу, то подскажите пожалуйста как или мне не стоит это делать и написать по обычному на каждый callback_data. Ниже сырой код который я написал:
from aiogram.types import CallbackQuery, Message, InputMediaPhoto, FSInputFile
from app.keyboards_bot.keyboard_builders.keyboard_builder import inline_builder
buy_keyboard = inline_builder(
["Купить", "Назад"],
["buy", "shop_menu"],
[1, 1]
)
products = {
"30 Gems": {
"callback_data": "F.data == '30g'",
"price": 260,
"photo": "",
"description": "",
"inline_keyboard": buy_keyboard,
},
"80 Gems": {
"callback_data": "F.data == '80g'",
"price": 600,
"photo": "",
"description": "",
"inline_keyboard": buy_keyboard,
},
"170 Gems": {
"callback_data": "F.data == '170g'",
"price": 1150,
"photo": "",
"description": "",
"inline_keyboard": buy_keyboard,
},
"360 Gems": {
"callback_data": "F.data == '360g'",
"price": 2150,
"photo": "",
"description": "",
"inline_keyboard": buy_keyboard,
},
"950 Gems": {
"callback_data": "F.data == '950g'",
"price": 5000,
"photo": "",
"description": "",
"inline_keyboard": buy_keyboard,
},
"2000 Gems": {
"callback_data": "F.data == '2000g'",
"price": 10000,
"photo": "",
"description": f'The price of this product is {dict.get("price")}',
"inline_keyboard": buy_keyboard,
}
}
router = Router()
def product_form(callback_data, photo, description, i_keyboard):
@router.callback_query(callback_data)
async def product_make_cmd(call: CallbackQuery):
await call.message.edit_media(
media=InputMediaPhoto(
media=FSInputFile(photo),
caption=description,
parse_mode="HTML"
),
reply_markup=i_keyboard
)
for product in products:
product_form(callback_data=product['callback_data'], photo=product['photo'], description=product['description'], i_keyboard=product['inline_keyboard'])```
Ответы (1 шт):
Идея использовать словарь мне нравится. Вот пример моего решения твоей задачки на aiogram 3.5x
(последней, на момент написания ответа, версии).
Я решил использовать InlineBuilder
и фабрику по созданию CallbackData
которую привязал к InlineBuilder
. Чуть более подробно расписал в докстрингах к функциям, а если хочешь узнать как работает CallbackData советую почитать документацию.
buy_menu_kb
- создает клавиатуру из словаря
handle_buy
- обрабатывает нажатие созданных кнопок. В этой функции ты можешь получить сумму гемов (count
) и цену (price
) и работать уже с ними как захочешь в рамках телеграмма.
from aiogram import Router, types
from aiogram.filters import Command
from aiogram.filters.callback_data import CallbackData
from aiogram.utils.keyboard import InlineKeyboardBuilder
router = Router(name=__name__)
class BuyCallbackFactory(CallbackData, prefix="buy"):
count: int
price: int
products = {
"Product 1": {"count": 100, "price": 10, "image": "image_1.jpg"},
"Product 2": {"count": 200, "price": 18, "image": "image_2.jpg"},
"Product 3": {"count": 300, "price": 25, "image": "image_3.jpg"},
}
async def buy_menu_kb(products_dict: dict) -> types.InlineKeyboardMarkup:
"""
Создает клавиатуру для покупки товара из словаря.
Args:
products_dict: Словарь с товарами.
Returns:
types.InlineKeyboardMarkup: Клавиатура с кнопками для покупки.
"""
builder = InlineKeyboardBuilder()
for product, details in products_dict.items():
count = details['count']
price = details['price']
callback_data = BuyCallbackFactory(count=count, price=price).pack()
builder.button(text=f"{product} - {count} гемов за {price} руб.", callback_data=callback_data)
builder.adjust(1)
return builder.as_markup()
@router.callback_query(BuyCallbackFactory.filter())
async def handle_buy(callback_query: types.CallbackQuery, callback_data: BuyCallbackFactory):
"""
Обрабатывает нажатие кнопки покупки.
Args:
callback_query: CallbackQuery объект.
callback_data: Данные callback.
"""
count = callback_data.count
price = callback_data.price
image_path = None
product_name = None
for product, details in products.items():
if details['count'] == count and details['price'] == price:
product_name = product
image_path = details['image']
break
if product_name:
image_file = types.FSInputFile(image_path)
await callback_query.message.answer_photo(photo=image_file, caption=f"Вы выбрали {count} гемов за {price} руб.")
await callback_query.answer()
@router.message(Command(commands=["buy_menu"]))
async def send_buy_menu(message: types.Message):
"""
Отправляет меню с кнопками для покупки товаров.
Args:
message: Объект сообщения.
"""
keyboard = await buy_menu_kb(products)
await message.answer("Выберите товар:", reply_markup=keyboard)
P.s: код далеко не идеален и может иметь свои недостатки.
Совет: разделить этот код на отдельные файлы у себя в проекте, к примеру на utils.py
, keyboards.py
, callbacks.py
, commands.py
и т.д.