# 📱 WAHA Integration — Manual Tecnico

> Camada de infraestrutura para envio/recebimento de mensagens WhatsApp via WAHA (https://waha.devlike.pro/).
> Vive em `server/utils/waha.ts` + `server/api/waha/*` — fora do dominio de metadados (App.objects).

---

## 1. Posicionamento Arquitetural

WAHA e **Infrastructure Layer**, nao **Metadata Layer**. Segue o mesmo padrao de Prisma, Socket.IO e do `fastapp-agent`:

```mermaid
graph TB
    subgraph "Metadata (App JSON)"
        OBJ["app.objects[]<br/>(comunicado, lead, ...)"]
        WF["app.workflows[]"]
    end
    subgraph "Engine"
        DB["dynamic-db.ts"]
        FADAP["FADAP Protocol"]
    end
    subgraph "Infrastructure"
        WAHA["server/utils/waha.ts<br/>+ server/api/waha/*"]
        AGENT["fastapp-agent.post.ts"]
        SOCKET["socket.ts"]
    end
    OBJ --> DB
    WF -->|trigger| WAHA
    OBJ -.referencia.-> WAHA

    style WAHA fill:#25D366,color:#000
    style OBJ fill:#0891b2,color:#fff
```

A futura feature de "Painel de Comunicados" sera um Object normal em `app.objects[]` (ex: `comunicado` com properties `mensagem`, `target_object`, `scheduled_at`). Quando um comunicado for disparado, o handler chama os endpoints `/api/waha/messages/*` desta camada.

---

## 2. Configuracao de Ambiente

`.env`:

```env
WAHA_BASE_URL=http://localhost:3000
WAHA_API_KEY=
WAHA_WEBHOOK_TOKEN=
```

| Variavel | Obrigatorio | Descricao |
|---|---|---|
| `WAHA_BASE_URL` | Sim | URL do servidor WAHA. Self-hosted Docker default `http://localhost:3000`. |
| `WAHA_API_KEY` | Recomendado | Header `X-Api-Key` enviado em toda chamada. Configure tambem no servidor WAHA via `WHATSAPP_API_KEY`. |
| `WAHA_WEBHOOK_TOKEN` | Recomendado | Token compartilhado para validar webhooks recebidos. Configure no painel WAHA ao definir o hook URL. |

Mapeado em `nuxt.config.ts` como `runtimeConfig.waha.*` — **server-only**, nunca exposto ao cliente.

### Subir o servidor WAHA (Docker, dev)

```bash
docker run -it --rm \
  -p 3000:3000/tcp \
  -e WHATSAPP_API_KEY=sua-chave-aqui \
  --name waha \
  devlikeapro/waha
```

Acesse `http://localhost:3000/dashboard` para criar a primeira session.

---

## 3. Camadas

```
server/
├── utils/
│   └── waha.ts                              # Wrapper HTTP + tipos + helpers
└── api/
    └── waha/
        ├── sessions/
        │   ├── index.get.ts                 # GET    listar sessions
        │   ├── index.post.ts                # POST   criar/iniciar session
        │   ├── [name].get.ts                # GET    status detalhado
        │   ├── [name].delete.ts             # DELETE logout (apaga creds)
        │   └── [name]/
        │       ├── stop.post.ts             # POST   para session
        │       └── qr.get.ts                # GET    QR code para parear
        ├── messages/
        │   ├── text.post.ts                 # POST   enviar texto
        │   └── image.post.ts                # POST   enviar imagem
        ├── check-number.get.ts              # GET    validar se numero existe no WhatsApp
        └── webhook.post.ts                  # POST   recebe eventos do servidor WAHA
```

### Funcoes do wrapper (`server/utils/waha.ts`)

| Funcao | Descricao |
|---|---|
| `waha.listSessions()` | Lista todas as sessions do servidor |
| `waha.getSession(name)` | Status detalhado de uma session |
| `waha.startSession(name, config?)` | Cria/inicia session (proximo passo: ler QR) |
| `waha.stopSession(name)` | Para session (mantem credenciais) |
| `waha.logoutSession(name)` | Logout completo (apaga credenciais) |
| `waha.getQr(name, format?)` | QR code para parear (`image` base64 PNG ou `raw`) |
| `waha.sendText(session, payload)` | Envia texto |
| `waha.sendImage(session, payload)` | Envia imagem (URL ou base64) |
| `waha.sendFile(session, payload)` | Envia arquivo generico |
| `waha.checkNumber(session, phone)` | Valida se numero tem WhatsApp |
| `phoneToChatId(phone, cc?)` | Helper: `"11999999999"` -> `"5511999999999@c.us"` |
| `isWahaConfigured()` | Verifica se ambiente esta minimamente setado |

---

## 4. Endpoints Publicos (proxy)

Todos os endpoints sao **server-side proxies** — a API key WAHA nunca sai do servidor.

### Sessions

| Rota | Metodo | Body / Query | Retorno |
|---|---|---|---|
| `/api/waha/sessions` | GET | — | `WahaSession[]` |
| `/api/waha/sessions` | POST | `{ name, config? }` | `WahaSession` (status inicial) |
| `/api/waha/sessions/:name` | GET | — | `WahaSession` |
| `/api/waha/sessions/:name` | DELETE | — | logout |
| `/api/waha/sessions/:name/stop` | POST | — | para session |
| `/api/waha/sessions/:name/qr` | GET | `?format=image\|raw` | QR code |

### Mensagens

| Rota | Metodo | Body | Notas |
|---|---|---|---|
| `/api/waha/messages/text` | POST | `{ session, chatId? \| phone?, text, linkPreview?, reply_to? }` | Pelo menos um entre `chatId` e `phone` |
| `/api/waha/messages/image` | POST | `{ session, chatId? \| phone?, file: { url } \| { mimetype, filename, data }, caption? }` | Aceita URL publica ou base64 |
| `/api/waha/check-number` | GET | `?session=&phone=` | Util para limpar listas antes de campanhas |

### Webhook

| Rota | Metodo | Descricao |
|---|---|---|
| `/api/waha/webhook` | POST | Receptor de eventos (mensagens recebidas, ack, status). Validado via `?token=` ou header `X-Webhook-Token`. |

---

## 5. Ciclo de Pareamento (primeira vez)

```mermaid
sequenceDiagram
    participant Admin
    participant API as /api/waha
    participant WAHA as Servidor WAHA
    participant WhatsApp

    Admin->>API: POST /sessions { name: "default" }
    API->>WAHA: POST /api/sessions
    WAHA-->>API: { status: "STARTING" }
    API-->>Admin: status

    loop ate WORKING
        Admin->>API: GET /sessions/default/qr
        API->>WAHA: GET /api/default/auth/qr
        WAHA-->>API: PNG base64
        API-->>Admin: QR code
        Admin->>WhatsApp: escaneia
        WhatsApp->>WAHA: pareado
        Admin->>API: GET /sessions/default
        API-->>Admin: { status: "WORKING" }
    end
```

---

## 6. Exemplo de Envio (uso futuro pelo Painel de Comunicados)

```ts
// Server-side handler do "comunicado", em segunda fase:
await $fetch('/api/waha/messages/text', {
    method: 'POST',
    body: {
        session: 'default',
        phone: lead.whatsapp,           // sera normalizado via phoneToChatId
        text: comunicado.mensagem
    }
});
```

---

## 7. Como o "Painel de Comunicados" vai usar isso (segunda fase)

A feature sera construida **dentro** do padrao Fastapp:

1. **Object** `comunicado` em `app.objects[]` com properties:
   - `titulo` (string), `mensagem` (string, multiline)
   - `target_object` (string — qual entity recebe, ex: "lead")
   - `target_filter` (json — filtros tipo `{ status: "ATIVO" }`)
   - `channel` (enum: `whatsapp` | `email`) — preparado para multi-canal
   - `waha_session` (string — qual session usar)
   - `scheduled_at` (datetime)
   - `status` (enum: `draft` | `scheduled` | `sending` | `sent` | `failed`)

2. **Page** `comunicados` em `app.pages[]` (CRUD dinamico, gerado pelo motor)

3. **Workflow** opcional para aprovacao multi-step

4. **Disparador** (server handler novo, ex: `/api/comunicado/[id]/dispatch.post.ts`):
   - Le o objeto `comunicado`
   - Resolve a lista de `lead`s pelo `target_filter`
   - Para cada lead: `waha.sendText(session, { chatId: phoneToChatId(lead.whatsapp), text: comunicado.mensagem })`
   - Atualiza `status` e gera log por destinatario

A camada WAHA atual **nao precisa mudar** para isso ser feito — ela ja expoe tudo que e necessario.

---

## 8. Eventos do Webhook (referencia)

WAHA empurra eventos como:

| Evento | Disparo |
|---|---|
| `message` | Mensagem recebida (texto, midia, etc) |
| `message.ack` | Ack de entrega/leitura |
| `session.status` | Mudanca de estado da session |
| `state.change` | Mudanca de estado WhatsApp |
| `group.join` / `group.leave` | Participacao em grupo |

Configure no WAHA via env vars do container:

```env
WHATSAPP_HOOK_URL=https://creator.fastapp.cloud/api/waha/webhook?token=SEU_WEBHOOK_TOKEN
WHATSAPP_HOOK_EVENTS=message,message.ack,session.status
```

A logica de roteamento de eventos e um TODO da segunda fase — o handler atual valida o token e loga.

---

## 9. Limitacoes Conhecidas

- Esta camada **nao** persiste mensagens enviadas/recebidas. A persistencia (historico, threads) sera feita pelo Object `comunicado` na segunda fase.
- Sem rate limiting proprio — confie no rate limit do servidor WAHA + cuidado com WhatsApp ban (recomendado: ~1 msg/segundo por session).
- `phoneToChatId` assume Brasil (`+55`) por padrao — passe o segundo arg para outro DDI.

---

**Status**: Infraestrutura pronta. Painel de Comunicados (segunda fase) ainda nao implementado.
**Branch**: `feat/Waha`
