WhatsApp automatiseren via de telefoon zelf (i.p.v. WhatsApp Web)

· 3 min lezen

De meeste WhatsApp-automatiseringsprojecten gebruiken whatsapp-web.js of een vergelijkbare bibliotheek die een onzichtbare Chromium-browser start, inlogt op WhatsApp Web, en via daar berichten stuurt en ontvangt.

Dat werkt — tot het niet meer werkt. Sessies die om onbekende redenen droppen, een geblokkeerd telefoonnummer omdat WhatsApp Web-verkeer verdacht veel op een bot lijkt, of een browserproces dat na een paar dagen vastloopt. Herkenbaar?

Wij bouwden een alternatief: stuur de telefoon zelf aan, via USB.

Het idee

In plaats van een browser die doet alsof-ie een telefoon is, gebruiken we een echte telefoon — een Nexus 6P met WhatsApp erop, via USB verbonden met een server. Een Docker-container draait Appium met de UiAutomator2-driver, die via ADB de telefoon kan aansturen: scherm uitlezen, knoppen indrukken, tekst typen, notificaties opvangen.

Daar overheen ligt een dunne REST API (Express + Node.js) met eindpunten voor het versturen en ophalen van berichten.

De architectuur

Twee Docker-containers op één server:

ContainerTaak
appium-adbADB-server + Appium 3 + UiAutomator2-driver. Beheert de USB-verbinding, past powersave-instellingen toe, en herstelt de verbinding als deze wegvalt.
wa-appium-apiExpress REST API (poort 3010). Biedt eindpunten voor chatlijst, berichten, en media. Ook de webhook-ontvanger voor notificaties.

De telefoon (niet geroot) hangt aan USB. De container mount /dev/bus/usb voor directe toegang. Een udev-regel zorgt dat de ADB-daemon de telefoon mag zien, en een RSA-vingerafdruk op het telefoonscherm geeft eenmalig toestemming.

Functionaliteiten

Inmiddels werkt het volgende:

FunctieHoe
Chatlijst lezenAppium leest het WhatsApp-hoofdscherm uit (contactnaam, laatste bericht, tijdstip, ongelezen telling).
Berichten sturenVia de zoekfunctie van WhatsApp wordt de juiste chat gevonden, tekst ingetypt en verstuurd.
Berichten ophalenEen chat openen, de zichtbare berichten uitlezen, terug naar home.
Media versturenFoto’s worden via adb push naar de telefoon gekopieerd, waarna een Android Intent (ACTION.SEND) het deelscherm opent en Appium het juiste contact laat selecteren. Geen broze navigatie door de galerij.
Real-time notificatiesEen kleine Kotlin-app op de telefoon luistert via NotificationListenerService naar binnenkomende WhatsApp-notificaties. Bij elk bericht (tekst of media) wordt een HTTP POST naar de API gestuurd.
Webhook fan-outDe API stuurt binnenkomende berichten door naar geregistreerde subscribers — andere services die op berichten willen reageren.
Job queueOmdat er maar één telefoon is, gaan alle Appium/ADB-operaties door een centrale FIFO-wachtrij. Geen conflicten op de UI-sessie.

Waarom dit beter is dan WhatsApp Web

AspectWhatsApp Web (Puppeteer)Telefoon (Appium)
DetectieWhatsApp kan Web-sessies herkennen als bots en blokkerenZiet eruit als een normale telefoongebruiker
StabiliteitBrowser-crash na dagen, sessie-drops, puppeteer-timeoutsUSB-kabel erin = stabiel
NotificatiesPollen of webhook via de APIAndroid NotificationListener = push
Root nodigNeeNee

De valkuil: UI-automation is traag

De grootste beperking is snelheid. Waar WhatsApp Web een API-call doet in milliseconden, moet Appium het scherm uitlezen, elementen zoeken, wachten op renders. Een simpele getChats kan 10-15 seconden duren.

Daarom gebruiken we twee lagen:

  • Primair: de vertrouwde WhatsApp-Web-API (wwebjs-api) voor snelle operaties
  • Fallback: de Appium-API als de Web-sessie wegvalt

En voor real-time notificaties hebben we de notification-listener-app, die geen Appium nodig heeft — die krijgt berichten binnen zodra het WhatsApp-scherm ze toont.

Open source

Het hele project staat op onze Forgejo: Dockerfiles, de REST API (server.js), de Kotlin notification-listener-app, en documentatie. Vrij te gebruiken, aan te passen, en vooral: te leren van.