feat(wecom): 完善客服消息适配逻辑
1. 新增客服open_kfid缓存常量与key生成工具 2. 重构入站适配器缓存客服映射关系 3. 重写出站适配器使用缓存判断客服场景 4. 修复多选卡片构建逻辑与日志方法调用 5. 调整wechat_woc常量导入顺序 6. 优化命令适配器配置异常处理逻辑
This commit is contained in:
parent
feee2e1166
commit
eda29502ed
@ -63,6 +63,7 @@ from yuxi.channels.contract.plugin.manifest import (
|
|||||||
PluginManifest,
|
PluginManifest,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from ._constants import DEFAULT_MAX_CONNECTIONS
|
||||||
from .adapters.capability_prover import WocCapabilityProver
|
from .adapters.capability_prover import WocCapabilityProver
|
||||||
from .adapters.directory_adapter import WeChatWocDirectoryAdapter
|
from .adapters.directory_adapter import WeChatWocDirectoryAdapter
|
||||||
from .adapters.doctor_adapter import WeChatWocDoctorAdapter
|
from .adapters.doctor_adapter import WeChatWocDoctorAdapter
|
||||||
@ -77,7 +78,6 @@ from .adapters.status_adapter import WeChatWocStatusAdapter
|
|||||||
from .adapters.stream_connector_adapter import WeChatWocStreamConnectorAdapter
|
from .adapters.stream_connector_adapter import WeChatWocStreamConnectorAdapter
|
||||||
from .adapters.whitelist_adapter import WeChatWocWhitelistAdapter
|
from .adapters.whitelist_adapter import WeChatWocWhitelistAdapter
|
||||||
from .adapters.wizard_adapter import WeChatWocWizardAdapter
|
from .adapters.wizard_adapter import WeChatWocWizardAdapter
|
||||||
from ._constants import DEFAULT_MAX_CONNECTIONS
|
|
||||||
from .lifecycle import WeChatWocLifecycleHandler
|
from .lifecycle import WeChatWocLifecycleHandler
|
||||||
from .woc_bridge_client import WocBridgeClient
|
from .woc_bridge_client import WocBridgeClient
|
||||||
|
|
||||||
|
|||||||
@ -66,6 +66,10 @@ RESPONSE_CODE_CACHE_TTL_SEC = 72 * 3600
|
|||||||
# 消息反向映射缓存 TTL(秒),1 天
|
# 消息反向映射缓存 TTL(秒),1 天
|
||||||
MSG_CACHE_TTL_SEC = 86400
|
MSG_CACHE_TTL_SEC = 86400
|
||||||
|
|
||||||
|
# 客服 open_kfid 映射缓存 TTL(秒),1 天(对齐 MSG_CACHE_TTL_SEC,
|
||||||
|
# 覆盖客服会话生命周期,确保出站回复能反查到 open_kfid)
|
||||||
|
KF_OPEN_KFID_CACHE_TTL_SEC = 86400
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# 企业微信 API 常量
|
# 企业微信 API 常量
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -94,6 +98,7 @@ __all__ = [
|
|||||||
"USER_CACHE_TTL_SEC",
|
"USER_CACHE_TTL_SEC",
|
||||||
"RESPONSE_CODE_CACHE_TTL_SEC",
|
"RESPONSE_CODE_CACHE_TTL_SEC",
|
||||||
"MSG_CACHE_TTL_SEC",
|
"MSG_CACHE_TTL_SEC",
|
||||||
|
"KF_OPEN_KFID_CACHE_TTL_SEC",
|
||||||
"WEBHOOK_TIMEOUT_SEC",
|
"WEBHOOK_TIMEOUT_SEC",
|
||||||
"DUPLICATE_CHECK_INTERVAL_SEC",
|
"DUPLICATE_CHECK_INTERVAL_SEC",
|
||||||
"RESPONSE_CODE_VALID_HOURS",
|
"RESPONSE_CODE_VALID_HOURS",
|
||||||
|
|||||||
@ -27,6 +27,7 @@ from yuxi.channels.contract.dtos.config import ConfigScope
|
|||||||
from yuxi.channels.contract.errors import (
|
from yuxi.channels.contract.errors import (
|
||||||
DependencyError,
|
DependencyError,
|
||||||
Error,
|
Error,
|
||||||
|
NotFoundError,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
from yuxi.channels.contract.ports.driven.config_port import ConfigPort
|
from yuxi.channels.contract.ports.driven.config_port import ConfigPort
|
||||||
@ -69,10 +70,11 @@ class WecomCommandAdapter:
|
|||||||
"""获取渠道命令列表。
|
"""获取渠道命令列表。
|
||||||
|
|
||||||
从 ``ConfigPort`` 读取 CHANNEL 作用域 ``commands`` 配置,返回
|
从 ``ConfigPort`` 读取 CHANNEL 作用域 ``commands`` 配置,返回
|
||||||
``ChannelCommand`` 元组。配置缺失或为空时返回内置默认命令
|
``ChannelCommand`` 元组。配置缺失(``NotFoundError``)或为空时
|
||||||
(``/help`` / ``/status``)。
|
降级返回内置默认命令(``/help`` / ``/status``),确保命令功能
|
||||||
|
在配置异常时仍可用。
|
||||||
|
|
||||||
@failure: 配置加载失败抛 ``DependencyError``。
|
@failure: 配置系统故障(非 NotFoundError)抛 ``DependencyError``。
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
cv = await self._config.get(
|
cv = await self._config.get(
|
||||||
@ -80,6 +82,11 @@ class WecomCommandAdapter:
|
|||||||
scope=ConfigScope.CHANNEL,
|
scope=ConfigScope.CHANNEL,
|
||||||
target=CHANNEL_TARGET,
|
target=CHANNEL_TARGET,
|
||||||
)
|
)
|
||||||
|
except NotFoundError:
|
||||||
|
# 配置未设置,降级返回内置默认命令
|
||||||
|
return _DEFAULT_COMMANDS
|
||||||
|
except Error:
|
||||||
|
raise
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise DependencyError(dep="wecom_config", cause=exc) from exc
|
raise DependencyError(dep="wecom_config", cause=exc) from exc
|
||||||
|
|
||||||
|
|||||||
@ -30,11 +30,12 @@ from yuxi.channels.contract.dtos.common import (
|
|||||||
from yuxi.channels.contract.dtos.config import ConfigScope
|
from yuxi.channels.contract.dtos.config import ConfigScope
|
||||||
from yuxi.channels.contract.dtos.inbound import SignatureVerifyResult
|
from yuxi.channels.contract.dtos.inbound import SignatureVerifyResult
|
||||||
from yuxi.channels.contract.errors import DependencyError, Error
|
from yuxi.channels.contract.errors import DependencyError, Error
|
||||||
|
from yuxi.channels.contract.ports.driven.cache_port import CachePort
|
||||||
from yuxi.channels.contract.ports.driven.config_port import ConfigPort
|
from yuxi.channels.contract.ports.driven.config_port import ConfigPort
|
||||||
from yuxi.channels.contract.ports.driven.logger_port import LoggerPort
|
from yuxi.channels.contract.ports.driven.logger_port import LoggerPort
|
||||||
|
|
||||||
from .. import signature
|
from .. import signature
|
||||||
from .._constants import WECOM_API_DEP
|
from .._constants import KF_OPEN_KFID_CACHE_TTL_SEC, WECOM_API_DEP
|
||||||
from ..wecom_client import WecomClient
|
from ..wecom_client import WecomClient
|
||||||
|
|
||||||
# 企业微信 MsgType → MessageFormat 映射
|
# 企业微信 MsgType → MessageFormat 映射
|
||||||
@ -46,6 +47,11 @@ _RESOURCE_SCHEME = "wecom"
|
|||||||
_RESOURCE_HOST = "resource"
|
_RESOURCE_HOST = "resource"
|
||||||
|
|
||||||
|
|
||||||
|
def _kf_open_kfid_key(account_id: str, peer_id: str) -> str:
|
||||||
|
"""构造 ``external_userid → open_kfid`` 反向映射的缓存 key。"""
|
||||||
|
return f"wecom:kf:{account_id}:{peer_id}"
|
||||||
|
|
||||||
|
|
||||||
class WecomInboundAdapter:
|
class WecomInboundAdapter:
|
||||||
"""企业微信入站适配器。
|
"""企业微信入站适配器。
|
||||||
|
|
||||||
@ -64,10 +70,12 @@ class WecomInboundAdapter:
|
|||||||
client: WecomClient,
|
client: WecomClient,
|
||||||
config_port: ConfigPort,
|
config_port: ConfigPort,
|
||||||
logger_port: LoggerPort,
|
logger_port: LoggerPort,
|
||||||
|
cache_port: CachePort,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._client = client
|
self._client = client
|
||||||
self._config = config_port
|
self._config = config_port
|
||||||
self._logger = logger_port
|
self._logger = logger_port
|
||||||
|
self._cache = cache_port
|
||||||
|
|
||||||
async def normalizeInbound(self, raw_event: RawEvent) -> MessageContent:
|
async def normalizeInbound(self, raw_event: RawEvent) -> MessageContent:
|
||||||
"""将企业微信原始事件规范化为统一消息内容。
|
"""将企业微信原始事件规范化为统一消息内容。
|
||||||
@ -99,6 +107,16 @@ class WecomInboundAdapter:
|
|||||||
account_id = raw_event.account_id or ""
|
account_id = raw_event.account_id or ""
|
||||||
text, fmt, attachments = _parse_message_content(event, account_id)
|
text, fmt, attachments = _parse_message_content(event, account_id)
|
||||||
|
|
||||||
|
# 客服场景:写入 external_userid → open_kfid 映射,供出站适配器反查
|
||||||
|
# open_kfid 以切换客服 API 域并填充消息体(B-4 修复)
|
||||||
|
open_kfid = event.get("OpenKfId", "") or event.get("open_kfid", "")
|
||||||
|
if open_kfid and from_user:
|
||||||
|
await self._cache.set(
|
||||||
|
_kf_open_kfid_key(account_id, from_user),
|
||||||
|
open_kfid,
|
||||||
|
ttl_seconds=KF_OPEN_KFID_CACHE_TTL_SEC,
|
||||||
|
)
|
||||||
|
|
||||||
# event_id 优先使用 MsgId,缺失时回退到 CreateTime + FromUserName
|
# event_id 优先使用 MsgId,缺失时回退到 CreateTime + FromUserName
|
||||||
event_id = msg_id or f"{create_time}_{from_user}"
|
event_id = msg_id or f"{create_time}_{from_user}"
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,11 @@ def _response_code_key(channel_msg_id: str) -> str:
|
|||||||
return f"wecom:response_code:{channel_msg_id}"
|
return f"wecom:response_code:{channel_msg_id}"
|
||||||
|
|
||||||
|
|
||||||
|
def _kf_open_kfid_key(account_id: str, peer_id: str) -> str:
|
||||||
|
"""构造 ``external_userid → open_kfid`` 反向映射的缓存 key。"""
|
||||||
|
return f"wecom:kf:{account_id}:{peer_id}"
|
||||||
|
|
||||||
|
|
||||||
# 附件类型 → 企业微信媒体类型映射
|
# 附件类型 → 企业微信媒体类型映射
|
||||||
_ATTACH_MEDIA_TYPE = {
|
_ATTACH_MEDIA_TYPE = {
|
||||||
"image": "image",
|
"image": "image",
|
||||||
@ -97,8 +102,9 @@ class WecomOutboundAdapter:
|
|||||||
) -> str:
|
) -> str:
|
||||||
"""发送消息,返回企业微信 ``msgid``。
|
"""发送消息,返回企业微信 ``msgid``。
|
||||||
|
|
||||||
依据设计 §2.1.2 / 决策 8,当 ``peer_id`` 为客服场景(``open_kfid``
|
依据设计 §2.1.2 / 决策 8,当 ``peer_id`` 存在客服场景映射
|
||||||
前缀)时切换至 ``kf_secret`` 并调用 ``/cgi-bin/kf/send_msg``;其余
|
(``external_userid → open_kfid``)时切换至 ``kf_secret`` 并调用
|
||||||
|
``/cgi-bin/kf/send_msg``,消息体补充 ``open_kfid`` 字段;其余
|
||||||
场景调用应用消息 ``/cgi-bin/message/send``。成功后缓存 ``msgid``
|
场景调用应用消息 ``/cgi-bin/message/send``。成功后缓存 ``msgid``
|
||||||
→ ``account_id`` 反向映射。若响应含 ``response_code``(模板卡片
|
→ ``account_id`` 反向映射。若响应含 ``response_code``(模板卡片
|
||||||
消息),同时缓存供 ``updateMessage`` 使用。
|
消息),同时缓存供 ``updateMessage`` 使用。
|
||||||
@ -109,8 +115,10 @@ class WecomOutboundAdapter:
|
|||||||
msg_body = _build_msg_body(payload)
|
msg_body = _build_msg_body(payload)
|
||||||
msg_body["touser"] = peer_id
|
msg_body["touser"] = peer_id
|
||||||
|
|
||||||
|
open_kfid = await self._resolve_open_kfid(account_id, peer_id)
|
||||||
try:
|
try:
|
||||||
if _is_kf_peer(peer_id):
|
if open_kfid:
|
||||||
|
msg_body["open_kfid"] = open_kfid
|
||||||
resp = await self._client.send_kf_message(account_id, msg_body)
|
resp = await self._client.send_kf_message(account_id, msg_body)
|
||||||
else:
|
else:
|
||||||
resp = await self._client.send_message(account_id, msg_body)
|
resp = await self._client.send_message(account_id, msg_body)
|
||||||
@ -185,7 +193,7 @@ class WecomOutboundAdapter:
|
|||||||
part_type, msg_body = first_part
|
part_type, msg_body = first_part
|
||||||
msg_body["touser"] = peer_id
|
msg_body["touser"] = peer_id
|
||||||
try:
|
try:
|
||||||
msg_id = await self._send_msg(account_id, msg_body, sent_ids)
|
msg_id = await self._send_msg(account_id, msg_body, sent_ids, peer_id=peer_id)
|
||||||
except Error:
|
except Error:
|
||||||
raise
|
raise
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@ -352,6 +360,18 @@ class WecomOutboundAdapter:
|
|||||||
raise NotFoundError(resource="response_code", id=channel_msg_id)
|
raise NotFoundError(resource="response_code", id=channel_msg_id)
|
||||||
return opt.unwrap()
|
return opt.unwrap()
|
||||||
|
|
||||||
|
async def _resolve_open_kfid(self, account_id: str, peer_id: str) -> str:
|
||||||
|
"""从缓存反查 ``peer_id`` 对应的 ``open_kfid``。
|
||||||
|
|
||||||
|
客服场景下入站适配器写入 ``external_userid → open_kfid`` 映射,
|
||||||
|
出站时据此判断是否走客服 API 并填充消息体 ``open_kfid`` 字段。
|
||||||
|
无映射时返回空字符串(非客服场景)。
|
||||||
|
"""
|
||||||
|
opt = await self._cache.get(_kf_open_kfid_key(account_id, peer_id))
|
||||||
|
if opt.is_nothing():
|
||||||
|
return ""
|
||||||
|
return opt.unwrap()
|
||||||
|
|
||||||
async def _send_msg(
|
async def _send_msg(
|
||||||
self,
|
self,
|
||||||
account_id: str,
|
account_id: str,
|
||||||
@ -362,10 +382,15 @@ class WecomOutboundAdapter:
|
|||||||
) -> str:
|
) -> str:
|
||||||
"""发送单条消息并记录到 ``sent_ids``(供回滚使用)。
|
"""发送单条消息并记录到 ``sent_ids``(供回滚使用)。
|
||||||
|
|
||||||
当 ``peer_id`` 为客服场景时切换至 ``kf_secret`` API 域。
|
当 ``peer_id`` 存在客服场景映射时切换至 ``kf_secret`` API 域,
|
||||||
|
并在消息体中补充 ``open_kfid`` 字段。
|
||||||
"""
|
"""
|
||||||
|
open_kfid = ""
|
||||||
|
if peer_id:
|
||||||
|
open_kfid = await self._resolve_open_kfid(account_id, peer_id)
|
||||||
try:
|
try:
|
||||||
if peer_id and _is_kf_peer(peer_id):
|
if open_kfid:
|
||||||
|
msg_body["open_kfid"] = open_kfid
|
||||||
resp = await self._client.send_kf_message(account_id, msg_body)
|
resp = await self._client.send_kf_message(account_id, msg_body)
|
||||||
else:
|
else:
|
||||||
resp = await self._client.send_message(account_id, msg_body)
|
resp = await self._client.send_message(account_id, msg_body)
|
||||||
@ -430,7 +455,7 @@ class WecomOutboundAdapter:
|
|||||||
|
|
||||||
msg_body = _build_attachment_msg_body(attachment.type, media_id)
|
msg_body = _build_attachment_msg_body(attachment.type, media_id)
|
||||||
msg_body["touser"] = peer_id
|
msg_body["touser"] = peer_id
|
||||||
return await self._send_msg(account_id, msg_body, sent_ids)
|
return await self._send_msg(account_id, msg_body, sent_ids, peer_id=peer_id)
|
||||||
|
|
||||||
async def _rollback(self, account_id: str, sent_ids: list[str]) -> None:
|
async def _rollback(self, account_id: str, sent_ids: list[str]) -> None:
|
||||||
"""撤回已发送消息(best-effort,失败记录日志不抛异常)。"""
|
"""撤回已发送消息(best-effort,失败记录日志不抛异常)。"""
|
||||||
@ -523,15 +548,6 @@ def _build_card_data(payload: FormattedMessage) -> dict[str, Any]:
|
|||||||
return dict(card["template_card"])
|
return dict(card["template_card"])
|
||||||
|
|
||||||
|
|
||||||
def _is_kf_peer(peer_id: str) -> bool:
|
|
||||||
"""判断 peer_id 是否为客服场景(open_kfid 前缀)。
|
|
||||||
|
|
||||||
企业微信客服账号 ID 以 ``kfid_`` / ``kf_`` 前缀开头(见企业微信开发
|
|
||||||
文档 "客服账号 ID" 说明),据此切换 ``kf_secret`` API 域。
|
|
||||||
"""
|
|
||||||
return peer_id.startswith(("kfid_", "kf_"))
|
|
||||||
|
|
||||||
|
|
||||||
def _build_attachment_msg_body(
|
def _build_attachment_msg_body(
|
||||||
attach_type: str,
|
attach_type: str,
|
||||||
media_id: str,
|
media_id: str,
|
||||||
|
|||||||
@ -60,6 +60,11 @@ def _card_key(card_id: str) -> str:
|
|||||||
return f"wecom:card:{card_id}"
|
return f"wecom:card:{card_id}"
|
||||||
|
|
||||||
|
|
||||||
|
def _kf_open_kfid_key(account_id: str, peer_id: str) -> str:
|
||||||
|
"""构造 ``external_userid → open_kfid`` 反向映射的缓存 key。"""
|
||||||
|
return f"wecom:kf:{account_id}:{peer_id}"
|
||||||
|
|
||||||
|
|
||||||
class WecomStreamingAdapter:
|
class WecomStreamingAdapter:
|
||||||
"""企业微信流式适配器。
|
"""企业微信流式适配器。
|
||||||
|
|
||||||
@ -102,6 +107,11 @@ class WecomStreamingAdapter:
|
|||||||
msg_body: dict[str, Any] = {"touser": peer_id}
|
msg_body: dict[str, Any] = {"touser": peer_id}
|
||||||
msg_body.update(card)
|
msg_body.update(card)
|
||||||
try:
|
try:
|
||||||
|
open_kfid = await self._resolve_open_kfid(account_id, peer_id)
|
||||||
|
if open_kfid:
|
||||||
|
msg_body["open_kfid"] = open_kfid
|
||||||
|
resp = await self._client.send_kf_message(account_id, msg_body)
|
||||||
|
else:
|
||||||
resp = await self._client.send_message(account_id, msg_body)
|
resp = await self._client.send_message(account_id, msg_body)
|
||||||
except Error:
|
except Error:
|
||||||
# 契约异常(DependencyError/TimeoutError 等)向上透传,由调用方
|
# 契约异常(DependencyError/TimeoutError 等)向上透传,由调用方
|
||||||
@ -269,5 +279,16 @@ class WecomStreamingAdapter:
|
|||||||
raise NotFoundError(resource="streaming_card", id=card_id)
|
raise NotFoundError(resource="streaming_card", id=card_id)
|
||||||
return opt.unwrap()
|
return opt.unwrap()
|
||||||
|
|
||||||
|
async def _resolve_open_kfid(self, account_id: str, peer_id: str) -> str:
|
||||||
|
"""从缓存反查 ``peer_id`` 对应的 ``open_kfid``。
|
||||||
|
|
||||||
|
客服场景下入站适配器写入 ``external_userid → open_kfid`` 映射,
|
||||||
|
流式出站时据此判断是否走客服 API。无映射时返回空字符串。
|
||||||
|
"""
|
||||||
|
opt = await self._cache.get(_kf_open_kfid_key(account_id, peer_id))
|
||||||
|
if opt.is_nothing():
|
||||||
|
return ""
|
||||||
|
return opt.unwrap()
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["WecomStreamingAdapter"]
|
__all__ = ["WecomStreamingAdapter"]
|
||||||
|
|||||||
@ -286,7 +286,7 @@ class WecomWhitelistAdapter:
|
|||||||
entries: list[WhitelistEntry] = []
|
entries: list[WhitelistEntry] = []
|
||||||
for raw in raw_list:
|
for raw in raw_list:
|
||||||
if not isinstance(raw, dict):
|
if not isinstance(raw, dict):
|
||||||
await self._logger.warning(
|
await self._logger.warn(
|
||||||
"wecom whitelist entry skipped: not a dict",
|
"wecom whitelist entry skipped: not a dict",
|
||||||
account_id=account_id,
|
account_id=account_id,
|
||||||
policy_type=policy_type.value,
|
policy_type=policy_type.value,
|
||||||
@ -297,7 +297,7 @@ class WecomWhitelistAdapter:
|
|||||||
entries.append(whitelistEntryFromDict(raw))
|
entries.append(whitelistEntryFromDict(raw))
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
# 跳过历史脏数据,避免单条非法条目阻断整批读取
|
# 跳过历史脏数据,避免单条非法条目阻断整批读取
|
||||||
await self._logger.warning(
|
await self._logger.warn(
|
||||||
"wecom whitelist entry skipped: invalid entry",
|
"wecom whitelist entry skipped: invalid entry",
|
||||||
account_id=account_id,
|
account_id=account_id,
|
||||||
policy_type=policy_type.value,
|
policy_type=policy_type.value,
|
||||||
|
|||||||
@ -171,9 +171,19 @@ def _build_vote_options(selects: tuple[Any, ...]) -> list[dict[str, Any]]:
|
|||||||
|
|
||||||
|
|
||||||
def _build_multiple_options(checkboxes: tuple[Any, ...]) -> list[dict[str, Any]]:
|
def _build_multiple_options(checkboxes: tuple[Any, ...]) -> list[dict[str, Any]]:
|
||||||
"""构建多项选择型 checkboxes。"""
|
"""构建多项选择型 checkboxes(嵌套结构)。
|
||||||
|
|
||||||
|
企业微信 multiple_interaction 卡片要求每个元素含 ``question_name`` 与
|
||||||
|
``option_list``(嵌套数组),而非扁平列表。每个 ``RichMessageCheckbox``
|
||||||
|
对应一个问题组,``cb.label`` 为问题名,``cb.options`` 为选项列表。
|
||||||
|
"""
|
||||||
result: list[dict[str, Any]] = []
|
result: list[dict[str, Any]] = []
|
||||||
for cb in checkboxes:
|
for cb in checkboxes:
|
||||||
for opt in cb.options:
|
option_list = [{"text": opt.label, "key": opt.value} for opt in cb.options]
|
||||||
result.append({"text": opt.label, "key": opt.value})
|
result.append(
|
||||||
|
{
|
||||||
|
"question_name": cb.label,
|
||||||
|
"option_list": option_list,
|
||||||
|
}
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -133,7 +133,7 @@ def channel_entry(host: PluginHost, manifest: ChannelManifest) -> PluginManifest
|
|||||||
# 15 个列表追加 + 1 个 identity_resolver 单实例赋值
|
# 15 个列表追加 + 1 个 identity_resolver 单实例赋值
|
||||||
host.registerAdapter(
|
host.registerAdapter(
|
||||||
"inbound",
|
"inbound",
|
||||||
WecomInboundAdapter(client, config_port, logger_port),
|
WecomInboundAdapter(client, config_port, logger_port, cache_port),
|
||||||
)
|
)
|
||||||
host.registerAdapter(
|
host.registerAdapter(
|
||||||
"outbound",
|
"outbound",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user