ForcePilot/backend/package/yuxi/channels/registry.py
Kris ede29b1809 refactor(channel): 完成频道模块大重构与功能扩展
本次提交对频道模块进行了全面重构并新增多项核心功能:
1.  优化适配器状态获取逻辑,修复状态返回空值问题
2.  新增4种频道异常类型,完善错误处理体系
3.  大幅精简Mixin类,移除冗余的抽象方法定义
4.  重构适配器注册系统,统一注册入口并新增内置适配器加载方法
5.  扩展插件系统,新增更多元数据配置项支持
6.  新增线程类型、会话范围等模型定义,扩展事件类型枚举
7.  优化用户映射逻辑,使用PostgreSQL upsert避免重复创建
8.  新增历史消息注入模块,支持多格式历史格式化与缓存管理
9.  新增线程能力配置与各平台预置适配配置
10. 新增线程绑定管理器,支持多类型线程绑定生命周期管理
11. 重构__init__.py,整理导出模块与类型
12. 扩展基础适配器类,新增凭证解析、状态存储等核心方法
13. 重写消息路由器,支持按频道加载策略、安全校验与多命令处理
14. 新增/history、/context、/summary等交互命令实现
15. 优化消息记录与统计逻辑,完善路由调度链路
2026-05-13 16:41:11 +08:00

225 lines
6.8 KiB
Python

from __future__ import annotations
from yuxi.channels.base import BaseChannelAdapter
_BUILTIN_ADAPTERS: dict[str, type[BaseChannelAdapter]] = {}
_BUILTIN_ADAPTER_ALIASES: dict[str, str] = {}
def _register_builtin(channel_id: str, cls: type[BaseChannelAdapter], aliases: list[str] | None = None) -> None:
"""统一的内置适配器注册入口 — 所有注册路径最终都调用此函数"""
_BUILTIN_ADAPTERS[channel_id] = cls
if aliases:
for alias in aliases:
_BUILTIN_ADAPTER_ALIASES[alias] = channel_id
def register_builtin_adapter(cls: type[BaseChannelAdapter] | None = None, *, aliases: list[str] | None = None):
def _decorator(cls_inner: type[BaseChannelAdapter]) -> type[BaseChannelAdapter]:
_register_builtin(cls_inner.channel_id, cls_inner, aliases)
return cls_inner
if cls is None:
return _decorator
return _decorator(cls)
def _load_builtin_adapters():
if _BUILTIN_ADAPTERS:
return
try:
from yuxi.channels.adapters.webchat import WebChatAdapter
_BUILTIN_ADAPTERS["webchat"] = WebChatAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.telegram import TelegramAdapter
_BUILTIN_ADAPTERS["telegram"] = TelegramAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.slack import SlackAdapter
_BUILTIN_ADAPTERS["slack"] = SlackAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.matrix import MatrixAdapter
_BUILTIN_ADAPTERS["matrix"] = MatrixAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.mattermost import MattermostAdapter
_BUILTIN_ADAPTERS["mattermost"] = MattermostAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.nostr import NostrAdapter
_BUILTIN_ADAPTERS["nostr"] = NostrAdapter
except ImportError:
pass
try:
from yuxi.channels.twitch import TwitchAdapter
_BUILTIN_ADAPTERS["twitch"] = TwitchAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.imessage import IMessageAdapter
_BUILTIN_ADAPTERS["imessage"] = IMessageAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.whatsapp import WhatsAppAdapter
_BUILTIN_ADAPTERS["whatsapp"] = WhatsAppAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.discord import DiscordAdapter
_BUILTIN_ADAPTERS["discord"] = DiscordAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.irc import IRCAdapter
_BUILTIN_ADAPTERS["irc"] = IRCAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.dingding import DingDingChannelAdapter
_BUILTIN_ADAPTERS["dingding"] = DingDingChannelAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.feishu import FeishuAdapter
_BUILTIN_ADAPTERS["feishu"] = FeishuAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.msteams import MSTeamsAdapter
_BUILTIN_ADAPTERS["msteams"] = MSTeamsAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.bluebubbles import BlueBubblesAdapter
_BUILTIN_ADAPTERS["bluebubbles"] = BlueBubblesAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.synologychat import SynologyChatAdapter
_BUILTIN_ADAPTERS["synologychat"] = SynologyChatAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.nextcloudtalk import NextcloudTalkAdapter
_BUILTIN_ADAPTERS["nextcloud-talk"] = NextcloudTalkAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.googlechat import GoogleChatAdapter
_BUILTIN_ADAPTERS["googlechat"] = GoogleChatAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.line import LINEAdapter
_BUILTIN_ADAPTERS["line"] = LINEAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.qqbot import QQBotAdapter
_BUILTIN_ADAPTERS["qqbot"] = QQBotAdapter
except ImportError:
pass
try:
from yuxi.channels.signal.channel import SignalChannel
_BUILTIN_ADAPTERS["signal"] = SignalChannel
except ImportError:
pass
try:
from yuxi.channels.adapters.zalo_user import ZaloUserAdapter
_BUILTIN_ADAPTERS["zalo_user"] = ZaloUserAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.urbit import UrbitAdapter
_BUILTIN_ADAPTERS["urbit"] = UrbitAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.zalo_oa import ZaloOAAdapter
_BUILTIN_ADAPTERS["zalo_oa"] = ZaloOAAdapter
except ImportError:
pass
try:
from yuxi.channels.adapters.wechat import WeChatAdapter
_BUILTIN_ADAPTERS["wechat"] = WeChatAdapter
except ImportError:
pass
class ChannelRegistry:
def __init__(self):
self._adapters: dict[str, type[BaseChannelAdapter]] = {}
self._alias_map: dict[str, str] = {}
def _build_alias_map(self) -> None:
for alias, channel_id in _BUILTIN_ADAPTER_ALIASES.items():
self._alias_map[alias] = channel_id
for channel_id, adapter_cls in {**_BUILTIN_ADAPTERS, **self._adapters}.items():
meta = getattr(adapter_cls, "meta", None)
if meta and meta.aliases:
for alias in meta.aliases:
self._alias_map[alias] = channel_id
def get(self, channel_id: str) -> type[BaseChannelAdapter] | None:
if channel_id in self._adapters:
return self._adapters[channel_id]
if channel_id in _BUILTIN_ADAPTERS:
return _BUILTIN_ADAPTERS[channel_id]
if not self._alias_map:
self._build_alias_map()
resolved = self._alias_map.get(channel_id)
if resolved:
return self._adapters.get(resolved) or _BUILTIN_ADAPTERS.get(resolved)
return None
def register(self, channel_id: str, adapter_cls: type[BaseChannelAdapter]) -> None:
self._adapters[channel_id] = adapter_cls
def list_channels(self) -> list[str]:
return list({*self._adapters.keys(), *_BUILTIN_ADAPTERS.keys()})
def unregister(self, channel_id: str) -> None:
self._adapters.pop(channel_id, None)
def load_builtins(self) -> None:
"""将 _BUILTIN_ADAPTERS 中的所有内置适配器加载到 self._adapters 中"""
_load_builtin_adapters()
for channel_id, adapter_cls in _BUILTIN_ADAPTERS.items():
self._adapters.setdefault(channel_id, adapter_cls)
BUILTIN_ADAPTERS = _BUILTIN_ADAPTERS