Skip to content

feat(events): forward Picture, UserAbout and BusinessName webhook events#94

Open
walterdiazesa wants to merge 1 commit into
evolution-foundation:developfrom
walterdiazesa:feat/forward-picture-userabout-businessname
Open

feat(events): forward Picture, UserAbout and BusinessName webhook events#94
walterdiazesa wants to merge 1 commit into
evolution-foundation:developfrom
walterdiazesa:feat/forward-picture-userabout-businessname

Conversation

@walterdiazesa

@walterdiazesa walterdiazesa commented Jun 27, 2026

Copy link
Copy Markdown

Description

Three whatsmeow events emitted by go.mau.fi/whatsmeow are silently dropped by the dispatcher in pkg/whatsmeow/service/whatsmeow.go because the central switch has no case for them. They only surface in stdout as Unhandled event *events.X and never reach consumers.

This adds the three missing handlers plus the matching subscribe categories so they can be received via webhook / queue / websocket. It mirrors the existing *events.PushName handling for the three new events.

Events

Event Source Fires when
*events.Picture types/events/events.go A user's profile picture or a group's photo changes. Fields: JID, Author, Timestamp, Remove, PictureID.
*events.UserAbout types/events/events.go A user's about/status text changes. Fields: JID, Status, Timestamp.
*events.BusinessName types/events/appstate.go A contact's verified business name changes, lazily on inbound messages. Fields: JID, OldBusinessName, NewBusinessName.

Changes

  1. pkg/whatsmeow/service/whatsmeow.go (myEventHandler): three new cases mirroring the existing *events.Contact / *events.PushName handlers. They set doWebhook = true and postMap["event"]; the raw event is already attached via postMap["data"] = rawEvt.
  2. pkg/whatsmeow/service/whatsmeow.go (CallWebhook): three new branches so consumers can subscribe to PICTURE / USER_ABOUT / BUSINESS_NAME independently, following the existing per-category split (e.g. MESSAGE vs SEND_MESSAGE vs READ_RECEIPT).
  3. pkg/internal/event_types/event_types.go: the three new constants in the const block, in AllEventTypes, and in validEventTypes so IsEventType accepts them.

The change is additive: existing subscribers keep receiving exactly what they did before. Consumers wanting the new events opt in by including PICTURE / USER_ABOUT / BUSINESS_NAME (or ALL) in their subscribe list.

Use cases

  • Picture: keep a CRM avatar cache in sync in real time instead of polling /chat/fetchProfilePictureUrl/{instance} per contact, which has no batch endpoint and risks rate-flagging on large sweeps.
  • UserAbout: surface each contact's status text in contact cards / chat lists without polling.
  • BusinessName: reflect a contact's verified business name after they edit it on their phone. Today the only signal is the Connected re-emit, which carries the user's own pushName, not the contact's verified business name.

Type of Change

  • New feature (non-breaking change which adds functionality)

Testing

  • go build ./... passes against develop (whatsmeow-lib pinned at 0923702).
  • go vet is clean on the two touched packages.
  • Additive only: no behavior change to existing events; the new events require explicit opt-in.

Notes

Reopened against develop (previously #57, which targeted main).

Summary by Sourcery

Forward additional whatsmeow profile-related events to downstream consumers via existing webhook/queue mechanisms.

New Features:

  • Expose Picture, UserAbout, and BusinessName events through the generic event handler so they can be delivered over webhooks, queues, and websockets.
  • Allow clients to subscribe specifically to PICTURE, USER_ABOUT, and BUSINESS_NAME event categories via CallWebhook routing.
  • Recognize PICTURE, USER_ABOUT, and BUSINESS_NAME as valid event types in the shared event type registry.

@sourcery-ai

sourcery-ai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR wires three previously unhandled Whatsmeow events (Picture, UserAbout, BusinessName) into the existing event dispatching and subscription system so they can be forwarded through webhooks/queues/websockets, mirroring existing PushName-style handling and extending the internal event type registry accordingly.

Sequence diagram for forwarding new Picture/UserAbout/BusinessName events

sequenceDiagram
    participant WhatsmeowClient as MyClient
    participant Service as whatsmeowService
    participant Sink as QueueOrWebhook
    participant Consumer

    WhatsmeowClient->>WhatsmeowClient: myEventHandler(rawEvt *events.Picture)
    Note over WhatsmeowClient: doWebhook = true<br/>postMap[event] = Picture<br/>postMap[data] = rawEvt
    WhatsmeowClient->>Service: CallWebhook(instance, queueName, jsonData, eventType=Picture)

    Service->>Service: contains(subscriptions, PICTURE)
    alt [subscription includes PICTURE]
        Service->>Sink: sendToQueueOrWebhook(instance, queueName, jsonData)
        Sink-->>Consumer: deliver Picture event
    else [no PICTURE subscription]
        Service-->>WhatsmeowClient: return without forwarding
    end

    %% The same flow applies to UserAbout/BusinessName with
    %% eventType UserAbout/BusinessName and subscriptions USER_ABOUT/BUSINESS_NAME
Loading

File-Level Changes

Change Details Files
Forward Picture/UserAbout/BusinessName whatsmeow events through the main event handler to downstream delivery.
  • Extend myEventHandler switch to handle *events.Picture, *events.UserAbout, and *events.BusinessName cases.
  • For each new event type, set doWebhook = true and populate postMap["event"] with the appropriate event string name.
  • Rely on existing postMap["data"] = rawEvt behavior so the raw event payload is included in the webhook body.
pkg/whatsmeow/service/whatsmeow.go
Allow consumers to subscribe to and receive Picture/UserAbout/BusinessName events via CallWebhook.
  • Add new CallWebhook switch branches for eventType values "Picture", "UserAbout", and "BusinessName".
  • For each new branch, gate dispatch on contains(subscriptions, "PICTURE"/"USER_ABOUT"/"BUSINESS_NAME") respectively, mirroring existing category-based filtering.
  • Log receipt of the event and forward jsonData via sendToQueueOrWebhook when the subscription matches.
pkg/whatsmeow/service/whatsmeow.go
Register new PICTURE, USER_ABOUT, and BUSINESS_NAME event type constants in the internal event type registry.
  • Introduce PICTURE, USER_ABOUT, and BUSINESS_NAME constants in the event type const block.
  • Include the new constants in the AllEventTypes slice so they are exposed to callers iterating over available event types.
  • Mark the new constants as valid in validEventTypes so IsEventType returns true for them.
pkg/internal/event_types/event_types.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The event type strings ("Picture", "UserAbout", "BusinessName") are duplicated across myEventHandler and CallWebhook; consider centralizing them (e.g. constants or a shared mapping) to reduce the risk of future typos or mismatches.
  • For the new subscription checks in CallWebhook, you might want to reuse the event type constants from pkg/internal/event_types (or a shared enum-like mapping) instead of hardcoding "PICTURE", "USER_ABOUT", and "BUSINESS_NAME" here to keep subscription keys consistent across the codebase.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The event type strings (`"Picture"`, `"UserAbout"`, `"BusinessName"`) are duplicated across `myEventHandler` and `CallWebhook`; consider centralizing them (e.g. constants or a shared mapping) to reduce the risk of future typos or mismatches.
- For the new subscription checks in `CallWebhook`, you might want to reuse the event type constants from `pkg/internal/event_types` (or a shared enum-like mapping) instead of hardcoding `"PICTURE"`, `"USER_ABOUT"`, and `"BUSINESS_NAME"` here to keep subscription keys consistent across the codebase.

## Individual Comments

### Comment 1
<location path="pkg/internal/event_types/event_types.go" line_range="19-21" />
<code_context>
 	NEWSLETTER    = "NEWSLETTER"
 	QRCODE        = "QRCODE"
 	BUTTON_CLICK  = "BUTTON_CLICK"
+	PICTURE       = "PICTURE"
+	USER_ABOUT    = "USER_ABOUT"
+	BUSINESS_NAME = "BUSINESS_NAME"
 )

</code_context>
<issue_to_address>
**🚨 question (security):** Double-check that exposing PICTURE/USER_ABOUT/BUSINESS_NAME as event types aligns with privacy expectations.

Since these fields can now flow through webhooks/queues, please confirm they’re acceptable to treat as event payloads from a privacy/compliance perspective (e.g., sensitivity, redaction needs, integrator consent). It may also be worth checking how we handle similar profile events today and aligning with those safeguards for consistency.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +19 to +21
PICTURE = "PICTURE"
USER_ABOUT = "USER_ABOUT"
BUSINESS_NAME = "BUSINESS_NAME"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 question (security): Double-check that exposing PICTURE/USER_ABOUT/BUSINESS_NAME as event types aligns with privacy expectations.

Since these fields can now flow through webhooks/queues, please confirm they’re acceptable to treat as event payloads from a privacy/compliance perspective (e.g., sensitivity, redaction needs, integrator consent). It may also be worth checking how we handle similar profile events today and aligning with those safeguards for consistency.

The dispatcher in pkg/whatsmeow/service/whatsmeow.go dropped three events
emitted by go.mau.fi/whatsmeow because the central switch had no case for
them, logging "Unhandled event" instead of forwarding them:

- events.Picture: a user's profile picture or a group's photo changes
  (JID, Author, Timestamp, Remove, PictureID).
- events.UserAbout: a user's about/status text changes (JID, Status,
  Timestamp).
- events.BusinessName: a contact's verified business name changes (JID,
  OldBusinessName, NewBusinessName).

Add the three handler cases in myEventHandler, the matching branches in
CallWebhook, and the PICTURE, USER_ABOUT and BUSINESS_NAME subscription
categories in pkg/internal/event_types so IsEventType accepts them.

The change is additive: existing subscribers are unaffected, and the new
events require opting in via the subscribe list (or ALL).
@walterdiazesa walterdiazesa force-pushed the feat/forward-picture-userabout-businessname branch from 6b73624 to 7f649d6 Compare June 27, 2026 03:25
@walterdiazesa

Copy link
Copy Markdown
Author

(Already addressed in #57.)

On centralizing the event strings: the whole dispatcher already uses string literals for this, both the postMap["event"] = "..." lines in myEventHandler and the case "..." / contains(subscriptions, "...") checks in CallWebhook. The event_types constants are only used to validate subscribe lists through IsEventType, not in the dispatch path. I kept the three new events in that same style so the file stays consistent. Moving everything to shared constants is a fair cleanup, but it touches every existing event in both functions, so it fits better as its own repo-wide PR than a partial change here.

On the privacy question: this is the same class of profile data the dispatcher already forwards through Contact and PushName over the same path, and delivery is opt-in, so a consumer only receives it after subscribing to PICTURE/USER_ABOUT/BUSINESS_NAME (or ALL). No new exposure beyond what ships today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant