Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions docs/wiki/guias-api/api-messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Documentação completa dos endpoints para enviar e gerenciar mensagens WhatsApp
- [Marcar como Lida](#marcar-como-lida)
- [Editar Mensagem](#editar-mensagem)
- [Deletar Mensagem](#deletar-mensagem)
- [Encaminhar Mensagem](#encaminhar-mensagem)
- [Presença no Chat](#presença-no-chat)
- [Download de Mídia](#download-de-mídia)
- [Status da Mensagem](#status-da-mensagem)
Expand Down Expand Up @@ -933,6 +934,121 @@ curl -X POST http://localhost:4000/message/delete \

---

### Encaminhar Mensagem

Encaminha uma mensagem existente para outro chat, com o rótulo **"Encaminhada"** exibido no destinatário (igual ao encaminhamento nativo do WhatsApp).

**Endpoint**: `POST /message/forward`

**Body**:
```json
{
"number": "5511999999999",
"message": {
"extendedTextMessage": {
"text": "Texto da mensagem original"
}
},
"id": "msg-custom-123",
"delay": 1000,
"formatJid": true,
"forwardingScore": 1
}
```

**Parâmetros**:

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `number` | string | ✅ Sim | Destino do encaminhamento (usuário, grupo `@g.us` ou newsletter) |
| `message` | object | ✅ Sim | Payload completo da mensagem original (`waE2E.Message`) — o objeto `data.Message` recebido no webhook ou retornado pelos endpoints `/send/*` |
| `id` | string | ❌ Não | ID customizado para a cópia encaminhada |
| `delay` | int32 | ❌ Não | Delay em milissegundos ("digitando...") antes de enviar |
| `formatJid` | bool | ❌ Não | Formatar número automaticamente (padrão: true) |
| `forwardingScore` | uint32 | ❌ Não | Sobrescreve o score calculado (padrão: score original + 1; valores ≥ 5 exibem "Encaminhada muitas vezes") |

**Como funciona**:

- O `message` deve ser enviado **exatamente como recebido** no webhook (`data.Message`) ou no response de um `/send/*`. O endpoint clona o payload, descarta o contexto antigo (citações/menções do chat de origem) e carimba `isForwarded: true` + `forwardingScore`.
- **Mídia é encaminhada por referência** (`mediaKey`/`directPath` reaproveitados), sem download nem re-upload — por isso é importante enviar o objeto de mídia completo (`URL`, `mediaKey`, `directPath`, `fileSHA256` etc.).
- Tipos suportados: texto (`conversation`/`extendedTextMessage`), imagem, vídeo, PTV, áudio, documento, sticker, localização, contato, enquete, lista, botões e interactive.
- Mensagens **view-once** (visualização única) não podem ser encaminhadas (retorna erro).

**Resposta de Sucesso (200)**: mesmo formato dos endpoints `/send/*`:
```json
{
"message": "success",
"data": {
"Info": {
"ID": "3EB0C5A277F7F9B6C599",
"Timestamp": "2025-11-11T10:30:00Z",
"Type": "ExtendedTextMessage"
},
"Message": {
"extendedTextMessage": {
"text": "Texto da mensagem original",
"contextInfo": {
"forwardingScore": 1,
"isForwarded": true
}
}
}
}
}
```

**Respostas de Erro (400)**:
```json
{ "error": "phone number is required" }
```
```json
{ "error": "message payload is required" }
```
```json
{ "error": "view-once messages cannot be forwarded" }
```
```json
{ "error": "unsupported message type for forwarding" }
```

**Exemplo cURL (texto)**:
```bash
curl -X POST http://localhost:4000/message/forward \
-H "Content-Type: application/json" \
-H "apikey: SUA-CHAVE-API" \
-d '{
"number": "5511999999999",
"message": {
"extendedTextMessage": {
"text": "Teste encaminhamento 1"
}
}
}'
```

**Exemplo cURL (imagem recebida via webhook)**:
```bash
curl -X POST http://localhost:4000/message/forward \
-H "Content-Type: application/json" \
-H "apikey: SUA-CHAVE-API" \
-d '{
"number": "5511888888888",
"message": {
"imageMessage": {
"URL": "https://mmg.whatsapp.net/...",
"mimetype": "image/jpeg",
"fileSHA256": "...",
"fileLength": 12345,
"mediaKey": "...",
"fileEncSHA256": "...",
"directPath": "/v/..."
}
}
}'
```

---

### Presença no Chat

Define o status de presença no chat (digitando, gravando áudio, online).
Expand Down
5 changes: 3 additions & 2 deletions docs/wiki/referencia/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ http://localhost:4000

---

### Mensagens (16 endpoints)
### Mensagens (17 endpoints)

**Envio:**
- `POST /send/text` - Mensagem de texto
Expand All @@ -59,6 +59,7 @@ http://localhost:4000
- `POST /message/markread` - Marcar como lida
- `POST /message/edit` - Editar mensagem
- `POST /message/delete` - Deletar mensagem
- `POST /message/forward` - Encaminhar mensagem (com rótulo "Encaminhada")
- `POST /message/presence` - Status de presença (digitando/gravando)
- `POST /message/downloadmedia` - Download de mídia
- `POST /message/status` - Status de entrega/leitura
Expand Down Expand Up @@ -179,6 +180,6 @@ http://localhost:4000

---

**Total de endpoints disponíveis: 79**
**Total de endpoints disponíveis: 80**

**Documentação Evolution GO v1.0**
18 changes: 13 additions & 5 deletions pkg/instance/service/instance_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,20 @@ func (i instances) GetQr(instance *instance_model.Instance) (*QrcodeStruct, erro
logger := i.loggerWrapper.GetLogger(instance.Id)
client := i.clientPointer[instance.Id]

// Se não há cliente ou o cliente está logado, precisamos iniciar um novo cliente
if client == nil || client.IsLoggedIn() {
if client != nil && client.IsLoggedIn() {
logger.LogInfo("[%s] Client is logged in, starting new instance for QR code", instance.Id)
} else {
// Se o cliente já está logado, NÃO reiniciar — apenas informar. Chamar StartInstance
// numa sessão logada derruba o whatsmeow (Disconnected → novo QR), o que pode ser
// disparado por polling do frontend logo após PairSuccess, quebrando a conexão.
if client != nil && client.IsLoggedIn() {
logger.LogInfo("[%s] Client is already logged in — returning 'session already logged in' (StartInstance skipped to avoid disconnect)", instance.Id)
return nil, fmt.Errorf("session already logged in")
}

// Se não há cliente OU cliente sem sessão logada, iniciar/reiniciar para gerar QR
if client == nil || (!client.IsConnected() && !client.IsLoggedIn()) {
if client == nil {
logger.LogInfo("[%s] No client found, starting new instance for QR code", instance.Id)
} else {
logger.LogInfo("[%s] Client exists but not connected/logged, restarting for QR code", instance.Id)
}

// Iniciar nova instância para gerar QR code
Expand Down
Loading