From 6caac8ab7817d51df3af26d0bb1aef701942f393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Wed, 26 Aug 2026 21:57:24 -0300 Subject: [PATCH 1/2] deps: move to bridge 0.19.0 Carries whatsapp-rust#1352: a sender whose network is bad re-runs its own outbox and resends the same message id, re-encrypted, and every delivery used to become another messages.upsert. A bot answering messages answered the same one two or three times. The declared surface is unchanged; the only diff in the bridge's .d.ts is the renumbered __wasm_bindgen_func_elem_* internals. --- README.md | 9 +++++++++ package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8f0784d..6e5cd47 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,15 @@ A few behaviors that differ from upstream — almost always to your advantage: `connectTimeoutMs`, `qrTimeout`, `printQRInTerminal`, `ignoreOfflineMessages`, `downloadHistory`, `agent`, `fetchAgent`, `mobile`, `mediaCache`, `userDevicesCache`, `callOfferCache` and `placeholderResendCache`. +- **A sender's resend reaches you once, not once per delivery.** When a + sender's network is bad, their client re-runs its own outbox and sends the + same message again — same `key.id`, re-encrypted. Upstream Baileys emits a + `messages.upsert` for each of those deliveries, so an auto-reply bot answers + the same message two or three times; the engine now recognises the repeat and + emits one. It is a short-lived, in-memory window (five minutes), so the one + case still open is a sender whose retry spans a restart of your process. If + your handler has side effects, keying them on `key.id` remains the thing that + makes it safe — that was true before this and is still true for the gap. - **`Boom` ships in the box.** baileyrs exports its own `@hapi/boom`-compatible `Boom`, so the existing `(err as Boom).output.statusCode` pattern works unchanged. If your diff --git a/package-lock.json b/package-lock.json index 8c201e7..e5ff501 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@hapi/boom": "^9.1.4", - "@oxidezap/whatsapp-rust-bridge": "0.18.0", + "@oxidezap/whatsapp-rust-bridge": "0.19.0", "long": "^5.3.2", "pino": "^10.3.1", "protobufjs": "^7.6.5" @@ -951,9 +951,9 @@ } }, "node_modules/@oxidezap/whatsapp-rust-bridge": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@oxidezap/whatsapp-rust-bridge/-/whatsapp-rust-bridge-0.18.0.tgz", - "integrity": "sha512-N0F9mvPrFxPluV/ruUbQmm75nRjX4Il+BMi93C4tw5BCvvgSoeKmsHpAvxu3QNKscUbw8yVLvnbmLVYay9SATg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@oxidezap/whatsapp-rust-bridge/-/whatsapp-rust-bridge-0.19.0.tgz", + "integrity": "sha512-1105rs8yu21w5beUR1czeCu3YZNBa2Z3FW3IsfC33B2DSTDfX2bmzCukmR57K/MrUrkmfykeVfWv+7GlYRDKRQ==", "license": "MIT" }, "node_modules/@oxlint/binding-android-arm-eabi": { diff --git a/package.json b/package.json index bf44463..22d3e34 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ }, "dependencies": { "@hapi/boom": "^9.1.4", - "@oxidezap/whatsapp-rust-bridge": "0.18.0", + "@oxidezap/whatsapp-rust-bridge": "0.19.0", "long": "^5.3.2", "pino": "^10.3.1", "protobufjs": "^7.6.5" From a3d750a6ac90f5b7480c765d7e03898bd12c516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Wed, 26 Aug 2026 22:01:44 -0300 Subject: [PATCH 2/2] docs(readme): scope the idempotency key to the whole message key Keying a side effect on key.id alone drops the second message when two participants of one group choose the same id, which is the case the engine's own check carries the sender for. --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6e5cd47..9b8ad77 100644 --- a/README.md +++ b/README.md @@ -250,8 +250,12 @@ A few behaviors that differ from upstream — almost always to your advantage: the same message two or three times; the engine now recognises the repeat and emits one. It is a short-lived, in-memory window (five minutes), so the one case still open is a sender whose retry spans a restart of your process. If - your handler has side effects, keying them on `key.id` remains the thing that - makes it safe — that was true before this and is still true for the gap. + your handler has side effects, keying them on the whole `key` — `remoteJid`, + `id`, `fromMe` and `participant` — remains the thing that makes it safe. Not + on `id` alone: the id is the sending client's to choose, so two participants + of one group can pick the same one, and a handler that keys on it would drop + the second person's message rather than a duplicate. That is why the engine's + own check carries the sender too. - **`Boom` ships in the box.** baileyrs exports its own `@hapi/boom`-compatible `Boom`, so the existing `(err as Boom).output.statusCode` pattern works unchanged. If your