# 适配器新目录结构 ## 1. 设计目标 - **模块化**:每个适配器从单文件拆分到独立目录,各模块职责清晰 - **可维护**:随着事件和 API 的增加,代码量会显著增长,目录结构有助于管理复杂度 - **一致性**:所有适配器遵循相同的目录布局和文件命名约定 - **兼容现有发现机制**:保持 YAML manifest + ComponentDiscoveryEngine 的注册体系 ## 2. 新目录布局 ### 2.1 整体结构 ``` pkg/platform/ ├── __init__.py ├── botmgr.py # PlatformManager + RuntimeBot(重构) ├── event_bus.py # EventBus(新增) ├── event_router.py # EventRouter(新增) ├── logger.py # EventLogger(保留) ├── webhook_pusher.py # WebhookPusher(重构为 WebhookHandler) │ ├── adapters/ # 适配器(新目录) │ ├── __init__.py │ │ │ ├── telegram/ │ │ ├── __init__.py │ │ ├── adapter.py # TelegramAdapter 主类 │ │ ├── event_converter.py # 平台事件 → 统一事件 │ │ ├── message_converter.py # MessageChain 互转 │ │ ├── api_impl.py # 通用 API 实现 │ │ ├── platform_api.py # call_platform_api 的动作映射 │ │ ├── types.py # 平台特有类型定义 │ │ └── manifest.yaml # 适配器清单 │ │ │ ├── discord/ │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── event_converter.py │ │ ├── message_converter.py │ │ ├── api_impl.py │ │ ├── platform_api.py │ │ ├── types.py │ │ ├── voice.py # Discord 语音连接管理(特有) │ │ └── manifest.yaml │ │ │ ├── aiocqhttp/ # OneBot v11 (QQ) │ │ └── ... │ ├── qqofficial/ │ │ └── ... │ ├── lark/ # 飞书 │ │ └── ... │ ├── dingtalk/ │ │ └── ... │ ├── slack/ │ │ └── ... │ ├── wechatpad/ │ │ └── ... │ ├── officialaccount/ # 微信公众号 │ │ └── ... │ ├── wecom/ # 企业微信 │ │ └── ... │ ├── wecombot/ │ │ └── ... │ ├── wecomcs/ │ │ └── ... │ ├── kook/ │ │ └── ... │ ├── line/ │ │ └── ... │ ├── satori/ │ │ └── ... │ ├── websocket/ # 内置 WebSocket 适配器 │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── manager.py # WebSocket 连接管理 │ │ └── manifest.yaml │ │ │ └── legacy/ # 旧版适配器(保留一段时间后移除) │ ├── gewechat/ │ ├── nakuru/ │ └── qqbotpy/ │ └── handlers/ # 事件处理器实现(新增) ├── __init__.py ├── base.py # AbstractEventHandler 基类 ├── pipeline_handler.py # PipelineHandler ├── agent_handler.py # AgentHandler ├── webhook_handler.py # WebhookHandler └── plugin_handler.py # PluginHandler ``` ### 2.2 适配器目录内各文件职责 以 Telegram 为例: | 文件 | 职责 | 关键类/函数 | |------|------|------------| | `adapter.py` | 主入口,继承 `AbstractPlatformAdapter`,组装其他模块 | `TelegramAdapter` | | `event_converter.py` | 将 Telegram 原生事件转换为统一事件类型 | `TelegramEventConverter` — 支持 Message/Edit/Delete/Reaction/MemberJoin 等所有事件 | | `message_converter.py` | `MessageChain` 与 Telegram 消息格式互转 | `TelegramMessageConverter.yiri2target()` / `target2yiri()` | | `api_impl.py` | 实现通用 API 方法(edit_message, delete_message, get_group_info 等) | 各 API 方法的 Telegram 实现 | | `platform_api.py` | 实现 `call_platform_api` 的动作分发表 | `PLATFORM_API_MAP = {"pin_message": ..., "unpin_message": ...}` | | `types.py` | 平台特有的类型定义 | Telegram 特有的枚举、配置结构等 | | `manifest.yaml` | 适配器清单:名称、配置 schema、支持的事件和 API 列表 | — | ## 3. 新基类设计 ### 3.1 AbstractPlatformAdapter 新基类继承自现有 `AbstractMessagePlatformAdapter` 并扩展,位于 `langbot-plugin-sdk` 中: ```python # langbot_plugin/api/definition/abstract/platform/adapter.py class AbstractPlatformAdapter(pydantic.BaseModel, metaclass=abc.ABCMeta): """平台适配器基类(EBA 版本) 相比旧版 AbstractMessagePlatformAdapter: - 新增通用 API 方法(edit_message, delete_message, get_group_info 等) - 新增透传 API(call_platform_api) - 新增能力声明(get_supported_events, get_supported_apis) - 事件监听器支持所有事件类型,不仅限于消息事件 """ bot_account_id: str = "" config: dict logger: AbstractEventLogger = pydantic.Field(exclude=True) class Config: arbitrary_types_allowed = True # ---- 能力声明 ---- def get_supported_events(self) -> list[str]: """返回此适配器支持的事件类型列表 默认实现从 manifest.yaml 读取。 适配器也可以 override 此方法动态声明。 """ return ["message.received"] def get_supported_apis(self) -> list[str]: """返回此适配器支持的 API 列表 默认实现从 manifest.yaml 读取。 """ return ["send_message", "reply_message"] # ---- 必需方法(抽象) ---- @abc.abstractmethod async def send_message(self, target_type, target_id, message) -> MessageResult: ... @abc.abstractmethod async def reply_message(self, event, message, quote_origin=False) -> MessageResult: ... @abc.abstractmethod async def run_async(self): ... @abc.abstractmethod async def kill(self) -> bool: ... @abc.abstractmethod def register_listener(self, event_type, callback): ... @abc.abstractmethod def unregister_listener(self, event_type, callback): ... # ---- 可选方法(默认抛 NotSupportedError) ---- # edit_message, delete_message, forward_message, # get_group_info, get_group_member_list, ... # call_platform_api, ... # (完整签名见 02-platform-api.md) # ---- 流式输出(保留) ---- async def reply_message_chunk(self, event, bot_message, message, quote_origin=False, is_final=False): raise NotSupportedError("reply_message_chunk") async def is_stream_output_supported(self) -> bool: return False # ---- 消息卡片(保留) ---- async def create_message_card(self, message_id, event) -> bool: return False async def is_muted(self, group_id) -> bool: return False ``` ### 3.2 AbstractMessagePlatformAdapter 兼容 旧的 `AbstractMessagePlatformAdapter` 保留为 `AbstractPlatformAdapter` 的类型别名: ```python # 向后兼容 AbstractMessagePlatformAdapter = AbstractPlatformAdapter ``` 现有适配器代码中的 `AbstractMessagePlatformAdapter` 引用不需要立即修改。 ### 3.3 EventConverter 新设计 现有 `AbstractEventConverter` 只有 `target2yiri` 和 `yiri2target` 两个静态方法,且只处理消息事件。 新设计支持多种事件类型: ```python class AbstractEventConverter: """事件转换器基类(EBA 版本) 适配器需要实现此转换器,将平台原生事件转换为统一事件。 """ @staticmethod def target2yiri(raw_event: typing.Any) -> typing.Optional[Event]: """将平台原生事件转换为统一事件 Args: raw_event: 平台 SDK 回调传入的原始事件对象 Returns: 统一 Event 对象,如果无法转换或不需要处理则返回 None """ raise NotImplementedError @staticmethod def yiri2target(event: Event) -> typing.Any: """将统一事件转换为平台原生事件(一般不需要)""" raise NotImplementedError ``` 具体适配器的 EventConverter 实现会是一个分发式的结构: ```python class TelegramEventConverter(AbstractEventConverter): """Telegram 事件转换器""" @staticmethod def target2yiri(update: telegram.Update) -> typing.Optional[Event]: # 消息事件 if update.message: return TelegramEventConverter._convert_message(update) # 消息编辑 if update.edited_message: return TelegramEventConverter._convert_edited_message(update) # 成员变动 if update.chat_member: return TelegramEventConverter._convert_chat_member(update) # 回调查询(按钮点击等) if update.callback_query: return TelegramEventConverter._convert_callback_query(update) # 其他 → PlatformSpecificEvent return TelegramEventConverter._convert_platform_specific(update) @staticmethod def _convert_message(update) -> MessageReceivedEvent: ... @staticmethod def _convert_edited_message(update) -> MessageEditedEvent: ... @staticmethod def _convert_chat_member(update) -> typing.Union[ MemberJoinedEvent, MemberLeftEvent, ... ]: ... @staticmethod def _convert_platform_specific(update) -> PlatformSpecificEvent: ... ``` ## 4. Manifest 文件格式扩展 现有 manifest.yaml 只声明 `kind`, `metadata`, `spec.config`, `execution`。 新增 `spec.supported_events` 和 `spec.supported_apis`: ```yaml apiVersion: v1 kind: MessagePlatformAdapter metadata: name: telegram label: en_US: Telegram zh_Hans: Telegram icon: telegram.svg description: en_US: Telegram Bot adapter zh_Hans: Telegram Bot 适配器 spec: config: # 现有配置 schema(保持不变) - key: token label: { en_US: "Bot Token", zh_Hans: "Bot Token" } type: string required: true sensitive: true # ... supported_events: - message.received - message.edited - message.deleted - message.reaction - feedback.received - group.member_joined - group.member_left - group.member_banned - group.info_updated - bot.invited_to_group - bot.removed_from_group - bot.muted - bot.unmuted - platform.specific supported_apis: required: - send_message - reply_message optional: - edit_message - delete_message - get_group_info - get_group_member_list - get_group_member_info - get_user_info - upload_file - get_file_url - call_platform_api platform_specific_apis: - action: pin_message description: { en_US: "Pin a message", zh_Hans: "置顶消息" } - action: unpin_message description: { en_US: "Unpin a message", zh_Hans: "取消置顶" } - action: get_chat_administrators description: { en_US: "Get chat admins", zh_Hans: "获取群管理员列表" } execution: python: path: pkg/platform/adapters/telegram/adapter.py attr: TelegramAdapter ``` ## 5. 适配器注册与发现 ### 5.1 Blueprint 更新 `templates/components.yaml` 中更新扫描路径: ```yaml kind: Blueprint spec: components: MessagePlatformAdapter: fromDirs: - path: pkg/platform/adapters/ # 新路径 ``` `ComponentDiscoveryEngine` 的递归扫描逻辑不变——它会扫描所有子目录中的 `.yaml` 文件。因此每个适配器目录下的 `manifest.yaml` 会被自动发现。 ### 5.2 PlatformManager 适配 `PlatformManager.initialize()` 的核心逻辑基本不变: ```python async def initialize(self): # 1. 发现适配器组件(自动扫描新目录结构) self.adapter_components = self.ap.discover.get_components_by_kind('MessagePlatformAdapter') # 2. 动态导入适配器类 for component in self.adapter_components: self.adapter_dict[component.metadata.name] = component.get_python_component_class() # 3. 从数据库加载 Bot 并实例化适配器(不变) await self.load_bots_from_db() ``` 变更点: - `execution.python.path` 从 `pkg/platform/sources/telegram.py` 变为 `pkg/platform/adapters/telegram/adapter.py` - `get_python_component_class()` 正常工作,因为它按路径动态导入 ## 6. RuntimeBot 重构 ### 6.1 现有问题 当前 `RuntimeBot.initialize()` 硬编码注册了两个回调: ```python # 现有代码 self.adapter.register_listener(platform_events.FriendMessage, on_friend_message) self.adapter.register_listener(platform_events.GroupMessage, on_group_message) ``` ### 6.2 新设计 `RuntimeBot` 改为注册一个通用的事件回调: ```python class RuntimeBot: async def initialize(self): # 注册通用事件回调,接收所有事件类型 self.adapter.register_listener(Event, self._on_event) async def _on_event( self, event: Event, adapter: AbstractPlatformAdapter, ): """统一事件入口""" # 1. 设置事件的 bot_uuid 和 adapter_name event.bot_uuid = self.bot_entity.uuid event.adapter_name = self.bot_entity.adapter # 2. 日志记录 await self._log_event(event) # 3. 提交给 EventBus await self.ap.event_bus.emit(event, adapter) ``` 适配器侧的 `register_listener` 实现也需调整: - 当 `event_type` 为 `Event`(基类)时,注册为"接收所有事件"的通配回调 - 适配器在收到平台原生事件时,通过 `EventConverter.target2yiri()` 转换后,调用所有匹配的回调 ## 7. 从现有单文件适配器迁移 ### 7.1 迁移模式 以 Telegram 为例,从 `sources/telegram.py`(445 行)拆分: | 原代码位置 | → 新文件 | |-----------|----------| | `TelegramMessageConverter` 类 | `telegram/message_converter.py` | | `TelegramEventConverter` 类 | `telegram/event_converter.py`(扩展,支持更多事件) | | `TelegramAdapter.__init__` / `run_async` / `kill` / `register_listener` | `telegram/adapter.py` | | `TelegramAdapter.send_message` / `reply_message` / `reply_message_chunk` | `telegram/adapter.py`(消息方法保留在主类)+ `telegram/api_impl.py`(新增 API) | | 新增代码 | `telegram/api_impl.py`(edit_message, delete_message, get_group_info 等) | | 新增代码 | `telegram/platform_api.py`(pin_message, unpin_message 等的映射) | | `telegram.yaml` | `telegram/manifest.yaml`(扩展 supported_events/apis) | ### 7.2 迁移顺序建议 1. **Telegram** — 功能最完整的适配器之一,适合作为模板 2. **Discord** — 第二个迁移,验证模式的通用性 3. **AioCQHTTP (OneBot)** — 国内最常用,确保兼容 4. **其他适配器** — 按使用频率排序 ### 7.3 渐进式迁移 不需要一次性迁移所有适配器。可以采用渐进策略: 1. 先在 `adapters/` 下建立新适配器 2. `Blueprint` 同时扫描 `sources/` 和 `adapters/` 两个目录 3. 旧适配器在 `sources/` 中继续工作 4. 逐个迁移到新结构 5. 全部迁移完成后移除 `sources/` 目录 ```yaml # 过渡期的 Blueprint kind: Blueprint spec: components: MessagePlatformAdapter: fromDirs: - path: pkg/platform/sources/ # 旧路径(尚未迁移的适配器) - path: pkg/platform/adapters/ # 新路径(已迁移的适配器) ```