mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
* feat: add OneTalk quote reply support * chore(task): archive 09-17-onetalk-quote-reply-protocol * chore: record journal * docs: format OneTalk DOM card collection plan * chore: release 0.8.29 * chore: release 0.8.30
6.3 KiB
6.3 KiB
OneTalk 引用回复跨端协议接入
Goal
让 Mind 用户能选择一条 OneTalk 时间线消息并发送引用回复,同时保持 OneTalk 原始载荷只在插件 MAIN world 内解析。协议必须在 Mind、Center WebSocket、Service Worker 和页面 SDK 间传递确定的引用目标,不让任一层按正文、时间或显示名称猜测引用内容。
Confirmed facts
- 2026-09-17 的真实 Chromium(OneTalk PWA)中,
_imBaaSSDK版本为5.0.110,其 MessageService 暴露sendUIMessages。读取已加载 SDK 源码确认:顶层referMessage被转换为发送请求的extParam.quoteMessage,接收侧再还原为extInfo.referMessage。 - 已用该 SDK 在当前真实会话中发送一条文本引用回复;SDK 返回本地受理对象,随后页面渲染出
quote-wrapper引用块。该实测证明引用序列化和 UI 渲染,不证明跨会话路由。 - 扩展当前稳定 SDK 入口是
window.IcbuIM.IMBaaSSDK.default.getMessageService().sendUIMessages;send.ts 和 send-sop.md 都规定cid是权威路由字段,conversationCode只能与它同值作为兼容字段。 - 当前
SendObservationCorrelator.execute只将cid、conversationCode、content和可选ext传给 SDK。SDK 读取的是顶层referMessage,所以把它放在ext.referMessage不会启用引用回复。 - 扩展的业务 ID 会把数字
.PNM别名规范化为无后缀形式;引用 SDK 参数需要恢复原始消息 ID 的.PNM传输形式。 - 当前
send.request/send.command的 payload 严格只允许conversationId与content,且所有 WebSocket 帧的protocolVersion都须精确等于共享ONETALK_PROTOCOL_VERSION。引用 ID 因而需要同时改 shared contract、Mind frame builder/parser、Center 转发和插件 page command;任一方仍使用旧帧都会被拒绝。 - Mind 的 OneTalk HTTP/实时投影将 Center
messageId原样赋给时间线消息的id与externalMessageId。它只持有规范化的content、senderId、参与者和时间,不持有 OneTalk SDK 生成引用所需的subType、originalData、展示名等原始字段。 - 插件当前在历史和实时解析时读取 raw OneTalk message,但只跨 MAIN 边界发布白名单后的
OneTalkMessage,不保留可按[conversationId, messageId]查找的引用源。引用功能需要新增仅存在于 MAIN world 的短生命周期索引;raw 数据不得经 WebSocket 发给 Mind 或持久化为第二份业务内容。 - Mind 现有 WhatsApp 引用使用
replyToMessageId,但其 HTTP schema 要求 UUID。OneTalk 的messageId可能是数字字符串,不能直接复用该 UUID 校验或将它误认为 Mind 数据库 ID。
Requirements
- Mind 发送请求、Center
send.request/send.command及插件 page command 增加可选顶层replyToMessageId,值为当前 OneTalk 时间线行的原始messageId;省略时普通发送的 wire shape 与行为保持不变。 replyToMessageId只能是非空 OneTalk 业务 ID,必须在 plugin MAIN world 归一化后与同一conversationId和当前channelAccountId下的原始来源精确匹配。它不接受 Mind 数据库 UUID、.PNM拼接后的别名、正文、时间或合成 ID。- 插件必须从仅 MAIN-world 的原始引用索引解析 SDK
referMessage:msgId、subType、senderAliId、senderName、contentAbstract、originalData和sendTime。解析不完整、ID 不存在、会话/账号不匹配或类型未验证时 fail closed 为rejected_before_send/invalid_request,且不调用 SDK。 - 解析成功时唯一的发送入口为
messageService.sendUIMessages({ cid, conversationCode: cid, content, referMessage });不使用quoteReply、DOM 点击、sendMessage或sendTextMessage。 cid与conversationCode的路由语义必须保持现有契约:前者为权威目标,二者必须同值,selected 会话和 URL 均不能替代cid。- 协议升级必须协调 Mind 与插件版本;共享帧 decoder 对版本和 payload keys 均 fail closed,不能采用“未知字段忽略”的兼容降级。
- SDK 返回值仍只是本地受理;必须沿用 WebSocket 旁路观察完整
direction: sent消息来确认真实投递。 - 第一版 Mind UI 在每条已加载的 OneTalk 普通文本消息上提供回复操作;选中后 composer 显示发送者与文本摘要,并允许取消。发送成功、发送前拒绝或当前会话切换时清除引用目标;历史消息本身不新增引用卡展示。
- 第一版仅允许
subType: 1文本引用(已在真实 Chromium 以 SDK 成功验证)。图片、文件、富卡、系统消息、无正文消息和未解析类型没有回复操作,也不能通过手工帧绕过这一限制。
Out of scope
- 为非 OneTalk 渠道改变既有 WhatsApp、邮件或通用 TradeBridge 引用协议。
- 向 Mind、Center WebSocket、数据库或日志传递 SDK raw message /
originalData,或建立第二份 raw 消息存储。 - 用页面私有
EventBus或quoteReply作为扩展发送实现。 - 为未知消息类型虚构 ID、发送者、原始数据或引用摘要。
Acceptance criteria
- Mind 选择的时间线消息以其 OneTalk
messageId作为可选replyToMessageId发送,且不将 Mind 数据库 ID 或完整 raw 载荷带入 WS 帧。 send.request、send.command和 page command 都以相同可选字段透传,普通发送保持原 payload shape;版本和 exact-key decoder 的联动升级有测试。- MAIN-world 索引只保存构建 SDK
referMessage所需的短生命周期原始引用数据,按 channel account、conversation 和 canonical message ID 隔离,且不跨桥发布。 - 有效引用调用包含同值的非空
cid与conversationCode、正文和顶层referMessage;msgId的.PNM规则、subType、senderAliId、senderName、contentAbstract、originalData、sendTime均有测试。 - 不存在/不完整/跨会话/跨账号/不受支持的
replyToMessageId得到 canonical pre-send rejection,绝不发送普通文本作为降级。 - 端到端测试覆盖 Mind frame、Center 转发、插件 SDK 输入和已确认发送;
opId或页面渲染不能单独作为投递成功证据。