from typing import Any from aiogram import F, Router from aiogram.enums import ParseMode from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import ( CallbackQuery, InlineQuery, InlineQueryResultArticle, InputTextMessageContent, Message, ) from aiogram.utils.keyboard import InlineKeyboardBuilder from config import INLINE_MODE, USERNAME_BOT from database import create_coupon, delete_coupon, get_all_coupons from filters.admin import IsAdminFilter from handlers.buttons import BACK from handlers.utils import format_days from logger import logger from ..panel.keyboard import AdminPanelCallback, build_admin_back_kb from .keyboard import ( AdminCouponDeleteCallback, build_coupons_kb, build_coupons_list_kb, format_coupons_list, ) router = Router() class AdminCouponsState(StatesGroup): waiting_for_coupon_type = State() waiting_for_balance_data = State() waiting_for_days_data = State() @router.callback_query( AdminPanelCallback.filter(F.action == "coupons"), IsAdminFilter(), ) async def handle_coupons(callback_query: CallbackQuery): await callback_query.message.edit_text( text="πŸ›  МСню управлСния ΠΊΡƒΠΏΠΎΠ½Π°ΠΌΠΈ:", reply_markup=build_coupons_kb() ) @router.callback_query( AdminPanelCallback.filter(F.action == "coupons_create"), IsAdminFilter(), ) async def handle_coupons_create(callback_query: CallbackQuery, state: FSMContext): text = "🎫 Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ Ρ‚ΠΈΠΏ ΠΊΡƒΠΏΠΎΠ½Π°:" kb = InlineKeyboardBuilder() kb.button(text="πŸ’° Баланс", callback_data="coupon_type_balance") kb.button(text="⏳ ВрСмя", callback_data="coupon_type_days") kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) kb.adjust(1) await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) await state.set_state(AdminCouponsState.waiting_for_coupon_type) @router.callback_query(F.data == "coupon_type_balance") async def handle_balance_coupon_selection( callback_query: CallbackQuery, state: FSMContext ): text = ( "🎫 Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅ для создания ΠΊΡƒΠΏΠΎΠ½Π° Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:\n\n" "πŸ“ ΠΊΠΎΠ΄ πŸ’° сумма πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'COUPON1 50 5' πŸ‘ˆ\n\n" ) kb = InlineKeyboardBuilder() kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) await state.set_state(AdminCouponsState.waiting_for_balance_data) @router.callback_query(F.data == "coupon_type_days") async def handle_days_coupon_selection( callback_query: CallbackQuery, state: FSMContext ): text = ( "🎫 Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅ для создания ΠΊΡƒΠΏΠΎΠ½Π° Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:\n\n" "πŸ“ ΠΊΠΎΠ΄ ⏳ Π΄Π½ΠΈ πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'DAYS10 10 50' πŸ‘ˆ\n\n" ) kb = InlineKeyboardBuilder() kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) await state.set_state(AdminCouponsState.waiting_for_days_data) @router.message(AdminCouponsState.waiting_for_balance_data, IsAdminFilter()) async def handle_balance_coupon_input( message: Message, state: FSMContext, session: Any ): text = message.text.strip() parts = text.split() kb = InlineKeyboardBuilder() kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) if len(parts) != 3: text = ( "❌ НСкоррСктный Ρ„ΠΎΡ€ΠΌΠ°Ρ‚! πŸ“ ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π²Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:\n" "🏷️ ΠΊΠΎΠ΄ πŸ’° сумма πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'COUPON1 50 5' πŸ‘ˆ" ) await message.answer(text=text, reply_markup=kb.as_markup()) return try: coupon_code = parts[0] coupon_amount = int(parts[1]) usage_limit = int(parts[2]) if coupon_amount <= 0: raise ValueError("Π‘ΡƒΠΌΠΌΠ° Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ большС 0") except ValueError: text = "⚠️ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΡŒ Π²Π²Π΅Π΄Π΅Π½Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ…!\nπŸ’± Π‘ΡƒΠΌΠΌΠ° Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ числом, Π° Π»ΠΈΠΌΠΈΡ‚ β€” Ρ†Π΅Π»Ρ‹ΠΌ числом." await message.answer(text=text, reply_markup=kb.as_markup()) return try: await create_coupon(session, coupon_code, coupon_amount, usage_limit, days=None) coupon_link = f"https://t.me/{USERNAME_BOT}?start=coupons_{coupon_code}" text = ( f"βœ… ΠšΡƒΠΏΠΎΠ½ с ΠΊΠΎΠ΄ΠΎΠΌ {coupon_code} ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ создан!\n" f"πŸ’° Π‘ΡƒΠΌΠΌΠ°: {coupon_amount} Ρ€ΡƒΠ±Π»Π΅ΠΉ\n" f"πŸ”’ Π›ΠΈΠΌΠΈΡ‚ использования: {usage_limit} Ρ€Π°Π·\n" f"πŸ”— Бсылка: {coupon_link}\n" ) kb = InlineKeyboardBuilder() if INLINE_MODE: kb.button(text="πŸ“€ ΠŸΠΎΠ΄Π΅Π»ΠΈΡ‚ΡŒΡΡ", switch_inline_query=f"coupon_{coupon_code}") kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) kb.adjust(1) await message.answer(text=text, reply_markup=kb.as_markup()) await state.clear() except Exception as e: logger.error(f"Ошибка ΠΏΡ€ΠΈ создании ΠΊΡƒΠΏΠΎΠ½Π°: {e}") await message.answer( "❌ ΠŸΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка ΠΏΡ€ΠΈ создании ΠΊΡƒΠΏΠΎΠ½Π°.", reply_markup=kb.as_markup() ) @router.message(AdminCouponsState.waiting_for_days_data, IsAdminFilter()) async def handle_days_coupon_input(message: Message, state: FSMContext, session: Any): text = message.text.strip() parts = text.split() kb = InlineKeyboardBuilder() kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) if len(parts) != 3: text = ( "❌ НСкоррСктный Ρ„ΠΎΡ€ΠΌΠ°Ρ‚! πŸ“ ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π²Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:\n" "🏷️ ΠΊΠΎΠ΄ ⏳ Π΄Π½ΠΈ πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'DAYS10 10 50' πŸ‘ˆ" ) await message.answer(text=text, reply_markup=kb.as_markup()) return try: coupon_code = parts[0] days = int(parts[1]) usage_limit = int(parts[2]) if days <= 0: raise ValueError("ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ Π΄Π½Π΅ΠΉ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ большС 0") except ValueError: text = "⚠️ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΡŒ Π²Π²Π΅Π΄Π΅Π½Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ…!\nπŸ’± Π”Π½ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ числом, Π° Π»ΠΈΠΌΠΈΡ‚ β€” Ρ†Π΅Π»Ρ‹ΠΌ числом." await message.answer(text=text, reply_markup=kb.as_markup()) return try: await create_coupon(session, coupon_code, 0, usage_limit, days=days) coupon_link = f"https://t.me/{USERNAME_BOT}?start=coupons_{coupon_code}" text = ( f"βœ… ΠšΡƒΠΏΠΎΠ½ с ΠΊΠΎΠ΄ΠΎΠΌ {coupon_code} ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ создан!\n" f"⏳ {format_days(days)}\n" f"πŸ”’ Π›ΠΈΠΌΠΈΡ‚ использования: {usage_limit} Ρ€Π°Π·\n" f"πŸ”— Бсылка: {coupon_link}\n" ) kb = InlineKeyboardBuilder() if INLINE_MODE: kb.button(text="πŸ“€ ΠŸΠΎΠ΄Π΅Π»ΠΈΡ‚ΡŒΡΡ", switch_inline_query=f"coupon_{coupon_code}") kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) kb.adjust(1) await message.answer(text=text, reply_markup=kb.as_markup()) await state.clear() except Exception as e: logger.error(f"Ошибка ΠΏΡ€ΠΈ создании ΠΊΡƒΠΏΠΎΠ½Π°: {e}") await message.answer( "❌ ΠŸΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка ΠΏΡ€ΠΈ создании ΠΊΡƒΠΏΠΎΠ½Π°.", reply_markup=kb.as_markup() ) @router.callback_query( AdminPanelCallback.filter(F.action == "coupons_list"), IsAdminFilter(), ) async def handle_coupons_list(callback_query: CallbackQuery, session: Any): try: data = AdminPanelCallback.unpack(callback_query.data) page = data.page if data.page is not None else 1 await update_coupons_list(callback_query.message, session, page) except Exception as e: logger.error(f"Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ списка ΠΊΡƒΠΏΠΎΠ½ΠΎΠ²: {e}") await callback_query.message.edit_text( "ΠŸΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ списка ΠΊΡƒΠΏΠΎΠ½ΠΎΠ²." ) @router.callback_query( AdminCouponDeleteCallback.filter(F.confirm.is_(None)), IsAdminFilter() ) async def handle_coupon_delete( callback_query: CallbackQuery, callback_data: AdminCouponDeleteCallback, session: Any, ): coupon_code = callback_data.coupon_code kb = InlineKeyboardBuilder() kb.button( text="βœ… Π”Π°, ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ", callback_data=AdminCouponDeleteCallback( coupon_code=coupon_code, confirm=True ).pack(), ) kb.button( text="❌ НСт, ΠΎΡ‚ΠΌΠ΅Π½ΠΈΡ‚ΡŒ", callback_data=AdminCouponDeleteCallback( coupon_code=coupon_code, confirm=False ).pack(), ) kb.adjust(1) await callback_query.message.edit_text( f"Π’Ρ‹ ΡƒΠ²Π΅Ρ€Π΅Π½Ρ‹, Ρ‡Ρ‚ΠΎ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ ΠΊΡƒΠΏΠΎΠ½ {coupon_code}?", reply_markup=kb.as_markup(), ) @router.callback_query( AdminCouponDeleteCallback.filter(F.confirm.is_not(None)), IsAdminFilter() ) async def confirm_coupon_delete( callback_query: CallbackQuery, callback_data: AdminCouponDeleteCallback, session: Any, ): coupon_code = callback_data.coupon_code confirm = callback_data.confirm if confirm: try: result = await delete_coupon(session, coupon_code) if not result: await callback_query.message.edit_text( f"❌ ΠšΡƒΠΏΠΎΠ½ с ΠΊΠΎΠ΄ΠΎΠΌ {coupon_code} Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½.", reply_markup=build_admin_back_kb("coupons"), ) return except Exception as e: logger.error(f"Ошибка ΠΏΡ€ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠΈ ΠΊΡƒΠΏΠΎΠ½Π°: {e}") await callback_query.message.edit_text( "ΠŸΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка ΠΏΡ€ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠΈ ΠΊΡƒΠΏΠΎΠ½Π°.", reply_markup=build_admin_back_kb("coupons"), ) return await update_coupons_list(callback_query.message, session) async def update_coupons_list(message, session: Any, page: int = 1): per_page = 10 result = await get_all_coupons(session, page, per_page) coupons = result["coupons"] if not coupons: await message.edit_text( text="❌ На Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ Π½Π΅Ρ‚ доступных ΠΊΡƒΠΏΠΎΠ½ΠΎΠ²!", reply_markup=build_admin_back_kb("coupons"), ) return kb = build_coupons_list_kb(coupons, result["current_page"], result["pages"]) text = format_coupons_list(coupons, USERNAME_BOT) await message.edit_text(text=text, reply_markup=kb) @router.inline_query(F.query.startswith("coupon_")) async def inline_coupon_handler(inline_query: InlineQuery, session: Any): if not INLINE_MODE: return coupon_code = inline_query.query.split("coupon_")[1] coupon_link = f"https://t.me/{USERNAME_BOT}?start=coupons_{coupon_code}" coupons = await get_all_coupons(session, page=1, per_page=10) coupon = next((c for c in coupons["coupons"] if c["code"] == coupon_code), None) if not coupon: await inline_query.answer( results=[], switch_pm_text="ΠšΡƒΠΏΠΎΠ½ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½", switch_pm_parameter="coupons", cache_time=1, ) return title = f"ΠšΡƒΠΏΠΎΠ½ {coupon['code']}" description = ( f"ΠŸΠΎΠ»ΡƒΡ‡ΠΈ {coupon['amount']} Ρ€ΡƒΠ±Π»Π΅ΠΉ!" if coupon["amount"] > 0 else f"ΠŸΡ€ΠΎΠ΄Π»ΠΈ подписку Π½Π° {format_days(coupon['days'])}!" ) message_text = ( f"🎫 ΠšΡƒΠΏΠΎΠ½: {coupon['code']}\n" f"{'πŸ’° Бонус: ' + str(coupon['amount']) + ' Ρ€ΡƒΠ±Π»Π΅ΠΉ' if coupon['amount'] > 0 else '⏳ ΠŸΡ€ΠΎΠ΄Π»Π΅Π½ΠΈΠ΅: ' + format_days(coupon['days'])}\n" f"πŸ‘‡ НаТми, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ!" ) builder = InlineKeyboardBuilder() builder.button(text="ΠΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΡƒΠΏΠΎΠ½", url=coupon_link) result = InlineQueryResultArticle( id=coupon_code, title=title, description=description, input_message_content=InputTextMessageContent( message_text=message_text, parse_mode=ParseMode.HTML ), reply_markup=builder.as_markup(), ) await inline_query.answer(results=[result], cache_time=86400, is_personal=True)