← Back to PRs

#16655: fix(whatsapp): resolve reply-to sender E.164 for LID JIDs (have bot see replys without a mention even with "requireMention: true", like the Discord integration)

by mascarenhas open 2026-02-15 00:11 View on GitHub →
channel: whatsapp-web stale size: S
WhatsApp quoted replies can identify the quoted sender using LID JIDs (e.g. `123@lid`). When reverse mapping is missing, we must not treat the raw JID as an E.164. - Avoid falling back to raw JID for `senderE164` in `describeReplyContext` - Resolve reply-to sender E.164 via `lidLookup` in the inbound monitor - Add unit tests for `describeReplyContext` ## Summary - **Problem:** In WhatsApp group chats, quoted replies can set `replyToSenderJid` to a LID JID (`...@lid`). When reverse mapping is missing, the code previously propagated the raw JID as `senderE164`, which later got normalized into a bogus `+<digits>` and broke implicit “reply-to-bot counts as mention” gating. - **Why it matters:** Users replying to the bot’s message (without an explicit @mention) were not activating the bot, even though reply-to-bot is intended to count as an implicit mention. - **What changed:** - `describeReplyContext()` no longer falls back to the raw JID for `senderE164` when E.164 cannot be resolved. - WhatsApp inbound monitor now attempts to resolve the quoted sender (`replyContext.senderJid`) to E.164 via `resolveJidToE164(..., { lidLookup })` and overwrites `senderE164/sender` when available. - Added unit tests to lock in the corrected behavior. - **What did NOT change (scope boundary):** No changes to mention regex matching, group policy defaults, config formats, or outbound sending. This is limited to WhatsApp inbound reply metadata normalization/resolution. ## Change Type (select all) - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Docs - [ ] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [x] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [x] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR None. ## User-visible / Behavior Changes - WhatsApp group messages that **reply/quote** a bot message are more likely to trigger the bot as an implicit mention even when the quoted sender is identified via `@lid` (when `lidLookup` can resolve it). - Prevents bogus `replyToSenderE164` values derived from LID JIDs (e.g. `262...@lid` → `+262...`) from interfering with gating logic. ## Security Impact (required) - New permissions/capabilities? (`No`) - Secrets/tokens handling changed? (`No`) - New/changed network calls? (`No`)\* - Command/tool execution surface changed? (`No`) - Data access scope changed? (`No`) \*Uses an existing Baileys `lidLookup` facility already present in the WhatsApp monitor runtime; this change only applies it to quoted-reply sender resolution. ## Repro + Verification ### Environment - OS: Linux (local dev) - Runtime/container: Node.js (OpenClaw gateway) - Model/provider: N/A (pre-LLM gating/normalization) - Integration/channel (if any): WhatsApp Web (Baileys) - Relevant config (redacted): group chats with `requireMention: true` ### Steps 1. Run OpenClaw gateway logged into WhatsApp Web. 2. In a WhatsApp group with mention-gating enabled (`requireMention: true`), send a message as the bot identity. 3. From another account, **reply/quote** that bot message without an explicit @mention. ### Expected - The reply should count as an implicit mention and proceed through normal processing (i.e., not be skipped as “no mention detected”). ### Actual (before) - The reply could be skipped when `replyToSenderJid` was a LID JID and reverse mapping was missing (implicit mention computed as false). ## Evidence - [x] Failing log before + passing behavior after (locally observed with verbose logs; reply-to-bot implicit mention was false before, true after when LID resolves) - [x] Unit test(s) added and passing: - `pnpm test:fast` includes `src/web/inbound/extract.test.ts` (new) ## Human Verification (required) - Verified scenarios: - Unit tests for `describeReplyContext`: - LID JID without reverse mapping does **not** populate `senderE164` - PN JID populates `senderE164` correctly - Edge cases checked: - Quoted sender present vs absent - What you did **not** verify: - Full end-to-end WhatsApp group activation via CI (relies on local reproduction / unit tests) - Behavior when `lidLookup` itself is unavailable (falls back to existing behavior; no E.164 resolution) ## Compatibility / Migration - Backward compatible? (`Yes`) - Config/env changes? (`No`) - Migration needed? (`No`) ## Failure Recovery (if this breaks) - How to disable/revert this change quickly: - Revert this commit. - Known bad symptoms reviewers should watch for: - `ReplyToSenderE164` unexpectedly becoming undefined for quoted replies where previously it was set to a raw `@lid` JID (intentional; it was not a real E.164). ## Risks and Mitigations - Risk: Slight additional async work on messages that contain quoted reply metadata (attempting LID→PN resolution). - Mitigation: Only runs when `replyContext.senderJid` exists; otherwise no behavior change. - Risk: Downstream code expecting `replyToSenderE164` to be populated with *something* even when not resolvable. - Mitigation: We still preserve `replyToSenderJid` and `replyToSender` for context; we avoid populating misleading E.164 values. ## AI-assisted disclosure This PR was **AI-assisted** (authored with help from Codex/OpenAI tooling). I validated the change by reproducing the issue locally (verbose gateway logs) and by adding/running unit tests (`src/web/inbound/extract.test.ts`). Reviewers should focus on the WhatsApp inbound reply-context normalization (`src/web/inbound/extract.ts` + `src/web/inbound/monitor.ts`) and the new test coverage. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes a bug where WhatsApp quoted replies with LID JIDs (e.g. `26285350879314@lid`) were incorrectly propagated as E.164 phone numbers, breaking the implicit "reply-to-bot counts as mention" gating in groups with `requireMention: true`. - **`extract.ts`**: `describeReplyContext()` no longer falls back to the raw JID for `senderE164` — it's now `undefined` when `jidToE164()` can't resolve the JID, preventing bogus `+<digits>` numbers from being created downstream via `normalizeE164`. - **`monitor.ts`**: Adds async LID→E.164 resolution for the quoted reply sender using the existing `resolveInboundJid` helper (backed by `lidLookup`). This correctly resolves LID JIDs when the Baileys signal repository has the mapping available. - **`extract.test.ts`**: New unit tests covering LID JID (no reverse mapping → `senderE164` is `undefined`) and PN JID (correctly extracts E.164) scenarios. The changes are well-scoped to inbound reply metadata normalization and do not affect mention regex matching, group policy defaults, or outbound sending. <h3>Confidence Score: 4/5</h3> - This PR is safe to merge — it's a well-scoped bug fix with correct logic and new test coverage. - The changes are minimal and targeted: two lines changed in extract.ts (removing an incorrect fallback), a ~12-line async resolution block added in monitor.ts using existing infrastructure, and new unit tests. The logic is sound — verified by tracing through jidToE164, resolveJidToE164, normalizeE164, and the downstream group-gating implicit mention detection. The defensive guard in monitor.ts (lines 261-264) is effectively unreachable after the extract.ts fix but is harmless. Score is 4 rather than 5 because the monitor.ts changes introduce an additional async call per message with reply context, which has a small performance surface, and because the defensive guard is dead code that could be cleaned up. - No files require special attention. All changes are straightforward and well-tested. <sub>Last reviewed commit: d4812e6</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs