"""Пользовательские хендлеры.""" from __future__ import annotations from aiogram import F, Router from aiogram.filters import CommandObject, CommandStart from aiogram.types import CallbackQuery, Message from remnawave import RemnawaveSDK from config import Settings from keyboards import ( clients_links_inline, clients_root_inline, faq_back_inline, faq_root_inline, main_menu, servers_inline, ) from services.clients import CLIENTS_INTRO, client_card, get_client from services.db import AuthError, Database from services.faq import faq_answer_text, faq_intro, get_faq from services.nodes_status import fetch_nodes_status, format_nodes_status router = Router(name="user") WELCOME = ( "👋 Привет, {name}!\n\n" "Я бот VPN на Remnawave.\n" "Открой Mini App или пользуйся кнопками ниже.\n\n" "Баланс: {balance:.0f} ₽" ) HELP = ( "ℹ️ Помощь\n\n" "• Mini App — кабинет в Telegram\n" "• Тарифы — оплата с баланса или криптой\n" "• Баланс — Crypto Bot / Heleket / Lava / Digiseller / код\n" "• Моя подписка — ссылка, срок, трафик, продление\n" "• Скачать клиент — топ приложений\n" "• Статус серверов — онлайн / оффлайн\n" "• FAQ — частые вопросы и ответы\n" "• Тикеты — написать в поддержку\n\n" "Не нашёл ответ — создай тикет или открой FAQ." ) def _is_admin(settings: Settings, user_id: int | None) -> bool: return bool(user_id and user_id in settings.admin_ids) def _kb(settings: Settings, is_admin: bool = False): return main_menu( is_admin=is_admin, webapp_url=settings.webapp_url or None, ) @router.message(CommandStart()) async def cmd_start( message: Message, command: CommandObject, settings: Settings, db: Database, ) -> None: user = message.from_user name = user.full_name if user else "друг" telegram_id = user.id if user else 0 bot_user = db.ensure_user( telegram_id, username=user.username if user else None, full_name=name if user else None, ) admin = _is_admin(settings, telegram_id or None) args = (command.args or "").strip() if args.startswith("qrauth_") and telegram_id > 0: token = args[len("qrauth_") :] try: db.approve_login_qr(token, telegram_id) await message.answer( "✅ Вход на сайт подтверждён\n\n" "Можно вернуться в браузер — кабинет откроется автоматически.", reply_markup=_kb(settings, admin), parse_mode="HTML", ) return except AuthError as exc: await message.answer( f"⚠️ Не удалось подтвердить QR: {exc}\n\n" + WELCOME.format(name=name, balance=bot_user.balance), reply_markup=_kb(settings, admin), parse_mode="HTML", ) return if args.startswith("ref_") and telegram_id > 0: code = args[len("ref_") :] if db.attach_referrer(telegram_id, code): await message.answer( "🎁 Реферальная ссылка принята! Бонус начислим после первой покупки тарифа.", parse_mode="HTML", ) db.ensure_referral_code(telegram_id) if bot_user.banned: await message.answer( "🚫 Аккаунт заблокирован. Напишите в поддержку, если это ошибка.", parse_mode="HTML", ) return text = WELCOME.format(name=name, balance=bot_user.balance) if not settings.webapp_url: text += ( "\n\nMini App пока выключен: задай WEBAPP_URL (https) в .env" ) await message.answer( text, reply_markup=_kb(settings, admin), parse_mode="HTML", ) @router.message(F.text == "ℹ️ Помощь") async def cmd_help(message: Message, settings: Settings) -> None: admin = _is_admin(settings, message.from_user.id if message.from_user else None) await message.answer(HELP, reply_markup=_kb(settings, admin), parse_mode="HTML") @router.message(F.text == "❓ FAQ") async def cmd_faq(message: Message) -> None: await message.answer( faq_intro(), reply_markup=faq_root_inline(), parse_mode="HTML", ) @router.callback_query(F.data == "faq:root") async def cb_faq_root(callback: CallbackQuery) -> None: await callback.answer() if callback.message: await callback.message.edit_text( faq_intro(), reply_markup=faq_root_inline(), parse_mode="HTML", ) @router.callback_query(F.data.startswith("faq:view:")) async def cb_faq_view(callback: CallbackQuery) -> None: item_id = (callback.data or "").split(":", 2)[2] item = get_faq(item_id) if not item: await callback.answer("Вопрос не найден", show_alert=True) return await callback.answer() if callback.message: await callback.message.edit_text( faq_answer_text(item), reply_markup=faq_back_inline(), parse_mode="HTML", ) @router.message(F.text == "📥 Скачать клиент") async def cmd_clients(message: Message) -> None: await message.answer( CLIENTS_INTRO, reply_markup=clients_root_inline(), parse_mode="HTML", disable_web_page_preview=True, ) @router.callback_query(F.data == "clients:root") async def cb_clients_root(callback: CallbackQuery) -> None: await callback.answer() if callback.message: await callback.message.edit_text( CLIENTS_INTRO, reply_markup=clients_root_inline(), parse_mode="HTML", disable_web_page_preview=True, ) @router.callback_query(F.data.startswith("clients:view:")) async def cb_clients_view(callback: CallbackQuery) -> None: client_id = (callback.data or "").split(":", 2)[2] client = get_client(client_id) if not client: await callback.answer("Клиент не найден", show_alert=True) return await callback.answer() if callback.message: await callback.message.edit_text( client_card(client), reply_markup=clients_links_inline(client.links), parse_mode="HTML", disable_web_page_preview=True, ) @router.message(F.text == "📡 Статус серверов") async def cmd_servers(message: Message, sdk: RemnawaveSDK) -> None: wait = await message.answer("⏳ Проверяю серверы…") try: nodes = await fetch_nodes_status(sdk) text = format_nodes_status(nodes, detailed=False) await wait.edit_text(text, reply_markup=servers_inline(), parse_mode="HTML") except Exception as exc: # noqa: BLE001 await wait.edit_text( f"Не удалось получить статус серверов.\n{exc}", parse_mode="HTML", ) @router.callback_query(F.data == "servers:refresh") async def cb_servers_refresh(callback: CallbackQuery, sdk: RemnawaveSDK) -> None: await callback.answer("Обновляю…") try: nodes = await fetch_nodes_status(sdk) text = format_nodes_status(nodes, detailed=False) if callback.message: await callback.message.edit_text( text, reply_markup=servers_inline(), parse_mode="HTML" ) except Exception as exc: # noqa: BLE001 if callback.message: await callback.message.edit_text( f"Ошибка обновления.\n{exc}", parse_mode="HTML", ) @router.callback_query(F.data == "menu:home") async def cb_menu_home(callback: CallbackQuery, settings: Settings) -> None: await callback.answer() admin = _is_admin(settings, callback.from_user.id if callback.from_user else None) if callback.message: await callback.message.answer( "🏠 Главное меню", reply_markup=_kb(settings, admin), )