dienst_echo_whatsappgroep/AGENTS.md

260 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
- **Enige geldige bewijzen van levering:** (1) de **live `message_ack`-progressie** zoals
die vlak ná verzending via de webhook binnenkomt; (2) de **zichtbare werkelijkheid op
het apparaat** van de ontvanger/betrokkene.
- **Trek NOOIT een leveringsconclusie uit een later gelezen fetchMessages-ack.** Claim geen
"bezorgd/gelezen" op basis van de store — dat geeft verkeerde adviezen.
- Dit is de reden dat ack-tracking bestaat: het hangt af van de live events, niet van de
store.
## 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` |
| ~~MacMini~~ | ~~LaunchDaemon (verouderd, gestopt juli 2026)~~ |
## 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) |
| ~~3000~~ | 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
```bash
# 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
```bash
ssh aiaiaiaiagent@nextcloud
docker ps --filter "name=wwebjs" # status
docker restart wwebjs-api # herstarten
docker logs wwebjs-api --tail 30 # logs
```
### Sessie status
```bash
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` |
```bash
ssh aiaiaiaiagent@nextcloud "sudo systemctl list-timers --no-pager | grep wwebjs"
```
## Release-proces
1. Code wijzigen lokaal in `/Users/eddy/src/dienst_echo_whatsappgroep/`
2. Commit + pushen naar remote: `git add -A && git commit -m "..." && git push origin main`
3. Git pull op nextcloud:
```bash
ssh aiaiaiaiagent@nextcloud "cd ~/whatsapp-scripts/dienst_echo_whatsappgroep && git pull && npm ci"
```
NB: `diensten/`, `.env`, en `AGENTS.md` staan in `.gitignore` — die worden niet overschreven door `git pull`.
Alleen als `.env` gewijzigd is, sync deze expliciet (géén `--delete`, géén volledige rsync):
```bash
rsync -avz /Users/eddy/src/dienst_echo_whatsappgroep/.env aiaiaiaiagent@nextcloud:/home/aiaiaiaiagent/whatsapp-scripts/dienst_echo_whatsappgroep/.env
```
4. Herstarten op nextcloud:
```bash
ssh aiaiaiaiagent@nextcloud "sudo systemctl restart dienst-bot"
ssh aiaiaiaiagent@nextcloud "sudo systemctl restart telegram-bot"
```
5. Logs checken:
```bash
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`)
- `sendTextMessage` en `sendImageMessage` hebben 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: true` om TIME_WAIT poortconflicten te voorkomen
- Bij `EADDRINUSE` wordt 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.js` registreert elke media-verzending en volgt de `message_ack`-events (webhook `message_ack` in `messageHandler.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_ack` staat sinds 2026-09-24 in wwebjs-api `.env` **niet** meer in `DISABLED_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**) pauzeert tussen de pagina's van één
dienst (niet vóór de eerste). Doel: de wwebjs media-pipeline (één chromium-tab) per-verzending
lucht geven en gelijktijdige storage/upload-conflicten verminderen. 0 = uitschakelen.
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.
## 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.)
**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")
1. 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.
2. Script meet acks (`ack≥3` = geaccepteerd, geïndexeerd in `dienst-media-index.json`;
idempotent — geaccepteerde worden nooit opnieuw verstuurd).
3. Hangende (`ack<3` na wachttijd) regenereren in volgorde: officiële pdf2png-pipeline →
**poppler** `pdftoppm -png -r 72` (bewezen) → gs → magick. Steekproefsgewijs visueel
controleren dat de juiste dienst op de pagina staat.
4. Her-testen tot ack 3; na max rondes → menselijke-inspectielijst.
### Script-parameters (voor gebruik)
```bash
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), wordt `POST /notify` naar 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 |