ForcePilot/backend/package/yuxi/channels/plugins/qqbot/entry.py
Kris c888db4e1f refactor(qqbot): 重构并完善QQ Bot适配器功能
1. 优化HTTP客户端关闭异常捕获范围
2. 重构事件工具方法抽取公共模块
3. 新增频控缓存常量与身份缓存配置
4. 完善适配器依赖注入与配置读取
5. 实现被动消息频控与令牌刷新串行化
6. 修复会话、状态、目录等适配器逻辑
7. 优化探测适配器账户获取逻辑
8. 完善凭据轮换与生命周期钩子
9. 优化WebSocket连接处理与心跳机制
2026-07-09 04:19:12 +08:00

188 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""QQ Bot 渠道插件入口。
实现 ``CHANNEL_ENTRY`` 契约,注册 15 个适配器与生命周期钩子,返回
``PluginManifest``。由宿主在 loaded 阶段通过 ``importlib`` 导入并调用。
调用顺序(严格):
1. ``getXxxPort`` 获取被驱动端口实例(宿主已从 manifest.accessible_ports
加载权限,无需运行时 declareXxx
2. 实例化 ``QQBotClient`` 与 ``QQBotLifecycleHandler``。
3. 实例化并注册 15 个适配器14 个列表追加 + 1 个 identity_resolver 单实例)。
4. ``registerLifecycleHandler`` 注册生命周期钩子(鸭子类型调用)。
5. 返回 ``PluginManifest``。
F-03 单真相源:本入口仅承接 discover 阶段解析的 ``ChannelManifest``
不再重复构造声明性字段capabilities / config_schema / accessible_ports /
injectable_pipelines / resource_quota / env_vars / credential_strategy 等),
所有声明性字段以 ``manifest.json`` 为唯一真相源。本函数仅负责适配器实例化、
client 实例化、LifecycleHandler 构造与 ``registerAdapter`` 调用。
QQ Bot 不实现 ``streaming`` / ``command`` / ``login`` 适配器v1 不需要),
``WizardAdapter`` 构造时不绑定 ``account_id``(协议方法在调用时按账户读取
ConfigPort无需占位变量。
WebSocket 长连接管理QQ Bot WS 连接由 ``StreamWorker`` 通过
``QQBotStreamConnectorAdapter`` 统一管理(账号启用/禁用由
``ChannelAccountOnline``/``ChannelAccountOffline`` 领域事件触发),
``LifecycleAdapter`` 与 ``LifecycleHandler`` 不再自管理 WS 连接。
依赖方向:仅 import ``yuxi.channels.contract.*`` + 标准库 + 同插件内部模块,
不污染框架层。
"""
from __future__ import annotations
from yuxi.channels.contract.plugin.entry import PluginHost
from yuxi.channels.contract.plugin.manifest import (
ChannelManifest,
PluginManifest,
)
from .adapters.directory_adapter import QQBotDirectoryAdapter
from .adapters.doctor_adapter import QQBotDoctorAdapter
from .adapters.identity_resolver_adapter import QQBotIdentityResolverAdapter
from .adapters.inbound_adapter import QQBotInboundAdapter
from .adapters.lifecycle_adapter import QQBotLifecycleAdapter
from .adapters.mention_adapter import QQBotMentionAdapter
from .adapters.message_ops_adapter import QQBotMessageOpsAdapter
from .adapters.outbound_adapter import QQBotOutboundAdapter
from .adapters.probeable_adapter import QQBotProbeableAdapter
from .adapters.rich_message_adapter import QQBotRichMessageAdapter
from .adapters.session_adapter import QQBotSessionAdapter
from .adapters.status_adapter import QQBotStatusAdapter
from .adapters.stream_connector_adapter import QQBotStreamConnectorAdapter
from .adapters.whitelist_adapter import QQBotWhitelistAdapter
from .adapters.wizard_adapter import QQBotWizardAdapter
from .lifecycle import QQBotLifecycleHandler
from .qqbot_client import QQBotClient
# 已注册适配器类型列表(与 registerAdapter 调用顺序一致)。
# 此处为运行时适配器清单,非声明性字段,需与下方 registerAdapter 调用保持一致。
# QQ Bot v1 不实现 streaming / command / login 适配器。
_ADAPTER_TYPES: tuple[str, ...] = (
"inbound",
"outbound",
"session",
"status",
"rich_message",
"mention",
"message_ops",
"directory",
"whitelist",
"wizard",
"doctor",
"lifecycle",
"probeable",
"identity_resolver",
"stream_connector",
)
def channel_entry(host: PluginHost, manifest: ChannelManifest) -> PluginManifest:
"""QQ Bot 渠道插件入口。
由宿主在 loaded 阶段调用,完成适配器注册、生命周期钩子注册,
返回 ``PluginManifest``。
能力边界accessible_ports / injectable_pipelines / resource_quota /
config_schema / env_vars / credential_strategy 等)由 discover 阶段
解析的 ``manifest`` 静态字段承载,宿主在加载期通过
``PluginCapabilityChecker.check`` 校验,运行时通过
``PluginHostImpl._checkPortAccess`` 从 manifest 读取并强制约束。本函数
不重复构造声明性字段F-03 单真相源)。
参数:
host: 插件宿主,提供端口获取与扩展点注册 API。
manifest: 渠道清单,由 discover 阶段从 ``manifest.json`` 解析得到,
承载所有声明性字段。本函数复用其 ``config_schema`` 注入
``LifecycleHandler``,并直接作为返回 ``PluginManifest.manifest``。
"""
# 1. 获取端口实例(宿主已从 manifest.accessible_ports 加载权限)
config_port = host.getConfigPort()
logger_port = host.getLoggerPort()
cache_port = host.getCachePort()
persistence_port = host.getPersistencePort()
# 2. 实例化 QQBotClient 与 LifecycleHandler
# 依赖关系为单向handler 在 onInit 调 client.attach_http_client 注入
# 连接池client 不引用 handler。先创建 client再创建 handler 并通过
# 构造器注入。config_schema 取自 manifestF-03 单真相源)。
client = QQBotClient(config_port, cache_port, logger_port)
handler = QQBotLifecycleHandler(
config_port,
logger_port,
cache_port,
manifest.config_schema,
client,
)
# 3. 实例化并注册 15 个适配器
# 14 个列表追加 + 1 个 identity_resolver 单实例赋值
host.registerAdapter(
"inbound",
QQBotInboundAdapter(logger_port),
)
host.registerAdapter(
"outbound",
QQBotOutboundAdapter(client, logger_port, cache_port, config_port),
)
host.registerAdapter("session", QQBotSessionAdapter())
host.registerAdapter("status", QQBotStatusAdapter())
host.registerAdapter("rich_message", QQBotRichMessageAdapter(logger_port))
host.registerAdapter("mention", QQBotMentionAdapter())
host.registerAdapter(
"message_ops",
QQBotMessageOpsAdapter(client, logger_port, cache_port),
)
host.registerAdapter(
"directory",
QQBotDirectoryAdapter(client, cache_port, config_port),
)
host.registerAdapter(
"whitelist",
QQBotWhitelistAdapter(cache_port, logger_port),
)
host.registerAdapter(
"wizard",
QQBotWizardAdapter(config_port, client, logger_port),
)
host.registerAdapter(
"doctor",
QQBotDoctorAdapter(client, config_port, logger_port),
)
host.registerAdapter(
"lifecycle",
QQBotLifecycleAdapter(client, config_port, logger_port),
)
host.registerAdapter(
"probeable",
QQBotProbeableAdapter(client, logger_port, persistence_port),
)
# identity_resolver 为单实例注册(非列表追加)
host.registerAdapter(
"identity_resolver",
QQBotIdentityResolverAdapter(client, cache_port, logger_port, config_port),
)
# stream_connectorQQ Bot WS 长连接适配器,由 StreamWorker 管理连接生命周期。
# 需 ConfigPort 读取 ws_heartbeat_interval_ms声明于 manifest config_schema
# 需 QQBotClient 动态获取 Gateway URL。
host.registerAdapter(
"stream_connector",
QQBotStreamConnectorAdapter(config_port, logger_port, client),
)
# 4. 注册生命周期钩子
# PluginHost Protocol 未声明 registerLifecycleHandler但 PluginHostImpl
# 已实现,运行时通过鸭子类型调用。
host.registerLifecycleHandler(handler)
# 5. 返回 PluginManifestmanifest 直接复用入参adapters 为运行时清单)
return PluginManifest(
manifest=manifest,
adapters=_ADAPTER_TYPES,
)
CHANNEL_ENTRY = channel_entry