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_coupon_audience = State() waiting_for_balance_data = State() waiting_for_days_data = State() waiting_for_percent_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="πŸ“‰ ΠŸΡ€ΠΎΡ†Π΅Π½Ρ‚", callback_data="coupon_type_percent") 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) async def show_coupon_audience_step(callback_query: CallbackQuery, state: FSMContext): text = "🎯 ΠšΠΎΠΌΡƒ доступСн ΠΊΡƒΠΏΠΎΠ½?" kb = InlineKeyboardBuilder() kb.button(text="πŸ‘€ ВсСм", callback_data="coupon_audience_all") kb.button(text="πŸ†• Волько Π½ΠΎΠ²Ρ‹ΠΌ", callback_data="coupon_audience_new") 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_audience) @router.callback_query(F.data == "coupon_type_balance", IsAdminFilter()) async def handle_balance_coupon_selection(callback_query: CallbackQuery, state: FSMContext): await state.update_data(coupon_type="balance") await show_coupon_audience_step(callback_query, state) @router.callback_query(F.data == "coupon_type_days", IsAdminFilter()) async def handle_days_coupon_selection(callback_query: CallbackQuery, state: FSMContext): await state.update_data(coupon_type="days", new_users_only=False) kb = InlineKeyboardBuilder() kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) kb.adjust(1) text = ( "🎫 Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅ для создания ΠΊΡƒΠΏΠΎΠ½Π° Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:\n\n" "πŸ“ ΠΊΠΎΠ΄ ⏳ Π΄Π½ΠΈ πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'DAYS10 10 50'\n\n" ) await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) await state.set_state(AdminCouponsState.waiting_for_days_data) @router.callback_query(F.data == "coupon_type_percent", IsAdminFilter()) async def handle_percent_coupon_selection(callback_query: CallbackQuery, state: FSMContext): await state.update_data(coupon_type="percent", new_users_only=False) kb = InlineKeyboardBuilder() kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) kb.adjust(1) text = ( "🎫 Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅ для создания ΠΊΡƒΠΏΠΎΠ½Π° Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:\n\n" "πŸ“ ΠΊΠΎΠ΄ πŸ“‰ ΠΏΡ€ΠΎΡ†Π΅Π½Ρ‚ πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'SALE20 20 10'\n" "Π“Π΄Π΅ 20 β€” это скидка 20%\n\n" ) await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) await state.set_state(AdminCouponsState.waiting_for_percent_data) @router.callback_query(F.data.in_(("coupon_audience_all", "coupon_audience_new")), IsAdminFilter()) async def handle_coupon_audience(callback_query: CallbackQuery, state: FSMContext): data = await state.get_data() coupon_type = data.get("coupon_type") if coupon_type != "balance": await callback_query.answer("Ошибка: Ρ€Π΅ΠΆΠΈΠΌ доступСн Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для ΠΊΡƒΠΏΠΎΠ½ΠΎΠ² Π½Π° баланс", show_alert=True) return await state.update_data(new_users_only=callback_query.data == "coupon_audience_new") kb = InlineKeyboardBuilder() kb.button(text=BACK, callback_data=AdminPanelCallback(action="coupons").pack()) kb.adjust(1) text = ( "🎫 Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅ для создания ΠΊΡƒΠΏΠΎΠ½Π° Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:\n\n" "πŸ“ ΠΊΠΎΠ΄ πŸ’° сумма πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'COUPON1 50 5'\n\n" ) await callback_query.message.edit_text(text=text, reply_markup=kb.as_markup()) await state.set_state(AdminCouponsState.waiting_for_balance_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()) kb.adjust(1) 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 if usage_limit <= 0: raise ValueError except ValueError: text = "⚠️ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅!\nΠ‘ΡƒΠΌΠΌΠ° ΠΈ Π»ΠΈΠΌΠΈΡ‚ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ†Π΅Π»Ρ‹ΠΌΠΈ числами большС 0." await message.answer(text=text, reply_markup=kb.as_markup()) return try: data = await state.get_data() new_users_only = bool(data.get("new_users_only")) ok = await create_coupon( session, coupon_code, coupon_amount, usage_limit, days=None, new_users_only=new_users_only, percent=None, ) if not ok: await message.answer("❌ ΠšΡƒΠΏΠΎΠ½ с Ρ‚Π°ΠΊΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ ΡƒΠΆΠ΅ сущСствуСт.", reply_markup=kb.as_markup()) return coupon_link = f"https://t.me/{USERNAME_BOT}?start=coupons_{coupon_code}" audience_txt = "πŸ†• Волько Π½ΠΎΠ²Ρ‹ΠΌ" if new_users_only else "πŸ‘€ ВсСм" text = ( f"βœ… ΠšΡƒΠΏΠΎΠ½ {coupon_code} создан!\n" f"πŸ’° Π‘ΡƒΠΌΠΌΠ°: {coupon_amount} Ρ€ΡƒΠ±Π»Π΅ΠΉ\n" f"πŸ”’ Π›ΠΈΠΌΠΈΡ‚: {usage_limit} Ρ€Π°Π·\n" f"🎯 Доступ: {audience_txt}\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()) kb.adjust(1) 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 if usage_limit <= 0: raise ValueError except ValueError: text = "⚠️ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅!\nΠ”Π½ΠΈ ΠΈ Π»ΠΈΠΌΠΈΡ‚ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ†Π΅Π»Ρ‹ΠΌΠΈ числами большС 0." await message.answer(text=text, reply_markup=kb.as_markup()) return try: ok = await create_coupon( session, coupon_code, 0, usage_limit, days=days, new_users_only=False, percent=None, ) if not ok: await message.answer("❌ ΠšΡƒΠΏΠΎΠ½ с Ρ‚Π°ΠΊΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ ΡƒΠΆΠ΅ сущСствуСт.", reply_markup=kb.as_markup()) return 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.message(AdminCouponsState.waiting_for_percent_data, IsAdminFilter()) async def handle_percent_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()) kb.adjust(1) if len(parts) != 3: text = ( "❌ НСкоррСктный Ρ„ΠΎΡ€ΠΌΠ°Ρ‚!\n" "🏷️ ΠΊΠΎΠ΄ πŸ“‰ ΠΏΡ€ΠΎΡ†Π΅Π½Ρ‚ πŸ”’ Π»ΠΈΠΌΠΈΡ‚\n" "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: 'SALE20 20 10'" ) await message.answer(text=text, reply_markup=kb.as_markup()) return try: coupon_code = parts[0] percent = int(parts[1]) usage_limit = int(parts[2]) if percent <= 0 or percent > 100: raise ValueError if usage_limit <= 0: raise ValueError except ValueError: text = "⚠️ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ Π΄Π°Π½Π½Ρ‹Π΅!\nΠŸΡ€ΠΎΡ†Π΅Π½Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ 1..100, Π»ΠΈΠΌΠΈΡ‚ β€” Ρ†Π΅Π»ΠΎΠ΅ число большС 0." await message.answer(text=text, reply_markup=kb.as_markup()) return try: ok = await create_coupon( session, coupon_code, 0, usage_limit, days=None, new_users_only=False, percent=percent, ) if not ok: await message.answer("❌ ΠšΡƒΠΏΠΎΠ½ с Ρ‚Π°ΠΊΠΈΠΌ ΠΊΠΎΠ΄ΠΎΠΌ ΡƒΠΆΠ΅ сущСствуСт.", reply_markup=kb.as_markup()) return text = ( f"βœ… ΠšΡƒΠΏΠΎΠ½ {coupon_code} создан!\nπŸ“‰ Π‘ΠΊΠΈΠ΄ΠΊΠ°: {percent}%\nπŸ”’ Π›ΠΈΠΌΠΈΡ‚: {usage_limit} Ρ€Π°Π·\n" ) kb = InlineKeyboardBuilder() 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] 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 percent_value = coupon.get("percent") if percent_value is not None and int(percent_value) > 0: await inline_query.answer( results=[], switch_pm_text="ΠŸΡ€ΠΎΡ†Π΅Π½Ρ‚Π½Ρ‹Π΅ ΠΊΡƒΠΏΠΎΠ½Ρ‹ Π½Π΅ ΠΏΡƒΠ±Π»ΠΈΠΊΡƒΡŽΡ‚ΡΡ ссылкой", switch_pm_parameter="coupons", cache_time=1, ) return coupon_link = f"https://t.me/{USERNAME_BOT}?start=coupons_{coupon_code}" title = f"ΠšΡƒΠΏΠΎΠ½ {coupon['code']}" days_value = coupon.get("days") amount_value = coupon.get("amount") or 0 if days_value is not None and int(days_value) > 0: days_int = int(days_value) description = f"ΠŸΡ€ΠΎΠ΄Π»ΠΈ подписку Π½Π° {format_days(days_int)}!" message_text = ( f"🎫 ΠšΡƒΠΏΠΎΠ½: {coupon['code']}\n" f"⏳ ΠŸΡ€ΠΎΠ΄Π»Π΅Π½ΠΈΠ΅: {format_days(days_int)}\n" f"πŸ‘‡ НаТми, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ!" ) elif int(amount_value) > 0: amount_int = int(amount_value) description = f"ΠŸΠΎΠ»ΡƒΡ‡ΠΈ {amount_int} Ρ€ΡƒΠ±Π»Π΅ΠΉ!" message_text = ( f"🎫 ΠšΡƒΠΏΠΎΠ½: {coupon['code']}\nπŸ’° Бонус: {amount_int} Ρ€ΡƒΠ±Π»Π΅ΠΉ\nπŸ‘‡ НаТми, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ!" ) else: description = "ΠšΡƒΠΏΠΎΠ½" message_text = f"🎫 ΠšΡƒΠΏΠΎΠ½: {coupon['code']}\nπŸ‘‡ НаТми, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ!" 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)