From 728cd5e974b4539f200cfcc28af26e22e929d952 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 15 Jan 2026 06:21:05 +0000 Subject: [PATCH] fix: document WhatsApp read receipts toggle (#882) (thanks @chrisrodz) --- CHANGELOG.md | 1 + docs/channels/telegram.md | 1 + docs/channels/whatsapp.md | 26 +++++++++++++++++ docs/gateway/configuration.md | 16 ++++++++++ ...unauthorized-senders-not-allowfrom.test.ts | 29 +++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 187c8fbcc..05826d4b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Browser: add Chrome extension relay takeover mode (toolbar button), plus `clawdbot browser extension install/path` and remote browser control via `clawdbot browser serve` + `browser.controlToken`. - CLI/Docs: add per-command CLI doc pages and link them from `clawdbot --help`. - Browser: copy the installed Chrome extension path to clipboard after `clawdbot browser extension install/path`. +- WhatsApp: add `channels.whatsapp.sendReadReceipts` to disable auto read receipts. (#882) — thanks @chrisrodz. ### Fixes - Browser: add tests for snapshot labels/efficient query params and labeled image responses. diff --git a/docs/channels/telegram.md b/docs/channels/telegram.md index 86eebc24d..e74f220dd 100644 --- a/docs/channels/telegram.md +++ b/docs/channels/telegram.md @@ -93,6 +93,7 @@ group messages, so use admin if you need full visibility. - Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. - Replies always route back to the same Telegram chat. - Long-polling uses grammY runner with per-chat sequencing; overall concurrency is capped by `agents.defaults.maxConcurrent`. +- Telegram Bot API does not support read receipts; there is no `sendReadReceipts` option. ## Formatting (Telegram HTML) - Outbound Telegram text uses `parse_mode: "HTML"` (Telegram’s supported tag subset). diff --git a/docs/channels/whatsapp.md b/docs/channels/whatsapp.md index d2cafe96c..e66e18769 100644 --- a/docs/channels/whatsapp.md +++ b/docs/channels/whatsapp.md @@ -138,6 +138,32 @@ Behavior: - Self-chat mode (allowFrom includes your number) avoids auto read receipts and ignores mention JIDs. - Read receipts sent for non-self-chat DMs. +## Read receipts +By default, the gateway marks inbound WhatsApp messages as read (blue ticks) once they are accepted. + +Disable globally: +```json5 +{ + channels: { whatsapp: { sendReadReceipts: false } } +} +``` + +Disable per account: +```json5 +{ + channels: { + whatsapp: { + accounts: { + personal: { sendReadReceipts: false } + } + } + } +} +``` + +Notes: +- Self-chat mode always skips read receipts. + ## WhatsApp FAQ: sending messages + pairing **Will Clawdbot message random contacts when I link WhatsApp?** diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 589e5ade8..a1f90fd7d 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -430,6 +430,22 @@ For groups, use `channels.whatsapp.groupPolicy` + `channels.whatsapp.groupAllowF } ``` +### `channels.whatsapp.sendReadReceipts` + +Controls whether inbound WhatsApp messages are marked as read (blue ticks). Default: `true`. + +Self-chat mode always skips read receipts, even when enabled. + +Per-account override: `channels.whatsapp.accounts..sendReadReceipts`. + +```json5 +{ + channels: { + whatsapp: { sendReadReceipts: false } + } +} +``` + ### `channels.whatsapp.accounts` (multi-account) Run multiple WhatsApp accounts in one gateway: diff --git a/src/web/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test.ts b/src/web/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test.ts index 1b0f2d3b2..b1ce05c2d 100644 --- a/src/web/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test.ts +++ b/src/web/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test.ts @@ -210,6 +210,35 @@ describe("web monitor inbox", () => { await listener.close(); }); + it("skips read receipts when disabled", async () => { + const onMessage = vi.fn(); + const listener = await monitorWebInbox({ + verbose: false, + onMessage, + sendReadReceipts: false, + }); + const sock = await createWaSocket(); + + const upsert = { + type: "notify", + messages: [ + { + key: { id: "rr-off-1", fromMe: false, remoteJid: "222@s.whatsapp.net" }, + message: { conversation: "read receipts off" }, + messageTimestamp: 1_700_000_000, + }, + ], + }; + + sock.ev.emit("messages.upsert", upsert); + await new Promise((resolve) => setImmediate(resolve)); + + expect(onMessage).toHaveBeenCalledTimes(1); + expect(sock.readMessages).not.toHaveBeenCalled(); + + await listener.close(); + }); + it("lets group messages through even when sender not in allowFrom", async () => { mockLoadConfig.mockReturnValue({ channels: { whatsapp: { allowFrom: ["+1234"], groupPolicy: "open" } },