通过 UnifyPort 添加与移除消息表情回应
通过 UnifyPort 添加消息表情回应时,调用 POST /v1/messages/reaction,提交选定的账号、会话、消息、原消息发送者及 emoji。要移除当前连接账号已有的回应,保留目标并发送 emoji: ""。最容易混淆的是:sender_id 指原消息的作者,不是操作员,也不是你想修改其回应的任意用户。
要点
- 当前发送操作支持
whatsapp和telegram;接收回应事件是另一项能力。 - 使用选中的原消息定位目标,不要使用回应事件自身的消息 ID。
- 回应他人的消息时明确传入
sender_id;省略时默认指连接账号自身。 - 添加与移除应是明确操作,不要设计成失败后反复重试的切换开关。
将请求字段对应到原消息
消息回应接口操作的是已有消息,不是发送一条包含表情的文本,也不使用引用回复 token。
| 请求字段 | 从已保存的 message.received 事件取值 |
|---|---|
account_id | 顶层 account_id |
conversation_id | data.conversation.id |
message_id | data.message.id |
sender_id | data.sender.id,即原消息作者 |
emoji | 操作员选择的表情;移除时为空字符串 |
在群聊中,会话标识群组,发送者标识成员。不要因为私聊中两者看起来相似,就把发送者替换成会话目标。保留渠道返回的完整标识,不要根据联系人名称或手机号自行拼接。
如果入口是 message.reaction 事件,则必须再区分:data.message.id 标识回应自身,data.message.target_message_id 才指向原消息。该事件的发送者是作出回应的人,不一定是原消息作者。应先找到已保存的原消息,再构造操作。回应 webhook 处理教程解决的是接收与状态投影问题;本文解决的是主动发送请求。
构造明确的添加或移除请求
以下 JavaScript 只是请求构造示例,不是已执行的集成测试,也不是完整接收器。输入应是经过验签、持久化,并由有权限的操作员选中的原始消息事件。函数名和错误文字属于本地应用代码。
function buildReactionRequest(original, emoji) {
if (original.type !== 'message.received' ||
!['whatsapp', 'telegram'].includes(original.provider)) {
throw new Error('Select a supported original message');
}
const conversation = original.data?.conversation;
const message = original.data?.message;
const sender = original.data?.sender;
if (!original.account_id || !conversation?.id ||
!message?.id || !sender?.id) {
throw new Error('Original message context is incomplete');
}
if (typeof emoji !== 'string') {
throw new Error('Choose an emoji or an empty string');
}
return {
account_id: original.account_id,
conversation_id: conversation.id,
message_id: message.id,
sender_id: sender.id,
emoji
};
}
传入 "👍" 请求添加回应,传入 "" 请求移除。该函数只检查上下文和数据类型,不保证上游接受任意表情。不要用 null、省略字段或自行添加的 remove 标记代替空字符串。
由后端使用 X-Api-Key,将生成的 JSON 发往 POST /v1/messages/reaction。密钥不得进入浏览器代码。发送前验证操作员对消息账号及消息的访问权限,并固定选中的目标,避免新消息到来后操作对象改变。
如果需要的是带引用的一句话,而非表情回应,应使用单独的 WhatsApp 引用回复流程。其不透明 reply_token 不是回应标识。
根据操作能力启用按钮,而非事件能力
当前渠道操作矩阵与 webhook 事件矩阵描述的是不同能力:
| 渠道 | 通过此接口发送回应 | 回应事件映射 |
|---|---|---|
whatsapp | 支持 | 支持 |
telegram | 支持 | 支持 |
line | 不支持 | 支持 |
twitter、zalo、tiktok、whatsapp-protocol | 不支持 | 不支持 |
这是文档中的适配器映射,不保证每个账号或上游条件下都可用。LINE 收件箱可以显示传入回应,但不能因此启用发送回应按钮。不支持的组合返回 501 unsupported_by_provider,重复调用不会增加能力。
UnifyPort 提供连接账号的非官方接口。Telegram 官方 Bot API另有机器人使用的 setMessageReaction。如果要求机器人身份,应使用对应原生契约;不要将其请求结构复制到本接口,也不要把其限制直接当成 UnifyPort 的行为定义。
分开保存意图、请求结果与观察状态
接口文档展示的成功响应为 200 OK,包含 data.ok: true。保存实际 HTTP 结果及返回的 request_id,不要把成功结果解释为其他人已经看见回应。
建议应用按以下方式处理:
- 同一账号、同一目标消息的操作串行执行,避免旧的添加任务在较新的移除任务之后运行。
- 发送前记录期望表情,另行记录 HTTP 结果。
- 出现
react_message_failed时检查错误与原始目标。超时则保留“结果不确定”,不要自动发送相反操作作为补偿。 - 收到匹配的回应事件时幂等更新观察状态;不要假定每次请求必有回调,或回调一定有序。
接收侧配置 signing_secret 并遵循投递文档:对时间戳、点号及原始请求体进行 HMAC-SHA256 验证,检查时间新鲜度,持久化后再确认投递。接收到 webhook 不等于获得了自动发起新操作的授权。
验收检查与常见问题
上线前分别测试:收到的私聊消息、其他群成员的消息、账号自己发送的消息、空字符串移除、不支持的渠道及网络结果不确定。确认新消息到来不会改变选定目标。这是建议测试清单,不是已完成的测试结果。
sender_id 能指定由谁作出回应吗?
不能。它指定被回应消息的作者。执行操作的是当前连接账号,不能用该字段冒充其他回应者。
再发送相同表情就能移除吗?
文档规定使用 emoji: "" 移除,不应假定存在切换语义。
为什么能接收 LINE 回应,却不能发送?
入站事件解析和出站操作有独立支持矩阵。当前出站回应操作只映射到 Telegram 和 WhatsApp。
下一步与参考资料
先对照消息回应接口实现目标检查,再加入收件箱按钮。证据不足时保留未确定状态,不要虚构回应查询接口或保证重放能力。
核对日期:2026-10-16。
让消息接入变成一条稳定的产品管线。
先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。