chatwoot-adapter 0.5.5: id-less own sends, and out of beta#44
Merged
Conversation
An engine that cannot read a message id back reports the empty sentinel — Baileys' `?? ''`, a case outbound.ts already logs for its own echo guard, and one the gateway's unique index sidesteps because '' would collide. handleSent keyed its de-duplication on that id unconditionally, so every id-less own send collapsed onto one marker: the first was mirrored into Chatwoot, and every later one was skipped as "already seen" for the marker's full three-day retention. Silently, and only in the operator's helpdesk thread. An id-less message cannot be de-duplicated by definition, so the only question is which way to fail. Relay it and skip both marker operations: a duplicate in the helpdesk is visible and harmless, a missing customer message is neither. The skip is logged. Found while investigating an own send that never reached Chatwoot. That one turned out to be unrelated — the send had failed outright, so there was correctly nothing to mirror — but the path it led through was genuinely broken. The test drives two id-less sends and asserts both are mirrored; it fails on the previous commit.
Ships the id-less own-send fix from the previous commit and moves the plugin out of beta. The promotion rests on evidence rather than elapsed time. The two-way relay was exercised end to end against a live WhatsApp account on BOTH engines — inbound relay, agent replies with text and media, own outbound messages mirrored without being re-sent, and a real 50-message history import that imported photos with their files and re-sent nothing. Tested against OpenWA 0.10.5. Two things I expected to block this did not survive checking, and both are worth recording. An own send that never reached Chatwoot turned out to be a send that had failed outright, so there was correctly nothing to mirror. And a batch of imported messages that looked like empty bubbles were captionless photos: empty content, one image attachment each, files intact — which is what a photo without a caption should look like. The `@lid` limitation is now stated plainly in the README instead of living only in code comments. A contact arriving privacy-id-first, or one whose mapping is lost, can appear as a second Chatwoot contact. It costs no messages and cannot affect a thread already mapped, but stable should mean "we know where the edges are", not "there are none".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The fix
When WhatsApp does not return an id for a message you send — occasional, and more common on Baileys —
handleSentstill used that missing id as its de-duplication key. Every id-less own send therefore collapsed onto a single marker: the first reached Chatwoot, and any later one was skipped as "already seen" for the marker's full three-day retention. Silently, and only in the Chatwoot thread; the message itself reached the recipient normally.An id-less message cannot be de-duplicated by definition, so the choice is which way to fail. It is now relayed with both marker operations skipped and the skip logged: a duplicate in the helpdesk is visible and harmless, a missing customer message is neither.
outbound.tsalready treats the same empty-id case as a real hazard for its own echo guard; this bringssent.tsin line.The test drives two id-less own sends and asserts both are mirrored. It fails against the previous commit.
Out of beta
The promotion rests on evidence, not elapsed time. Exercised end to end against a live WhatsApp account on both engines:
The backfill run matters most here: it replays
fromMehistory as outgoing through the same code path that carried the duplicate-send defect, and it had never been executed even once before now.Two blockers that did not survive checking
Both are recorded because the reasoning is more useful than the conclusion.
An own send that never reached Chatwoot looked like a mirroring gap at roughly 2% of outgoing traffic. It was a send that had failed outright — status
failed, never delivered — so there was correctly nothing to mirror. The measurement had counted failed sends.A batch of imported messages appeared as empty bubbles, ~14% of the import. They were captionless photos: empty content, one image attachment each, files intact at 36 KB–349 KB. That is exactly what a photo without a caption should look like. Acting on that reading would have added placeholder text to images that render correctly today.
Known limitation, now in the README
A contact arriving privacy-id-first, or one whose stored mapping is lost, can appear as a second Chatwoot contact. It costs no messages, and cannot affect a thread already mapped — once a chat is mapped the adapter never creates a second conversation for it. Merging the contacts in Chatwoot is safe. It previously lived only in code comments;
stableshould mean the edges are known, not absent.Catalog regenerated;
tsc --noEmitclean.