Repository navigation
fix(baileys): forward chat state in the CHATS_UPDATE webhook - #2791
lschiaffino wants to merge 1 commit into
Conversation
The chats.update handler sent only { remoteJid, instanceId }, so webhook
consumers never learned that a chat was archived, pinned or read from the
phone. Forward archived, pinned and unreadCount when Baileys reports them.
Reviewer's GuideAPIForward Baileys chat-state fields in the CHATS_UPDATE webhook so consumers can mirror archive, pin, and read changes.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts" line_range="906-908" />
<code_context>
+ return {
+ remoteJid: chat.id,
+ instanceId: this.instanceId,
+ ...(typeof chat.archived === 'boolean' ? { archived: chat.archived } : {}),
+ ...(chat.pinned !== undefined && chat.pinned !== null ? { pinned: Number(chat.pinned) } : {}),
+ ...(typeof chat.unreadCount === 'number' ? { unreadCount: chat.unreadCount } : {}),
+ };
});
</code_context>
<issue_to_address>
**Older chat state overwrites newer state**
When two updates change the same chat field before the earlier webhook delivery completes, `chatHandle['chats.update']` starts each `sendDataWebhook` without awaiting it, so consecutive updates can be delivered out of order; a webhook consumer applying the included state fields then ends with the older value.
Serialize webhook emissions per instance or chat so later state updates cannot overtake earlier ones.
</issue_to_address>
### Comment 2
<location path="src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts" line_range="907" />
<code_context>
+ remoteJid: chat.id,
+ instanceId: this.instanceId,
+ ...(typeof chat.archived === 'boolean' ? { archived: chat.archived } : {}),
+ ...(chat.pinned !== undefined && chat.pinned !== null ? { pinned: Number(chat.pinned) } : {}),
+ ...(typeof chat.unreadCount === 'number' ? { unreadCount: chat.unreadCount } : {}),
+ };
</code_context>
<issue_to_address>
**Pinned state arrives as a number**
When baileys emits `pinned` as a boolean, `Number(chat.pinned)` converts Baileys’ boolean pin flag to `0` or `1`, so consumers expecting a boolean receive the wrong wire type and cannot reliably mirror pin state.
Forward `chat.pinned` without converting it to a number.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 2 findings to address first, and if the forwarded chat state is wrong or should not be shared, the webhook sends that information to consumers and the delivery cannot be recalled. Reverting stops future payloads but does not undo values already received.
Blocking findings: src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:908, src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:907
| ...(typeof chat.archived === 'boolean' ? { archived: chat.archived } : {}), | ||
| ...(chat.pinned !== undefined && chat.pinned !== null ? { pinned: Number(chat.pinned) } : {}), | ||
| ...(typeof chat.unreadCount === 'number' ? { unreadCount: chat.unreadCount } : {}), |
There was a problem hiding this comment.
🟡 Medium · Older chat state overwrites newer state
When two updates change the same chat field before the earlier webhook delivery completes, chatHandle['chats.update'] starts each sendDataWebhook without awaiting it, so consecutive updates can be delivered out of order; a webhook consumer applying the included state fields then ends with the older value.
Serialize webhook emissions per instance or chat so later state updates cannot overtake earlier ones.
Prompt for AI agents
In `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts` at lines 906-908:
**Older chat state overwrites newer state**
When two updates change the same chat field before the earlier webhook delivery completes, `chatHandle['chats.update']` starts each `sendDataWebhook` without awaiting it, so consecutive updates can be delivered out of order; a webhook consumer applying the included state fields then ends with the older value.
Serialize webhook emissions per instance or chat so later state updates cannot overtake earlier ones.| remoteJid: chat.id, | ||
| instanceId: this.instanceId, | ||
| ...(typeof chat.archived === 'boolean' ? { archived: chat.archived } : {}), | ||
| ...(chat.pinned !== undefined && chat.pinned !== null ? { pinned: Number(chat.pinned) } : {}), |
There was a problem hiding this comment.
🟡 Medium · Pinned state arrives as a number
When baileys emits pinned as a boolean, Number(chat.pinned) converts Baileys’ boolean pin flag to 0 or 1, so consumers expecting a boolean receive the wrong wire type and cannot reliably mirror pin state.
Forward chat.pinned without converting it to a number.
Prompt for AI agents
In `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts` at line 907:
**Pinned state arrives as a number**
When baileys emits `pinned` as a boolean, `Number(chat.pinned)` converts Baileys’ boolean pin flag to `0` or `1`, so consumers expecting a boolean receive the wrong wire type and cannot reliably mirror pin state.
Forward `chat.pinned` without converting it to a number.
📋 Description
The
chats.updatehandler inwhatsapp.baileys.service.tssends the webhook with only{ remoteJid, instanceId }. Baileys emitschats.updatewhen a chat is archived, unarchived, pinned or read from the phone (app-state sync), but that state is dropped, so webhook consumers can't mirror it. This forwardsarchived,pinnedandunreadCountwhen Baileys includes them (spread only when present, so existing consumers that readremoteJidare unaffected).🧪 Type of Change
🧪 Testing
✅ Checklist
📝 Additional Notes
Prepared with AI assistance (Claude) and reviewed by me.
Summary by Sourcery
Bug Fixes: