Fix/send webhook newsletter group#18
Conversation
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.
Reviewer's GuideImplements 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 subscriptionssequenceDiagram
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
Class diagram for telemetry service and configuration changesclassDiagram
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
Flow diagram for start.sh Docker bootstrap scriptflowchart 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]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The new GROUP/NEWSLETTER routing logic in
CallWebhookis duplicated acrossMESSAGE,SendMessage, andReceipt; consider extracting a small helper (e.g.,shouldForwardGroupOrNewsletter(data, subscriptions)) to centralize theChatparsing and suffix checks and reduce nested type assertions. - In
SendTelemetry, relying on string matching inerr.Error()to silence DNS/connection errors is brittle; consider checking specific error types (e.g., viaerrors.Isonnet.DNSError/syscall.ECONNREFUSED) for more robust filtering. - The
start.shscript usessed -iwithout a backup suffix, which behaves differently or fails on macOS/BSD sed; consider adding a portable branch (e.g., detect OS and adjustsed -iusage) 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| // 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") { |
There was a problem hiding this comment.
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:
- Ensure the following imports are present at the top of
pkg/telemetry/telemetry.go:errorsnetossyscall
- Remove the
stringsimport if it is only used in the removedstrings.Containschecks. - Confirm that this code is inside a function where
returncorrectly exits the telemetry send operation (e.g.SendTelemetry); if the function should not return here, adjust the control flow accordingly (e.g. replacereturnwith just a comment and let the function continue if appropriate).
| 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 |
There was a problem hiding this comment.
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.
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
Testing
Screenshots (if applicable)
Checklist
Additional Notes
Summary by Sourcery
Extend webhook routing and telemetry configurability while adding a Docker-based local environment setup.
New Features:
Enhancements:
Build:
Documentation: