Skip to content

Fix/send webhook newsletter group#18

Open
oismaelash wants to merge 4 commits intoEvolutionAPI:mainfrom
oismaelash:fix/send-webhook-newsletter-group
Open

Fix/send webhook newsletter group#18
oismaelash wants to merge 4 commits intoEvolutionAPI:mainfrom
oismaelash:fix/send-webhook-newsletter-group

Conversation

@oismaelash
Copy link
Copy Markdown

@oismaelash oismaelash commented Apr 8, 2026

Description

Implementação de filtros adicionais na função CallWebhook para permitir o encaminhamento de eventos de mensagens ( Message , SendMessage ) e recibos ( Receipt ) com base nas assinaturas de GROUP e NEWSLETTER .

Anteriormente, esses eventos só eram encaminhados se a assinatura principal (como MESSAGE , SEND_MESSAGE ou READ_RECEIPT ) estivesse presente. Com esta mudança, se a assinatura principal estiver ausente, o sistema verifica se o evento se originou de um grupo ( @g.us ) ou newsletter ( @newsletter ) e o encaminha se o usuário possuir a assinatura correspondente ( GROUP ou NEWSLETTER ).

Related Issue

Closes #(issue_number)

Type of Change

  • [] Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement

Testing

  • Manual testing completed
  • Functionality verified in development environment
  • No breaking changes introduced

Screenshots (if applicable)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have tested my changes thoroughly
  • Any dependent changes have been merged and published

Additional Notes

Summary by Sourcery

Extend webhook routing and telemetry configurability while adding a Docker-based local environment setup.

New Features:

  • Route message, send-message, and receipt events to webhooks based on GROUP and NEWSLETTER subscriptions when primary event subscriptions are absent.
  • Introduce a TELEMETRY_ENABLED configuration flag to toggle telemetry collection at runtime.
  • Provide a docker-compose stack and startup script to bootstrap Evolution Go with PostgreSQL, RabbitMQ, and MinIO via a single command.

Enhancements:

  • Suppress noisy telemetry network errors for common DNS and connection-refused failures.
  • Document the new Docker-based startup flow in the README.

Build:

  • Add docker-compose.yml and a start.sh helper script to simplify containerized development and deployment.

Documentation:

  • Update README with instructions for starting the project using the new Docker helper script.

Update README to recommend using the new start.sh script for easier Docker setup.
Add docker-compose.yml to define all required services (PostgreSQL, RabbitMQ, MinIO).
Add start.sh script to automate submodule initialization, environment configuration, and container startup.
…mage tag

Remove explicit image tag for evolution-go service to rely on build context
Standardize container names by removing project prefix (evolution-)
Add POSTGRES_DB environment variable for postgres service
Introduce TELEMETRY_ENABLED environment variable to control telemetry collection.
When disabled, middleware skips telemetry entirely. When enabled, common network
errors (DNS lookup failures, connection refused) are silently ignored to avoid
log spam in restricted environments.
Extend webhook event filtering to support GROUP and NEWSLETTER subscriptions
for Message, SendMessage, and Receipt events. When the main subscription
(e.g., MESSAGE) is not present, check if the event originates from a group
or newsletter chat and forward it if the corresponding subscription exists.
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 8, 2026

Reviewer's Guide

Implements group/newsletter-aware webhook dispatch when primary event subscriptions are missing, adds configurable telemetry with improved error handling, and introduces a Docker-based bootstrap flow (docker-compose + start.sh) wired via a new TelemetryEnabled config flag and updated README instructions.

Sequence diagram for webhook dispatch with group and newsletter subscriptions

sequenceDiagram
    participant EventSource
    participant whatsmeowService
    participant LoggerWrapper
    participant QueueOrWebhook

    EventSource->>whatsmeowService: CallWebhook(instance, queueName, eventType, data)

    alt eventType is MESSAGE or SendMessage or Receipt
        whatsmeowService->>whatsmeowService: contains(subscriptions, primarySubscription)
        alt primary subscription present
            whatsmeowService->>LoggerWrapper: LogInfo([instance.Id] Event received type eventType)
            whatsmeowService->>QueueOrWebhook: sendToQueueOrWebhook(instance, queueName, jsonData)
        else primary subscription missing
            whatsmeowService->>whatsmeowService: extract chat from data["data"].Info.Chat or data["data"].Chat
            alt chat has suffix @g.us and contains(subscriptions, GROUP)
                whatsmeowService->>LoggerWrapper: LogInfo([instance.Id] Event received type eventType (Group))
                whatsmeowService->>QueueOrWebhook: sendToQueueOrWebhook(instance, queueName, jsonData)
            else chat has suffix @newsletter and contains(subscriptions, NEWSLETTER)
                whatsmeowService->>LoggerWrapper: LogInfo([instance.Id] Event received type eventType (Newsletter))
                whatsmeowService->>QueueOrWebhook: sendToQueueOrWebhook(instance, queueName, jsonData)
            else
                whatsmeowService-->>EventSource: do not forward event
            end
        end
    else other eventType
        whatsmeowService-->>EventSource: existing handling
    end
Loading

Class diagram for telemetry service and configuration changes

classDiagram
    class TelemetryService {
        <<interface>>
        TelemetryMiddleware() gin.HandlerFunc
    }

    class telemetryService {
        bool enabled
        TelemetryMiddleware() gin.HandlerFunc
    }

    class Config {
        bool TelemetryEnabled
        %% other fields
    }

    class telemetry_pkg {
        SendTelemetry(route string) void
        NewTelemetryService(enabled bool) TelemetryService
    }

    class main_pkg {
        setupRouter(db *gorm.DB, authDB *sql.DB, sqliteDB *sql.DB, sqliteDB2 *sql.DB, config *config.Config) *gin.Engine
    }

    TelemetryService <|.. telemetryService
    telemetry_pkg ..> TelemetryService : returns
    telemetry_pkg ..> telemetryService : constructs

    Config "1" --> "1" telemetryService : configures via TelemetryEnabled

    main_pkg ..> Config : reads TelemetryEnabled
    main_pkg ..> TelemetryService : uses
    main_pkg ..> telemetry_pkg : calls NewTelemetryService
Loading

Flow diagram for start.sh Docker bootstrap script

flowchart TD
    A[start.sh] --> B[Update git submodules
    git submodule update --init --recursive]
    B --> C{Submodules OK?}
    C -- no --> Z[Exit with error
    check git installation]
    C -- yes --> D[Check if .env exists]
    D --> E{.env present?}
    E -- no --> F[Copy .env.example to .env]
    F --> G[Adjust hosts for Docker
    replace localhost:5432 with postgres:5432
    replace localhost:5672 with rabbitmq:5672
    replace localhost:9000 with minio:9000]
    E -- yes --> H[Keep existing .env]

    G --> I[Detect docker compose command]
    H --> I

    I --> J{docker-compose or docker compose found?}
    J -- no --> Y[Exit with error
    install Docker Compose]
    J -- yes --> K[Run docker compose up -d --build]

    K --> L{Startup successful?}
    L -- no --> X[Exit with error
    starting containers]
    L -- yes --> M[Print service URLs and log hints]
Loading

File-Level Changes

Change Details Files
Extend CallWebhook routing to forward MESSAGE, SendMessage, and Receipt events based on GROUP/NEWSLETTER subscriptions when primary event subscriptions are absent.
  • Add secondary routing logic that inspects data["data"]["Info"]["Chat"] or data["data"]["Chat"] to detect group (@g.us) and newsletter (@newsletter) contexts
  • Gate secondary routing on presence of GROUP or NEWSLETTER in instance subscriptions
  • Add specialized log messages for group/newsletter events before forwarding to queue/webhook
pkg/whatsmeow/service/whatsmeow.go
Make telemetry opt-out via config and avoid noisy logging in restricted environments.
  • Extend telemetryService with an enabled flag and short-circuit middleware when disabled
  • Change NewTelemetryService constructor to accept an enabled parameter and wire it from Config.TelemetryEnabled in main setup
  • Silence common DNS/connection-refused telemetry errors by checking error text before logging
pkg/telemetry/telemetry.go
cmd/evolution-go/main.go
pkg/config/config.go
pkg/config/env/env.go
Introduce Docker-based bootstrap workflow and document it.
  • Add docker-compose.yml defining evolution-go, postgres, rabbitmq, and minio services with persistent volumes and shared network
  • Create start.sh helper script that initializes git submodules, creates/adjusts .env from .env.example for container hosts, and runs docker compose up -d --build
  • Update README Docker section to recommend start.sh usage and briefly describe what it does
docker-compose.yml
start.sh
README.md

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

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The new GROUP/NEWSLETTER routing logic in CallWebhook is duplicated across MESSAGE, SendMessage, and Receipt; consider extracting a small helper (e.g., shouldForwardGroupOrNewsletter(data, subscriptions)) to centralize the Chat parsing and suffix checks and reduce nested type assertions.
  • In SendTelemetry, relying on string matching in err.Error() to silence DNS/connection errors is brittle; consider checking specific error types (e.g., via errors.Is on net.DNSError/syscall.ECONNREFUSED) for more robust filtering.
  • The start.sh script uses sed -i without a backup suffix, which behaves differently or fails on macOS/BSD sed; consider adding a portable branch (e.g., detect OS and adjust sed -i usage) to avoid setup issues on non-Linux environments.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new GROUP/NEWSLETTER routing logic in `CallWebhook` is duplicated across `MESSAGE`, `SendMessage`, and `Receipt`; consider extracting a small helper (e.g., `shouldForwardGroupOrNewsletter(data, subscriptions)`) to centralize the `Chat` parsing and suffix checks and reduce nested type assertions.
- In `SendTelemetry`, relying on string matching in `err.Error()` to silence DNS/connection errors is brittle; consider checking specific error types (e.g., via `errors.Is` on `net.DNSError`/`syscall.ECONNREFUSED`) for more robust filtering.
- The `start.sh` script uses `sed -i` without a backup suffix, which behaves differently or fails on macOS/BSD sed; consider adding a portable branch (e.g., detect OS and adjust `sed -i` usage) to avoid setup issues on non-Linux environments.

## Individual Comments

### Comment 1
<location path="pkg/telemetry/telemetry.go" line_range="61-62" />
<code_context>
 	resp, err := http.Post(url, "application/json", bytes.NewBuffer(data))
 	if err != nil {
-		log.Println("Erro ao enviar telemetria:", err)
+		// Silence DNS lookup errors or connection refused as they are common in restricted environments
+		if !strings.Contains(err.Error(), "no such host") && !strings.Contains(err.Error(), "connection refused") {
+			log.Println("Erro ao enviar telemetria:", err)
+		}
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Error filtering based on substrings in `err.Error()` is brittle and may miss or misclassify network errors.

Using `strings.Contains(err.Error(), "no such host")` / `"connection refused"` hard-codes behavior to specific message text, which can differ by OS, Go version, or localization and may change over time. This can cause relevant errors to be hidden or misclassified. Prefer checking concrete error types instead, e.g. using `errors.Is` with `syscall.ECONNREFUSED` or asserting to `*net.OpError` / `net.Error` and inspecting the underlying cause so DNS/connection issues are handled reliably without relying on message strings.

Suggested implementation:

```golang
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(data))
	if err != nil {
		// Silence expected DNS/connection errors (common in restricted environments),
		// but log unexpected failures so issues are still visible.
		var opErr *net.OpError
		if errors.As(err, &opErr) {
			// DNS resolution failures (e.g. unknown host / no such host)
			if _, ok := opErr.Err.(*net.DNSError); ok {
				return
			}

			// Connection refused and similar low-level network issues
			if sysErr, ok := opErr.Err.(*os.SyscallError); ok && errors.Is(sysErr, syscall.ECONNREFUSED) {
				return
			}
		}

		log.Println("Erro ao enviar telemetria:", err)

```

To make this compile and fully integrate:

1. Ensure the following imports are present at the top of `pkg/telemetry/telemetry.go`:
   - `errors`
   - `net`
   - `os`
   - `syscall`
2. Remove the `strings` import if it is only used in the removed `strings.Contains` checks.
3. Confirm that this code is inside a function where `return` correctly exits the telemetry send operation (e.g. `SendTelemetry`); if the function should not return here, adjust the control flow accordingly (e.g. replace `return` with just a comment and let the function continue if appropriate).
</issue_to_address>

### Comment 2
<location path="start.sh" line_range="28-30" />
<code_context>
+        cp .env.example .env
+        
+        # Automatically adjust for Docker Compose
+        sed -i 's/localhost:5432/postgres:5432/g' .env
+        sed -i 's/localhost:5672/rabbitmq:5672/g' .env
+        sed -i 's/localhost:9000/minio:9000/g' .env
+        
+        echo -e "${GREEN}.env file created from .env.example and adjusted for Docker!${NC}\n"
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `sed -i` without a backup suffix is not portable between GNU and BSD/macOS sed.

On GNU sed `-i` works without an argument, but BSD/macOS sed requires a suffix (e.g. `-i ''`). This will cause the script to fail on macOS. Consider either branching on the OS to choose the correct `sed -i` invocation, or avoiding `-i` by writing to a temporary file and moving it back to `.env` for portability.
</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 +61 to +62
// Silence DNS lookup errors or connection refused as they are common in restricted environments
if !strings.Contains(err.Error(), "no such host") && !strings.Contains(err.Error(), "connection refused") {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Error filtering based on substrings in err.Error() is brittle and may miss or misclassify network errors.

Using strings.Contains(err.Error(), "no such host") / "connection refused" hard-codes behavior to specific message text, which can differ by OS, Go version, or localization and may change over time. This can cause relevant errors to be hidden or misclassified. Prefer checking concrete error types instead, e.g. using errors.Is with syscall.ECONNREFUSED or asserting to *net.OpError / net.Error and inspecting the underlying cause so DNS/connection issues are handled reliably without relying on message strings.

Suggested implementation:

	resp, err := http.Post(url, "application/json", bytes.NewBuffer(data))
	if err != nil {
		// Silence expected DNS/connection errors (common in restricted environments),
		// but log unexpected failures so issues are still visible.
		var opErr *net.OpError
		if errors.As(err, &opErr) {
			// DNS resolution failures (e.g. unknown host / no such host)
			if _, ok := opErr.Err.(*net.DNSError); ok {
				return
			}

			// Connection refused and similar low-level network issues
			if sysErr, ok := opErr.Err.(*os.SyscallError); ok && errors.Is(sysErr, syscall.ECONNREFUSED) {
				return
			}
		}

		log.Println("Erro ao enviar telemetria:", err)

To make this compile and fully integrate:

  1. Ensure the following imports are present at the top of pkg/telemetry/telemetry.go:
    • errors
    • net
    • os
    • syscall
  2. Remove the strings import if it is only used in the removed strings.Contains checks.
  3. Confirm that this code is inside a function where return correctly exits the telemetry send operation (e.g. SendTelemetry); if the function should not return here, adjust the control flow accordingly (e.g. replace return with just a comment and let the function continue if appropriate).

Comment on lines +28 to +30
sed -i 's/localhost:5432/postgres:5432/g' .env
sed -i 's/localhost:5672/rabbitmq:5672/g' .env
sed -i 's/localhost:9000/minio:9000/g' .env
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Using sed -i without a backup suffix is not portable between GNU and BSD/macOS sed.

On GNU sed -i works without an argument, but BSD/macOS sed requires a suffix (e.g. -i ''). This will cause the script to fail on macOS. Consider either branching on the OS to choose the correct sed -i invocation, or avoiding -i by writing to a temporary file and moving it back to .env for portability.

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