Skip to content

Fix(chat) enviar history sync request como peer para o próprio jid, não para o chat do contato - #150

Open
iagocotta wants to merge 3 commits into
evolution-foundation:developfrom
iagocotta:fix(chat)--enviar-HistorySyncRequest-como-peer-para-o-próprio-JID,-não-para-o-chat-do-contato

Hidden character warning

The head ref may contain hidden characters: "fix(chat)--enviar-HistorySyncRequest-como-peer-para-o-pr\u00f3prio-JID,-n\u00e3o-para-o-chat-do-contato"
Open

Fix(chat) enviar history sync request como peer para o próprio jid, não para o chat do contato#150
iagocotta wants to merge 3 commits into
evolution-foundation:developfrom
iagocotta:fix(chat)--enviar-HistorySyncRequest-como-peer-para-o-próprio-JID,-não-para-o-chat-do-contato

Conversation

@iagocotta

@iagocotta iagocotta commented Jul 31, 2026

Copy link
Copy Markdown

Problema

chatService.HistorySyncRequest envia a requisição de history sync sob demanda (peer data operation do tipo HISTORY_SYNC_ON_DEMAND) para o JID do chat do contato em vez do próprio JID do cliente. Consequência: o WhatsApp descarta a requisição silenciosamente e nenhum HistorySyncNotification chega — o caller recebe 200 OK, o cliente whatsmeow loga o SendMessage como enviado, mas nenhum webhook de history sync é disparado.

Important

O history sync do pareamento inicial (bootstrap) funciona porque o próprio whatsmeow usa o roteamento correto internamente. Só o endpoint sob demanda POST /chat/history-sync está quebrado.

Causa raiz

pkg/chat/service/chat_service.go:237

res, err := client.SendMessage(context.Background(),
    messageInfo.Chat,     // <-- JID do chat do contato
    histRequest,
    whatsmeow.SendRequestExtra{Peer: true})

Peer messages que carregam PeerDataOperationRequestMessage (ex: history sync sob demanda) precisam ser roteadas para o próprio device do cliente (own JID), não para um contato. É exatamente isso que o helper upstream faz:

// go.mau.fi/whatsmeow/send.go
func (cli *Client) SendPeerMessage(ctx context.Context, message *waE2E.Message) (SendResponse, error) {
    ownID := cli.getOwnID().ToNonAD()
    if ownID.IsEmpty() {
        return SendResponse{}, ErrNotLoggedIn
    }
    return cli.SendMessage(ctx, ownID, message, SendRequestExtra{Peer: true})
}

Enviar uma peer message de protocolo para um JID de contato não é uma operação válida: o roteamento do WhatsApp descarta a mensagem e, como não há resposta, nenhum HistorySyncNotification é gerado. Sem a notificação, nenhum events.HistorySync é despachado para o event handler, então nenhum webhook é emitido.

Reprodução

  1. POST /chat/history-sync com qualquer messageInfo válido — o endpoint retorna 200
  2. Habilitar HISTORY_SYNC na lista de subscriptions
  3. Observar: nenhum webhook HistorySync é entregue, nunca
  4. No log level DEBUG do whatsmeow, vê-se a mensagem sendo enviada como peer para o JID do contato e sendo ackada, mas sem notificação de follow-up

Correção

Usar client.SendPeerMessage(ctx, histRequest) — o helper upstream que garante que a requisição vai para o próprio JID como peer message:

- res, err := client.SendMessage(context.Background(),
-     messageInfo.Chat,
-     histRequest,
-     whatsmeow.SendRequestExtra{Peer: true})
+ res, err := client.SendPeerMessage(context.Background(), histRequest)

Fluxo após o fix

  1. A requisição é roteada corretamente como peer para o próprio device
  2. WhatsApp responde com HistorySyncNotification referenciando o media handle
  3. handleHistorySyncNotificationLoop do whatsmeow baixa o blob via DownloadHistorySync e despacha events.HistorySync{Data: blob}
  4. O case *events.HistorySync: já existente no Evolution-Go (em pkg/whatsmeow/service/whatsmeow.go) emite o webhook HistorySync com o payload das conversations

Impacto

  • Nenhuma mudança de API — assinatura do endpoint e schema de resposta ficam iguais
  • Nenhuma mudança de config necessária
  • Consumidores que estavam vendo no-ops silenciosos do POST /chat/history-sync passam a receber webhooks HistorySync (é preciso ter HISTORY_SYNC na lista de subscribe)
  • O campo messageInfo.Chat do input continua sendo usado — ele é passado dentro de HistorySyncOnDemandRequest.ChatJID pelo BuildHistorySyncRequest, que é o lugar correto para ele. Ele apenas não deve ser o destino de roteamento da mensagem externa

Testado

  • Chamada de POST /chat/history-sync com messageInfo válido numa sessão que tinha mensagens mais antigas que a âncora
  • Antes do fix: endpoint retornava 200 mas nenhum webhook HistorySync chegou
  • Depois do fix: HistorySyncNotification chegou em 2–5 s e o webhook HistorySync foi entregue com as conversations reconstruídas

Summary by Sourcery

Fix on-demand history sync routing and introduce unified send/forward message service with new forwarding API and docs.

New Features:

  • Add unified send message service with support for text, links, media, polls, stickers, locations, contacts, buttons, lists, carousels, status updates, and message forwarding.
  • Expose a new POST /message/forward API to forward existing messages, including media by reference, and document it in the API reference and messages guide.

Bug Fixes:

  • Route on-demand history sync requests as peer messages to the client’s own JID so HistorySync notifications and webhooks are correctly delivered.
  • Prevent QR code generation from restarting an already logged-in instance, avoiding unintended disconnects triggered by frontend polling.

Enhancements:

  • Add robust connection management with retry and reconnection logic around message sending operations, including better logging and newsletter-specific media upload handling.
  • Support forwarding score and forwarded labels across various message types, including lists, buttons, and interactive messages, while preserving or normalizing context info.
  • Improve media handling with thumbnail generation for images and PDFs, audio conversion utilities, and optional in-webhook base64 media embedding.
  • Wire the new send/forward handlers into the HTTP router with input validation middleware and Swagger annotations for better API discoverability.

Documentation:

  • Extend API documentation to cover the new message forwarding endpoint, its payload, behavior, and error cases, and update endpoint counts and listings in the API reference.

@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.

Sorry @iagocotta, your pull request is larger than the review limit of 150000 diff characters

@sourcery-ai

sourcery-ai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Reviewer's Guide

Fixes history sync peer routing, adds a complete message sending service layer (including a new /message/forward endpoint and many interactive/Status helpers), adjusts QR/instance behavior when already logged in, and wires new send routes + docs updates.

Sequence diagram for fixed history sync peer routing

sequenceDiagram
    actor User
    participant API as EvolutionAPI
    participant ChatSvc as chatService
    participant WClient as whatsmeow.Client
    participant WA as WhatsApp
    participant WHLoop as handleHistorySyncNotificationLoop
    participant Webhook

    User->>API: POST /chat/history-sync
    API->>ChatSvc: HistorySyncRequest(data, instance)
    ChatSvc->>WClient: BuildHistorySyncRequest(messageInfo, count)
    ChatSvc->>WClient: SendPeerMessage(ctx, histRequest)
    WClient->>WA: HISTORY_SYNC_ON_DEMAND (peer to own JID)
    WA-->>WClient: HistorySyncNotification
    WClient->>WHLoop: enqueue notification
    WHLoop->>WClient: DownloadHistorySync(handle)
    WHLoop-->>Webhook: events.HistorySync{Data: blob}
Loading

Sequence diagram for the new /message/forward flow

sequenceDiagram
    actor Client
    participant API as EvolutionAPI
    participant Handler as SendHandler
    participant Service as SendService
    participant WClient as whatsmeow.Client

    Client->>API: POST /message/forward
    API->>Handler: SendForward(ctx)
    Handler->>Handler: validate Number, Message
    Handler->>Service: SendForward(data, instance)
    Service->>Service: normalizeForwardMessage(message)
    Service->>Service: forwardMessageType(message)
    Service->>Service: SendMessage(instance, message, messageType, sendData)
    Service->>WClient: SendMessage(ctx, recipient, msg, SendRequestExtra)
    WClient-->>Service: SendResponse
    Service-->>Handler: MessageSendStruct
    Handler-->>Client: 200 OK + forwarded message info
Loading

File-Level Changes

Change Details Files
Route on-demand HistorySyncRequest peer messages to the client’s own JID so WhatsApp delivers HistorySyncNotification and webhooks fire correctly.
  • Replace SendMessage(peer=true) targeting messageInfo.Chat with SendPeerMessage using client’s own JID for HISTORY_SYNC_ON_DEMAND requests.
  • Preserve HistorySyncOnDemandRequest.ChatJID usage for chat selection while fixing only the outer routing target.
pkg/chat/service/chat_service.go
Prevent QR generation from restarting an already-logged-in client instance, avoiding unintended disconnect/reconnect loops.
  • Short-circuit GetQr when client exists and IsLoggedIn, returning an explicit 'session already logged in' error instead of calling StartInstance.
  • Change client (re)start condition to only when no client or client is neither connected nor logged in, and improve log messages to distinguish branches.
pkg/instance/service/instance_service.go
Introduce a centralized SendService abstraction that encapsulates all message sending logic, media handling, forwarding, interactive messages, and status posts.
  • Create send_service.go implementing SendService with methods for text, links (with metadata fetch), media (URL/file, audio conversion, thumbnails, newsletter-specific upload), polls, stickers, locations, contacts, buttons, lists, carousels, status updates, and forwarding.
  • Implement robust client connection management with ensureClientConnected(+retry) and integrate whatsmeow reconnection and user-existence checks.
  • Add common SendMessage pipeline that handles JID validation/formatting, quoting, mentions, forwardingScore/isForwarded, additional biz/bot nodes, media download for webhooks, and webhook/global-queue dispatch.
  • Add extensive helpers for thumbnails (images/PDF), audio conversion (local ffmpeg or external API), WebP stickers, Pix payment payloads, and interactive NativeFlow/Carousel building with proper ContextInfo/MessageSecret for iOS/Web compatibility.
sendMessage/service/send_service.go
Expose the new sending capabilities (including message forwarding and status posting) via HTTP handlers and wire them into the main router.
  • Add send_handler.go defining Gin handlers for /send/* endpoints (text, link, media, poll, sticker, location, contact, button, list, carousel, status text/media) and a new /message/forward endpoint that takes a full waE2E.Message payload.
  • Support both JSON and multipart/form-data for media & status-media, including base64 media in the JSON case, and validate required fields per endpoint.
  • Extend routes.go to register the new send handlers and /message/forward, plugging them behind existing auth and JID validation middleware.
sendMessage/handler/send_handler.go
routes/routes.go
Document the new message forwarding endpoint and update API reference counts/links.
  • Add 'Encaminhar Mensagem' section to api-messages.md describing POST /message/forward, its body schema (including forwardingScore semantics) and example success/error payloads.
  • Update api-reference.md messages section to include /message/forward and bump total endpoint count from 79 to 80.
docs/wiki/guias-api/api-messages.md
docs/wiki/referencia/api-reference.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

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