b763ea48cf
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
107 lines
6.7 KiB
Markdown
107 lines
6.7 KiB
Markdown
# newwhats.clube67.com — Contrato de integração Motor ↔ Satélite
|
|
|
|
> O **motor** (newwhats) expõe a plataforma WhatsApp + Secretária IA; cada
|
|
> **satélite** (ex.: mercado.clube67.com / "Alemão Conveniências", plugin
|
|
> `newwhats` v2.1.0) consome o motor e fornece dados do seu negócio.
|
|
> Fonte da verdade do contrato (auditado em 28/jun/2026).
|
|
|
|
## ⚠️ Regra de registro bidirecional
|
|
- **Cada plugin satélite é ÚNICO do projeto onde está instalado** — o `newwhats`
|
|
do `mercado.clube67.com` pode divergir do de outro projeto. Não copiar cego.
|
|
- **Toda mudança no plugin** que toque o contrato deve ser registrada **no motor**
|
|
e nesta doc; **toda mudança no motor** que afete o contrato deve ser refletida no
|
|
doc do plugin (`INTEGRACAO-MOTOR.md`, dentro de cada plugin).
|
|
- O plugin é um **braço inteligente do motor**: consome, alimenta e configura — não
|
|
duplica lógica do motor.
|
|
|
|
## Satélites conhecidos
|
|
- **mercado.clube67.com** ("Alemão Conveniências") — plugin CommonJS no
|
|
PluginManager. Domínio: clubes/planos/sorteio/vagas/promoções.
|
|
- **scoreodonto.com** (28/jun/2026) — clonado e **adaptado ao odonto**. Módulo
|
|
**ESM** em `backend/newwhats/` (sem PluginManager; `registerNewwhats(app, pool)`
|
|
no `server.js`), config por ENV. `/nw/*` e `sync-knowledge` usam
|
|
clínica/especialidades/dentistas/procedimentos/planos (multi-tenant por
|
|
`clinica_id`). Removidos os recursos só-mercado e o `media-transcribe`. Em branch
|
|
`feat/newwhats-satelite` (sem deploy); falta parear com o motor (apiKey + agente)
|
|
e preencher as ENV. Ver `backend/newwhats/INTEGRACAO-MOTOR.md`.
|
|
|
|
## Docs relacionadas
|
|
- [tarefas.md](tarefas.md) — checklist mestre de pendências.
|
|
- [wasabi-midias.md](wasabi-midias.md) — storage, index e delete em lote.
|
|
- [ativacao-whatsapp.md](ativacao-whatsapp.md) — trajeto inteligente de ativação.
|
|
- `INTEGRACAO-MOTOR.md` — dentro de cada plugin satélite (lado do satélite).
|
|
|
|
## Config do satélite (plugin_configs, manifest `newwhats`)
|
|
`newwhats_url` (URL do motor) · `integration_key` (x-nw-key, rotas `/nw/*`) ·
|
|
`api_token` (Bearer, envio de mensagens) · `webhook_secret` (HMAC do webhook).
|
|
|
|
## Contrato A — Satélite → Motor (ext-api `/api/ext/v1/*`)
|
|
| Satélite chama | Motor (ext-api) | Status |
|
|
|---|---|---|
|
|
| `inbox`, `inbox/:chatId/messages`, `inbox/:chatId/send` | idem | ✅ |
|
|
| `media/:messageId` | `media/:messageId` | ✅ |
|
|
| `stream` (WS) | `stream` | ✅ |
|
|
| `secretaria/ask` | `secretaria/ask` (routes.ts) | ✅ |
|
|
| `secretaria/numbers` | **alias adicionado** → `sec_numbers` | ✅ (corrigido) |
|
|
| `secretaria/*` interno (`sync-knowledge`) | `/api/secretaria/*` (plugin) | ✅ |
|
|
|
|
> Demais dados da Secretária no ext-api ficam sob `/sec/*` (`sec/agent`,
|
|
> `sec/nodes`, `sec/slots`, `sec/conversations`, `sec/keys`, `sec/handoff`).
|
|
|
|
## Contrato B — Motor → Satélite
|
|
| Mecanismo | Satélite | Auth |
|
|
|---|---|---|
|
|
| Webhook `POST /api/webhook/newwhats` (`message.new`, `session.status`) | `webhook-receiver.js` | HMAC `webhook_secret` |
|
|
| Secretária consulta `/nw/*` (`/cliente`, `/clube`, `/parceiros`, `/promocoes`, `/sorteio`, `/vagas`, `/ping`) | `routes.js` | `integration_key` (timingSafeEqual) |
|
|
|
|
## O que o satélite já faz (e poderia migrar ao motor)
|
|
- `media-transcribe.js`: transcrição de áudio + descrição de imagem via Gemini
|
|
(cache Redis, fallback Whisper). **Hoje só no satélite** → candidato a ir ao motor.
|
|
- `sync-knowledge.js`: monta o nó de conhecimento com dados reais e envia ao motor.
|
|
- `smart-router.js` (roteamento sem LLM), `personas.js`, `follow-up-detector.js`,
|
|
`context-builder.js` (contexto local do negócio).
|
|
|
|
## Estado da sincronização (faseado)
|
|
1. ✅ **Contrato documentado** (este arquivo).
|
|
2. ✅ **Compatibilidade de API**: alias `GET /secretaria/numbers` no ext-api do
|
|
motor (retrocompatível) — fecha o gap do `webhook-receiver`. (commit motor)
|
|
3. ✅ **Config/conexão** (auditado 28/jun/2026, satélite `mercado`@VPS3 ↔ motor dev
|
|
`newwhats_dev`). Resultado:
|
|
- **Satélite → Motor (ext-api/proxy/WS)** ✅ — `proxy.js` envia `x-nw-key` =
|
|
`integration_key` do satélite; o ext-api valida contra a tabela `apiKey`. A
|
|
chave do satélite (`nw_t…_001`, len 34) **é igual** à `apiKey` ativa do motor.
|
|
**Teste vivo:** `GET /api/ext/v1/sessions` com a chave → **200**; sem chave /
|
|
chave inválida → **401** (correto).
|
|
- **Webhook Motor → Satélite (HMAC)** ✅ — `ext_webhooks` do motor: url
|
|
`http://127.0.0.1:4001/api/webhook/newwhats`, eventos `[message.new,
|
|
session.status]`, ativo, `secret` (len 64) **igual** ao `webhook_secret` do
|
|
satélite. Assinatura `x-nw-signature: sha256=HMAC` (`webhook-dispatcher.ts`).
|
|
- **Topologia co-locada** (`127.0.0.1`): satélite espera motor em `:8008`; motor
|
|
espera satélite em `:4001`. Para teste vivo ponta-a-ponta o **satélite precisa
|
|
rodar co-locado com o motor dev** (hoje não está rodando na VPS4).
|
|
- ⚠️ **`/nw/*` (Motor → Satélite, Secretária)**: o satélite valida o `x-nw-key`
|
|
entrante contra `plugin_configs.integration_key` (`nw_t…_001`); o `PluginPair`
|
|
do motor usa outra chave (`nwk_…1039`, `lastSeenAt=null`). Se esse sentido for
|
|
usado, **reconciliar a chave** (não afeta a Fase 4, que é Satélite → Motor).
|
|
- ⚠️ `api_token` não existe em `plugin_configs` do satélite — não é necessário: o
|
|
ext-api autentica por `x-nw-key` (= `integration_key`), não por Bearer separado.
|
|
4. ✅ **Conhecimento**: `sync-knowledge` rodado end-to-end (28/jun/2026). Driver
|
|
executou o `buildKnowledgeText` real do satélite (DB `mercado`@VPS3: 2 clubes,
|
|
4 planos, 6 promoções → 1895 chars) e publicou no motor dev via
|
|
`/api/secretaria` (nginx-dev :8022 → backend, rotas internas sem auth). Nó
|
|
`knowledge` **atualizado**. **RAG indexou**: `sec_knowledge_chunks` = 4 chunks
|
|
do nó, todos com embedding Gemini (768d). ✅ pipeline ok.
|
|
- ⚠️ A **geração final** do LLM (`gemini-2.0-flash:generateContent`) retorna
|
|
**429 quota** (free tier) — billing da chave Gemini, não defeito do pipeline.
|
|
- Corrigido de quebra: `/secretaria/ask` filtrava `prisma.chat` por `remoteJid`
|
|
(inexistente) → 500; agora usa `jid`.
|
|
5. **Mover recursos para o motor** (genéricos a todos os satélites):
|
|
- ✅ **Transcrição** (áudio→texto, imagem→descrição) já no motor via Gemini
|
|
(`messages.transcription`). O `media-transcribe.js` do satélite vira redundante.
|
|
- ⏳ memória (a secretária já tem memória por contato no motor; falta o satélite
|
|
deixar de duplicar). Satélite passa a só enviar contexto.
|
|
|
|
## Inconsistência anotada
|
|
A transcrição no satélite usa `process.env.GEMINI_API_KEY`; o RAG/memória do motor
|
|
usa `cfg.gemini_key` (config do plugin). Unificar a fonte da chave ao migrar.
|