16 KiB
Dienst Bot — Productie-informatie
🚨 VERPLICHT: bridgestore is GEEN waarheid over bezorging
De wwebjs-api bridgestore is niet betrouwbaar als bewijs dat een bericht bezorgd/gelezen is. Bevestigd 2026-09-24: een dienst-PNG werd 3× naar dezelfde ontvanger "verzonden" (success + message-ID), de store toonde 3 berichten allemaal ack 3, maar de ontvanger zag in de chat maar één bericht (ghost-sends: id geretourneerd, nooit echt zichtbaar).
/chat/fetchMessages-acks en de lokale message-store van wwebjs-api zijn een lokale registratie, géén bezorgbewijs. Ze kunnen ack 1/2/3 tonen voor berichten die nooit aankwamen.- Ook de live
message_ack-progressie is sinds 2026-09-26 géén bezorgbewijs meer: de staged-ghost van dienst 5219 blz 2 kreeg via de webhook eenack=2afgegeven voor een bericht dat nooit getransmitteerd was (stond niet eens in de WhatsApp-app van de eigenaar). De bridge resolvet media-sends optimistisch op lokale staging en fabriceert daarna ack-events + delivery-info. Ack-tracking (stopt bij ack ≥ 2) kan deze klasse per ontwerp niet vangen. - Enige geldige bewijzen van levering: (1) de zichtbare werkelijkheid op het
apparaat van de ontvanger/betrokkene; (2)
getInfo(message.getInfo()): bij een staged-ghost blijftreadleeg terwijldeliverygevuld kan zijn — het enige betrouwbare bridge-signaal isread/readRemaining. - Trek NOOIT een leveringsconclusie uit een later gelezen fetchMessages-ack. Claim geen "bezorgd/gelezen" op basis van de store — dat geeft verkeerde adviezen.
Waar draait de bot?
De bot draait op de nextcloud server (192.168.5.252) als systemd service.
| Server | Locatie |
|---|---|
| nextcloud (192.168.5.252) | systemd service dienst-bot.service |
| nextcloud (192.168.5.252) | systemd service telegram-bot.service |
Bestanden & paden (lokaal)
| Wat | Pad |
|---|---|
| Source code | /Users/eddy/src/dienst_echo_whatsappgroep/ |
| Environment (.env) | /Users/eddy/src/dienst_echo_whatsappgroep/.env |
| Script | src/index.js |
| Groep | Dienst opvragen EdGPT-EBS (120363407613652082@g.us) |
Nextcloud server (systemd)
Poort-overzicht
| Poort | Service | Type | Beheer |
|---|---|---|---|
| 3001 | wwebjs-api | Docker container | WhatsApp bridge |
| 3002 | dienst-bot | systemd service | Dienst opvragen bot (dit project) |
| 3003 | aankondigingen-webhook | systemd service | Losse webhook receiver (Eddy sessie) |
| 3004 | telegram-bot | systemd service | Telegram watchdog (dit project) |
| Forgejo | systemd service | NIET onze service! Niet aanraken. |
dienst-bot
| Wat | Waarde |
|---|---|
| Systemd service | dienst-bot.service |
| Working directory | /home/aiaiaiaiagent/whatsapp-scripts/dienst_echo_whatsappgroep |
| User | aiaiaiaiagent |
| Poort dienst-bot | 3002 |
| Poort telegram-bot | 3004 |
| Health check dienst-bot | curl http://localhost:3002/health |
| Health check telegram-bot | curl http://localhost:3004/health |
Commando's
# Status
ssh aiaiaiaiagent@nextcloud "systemctl status dienst-bot"
ssh aiaiaiaiagent@nextcloud "systemctl status telegram-bot"
# Herstarten
ssh aiaiaiaiagent@nextcloud "sudo systemctl restart dienst-bot"
ssh aiaiaiaiagent@nextcloud "sudo systemctl restart telegram-bot"
# Logs
ssh aiaiaiaiagent@nextcloud "journalctl -u dienst-bot --since today"
ssh aiaiaiaiagent@nextcloud "journalctl -u telegram-bot --since today"
WhatsApp API (wwebjs-api)
De wwebjs-api draait als Docker container op nextcloud.
| Wat | Waarde |
|---|---|
| Docker container | wwebjs-api |
| Image | wwebjs-api-api |
| URL | http://192.168.5.252:3001 |
| Sessie dienst-bot | EdGpt |
| Sessie persoonlijk | Eddy |
| Webhook dienst-bot | http://192.168.5.252:3002/webhook |
| Webhook aankondigingen | http://192.168.5.252:3003/webhook (Eddy-sessie, niet dit project) |
Docker commando's
ssh aiaiaiaiagent@nextcloud
docker ps --filter "name=wwebjs" # status
docker restart wwebjs-api # herstarten
docker logs wwebjs-api --tail 30 # logs
Sessie status
curl http://192.168.5.252:3001/session/status/EdGpt
# → {"success":true,"state":"CONNECTED","message":"session_connected"}
Wekelijkse container restart
De wwebjs-api container wordt elke zondag om 02:00 automatisch herstart om vastlopers te voorkomen.
| Wat | Waarde |
|---|---|
| Timer | docker-restart-wwebjs.timer |
| Service | docker-restart-wwebjs.service |
| Commando | docker restart wwebjs-api |
| Volgende run | check: systemctl list-timers --no-pager | grep wwebjs |
ssh aiaiaiaiagent@nextcloud "sudo systemctl list-timers --no-pager | grep wwebjs"
Release-proces
- Code wijzigen lokaal in
/Users/eddy/src/dienst_echo_whatsappgroep/ - Commit + pushen naar remote:
git add -A && git commit -m "..." && git push origin main - Git pull op nextcloud:
NB:ssh aiaiaiaiagent@nextcloud "cd ~/whatsapp-scripts/dienst_echo_whatsappgroep && git pull && npm ci"diensten/,.env, enAGENTS.mdstaan in.gitignore— die worden niet overschreven doorgit pull. Alleen als.envgewijzigd is, sync deze expliciet (géén--delete, géén volledige rsync):rsync -avz /Users/eddy/src/dienst_echo_whatsappgroep/.env aiaiaiaiagent@nextcloud:/home/aiaiaiaiagent/whatsapp-scripts/dienst_echo_whatsappgroep/.env - Herstarten op nextcloud:
ssh aiaiaiaiagent@nextcloud "sudo systemctl restart dienst-bot" ssh aiaiaiaiagent@nextcloud "sudo systemctl restart telegram-bot" - Logs checken:
ssh aiaiaiaiagent@nextcloud "journalctl -u dienst-bot --since '5 minutes ago'" ssh aiaiaiaiagent@nextcloud "journalctl -u telegram-bot --since '5 minutes ago'"
Configuratie
De .env bevat alle instellingen (niet in git). Template staat in .env.example.
Resilience maatregelen (juni 2026)
Deze maatregelen zijn toegevoegd na een incident met HTTP 500 van de wwebjs-api en een EADDRINUSE restart-loop.
Retry-logic (src/whatsappApi.js)
sendTextMessageensendImageMessagehebben ingebouwde retry met exponential backoff- Maximaal 3 pogingen, met vertraging van 1s → 2s → 4s
- Wordt geactiveerd bij 5xx HTTP-fouten (500, 502, 503, 504) en connectiefouten (ECONNRESET, ECONNREFUSED, ETIMEDOUT)
- Niet-retryable fouten (4xx) worden direct doorgelaten
Port binding en graceful shutdown (src/index.js)
- Gebruikt de Node.js HTTP server met
reuseAddr: trueom TIME_WAIT poortconflicten te voorkomen - Bij
EADDRINUSEwordt automatisch na 5 seconden opnieuw geprobeerd te binden - Graceful shutdown: bij SIGTERM/SIGINT worden actieve connecties netjes afgesloten (max 10s timeout)
Ack-tracking voor hangende media (september 2026)
Sommige mediabestand-verzendingen keren success + message-ID terug maar worden nooit bezorgd (blijft op één grijs vinkje). Bevinding 2026-09-24: dit is een bestand × ontvanger-interactie, geen sessie-probleem — hetzelfde bestand hing (ack 1) voor één ontvanger maar bezorgde (ack 3) bij de admin in dezelfde verse sessie, en hing ook ná een sessie-herstart. Daarom wordt er bij een ack-1-hang geen sessie-herstart meer gedaan.
src/ackTracker.jsregistreert elke media-verzending en volgt demessage_ack-events (webhookmessage_ackinmessageHandler.js).- ack ≥ 2 = bezorgd → tracking verwijderd. Blijft >
ACK_STUCK_TIMEOUT_MS(5 min) op ack < 2 → éénmalige admin-notificatie met bestandsnaam + chat + advies "opnieuw renderen + bezorgtest". Geen resend naar de aanvrager. - Pruning na
ACK_PRUNED_AFTER_MS(30 min); de check-loop is activity-driven (draait alleen bij openstaande tracking, geen polling in rust). - Vereiste aan de bridge:
message_ackstaat sinds 2026-09-24 in wwebjs-api.envniet meer inDISABLED_CALLBACKS; container is daarvoor herstart. - Env-vars:
ACK_STUCK_TIMEOUT_MS(default 300000),ACK_PRUNED_AFTER_MS(default 1800000),ACK_CHECK_INTERVAL_MS(default 30000). - Media-spreiding:
MEDIA_SEND_DELAY_MS(default 500; praktijktest op 500) pauzeert na élke media-send (insendImageMessage), niet alleen tussen pagina's van één dienst. De wwebjs-media-pipeline is EEN chromium-tab; opeenvolgende media-sends — óók naar verschillende ontvangers — overlappen anders en veroorzaken de staged-ghost-race. 0 = uitschakelen. (Tijdelijke mitigatie; permanente fix = fork: send óf écht verzenden óf zichtbaar falen, zie RCA.)
Logregels: [ack] track <id> (<bestand> → <chat>), [ack] delivered ack=<n> <id>, [ack] pruned <n> stale entries.
Ack-semantiek (wwebjs 1.34.7): ack 1 = verstuurd naar server (één grijs vinkje), ack 2 = bezorgd op apparaat (twee vinkjes), ack 3 = geopend/gelezen. Let op: eerdere notities die "één grijs vinkje (ack 2)" noemden, zaten er dus naast. Let op 2 (2026-09-26): óók deze acks zijn geen leveringsbewijs — zie RCA hieronder.
RCA 2026-09-26: staged-ghost = race-conditie, géén bestandprobleem
Casus: dienst 5219 (2 pagina's), 09:29 gevraagd in de groep, bot stuurt beide pagina's
prive. App toont alleen blz 1; blz 2 stond in het geheel niet in de WhatsApp-app van de
eigenaar (geen transmissie). Toch: bridge retourneerde success + message-ID (3EB040AD…),
store toont het bericht, live message_ack gaf ack=2, getInfo toonde delivery gevuld
maar read leeg. Geen enkele fout in de container-logs.
Mechanisme (bewezen uit fork-broncode + logs):
- De send loopt via
WAWebSendMsgChatAction.addAndSendMsgToChat(chat, message)→[msgPromise, sendMsgResultPromise]. De bridge zetwaitUntilMsgSent: true(stock wwebjs-api,clientController.js), maar de promise resolve ook optimistisch op lokale staging — een echte serverbevestiging wordt nooit afgewacht. HTTP-success + message-ID betekenen dus niet "verzonden". - Patroon (gebruiker, consistent met logs): de ghost treedt alleen op bij 2+ media direct achter elkaar, en dan altijd de 2e van het paar; enkele sends falen nooit.
- Verklaring: de bot wacht per pagina op het HTTP-signaal (staging), niet op echte transmissie → blz 2 wordt gestart terwijl blz 1's echte upload/transmissie nog in de lucht is. Twee echte media-transmissies overlappen in de ene chromium-tab → de 2e verliest (intermitterend) → staged-ghost.
- Gevolgen:
MEDIA_SEND_DELAY_MSkan dit niet oplossen (wacht op optimistisch signaal); poppler/re-render maskeert het alleen (nieuwe poging = nieuwe race-rol, soms gewonnen — wassen neus);sendMessageViaUilost het óók niet op (wacht op dezelfde promises, alleen omzeilt het de media-editor); ack-tracking kan het niet vangen (stopt bij gefabriceerde ack ≥ 2).
Enige betrouwbare signalen: apparaat-waarheid; getInfo read/readRemaining (blijft
leeg bij staged). Aanbevolen richting (nog niet geïmplementeerd): de fork moet ná
addAndSendMsgToChat écht wachten op transmissiebevestiging — pas dan serialiseren
multi-page sends vanzelf en is de race weg.
Media bezorgtest (afbeeldingen die WhatsApp weigert)
Sommige dienst-PNG's worden wél verzonden (success + message-ID) maar komen niet als beeld aan: één grijs vinkje (ack 2), nooit geopend (ack 3). Niet te detecteren met een bestandscheck (formaat/CRC volledig geldig) — alleen de praktijktest vangt het. Oorzaak-klasse: wwjs 1.34.7-image-pipeline-regressie (wwebjs #201772/#201732), inhoudsgebonden.
Bevinding 2026-09-24 (bestand × ontvanger): de weigering is een interactie tussen bestand en ontvanger, niet de sessie. Hetzelfde (geldige, decodeerbare) bestand hing op ack 1 voor de ene ontvanger, terwijl de admin in dezelfde verse sessie ack 3 kreeg; ná een sessie-herstart hing hetzelfde bestand opnieuw. Consequentie: een sessie-herstart lost een ack-1-hang niet op en wordt sindsdien niet meer ingezet — wel detectie via ack-tracking (zie hierboven), re-render via poppler en een verse bezorgtest. Ack-telling: ack 1 = één grijs vinkje (niet bezorgd), ack 2 = bezorgd op apparaat, ack 3 = geopend. (Eerdere notities "één grijs vinkje (ack 2)" zaten er dus naast.)
⚠️ 2026-09-26: onderscheid twee foutklassen. De hierboven beschreven bestand × ontvanger/offline-klasse (ack 1-hang, ontvanger offline of app weigert render) is niet hetzelfde als de staged-ghost-race (zie RCA hierboven): daar is het bestand nooit het probleem en re-render (poppler) is een wassen neus — het maskert de race alleen. Check bij een "niet aangekomen bladzijde" eerst: was het de 2e pagina van een paar binnen ~1s? Dan is het de race, geen bestand. Echte remedie is nog in ontwerp (fork: wachten op transmissiebevestiging); voor nu: opnieuw versturen (eventuele nieuwe poging) en apparaat als waarheid.
Na elke omruiling van de dienstenmap: de volledige media-test doorlopen vóór ingebruikname.
Afbeeldingsformaat: sinds 07b511e leidt sendImageMessage de mimetype af uit de
extensie — png→image/png, jpg/jpeg→image/jpeg (onbekend→image/png). De bot is
dus voorbereid als de bron-repo overgaat naar JPEG; de bestandsnaam uit
dienst-tijden.json wordt al extensie-agnostisch ingelezen (getPngFilePaths).
Werkwijze (gedetailleerd: vault-notitie "Dienst media bezorgtest")
- Verzend alle png's gedoseerd naar de beheerderchat via het generieke script
dienst-media-test.js(server:/home/aiaiaiaiagent/whatsapp-scripts/dienst-media-test.js), batch 20, wachttijd zodat de beheerder de chat opent. - Script meet acks (
ack≥3= geaccepteerd, geïndexeerd indienst-media-index.json; idempotent — geaccepteerde worden nooit opnieuw verstuurd). - Hangende (
ack<3na wachttijd) regenereren in volgorde: officiële pdf2png-pipeline → popplerpdftoppm -png -r 72(bewezen) → gs → magick. Steekproefsgewijs visueel controleren dat de juiste dienst op de pagina staat. - Her-testen tot ack 3; na max rondes → menselijke-inspectielijst.
Script-parameters (voor gebruik)
ssh aiaiaiaiagent@nextcloud
node /home/aiaiaiaiagent/whatsapp-scripts/dienst-media-test.js \
--map /home/aiaiaiaiagent/whatsapp-scripts/Whatsapp_Dienst/diensten/png \
--json /home/aiaiaiaiagent/whatsapp-scripts/Whatsapp_Dienst/diensten/dienst-tijden.json \
--chat 275749852475605@lid \
--batch 20 --wacht-minuten 10 --rondes 3
Telegram Watchdog (src/telegramBot.js)
Sinds juli 2026 draait er een apart telegram-bot proces als backup communicatiekanaal.
| Wat | Waarde |
|---|---|
| Systemd service | telegram-bot.service |
| Poort | 3004 |
| Health check | curl http://localhost:3004/health |
Hoe het werkt
- Startup: stuurt "online" melding naar admin via Telegram
- Health checks (elke 5 min): checkt dienst-bot (
/health) en wwebjs-api (/session/status) - Statusovergangen: alleen bij up→down of down→up wordt een notificatie gestuurd (geen spam)
- Fallback: als
notifyAdmin()in dienst-bot faalt (WhatsApp API down), wordtPOST /notifynaar telegram-bot gestuurd
.env variabelen
| Variabele | Doel |
|---|---|
TELEGRAM_BOT_TOKEN |
Token van @BotFather |
TELEGRAM_ADMIN_CHAT_ID |
Jouw Telegram chat-ID |
TELEGRAM_BOT_PORT=3004 |
Poort voor interne /notify endpoint |
DIENST_BOT_HEALTH_URL |
URL voor dienst-bot health check |
WWEBJS_API_SESSION_URL |
URL voor wwebjs-api session status |
HEALTH_CHECK_INTERVAL_MS=300000 |
Interval tussen health checks |
TELEGRAM_BOT_URL |
(in dienst-bot .env) URL voor Telegram fallback |