# bot/management/commands/runbots.py import asyncio import logging import signal from typing import Sequence, Tuple from django.core.management.base import BaseCommand from bot.models import TelegramBot, BotConfig from bot.bot_factory import get_all_active_bots_with_configs, build_application_for_bot log = logging.getLogger(__name__) class Command(BaseCommand): help = "Запуск ВСЕХ активных ботов (PTB 22.3) в одном процессе." def handle(self, *args, **options): logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s") # ВАЖНО: ORM здесь, в синхронном контексте bot_cfg_pairs: Sequence[Tuple[TelegramBot, BotConfig]] = get_all_active_bots_with_configs() if not bot_cfg_pairs: self.stderr.write(self.style.ERROR("Нет активных ботов (is_active=True).")) return asyncio.run(self._amain(bot_cfg_pairs)) async def _amain(self, bot_cfg_pairs: Sequence[Tuple[TelegramBot, BotConfig]]): apps = [] try: # Ни одной ORM-операции тут — только PTB for tb, cfg in bot_cfg_pairs: app, allowed_updates = build_application_for_bot(tb, cfg) await app.initialize() await app.start() await app.updater.start_polling(allowed_updates=allowed_updates) apps.append(app) log.info("Bot started: %s (@%s)", tb.name, tb.username or "—") # Ждём SIGINT/SIGTERM stop_event = asyncio.Event() def _stop(*_): stop_event.set() loop = asyncio.get_running_loop() for sig in (signal.SIGINT, signal.SIGTERM): try: loop.add_signal_handler(sig, _stop) except NotImplementedError: pass # Windows await stop_event.wait() finally: for app in reversed(apps): try: await app.updater.stop() await app.stop() await app.shutdown() except Exception: log.exception("Shutdown error") log.info("All bots stopped gracefully.")