Whats app client integration
Introduction#
whatsapp-web.js Client in the main process with LocalAuth, headless Puppeteer, QR events, send loop, and logout cleanup.
Project structure#
The integration spans three primary areas:
- Electron main process orchestrating WhatsApp Web client lifecycle and IPC
- React renderer components managing UI state and user interactions
- Python backend utilities for advanced contact processing and validation
graph TB subgraph "Electron Main Process" MJS["main.js<br/>WhatsApp Client, Events, IPC"] PRE["preload.js<br/>IPC Bridge"] end subgraph "React Renderer" WAF["WhatsAppForm.jsx<br/>UI & State"] PYO["pyodide.js<br/>Manual Numbers Parser"] end subgraph "Python Backend" APP["app.py<br/>Flask API"] EXC["extract_contacts.py<br/>CLI Utilities"] REQ["requirements.txt<br/>Dependencies"] end subgraph "External Libraries" WWEB["whatsapp-web.js<br/>Client & LocalAuth"] QRCODE["qrcode<br/>QR Generation"] PUPPETEER["Puppeteer<br/>Headless Browser"] end WAF --> PRE PRE --> MJS MJS --> WWEB MJS --> QRCODE MJS --> PUPPETEER WAF --> PYO PYO --> APP APP --> EXC
Core components#
- Electron main process manages the WhatsApp client lifecycle, Puppeteer configuration, event emission, and cleanup.
- Preload script exposes a secure IPC API to the renderer for WhatsApp operations.
- React component handles UI rendering, QR display, status updates, and user actions.
- Pyodide runtime enables Python-powered manual number parsing directly in the browser.
- Python backend extracts and validates contacts.
Architecture overview#
The integration follows an event-driven model:
- Renderer triggers client initialization via IPC
- Main process creates a Client with LocalAuth and headless Puppeteer
- QR event emits a data URL for the renderer to display
- Ready and authenticated events clear QR and update status
- Disconnected and auth_failure events notify the renderer
- Message sending loops through contacts with personalization and rate limiting
- Contact import supports CSV and TXT formats with fallback parsing
sequenceDiagram
participant UI as "WhatsAppForm.jsx"
participant PRE as "preload.js"
participant MAIN as "main.js"
participant WWEB as "whatsapp-web.js"
participant QR as "qrcode"
UI->>PRE : invoke("whatsapp-start-client")
PRE->>MAIN : ipc invoke
MAIN->>WWEB : new Client(LocalAuth, Puppeteer config)
MAIN->>WWEB : client.on("qr")
WWEB-->>MAIN : qr string
MAIN->>QR : toDataURL(qr)
QR-->>MAIN : data URL
MAIN-->>UI : emit("whatsapp-qr", dataUrl)
UI-->>UI : render QR code
MAIN->>WWEB : client.on("ready"/"authenticated")
MAIN-->>UI : emit("whatsapp-status", "ready"/"authenticated")
MAIN-->>UI : emit("whatsapp-qr", null)
UI->>PRE : invoke("whatsapp-send-messages", {contacts, message})
PRE->>MAIN : ipc invoke
MAIN->>WWEB : isRegisteredUser(chatId)
WWEB-->>MAIN : boolean
MAIN->>WWEB : sendMessage(chatId, personalizedMessage)
MAIN-->>UI : emit("whatsapp-send-status", progress)Detailed component analysis#
Client initialization pattern with LocalAuth and puppeteer#
- Client creation uses LocalAuth strategy for automatic session persistence and improved security.
- Puppeteer runs in headless mode with hardened arguments to improve stability on CI and desktop environments.
- Event handlers are attached immediately after client instantiation to capture qr, ready, authenticated, auth_failure, and disconnected states.
- Initialization is triggered via IPC from the renderer and guarded against duplicate instances.
flowchart TD Start(["Initialize WhatsApp"]) --> CheckExisting["Check Existing Client"] CheckExisting --> |Exists| Notify["Notify 'already running'"] CheckExisting --> |None| CreateClient["Create Client with LocalAuth"] CreateClient --> SetEvents["Attach Event Handlers"] SetEvents --> StartPuppeteer["Start Headless Browser"] StartPuppeteer --> EmitReady["Emit 'ready' when ready"] StartPuppeteer --> EmitQR["Emit 'qr' with data URL"] StartPuppeteer --> EmitAuth["Emit 'authenticated' on success"] StartPuppeteer --> EmitFail["Emit 'auth_failure' on failure"] StartPuppeteer --> EmitDisc["Emit 'disconnected' on disconnect"]
Event-Driven architecture#
- qr: Converts QR string to a data URL and sends it to the renderer; updates status to prompt scanning.
- ready: Clears QR display and signals readiness.
- authenticated: Clears QR display and confirms successful authentication.
- auth_failure: Emits a failure message with the provided reason.
- disconnected: Emits disconnection reason and resets the client reference.
sequenceDiagram
participant MAIN as "main.js"
participant RENDER as "WhatsAppForm.jsx"
MAIN->>RENDER : emit("whatsapp-status", "Scan QR code...")
MAIN->>MAIN : QRCode.toDataURL(qr)
MAIN-->>RENDER : emit("whatsapp-qr", dataUrl)
MAIN->>RENDER : emit("whatsapp-status", "Client is ready!")
MAIN-->>RENDER : emit("whatsapp-qr", null)
MAIN->>RENDER : emit("whatsapp-status", "Authenticated!")
MAIN-->>RENDER : emit("whatsapp-qr", null)
MAIN->>RENDER : emit("whatsapp-status", "Authentication failed : " + msg)
MAIN->>RENDER : emit("whatsapp-status", "Client disconnected : " + reason)
MAIN->>MAIN : reset client referenceQR code generation and display mechanism#
- QR string received from the client is transformed into a data URL using the QRCode library.
- The renderer displays the QR code image and handles load/error states.
- On ready or authenticated events, the QR is cleared to prevent stale displays.
flowchart TD QRStr["Receive QR String"] --> QRToDataURL["QRCode.toDataURL(qr)"] QRToDataURL --> DataURL["Data URL"] DataURL --> SendQR["Send 'whatsapp-qr' to Renderer"] SendQR --> RenderQR["Render QR Image"] RenderQR --> OnLoad["onLoad -> clear error"] RenderQR --> OnError["onError -> show retry"]
Message sending workflow: personalization, rate limiting, error handling#
- Personalization: Replaces {{name}} with the contact’s name or defaults to “Friend”.
- Chat ID construction: Builds
@c.usIDs for local and international numbers. - Rate limiting: Delays between messages using timeouts to reduce spam risk.
- Error handling: Per-contact try/catch captures registration checks and send failures; updates status and continues.
flowchart TD
Start(["Start Sending"]) --> CheckClient["Check Client Info"]
CheckClient --> |Missing| NoClient["Notify 'scan QR first'"] --> End
CheckClient --> |Ready| Loop["For Each Contact"]
Loop --> Personalize["Replace {{name}} with contact name"]
Personalize --> BuildChatId["Build chatId (@c.us)"]
BuildChatId --> CheckReg["Check isRegisteredUser"]
CheckReg --> |Registered| SendMsg["sendMessage(chatId, message)"]
CheckReg --> |Not Registered| FailNotReg["Log 'not registered'"]
SendMsg --> Delay["Wait 3s"]
FailNotReg --> Next["Next Contact"]
Delay --> Next
Next --> Loop
Loop --> Done["Aggregate Results"]
Done --> End(["Complete"])Contact import functionality: CSV and TXT#
- CSV import: Uses a streaming parser to read rows and extract number/name pairs.
- TXT import: Reads file content and splits lines; attempts to parse comma/tab/pipe-separated values or extract phone numbers from lines.
- Fallback parsing: Attempts pandas-based parsing first, then falls back to manual CSV parsing if needed.
- Error handling: Returns empty array on errors and logs failures.
flowchart TD
Start(["Import Contacts"]) --> Dialog["Open File Dialog (CSV/TXT)"]
Dialog --> ExtCheck{"File Extension?"}
ExtCheck --> |CSV| Stream["Stream CSV with csv-parser"]
Stream --> RowLoop["For Each Row"]
RowLoop --> ExtractCSV["Extract number/name"]
ExtractCSV --> CollectCSV["Collect Contacts"]
ExtCheck --> |TXT| ReadTXT["Read File Content"]
ReadTXT --> Lines["Split by Lines"]
Lines --> ParseTXT["Split by separators or regex"]
ParseTXT --> ExtractTXT["Extract number/name"]
ExtractTXT --> CollectTXT["Collect Contacts"]
ExtCheck --> |Other| Error["Return []"]
CollectCSV --> Return["Return Contacts"]
CollectTXT --> ReturnAuthentication lifecycle management, session persistence, and cleanup#
- Session persistence: LocalAuth stores session data locally, enabling clean reconnects.
- Startup cleanup: Removes cached .wwebjs_cache and .wwebjs_auth directories so the client starts clean.
- Logout procedure: Calls client.logout(), clears client reference, deletes auth/cache files, and resets UI state.
- Forced cleanup: Even if logout fails, auth/cache files are removed and UI state is reset.
stateDiagram-v2 [*] --> Uninitialized Uninitialized --> Initializing : "start-client" Initializing --> QR : "qr event" Initializing --> Ready : "ready event" Initializing --> Authenticated : "authenticated event" QR --> Ready : "client ready" Ready --> Authenticated : "login success" Authenticated --> Disconnected : "disconnected event" Authenticated --> LoggingOut : "logout" LoggingOut --> Uninitialized : "cleanup complete" Disconnected --> Initializing : "reconnect"
Dependency analysis#
Key external dependencies and their roles:
- whatsapp-web.js: Provides the WhatsApp Web client and LocalAuth strategy.
- qrcode: Converts QR strings to data URLs for display.
- qrcode-terminal: Alternative terminal-based QR generation (present in dependencies).
- nodemailer/googleapis: Used for email features (not covered in this page).
- Puppeteer: Headless browser engine configured in the Electron main process.
graph LR MAIN["main.js"] --> WWEB["whatsapp-web.js"] MAIN --> QRC["qrcode"] MAIN --> PUP["Puppeteer Args"] PRE["preload.js"] --> IPC["IPC Bridge"] WAF["WhatsAppForm.jsx"] --> PRE PYO["pyodide.js"] --> APP["app.py"] APP --> EXC["extract_contacts.py"]
Performance considerations#
- Headless browser configuration: Hardened Chromium flags improve stability and reduce resource contention.
- Rate limiting: Delays between messages help avoid rate limits and detection mechanisms.
- Streaming parsers: CSV parsing uses streaming to handle large files efficiently.
- Cleanup: Removing auth/cache directories prevents accumulation of stale session data.
Troubleshooting guide#
Common issues and resolutions:
- QR code not loading: Verify network connectivity, restart the app, and clear browser cache. The renderer includes retry logic on QR load failure.
- Authentication failures: Check device link instructions, ensure phone has active internet, and retry. The client emits auth_failure with a reason.
- Disconnections: The client emits disconnected with a reason; restart the client to reconnect.
- Contact import errors: Confirm file format compatibility (CSV/TXT), UTF-8 encoding, and proper column headers. The backend includes fallback parsing strategies.
- Logout issues: The logout handler attempts logout and forces cleanup if it fails, so auth/cache files are removed.
Conclusion#
One client instance at a time. Re-init after logout or auth failure rather than trying to revive a half-dead Client object.