Skip to content

Repository files navigation

wpp-tui

WhatsApp TUI client built with Baileys + Ink (React for terminal).

Real-time chat, poll vote decryption, event logging, and payload extraction.

Quick Start

pnpm install
pnpm dev

Scan the QR code with WhatsApp > Settings > Linked Devices. On subsequent runs it reconnects automatically.

Screenshots

┌─ WA Bot | [1:Chat]  2:Stats   3:Debug ──── ● danielzin ─┐
├─ CHATS ──────────────────────┐┌─ MESSAGES — General ─────┐
│ > # General            (343) ││ 18:36 danielhe4rt  eae   │
│   # He4rt Delas 💕      (52) ││ 18:37 Clinton      oi    │
│   # Vagas             (343) ││ 18:37 Bruna     🖼️ sticker│
│   # Comunidade Exemplo   (89) ││ 18:38 danielhe4rt  📊 PHP │
│   @ danielhe4rt              ││   ■ Sim!! (3)            │
│                              ││   □ Não (correto) (5)    │
│                              ││ 18:39 Mina    🗳️ → Sim   │
└──────────────────────────────┘└──────────────────────────┘
┌─ > Message General... or /help ──────────────────────────┐

Features

TUI (Ink + React)

  • 3 tabs: Chat, Stats, Debug — cycle with Tab or /chat, /stats, /debug
  • Chat tab: sidebar with groups (#) and DMs (@), message feed with auto-scroll
  • Stats tab: top senders bar chart, message type distribution, group activity
  • Debug tab: real-time event stream with scroll, connection info, store counters
  • QR code rendered inline in the terminal
  • Message history loaded from logs on startup
  • Group metadata fetched via WhatsApp API and cached to disk
  • Poll decryption: votes decrypted with AES-256-GCM + HMAC-SHA256, shown per-user

Event Logging

Every WhatsApp event is persisted to logs/<event>/<date>.json with timestamps. Supported events:

Event Description
messages.upsert Incoming/outgoing messages
messages.update Delivery/read status changes
messages.reaction Emoji reactions
chats.update Chat metadata changes
presence.update Typing/online indicators
message-receipt.update Read receipts
contacts.update Contact name changes
group.member-tag.update Group member tags/roles
connection.update Connection state changes
creds.update Auth credential updates

Payload Extraction (make extract-payload)

Parses raw logs into structured JSON files in extracted/<date>/:

File Content
messages.json All messages with parsed content
users.json Users with phone, name, groups
groups-full.json Group metadata, admins, description, member tags
reactions.json Emoji reactions per message, top emojis, top reactors
threads.json Reply chains, most quoted messages
polls.json Decrypted poll results with voter names
member-profiles.json Users with group tags, admin status, activity breakdown
activity.json Messages per hour, media breakdown, conversation starters
stickers.json Sticker metadata (animated, AI, lottie)
receipts.json Read/delivery timestamps
presence.json Typing/online events
timeline.json Unified chronological feed
stats.json Summary statistics

Webhook Proxy

Optionally forward all events to an external webhook:

WEBHOOK_URL=https://your-webhook.example.com pnpm dev

Modo headless

Para rodar o coletor como serviço 24/7 (sem TUI, ex.: num server via systemd), use o Modo headless. Ele reaproveita o mesmo núcleo de conexão da TUI (startCollectorCore, ver docs/adr/0001): uma única fonte de verdade para os dois modos.

pnpm build              # gera dist/
pnpm start:headless     # = node --env-file-if-exists=.env dist/index.js --headless

O modo é ativado por --headless na linha de comando ou pela env HEADLESS=1 (aceita 1/true/yes). Sem nenhum dos dois, o dist/index.js sobe a TUI normalmente.

Parear a sessão no server (pairing-code, sem QR)

O serviço headless de longa duração não pareia: continua fail-fast. Quem estabelece a sessão é o Comando de pareamento one-shot, um processo à parte que usa o pairing-code do Baileys — sem QR, sem TUI, sem scp do diretório de auth (ver docs/adr/0003, que supersede o 0002).

No próprio server, dentro do WorkingDirectory do serviço:

pnpm pair 5500900000002          # apenas dígitos com DDI (sem +, (), -, espaços)
# ou: make pair NUMBER=5500900000002
  1. O comando imprime um código de 8 caracteres no stdout (estado e logs vão para o stderr — o stdout carrega só o código).
  2. No celular: Aparelhos conectados > Conectar com número e digite o código.
  3. Ao conectar, a sessão é gravada em baileys_auth_info/ e o processo sai com código 0. Sem parear em 120s, sai com código ≠ 0.

Detalhes:

  • O comando não sobe o Coletor e não exige WEBHOOK_URL / WHATSAPP_WEBHOOK_SECRET — é o caminho mínimo de login.
  • Se já existir uma sessão registrada, ele recusa (exit ≠ 0) para não destruir uma sessão viva por engano. Use --force (make pair NUMBER=… FORCE=1) para descartar a sessão atual e re-parear.
  • Número ausente (nem argumento nem env PAIR_NUMBER) ou inválido → erro claro e exit ≠ 0, sem tocar o socket.

Se o serviço headless receber um evento de QR (sinal de que não há sessão válida), ele loga FATAL (sem sessão válida) e sai com código ≠ 0 — em vez de ficar gerando QRs num loop. Um loggedOut no server também derruba o coletor: rode o Comando de pareamento de novo e reinicie o serviço (recuperação manual e explícita, ADR-0003).

Variáveis obrigatórias

No headless, WEBHOOK_URL e WHATSAPP_WEBHOOK_SECRET são obrigatórias. Se faltar qualquer uma, o processo loga FATAL e sai com código ≠ 0 antes de conectar (fail-fast). No Modo TUI o coletor continua opcional. Veja o .env.example para todas as variáveis.

Logs (JSON na stdout)

O headless emite logs em JSON na stdout por padrão (ideal para journald). Defina LOG_PRETTY=1 para formato legível via pino-pretty (debug local). Não escreve wa-logs.txt a menos que WA_LOG_FILE seja definido explicitamente.

dois loggers, controlados separadamente, e toda linha carrega um campo component (whatsapp / collector / retention / baileys):

Variável Logger Padrão
LOG_LEVEL app / coletor info
BAILEYS_LOG_LEVEL protocolo Baileys (barulhento) warn

Se LOG_RETENTION_DAYS estiver ausente/0, o boot emite um WARN alertando sobre crescimento ilimitado de disco.

Deploy via systemd (--user)

O serviço roda como unit de usuário (systemd --user), com a conta do próprio login — sem sudo e sem /etc/systemd/system/. A unit-fonte está em deploy/whatsapp-collector.service e os alvos make svc-* cuidam do ciclo de vida. Nenhum precisa de sudo, exceto make svc-linger.

pnpm install && pnpm build        # 1. dependências + dist/
make pair NUMBER=5500900000002    # 2. pareia a sessão (ver seção acima)
make svc-install                  # 3. instala a unit em ~/.config/systemd/user,
                                  #    resolve o node absoluto e habilita+inicia
make svc-linger                   # 4. (único com sudo) roda sem login aberto
make svc-logs                     # segue os logs (JSON no journald do usuário)

Configure ~/wpp-tui/.env (base no .env.example) antes do svc-installWEBHOOK_URL e WHATSAPP_WEBHOOK_SECRET são obrigatórias.

Demais alvos: svc-start, svc-stop, svc-restart, svc-status, svc-redeploy (build + restart) e svc-uninstall.

O sinal de saúde é a linha de heartbeat dos logs (não há endpoint HTTP /health nesta versão).

Keyboard Shortcuts

Key Action
Tab Cycle tabs
↑↓ Navigate chats (Chat tab) / Scroll (Debug tab)
Ctrl+Q Quit
/chat Switch to Chat tab
/stats Switch to Stats tab
/debug Switch to Debug tab
/quit Quit

Scripts

pnpm dev              # dev server with hot reload
pnpm build            # production build with tsup
pnpm start            # run production build (TUI)
pnpm start:headless   # run production build in headless mode (collector, no TUI)
pnpm typecheck        # type check without emitting
make extract-payload  # extract structured data from logs

Project Structure

src/
├── index.tsx              # entry point — routes to TUI or headless (dynamic import)
├── app.tsx                # tab system, keyboard handling, layout
├── app-render.tsx         # TUI renderer (extracted from index.tsx for dynamic import)
├── headless.ts            # headless runner — bootstraps the collector core without UI
├── logging.ts             # logger factory (app + baileys loggers with component tags)
├── logger.ts              # legacy pino logger (file output, used by the TUI)
├── types.ts               # shared TypeScript types
├── event-logger.ts        # persists raw events to disk + optional webhook
├── history.ts             # loads message history from logs on startup
├── message-store.ts       # persists raw messages for poll decryption (TUI-only)
├── group-cache.ts         # caches group metadata to disk
├── poll-decrypt.ts        # AES-256-GCM poll vote decryption
├── retention.ts           # periodic cleanup of old raw logs
├── collector/
│   ├── core.ts            # collector core (connection, auth, routing) — shared by TUI + headless
│   ├── headless-config.ts # environment resolution for headless mode (fail-fast validation)
│   ├── shutdown.ts        # graceful shutdown handler (SIGTERM/SIGINT + force-exit timeout)
│   ├── outbox.ts          # SQLite event queue
│   ├── event-router.ts    # routes events to the collector outbox
│   ├── webhook-sender.ts  # sends queued events to the webhook
│   ├── heartbeat.ts       # periodic health signal
│   ├── extractors.ts      # event data extraction helpers
│   ├── helpers.ts         # collector utility functions
│   └── types.ts           # collector-specific types
├── hooks/
│   └── use-socket.ts      # React hook wrapping the collector core (TUI-only: storeMessage, parseContent, dedup)
└── components/
    ├── header.tsx          # status bar with tab navigation
    ├── chat-list.tsx       # sidebar with groups/DMs
    ├── message-feed.tsx    # message display with poll results
    ├── input-bar.tsx       # text input with commands
    ├── qr-view.tsx         # QR code renderer
    ├── stats-view.tsx      # statistics dashboard
    └── debug-view.tsx      # real-time event log

scripts/
└── extract.py             # payload extraction with poll decryption

Tech Stack

License

ISC

About

WhatsApp TUI client built with Baileys + Ink (React for terminal). Real-time chat, poll decryption, event logging, and payload extraction.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

16 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages