import os from re import Match from aiogram import Router, F from aiogram.types import CallbackQuery, FSInputFile, Message from aiogram.filters import Command from aiogram.fsm.context import FSMContext from sqlalchemy.ext.asyncio import AsyncSession from config import PAGE_SIZE from misc.kb import main_menu_kb, render_category from misc.kb.client import ( get_back_to_catalogue, render_cart, render_product_interactions, render_products, ) from misc.texts import get_product_description from repositories.categories import CategoriesRepository from repositories.products import ProductRepository from services.orders import OrderService router = Router() @router.message(Command("start")) async def main_menu( msg: Message, state: FSMContext, session: AsyncSession, order_service: OrderService ): await state.clear() cart_items = await order_service.get_cart_items_count(session, msg.from_user.id) await msg.answer( "hii!", reply_markup=main_menu_kb(cart_items) ) # TODO: Write a welcome message @router.callback_query(F.data.startswith("menu:")) async def main_menu_cb( cb: CallbackQuery, state: FSMContext, session: AsyncSession, order_service: OrderService, ): await state.clear() cart_items = await order_service.get_cart_items_count(session, cb.from_user.id) await cb.message.edit_text( "hii!", reply_markup=main_menu_kb(cart_items) ) # TODO: Write a welcome message @router.message(Command("orders")) async def get_orders( msg: Message, state: FSMContext, order_service: OrderService, session: AsyncSession, ): await state.clear() count = await order_service.get_order_count(session, msg.from_user.id) await msg.answer(f"you have {count} orders.") @router.callback_query(F.data.startswith("cat:")) async def subcatalogue( cb: CallbackQuery, state: FSMContext, session: AsyncSession, categories_repo: CategoriesRepository, products_repo: ProductRepository, ): await state.clear() cat_id = cb.data.split(":")[1] if cat_id == "root": children = await categories_repo.get_categories_by_parent_id(session) kb = render_category(children, show_menu=True) await cb.message.edit_text( "category selection", reply_markup=kb, ) return cat_id = int(cat_id) children = await categories_repo.get_categories_by_parent_id(session, cat_id) category = await categories_repo.get_category_by_id(session, cat_id) parent_id = category.parent_id or "root" if not children: products = await products_repo.get_product_by_category( session, cat_id, limit=PAGE_SIZE + 1, offset=0 ) products_len = len(products) if not products: await cb.message.edit_text( "🍃 Здесь ничего нет...", reply_markup=get_back_to_catalogue(parent_id), ) return if cb.message.photo: await cb.message.delete() await cb.message.answer( "hya", reply_markup=render_products( products[:PAGE_SIZE], parent_id=parent_id, cat_id=cat_id, page=0, show_prev=products_len < 0, show_next=products_len > PAGE_SIZE, ), ) return await cb.message.edit_text( "hya", reply_markup=render_products( products[:PAGE_SIZE], parent_id=parent_id, cat_id=cat_id, page=0, show_prev=products_len < 0, show_next=products_len > PAGE_SIZE, ), ) return kb = render_category(children, parent_id) await cb.message.edit_text( "category selection", reply_markup=kb, ) @router.callback_query( F.data.regexp(r"products:(?P.*):(?P\d+)").as_( "pagination_match" ) ) async def products_pagination( cb: CallbackQuery, state: FSMContext, pagination_match: Match[str], session: AsyncSession, products_repo: ProductRepository, categories_repo: CategoriesRepository, ): await state.clear() pagination_data = pagination_match.groupdict() category_id = int(pagination_data.get("category_id")) category = await categories_repo.get_category_by_id(session, category_id) page_id = int(pagination_data.get("page_id", 0)) products = await products_repo.get_product_by_category( session, category_id, limit=PAGE_SIZE + 1, offset=page_id * PAGE_SIZE ) products_len = len(products) await cb.message.edit_reply_markup( reply_markup=render_products( products[:PAGE_SIZE], parent_id=category.parent_id, cat_id=category_id, page=page_id, show_prev=page_id > 0, show_next=products_len > PAGE_SIZE, ), ) @router.callback_query(F.data.startswith("product:")) async def product_card( cb: CallbackQuery, state: FSMContext, session: AsyncSession, products_repo: ProductRepository, order_service: OrderService, ): await state.clear() product_id = int(cb.data.split(":")[1]) product = await products_repo.get_product_by_id(session, product_id) order_item = await order_service.get_cart_product_amount( session, customer=cb.from_user.id, product_id=product_id ) if product.file_id: await cb.message.delete() msg = await cb.message.answer_photo( product.file_id, caption=get_product_description(product), reply_markup=render_product_interactions( product_id=product.id, category_id=product.category_id, show_cart=bool(order_item), cart_amount=order_item, ), ) return if not os.path.isfile(product.img_path): await cb.message.edit_text( get_product_description(product), reply_markup=render_product_interactions( product_id=product.id, category_id=product.category_id, show_cart=bool(order_item), cart_amount=order_item, ), ) return await cb.message.delete() msg = await cb.message.answer_photo( FSInputFile(product.img_path), caption=get_product_description(product), reply_markup=render_product_interactions( product_id=product.id, category_id=product.category_id, show_cart=bool(order_item), cart_amount=order_item, ), ) if not msg.photo: return # TODO: Add logging await products_repo.add_product_file_id( session, product_id, file_id=msg.photo[-1].file_id ) @router.callback_query(F.data.startswith("cart_action:")) async def add_to_cart( cb: CallbackQuery, state: FSMContext, session: AsyncSession, order_service: OrderService, products_repo: ProductRepository, ): await state.clear() action = cb.data.split(":")[1] product_id = int(cb.data.split(":")[2]) product = await products_repo.get_product_by_id(session, product_id) show_cart = True if action == "add": quantity = await order_service.add_to_cart( session, customer=cb.from_user.id, product_id=product_id ) elif action == "remove": quantity = await order_service.remove_from_cart( session, customer=cb.from_user.id, product_id=product_id ) show_cart = bool(quantity) else: show_cart = False quantity = 0 await order_service.clear_cart_item( session, customer=cb.from_user.id, product_id=product_id ) try: await cb.message.edit_reply_markup( reply_markup=render_product_interactions( product_id=product_id, category_id=product.category_id, show_cart=show_cart, cart_amount=quantity, ) ) except Exception as e: await cb.answer("🍃") raise e @router.callback_query(F.data == "cart") async def cart_init( cb: CallbackQuery, state: FSMContext, session: AsyncSession, order_service: OrderService, ): await state.clear() cart = await order_service.build_cart_dto(session, customer=cb.from_user.id) await cb.message.edit_text( f"total: {cart.total}₽", reply_markup=render_cart( cart.items[:PAGE_SIZE], show_next=len(cart.items) > PAGE_SIZE ), ) @router.message(Command("cart")) async def cart_cmd( msg: Message, state: FSMContext, session: AsyncSession, order_service: OrderService, ): await state.clear() cart = await order_service.build_cart_dto(session, customer=msg.from_user.id) await msg.answer( f"total: {cart.total}₽", reply_markup=render_cart( cart.items[:PAGE_SIZE], show_next=len(cart.items) > PAGE_SIZE ), ) @router.callback_query(F.data.startswith("cart:")) async def cart_pagination( cb: CallbackQuery, state: FSMContext, session: AsyncSession, order_service: OrderService, ): await state.clear() page = int(cb.data.split(":")[1]) cart = await order_service.build_cart_dto( session, customer=cb.from_user.id, offset=page * PAGE_SIZE ) await cb.message.edit_reply_markup( reply_markup=render_cart( cart.items[:PAGE_SIZE], page=page, show_next=len(cart.items) > PAGE_SIZE, show_prev=page > 0, ), )