import asyncio import logging from pathlib import Path from aiogram import Bot, Dispatcher from aiogram.client.default import DefaultBotProperties from aiogram.client.session.aiohttp import AiohttpSession from aiogram.enums import ParseMode from aiogram.exceptions import TelegramNetworkError from aiogram.types import BotCommand from app.bot.handlers.main import build_router from app.config import Settings from app.db.session import create_engine_and_session_factory, ensure_database_exists, init_db from app.services.bot_config_service import BotConfigService from app.services.notification_service import SubscriptionNotificationService from app.services.payment_service import PaymentService from app.services.remnawave_client import RemnawaveApiClient from app.services.support_ticket_service import SupportTicketService from app.services.sync_service import SyncService async def main() -> None: settings = Settings() logging.basicConfig( level=getattr(logging, settings.log_level.upper(), logging.INFO), format="%(asctime)s | %(levelname)s | %(name)s | %(message)s", ) logger = logging.getLogger(__name__) engine = None remnawave_client = None bot = None try: if settings.create_database_on_start: logger.info( "Ensuring MariaDB database '%s' exists on %s:%s", settings.db_name, settings.db_host, settings.db_port, ) await ensure_database_exists( settings.database_server_url, settings.db_name, echo=settings.db_echo, ) engine, session_factory = create_engine_and_session_factory( settings.database_url, echo=settings.db_echo, ) if settings.create_tables_on_start: await init_db(engine) remnawave_client = RemnawaveApiClient( base_url=settings.remnawave_base_url, api_token=settings.remnawave_api_token, caddy_api_key=settings.remnawave_caddy_api_key, timeout_seconds=settings.remnawave_timeout_seconds, ) sync_service = SyncService( settings=settings, session_factory=session_factory, remnawave_client=remnawave_client, ) bot_config_service = BotConfigService( settings=settings, session_factory=session_factory, ) support_ticket_service = SupportTicketService(session_factory=session_factory) payment_service = PaymentService( settings=settings, session_factory=session_factory, remnawave_client=remnawave_client, sync_service=sync_service, config_service=bot_config_service, ) bot_session = AiohttpSession(proxy=settings.telegram_proxy_url or None) bot = Bot( token=settings.bot_token, session=bot_session, default=DefaultBotProperties(parse_mode=ParseMode.HTML), ) dispatcher = Dispatcher() dispatcher.include_router( build_router( settings=settings, sync_service=sync_service, support_ticket_service=support_ticket_service, payment_service=payment_service, bot_config_service=bot_config_service, ) ) await bot.set_my_commands( [ BotCommand(command="start", description="Открыть главное меню"), ] ) project_root = Path(__file__).resolve().parent.parent notification_service = SubscriptionNotificationService( session_factory=session_factory, sync_service=sync_service, config_service=bot_config_service, project_root=project_root, ) notification_task = asyncio.create_task( notification_service.run_periodic(bot), ) try: await dispatcher.start_polling(bot) finally: notification_task.cancel() try: await notification_task except asyncio.CancelledError: pass except TelegramNetworkError: logger.exception( "Cannot reach Telegram API. Check outbound HTTPS access to api.telegram.org:443, DNS, firewall, VPN, or proxy." ) raise finally: if remnawave_client is not None: await remnawave_client.close() if bot is not None: await bot.session.close() if engine is not None: await engine.dispose()