dienst_echo_whatsappgroep/README.md

245 lines
9.2 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
> Dit project is gemaakt door middel van vibecoding met [OpenCode 1.14.40](https://opencode.ai).
WhatsApp bot die dienstschema's (PNG) privé verstuurt op basis van een 4-cijferig dienstnummer, gepost in een speciale WhatsApp groep.
## Hoe het werkt
1. Iemand stuurt een **4-cijferig dienstnummer** in de groep (bijv. `4002`)
2. De bot zoekt het dienstschema op in `dienst-tijden.json`
3. De bijbehorende PNG-bestanden worden **privé** naar de afzender gestuurd
4. Via een **vastgezet welkomstbericht** in de groep staat uitleg over het gebruik
De bot gebruikt een **speciale groep** (Dienst opvragen EdGPT-EBS) waarin **elk bericht dat uit exact 4 cijfers bestaat** wordt verwerkt. Geen @mention nodig.
## Vereisten
- [Node.js](https://nodejs.org/) 18+
- [wwebjs-api](https://github.com/pedroslopez/whatsapp-web.js) Docker container op nextcloud (poort 3001)
- WhatsApp sessie "EdGpt" actief in wwebjs-api
- Telegram bot token (voor watchdog, optioneel)
## Installatie
```bash
git clone <repo-url>
cd dienst_echo_whatsappgroep
npm install
```
## Configuratie
Kopieer `.env.example` naar `.env` en vul de waarden in:
```env
# WhatsApp API
WHATSAPP_API_ENDPOINT=http://192.168.5.252:3001
WHATSAPP_SESSION=EdGpt
# Webhook
WEBHOOK_PORT=3002
ALLOWED_GROUP=120363407613652082@g.us
# Canonical dienstenroot (alleen-lezen consumer)
DIENSTEN_DIR=../Whatsapp_Dienst/diensten
# Admin
ADMIN_NUMBER=31620248011@c.us
# Telegram watchdog (optioneel, backup kanaal)
TELEGRAM_BOT_URL=http://localhost:3004
TELEGRAM_BOT_TOKEN=
TELEGRAM_ADMIN_CHAT_ID=
```
### wwebjs-api webhook config
In de `.env` van wwebjs-api:
```env
BASE_WEBHOOK_URL=http://192.168.5.252:3002/webhook
ENABLE_LOCAL_CALLBACK_EXAMPLE=FALSE
ENABLE_WEBHOOK=TRUE
```
Belangrijk: gebruik **`docker compose up -d`** (niet `docker restart`) om `.env` wijzigingen door te voeren.
## Data
`Whatsapp_Dienst` is de enige eigenaar van bron-PDF's en gegenereerde dienstenartefacten. De echo-bot heeft geen eigen actieve `diensten/`-directory en leest uitsluitend de root die met `DIENSTEN_DIR` is geconfigureerd.
Onder `DIENSTEN_DIR` staan:
- `dienst-tijden.json` — array met dienstmetadata:
```json
[
{
"dienstnummer": "4002",
"startTime": "09:30",
"endTime": "10:30",
"paginas": [
{ "bestandsnaam": "4002-001.png" },
{ "bestandsnaam": "4002-002.png" }
]
}
]
```
- `png/` — PNG-bestanden met dienstschema's (bijv. `4002-001.png`)
`DIENSTEN_DIR` is de enige data-padvariabele. De echo-bot maakt of genereert geen bestanden in deze root.
## Gebruik
### Starten (nextcloud — huidig)
De bot draait op **nextcloud** via **systemd** (gemigreerd van Mac Mini LaunchDaemon → PM2, nu systemd).
```bash
# Status
ssh aiaiaiaiagent@nextcloud "sudo -A systemctl status dienst-bot"
# Herstarten
ssh aiaiaiaiagent@nextcloud "sudo -A systemctl restart dienst-bot"
# Logs volgen
ssh aiaiaiaiagent@nextcloud "sudo -A journalctl -u dienst-bot -n 50 -f"
```
### Oude Mac Mini (niet meer actief)
Plist: `/Library/LaunchDaemons/nl.eddydevink.dienst-bot.plist` → `.disabled`
Wrapper: `run-dienst-bot.sh` (codesign fix voor Homebrew Node)
## Projectstructuur
```
src/
index.js # Express webhook server + cron job
messageHandler.js # Berichtverwerking en filtering
rateLimiter.js # Spam protectie (max 5 per dag)
dailyStats.js # Dagelijkse aanvraag-teller
dienstService.js # JSON lookup en PNG bestandspaden
whatsappApi.js # WhatsApp API client (bericht + afbeelding)
ackTracker.js # Ack-tracking: hangende media detecteren
telegramBot.js # Telegram watchdog (apart process, health checks)
```
## Welkomstbericht
De groep heeft een **vastgezet welkomstbericht** dat automatisch door EdGpt wordt herplaatst als het is verlopen (30 dagen). De tekst luidt:
> *Welkom in Dienst opvragen EdGpt-EBS. Stuur hier je 4 cijferige dienstnummer en EdGpt zal het prive naar je toe sturen, alleen 4 cijfers meer niet.*
Bij elke interactie met de groep controleert de bot of het pinned bericht nog bestaat en plaatst het opnieuw indien nodig.
## Foutafhandeling
- **Ongeldig dienstnummer**: de bot stuurt een melding terug naar de afzender
- **Fout bij verzenden tekst/PNG**: maximaal 3 pogingen met exponential backoff (1s, 2s, 4s) voor 5xx HTTP-fouten en connectieproblemen (ECONNRESET, ECONNREFUSED, ETIMEDOUT). Bij definitieve fout krijgt de admin een melding.
- **Admin meldingen**: eerst via WhatsApp (`ADMIN_NUMBER`), bij falen (WhatsApp API down) via Telegram watchdog
- **EADDRINUSE (poort 3002 in gebruik)**: de server wacht 5 seconden en herhaalt de bind-poging, met `SO_REUSEADDR` om TIME_WAIT te omzeilen
- **Graceful shutdown**: de server handelt SIGTERM en SIGINT af door actieve connecties netjes te sluiten (10s timeout)
- **Restart-loop preventie**: systemd `Restart=always` met `RestartSec=5`
- **Webhook fout**: wordt gelogd in de logs
## Ack-tracking (ghost-send detectie, sinds 2026-09-24)
Sommige mediabestand-verzendingen keren `success` + message-ID terug maar worden nooit
**bezorgd**: ze blijven op één grijs vinkje hangen (ack 1) en de ontvanger ziet niets
aankomen. Dit is een bestand×ontvanger-interactie in WhatsApp's mediaverwerking (niet de
sessie — zie AGENTS.md). De bot detecteert dit via `message_ack`-events en waarschuwt.
### Hoe het werkt
1. **Vereiste:** wwebjs-api .env — `message_ack` uit `DISABLED_CALLBACKS` halen
(staat er sinds 2026-09-24 niet meer in) + container herstarten.
2. `sendImageMessage` registreert elke verzending (`src/ackTracker.js`) met message-ID,
chat en bestandsnaam; de webhook (`message_ack` in `messageHandler.js`) update de ack.
3. Bij **ack ≥ 2** (bezorgd op apparaat) wordt de tracking verwijderd.
4. Blijft een bericht **> `ACK_STUCK_TIMEOUT_MS` (5 min) op ack < 2** → éénmalige
admin-notificatie (WhatsApp → Telegram-fallback) met bestandsnaam + chat en het advies
"opnieuw renderen + bezorgtest". Er volgt **geen sessie-herstart** (die helpt niet bij
een ack-1-hang) en geen resend naar de aanvrager.
5. **Pruning:** entries worden na `ACK_PRUNED_AFTER_MS` (30 min) opgeruimd; de check-loop
is **activity-driven** (loopt alleen zolang er tracking open staat, geen 30s-polling
in rust).
### Ack-semantiek (wwebjs 1.34.7)
| ack | Betekenis |
|-----|-----------|
| 0 | onbekend/nete verstuurd |
| 1 | verstuurd naar server (één grijs vinkje) — **hangt hier** |
| 2 | bezorgd op apparaat (twee vinkjes) |
| 3 | geopend/gelezen (blauwe vinkjes) |
### Configuratie
| Variabele | Doel | Default |
|-----------|------|---------|
| `ACK_STUCK_TIMEOUT_MS` | Leeftijd zonder ack 2 = stuck | `300000` (5 min) |
| `ACK_PRUNED_AFTER_MS` | Stale tracking opruimen | `1800000` (30 min) |
| `ACK_CHECK_INTERVAL_MS` | Checkinterval (alleen actief bij pending) | `30000` |
Opvallende logregels: `[ack] track …`, `[ack] delivered ack=…`, `[ack] pruned …`.
> **Let op — bridgestore is geen bezorgbewijs (2026-09-24):** de lokale store van wwebjs-api
> (`/chat/fetchMessages`-acks) kan ack 1/2/3 tonen voor berichten die de ontvanger nooit zag
> (ghost-sends). Trek hier géén leveringsconclusie uit. Ack-tracking gebruikt de **live
> `message_ack`-progressie**, niet de store; bevestiging komt van het apparaat van de
> ontvanger.
## Spam protectie
Elke gebruiker kan maximaal `MAX_REQUESTS_PER_DAY` (standaard 5) diensten per dag opvragen. Bij overschrijding krijgt de gebruiker een melding. De teller wordt dagelijks automatisch gereset (bij de eerste request na middernacht).
## Dagelijkse samenvatting
Elke dag om **10:00** stuurt de bot een WhatsApp-bericht naar `ADMIN_NUMBER` met het totaal aantal **alle** 4-cijferige berichten van de vorige dag (00:00 t/m 23:59).
```
📊 Aantal dienstaanvragen gisteren: N
```
De teller wordt bijgehouden in memory door `dailyStats.js` en gereset na verzending van de samenvatting. De scheduling gebruikt `node-cron` (`0 10 * * *`).
## Telegram Watchdog
Sinds juli 2026 draait er een apart **telegram-bot** proces (`src/telegramBot.js`) als backup communicatiekanaal. Dit proces draait als eigen systemd service (`telegram-bot.service`) op nextcloud.
### Wat het doet
- **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 de dienst-bot faalt (WhatsApp API onbereikbaar), wordt het bericht doorgestuurd naar Telegram
### Configuratie
| Variabele | Doel |
|-----------|------|
| `TELEGRAM_BOT_TOKEN` | Token van @BotFather |
| `TELEGRAM_ADMIN_CHAT_ID` | Jouw Telegram chat-ID |
| `TELEGRAM_BOT_PORT=3004` | Poort voor intern `/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 |
### Commando's
```bash
# Status
ssh aiaiaiaiagent@nextcloud "sudo -A systemctl status telegram-bot"
# Herstarten
ssh aiaiaiaiagent@nextcloud "sudo -A systemctl restart telegram-bot"
# Logs
ssh aiaiaiaiagent@nextcloud "sudo -A journalctl -u telegram-bot -n 50 -f"
```
## Licentie
Public Domain