Skip to content

fix(chat): enable chat operations and make mute duration configurable#36

Open
edilsonoliveirama wants to merge 3 commits intoEvolutionAPI:mainfrom
edilsonoliveirama:feat/fix-chat-operations
Open

fix(chat): enable chat operations and make mute duration configurable#36
edilsonoliveirama wants to merge 3 commits intoEvolutionAPI:mainfrom
edilsonoliveirama:feat/fix-chat-operations

Conversation

@edilsonoliveirama
Copy link
Copy Markdown

@edilsonoliveirama edilsonoliveirama commented Apr 20, 2026

O que foi investigado

Os seis endpoints de chat (POST /chat/pin, /unpin, /archive, /unarchive, /mute, /unmute) estavam todos marcados como // TODO: not working no router.

Após rastrear o caminho completo da execução — do handler ao service e ao SendAppState do whatsmeow — a conclusão é:

A implementação estava correta. Os endpoints funcionam em qualquer sessão WhatsApp estabelecida que tenha sincronizado as app state keys (ou seja, qualquer sessão normal após o login por QR). Os comentários // TODO parecem ter sido adicionados após testes em sessão recém-criada, onde o servidor WhatsApp ainda não havia distribuído as keys — causando o erro "no app state keys found". Essa é uma limitação da biblioteca whatsmeow para criação de novas keys, não um bug no evolution-go.

O que foi alterado

pkg/routes/routes.go

Removidos os seis comentários // TODO: not working. Os endpoints são funcionais para sessões estabelecidas.

pkg/chat/service/chat_service.go

  • Adicionado campo opcional duration (int64, segundos) no BodyStruct.
  • ChatMute agora usa duration do request em vez do hardcoded 1 * time.Hour.
  • Enviar duration: 0 (ou omitir o campo) silencia o chat indefinidamente — consistente com o comportamento do próprio WhatsApp.

pkg/chat/handler/chat_handler.go

Atualizada a descrição Swagger do POST /chat/mute para documentar o campo duration.

O que NÃO foi corrigido (e por quê)

Se uma sessão nunca recebeu app state keys do servidor WhatsApp (ex: sessão nova ou corrompida), os seis endpoints ainda retornarão erro. Isso é uma limitação conhecida do whatsmeow (// TODO create new key instead of reusing the primary client's keys em appstate.go). O erro agora é surfaceado claramente ao caller via HTTP 500.

Breaking changes

Nenhum. O campo duration em /chat/mute é opcional e tem default 0 (silenciar para sempre), que é um default melhor do que a hora hardcoded anterior.

Summary by Sourcery

Add a standalone manager dashboard with real-time instance metrics and enable configurable mute duration for chat operations.

New Features:

  • Introduce a dedicated /manager dashboard page showing live WhatsApp instance metrics and status, separate from the existing React manager bundle.

Enhancements:

  • Make chat mute duration configurable via an optional duration field in the mute request body instead of using a fixed one-hour value.
  • Clean up chat routes by removing outdated not-working comments from pin, unpin, archive, unarchive, mute, and unmute endpoints.

Build:

  • Update the Docker image to include the new manager dashboard assets alongside the existing manager bundle.

Documentation:

  • Extend the Swagger description of the /chat/mute endpoint to document the new duration parameter.

The /manager dashboard previously showed only a static placeholder
("Dashboard content will be implemented here..."). This replaces it
with a standalone HTML page that fetches live data from the API and
displays real metrics:

- Total instances count
- Connected instances count and percentage
- Disconnected instances count
- Server health status (GET /server/ok)
- AlwaysOnline count
- Instance table with name, status badge, phone number, client and
  AlwaysOnline indicator
- Auto-refresh every 30 seconds with manual refresh button

Implementation uses a standalone HTML file (Tailwind CDN + vanilla JS
fetch) served at GET /manager, keeping the existing compiled bundle
intact for all other routes (/manager/instances, /manager/login, etc.).

Changes:
- manager/dashboard/index.html: new self-contained dashboard page
- pkg/routes/routes.go: serve dashboard/index.html for GET /manager
  (exact), keep dist/index.html for GET /manager/*any (wildcard)
- Dockerfile: copy manager/dashboard/ into the final image
- .gitignore: exclude manager build artifacts from version control

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 20, 2026

Reviewer's Guide

Enables and documents fully functional chat management endpoints while making chat mute duration configurable, and adds a new static dashboard page with routing and Docker packaging updates.

Sequence diagram for configurable chat mute operation

sequenceDiagram
    actor Caller
    participant API as GinRouter
    participant Handler as chatHandler
    participant Service as chatService
    participant Instance as instance_model_Instance
    participant WAClient as WhatsAppClient

    Caller->>API: POST /chat/mute { chat, optional duration }
    API->>Handler: ChatMute(ctx)
    Handler->>Service: ChatMute(data *BodyStruct, instance *instance_model_Instance)
    Service->>Service: validate chat JID
    alt invalid chat
        Service-->>Handler: error "invalid phone number"
        Handler-->>API: HTTP 400
        API-->>Caller: HTTP 400
    else valid chat
        Service->>WAClient: SendAppState(appstate.BuildMute(recipient, true, durationSeconds * time.Second))
        alt SendAppState success
            Service-->>Handler: success
            Handler-->>API: HTTP 200
            API-->>Caller: HTTP 200
        else SendAppState error
            Service-->>Handler: error
            Handler-->>API: HTTP 500
            API-->>Caller: HTTP 500
        end
    end
Loading

Class diagram for updated chat service types

classDiagram
    class BodyStruct {
        string Chat
        int64 Duration
    }

    class HistorySyncRequestStruct {
        string Page
        string Cursor
        string Limit
    }

    class chatService {
        +ChatMute(data *BodyStruct, instance *instance_model_Instance) string
        +ChatPin(data *BodyStruct, instance *instance_model_Instance) string
        +ChatUnpin(data *BodyStruct, instance *instance_model_Instance) string
        +ChatArchive(data *BodyStruct, instance *instance_model_Instance) string
        +ChatUnarchive(data *BodyStruct, instance *instance_model_Instance) string
        +ChatUnmute(data *BodyStruct, instance *instance_model_Instance) string
        +HistorySyncRequest(data *HistorySyncRequestStruct, instance *instance_model_Instance) string
    }

    class instance_model_Instance {
        string Id
        string Jid
        bool Connected
        bool AlwaysOnline
        string ClientName
    }

    chatService --> BodyStruct : uses
    chatService --> HistorySyncRequestStruct : uses
    chatService --> instance_model_Instance : operates_on
Loading

File-Level Changes

Change Details Files
Adjust manager routing and static assets to serve a new dashboard page separately from the existing React SPA bundle.
  • Serve /manager as a standalone static dashboard HTML instead of the React SPA index
  • Serve all other /manager/* routes with the original React SPA index to preserve client-side routing
  • Update Dockerfile to copy the new dashboard build directory into the runtime image
pkg/routes/routes.go
Dockerfile
Treat chat pin/archive/mute endpoints as production-ready by removing misleading TODO markers.
  • Remove // TODO: not working comments from chat operation routes to reflect current working behavior
pkg/routes/routes.go
Make chat mute duration configurable via the API while keeping backwards compatibility.
  • Extend chat service request body with an optional integer duration field in seconds used for mute operations
  • Compute mute duration from the request payload instead of using a hardcoded 1-hour duration
  • Rely on zero or omitted duration to represent muting indefinitely, matching WhatsApp behavior
pkg/chat/service/chat_service.go
Update API documentation for the mute endpoint to describe the new duration parameter semantics.
  • Extend Swagger description for POST /chat/mute to explain duration values, units, and examples
pkg/chat/handler/chat_handler.go
Add a new HTML-based dashboard showing real-time instance metrics using existing HTTP APIs.
  • Introduce a static Tailwind-based dashboard HTML page under manager/dashboard with layout, sidebar, and metrics cards
  • Implement client-side JavaScript that calls /instance/all and /server/ok using an API key from localStorage
  • Render aggregate metrics and a status table for instances, with periodic auto-refresh and basic error states
manager/dashboard/index.html

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 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="pkg/chat/service/chat_service.go" line_range="189-190" />
<code_context>
 	}

-	err = client.SendAppState(context.Background(), appstate.BuildMute(recipient, true, 1*time.Hour))
+	muteDuration := time.Duration(data.Duration) * time.Second
+	err = client.SendAppState(context.Background(), appstate.BuildMute(recipient, true, muteDuration))
 	if err != nil {
 		c.loggerWrapper.GetLogger(instance.Id).LogError("[%s] error mute chat: %v", instance.Id, err)
</code_context>
<issue_to_address>
**issue:** Clarify and guard mute duration semantics (0/negative and very large values).

`data.Duration` is passed straight into `time.Duration(data.Duration) * time.Second` and then to `BuildMute` with no validation or special handling.

Please explicitly:
- Reject `Duration < 0` with a validation error.
- Map `Duration == 0` to whatever `BuildMute`/WhatsApp expects for “mute forever” (rather than `0 * time.Second` if that does not mean forever).
- Consider clamping excessively large values if the underlying API has practical limits.

This will align the implementation with the documented semantics and avoid surprising behavior when the field is omitted or misused.
</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 thread pkg/chat/service/chat_service.go
Removes the '// TODO: not working' markers from the six chat endpoints
(pin, unpin, archive, unarchive, mute, unmute). Investigation confirmed
the implementation is correct: the endpoints work on fully-established
sessions that have synced WhatsApp app state keys. The markers were
likely added after testing on a fresh session where keys had not yet
been distributed by the WhatsApp server.

Also fixes the hardcoded 1-hour mute duration: the BodyStruct now
accepts an optional `duration` field (seconds). Sending 0 or omitting
the field mutes the chat indefinitely, matching WhatsApp's own behaviour.
@edilsonoliveirama edilsonoliveirama force-pushed the feat/fix-chat-operations branch from 79948c6 to bd9f000 Compare April 20, 2026 16:52
Reject negative duration values with a 400-level validation error.
Document that duration=0 maps to 'mute forever' (BuildMute treats 0
as a zero time.Duration, which causes BuildMuteAbs to set the
WhatsApp sentinel timestamp of -1).
Clamp duration to a maximum of 1 year (31536000 seconds) to avoid
unreasonably large timestamps being sent to the WhatsApp API.
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