mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-02 18:20:24 +00:00
289 lines
11 KiB
Python
289 lines
11 KiB
Python
import asyncio
|
||
import logging
|
||
import sys
|
||
import os
|
||
from aiogram import Bot, Dispatcher
|
||
from aiogram.fsm.storage.memory import MemoryStorage
|
||
from aiogram.client.default import DefaultBotProperties
|
||
from aiogram.enums import ParseMode
|
||
from lucky_game import lucky_game_router
|
||
|
||
print("🚀 Запуск бота...")
|
||
print(f"📍 Рабочая директория: {os.getcwd()}")
|
||
print(f"📁 Файлы в директории: {os.listdir('.')}")
|
||
|
||
if os.path.exists('.env'):
|
||
print("✅ Файл .env найден")
|
||
else:
|
||
print("❌ Файл .env НЕ НАЙДЕН!")
|
||
print("💡 Создайте файл .env в корне проекта")
|
||
|
||
from config import load_config, debug_environment
|
||
from database import Database
|
||
from remnawave_api import RemnaWaveAPI
|
||
from subscription_monitor import create_subscription_monitor
|
||
from middlewares import DatabaseMiddleware, UserMiddleware, LoggingMiddleware, ThrottlingMiddleware, WorkflowDataMiddleware, BotMiddleware
|
||
from handlers import router
|
||
from admin_handlers import admin_router
|
||
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||
handlers=[
|
||
logging.FileHandler('bot.log'),
|
||
logging.StreamHandler(sys.stdout)
|
||
]
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class BotApplication:
|
||
|
||
def __init__(self):
|
||
self.config = None
|
||
self.db = None
|
||
self.api = None
|
||
self.bot = None
|
||
self.dp = None
|
||
self.monitor_service = None
|
||
|
||
async def initialize(self):
|
||
|
||
debug_environment()
|
||
|
||
self.config = load_config()
|
||
|
||
print(f"🔧 Загруженная конфигурация:")
|
||
print(f" BOT_USERNAME: '{self.config.BOT_USERNAME}'")
|
||
print(f" REFERRAL_FIRST_REWARD: {self.config.REFERRAL_FIRST_REWARD}")
|
||
print(f" ADMIN_IDS: {self.config.ADMIN_IDS}")
|
||
|
||
if not self.config.BOT_TOKEN:
|
||
logger.error("BOT_TOKEN is required")
|
||
raise ValueError("BOT_TOKEN is required")
|
||
|
||
if not self.config.REMNAWAVE_URL or not self.config.REMNAWAVE_TOKEN:
|
||
logger.error("REMNAWAVE_URL and REMNAWAVE_TOKEN are required")
|
||
raise ValueError("REMNAWAVE_URL and REMNAWAVE_TOKEN are required")
|
||
|
||
if not self.config.BOT_USERNAME:
|
||
logger.warning("⚠️ BOT_USERNAME не установлен! Реферальные ссылки работать не будут!")
|
||
print("💡 Добавьте BOT_USERNAME=your_bot_username в .env файл")
|
||
|
||
logger.info("Starting RemnaWave Bot...")
|
||
logger.info(f"RemnaWave URL: {self.config.REMNAWAVE_URL}")
|
||
logger.info(f"Admin IDs: {self.config.ADMIN_IDS}")
|
||
logger.info(f"Bot Username: {self.config.BOT_USERNAME}")
|
||
|
||
self.db = Database(self.config.DATABASE_URL)
|
||
await self._init_database()
|
||
|
||
self.api = RemnaWaveAPI(
|
||
self.config.REMNAWAVE_URL,
|
||
self.config.REMNAWAVE_TOKEN,
|
||
self.config.SUBSCRIPTION_BASE_URL
|
||
)
|
||
logger.info("RemnaWave API initialized")
|
||
|
||
await self._test_api_connection()
|
||
|
||
self.bot = Bot(
|
||
token=self.config.BOT_TOKEN,
|
||
default=DefaultBotProperties(parse_mode=ParseMode.HTML)
|
||
)
|
||
|
||
await self._test_bot_token()
|
||
|
||
self._setup_dispatcher()
|
||
|
||
await self._init_monitor_service()
|
||
|
||
async def _init_database(self):
|
||
max_retries = 3
|
||
for attempt in range(max_retries):
|
||
try:
|
||
await self.db.init_db()
|
||
logger.info("Database initialized successfully")
|
||
break
|
||
except Exception as e:
|
||
logger.error(f"Database initialization attempt {attempt + 1} failed: {e}")
|
||
if attempt == max_retries - 1:
|
||
logger.error("Failed to initialize database after all retries")
|
||
raise
|
||
await asyncio.sleep(2)
|
||
|
||
async def _test_api_connection(self):
|
||
try:
|
||
system_stats = await self.api.get_system_stats()
|
||
if system_stats:
|
||
logger.info("RemnaWave API connection successful")
|
||
else:
|
||
logger.warning("RemnaWave API connection test failed - continuing anyway")
|
||
except Exception as e:
|
||
logger.warning(f"RemnaWave API connection error: {e} - continuing anyway")
|
||
|
||
async def _test_bot_token(self):
|
||
try:
|
||
bot_info = await self.bot.get_me()
|
||
logger.info(f"Bot started: @{bot_info.username} ({bot_info.first_name})")
|
||
|
||
if not self.config.BOT_USERNAME and bot_info.username:
|
||
self.config.BOT_USERNAME = bot_info.username
|
||
logger.info(f"✅ BOT_USERNAME автоматически установлен: {bot_info.username}")
|
||
print("💡 Добавьте BOT_USERNAME в .env файл для постоянного сохранения")
|
||
|
||
except Exception as e:
|
||
logger.error(f"Invalid bot token or network error: {e}")
|
||
raise
|
||
|
||
def _setup_dispatcher(self):
|
||
storage = MemoryStorage()
|
||
self.dp = Dispatcher(storage=storage)
|
||
|
||
self.dp.workflow_data.update({
|
||
"config": self.config,
|
||
"api": self.api,
|
||
"db": self.db,
|
||
"monitor_service": None
|
||
})
|
||
|
||
self.dp.message.middleware(LoggingMiddleware())
|
||
self.dp.callback_query.middleware(LoggingMiddleware())
|
||
|
||
self.dp.message.middleware(ThrottlingMiddleware(rate_limit=0.5))
|
||
self.dp.callback_query.middleware(ThrottlingMiddleware(rate_limit=0.3))
|
||
|
||
self.dp.message.middleware(WorkflowDataMiddleware())
|
||
self.dp.callback_query.middleware(WorkflowDataMiddleware())
|
||
|
||
self.dp.message.middleware(BotMiddleware(self.bot))
|
||
self.dp.callback_query.middleware(BotMiddleware(self.bot))
|
||
|
||
self.dp.message.middleware(DatabaseMiddleware(self.db))
|
||
self.dp.callback_query.middleware(DatabaseMiddleware(self.db))
|
||
|
||
self.dp.message.middleware(UserMiddleware(self.db, self.config))
|
||
self.dp.callback_query.middleware(UserMiddleware(self.db, self.config))
|
||
|
||
self.dp.include_router(router)
|
||
self.dp.include_router(admin_router)
|
||
self.dp.include_router(lucky_game_router)
|
||
|
||
async def _init_monitor_service(self):
|
||
try:
|
||
logger.info("🔧 Initializing subscription monitor service...")
|
||
|
||
if not self.bot:
|
||
logger.error("❌ Bot instance is None, cannot initialize monitor")
|
||
return
|
||
|
||
if not self.db:
|
||
logger.error("❌ Database instance is None, cannot initialize monitor")
|
||
return
|
||
|
||
if not self.config:
|
||
logger.error("❌ Config instance is None, cannot initialize monitor")
|
||
return
|
||
|
||
self.monitor_service = await create_subscription_monitor(
|
||
self.bot, self.db, self.config, self.api
|
||
)
|
||
|
||
if not self.monitor_service:
|
||
logger.error("❌ Failed to create monitor service instance")
|
||
return
|
||
|
||
self.dp.workflow_data["monitor_service"] = self.monitor_service
|
||
logger.info("✅ Monitor service added to workflow_data")
|
||
|
||
logger.info("🚀 Starting monitor service...")
|
||
await self.monitor_service.start()
|
||
|
||
status = await self.monitor_service.get_service_status()
|
||
if status['is_running']:
|
||
logger.info("✅ Subscription monitor service started successfully")
|
||
logger.info(f"📊 Monitor status: interval={status['check_interval']}s, daily_hour={status['daily_check_hour']}, warning_days={status['warning_days']}")
|
||
else:
|
||
logger.warning("⚠️ Monitor service created but not running")
|
||
logger.warning(f"📊 Monitor status: {status}")
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ Failed to initialize monitor service: {e}", exc_info=True)
|
||
logger.warning("⚠️ Continuing without monitor service")
|
||
self.monitor_service = None
|
||
|
||
async def start(self):
|
||
logger.info("Bot polling started successfully")
|
||
|
||
if self.config.BOT_USERNAME:
|
||
logger.info(f"🎁 Реферальная система активна! Ссылки: https://t.me/{self.config.BOT_USERNAME}?start=ref_USERID")
|
||
else:
|
||
logger.warning("⚠️ Реферальная система неактивна! Установите BOT_USERNAME")
|
||
|
||
try:
|
||
await self.dp.start_polling(self.bot)
|
||
except Exception as e:
|
||
logger.error(f"Error during polling: {e}")
|
||
raise
|
||
finally:
|
||
await self.shutdown()
|
||
|
||
async def shutdown(self):
|
||
logger.info("Shutting down bot...")
|
||
|
||
if self.monitor_service:
|
||
try:
|
||
await self.monitor_service.stop()
|
||
logger.info("Monitor service stopped")
|
||
except Exception as e:
|
||
logger.error(f"Error stopping monitor service: {e}")
|
||
|
||
if self.api:
|
||
try:
|
||
await self.api.close()
|
||
logger.info("API connection closed")
|
||
except Exception as e:
|
||
logger.error(f"Error closing API: {e}")
|
||
|
||
if self.db:
|
||
try:
|
||
await self.db.close()
|
||
logger.info("Database connection closed")
|
||
except Exception as e:
|
||
logger.error(f"Error closing database: {e}")
|
||
|
||
if self.bot:
|
||
try:
|
||
await self.bot.session.close()
|
||
logger.info("Bot session closed")
|
||
except Exception as e:
|
||
logger.error(f"Error closing bot session: {e}")
|
||
|
||
logger.info("Bot shutdown complete")
|
||
|
||
async def main():
|
||
app = None
|
||
try:
|
||
app = BotApplication()
|
||
await app.initialize()
|
||
await app.start()
|
||
|
||
except KeyboardInterrupt:
|
||
logger.info("Bot stopped by user (Ctrl+C)")
|
||
except Exception as e:
|
||
logger.error(f"Critical error in main: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
raise
|
||
finally:
|
||
if app:
|
||
await app.shutdown()
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
asyncio.run(main())
|
||
except KeyboardInterrupt:
|
||
logger.info("Bot stopped by user")
|
||
except Exception as e:
|
||
logger.error(f"Fatal error: {e}")
|
||
sys.exit(1)
|