Skip to content

fix(engine): forward the original deleted message id as revokedId on message.revoked#567

Open
JibayMcs wants to merge 2 commits into
rmyndharis:mainfrom
JibayMcs:fix/revokedId-v0.7.17
Open

fix(engine): forward the original deleted message id as revokedId on message.revoked#567
JibayMcs wants to merge 2 commits into
rmyndharis:mainfrom
JibayMcs:fix/revokedId-v0.7.17

Conversation

@JibayMcs

@JibayMcs JibayMcs commented Jul 1, 2026

Copy link
Copy Markdown

Problem

Use case. I use OpenWA to back a two-way synced WhatsApp inbox: messages are
persisted on my side (keyed by their WhatsApp id) and rendered in a conversation UI.
When a user deletes a message for everyone from their phone, I need that deletion to
propagate to the stored conversation — i.e. flag the corresponding row as revoked and
update the UI. The message.revoked webhook is exactly the signal for that... except it
doesn't carry the id of the message that was deleted, so there's nothing to match against.

Digging in, the root cause is on the whatsapp-web.js engine. whatsapp-web.js emits
message_revoke_everyone(after, before) where:

  • after is the revocation notification — a new, distinct message whose id has
    nothing to do with the deleted one;
  • before is the original deleted message (present whenever it's in the local store).

The current handler only reads after and emits id: after.id._serialized. That id
never matches any stored row, so:

  1. OpenWA's own reconciliation is a silent no-op. In SessionService, the revoke
    handler runs messageRepository.update({ waMessageId: message.id }, { type: 'revoked' }).
    Because message.id is the notification id, it matches zero rows — the stored message
    is never flagged as revoked on wwebjs.
  2. Webhook consumers can't reconcile either. Anyone building an inbox on top of
    message.revoked (like the case above) receives an id they can't map back to the
    message they stored, so the deletion can't be reflected.

Baileys is not affected the same way (its revoke arrives as a protocolMessage whose key
already points at the original), but the payload shape was inconsistent between the two
engines, so there was no reliable cross-engine field to match on.

Fix

Add an optional revokedId to RevokedMessage: the serialized id of the original
deleted message. Both adapters populate it, so consumers have one reliable field.

  • whatsapp-web.js adapter — capture the second callback arg and set
    revokedId: before?.id?._serialized (undefined when the original isn't in the local
    store). id still carries the revocation notification, unchanged.
  • Baileys adapter — mirror the original into revokedId (id === revokedId there),
    so the field is always present and consistent across engines.
  • SessionService — reconcile the stored message on revokedId ?? id, so the
    original is matched on wwebjs while Baileys behaviour is preserved.

Guidance for consumers (documented on the interface): match on revokedId, falling
back to id.

Backwards compatibility

  • revokedId is optional and purely additive; id, chatId, from, to, type,
    body, timestamp are unchanged.
  • Existing consumers keep working; those that want reliable reconciliation switch to
    revokedId ?? id.

Tests

  • whatsapp-web.js adapter: revokedId is taken from before and is distinct from
    id; revokedId is undefined when whatsapp-web.js provides no before.
  • Baileys adapter: revokedId mirrors the original id (id === revokedId).
  • SessionService: the repository update targets revokedId when present and falls
    back to id.

Notes

Branch is cut from the v0.7.17 tag; the affected handler and interface are byte-identical
on main, so it applies cleanly.
It works like a charm on my docker instance 🥳

Thanks for your work on this amazing tool !

JibayMcs added 2 commits July 1, 2026 11:40
The message_revoke_everyone handler only used 'after' (the revocation
notification) and discarded 'before' (the original deleted message). Consumers
never received the id of the message that was actually deleted and could not
reconcile it in their own storage.

Forward before.id as an optional 'revokedId' on the RevokedMessage payload
(optional because whatsapp-web.js may not always capture the original message).
- Add `revokedId` to revoked message interface and Baileys adapter
- Set `revokedId` from the protocolMessage key (mirrors `id` in Baileys)
- Update baileys adapter spec to assert `revokedId` equals original message id
- Add session service spec that verifies DB update uses `revokedId` (original)
  rather than the revocation notification id
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant