Files
marzban_tg_bot/main.py
2026-01-09 22:21:26 +03:00

117 lines
4.5 KiB
Python

import asyncio
import logging
from datetime import datetime
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from config import CONFIG
from database import db
from marzban import marzban
from handlers import routers
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Assuming grant_subscription is defined elsewhere or was intended to be added here.
# Based on the user's instruction and the provided "Code Edit" snippet,
# it seems the user intended to add or modify this function.
# Since the original content does not contain this function, I will add it
# at the module level, as it's a common pattern for helper functions.
# The "Code Edit" snippet was syntactically incorrect in its placement,
# so I'm placing it logically.
# Note: 'PLANS' is not defined in the provided content, assuming it's imported or defined elsewhere.
# The '... rest of logic uses expire_days ...' part is a placeholder as it was in the instruction.
async def grant_subscription(user_id, plan_id, is_unlimited_promo=False, bonus_days=0):
# This function body is taken directly from the user's instruction.
# 'PLANS' is not defined in the provided document, assuming it's available globally.
# This is a placeholder for the actual implementation.
PLANS = {} # Placeholder for demonstration, replace with actual PLANS source
plan = PLANS.get(plan_id)
if not plan and not is_unlimited_promo:
return
# Determine duration and data limit
if is_unlimited_promo:
expire_days = 365 * 10 # 10 years
data_limit = 0 # Unlimited
else:
expire_days = plan['days'] + bonus_days
data_limit = plan['limit_gb']
# ... rest of logic uses expire_days ...
# This part was indicated by '{{ ... }}' in the user's instruction,
# implying existing logic that uses expire_days.
# For now, it's a comment.
logger.info(f"Granting subscription for user {user_id} with plan {plan_id}. "
f"Expire days: {expire_days}, Data limit: {data_limit} GB.")
async def main():
# Инициализация бота
bot = Bot(token=CONFIG["BOT_TOKEN"])
dp = Dispatcher(storage=MemoryStorage())
# Подключение роутеров из папки handlers
for router in routers:
dp.include_router(router)
# Инициализация сервисов
await db.connect()
await marzban.init_session()
# Web Server setup
try:
from server import app as web_app
import uvicorn
web_app.state.bot = bot
config = uvicorn.Config(web_app, host="0.0.0.0", port=CONFIG["WEB_APP_PORT"], log_level="info")
server = uvicorn.Server(config)
except ImportError:
logger.error("Could not import server or uvicorn")
server = None
try:
await bot.delete_webhook(drop_pending_updates=True)
# Set Menu Button
from aiogram.types import MenuButtonWebApp, WebAppInfo
if CONFIG["WEB_APP_URL"]:
try:
await bot.set_chat_menu_button(
menu_button=MenuButtonWebApp(text="🚀 Dashboard", web_app=WebAppInfo(url=CONFIG["WEB_APP_URL"]))
)
logger.info(f"Menu button set to {CONFIG['WEB_APP_URL']}")
except Exception as e:
logger.error(f"Failed to set menu button: {e}")
else:
# Если URL не задан, сбрасываем кнопку (чтобы не осталась старая ссылка)
await bot.delete_chat_menu_button()
logger.info("WEB_APP_URL not found, menu button reset to default.")
logger.info(f"Config: BASE_URL={CONFIG['BASE_URL']}, WEB_APP_URL={CONFIG['WEB_APP_URL']}")
logger.info("Bot started!")
if server:
logger.info(f"Starting Web App on port {CONFIG['WEB_APP_PORT']}")
await asyncio.gather(
dp.start_polling(bot),
server.serve()
)
else:
await dp.start_polling(bot)
finally:
await marzban.close_session()
await bot.session.close()
if __name__ == "__main__":
try:
asyncio.run(main())
except (KeyboardInterrupt, SystemExit):
logger.info("Bot stopped!")