处理 LINE 撤回事件:避免已删除消息重新出现在收件箱
LINE 用户撤回消息后,应从客服界面移除内容,并停止在下游使用它。LINE 官方 webhook 指南建议取消展示并删除已存储的内容。通过 UnifyPort 接入时,可处理标准化的 message.deleted 事件,持久化删除标记,再执行可重试的清理任务。只删除收件箱里的一行还不够:迟到事件可能重建消息,搜索或 AI 上下文也可能仍保留副本。
要点
- 接收撤回通知与调用 API 主动撤回消息是两件事。
- 按事件 ID 去重,但要用
data.message.id及账号范围定位目标消息。 - 保留最小删除标记,阻止迟到的接收或编辑事件恢复内容。
- 单独跟踪下游清理状态;webhook 确认成功不代表所有副本已删除。
LINE 撤回对已存储内容意味着什么
LINE 消息接收指南说明,用户撤回消息时,服务器会收到 unsend 事件。官方建议尊重用户意图,避免该消息继续被查看或使用,包括从管理界面移除,以及从存储中删除。
这是官方建议。下文的删除标记、事务发件箱和验收测试是应用架构建议,不是 LINE 内置功能,也不是关于法定保留期限的结论。
编辑是替换内容,撤回则是停止使用内容。如果同时处理编辑,可保留LINE message.updated 对账流程,但必须在写入新文本前检查删除标记。修订历史不能变成客服或检索任务仍可访问的隐藏副本。
先区分两种事件契约
LINE 官方 Messaging API 接收器与 UnifyPort 接收器使用不同的载荷及验签契约。未经独立适配,不要把原生 LINE 请求直接交给标准化事件处理器。
UnifyPort 的标准事件参考将 message.deleted 定义为消息被删除或撤回。在 message 对象中,只保证 data.message.id;文本和媒体会被移除。平台事件矩阵列出了 LINE、Telegram 和 WhatsApp 对该事件的映射支持,但不保证所有上游账号或部署都会产生每一种已映射事件。
以下为使用真实字段及虚构标识符的示例,并非实际抓取的投递:
{
"id": "evt_6d91a2c8",
"type": "message.deleted",
"provider": "line",
"account_id": "acc_8c21d0",
"occurred_at": "2026-09-23T08:15:00Z",
"data": {
"conversation": { "id": "c8f2a4d91e", "type": "group" },
"message": { "id": "551842037194" },
"event": { "kind": "message_deleted" }
}
}
顶层 id 标识事件,data.message.id 标识被移除的消息。不要用前者查询消息,也不要把缺少文本解释为“编辑成空字符串”。
先建立删除屏障,再清理副本
在 subscribed_events 中加入 message.deleted,并保留业务需要的接收、编辑事件。启用 signing_secret。按照投递契约,对时间戳、点号与原始请求体组成的字节串验证 HMAC-SHA256,并在接受任务前执行时间戳新鲜度检查。重放防护教程解释了为什么验签与持久化去重不能互相替代。
完成身份验证和载荷校验后:
- 在正确的工作区、平台和消息账号内定位目标。可用时使用会话与消息标识符;会话信息缺失时,只使用既有且无歧义的账号内映射。不要跨会话猜测,无法定位的目标进入隔离审核。
- 在同一事务中记录事件去重信息、创建或保留最小删除标记、移除在线内容,并写入清理任务。与同一目标的接收、编辑写入进行串行协调。
- 持久化接受后才返回 2xx。下游清理由 worker 独立重试。
- 所有接收及编辑写入都必须检查标记,迟到事件不能重新填充内容或触发新的 AI 任务。
以下是建议的应用状态规则,不是 API 字段:
| 已存状态 | 新到事件 | 处理 |
|---|---|---|
| 没有原消息 | 删除 | 保存不含内容的标记,不等待原消息 |
| 在线内容 | 删除 | 停止展示并排入清理任务 |
| 删除标记 | 接收或编辑 | 不恢复内容 |
| 删除标记 | 重复删除 | 保持状态,继续未完成的清理 |
不能只依赖到达顺序或“最后时间戳覆盖”。UnifyPort 不保证投递顺序。删除标记是应用主动采用的规则:对同一消息身份,删除继续有效。
跟踪副本,而不仅是收件箱记录
维护来源消息到衍生产物的本地映射。以下是需要盘点的清理目标,并非 UnifyPort 自动执行的能力:
| 副本或任务 | 建议动作 |
|---|---|
| 收件箱正文、预览 | 删除内容并使缓存失效 |
| 已下载附件 | 删除自己持有的副本和缩略图 |
| 搜索、向量索引 | 删除条目,清理未完成时阻止检索 |
| AI 上下文、摘要、待发回复 | 排除来源,使受影响摘要失效,取消或审核依赖任务 |
| CRM、团队聊天转发 | 支持时删除或脱敏,跟踪无法清理的副本 |
| 原始事件日志、死信队列、备份 | 执行明确的访问及保留策略,防止恢复到在线视图 |
worker 应在检索前、发布生成文本前以及重建索引时重新检查删除状态。条件允许时,让发布也使用相同的消息级保护机制。已经交给外部系统的工作不一定能收回,应记录该边界,而不是承诺彻底抹除。
最小标记可以只保留限定范围的标识符和清理状态,不保留被撤回文本。结合重放、备份恢复和隐私要求确定保留期;旧事件仍可重放时过早删除标记,会重新引入内容恢复风险。
验收测试与限制
使用受控测试消息,不使用客户数据。以下是建议测试,不是已执行的结果:
- 删除先于原消息到达:后到的接收事件仍被抑制。
- 删除与编辑或索引任务并行:内容不会重新发布。
- 重复投递删除:不重复产生副作用,未完成清理仍会重试。
- 下游删除失败:收件箱隐藏内容,同时明确显示清理仍待完成。
- 恢复备份:开放搜索或收件箱访问前,先应用删除标记。
UnifyPort 提供非官方接口与标准化事件流,不会自动删除 CRM 或 AI 系统中的内容。它没有 REST 消息历史读取 API,也不保证重放漏收事件。没收到删除事件,不等于没有发生撤回。产品需要原生官方账号契约时,应优先采用 LINE 官方路径。
常见问题
message.deleted 是撤回他人消息的请求吗?
不是。它报告已观察到的删除或撤回。清理自己保存的副本与主动调用平台动作是不同操作。
能从删除载荷中找回原文吗?
不能。文档保证目标消息 ID,不保证已移除的文本或媒体。不要把撤回处理设计成内容恢复功能。
HTTP 200 表示下游全部删完了吗?
不是。它只是投递确认,应用需要单独跟踪清理完成状态与失败。
下一步与来源
查看平台 webhook 事件矩阵,然后在启用下游 AI 处理前,为接收器加入“删除先于原消息”的测试。
资料核对日期:2026-09-23。
让消息接入变成一条稳定的产品管线。
先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。