127 lines
4.4 KiB
Python
127 lines
4.4 KiB
Python
|
|
"""L6 Capability API:对外能力接口(SendResult + WeChatCapabilities)。
|
|||
|
|
|
|||
|
|
P1 阶段:WeChatCapabilities.send_text 委托给旧 xdotool_driver.send_text,
|
|||
|
|
把返回的 local_send_id 包成 SendResult(success=True, verified=True)。
|
|||
|
|
P3 阶段:替换为 FlowOrchestrator.send_text,统一走状态机 + DB 校验 + 幂等。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
from dataclasses import dataclass, field
|
|||
|
|
from typing import Optional
|
|||
|
|
|
|||
|
|
logger = logging.getLogger("woc-bridge")
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class SendResult:
|
|||
|
|
"""发送文本结果。
|
|||
|
|
|
|||
|
|
所有字段带默认值,避免构造时必填字段顺序问题。
|
|||
|
|
diagnostics 用 field(default=None) + Optional 避免可变默认值陷阱。
|
|||
|
|
|
|||
|
|
Attributes:
|
|||
|
|
success: 是否成功(含校验通过)
|
|||
|
|
local_id: 本地生成的消息 ID(如 local_<unix>_<rand>)
|
|||
|
|
verified: 是否经 DB 校验确认(P1 阶段默认 True,P3 接入真实校验)
|
|||
|
|
skipped: 是否因幂等命中而跳过实际发送
|
|||
|
|
error: 错误描述(成功时为空)
|
|||
|
|
error_code: 错误码(如 WINDOW_NOT_FOUND / SEND_TIMEOUT)
|
|||
|
|
retryable: 是否可安全重试
|
|||
|
|
diagnostics: 诊断信息字典(trace_id / image_confidence / duration_ms 等)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
success: bool = False
|
|||
|
|
local_id: str = ""
|
|||
|
|
verified: bool = False
|
|||
|
|
skipped: bool = False
|
|||
|
|
error: str = ""
|
|||
|
|
error_code: str = ""
|
|||
|
|
retryable: bool = False
|
|||
|
|
diagnostics: Optional[dict] = field(default=None)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class WeChatCapabilities:
|
|||
|
|
"""L6 对外能力 API:业务层(路由 / outbox)通过此类调用 UI 自动化。
|
|||
|
|
|
|||
|
|
P1 阶段:xdotool_driver 非空、orchestrator 为 None,send_text 委托给
|
|||
|
|
旧 xdotool_driver.send_text,包成 SendResult。
|
|||
|
|
P3 阶段:orchestrator 非空、xdotool_driver 为 None,send_text 委托给
|
|||
|
|
FlowOrchestrator.send_text(TODO: P3 时统一走 orchestrator)。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self, xdotool_driver=None, orchestrator=None) -> None:
|
|||
|
|
"""初始化能力 API。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
xdotool_driver: 旧 XdotoolDriver 实例(P1 阶段非空)
|
|||
|
|
orchestrator: P3 阶段的 FlowOrchestrator 实例(P1 阶段为 None)
|
|||
|
|
"""
|
|||
|
|
self.xdotool_driver = xdotool_driver
|
|||
|
|
self.orchestrator = orchestrator
|
|||
|
|
|
|||
|
|
async def send_text(
|
|||
|
|
self,
|
|||
|
|
to_wxid: str,
|
|||
|
|
content: str,
|
|||
|
|
display_name: Optional[str] = None,
|
|||
|
|
client_request_id: str = "",
|
|||
|
|
) -> SendResult:
|
|||
|
|
"""发送文本消息。
|
|||
|
|
|
|||
|
|
P1 阶段:若 orchestrator 非空则委托 orchestrator.send_text;
|
|||
|
|
否则调 xdotool_driver.send_text,把返回的 local_send_id 包成
|
|||
|
|
SendResult(success=True, local_id=..., verified=True)。
|
|||
|
|
|
|||
|
|
TODO: P3 时统一走 orchestrator,移除 xdotool_driver 分支。
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
to_wxid: 目标 wxid
|
|||
|
|
content: 文本内容
|
|||
|
|
display_name: 用于搜索定位会话的显示名(备注/昵称/微信号)
|
|||
|
|
client_request_id: 幂等键维度(P1 阶段忽略,P3 接入 IdemCache)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
SendResult:发送结果
|
|||
|
|
"""
|
|||
|
|
# TODO: P3 时统一走 orchestrator
|
|||
|
|
if self.orchestrator is not None:
|
|||
|
|
return await self.orchestrator.send_text(
|
|||
|
|
to_wxid=to_wxid,
|
|||
|
|
content=content,
|
|||
|
|
display_name=display_name,
|
|||
|
|
client_request_id=client_request_id,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# P1 阶段:委托给旧 xdotool_driver
|
|||
|
|
if self.xdotool_driver is None:
|
|||
|
|
return SendResult(
|
|||
|
|
success=False,
|
|||
|
|
error="no backend available (xdotool_driver and orchestrator both None)",
|
|||
|
|
error_code="BRIDGE_NOT_INITIALIZED",
|
|||
|
|
retryable=False,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
local_send_id = await self.xdotool_driver.send_text(
|
|||
|
|
to_wxid=to_wxid,
|
|||
|
|
content=content,
|
|||
|
|
display_name=display_name,
|
|||
|
|
)
|
|||
|
|
return SendResult(
|
|||
|
|
success=True,
|
|||
|
|
local_id=str(local_send_id),
|
|||
|
|
verified=True,
|
|||
|
|
)
|
|||
|
|
except Exception as exc:
|
|||
|
|
logger.error(
|
|||
|
|
"[capability] send_text failed to=%s: %s", to_wxid, exc
|
|||
|
|
)
|
|||
|
|
return SendResult(
|
|||
|
|
success=False,
|
|||
|
|
error=str(exc),
|
|||
|
|
error_code="SEND_FAILED",
|
|||
|
|
retryable=True,
|
|||
|
|
)
|