feat: generalize session sending with multiline text and structured mentions
Add named-session file/image/reply sending, preserve multiline text, and share safe member/all mention handling. Protect existing drafts, bind popup selection to the current window, validate structured mention tokens, and require fresh matching visible messages without automatic resend. Core tests: 121 passed. Windows 4.1.13.63 final validation: 9/9 passed. Record explicit coverage limits and sanitized evidence.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# 通用发送增量验收 — 2026-09-06
|
||||
|
||||
## 环境与发布包
|
||||
|
||||
- Windows 真机:`10.1.1.101`,微信 `4.1.13.63`,现有已登录交互会话;未重启微信、重新登录或绕过锁屏。
|
||||
- 最终发布目录:`C:\Users\Rogee\wx-agent\releases\generic-send-final-20260906T090401`。
|
||||
- `WxAgent.Host.exe` SHA-256:`45E9994739D33E0786F3D224E9905DDF7439CCD322E6BAF21B431880706A0229`。
|
||||
- 仅操作文件传输助手和用户批准的“消息测试专用群组”;成员提及目标为批准的测试成员。各次发送使用不同标记,不对失败/未确认发送自动重试。
|
||||
|
||||
## 新增与修复
|
||||
|
||||
- `chat send-file`、`chat send-image`、`chat reply-latest` 支持 `--session`,默认仍为文件传输助手。
|
||||
- `chat send` 与 `chat reply-latest` 支持多行,包括中间空行;统一 CRLF/CR 为 LF,保留正文空行。单条最多 4000 字符,不自动拆分。
|
||||
- 新增 `chat mention --session <会话> --member <显示名称> --text <正文> --confirm CONFIRM`。`group at-all` 复用同一提及实现。
|
||||
- 提及定位限定于当前绑定窗口内新打开的 `MentionPopover → chat_mention_list → ListItem + 精确名称`,不再搜索整个桌面的同名元素。
|
||||
- 真机确认:选中提及对象后,输入框 Value 是结构化对象替代字符 `U+FFFC`,不是 `@姓名`。校验对象后粘贴正文,避免输入法转换,同时保留提及对象。
|
||||
- 已有草稿拒绝覆盖;输入、点击发送和结果确认检查会话。附件与引用回复不再把任意新消息当成成功:文件匹配精确文件名行,引用匹配类型与正文,提及匹配显示名称与正文,所有确认要求新指纹。
|
||||
- 剪贴板实现复用原 STA lease,新增文本粘贴;M5 附件调用仅跟随类型重命名,并未追加朋友圈操作验收。
|
||||
|
||||
## 验证结果
|
||||
|
||||
Linux:`dotnet test tests/WxAgent.Core.Tests -c Release --no-restore`,**121/121 通过**;完整解决方案 Release build 无警告、无错误。新增测试覆盖新旧指纹、错误类型/正文/文件名、结构化提及、Unicode 提及间隔、文本长度/控制字符及多行引用解析。
|
||||
|
||||
最终包执行 `Test-GeneralSending.ps1 -IncludeMentions`,**9/9 通过**:
|
||||
|
||||
| 检查 | 结果 |
|
||||
| --- | --- |
|
||||
| doctor | 输入桌面可用、微信窗口存在、无错误 |
|
||||
| inspect-ui | 脱敏 UI 树输出成功 |
|
||||
| smoke | 文件传输助手基线通过 |
|
||||
| send-multiline | 测试群发送含空行的三行正文,新消息确认 |
|
||||
| send-file | 测试群发送小文件,新文件名/类型确认 |
|
||||
| send-image | 测试群发送测试 PNG,新图片类型确认 |
|
||||
| reply-latest | 测试群引用最新消息并发送含空行正文,引用类型/正文确认 |
|
||||
| mention-member | 精确选择真实成员提及对象,正文新消息确认 |
|
||||
| mention-all | 精确选择所有人提及对象,正文新消息确认 |
|
||||
|
||||
## 失败诊断与草稿保护
|
||||
|
||||
中间候选并非通过记录:最初把弹窗误认为顶层窗口、随后把提及对象误认为文字,均在发送前停止。读取真实 UIA 路径与 Value 后修正;没有把这些失败算作发送成功。
|
||||
|
||||
- 不存在的成员:`ControlNotFound`,未调用发送。
|
||||
- 已有草稿:`InvalidOperationState`,拒绝覆盖。
|
||||
- 仅自动清理了精确匹配的测试草稿 `@` 和单个结构化提及对象。
|
||||
- 一次键入尝试留下非预期 4 字符草稿,具体转换原因未确认,自动清理拒绝;**用户明确授权后**仅清理该草稿,记录长度和授权,不记录内容。
|
||||
|
||||
脱敏证据:[`evidence/General-Sending-20260906/`](evidence/General-Sending-20260906/)。完整中间诊断留在本机忽略的 `artifacts/generic-send-current/`,不提交消息正文、完整 UI 树或密钥。
|
||||
|
||||
## 验收边界 / 尚未覆盖
|
||||
|
||||
- 可见消息确认不等于对端已收到;图片仅匹配新图片类型,不进行图片内容哈希核对。本次没有把对端收件、多账号消息归属或后台数据库关联算作通过。
|
||||
- 提及当前要求名称在弹窗可见列表中唯一;长列表滚动、不可见重名的完整枚举尚未覆盖,不猜选不可见成员。提及附加正文仍限单行。
|
||||
- 超过 4000 字符、自动分段、多附件批量发送、真正的 URL 卡片仍未在本增量实现或验收。
|
||||
- 导航补齐和合并聊天记录展开尚未由本提交实现;不能把本记录视为四项需求全部完成。
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"CurrentSessionId": 1,
|
||||
"InputDesktopAvailable": true,
|
||||
"WindowFound": true,
|
||||
"Errors": []
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Success": true,
|
||||
"Code": null,
|
||||
"Message": "visible message confirmed"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Success": true,
|
||||
"Code": null,
|
||||
"Message": "visible message confirmed"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"MissingMember": {
|
||||
"error": "ControlNotFound",
|
||||
"Message": "A unique mention option in a fresh WeChat popup was not found; no send was attempted."
|
||||
},
|
||||
"ExistingDraft": {
|
||||
"error": "InvalidOperationState",
|
||||
"Message": "The conversation contains an existing draft; it was not overwritten."
|
||||
},
|
||||
"ExactOwnedDraftCleanup": {
|
||||
"DraftGuardPassed": true,
|
||||
"Cleared": true,
|
||||
"OwnedDraftLength": 1
|
||||
},
|
||||
"UserApprovedUnexpectedDraftCleanup": {
|
||||
"UserApprovedCleanup": true,
|
||||
"Cleared": true,
|
||||
"DraftGuardPassed": true,
|
||||
"ClearedDraftLength": 4
|
||||
},
|
||||
"ExactStructuredTokenCleanup": {
|
||||
"UserApprovedCleanup": false,
|
||||
"Cleared": true,
|
||||
"DraftGuardPassed": true,
|
||||
"ClearedDraftLength": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Fingerprint": "b787bffca17193219159d001a2cdd7d70d82aaa8628dffa0135173af205f698d",
|
||||
"Type": "Quote",
|
||||
"length": 114,
|
||||
"content": null
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Fingerprint": "dcd1b8ceaef77ca58f0656abdf7dce58aadcf997a991d5bb94a88dc3be8d7042",
|
||||
"Type": "File",
|
||||
"length": 77,
|
||||
"content": null
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Fingerprint": "8d879245192fb79dc95a57672367997091df48454211605c428cc5c127280178",
|
||||
"Type": "Image",
|
||||
"length": 2,
|
||||
"content": null
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Fingerprint": "3ab05f45aecf440154d308dc4ca647ab760fb8098efd9029a19df735cc8c02e6",
|
||||
"Type": "Text",
|
||||
"length": 112,
|
||||
"content": null
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"doctorErrors": [],
|
||||
"nodes": 162,
|
||||
"sanitized": true,
|
||||
"confirmed": true
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"Checks": [
|
||||
{
|
||||
"Stage": "doctor",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "inspect-ui",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "smoke",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "send-multiline",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "send-file",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "send-image",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "reply-latest",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "mention-member",
|
||||
"ExitCode": 0
|
||||
},
|
||||
{
|
||||
"Stage": "mention-all",
|
||||
"ExitCode": 0
|
||||
}
|
||||
],
|
||||
"BinarySha256": "45E9994739D33E0786F3D224E9905DDF7439CCD322E6BAF21B431880706A0229",
|
||||
"Success": true,
|
||||
"MentionAttempted": true
|
||||
}
|
||||
Reference in New Issue
Block a user