Revisão de PR · he4rt/wpp-tui #1

Coletor de
WhatsApp

Um bot que escuta os grupos da comunidade e repassa cada acontecimento para a plataforma. Este documento explica o que o PR entrega e, principalmente, por que cada decisão foi tomada.

8
commits
31
testes ✓
typecheck
limpo
data lake
filosofia
01

O cenário

A He4rt precisa de dados da vida nos grupos do WhatsApp — quem fala, o que rola, quem entra e sai — para alimentar relatórios e métricas. O bot é um observador: não responde nada, só escuta e entrega. A inteligência (o que guardar, como agregar) mora na plataforma.

💬
WhatsApp
grupos da comunidade
🤖
Bot (este PR)
escuta e repassa
🗄️
Plataforma
guarda e analisa
02

O pipeline de coleta

Todo acontecimento do WhatsApp passa por uma esteira até virar uma requisição segura para a plataforma. Cada etapa tem um propósito: filtrar ruído, focar em grupos, e garantir que nada se perca nem duplique.

  1. 1
    Evento do WhatsApp — mensagem, reação, presença, entrada/saída…
  2. 2
    Denylist — descarta o que não interessa (credenciais, sync em massa, recibos)
  3. 3
    Só grupos — conversa privada (DM) é ignorada
  4. 4
    Fan-out + extração — 1 evento por item, com identidade única (UUIDv5)
  5. 5
    Outbox durável — fila em disco (SQLite/WAL) com reenvio automático e backoff
  6. 6
    POST assinado (HMAC) → plataforma confere a assinatura e deduplica pelo ID

A fila ser durável é o que garante a entrega: se a plataforma estiver fora do ar, os eventos esperam em disco e são reenviados — sem perda.

03

As decisões — e o porquê

O coração da revisão. Cada escolha abaixo tem um motivo e um trade-off assumido.

Decisão

Data lake puro

Por quê: o bot manda o dado cru; a plataforma decide o que persistir. Mantém o bot simples e dá liberdade pra plataforma evoluir sem mexer no bot.

trade-off: a plataforma assume a regra de negócio.

Decisão

ID único = UUIDv5

Por quê: o banco da plataforma guarda numa coluna uuid nativa — um hash comum de 64 caracteres era rejeitado. O UUIDv5 é um UUID válido e determinístico (mesmo evento → mesmo ID → dedup).

trade-off: nenhum — mantém a dedup, sem lib extra.

Decisão

Recibos fora (denylist)

Por quê: recibo de "lido/entregue" gera 1 evento por destinatário — em grupo grande, inunda a tabela sem valor de coleta.

trade-off: perdemos rastro fino de leitura (aceito).

Decisão

Snapshot do grupo

Por quê: nome, descrição, membros e admins vinham por uma chamada de API (não evento) e não chegavam à plataforma. Agora viram um evento groups.metadata, com dedup por conteúdo.

trade-off: reenvio a cada conexão (deduplicado).

Decisão · operação

Pronto pra produção: nível de log + limpeza

Por quê: nada apagava os logs — o disco encheria em prod. Agora o volume é controlado por configuração (LOG_LEVEL) e há limpeza automática por idade. Padrão mantém o comportamento antigo (compatível); produção liga via .env.

04

Correções de robustez

Três bugs sutis encontrados e corrigidos no caminho — cada um com causa-raiz investigada, não remendo.

QR de login
não escaneava
antes
módulos escuros como blocos → invertido em terminal escuro → ilegível pro scanner
depois
fundo branco/preto forçado → escaneável em qualquer tema
Extração de logs
quebrava no parse
antes
lia o arquivo como 1 JSON só → estourava ao virar 1-objeto-por-linha
depois
lê NDJSON linha a linha, com compatibilidade ao formato antigo
Conexão (SOCKET_URL)
URL inválida
antes
variável vazia no .env passava string vazia → crash ao conectar
depois
valor vazio cai no padrão do WhatsApp
05

O que ganhamos

Coleta confiável da atividade dos grupos, sem perda nem duplicata.

Nome, membros e admins do grupo agora chegam à plataforma.

IDs compatíveis com o banco — integração desbloqueada.

Menos ruído: recibos fora, mensagens de sistema escondidas.

Operável em produção: logs sob controle, config por .env.

Métricas (membros, atividade) derivam dos eventos já coletados.

Próximo passo (na plataforma): tratar o evento groups.metadata para preencher o nome do grupo e a lista de membros/admins. Sem isso, o bot já envia, mas o nome do grupo segue vazio.