Introduce balance field to User model with Alembic migration. Add referral menu, admin /refpay command, and deep link sharing. Update Dockerfile to multi-stage build and add Postgres service to compose. Fix deep link parsing logic and conditional proxy initialization.
231 lines
7.8 KiB
Python
231 lines
7.8 KiB
Python
from misc.utils import fetch_quote
|
||
from db.models.tickets import TicketStatus
|
||
|
||
# ============================================================
|
||
# PREMIUM EMOJI
|
||
# ============================================================
|
||
|
||
|
||
class PremiumEmoji:
|
||
def __init__(self, emoji_id: int, emoji: str):
|
||
self.emoji_id = emoji_id
|
||
self.emoji = emoji
|
||
|
||
def __str__(self) -> str:
|
||
return f'<tg-emoji emoji-id="{self.emoji_id}">{self.emoji}</tg-emoji>'
|
||
|
||
|
||
# — Иконки бота
|
||
MALENIA_LOGO = PremiumEmoji(5265053829522562789, "☀️")
|
||
MALENIA_HEART = PremiumEmoji(5276516908257025723, "❤️")
|
||
MALENIA_LINK = PremiumEmoji(5276012730636082950, "🔗")
|
||
MALENIA_COINS = PremiumEmoji(5276298354551199712, "🪙")
|
||
MALENIA_LOCKED = PremiumEmoji(5276346642868508688, "🔒")
|
||
MALENIA_SUPPORT = PremiumEmoji(5275968088746006467, "🎧")
|
||
|
||
|
||
# ============================================================
|
||
# МАППИНГИ
|
||
# ============================================================
|
||
|
||
TICKET_STATUS_EMOJIS = {
|
||
TicketStatus.CLOSED.value: "🔴",
|
||
TicketStatus.OPEN.value: "🟢",
|
||
TicketStatus.PENDING.value: "🟡",
|
||
}
|
||
|
||
|
||
# ============================================================
|
||
# СТАТУСЫ ПОДПИСКИ
|
||
# ============================================================
|
||
|
||
# — Иконки статусов
|
||
STATUS_ICON_ACTIVE = "🟢"
|
||
STATUS_ICON_DISABLED = "🔴"
|
||
STATUS_ICON_EXPIRED = "🟡"
|
||
STATUS_ICON_LIMITED = "🟠"
|
||
STATUS_ICON_UNKNOWN = "⚪"
|
||
|
||
# — Тексты статусов
|
||
STATUS_ACTIVE = "Активна"
|
||
STATUS_DISABLED = "Отключена"
|
||
STATUS_EXPIRED = "Истекла"
|
||
STATUS_LIMITED = "Превышен лимит трафика"
|
||
STATUS_UNKNOWN = "Неизвестен"
|
||
|
||
|
||
# ============================================================
|
||
# ФОРМАТИРОВАНИЕ ДАННЫХ
|
||
# ============================================================
|
||
|
||
# — Дни до истечения подписки
|
||
DAYS_LEFT_POSITIVE = "{days} дн."
|
||
DAYS_LEFT_EXPIRED = "истекла {days} дн. назад"
|
||
DAYS_LEFT_TODAY = "истекает сегодня"
|
||
|
||
# — Username
|
||
NO_USERNAME = "нет username"
|
||
USERNAME_DISPLAY = "@{username}"
|
||
|
||
# — Internal Squads
|
||
NO_SQUADS = "нет"
|
||
|
||
# — HWID лимит
|
||
UNLIMITED_HWID = "без ограничений"
|
||
|
||
# — Трафик
|
||
NO_TRAFFIC_LIMIT = "без ограничений"
|
||
TRAFFIC_WITH_LIMIT = "{used} / {limit}"
|
||
TRAFFIC_NO_LIMIT = "{used}"
|
||
|
||
# — Прочее
|
||
NOTHING_PLACEHOLDER = "🍃"
|
||
|
||
|
||
# ============================================================
|
||
# ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС
|
||
# ============================================================
|
||
|
||
MAIN_MENU = (
|
||
str(MALENIA_LOGO)
|
||
+ " <b>Malenia — <i>{quote}</i></b>\n\n"
|
||
+ f"<i>{MALENIA_COINS} Личный кабинет</i>\n"
|
||
+ f"<b>{MALENIA_LINK} Ваша ссылка на подписку: {{link}}</b>"
|
||
)
|
||
|
||
FAQ_TEXT = "faq"
|
||
|
||
GENERAL_PROCESSING = "<i>⏳ Выполняю...</i>"
|
||
|
||
STANDARD_FALLBACK = (
|
||
"<b>❌ Что-то пошло не так</b>, прожмите /start и повторите попытку позже."
|
||
)
|
||
|
||
CALLBACK_FALLBACK = "❌ Что-то пошло не так"
|
||
|
||
CONTEXT_REINITIATED = (
|
||
"🔴 Контекст данного взаимодействия сброшен. "
|
||
"Данные могут отличаться от указанных вами."
|
||
)
|
||
|
||
|
||
# ============================================================
|
||
# РЕФЕРАЛЬНАЯ СИСТЕМА
|
||
# ============================================================
|
||
|
||
REFERALS_MENU = (
|
||
"<b>👥 Реферальная система</b>\n\n"
|
||
f"<b>{MALENIA_COINS} Ваш реферальный баланс:</b> "
|
||
"<code>{balance}₽</code>\n"
|
||
f"<b>{MALENIA_LINK} Ваша реферальная ссылка:</b> "
|
||
"<code>{link}</code>"
|
||
)
|
||
|
||
# ============================================================
|
||
# ОФОРМЛЕНИЕ ПОДПИСКИ
|
||
# ============================================================
|
||
|
||
SUBSCRIPTION_DEVICE_SELECTOR = (
|
||
"выбери колво устройств епта. тут ещё цена есть смотри опа: {price}"
|
||
)
|
||
|
||
SUBSCRIPTION_DURATION_SELECTOR = (
|
||
"тут короче сколько время. а ещё цена смотри ОПА {price}"
|
||
)
|
||
|
||
WHITELISTS_ALREADY_ON = "уже включены!"
|
||
|
||
TEMP_SUPPORT_FORM = "обратитесь в поддержку, прикрепив текст ниже:\n\n"
|
||
|
||
CHECKOUT_PAYMENT_CHOICE = (
|
||
"в поддержку чирканите епта там всё обьяснят\n" + TEMP_SUPPORT_FORM
|
||
)
|
||
|
||
|
||
# ============================================================
|
||
# ПОДДЕРЖКА
|
||
# ============================================================
|
||
|
||
SUPPORT_INIT = (
|
||
f"<b>{MALENIA_SUPPORT} <i>Что-то не работает? Жалоба? Предложение?</i></b>\n"
|
||
"— всё сюда, желательно со скриншотами. "
|
||
"Чем детальнее описание, тем быстрее ответ.\n\n"
|
||
"<i>❌ Для отмены — /cancel</i>"
|
||
)
|
||
|
||
SUPPORT_MSG_SENT = (
|
||
"⏳ Сообщение отправлено. Для выхода — /cancel, "
|
||
"вы можете вернуться сюда из главного меню в любое время."
|
||
)
|
||
|
||
SUPPORT_SIGNATURE = "<b>✍️ Сообщение от технической поддержки:</b>"
|
||
|
||
|
||
# ============================================================
|
||
# ШАБЛОНЫ — ОБЩИЕ БЛОКИ
|
||
# ============================================================
|
||
|
||
# Используется в SUPPORT_SUBSCRIPTION и ADMIN_TICKET_WITH_RW
|
||
_SUPPORT_USER_DESCRIPTION = (
|
||
"👤 <b>Имя:</b> {full_name}\n"
|
||
"🔗 <b>Username:</b> {username_display}\n"
|
||
"🆔 <b>Telegram ID:</b> <code>{telegram_id}</code>\n\n"
|
||
"💰 <b>Реферальный баланс:</b> <code>{balance}</code>"
|
||
)
|
||
|
||
_DIVIDER = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
||
|
||
|
||
# ============================================================
|
||
# ШАБЛОНЫ — ЗАЯВКИ И ТИКЕТЫ
|
||
# ============================================================
|
||
|
||
SUPPORT_SUBSCRIPTION = (
|
||
"<b>🎫 ЗАЯВКА НА ПОКУПКУ:</b>\n\n"
|
||
+ _SUPPORT_USER_DESCRIPTION
|
||
+ "\n"
|
||
+ _DIVIDER
|
||
+ "🗓️ Подписка на <b>{duration}</b> месяцев.\n"
|
||
+ "📱 Количество устройств: <b>{devices}</b>\n"
|
||
+ "🏳️ Белые списки: <b>{whitelist_description}</b>\n\n"
|
||
+ "💰 Итого: <b>{price}₽</b>"
|
||
)
|
||
|
||
ADMIN_TICKET_WITH_RW = (
|
||
"🎫 <b>ТИКЕТ T-{ticket_number:04d}</b>\n\n"
|
||
+ _SUPPORT_USER_DESCRIPTION
|
||
+ "\n"
|
||
+ _DIVIDER
|
||
+ "📋 <b>ПОДПИСКА REMNAWAVE</b>\n\n"
|
||
+ "{status_icon} <b>Статус:</b> {status}\n"
|
||
+ "📅 <b>Истекает:</b> {expire_date}\n"
|
||
+ "⏳ <b>Осталось:</b> {days_left}\n"
|
||
+ "📊 <b>Трафик:</b> {used_traffic}\n"
|
||
+ "📱 <b>HWID лимит:</b> {hwid_limit}\n"
|
||
+ "🎯 <b>Internal Squads:</b> {squads}\n\n"
|
||
+ _DIVIDER
|
||
+ "🔑 <b>UUID в Remnawave:</b> <code>{remnawave_uuid}</code>"
|
||
)
|
||
|
||
ADMIN_TICKET_WITHOUT_RW = (
|
||
"🎫 <b>ТИКЕТ T-{ticket_number:04d}</b>\n\n"
|
||
"👤 <b>Имя:</b> {full_name}\n"
|
||
"🔗 <b>Username:</b> {username_display}\n"
|
||
"🆔 <b>Telegram ID:</b> <code>{telegram_id}</code>\n\n"
|
||
+ _DIVIDER
|
||
+ "⚠️ <b>Аккаунт в Remnawave не найден</b>"
|
||
)
|
||
|
||
ADMIN_STANDARD_FALLBACK = (
|
||
"<b>❌ Что-то пошло не так</b>, обратитесь к логам для подробностей."
|
||
)
|
||
|
||
|
||
# ============================================================
|
||
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
|
||
# ============================================================
|
||
|
||
|
||
def build_main_menu_text(link: str) -> str:
|
||
return MAIN_MENU.format(quote=fetch_quote(), link=link)
|