"""钉钉渠道插件入口。 实现 ``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_id:CHANNEL_ENTRY 在插件加载时不知具体账户, # WizardAdapter 传入空字符串占位。WhitelistAdapter 由协议方法传入 account_id, # 无需构造时绑定。 _PLACEHOLDER_ACCOUNT_ID = "" # 已注册适配器类型列表(与 registerAdapter 调用顺序一致)。 # 此处为运行时适配器清单,非声明性字段,需与下方 registerAdapter 调用保持一致。 # 不注册 login_adapter(supports_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 取自 manifest(F-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. 返回 PluginManifest(manifest 直接复用入参,adapters 为运行时清单) return PluginManifest( manifest=manifest, adapters=_ADAPTER_TYPES, ) CHANNEL_ENTRY = channel_entry