# 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 (poort 3000) - WhatsApp sessie "EdGpt" actief in wwebjs-api ## Installatie ```bash git clone cd dienst_echo_whatsappgroep npm install ``` ## Configuratie Kopieer `.env.example` naar `.env` en vul de waarden in: ```env # WhatsApp API WHATSAPP_API_ENDPOINT=http://localhost:3000 WHATSAPP_SESSION=EdGpt # Webhook WEBHOOK_PORT=3002 ALLOWED_GROUP=120363407613652082@g.us # Bestanden DIENSTEN_JSON=./diensten/dienst-tijden.json DIENSTEN_PNG=./diensten/png # Admin ADMIN_NUMBER=31620248011@c.us ``` ### wwebjs-api webhook config In de `.env` van wwebjs-api: ```env BASE_WEBHOOK_URL=http://host.docker.internal: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 De map `diensten/` bevat: - `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`) Beide zijn uitgesloten van git via `.gitignore`. ## Gebruik ### Starten De bot draait als macOS LaunchDaemon (`/Library/LaunchDaemons/nl.eddydevink.dienst-bot.plist`) met `RunAtLoad` en `KeepAlive`. De LaunchDaemon start via `run-dienst-bot.sh`, een wrapper die bij elke start de code signing signature van Node.js herstelt. Dit voorkomt uitval na een `brew upgrade node`. Herstarten: ```bash sudo launchctl bootout system /Library/LaunchDaemons/nl.eddydevink.dienst-bot.plist sudo launchctl bootstrap system /Library/LaunchDaemons/nl.eddydevink.dienst-bot.plist ``` ### Stoppen ```bash sudo launchctl bootout system /Library/LaunchDaemons/nl.eddydevink.dienst-bot.plist ``` ### Logs bekijken ```bash tail -f /Users/eddy/.pm2/logs/dienst-bot-out.log tail -f /Users/eddy/.pm2/logs/dienst-bot-error.log ``` Of met PM2: ```bash npx pm2 start src/index.js --name dienst-bot npx pm2 save ``` ### Stoppen ```bash npx pm2 stop dienst-bot ``` ### Logs bekijken ```bash npx pm2 logs dienst-bot ``` ## 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) pinnedMessageService.js # Vastgezet welkomstbericht beheer ``` ## 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 WhatsApp-melding. - **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**: de LaunchDaemon heeft `ThrottleInterval=10` — maximaal 1 herstart per 10 seconden - **Webhook fout**: wordt gelogd in de logs ## 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 * * *`). ## Licentie Public Domain