Compare commits

...

26 Commits

Author SHA1 Message Date
Egor ae392691b1 Merge pull request #57 from Fr1ngg/dev
🔄 Переключение лимита трафика
2025-09-12 09:11:21 +03:00
Egor 35a202d280 Add files via upload
🔄 Переключение лимита трафика
2025-09-12 09:09:32 +03:00
Egor e9a2d8a232 Update texts.py 2025-09-12 09:07:16 +03:00
Egor b76b99ca2e Update inline.py 2025-09-12 09:06:52 +03:00
Egor 8960ea9c10 Update subscription.py 2025-09-12 09:06:25 +03:00
Egor 9f94acb580 Merge pull request #56 from Fr1ngg/dev
Индивидуальный сброс устройств
2025-09-12 08:34:50 +03:00
Egor c9811fd518 Update inline.py 2025-09-12 08:32:29 +03:00
Egor e25fb1b178 Update subscription.py 2025-09-12 08:31:30 +03:00
Egor 779a03d008 Merge pull request #55 from Fr1ngg/dev
Переработка меню управлением кол-вом устройств, добавлена возможность уменьшить кол-во устройств на подписке
2025-09-12 04:19:46 +03:00
Egor f2a972aeea Update texts.py 2025-09-12 04:18:21 +03:00
Egor 2a9163a28b Update inline.py 2025-09-12 04:17:48 +03:00
Egor 20a628089d Update subscription.py 2025-09-12 04:16:07 +03:00
Egor cf01d917d1 Merge pull request #54 from yazhog/main
Все сообщения бота с прикрепленным логотипом
2025-09-12 02:51:24 +03:00
yazhog 1544da7e12 Образец логотипа 2025-09-11 22:44:06 +03:00
yazhog 94a533508e Merge pull request #13 from yazhog/codex/find-message-handling-functions-in-bot
Send referral messages as images
2025-09-11 22:40:56 +03:00
yazhog 98cdfcff81 merge main 2025-09-11 22:40:24 +03:00
yazhog 23ddaa81be Set connect button mode to guide 2025-09-11 22:29:31 +03:00
yazhog 667e988c47 refactor: add logo file setting 2025-09-11 22:29:26 +03:00
yazhog ea6b20fefa feat: toggle logo mode 2025-09-11 21:50:29 +03:00
yazhog 4e7154e817 Fix QR detection for returning logo 2025-09-10 02:04:29 +03:00
yazhog 7992cf9e26 Change QR photo back to logo when leaving referral QR 2025-09-10 01:58:26 +03:00
yazhog ea958ea6c0 refactor referral handlers with photo helper 2025-09-10 01:48:41 +03:00
yazhog 9b32de5fb2 Avoid replacing media when editing captions 2025-09-10 01:37:43 +03:00
yazhog b71d0d058f Respond to referral QR callbacks immediately 2025-09-10 01:26:14 +03:00
yazhog d36e353d9b Patch message methods to send logo image 2025-09-10 01:17:30 +03:00
yazhog e68d6ae19a Remove vpn logo asset from repo 2025-09-10 01:10:58 +03:00
11 changed files with 1251 additions and 337 deletions
+4 -1
View File
@@ -190,11 +190,14 @@ CRYPTOBOT_INVOICE_EXPIRES_HOURS=24
# ===== ИНТЕРФЕЙС И UX =====
# Включить логотип для всех сообщений (true - с изображением, false - только текст)
ENABLE_LOGO_MODE=true
LOGO_FILE=vpn_logo.png
# Режим работы кнопки "Подключиться"
# guide - открывает гайд подключения (режим 1)
# miniapp_subscription - открывает ссылку подписки в мини-приложении (режим 2)
# miniapp_custom - открывает заданную ссылку в мини-приложении (режим 3)
# link - открывает ссылку подписки напрямую (режим 4)
CONNECT_BUTTON_MODE=guide
# URL для режима miniapp_custom (обязателен при CONNECT_BUTTON_MODE=miniapp_custom)
+3
View File
@@ -30,6 +30,9 @@ from app.handlers.admin import (
)
from app.handlers.stars_payments import register_stars_handlers
from app.utils.message_patch import patch_message_methods
patch_message_methods()
logger = logging.getLogger(__name__)
+2
View File
@@ -138,6 +138,8 @@ class Settings(BaseSettings):
CONNECT_BUTTON_MODE: str = "guide"
MINIAPP_CUSTOM_URL: str = ""
ENABLE_LOGO_MODE: bool = True
LOGO_FILE: str = "vpn_logo.png"
DEFAULT_LANGUAGE: str = "ru"
AVAILABLE_LANGUAGES: str = "ru,en"
+44 -46
View File
@@ -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,
@@ -90,20 +91,12 @@ async def show_referral_info(
referral_text += "\n"
referral_text += "📢 Приглашайте друзей и зарабатывайте!"
if callback.message.text:
await callback.message.edit_text(
referral_text,
reply_markup=get_referral_keyboard(db_user.language),
parse_mode="HTML"
)
else:
await callback.message.delete()
await callback.message.answer(
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()
@@ -111,6 +104,8 @@ async def show_referral_qr(
callback: types.CallbackQuery,
db_user: User,
):
await callback.answer()
texts = get_texts(db_user.language)
bot_username = (await callback.bot.get_me()).username
@@ -144,7 +139,6 @@ async def show_referral_qr(
caption=f"🔗 Ваша реферальная ссылка:\n{referral_link}",
reply_markup=keyboard,
)
await callback.answer()
async def show_detailed_referral_list(
@@ -154,15 +148,17 @@ async def show_detailed_referral_list(
page: int = 1
):
texts = get_texts(db_user.language)
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_text(
await edit_or_answer_photo(
callback,
"📋 У вас пока нет рефералов.\n\nПоделитесь своей реферальной ссылкой, чтобы начать зарабатывать!",
reply_markup=types.InlineKeyboardMarkup(inline_keyboard=[
[types.InlineKeyboardButton(text=texts.BACK, callback_data="menu_referrals")]
])
types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text=texts.BACK, callback_data="menu_referrals")]]
),
parse_mode=None,
)
await callback.answer()
return
@@ -205,14 +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_text(
await edit_or_answer_photo(
callback,
text,
reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard),
parse_mode="HTML"
types.InlineKeyboardMarkup(inline_keyboard=keyboard),
)
await callback.answer()
@@ -223,31 +219,31 @@ async def show_referral_analytics(
db: AsyncSession
):
texts = get_texts(db_user.language)
analytics = await get_referral_analytics(db, db_user.id)
text = f"📊 <b>Аналитика рефералов</b>\n\n"
text += f"💰 <b>Доходы по периодам:</b>\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"🏆 <b>Топ-{len(analytics['top_referrals'])} рефералов:</b>\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_text(
await edit_or_answer_photo(
callback,
text,
reply_markup=types.InlineKeyboardMarkup(inline_keyboard=[
types.InlineKeyboardMarkup(inline_keyboard=[
[types.InlineKeyboardButton(text=texts.BACK, callback_data="menu_referrals")]
]),
parse_mode="HTML"
)
await callback.answer()
@@ -257,34 +253,36 @@ async def create_invite_message(
db_user: User
):
texts = get_texts(db_user.language)
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"
)]
])
await callback.message.edit_text(
f"📝 <b>Приглашение создано!</b>\n\n"
f"Нажмите кнопку «📤 Поделиться» чтобы отправить приглашение в любой чат, или скопируйте текст ниже:\n\n"
f"<code>{invite_text}</code>",
reply_markup=keyboard,
parse_mode="HTML"
await edit_or_answer_photo(
callback,
(
f"📝 <b>Приглашение создано!</b>\n\n"
f"Нажмите кнопку «📤 Поделиться» чтобы отправить приглашение в любой чат, или скопируйте текст ниже:\n\n"
f"<code>{invite_text}</code>"
),
keyboard,
)
await callback.answer()
File diff suppressed because it is too large Load Diff
+206 -49
View File
@@ -2,6 +2,8 @@ from typing import List, Optional
from aiogram import types
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from datetime import datetime
from app.database.models import User
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import settings, PERIOD_PRICES, TRAFFIC_PRICES
from app.localization.texts import get_texts
@@ -195,40 +197,6 @@ def get_subscription_keyboard(
return InlineKeyboardMarkup(inline_keyboard=keyboard)
def get_subscription_settings_keyboard(language: str = "ru", show_countries_management: bool = True) -> InlineKeyboardMarkup:
from app.config import settings
texts = get_texts(language)
keyboard = []
if show_countries_management:
keyboard.append([
InlineKeyboardButton(text="🌍 Добавить страны", callback_data="subscription_add_countries")
])
keyboard.extend([
[
InlineKeyboardButton(text="📱 Добавить устройства", callback_data="subscription_add_devices")
],
[
InlineKeyboardButton(text="🔄 Сбросить устройства", callback_data="subscription_reset_devices")
]
])
if settings.is_traffic_selectable():
keyboard.insert(-2, [
InlineKeyboardButton(text="📈 Добавить трафик", callback_data="subscription_add_traffic")
])
keyboard.insert(-2, [
InlineKeyboardButton(text="🔄 Сбросить трафик", callback_data="subscription_reset_traffic")
])
keyboard.append([
InlineKeyboardButton(text="⬅️ Назад", callback_data="menu_subscription")
])
return InlineKeyboardMarkup(inline_keyboard=keyboard)
def get_trial_keyboard(language: str = "ru") -> InlineKeyboardMarkup:
texts = get_texts(language)
@@ -778,7 +746,7 @@ def get_add_traffic_keyboard(language: str = "ru", subscription_end_date: dateti
return InlineKeyboardMarkup(inline_keyboard=buttons)
def get_add_devices_keyboard(current_devices: int, language: str = "ru", subscription_end_date: datetime = None) -> InlineKeyboardMarkup:
def get_change_devices_keyboard(current_devices: int, language: str = "ru", subscription_end_date: datetime = None) -> InlineKeyboardMarkup:
from app.utils.pricing_utils import get_remaining_months
from app.config import settings
@@ -793,32 +761,77 @@ def get_add_devices_keyboard(current_devices: int, language: str = "ru", subscri
buttons = []
for count in [1, 2, 3, 4, 5]:
new_total = current_devices + count
if settings.MAX_DEVICES_LIMIT > 0 and new_total > settings.MAX_DEVICES_LIMIT:
continue
price_per_month = count * device_price_per_month
total_price = price_per_month * months_multiplier
if language == "ru":
text = f"📱 +{count} устройство(а) (итого: {new_total}) - {total_price//100}{period_text}"
min_devices = 1
max_devices = settings.MAX_DEVICES_LIMIT if settings.MAX_DEVICES_LIMIT > 0 else 20
start_range = max(1, min(current_devices - 3, max_devices - 6))
end_range = min(max_devices + 1, max(current_devices + 4, 7))
for devices_count in range(start_range, end_range):
if devices_count == current_devices:
emoji = ""
action_text = " (текущее)"
price_text = ""
elif devices_count > current_devices:
emoji = ""
additional_devices = devices_count - current_devices
current_chargeable = max(0, current_devices - settings.DEFAULT_DEVICE_LIMIT)
new_chargeable = max(0, devices_count - settings.DEFAULT_DEVICE_LIMIT)
chargeable_devices = new_chargeable - current_chargeable
if chargeable_devices > 0:
price_per_month = chargeable_devices * device_price_per_month
total_price = price_per_month * months_multiplier
price_text = f" (+{total_price//100}{period_text})"
action_text = ""
else:
price_text = " (бесплатно)"
action_text = ""
else:
text = f"📱 +{count} device(s) (total: {new_total}) - {total_price//100}{period_text}"
emoji = ""
action_text = ""
price_text = " (без возврата)"
button_text = f"{emoji} {devices_count} устр.{action_text}{price_text}"
buttons.append([
InlineKeyboardButton(text=text, callback_data=f"add_devices_{count}")
InlineKeyboardButton(text=button_text, callback_data=f"change_devices_{devices_count}")
])
if current_devices < start_range or current_devices >= end_range:
current_button = f"{current_devices} устр. (текущее)"
buttons.insert(0, [
InlineKeyboardButton(text=current_button, callback_data=f"change_devices_{current_devices}")
])
buttons.append([
InlineKeyboardButton(
text="⬅️ Назад" if language == "ru" else "⬅️ Back",
callback_data="menu_subscription"
text="⬅️ Назад" if language == "ru" else "⬅️ Back",
callback_data="subscription_settings"
)
])
return InlineKeyboardMarkup(inline_keyboard=buttons)
def get_confirm_change_devices_keyboard(new_devices_count: int, price: int, language: str = "ru") -> InlineKeyboardMarkup:
texts = get_texts(language)
return InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(
text="✅ Подтвердить изменение",
callback_data=f"confirm_change_devices_{new_devices_count}_{price}"
)
],
[
InlineKeyboardButton(
text="❌ Отмена",
callback_data="subscription_settings"
)
]
])
def get_reset_traffic_confirm_keyboard(price_kopeks: int, language: str = "ru") -> InlineKeyboardMarkup:
from app.config import settings
@@ -1109,3 +1122,147 @@ def get_cryptobot_payment_keyboard(
)
]
])
def get_devices_management_keyboard(
devices: List[dict],
pagination,
language: str = "ru"
) -> InlineKeyboardMarkup:
keyboard = []
for i, device in enumerate(devices):
platform = device.get('platform', 'Unknown')
device_model = device.get('deviceModel', 'Unknown')
device_info = f"{platform} - {device_model}"
if len(device_info) > 25:
device_info = device_info[:22] + "..."
keyboard.append([
InlineKeyboardButton(
text=f"🔄 {device_info}",
callback_data=f"reset_device_{i}_{pagination.page}"
)
])
if pagination.total_pages > 1:
nav_row = []
if pagination.has_prev:
nav_row.append(
InlineKeyboardButton(
text="⬅️",
callback_data=f"devices_page_{pagination.prev_page}"
)
)
nav_row.append(
InlineKeyboardButton(
text=f"{pagination.page}/{pagination.total_pages}",
callback_data="current_page"
)
)
if pagination.has_next:
nav_row.append(
InlineKeyboardButton(
text="➡️",
callback_data=f"devices_page_{pagination.next_page}"
)
)
keyboard.append(nav_row)
keyboard.append([
InlineKeyboardButton(
text="🔄 Сбросить все устройства",
callback_data="reset_all_devices"
)
])
keyboard.append([
InlineKeyboardButton(
text="⬅️ Назад" if language == "ru" else "⬅️ Back",
callback_data="subscription_settings"
)
])
return InlineKeyboardMarkup(inline_keyboard=keyboard)
def get_updated_subscription_settings_keyboard(language: str = "ru", show_countries_management: bool = True) -> InlineKeyboardMarkup:
from app.config import settings
texts = get_texts(language)
keyboard = []
if show_countries_management:
keyboard.append([
InlineKeyboardButton(text="🌐 Добавить страны", callback_data="subscription_add_countries")
])
keyboard.extend([
[
InlineKeyboardButton(text="📱 Изменить устройства", callback_data="subscription_change_devices")
],
[
InlineKeyboardButton(text="🔧 Управление устройствами", callback_data="subscription_manage_devices")
]
])
if settings.is_traffic_selectable():
keyboard.insert(-2, [
InlineKeyboardButton(text="🔄 Переключить трафик", callback_data="subscription_switch_traffic")
])
keyboard.insert(-2, [
InlineKeyboardButton(text="🔄 Сбросить трафик", callback_data="subscription_reset_traffic")
])
keyboard.append([
InlineKeyboardButton(text="⬅️ Назад", callback_data="menu_subscription")
])
return InlineKeyboardMarkup(inline_keyboard=keyboard)
def get_device_reset_confirm_keyboard(device_info: str, device_index: int, page: int, language: str = "ru") -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(
text="✅ Да, сбросить это устройство",
callback_data=f"confirm_reset_device_{device_index}_{page}"
)
],
[
InlineKeyboardButton(
text="❌ Отмена",
callback_data=f"devices_page_{page}"
)
]
])
def get_device_management_help_keyboard(language: str = "ru") -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(
text="❓ Как подключить устройство заново?",
callback_data="device_connection_help"
)
],
[
InlineKeyboardButton(
text="🔧 Управление устройствами",
callback_data="subscription_manage_devices"
)
],
[
InlineKeyboardButton(
text="⬅️ К подписке",
callback_data="menu_subscription"
)
]
])
+86
View File
@@ -162,6 +162,50 @@ class RussianTexts(Texts):
TRIAL_ACTIVATED = "🎉 Тестовая подписка активирована!"
TRIAL_ALREADY_USED = "❌ Тестовая подписка уже была использована"
CHANGE_DEVICES_TITLE = "📱 Изменение количества устройств"
CHANGE_DEVICES_INFO = """
📱 <b>Изменение количества устройств</b>
Текущий лимит: {current_devices} устройств
Выберите новое количество устройств:
💡 <b>Важно:</b>
При увеличении - доплата пропорционально оставшемуся времени
При уменьшении - возврат средств не производится
"""
CHANGE_DEVICES_CONFIRM = """
📱 <b>Подтверждение изменения</b>
Текущее количество: {current_devices} устройств
Новое количество: {new_devices} устройств
Действие: {action}
💰 {cost}
Подтвердить изменение?
"""
CHANGE_DEVICES_SUCCESS_INCREASE = """
Количество устройств увеличено!
📱 Было: {old_count} Стало: {new_count}
💰 Списано: {amount}
"""
CHANGE_DEVICES_SUCCESS_DECREASE = """
Количество устройств уменьшено!
📱 Было: {old_count} Стало: {new_count}
Возврат средств не производится
"""
DEVICES_NO_CHANGE = "ℹ️ Количество устройств не изменилось"
DEVICES_MINIMUM_LIMIT = "⚠️ Минимальное количество устройств: {limit}"
DEVICES_LIMIT_EXCEEDED = "⚠️ Превышен максимальный лимит устройств ({limit})"
DEVICES_INSUFFICIENT_BALANCE = "⚠️ Недостаточно средств!\nТребуется: {required} (за {months} мес)\nУ вас: {balance}"
BUY_SUBSCRIPTION_START = """
💎 <b>Настройка подписки</b>
@@ -408,6 +452,48 @@ class RussianTexts(Texts):
Ваша подписка автоматически продлена на {days} дней.
Списано с баланса: {amount}
"""
SWITCH_TRAFFIC_TITLE = "🔄 Переключение лимита трафика"
SWITCH_TRAFFIC_INFO = """
🔄 <b>Переключение лимита трафика</b>
Текущий лимит: {current_traffic}
Выберите новый лимит трафика:
💡 <b>Важно:</b>
При увеличении - доплата за разницу пропорционально оставшемуся времени
При уменьшении - возврат средств не производится
Счетчик использованного трафика НЕ сбрасывается
"""
SWITCH_TRAFFIC_CONFIRM = """
🔄 <b>Подтверждение переключения трафика</b>
Текущий лимит: {current_traffic}
Новый лимит: {new_traffic}
Действие: {action}
💰 {cost}
Подтвердить переключение?
"""
SWITCH_TRAFFIC_SUCCESS_INCREASE = """
Лимит трафика увеличен!
📊 Было: {old_traffic} Стало: {new_traffic}
💰 Списано: {amount}
"""
SWITCH_TRAFFIC_SUCCESS_DECREASE = """
Лимит трафика уменьшен!
📊 Было: {old_traffic} Стало: {new_traffic}
Возврат средств не производится
"""
TRAFFIC_NO_CHANGE = "ℹ️ Лимит трафика не изменился"
TRAFFIC_INSUFFICIENT_BALANCE = "⚠️ Недостаточно средств!\nТребуется: {required} (за {months} мес)\nУ вас: {balance}"
AUTOPAY_FAILED = """
<b>Ошибка автоплатежа</b>
+40
View File
@@ -0,0 +1,40 @@
from pathlib import Path
from aiogram.types import Message, FSInputFile, InputMediaPhoto
from app.config import settings
LOGO_PATH = Path(settings.LOGO_FILE)
def is_qr_message(message: Message) -> bool:
return bool(message.caption and message.caption.startswith("\U0001F517 Ваша реферальная ссылка"))
_original_answer = Message.answer
_original_edit_text = Message.edit_text
async def _answer_with_photo(self: Message, text: str = None, **kwargs):
if LOGO_PATH.exists():
return await self.answer_photo(FSInputFile(LOGO_PATH), caption=text, **kwargs)
return await _original_answer(self, text, **kwargs)
async def _edit_with_photo(self: Message, text: str, **kwargs):
if self.photo:
media = self.photo[-1].file_id
if is_qr_message(self) and LOGO_PATH.exists():
media = FSInputFile(LOGO_PATH)
media_kwargs = {"media": media, "caption": text}
if "parse_mode" in kwargs:
media_kwargs["parse_mode"] = kwargs.pop("parse_mode")
return await self.edit_media(InputMediaPhoto(**media_kwargs), **kwargs)
return await _original_edit_text(self, text, **kwargs)
def patch_message_methods():
if not settings.ENABLE_LOGO_MODE:
return
Message.answer = _answer_with_photo
Message.edit_text = _edit_with_photo
+58
View File
@@ -0,0 +1,58 @@
from aiogram import types
from aiogram.exceptions import TelegramBadRequest
from aiogram.types import FSInputFile, InputMediaPhoto
from app.config import settings
from .message_patch import LOGO_PATH, is_qr_message
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:
if not settings.ENABLE_LOGO_MODE:
try:
if callback.message.photo:
await callback.message.delete()
await callback.message.answer(
caption,
reply_markup=keyboard,
parse_mode=parse_mode,
)
else:
await callback.message.edit_text(
caption,
reply_markup=keyboard,
parse_mode=parse_mode,
)
except TelegramBadRequest:
await callback.message.delete()
await callback.message.answer(
caption,
reply_markup=keyboard,
parse_mode=parse_mode,
)
return
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,
)
+2
View File
@@ -67,6 +67,8 @@ services:
# Timezone
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
# Логотип для сообщений
- ./vpn_logo.png:/app/vpn_logo.png:ro
ports:
- "${TRIBUTE_WEBHOOK_PORT:-8081}:8081"
- "${YOOKASSA_WEBHOOK_PORT:-8082}:8082"
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB