Files
Click/main.py
T
Yuriy Yuriev 0102461e78 fix view
2026-05-18 21:19:05 +07:00

179 lines
6.6 KiB
Python

"""
Главный файл бота с сохранением задач.
"""
import asyncio
import logging
from types import SimpleNamespace
from aiogram import Bot, Dispatcher
from aiogram.types import BotCommand
from aiogram.client.session.aiohttp import AiohttpSession
from config.settings import settings
from core.logger import setup_logger
from managers.proxy_manager import ProxyManager
from managers.background_tasks import BackgroundTaskManager
from services.browser_service import BrowserService
from handlers.commands import BotInterface
from managers.storage import TaskStorage, ChatStorage
logger = setup_logger(__name__)
async def set_bot_commands(bot: Bot, is_admin: bool = False):
"""Устанавливает команды бота в меню."""
if is_admin:
commands = [
BotCommand(command="start", description="🏠 Главное меню"),
BotCommand(command="logout", description="🔒 Выйти"),
BotCommand(command="streamers", description="📺 Стримеры"),
BotCommand(command="tasks", description="📊 Задачи"),
BotCommand(command="status", description="📈 Статус"),
]
else:
commands = [
BotCommand(command="start", description="🤖 Старт"),
]
await bot.set_my_commands(commands)
class BotApplication:
def __init__(self):
self.bot: Bot = None
self.dispatcher: Dispatcher = None
self.proxy_manager = ProxyManager()
self.background_tasks = BackgroundTaskManager()
self.browser_service = BrowserService(self.proxy_manager)
self.storage = TaskStorage()
self.chat_storage = ChatStorage()
self.interface = BotInterface(
background_tasks=self.background_tasks,
proxy_manager=self.proxy_manager,
browser_service=self.browser_service,
storage=self.storage,
chat_storage=self.chat_storage,
bot_ref=self,
)
async def _update_bot_commands(self):
is_admin = any(
self.interface.auth_manager.is_admin(uid)
for uid in self.interface.auth_manager._sessions
)
await set_bot_commands(self.bot, is_admin=is_admin)
async def initialize(self):
"""Инициализация бота."""
logger.info("Initializing bot...")
if not settings.BOT_TOKEN:
raise ValueError("BOT_TOKEN not set!")
session = AiohttpSession(proxy=settings.TELEGRAM_PROXY) if settings.TELEGRAM_PROXY else AiohttpSession()
self.bot = Bot(token=settings.BOT_TOKEN, session=session)
self.dispatcher = Dispatcher()
self.interface.register(self.dispatcher)
await self._update_bot_commands()
# Восстанавливаем задачи
await self._restore_tasks()
logger.info(f"Proxies: {self.proxy_manager.count}")
logger.info("Bot initialized!")
async def _restore_tasks(self):
"""Восстанавливает задачи после перезапуска."""
saved_tasks = await self.storage.load_tasks()
if not saved_tasks:
logger.info("No tasks to restore")
return
restored = 0
notify_users: dict = {} # user_id -> chat_id
for task_id, params in saved_tasks.items():
# Завершённые и остановленные — загружаем в память но не запускаем
if params.completed or params.stopped:
await self.interface.task_manager.add_task(task_id, params)
continue
# Активные задачи ставим на паузу и перезапускаем
params.paused = True
await self.interface.task_manager.add_task(task_id, params)
if params.task_type == "twitch_irc" and params.chat_id:
msg_mock = SimpleNamespace(
chat=SimpleNamespace(id=params.chat_id),
bot=self.bot,
)
await self.background_tasks.start_task(
task_id=task_id,
coro=self.interface._run_twitch(task_id, params, msg_mock),
task_type="twitch_irc",
metadata={'type': 'twitch_irc', 'channel': params.channel, 'chat_id': params.chat_id}
)
restored += 1
elif params.task_type == "user_visit" and params.chat_id:
await self.background_tasks.start_task(
task_id=task_id,
coro=self.interface._run_url_visits(task_id, params, self.bot),
task_type="user_visit",
metadata={'type': 'user_visit', 'url': params.url, 'chat_id': params.chat_id}
)
restored += 1
if params.user_id and params.chat_id:
notify_users[params.user_id] = params.chat_id
# Уведомляем пользователей о рестарте
for uid, chat_id in notify_users.items():
try:
await self.bot.send_message(
chat_id,
"🔄 Бот был перезапущен\n\n"
"Ваши задачи приостановлены.\n"
"Нажмите ▶️ в списке задач для возобновления."
)
except Exception as e:
logger.warning(f"Failed to notify user {uid}: {e}")
logger.info(f"🔄 Restored {restored} tasks")
async def start(self):
"""Запуск бота."""
await self.initialize()
logger.info("Starting polling...")
await self.dispatcher.start_polling(self.bot)
async def stop(self):
"""Остановка с сохранением."""
logger.info("Shutting down...")
# Сохраняем задачи
tasks = self.interface.task_manager._tasks
await self.storage.save_tasks(tasks)
await self.background_tasks.cancel_all()
await self.browser_service.cleanup()
if self.bot:
await self.bot.session.close()
logger.info("Bot stopped")
async def main():
app = BotApplication()
try:
await app.start()
except KeyboardInterrupt:
logger.info("Interrupted")
finally:
await app.stop()
if __name__ == "__main__":
asyncio.run(main())