245 lines
9.2 KiB
Markdown
245 lines
9.2 KiB
Markdown
# 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
|