Files
trade-message-center/docs/onetalk-quote-reply-mind-integration.md
T
YBF 784bbec2a0 feat: 支持 OneTalk 文本引用回复协议 (#66)
* 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
2026-09-17 11:56:35 +08:00

5.6 KiB
Raw Blame History

OneTalk 引用回复:Mind 对接说明

适用范围:Mind 已接入 Bright OneTalk WebSocket 的文本发送链路。本说明只增加引用目标字段,不改变连接、授权、普通发送或发送结果协议。

1. 能力与边界

Mind 在 OneTalk 时间线中选择一条文本消息后,可以发送一条引用该消息的新文本。Mind 只向 Bright 提交被引用消息的 OneTalk messageId;Bright 和插件负责在同一账号、同一会话内解析 OneTalk SDK 所需的原始引用对象。

Mind 不得发送、保存或展示下列数据:

  • OneTalk SDK referMessage / originalData
  • .PNM 后缀的 SDK 传输别名;
  • 根据正文、时间、发送人或 UI 序号推导出的 ID;
  • Mind 数据库 UUID(它不是 OneTalk messageId)。

第一版只支持“引用文本后发送文本”。图片和文件仍走既有普通发送,不能附带引用字段。

2. 引用目标的来源

从 Bright 返回的 OneTalk 时间线消息中读取该条消息的 id / messageId,将其原样作为 replyToMessageId

type ReplyTarget = {
    messageId: string; // Bright OneTalk messageId,不是 Mind UUID
    from: string;
    text: string;
};

仅当时间线行满足以下条件时显示“回复”入口:

  • 消息正文是非空文本;
  • messageId 为非空字符串;
  • 消息属于当前 OneTalk 会话,且不是 system、optimistic 或本地临时消息。

messageId 不做 trim、拼接或格式转换;例如 123456.PNM 不是合法的 replyToMessageId

3. 发送帧增量

沿用既有 send.request WebSocket 帧。在当前共享协议版本 9 中,引用发送的 payload 必须恰好为以下形状:

{
    "conversationId": "c123",
    "content": {
        "kind": "text",
        "text": "这条消息是在回复前文"
    },
    "replyToMessageId": "123456"
}

完整帧示例(scoperequestIdsendRequestId 继续使用当前 Mind 的既有生成与关联逻辑):

{
    "protocolVersion": 9,
    "connectionType": "mind_page",
    "type": "send.request",
    "requestId": "mind-frame-001",
    "sendRequestId": "mind-send-001",
    "scope": {
        "workspaceId": "workspace-1",
        "mindUserId": "mind-user-1",
        "channelAccountId": "286995452"
    },
    "payload": {
        "conversationId": "c123",
        "content": {
            "kind": "text",
            "text": "这条消息是在回复前文"
        },
        "replyToMessageId": "123456"
    }
}

普通发送保持原样,省略 replyToMessageId

{
    "conversationId": "c123",
    "content": { "kind": "text", "text": "普通文本" }
}

不要把 replyToMessageId 放到 contentext 或其他扩展对象中;协议采用严格字段校验,未知字段或错误层级会被拒绝。

4. Mind 侧校验

发起引用发送前,Mind 必须确认:

  • conversationId 是当前会话的 OneTalk conversationId
  • replyToMessageId 非空、没有首尾空白,且不是纯数字加 .PNM
  • content.kind === "text"content.text 非空;
  • 当前 WebSocket scope 中的 channelAccountId 与时间线消息所属账号一致。

不要在客户端把无效引用降级为普通文本发送。若本地无法确定目标 ID,应禁用发送并提示用户重新选择消息。

5. Composer 交互

点击“回复”后,Mind 只保留临时 UI 状态 { messageId, from, text }:composer 中显示发送者和截断文本摘要,并提供“取消引用”。

以下场景必须清除该临时状态:

  • 切换 OneTalk 会话;
  • 收到 confirmed_sent
  • 收到 rejected_before_send

收到 delivery_unknown 时,不能自动重试或伪造发送成功;应保留既有“结果未知”的可见状态,由用户决定后续操作。

第一版不要求 Mind 解析或重新绘制远端消息里的引用卡片。引用内容由 OneTalk 页面渲染,Mind 时间线仍按 Bright 已返回的规范化消息展示。

6. 结果处理

send.result 与普通发送使用同一关联键 sendRequestId,结果语义不变:

status Mind 行为
confirmed_sent 将返回的已发送消息并入时间线,清除引用临时状态。
rejected_before_send 显示 reason,清除引用临时状态;不得改发普通文本。
delivery_unknown 显示结果未知;不得自动重试、不得假定消息已发送。

常见拒绝原因包括 invalid_request(引用 ID 不合法、引用目标不在当前会话或不支持的内容类型)、waiting_for_pagepage_not_foundpage_identity_mismatchsend_not_supported。Mind 应直接展示已有的发送错误状态,不自行转换错误码。

7. 联调验收

  1. 加载 OneTalk 会话与文本消息,确认时间线行使用 Bright 返回的真实 messageId
  2. 选择一条文本消息,发送引用文本,检查发送帧只有顶层 payload.replyToMessageId
  3. 确认 send.result 与同一个 sendRequestId 关联,并仅在 confirmed_sent 时显示发送成功。
  4. 验证切换会话、取消引用与 rejected_before_send 都会清除 composer 的引用状态。
  5. 验证普通文本、图片和文件发送均不携带 replyToMessageId,原有行为不变。

8. 兼容性

Mind、Bright 与 Chrome 插件必须使用兼容的 OneTalk 协议版本部署。当前源码的协议版本为 9;上线前以实际运行中的共享 contract 为准,不要让旧客户端向新协议帧附加未知字段。