ForcePilot/backend/package/yuxi/channels/adapters/irc/gateway.py
Kris 2dd1a075f2 feat(irc): 实现完整的IRC适配器基础功能模块
新增IRC协议相关的全套工具模块,包括:
- 核心协议解析与CTCP处理
- 消息发送缓存与文本 sanitize
- 账号配置管理与运行时状态
- 命令处理与权限控制
- 服务发现与诊断工具
- 多账号网关与配置加载
2026-05-12 00:45:10 +08:00

114 lines
4.0 KiB
Python

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)