ForcePilot/backend/package/yuxi/channels/plugins/dingtalk/entry.py

203 lines
8.5 KiB
Python
Raw Normal View History

"""钉钉渠道插件入口。
实现 ``CHANNEL_ENTRY`` 契约注册 17 个适配器与生命周期钩子返回
``PluginManifest``由宿主在 loaded 阶段通过 ``importlib`` 导入并调用
调用顺序严格
1. ``getXxxPort`` 获取被驱动端口实例宿主已从 manifest.accessible_ports
加载权限无需运行时 declareXxx
2. 实例化 ``DingTalkLifecycleHandler`` ``DingTalkClient``
3. 实例化并注册 17 个适配器16 个列表追加 + 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`` 调用
per-account 适配器处理``WizardAdapter`` ``__init__``
``account_id`` ``CHANNEL_ENTRY`` 在插件加载时不知具体账户传入空字符串
占位``_PLACEHOLDER_ACCOUNT_ID````WhitelistAdapter`` 由协议方法传入
``account_id``无需构造时绑定
Stream WS 长连接管理钉钉 Stream 连接由 ``StreamWorker`` 通过
``DingTalkStreamConnectorAdapter`` 统一管理账号启用/禁用由
``ChannelAccountOnline``/``ChannelAccountOffline`` 领域事件触发
``LifecycleAdapter`` ``LifecycleHandler`` 不自管理 WS 连接设计方案 §2.4.4
依赖方向 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.command_adapter import DingTalkCommandAdapter
from .adapters.directory_adapter import DingTalkDirectoryAdapter
from .adapters.doctor_adapter import DingTalkDoctorAdapter
from .adapters.identity_resolver_adapter import DingTalkIdentityResolverAdapter
from .adapters.inbound_adapter import DingTalkInboundAdapter
from .adapters.lifecycle_adapter import DingTalkLifecycleAdapter
from .adapters.mention_adapter import DingTalkMentionAdapter
from .adapters.message_ops_adapter import DingTalkMessageOpsAdapter
from .adapters.outbound_adapter import DingTalkOutboundAdapter
from .adapters.probeable_adapter import DingTalkProbeableAdapter
from .adapters.rich_message_adapter import DingTalkRichMessageAdapter
from .adapters.session_adapter import DingTalkSessionAdapter
from .adapters.status_adapter import DingTalkStatusAdapter
from .adapters.stream_connector_adapter import DingTalkStreamConnectorAdapter
from .adapters.streaming_adapter import DingTalkStreamingAdapter
from .adapters.whitelist_adapter import DingTalkWhitelistAdapter
from .adapters.wizard_adapter import DingTalkWizardAdapter
from .dingtalk_client import DingTalkClient
from .lifecycle import DingTalkLifecycleHandler
# per-account 适配器占位 account_idCHANNEL_ENTRY 在插件加载时不知具体账户,
# WizardAdapter 传入空字符串占位。WhitelistAdapter 由协议方法传入 account_id
# 无需构造时绑定。
_PLACEHOLDER_ACCOUNT_ID = ""
# 已注册适配器类型列表(与 registerAdapter 调用顺序一致)。
# 此处为运行时适配器清单,非声明性字段,需与下方 registerAdapter 调用保持一致。
# 不注册 login_adaptersupports_qr_login=false设计方案 §2.5.3)。
_ADAPTER_TYPES: tuple[str, ...] = (
"inbound",
"outbound",
"session",
"status",
"rich_message",
"streaming",
"mention",
"message_ops",
"directory",
"command",
"whitelist",
"wizard",
"doctor",
"lifecycle",
"probeable",
"identity_resolver",
"stream_connector",
)
def channel_entry(host: PluginHost, manifest: ChannelManifest) -> PluginManifest:
"""钉钉渠道插件入口。
由宿主在 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. 实例化 DingTalkClient 与 LifecycleHandler
# 依赖关系为单向handler 在 onInit 调 client.attach_http_client 注入
# 连接池client 不引用 handler。先创建 client再创建 handler 并通过
# 构造器注入。config_schema 取自 manifestF-03 单真相源)。
client = DingTalkClient(config_port, cache_port, logger_port)
handler = DingTalkLifecycleHandler(
config_port,
logger_port,
cache_port,
manifest.config_schema,
client,
)
# 3. 实例化并注册 17 个适配器
# 16 个列表追加 + 1 个 identity_resolver 单实例赋值
host.registerAdapter(
"inbound",
DingTalkInboundAdapter(client, config_port, logger_port),
)
host.registerAdapter(
"outbound",
DingTalkOutboundAdapter(client, logger_port, cache_port, config_port),
)
host.registerAdapter("session", DingTalkSessionAdapter())
host.registerAdapter("status", DingTalkStatusAdapter(logger_port))
host.registerAdapter("rich_message", DingTalkRichMessageAdapter(logger_port))
host.registerAdapter(
"streaming",
DingTalkStreamingAdapter(client, logger_port, cache_port),
)
host.registerAdapter("mention", DingTalkMentionAdapter(config_port))
host.registerAdapter(
"message_ops",
DingTalkMessageOpsAdapter(client, logger_port, cache_port, config_port),
)
host.registerAdapter(
"directory",
DingTalkDirectoryAdapter(client, cache_port, config_port),
)
host.registerAdapter("command", DingTalkCommandAdapter(config_port))
host.registerAdapter(
"whitelist",
DingTalkWhitelistAdapter(
persistence_port,
logger_port,
),
)
host.registerAdapter(
"wizard",
DingTalkWizardAdapter(config_port, client, _PLACEHOLDER_ACCOUNT_ID),
)
host.registerAdapter("doctor", DingTalkDoctorAdapter(client, config_port))
host.registerAdapter(
"lifecycle",
DingTalkLifecycleAdapter(client, config_port, logger_port),
)
host.registerAdapter(
"probeable",
DingTalkProbeableAdapter(client, config_port, persistence_port),
)
# identity_resolver 为单实例注册(非列表追加)
host.registerAdapter(
"identity_resolver",
DingTalkIdentityResolverAdapter(client, cache_port, config_port),
)
# stream_connector钉钉 Stream WS 长连接适配器,由 StreamWorker 管理连接生命周期。
# 需 ConfigPort 读取 stream_endpoint_path / api_base_url均声明于 manifest
# config_schema支持按部署环境定制 Stream 端点 API 路径。
host.registerAdapter(
"stream_connector",
DingTalkStreamConnectorAdapter(persistence_port, logger_port, config_port),
)
# 4. 注册生命周期钩子
# PluginHost Protocol 未声明 registerLifecycleHandler但 PluginHostImpl
# 已实现,运行时通过鸭子类型调用。
host.registerLifecycleHandler(handler)
# 5. 返回 PluginManifestmanifest 直接复用入参adapters 为运行时清单)
return PluginManifest(
manifest=manifest,
adapters=_ADAPTER_TYPES,
)
CHANNEL_ENTRY = channel_entry