WechatOnCloud/bridge/woc_bridge/ui/capabilities.py
Kris 15fc62d478 feat: 完成微信UI自动化新架构全量开发与集成
- 新增六层UI自动化架构:从Backend到Capabilities的完整分层实现
- 添加WeChat 4.0分辨率适配Profile与图像模板资源
- 实现幂等缓存、熔断器、重试策略、链路追踪与监控指标
- 新增头像下载安全校验、发布朋友圈路径白名单防护
- 优化密钥缓存、DB校验逻辑与初始化流程
- 补充完整错误码体系与启动清场机制
2026-07-16 01:19:47 +08:00

127 lines
4.4 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.

"""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 阶段默认 TrueP3 接入真实校验)
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 为 Nonesend_text 委托给
旧 xdotool_driver.send_text包成 SendResult。
P3 阶段orchestrator 非空、xdotool_driver 为 Nonesend_text 委托给
FlowOrchestrator.send_textTODO: 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,
)