Files
remnawave-bedolaga-telegram…/app/services/menu_layout/constants.py
gy9vin 0538d0e337 feat(traffic): улучшение системы докупки и сброса трафика
- Добавлен ENV переключатель TRAFFIC_TOPUP_ENABLED для вкл/выкл докупки
- Добавлена отдельная конфигурация пакетов TRAFFIC_TOPUP_PACKAGES_CONFIG
- Добавлено поле purchased_traffic_gb для отслеживания докупленного трафика
- Добавлены режимы расчета цены сброса (period/traffic/traffic_with_purchased)
- Исправлен абьюз: цена сброса теперь учитывает докупленный трафик
- Сброс purchased_traffic_gb при продлении/покупке подписки
- UX: меню сброса теперь показывает цену и баланс вместо alert
- UX: кнопка пополнения если не хватает средств на сброс
- Добавлена миграция для нового поля purchased_traffic_gb
- Добавлена локализация TRAFFIC_TOPUP_DISABLED (ru/en/ua/zh)
2025-12-25 14:48:24 +03:00

502 lines
23 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Константы для конструктора меню."""
from typing import Any, Dict, List
# Ключ для хранения конфигурации в SystemSetting
MENU_LAYOUT_CONFIG_KEY = "menu_layout_config"
# Дефолтная конфигурация меню
DEFAULT_MENU_CONFIG: Dict[str, Any] = {
"version": 1,
"rows": [
{
"id": "connect_row",
"buttons": ["connect"],
"conditions": {"has_active_subscription": True, "subscription_is_active": True},
"max_per_row": 1,
},
{
"id": "happ_row",
"buttons": ["happ_download"],
"conditions": {"has_active_subscription": True, "happ_enabled": True},
"max_per_row": 1,
},
{
"id": "subscription_traffic_row",
"buttons": ["subscription", "buy_traffic"],
"conditions": {"has_active_subscription": True},
"max_per_row": 2,
},
{
"id": "balance_row",
"buttons": ["balance"],
"conditions": None,
"max_per_row": 1,
},
{
"id": "trial_buy_row",
"buttons": ["trial", "buy_subscription"],
"conditions": None,
"max_per_row": 2,
},
{
"id": "simple_subscription_row",
"buttons": ["simple_subscription"],
"conditions": {"simple_subscription_enabled": True},
"max_per_row": 1,
},
{
"id": "resume_row",
"buttons": ["resume_checkout"],
"conditions": {"has_saved_cart": True},
"max_per_row": 1,
},
{
"id": "promo_referral_row",
"buttons": ["promocode", "referrals"],
"conditions": None,
"max_per_row": 2,
},
{
"id": "contests_row",
"buttons": ["contests"],
"conditions": {"contests_visible": True},
"max_per_row": 2,
},
{
"id": "support_info_row",
"buttons": ["support", "info"],
"conditions": None,
"max_per_row": 2,
},
{
"id": "language_row",
"buttons": ["language"],
"conditions": {"language_selection_enabled": True},
"max_per_row": 2,
},
{
"id": "admin_row",
"buttons": ["admin_panel"],
"conditions": {"is_admin": True},
"max_per_row": 1,
},
{
"id": "moderator_row",
"buttons": ["moderator_panel"],
"conditions": {"is_moderator": True},
"max_per_row": 1,
},
],
"buttons": {
"connect": {
"type": "builtin",
"builtin_id": "connect",
"text": {"ru": "🔗 Подключиться", "en": "🔗 Connect"},
"action": "subscription_connect",
"enabled": True,
"visibility": "subscribers",
"conditions": {"has_active_subscription": True, "subscription_is_active": True},
"dynamic_text": False,
"open_mode": "callback", # "callback" или "direct"
"webapp_url": None, # URL для Mini App при open_mode="direct"
},
"happ_download": {
"type": "builtin",
"builtin_id": "happ_download",
"text": {"ru": "⬇️ Скачать Happ", "en": "⬇️ Download Happ"},
"action": "subscription_happ_download",
"enabled": True,
"visibility": "subscribers",
"conditions": None,
"dynamic_text": False,
},
"subscription": {
"type": "builtin",
"builtin_id": "subscription",
"text": {"ru": "📊 Подписка", "en": "📊 Subscription"},
"action": "menu_subscription",
"enabled": True,
"visibility": "subscribers",
"conditions": None,
"dynamic_text": False,
},
"buy_traffic": {
"type": "builtin",
"builtin_id": "buy_traffic",
"text": {"ru": "📈 Докупить трафик", "en": "📈 Buy traffic"},
"action": "buy_traffic",
"enabled": True,
"visibility": "subscribers",
"conditions": {"has_traffic_limit": True, "traffic_topup_enabled": True},
"dynamic_text": False,
},
"balance": {
"type": "builtin",
"builtin_id": "balance",
"text": {"ru": "💰 Баланс: {balance}", "en": "💰 Balance: {balance}"},
"action": "menu_balance",
"enabled": True,
"visibility": "all",
"conditions": None,
"dynamic_text": True,
},
"trial": {
"type": "builtin",
"builtin_id": "trial",
"text": {"ru": "🎁 Пробный период", "en": "🎁 Free trial"},
"action": "menu_trial",
"enabled": True,
"visibility": "all",
"conditions": {"show_trial": True},
"dynamic_text": False,
},
"buy_subscription": {
"type": "builtin",
"builtin_id": "buy_subscription",
"text": {"ru": "🛒 Купить подписку", "en": "🛒 Buy subscription"},
"action": "menu_buy",
"enabled": True,
"visibility": "all",
"conditions": {"show_buy": True},
"dynamic_text": False,
},
"simple_subscription": {
"type": "builtin",
"builtin_id": "simple_subscription",
"text": {"ru": "💳 Простая подписка", "en": "💳 Simple subscription"},
"action": "simple_subscription_purchase",
"enabled": True,
"visibility": "all",
"conditions": None,
"dynamic_text": False,
},
"resume_checkout": {
"type": "builtin",
"builtin_id": "resume_checkout",
"text": {"ru": "↩️ Вернуться к оформлению", "en": "↩️ Resume checkout"},
"action": "return_to_saved_cart",
"enabled": True,
"visibility": "all",
"conditions": None,
"dynamic_text": False,
},
"promocode": {
"type": "builtin",
"builtin_id": "promocode",
"text": {"ru": "🎟️ Промокод", "en": "🎟️ Promo code"},
"action": "menu_promocode",
"enabled": True,
"visibility": "all",
"conditions": None,
"dynamic_text": False,
},
"referrals": {
"type": "builtin",
"builtin_id": "referrals",
"text": {"ru": "👥 Рефералы", "en": "👥 Referrals"},
"action": "menu_referrals",
"enabled": True,
"visibility": "all",
"conditions": {"referral_enabled": True},
"dynamic_text": False,
},
"contests": {
"type": "builtin",
"builtin_id": "contests",
"text": {"ru": "🎲 Конкурсы", "en": "🎲 Contests"},
"action": "contests_menu",
"enabled": True,
"visibility": "all",
"conditions": None,
"dynamic_text": False,
},
"support": {
"type": "builtin",
"builtin_id": "support",
"text": {"ru": "💬 Поддержка", "en": "💬 Support"},
"action": "menu_support",
"enabled": True,
"visibility": "all",
"conditions": {"support_enabled": True},
"dynamic_text": False,
},
"info": {
"type": "builtin",
"builtin_id": "info",
"text": {"ru": " Инфо", "en": " Info"},
"action": "menu_info",
"enabled": True,
"visibility": "all",
"conditions": None,
"dynamic_text": False,
},
"language": {
"type": "builtin",
"builtin_id": "language",
"text": {"ru": "🌐 Язык", "en": "🌐 Language"},
"action": "menu_language",
"enabled": True,
"visibility": "all",
"conditions": None,
"dynamic_text": False,
},
"admin_panel": {
"type": "builtin",
"builtin_id": "admin_panel",
"text": {"ru": "⚙️ Админ панель", "en": "⚙️ Admin panel"},
"action": "admin_panel",
"enabled": True,
"visibility": "admins",
"conditions": None,
"dynamic_text": False,
},
"moderator_panel": {
"type": "builtin",
"builtin_id": "moderator_panel",
"text": {"ru": "🧑‍⚖️ Модерация", "en": "🧑‍⚖️ Moderation"},
"action": "moderator_panel",
"enabled": True,
"visibility": "moderators",
"conditions": None,
"dynamic_text": False,
},
},
}
# Информация о встроенных кнопках для API
BUILTIN_BUTTONS_INFO: List[Dict[str, Any]] = [
{
"id": "connect",
"default_text": {"ru": "🔗 Подключиться", "en": "🔗 Connect"},
"callback_data": "subscription_connect",
"default_conditions": {"has_active_subscription": True, "subscription_is_active": True},
"supports_dynamic_text": False,
"supports_direct_open": True,
},
{
"id": "happ_download",
"default_text": {"ru": "⬇️ Скачать Happ", "en": "⬇️ Download Happ"},
"callback_data": "subscription_happ_download",
"default_conditions": {"happ_enabled": True},
"supports_dynamic_text": False,
},
{
"id": "subscription",
"default_text": {"ru": "📊 Подписка", "en": "📊 Subscription"},
"callback_data": "menu_subscription",
"default_conditions": {"has_active_subscription": True},
"supports_dynamic_text": False,
},
{
"id": "buy_traffic",
"default_text": {"ru": "📈 Докупить трафик", "en": "📈 Buy traffic"},
"callback_data": "buy_traffic",
"default_conditions": {"has_traffic_limit": True},
"supports_dynamic_text": False,
},
{
"id": "balance",
"default_text": {"ru": "💰 Баланс: {balance}", "en": "💰 Balance: {balance}"},
"callback_data": "menu_balance",
"default_conditions": None,
"supports_dynamic_text": True,
},
{
"id": "trial",
"default_text": {"ru": "🎁 Пробный период", "en": "🎁 Free trial"},
"callback_data": "menu_trial",
"default_conditions": {"show_trial": True},
"supports_dynamic_text": False,
},
{
"id": "buy_subscription",
"default_text": {"ru": "🛒 Купить подписку", "en": "🛒 Buy subscription"},
"callback_data": "menu_buy",
"default_conditions": {"show_buy": True},
"supports_dynamic_text": False,
},
{
"id": "simple_subscription",
"default_text": {"ru": "💳 Простая подписка", "en": "💳 Simple subscription"},
"callback_data": "simple_subscription_purchase",
"default_conditions": {"simple_subscription_enabled": True},
"supports_dynamic_text": False,
},
{
"id": "resume_checkout",
"default_text": {"ru": "↩️ Вернуться к оформлению", "en": "↩️ Resume checkout"},
"callback_data": "return_to_saved_cart",
"default_conditions": {"has_saved_cart": True},
"supports_dynamic_text": False,
},
{
"id": "promocode",
"default_text": {"ru": "🎟️ Промокод", "en": "🎟️ Promo code"},
"callback_data": "menu_promocode",
"default_conditions": None,
"supports_dynamic_text": False,
},
{
"id": "referrals",
"default_text": {"ru": "👥 Рефералы", "en": "👥 Referrals"},
"callback_data": "menu_referrals",
"default_conditions": {"referral_enabled": True},
"supports_dynamic_text": False,
},
{
"id": "contests",
"default_text": {"ru": "🎲 Конкурсы", "en": "🎲 Contests"},
"callback_data": "contests_menu",
"default_conditions": {"contests_visible": True},
"supports_dynamic_text": False,
},
{
"id": "support",
"default_text": {"ru": "💬 Поддержка", "en": "💬 Support"},
"callback_data": "menu_support",
"default_conditions": {"support_enabled": True},
"supports_dynamic_text": False,
},
{
"id": "info",
"default_text": {"ru": " Инфо", "en": " Info"},
"callback_data": "menu_info",
"default_conditions": None,
"supports_dynamic_text": False,
},
{
"id": "language",
"default_text": {"ru": "🌐 Язык", "en": "🌐 Language"},
"callback_data": "menu_language",
"default_conditions": {"language_selection_enabled": True},
"supports_dynamic_text": False,
},
{
"id": "admin_panel",
"default_text": {"ru": "⚙️ Админ панель", "en": "⚙️ Admin panel"},
"callback_data": "admin_panel",
"default_conditions": {"is_admin": True},
"supports_dynamic_text": False,
},
{
"id": "moderator_panel",
"default_text": {"ru": "🧑‍⚖️ Модерация", "en": "🧑‍⚖️ Moderation"},
"callback_data": "moderator_panel",
"default_conditions": {"is_moderator": True},
"supports_dynamic_text": False,
},
]
# Все доступные callback_data в боте (для добавления кастомных кнопок)
AVAILABLE_CALLBACKS: List[Dict[str, Any]] = [
# Меню
{"callback_data": "back_to_menu", "name": "Назад в меню", "category": "menu", "icon": "⬅️",
"text": {"ru": "⬅️ Назад", "en": "⬅️ Back"}},
{"callback_data": "menu_faq", "name": "FAQ", "category": "menu", "icon": "",
"text": {"ru": "❓ FAQ", "en": "❓ FAQ"}},
{"callback_data": "menu_info_promo_groups", "name": "Промо-группы", "category": "menu", "icon": "👥",
"text": {"ru": "👥 Промо-группы", "en": "👥 Promo groups"}},
{"callback_data": "menu_privacy_policy", "name": "Политика конфиденциальности", "category": "menu", "icon": "🔒",
"text": {"ru": "🔒 Политика конфиденциальности", "en": "🔒 Privacy Policy"}},
{"callback_data": "menu_public_offer", "name": "Публичная оферта", "category": "menu", "icon": "📜",
"text": {"ru": "📜 Публичная оферта", "en": "📜 Public Offer"}},
{"callback_data": "menu_rules", "name": "Правила", "category": "menu", "icon": "📋",
"text": {"ru": "📋 Правила", "en": "📋 Rules"}},
{"callback_data": "menu_server_status", "name": "Статус серверов", "category": "menu", "icon": "🖥️",
"text": {"ru": "🖥️ Статус серверов", "en": "🖥️ Server Status"}},
# Баланс
{"callback_data": "balance_history", "name": "История баланса", "category": "balance", "icon": "📜",
"text": {"ru": "📜 История", "en": "📜 History"}},
{"callback_data": "balance_topup", "name": "Пополнить баланс", "category": "balance", "icon": "💳",
"text": {"ru": "💳 Пополнить", "en": "💳 Top up"}},
# Подписка
{"callback_data": "subscription_extend", "name": "Продлить подписку", "category": "subscription", "icon": "📅",
"text": {"ru": "📅 Продлить", "en": "📅 Extend"}, "requires_subscription": True},
{"callback_data": "subscription_autopay", "name": "Автоплатёж", "category": "subscription", "icon": "🔄",
"text": {"ru": "🔄 Автоплатёж", "en": "🔄 Autopay"}, "requires_subscription": True},
{"callback_data": "subscription_settings", "name": "Настройки подписки", "category": "subscription", "icon": "⚙️",
"text": {"ru": "⚙️ Настройки", "en": "⚙️ Settings"}, "requires_subscription": True},
{"callback_data": "open_subscription_link", "name": "Показать ссылку подписки", "category": "subscription", "icon": "🔗",
"text": {"ru": "🔗 Показать ссылку", "en": "🔗 Show link"}, "requires_subscription": True},
{"callback_data": "subscription_add_countries", "name": "Добавить страны", "category": "subscription", "icon": "🌍",
"text": {"ru": "🌍 Добавить страны", "en": "🌍 Add countries"}, "requires_subscription": True},
{"callback_data": "subscription_reset_traffic", "name": "Сбросить трафик", "category": "subscription", "icon": "🔄",
"text": {"ru": "🔄 Сбросить трафик", "en": "🔄 Reset traffic"}, "requires_subscription": True},
{"callback_data": "subscription_switch_traffic", "name": "Переключить трафик", "category": "subscription", "icon": "🔀",
"text": {"ru": "🔀 Переключить трафик", "en": "🔀 Switch traffic"}, "requires_subscription": True},
{"callback_data": "subscription_change_devices", "name": "Изменить устройства", "category": "subscription", "icon": "📱",
"text": {"ru": "📱 Изменить устройства", "en": "📱 Change devices"}, "requires_subscription": True},
{"callback_data": "subscription_manage_devices", "name": "Управление устройствами", "category": "subscription", "icon": "📲",
"text": {"ru": "📲 Управление устройствами", "en": "📲 Manage devices"}, "requires_subscription": True},
{"callback_data": "subscription_upgrade", "name": "Улучшить подписку", "category": "subscription", "icon": "⬆️",
"text": {"ru": "⬆️ Улучшить", "en": "⬆️ Upgrade"}, "requires_subscription": True},
# Подключение устройств
{"callback_data": "device_guide_ios", "name": "Инструкция iOS", "category": "devices", "icon": "📱",
"text": {"ru": "📱 iOS", "en": "📱 iOS"}, "requires_subscription": True},
{"callback_data": "device_guide_android", "name": "Инструкция Android", "category": "devices", "icon": "🤖",
"text": {"ru": "🤖 Android", "en": "🤖 Android"}, "requires_subscription": True},
{"callback_data": "device_guide_windows", "name": "Инструкция Windows", "category": "devices", "icon": "💻",
"text": {"ru": "💻 Windows", "en": "💻 Windows"}, "requires_subscription": True},
{"callback_data": "device_guide_mac", "name": "Инструкция macOS", "category": "devices", "icon": "🎯",
"text": {"ru": "🎯 macOS", "en": "🎯 macOS"}, "requires_subscription": True},
{"callback_data": "device_guide_tv", "name": "Инструкция Android TV", "category": "devices", "icon": "📺",
"text": {"ru": "📺 Android TV", "en": "📺 Android TV"}, "requires_subscription": True},
{"callback_data": "device_guide_appletv", "name": "Инструкция Apple TV", "category": "devices", "icon": "📺",
"text": {"ru": "📺 Apple TV", "en": "📺 Apple TV"}, "requires_subscription": True},
# Happ
{"callback_data": "happ_download_ios", "name": "Скачать Happ iOS", "category": "happ", "icon": "🍎",
"text": {"ru": "🍎 iOS", "en": "🍎 iOS"}},
{"callback_data": "happ_download_android", "name": "Скачать Happ Android", "category": "happ", "icon": "🤖",
"text": {"ru": "🤖 Android", "en": "🤖 Android"}},
{"callback_data": "happ_download_macos", "name": "Скачать Happ macOS", "category": "happ", "icon": "🖥️",
"text": {"ru": "🖥️ macOS", "en": "🖥️ macOS"}},
{"callback_data": "happ_download_windows", "name": "Скачать Happ Windows", "category": "happ", "icon": "💻",
"text": {"ru": "💻 Windows", "en": "💻 Windows"}},
# Рефералы
{"callback_data": "referral_create_invite", "name": "Создать инвайт", "category": "referral", "icon": "✉️",
"text": {"ru": "✉️ Создать инвайт", "en": "✉️ Create invite"}},
{"callback_data": "referral_show_qr", "name": "QR код реферала", "category": "referral", "icon": "📱",
"text": {"ru": "📱 QR код", "en": "📱 QR code"}},
{"callback_data": "referral_list", "name": "Список рефералов", "category": "referral", "icon": "👥",
"text": {"ru": "👥 Мои рефералы", "en": "👥 My referrals"}},
{"callback_data": "referral_analytics", "name": "Аналитика рефералов", "category": "referral", "icon": "📊",
"text": {"ru": "📊 Аналитика", "en": "📊 Analytics"}},
# Поддержка
{"callback_data": "create_ticket", "name": "Создать тикет", "category": "support", "icon": "✏️",
"text": {"ru": "✏️ Создать тикет", "en": "✏️ Create ticket"}},
{"callback_data": "my_tickets", "name": "Мои тикеты", "category": "support", "icon": "📋",
"text": {"ru": "📋 Мои тикеты", "en": "📋 My tickets"}},
# Триал
{"callback_data": "trial_activate", "name": "Активировать триал", "category": "trial", "icon": "🎁",
"text": {"ru": "🎁 Активировать", "en": "🎁 Activate"}},
# Покупка
{"callback_data": "clear_saved_cart", "name": "Очистить корзину", "category": "purchase", "icon": "🗑️",
"text": {"ru": "🗑️ Очистить корзину", "en": "🗑️ Clear cart"}},
{"callback_data": "subscription_confirm", "name": "Подтвердить покупку", "category": "purchase", "icon": "",
"text": {"ru": "✅ Подтвердить", "en": "✅ Confirm"}},
{"callback_data": "subscription_cancel", "name": "Отменить покупку", "category": "purchase", "icon": "",
"text": {"ru": "❌ Отменить", "en": "❌ Cancel"}},
]
# Динамические плейсхолдеры для текста кнопок
DYNAMIC_PLACEHOLDERS: List[Dict[str, str]] = [
{"placeholder": "{balance}", "description": "Баланс пользователя", "example": "1 500 ₽", "category": "user"},
{"placeholder": "{username}", "description": "Имя пользователя", "example": "John", "category": "user"},
{"placeholder": "{subscription_days}", "description": "Дней до окончания подписки", "example": "14", "category": "subscription"},
{"placeholder": "{traffic_used}", "description": "Использованный трафик", "example": "5.2 GB", "category": "subscription"},
{"placeholder": "{traffic_left}", "description": "Оставшийся трафик", "example": "94.8 GB", "category": "subscription"},
{"placeholder": "{referral_count}", "description": "Количество рефералов", "example": "12", "category": "referral"},
{"placeholder": "{referral_earnings}", "description": "Заработок с рефералов", "example": "500 ₽", "category": "referral"},
]