Skip to content

feat(gateway): WhatsApp group authz + owner detection + reactions (fixes #7269) - #53623

Open
sdugoten wants to merge 5 commits into
NousResearch:mainfrom
sdugoten:feat/whatsapp-group-parity-owner-detection
Open

feat(gateway): WhatsApp group authz + owner detection + reactions (fixes #7269)#53623
sdugoten wants to merge 5 commits into
NousResearch:mainfrom
sdugoten:feat/whatsapp-group-parity-owner-detection

Conversation

@sdugoten

@sdugoten sdugoten commented Jun 27, 2026

Copy link
Copy Markdown

What & why

Fixes #7269 — in a WhatsApp group with require_mention: true, the bot only replies to senders already in WHATSAPP_ALLOWED_USERS; a normal group member who @mentions it is rejected. This brings WhatsApp to Telegram parity: any allowed-group member can talk to the bot, the bot knows which sender is the owner, and messages get 👀→✅ progress reactions — without changing the [name] sender prefix or any other platform's behavior.

The changes

1. Group-member authorizationgateway/authz_mixin.py
Adds WHATSAPP / WHATSAPP_CLOUDWHATSAPP_GROUP_ALLOWED_USERS to the group-allowlist dict, so WHATSAPP_GROUP_ALLOWED_USERS=* authorizes the whole group exactly like TELEGRAM_GROUP_ALLOWED_CHATS. Direct fix for the issue.

2. Owner detection (both transports)whatsapp_common.py, whatsapp_cloud.py, plugins/platforms/whatsapp/adapter.py
_set_owner_flag() resolves the group sender's LID to the owner via WHATSAPP_ALLOWED_USERS + the on-disk alias map and sets source.is_owner at intake. Called by both the Cloud adapter and the Baileys plugin adapter. A generic _is_owner() fallback covers the DM path; wildcard * is never an owner.

3. Surfacing the owner — gated, additive, no [name] changegateway/run.py, gateway/session.py

  • single-user sessions → an **Owner:** yes/no context line;
  • cache-shared group sessions → keep [name] and prepend an owner-only [SYSTEM: sender NAME is the owner] marker (guests byte-identical to upstream).

Both outputs are gated to WHATSAPP/WHATSAPP_CLOUD, so Telegram/Slack/Discord/etc. are completely unaffected. The gate is a source.platform in {...} set with a comment inviting other platforms to opt in once their adapter sets source.is_owner.

4. Reaction progress indicatorsplugins/platforms/whatsapp/adapter.py, scripts/whatsapp-bridge/bridge.js
Overrides the base on_processing_start / on_processing_complete hooks (which the gateway already calls for every platform): 👀 while processing, ✅/❌ when done, and clears the 👀 on cancel so it never lingers (parity with the Telegram adapter). New bridge endpoint POST /send-reaction (Baileys react; empty emoji removes). Disable with WHATSAPP_REACTIONS=false.

Scope notes

How to test

.env: WHATSAPP_GROUP_ALLOWED_USERS=*, WHATSAPP_ALLOWED_USERS=<owner phone/LID> (concrete, not *).
config.yaml: whatsapp: { require_mention: true }.

  1. A non-owner member @mentions the bot → it replies (previously rejected). (1)
  2. The owner's messages arrive marked [SYSTEM: sender … is the owner]; everyone else plain [name]. (2,3)
  3. Send a message → 👀 appears while processing, flips to ✅ on success. (4)

Platforms tested

macOS (Apple Silicon), Baileys bridge (bot mode). Owner-surfacing + reactions are additive/gated; non-WhatsApp adapters unaffected.

Related

@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery platform/whatsapp WhatsApp Business adapter P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jun 27, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to #53348 (the Signal-parity PR this mirrors for WhatsApp) — same authz/owner-detection pattern, different platform, so not a duplicate. Closed earlier WhatsApp-group attempts: #3636, #11597, #15413.

Fixes the issue where a WhatsApp group member is rejected under require_mention
unless they're in WHATSAPP_ALLOWED_USERS, and brings WhatsApp to Telegram parity.

- authz_mixin: WHATSAPP / WHATSAPP_CLOUD group-allowlist entry so
  WHATSAPP_GROUP_ALLOWED_USERS authorizes the group; generic _is_owner() fallback.
- whatsapp_common: _set_owner_flag() resolves the group sender (LID) to the owner
  via WHATSAPP_ALLOWED_USERS; called at intake by BOTH the Cloud adapter and the
  Baileys plugin adapter (plugins/platforms/whatsapp/adapter.py).
- run.py/session.py: owner surfaced WITHOUT changing the [name] prefix — a
  **Owner:** context line + an additive [SYSTEM: sender NAME is the owner] marker.
  Gated to WHATSAPP/WHATSAPP_CLOUD so other platforms stay byte-identical (other
  platforms can opt in by adding themselves to the set).
- plugin adapter + bridge.js: reaction progress indicators — 👀 while processing,
  ✅/❌ when done, cleared on cancel (parity with Telegram); WHATSAPP_REACTIONS=false
  to disable. New bridge POST /send-reaction.
@sdugoten
sdugoten force-pushed the feat/whatsapp-group-parity-owner-detection branch from 775ad47 to 94cbdee Compare June 27, 2026 13:05
@sdugoten sdugoten changed the title feat(gateway): authorize WhatsApp group members + owner detection (fixes #7269) feat(gateway): WhatsApp group authz + owner detection + reactions (fixes #7269) Jun 27, 2026
@sdugoten
sdugoten marked this pull request as ready for review June 27, 2026 13:32

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling WhatsApp group parity; the underlying intake path still needs work on current main.

Problems

  • scripts/whatsapp-bridge/bridge.js:637 drops a non-self sender not in WHATSAPP_ALLOWED_USERS before the Python gateway sees it, including a group member. This PR does not change that gate, so its gateway allowlist cannot fix the stated Baileys configuration.
  • WhatsApp Cloud currently drops group-shaped inbound payloads at gateway/platforms/whatsapp_cloud.py:1891-1899; the proposed Cloud authz/owner wiring cannot provide the claimed group behavior.
  • WHATSAPP_REACTIONS is a new user-facing non-secret environment setting. AGENTS.md requires behavioral settings to use config.yaml.
  • No regression test covers the bridge → adapter → gateway authorization path; existing group-policy coverage is adapter-level (tests/gateway/test_whatsapp_group_gating.py:262-272).

Suggested changes

  • Rework the Baileys intake gate around the existing whatsapp.group_policy / group_allow_from model, then add an end-to-end regression for an unlisted member mentioning the bot in an allowlisted group.
  • Scope the patch to Baileys until Cloud group intake is implemented, and place reaction configuration in config.yaml.

Automated hermes-sweeper review.

Comment thread gateway/authz_mixin.py
Platform.TELEGRAM: "TELEGRAM_GROUP_ALLOWED_CHATS",
Platform.QQBOT: "QQ_GROUP_ALLOWED_USERS",
Platform.WHATSAPP: "WHATSAPP_GROUP_ALLOWED_USERS",
Platform.WHATSAPP_CLOUD: "WHATSAPP_GROUP_ALLOWED_USERS",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WHATSAPP_CLOUD cannot use this group allowlist yet: current gateway/platforms/whatsapp_cloud.py:1891-1899 drops every group-shaped payload before gateway authz. Please omit the Cloud entry or implement Cloud group intake first.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
sdugoten and others added 2 commits July 15, 2026 12:58
…ope, bridge group gate, reactions config, e2e test

Four items from the hermes-sweeper review:

1. bridge.js group gate (scripts/whatsapp-bridge/bridge.js)
   The WHATSAPP_ALLOWED_USERS gate dropped non-self, non-allowlisted senders —
   including group members — before the Python gateway saw them, so a concrete
   allowlist (required for owner detection) meant only the owner could trigger
   the bot in a group. Gate is now DM-only; group messages pass through to the
   gateway, which authorizes via the documented whatsapp.group_policy /
   group_allow_from model and applies require_mention.

2. Scope to Baileys (gateway/platforms/whatsapp_cloud.py)
   WhatsApp Cloud drops group-shaped inbound earlier in the handler, so wiring
   owner detection there was dead code. Removed; documented to re-add once Cloud
   group intake exists. Baileys owner detection is unchanged.

3. Reactions setting -> config.yaml (plugins/platforms/whatsapp/adapter.py)
   WHATSAPP_REACTIONS is a behavioral setting; AGENTS.md requires config.yaml.
   Now read from whatsapp.reactions (legacy env kept as a fallback).

4. Regression test (tests/gateway/test_whatsapp_group_gating.py)
   Adds an end-to-end authorization test: an unlisted group member @mentioning
   the bot in an allowlisted group is processed by the adapter AND authorized by
   the gateway, while the same sender in a DM is denied.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	gateway/authz_mixin.py
#	gateway/session.py
#	scripts/whatsapp-bridge/bridge.js
sdugoten added 2 commits July 26, 2026 16:16
Signal DMs already resolve source.is_owner via the generic _is_owner()
fallback (SIGNAL_ALLOWED_USERS), but the two owner-marker opt-in gates
(the **Owner:** session-prompt line and the additive [SYSTEM: sender
NAME is the owner] message marker) only listed WHATSAPP/WHATSAPP_CLOUD.
Add Platform.SIGNAL to both gates so Signal gets the same owner
disclosure as WhatsApp once is_owner is set, and add regression tests
covering all three gated platforms plus the non-gated/shared-session
cases.
…-parity-owner-detection

# Conflicts:
#	gateway/run.py
#	tests/gateway/test_shared_group_sender_prefix.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/whatsapp WhatsApp Business adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Question] Whatsapp groups and require_mention: true, only reply to allowed users

3 participants