From ea958ea6c0f0a910f3e284b241fd342d4954d56e Mon Sep 17 00:00:00 2001 From: yazhog Date: Wed, 10 Sep 2025 01:48:41 +0300 Subject: [PATCH] refactor referral handlers with photo helper --- app/handlers/referral.py | 100 ++++++++++++++----------------------- app/utils/photo_message.py | 37 ++++++++++++++ 2 files changed, 74 insertions(+), 63 deletions(-) create mode 100644 app/utils/photo_message.py diff --git a/app/handlers/referral.py b/app/handlers/referral.py index 8fa66e37..738e6d12 100644 --- a/app/handlers/referral.py +++ b/app/handlers/referral.py @@ -11,6 +11,7 @@ from app.config import settings from app.database.models import User from app.keyboards.inline import get_referral_keyboard from app.localization.texts import get_texts +from app.utils.photo_message import edit_or_answer_photo from app.utils.user_utils import ( get_detailed_referral_list, get_referral_analytics, @@ -91,25 +92,11 @@ async def show_referral_info( referral_text += "📢 Приглашайте друзей и зарабатывайте!" - is_qr = callback.message.caption and callback.message.caption.startswith("🔗 Ваша реферальная ссылка") - media = callback.message.photo[-1].file_id if callback.message.photo and not is_qr else FSInputFile("vpn_logo.png") - if callback.message.photo and not is_qr: - await callback.message.edit_media( - types.InputMediaPhoto( - media=media, - caption=referral_text, - parse_mode="HTML", - ), - reply_markup=get_referral_keyboard(db_user.language), - ) - else: - await callback.message.delete() - await callback.message.answer_photo( - FSInputFile("vpn_logo.png"), - caption=referral_text, - reply_markup=get_referral_keyboard(db_user.language), - parse_mode="HTML", - ) + await edit_or_answer_photo( + callback, + referral_text, + get_referral_keyboard(db_user.language), + ) await callback.answer() @@ -161,19 +148,17 @@ async def show_detailed_referral_list( page: int = 1 ): texts = get_texts(db_user.language) - media = callback.message.photo[-1].file_id if callback.message.photo else FSInputFile("vpn_logo.png") referrals_data = await get_detailed_referral_list(db, db_user.id, limit=10, offset=(page - 1) * 10) if not referrals_data['referrals']: - await callback.message.edit_media( - types.InputMediaPhoto( - media=media, - caption="📋 У вас пока нет рефералов.\n\nПоделитесь своей реферальной ссылкой, чтобы начать зарабатывать!", + await edit_or_answer_photo( + callback, + "📋 У вас пока нет рефералов.\n\nПоделитесь своей реферальной ссылкой, чтобы начать зарабатывать!", + types.InlineKeyboardMarkup( + inline_keyboard=[[types.InlineKeyboardButton(text=texts.BACK, callback_data="menu_referrals")]] ), - reply_markup=types.InlineKeyboardMarkup(inline_keyboard=[ - [types.InlineKeyboardButton(text=texts.BACK, callback_data="menu_referrals")] - ]), + parse_mode=None, ) await callback.answer() return @@ -216,17 +201,14 @@ async def show_detailed_referral_list( keyboard.append(nav_buttons) keyboard.append([types.InlineKeyboardButton( - text=texts.BACK, + text=texts.BACK, callback_data="menu_referrals" )]) - - await callback.message.edit_media( - types.InputMediaPhoto( - media=media, - caption=text, - parse_mode="HTML", - ), - reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard), + + await edit_or_answer_photo( + callback, + text, + types.InlineKeyboardMarkup(inline_keyboard=keyboard), ) await callback.answer() @@ -237,33 +219,29 @@ async def show_referral_analytics( db: AsyncSession ): texts = get_texts(db_user.language) - media = callback.message.photo[-1].file_id if callback.message.photo else FSInputFile("vpn_logo.png") analytics = await get_referral_analytics(db, db_user.id) text = f"📊 Аналитика рефералов\n\n" - + text += f"💰 Доходы по периодам:\n" text += f"• Сегодня: {texts.format_price(analytics['earnings_by_period']['today'])}\n" text += f"• За неделю: {texts.format_price(analytics['earnings_by_period']['week'])}\n" text += f"• За месяц: {texts.format_price(analytics['earnings_by_period']['month'])}\n" text += f"• За квартал: {texts.format_price(analytics['earnings_by_period']['quarter'])}\n\n" - + if analytics['top_referrals']: text += f"🏆 Топ-{len(analytics['top_referrals'])} рефералов:\n" for i, ref in enumerate(analytics['top_referrals'], 1): text += f"{i}. {ref['referral_name']}: {texts.format_price(ref['total_earned_kopeks'])} ({ref['earnings_count']} начислений)\n" text += "\n" - + text += "📈 Продолжайте развивать свою реферальную сеть!" - - await callback.message.edit_media( - types.InputMediaPhoto( - media=media, - caption=text, - parse_mode="HTML", - ), - reply_markup=types.InlineKeyboardMarkup(inline_keyboard=[ + + await edit_or_answer_photo( + callback, + text, + types.InlineKeyboardMarkup(inline_keyboard=[ [types.InlineKeyboardButton(text=texts.BACK, callback_data="menu_referrals")] ]), ) @@ -278,37 +256,33 @@ async def create_invite_message( bot_username = (await callback.bot.get_me()).username referral_link = f"https://t.me/{bot_username}?start={db_user.referral_code}" - + invite_text = f"🎉 Присоединяйся к VPN сервису!\n\n" invite_text += f"💎 При первом пополнении от {texts.format_price(settings.REFERRAL_MINIMUM_TOPUP_KOPEKS)} ты получишь {texts.format_price(settings.REFERRAL_FIRST_TOPUP_BONUS_KOPEKS)} бонусом на баланс!\n\n" invite_text += f"🚀 Быстрое подключение\n" invite_text += f"🌍 Серверы по всему миру\n" invite_text += f"🔒 Надежная защита\n\n" invite_text += f"👇 Переходи по ссылке:\n{referral_link}" - + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ [types.InlineKeyboardButton( text="📤 Поделиться", - switch_inline_query=invite_text + switch_inline_query=invite_text )], [types.InlineKeyboardButton( text=texts.BACK, callback_data="menu_referrals" )] ]) - - media = callback.message.photo[-1].file_id if callback.message.photo else FSInputFile("vpn_logo.png") - await callback.message.edit_media( - types.InputMediaPhoto( - media=media, - caption=( - f"📝 Приглашение создано!\n\n" - f"Нажмите кнопку «📤 Поделиться» чтобы отправить приглашение в любой чат, или скопируйте текст ниже:\n\n" - f"{invite_text}" - ), - parse_mode="HTML", + + await edit_or_answer_photo( + callback, + ( + f"📝 Приглашение создано!\n\n" + f"Нажмите кнопку «📤 Поделиться» чтобы отправить приглашение в любой чат, или скопируйте текст ниже:\n\n" + f"{invite_text}" ), - reply_markup=keyboard, + keyboard, ) await callback.answer() diff --git a/app/utils/photo_message.py b/app/utils/photo_message.py new file mode 100644 index 00000000..f9ef5c1b --- /dev/null +++ b/app/utils/photo_message.py @@ -0,0 +1,37 @@ +from aiogram import types +from aiogram.exceptions import TelegramBadRequest +from aiogram.types import FSInputFile, InputMediaPhoto + +from .message_patch import LOGO_PATH + + +def _is_qr_message(message: types.Message) -> bool: + return bool(message.caption and message.caption.startswith("\uD83D\uDD17 Ваша реферальная ссылка")) + + +def _resolve_media(message: types.Message): + if message.photo and not _is_qr_message(message): + return message.photo[-1].file_id + return FSInputFile(LOGO_PATH) + + +async def edit_or_answer_photo( + callback: types.CallbackQuery, + caption: str, + keyboard: types.InlineKeyboardMarkup, + parse_mode: str | None = "HTML", +) -> None: + media = _resolve_media(callback.message) + try: + await callback.message.edit_media( + InputMediaPhoto(media=media, caption=caption, parse_mode=parse_mode), + reply_markup=keyboard, + ) + except TelegramBadRequest: + await callback.message.delete() + await callback.message.answer_photo( + FSInputFile(LOGO_PATH), + caption=caption, + reply_markup=keyboard, + parse_mode=parse_mode, + )