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,
+ )