Architecture¶
PyWacli bridges WhatsApp → Node.js (Baileys) → Python, persisting everything to a SQL database and syncing media to object storage, with a live terminal UI on top.
flowchart TD
WA[WhatsApp mobile] -- Baileys library --> NODE
subgraph Node.js
NODE[baileys_service.js]
WS[ws_server.js / command server]
SM[session_manager.js\nsocket singleton]
NODE --- SM
NODE --- WS
end
NODE -- normalized JSON events --> PY[Python event processor]
subgraph Python
PY --> DB[(SQL database\nSQLite / Postgres / MySQL)]
PY --> STORE[Media storage\nS3 / R2 / B2 / local]
DASH[Rich dashboard]
DB --> DASH
end
SEND[Send / Automate] -- local command server --> WS
Components¶
| Component | Language | Role |
|---|---|---|
baileys_service.js |
Node.js | Connects to WhatsApp via Baileys, downloads media, normalizes events |
session_manager.js |
Node.js | Holds the single Baileys socket as a singleton |
ws_server.js |
Node.js | WebSocket / local command server for outgoing messages |
event_processor.py |
Python | Consumes normalized events and routes them to the DB and storage |
websocket_services.py |
Python | WebSocket consumer for real-time events |
message_sender.py |
Python | Sends outgoing messages through the live session |
db/ |
Python | SQLAlchemy engine, schema, and read/write queries |
ai_engine/ |
Python | Provider factory, prompt templates, and skills for AI Automate |
cli/ui/dashboard.py |
Python | The live Rich dashboard |
Data flow¶
- Capture — Baileys receives WhatsApp events; the Node bridge normalizes them into clean JSON and writes them to stdout (and downloads media files).
- Process — the Python event processor reads those events and persists them to the database; media is uploaded to each configured storage target.
- Display — the dashboard reads from the database and re-renders on an interval.
- Send — Send/Automate push outgoing messages to the local command server, which forwards them through the one live socket.
The single-socket model¶
WhatsApp allows one linked-device socket per session. PyWacli embraces this: connect owns that
socket and exposes a small local command server (send_host/send_port, default
127.0.0.1:8765). Every other action coordinates through it — so you never risk two competing
connections.