← 所有文章
教程

用 group.updated 同步 WhatsApp 群成员列表

要让应用中的 WhatsApp 群成员列表保持更新,可把 UnifyPort 的 group.updated webhook 作为刷新信号,再通过 List conversation members 查询成员。遍历 data.changes[] 的所有条目,但不要把其中的参与者 ID 当成完整名单。只有所有分页都成功后,才发布新的成员列表;若读取期间又收到变更,应安排下一轮查询。

要点

  • data.actor 是执行变更的人,changes[].members[] 才是受影响的参与者。
  • 是否在群内与是否为管理员,是名单中的两个不同维度。
  • 部分结果或查询失败不能清空先前观察到的成员。
  • 本文实现的是读取侧同步,不是自动管理成员。

理解事件,不要把通知变成操作指令

WhatsApp 的管理员说明区分任命和撤销管理员。应用也应保留这个区别:角色变化不等于成员离群。

UnifyPort 的 group.updated 文档定义了 data.conversation、data.actor 和 data.changes[]。一次事件可以包含多项变更。使用接收端的工作空间上下文、provider、account_id 和 data.conversation.id 定位群组,不要用群名匹配。

变更类型对成员列表的影响建议处理
member_added / member_removed成员关系可能变化请求刷新名单
promoted / demoted角色可能变化同时刷新成员标识和角色
renamed / description_changed / avatar_changed群资料变化转交独立的群资料刷新流程
dissolved群生命周期变化标记待核查,不自动删除已存历史

必须遍历整个数组,不能只读取 changes[0]。同一事件中若同时出现改名和新增成员,两种刷新都需要处理。未知或格式异常的条目应保留调查,而不是猜测效果。

如果操作员确实要调整角色,请使用独立的管理员升降级流程。收到移除通知不应再次发起移除请求;主动写入属于移除群成员流程。

用完整查询构建名单

查询会话成员文档返回 peer_id、display_name、avatar_url 和 role。角色包括 member、admin、superadmin,其中最后一种表示群主或创建者。

以下为后端请求示例,环境变量来自实际选中的消息账号和群组;本文未执行该请求:

curl --get \
  "https://api.unifyport.ai/v1/accounts/${UNIFYPORT_ACCOUNT_ID}/conversations/members" \
  -H "X-Api-Key: ${UNIFYPORT_API_KEY}" \
  --data-urlencode "conversation_id=${WHATSAPP_GROUP_ID}" \
  --data-urlencode "type=group" \
  --data-urlencode "limit=50"

当 data.has_more 为 true 时,把 data.next_cursor 放入下一次请求的 cursor 查询参数。先把所有分页累积到暂存集合,以返回的 peer_id 为键。API key 必须留在后端。

成员没有出现在第一页,不能据此从界面删除。任意一页失败,都应保留上一次完整名单,并显示刷新尚未完成。成功完成但结果为空的查询,与请求失败是两种状态。

标识符要原样保存。事件中的参与者 ID 不一定可与成员列表的 peer_id 互换。尤其是 WhatsApp 成员管理接口要求使用返回的成员 LID;不要从手机号构造 LID,也不要通过字符串改写强行合并不同格式的 ID。

防止刷新遗漏下一次触发

以下是建议的应用算法,不是新增的 UnifyPort 字段,也不是服务端事务保证:

  1. 持久化接收事件。 订阅 group.updated,同时保留其他必要订阅。按照webhook 投递契约先验证签名和时间戳,再信任正文。签名使用 signing_secret,对 X-Device-Timestamp、一个点和原始正文计算十六进制 HMAC-SHA256。
  2. 原子记录刷新需求。 在同一事务内,按事件 ID 去重并保存已认证的普通事件,同时递增该群在应用内的刷新代次。完成后返回 2xx。重复投递不能产生新的业务操作。
  3. 每个作用域内的群由一个 worker 读取。 记录当前本地代次,再拉取全部成员分页。可把多条待处理事件合并为一次读取,而不是每个参与者都查一次。
  4. 发布前再次检查。 在与接收流程协调的事务内,对比开始读取时的代次与当前代次。如果分页期间代次已变化,将暂存结果保留为临时观察并安排下一轮,不要标记名单已更新完毕。
  5. 发布完成的观察结果。 所有页成功,且没有新的本地触发待处理时,替换展示名单并标记该代次已处理。此后提交的新事件会创建新的刷新任务。

worker 崩溃后应能恢复未完成代次。仅保存在内存中的标记可能丢失刷新需求。通过串行执行或写入保护,防止较慢的旧任务覆盖较新的完成结果。

这一机制只能发现应用在读取期间收到的事件,不能证明上游成员始终未变:公开接口没有提供跨分页及 webhook 的共享快照版本。界面应显示“最后观察时间”,而不是承诺名单绝对实时。迟到的事件可以再次触发查询,无须把名单退回旧增量对应的状态。

处理没有通知与读取失败的情况

UnifyPort 是非官方接口。事件支持矩阵说明的是解析器映射,不保证每个上游账号都发出所有通知,也不保证遗漏事件可重放。

首次打开群视图时初始化名单,提供明确的刷新按钮,并按业务需要考虑对活跃群进行受控的定期核对。本文不声明轮询配额或新鲜度保证。

成员查询失败,不能单独证明群已解散或所有人都离开。将不可用、过期与空名单分开。待审批申请同样属于另一流程:group.join_request 不代表成员已经加入。

常见问题

能否用 changes[].members[] 重建全群名单?

不能。数组只描述某项变更影响的参与者,不包含所有当前成员。应查询并完整遍历成员分页。

data.actor 是加入或退出的成员吗?

它表示执行变更的人。受影响成员应看对应的 members[],当前名单则通过成员查询获取。

角色通知可以直接授予应用内权限吗?

不要自动把消息平台角色等同于应用权限。操作员授权应独立管理,敏感操作前还应重新核查。

下一步与参考资料

按查询会话成员文档实现刷新 worker。建议测试:成员位于后续页、中途分页失败、重复事件、同一事件多项变更、分页期间收到新变更,以及发布前 worker 崩溃。这些是待执行测试,不是已验证结果。

资料核对日期:2026-11-09。

UnifyPort API

让消息接入变成一条稳定的产品管线。

先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。