from __future__ import annotations import asyncio from typing import Any from yuxi.utils.logging_config import logger from .accounts import get_account_config, list_enabled_irc_accounts, list_irc_account_ids class IRCGateway: def __init__(self, config: dict[str, Any]): self._config = config self._adapters: dict[str, Any] = {} self._tasks: dict[str, asyncio.Task] = {} self._running = False self._reload_lock = asyncio.Lock() @property def account_ids(self) -> list[str]: return list(self._adapters.keys()) @property def is_running(self) -> bool: return self._running async def start_all(self) -> None: self._running = True accounts = list_enabled_irc_accounts(self._config) if not accounts: logger.info("IRC gateway: no accounts configured, using single-adapter mode") return for account_config in accounts: account_id = account_config.get("_account_id", "default") await self._start_account(account_id, account_config) logger.info(f"IRC gateway started with {len(self._adapters)} accounts") async def start_account(self, account_id: str) -> None: account_config = get_account_config(self._config, account_id) if account_config is None: raise ValueError(f"IRC account '{account_id}' not found or disabled") await self._start_account(account_id, account_config) async def stop_account(self, account_id: str) -> None: adapter = self._adapters.pop(account_id, None) if adapter is None: return try: await adapter.disconnect() except Exception as e: logger.warning(f"IRC gateway: error stopping account '{account_id}': {e}") task = self._tasks.pop(account_id, None) if task and not task.done(): task.cancel() logger.info(f"IRC gateway: stopped account '{account_id}'") async def stop_all(self) -> None: self._running = False for account_id in list(self._adapters.keys()): await self.stop_account(account_id) logger.info("IRC gateway: all accounts stopped") async def reload(self, new_config: dict[str, Any]) -> None: async with self._reload_lock: self._config = new_config new_ids = set(list_irc_account_ids(new_config)) old_ids = set(self._adapters.keys()) for removed_id in old_ids - new_ids: await self.stop_account(removed_id) for existing_id in old_ids & new_ids: account_config = get_account_config(new_config, existing_id) if account_config: logger.info(f"IRC gateway: reloading account '{existing_id}'") await self.stop_account(existing_id) await self._start_account(existing_id, account_config) for added_id in new_ids - old_ids: account_config = get_account_config(new_config, added_id) if account_config: await self._start_account(added_id, account_config) logger.info(f"IRC gateway: config reloaded ({len(self._adapters)} accounts)") async def _start_account(self, account_id: str, account_config: dict[str, Any]) -> None: if account_id in self._adapters: logger.warning(f"IRC gateway: account '{account_id}' already running") return from .adapter import IRCAdapter adapter = IRCAdapter(account_config) self._adapters[account_id] = adapter try: await adapter.connect() except Exception as e: logger.error(f"IRC gateway: failed to start account '{account_id}': {e}") self._adapters.pop(account_id, None) raise logger.info(f"IRC gateway: account '{account_id}' connected") def get_adapter(self, account_id: str): return self._adapters.get(account_id) def get_all_adapters(self) -> dict[str, Any]: return dict(self._adapters)