From 9569485f7814be70d73f0354cf71f5eff893402a Mon Sep 17 00:00:00 2001 From: Eddy Date: Tue, 12 May 2026 22:17:09 +0200 Subject: [PATCH 1/7] docs: add daily WhatsApp summary design spec --- .../specs/2025-05-12-daily-summary-design.md | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 docs/superpowers/specs/2025-05-12-daily-summary-design.md diff --git a/docs/superpowers/specs/2025-05-12-daily-summary-design.md b/docs/superpowers/specs/2025-05-12-daily-summary-design.md new file mode 100644 index 0000000..f28efd3 --- /dev/null +++ b/docs/superpowers/specs/2025-05-12-daily-summary-design.md @@ -0,0 +1,96 @@ +# Dagelijkse WhatsApp samenvatting — Design + +## Doel + +Elke dag om 10:00 uur een WhatsApp-bericht sturen naar de admin (31620248011) met het totaal aantal dienstaanvragen van de vorige dag (00:00 t/m 23:59). + +## Requirement samenvatting + +| Item | Keuze | +|------|-------| +| Inhoud bericht | Alleen totaal aantal | +| Periode | Vorige kalenderdag (00:00-23:59) | +| Persistentie | In-memory (geen disk) | +| Verzendtijd | 10:00 elke ochtend | +| Ontvanger | `ADMIN_NUMBER` uit `.env` (31620248011@c.us) | + +## Architectuur + +### Nieuwe module: `src/dailyStats.js` + +Houdt twee tellers bij in memory: + +- `dailyTotal` — aantal aanvragen vandaag (lopende dag) +- `yesterdayTotal` — aantal aanvragen van gisteren (voor verzending om 10:00) + +Publieke API: + +- `increment()` — verhoogt `dailyTotal` met 1 +- `getYesterdayTotal()` — returned `yesterdayTotal` +- `resetDay()` — zet `yesterdayTotal = dailyTotal`, dan `dailyTotal = 0` + +### Scheduling: `node-cron` + +Cron expressie `0 10 * * *` triggert exact om 10:00 elke dag een callback die: + +1. `dailyStats.resetDay()` aanroept (snapshot van gisteren) +2. `sendTextMessage(adminNumber, bericht)` aanroept om het totaal te versturen +3. Bij falen van verzending: error loggen, maar reset blijft van kracht + +### Bestaande modules — wijzigingen + +**`src/index.js`** +- Importeert `node-cron` en `dailyStats` +- Registreert cron job bij server startup +- Huidige `express` setup blijft ongewijzigd + +**`src/messageHandler.js`** +- Importeert `dailyStats` +- Roept `dailyStats.increment()` aan na de bestaande `increment(author)` (rate limiter) + +### Data flow + +``` +webhook POST /webhook + → messageHandler.handleWebhook() + → rateLimiter.increment(author) [bestaand] + → dailyStats.increment() [nieuw, 1 regel] + +om 10:00 (node-cron trigger): + → dailyStats.resetDay() + → whatsappApi.sendTextMessage(admin, "Aantal dienstaanvragen gisteren: N") +``` + +### Foutafhandeling + +- Verzending faalt → error wordt gelogd, geen retry, reset is al gedaan +- Server herstart voor 10:00 → `dailyTotal` en `yesterdayTotal` zijn 0, cron start opnieuw, om 10:00 wordt bericht met "0" verstuurd (verlies van telling geaccepteerd conform in-memory keuze) +- Server draait niet om 10:00 → geen bericht die dag + +### Afhankelijkheden + +- `node-cron` toevoegen aan `package.json` +- Geen nieuwe `.env` variabelen — `ADMIN_NUMBER` wordt hergebruikt + +### Berichtformaat + +``` +Aantal dienstaanvragen gisteren: N +``` + +Waar `N` het totaal aantal is van 00:00 tot 23:59 van de vorige kalenderdag. + +## Teststrategie + +1. **`dailyStats` unit tests** + - `increment()` verhoogt `dailyTotal` correct + - `resetDay()` kopieert `dailyTotal` naar `yesterdayTotal` en zet `dailyTotal` op 0 + - `getYesterdayTotal()` returned gecachte waarde + +2. **Integratietest** + - Cron callback handmatig aanroepen → verifieer dat `sendTextMessage` wordt aangeroepen met juiste admin nummer en bericht + +## Scope & grenzen + +- Binnen scope: teller bijhouden, cron trigger, WhatsApp verzending +- Buiten scope: persistentie op disk, uitsplitsingen per gebruiker/dienst, herstel van gemiste data na crash, historische rapportages \ No newline at end of file From 5a757ab8be956176af7fcd454c693e7f60a6ebd1 Mon Sep 17 00:00:00 2001 From: Eddy Date: Tue, 12 May 2026 22:21:57 +0200 Subject: [PATCH 2/7] docs: add daily WhatsApp summary implementation plan --- .../plans/2025-05-12-daily-summary.md | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 docs/superpowers/plans/2025-05-12-daily-summary.md diff --git a/docs/superpowers/plans/2025-05-12-daily-summary.md b/docs/superpowers/plans/2025-05-12-daily-summary.md new file mode 100644 index 0000000..3c4c5a7 --- /dev/null +++ b/docs/superpowers/plans/2025-05-12-daily-summary.md @@ -0,0 +1,238 @@ +# Dagelijkse WhatsApp samenvatting — Implementation Plan + +> **Voor agentic workers:** REQUIRED SUB-SKILL: Gebruik superpowers:subagent-driven-development (aanbevolen) of superpowers:executing-plans om dit plan taak-voor-taak te implementeren. Stappen gebruiken checkbox (`- [ ]`) syntax voor tracking. + +**Goal:** Elke dag om 10:00 een WhatsApp-bericht naar de admin sturen met het totaal aantal dienstaanvragen van de vorige dag. + +**Architecture:** Nieuwe module `dailyStats.js` houdt een dagtotaal bij in memory. `messageHandler.js` roept increment aan bij elke aanvraag. `node-cron` triggert om 10:00 de reset en verzending via bestaande `whatsappApi.sendTextMessage()`. + +**Tech Stack:** Node.js, node-cron (nieuwe dependency), bestaande axios/express stack + +**Files:** +- Create: `src/dailyStats.js` +- Modify: `src/messageHandler.js` +- Modify: `src/index.js` +- Modify: `package.json` + +--- + +### Task 1: Create dailyStats module + +**Files:** +- Create: `src/dailyStats.js` + +- [ ] **Step 1: Write the dailyStats module** + +```javascript +let dailyTotal = 0 +let yesterdayTotal = 0 + +function increment() { + dailyTotal++ +} + +function getYesterdayTotal() { + return yesterdayTotal +} + +function resetDay() { + yesterdayTotal = dailyTotal + dailyTotal = 0 +} + +module.exports = { increment, getYesterdayTotal, resetDay } +``` + +- [ ] **Step 2: Verify the module loads without errors** + +Run: `node -e "const m = require('./src/dailyStats'); m.increment(); m.increment(); m.resetDay(); console.log(m.getYesterdayTotal() === 2 ? 'OK' : 'FAIL')"` +Expected: `OK` + +- [ ] **Step 3: Commit** + +```bash +git add src/dailyStats.js +git commit -m "feat: add dailyStats module for tracking daily request totals" +``` + +--- + +### Task 2: Integrate dailyStats into messageHandler + +**Files:** +- Modify: `src/messageHandler.js` + +- [ ] **Step 1: Add dailyStats import and increment call** + +`src/messageHandler.js` line 1-4, add import: + +```javascript +const { findDienst, getPngFilePaths } = require('./dienstService') +const { sendTextMessage, sendImageMessage, notifyAdmin } = require('./whatsappApi') +const { checkLimit, increment, MAX_REQUESTS_PER_DAY } = require('./rateLimiter') +const { increment: incrementDaily } = require('./dailyStats') +``` + +`src/messageHandler.js` after `increment(author)` (line 51), add: + +```javascript + increment(author) + incrementDaily() +``` + +- [ ] **Step 2: Verify messageHandler still loads** + +Run: `node -e "require('./src/messageHandler'); console.log('OK')"` +Expected: `OK` (may show dotenv warning, that's fine) + +- [ ] **Step 3: Commit** + +```bash +git add src/messageHandler.js +git commit -m "feat: integrate dailyStats counter into message handler" +``` + +--- + +### Task 3: Add node-cron and schedule daily 10:00 summary + +**Files:** +- Modify: `package.json` +- Modify: `src/index.js` + +- [ ] **Step 1: Install node-cron** + +Run: `npm install node-cron` +Expected: node-cron added to `node_modules` and `package.json` + +- [ ] **Step 2: Add cron job to index.js** + +`src/index.js` — replace the entire file: + +```javascript +require('dotenv').config() +const cron = require('node-cron') +const express = require('express') +const { handleWebhook } = require('./messageHandler') +const { loadDiensten } = require('./dienstService') +const { resetDay, getYesterdayTotal } = require('./dailyStats') +const { sendTextMessage } = require('./whatsappApi') + +const app = express() +app.use(express.json({ limit: '10mb' })) + +app.post('/webhook', async (req, res) => { + try { + await handleWebhook(req.body) + res.json({ success: true }) + } catch (err) { + console.error('Webhook processing error:', err.message) + res.status(500).json({ success: false, error: err.message }) + } +}) + +app.get('/health', (req, res) => { + res.json({ status: 'ok' }) +}) + +const PORT = process.env.WEBHOOK_PORT || 3002 + +loadDiensten() + +cron.schedule('0 10 * * *', () => { + const adminNumber = process.env.ADMIN_NUMBER + if (!adminNumber) { + console.error('ADMIN_NUMBER not configured, skipping daily summary') + return + } + + resetDay() + const total = getYesterdayTotal() + + sendTextMessage(adminNumber, `Aantal dienstaanvragen gisteren: ${total}`) + .then(() => console.log(`Daily summary sent: ${total} requests`)) + .catch(err => console.error('Failed to send daily summary:', err.message)) +}) + +app.listen(PORT, () => { + console.log(`dienst-bot listening on port ${PORT}`) + console.log(`Allowed group: ${process.env.ALLOWED_GROUP}`) + console.log(`Bot name: ${process.env.BOT_NAME}`) + console.log('Daily summary cron job scheduled for 10:00') +}) +``` + +- [ ] **Step 3: Verify server starts without errors** + +Run: `timeout 3 node src/index.js || true` +Expected: Output shows `dienst-bot listening on port ...` and `Daily summary cron job scheduled for 10:00` + +- [ ] **Step 4: Test cron callback manually** + +Run: `node -e " +require('dotenv').config() +const { increment, resetDay, getYesterdayTotal } = require('./src/dailyStats') +increment() +increment() +increment() +resetDay() +console.log('Total:', getYesterdayTotal()) +console.log(getYesterdayTotal() === 3 ? 'PASS' : 'FAIL') +"` +Expected: `Total: 3` followed by `PASS` + +- [ ] **Step 5: Commit** + +```bash +git add package.json package-lock.json src/index.js +git commit -m "feat: add node-cron daily summary job at 10:00" +``` + +--- + +### Task 4: Verify end-to-end flow + +**Files:** (none, verification only) + +- [ ] **Step 1: Start the server** + +Run: `node src/index.js &` +Wait 2 seconds for startup. + +- [ ] **Step 2: Simulate a webhook request to increment the counter** + +Run: +```bash +curl -s -X POST http://localhost:3002/webhook \ + -H 'Content-Type: application/json' \ + -d '{"dataType":"message","sessionId":"EdGpt","data":{"message":{"from":"120363407356808413@g.us","fromMe":false,"author":"test@c.us","body":"@EdGpt 0001","_data":{"mentionedJidList":["bot@c.us"]}}}}}' +``` +Expected: `{"success":true}` + +- [ ] **Step 3: Trigger the cron callback directly to verify summary message** + +Run: +```bash +node -e " +require('dotenv').config() +const { resetDay, getYesterdayTotal } = require('./src/dailyStats') +const { sendTextMessage } = require('./src/whatsappApi') +resetDay() +const total = getYesterdayTotal() +console.log('Would send to admin:', process.env.ADMIN_NUMBER) +console.log('Message:', 'Aantal dienstaanvragen gisteren: ' + total) +console.log(total > 0 ? 'PASS' : 'FAIL - expected >0') +" +``` +Expected: `PASS` (shows message with total > 0) + +- [ ] **Step 4: Stop the server** + +Run: `kill %1 2>/dev/null; true` + +- [ ] **Step 5: Commit any remaining changes** + +```bash +git status +# Only commit if there are remaining changes +``` \ No newline at end of file From 64e239b368b2ad0496ca7b6130a9aea5dbdc27f4 Mon Sep 17 00:00:00 2001 From: Eddy Date: Tue, 12 May 2026 22:23:47 +0200 Subject: [PATCH 3/7] feat: add dailyStats module for tracking daily request totals --- src/dailyStats.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/dailyStats.js diff --git a/src/dailyStats.js b/src/dailyStats.js new file mode 100644 index 0000000..0e4100c --- /dev/null +++ b/src/dailyStats.js @@ -0,0 +1,17 @@ +let dailyTotal = 0 +let yesterdayTotal = 0 + +function increment() { + dailyTotal++ +} + +function getYesterdayTotal() { + return yesterdayTotal +} + +function resetDay() { + yesterdayTotal = dailyTotal + dailyTotal = 0 +} + +module.exports = { increment, getYesterdayTotal, resetDay } \ No newline at end of file From f7bc98eaed6712454a23951346dfa11e3f70731b Mon Sep 17 00:00:00 2001 From: Eddy Date: Tue, 12 May 2026 22:26:34 +0200 Subject: [PATCH 4/7] feat: integrate dailyStats counter into message handler --- src/messageHandler.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/messageHandler.js b/src/messageHandler.js index 789a84b..e15631f 100644 --- a/src/messageHandler.js +++ b/src/messageHandler.js @@ -1,6 +1,7 @@ const { findDienst, getPngFilePaths } = require('./dienstService') const { sendTextMessage, sendImageMessage, notifyAdmin } = require('./whatsappApi') const { checkLimit, increment, MAX_REQUESTS_PER_DAY } = require('./rateLimiter') +const { increment: incrementDaily } = require('./dailyStats') const BOT_NAME = (process.env.BOT_NAME || 'EdGpt').toLowerCase() const ALLOWED_GROUP = process.env.ALLOWED_GROUP @@ -49,6 +50,7 @@ async function handleWebhook(payload) { } increment(author) + incrementDaily() const dienst = findDienst(dienstnummer) if (!dienst) { From 2dabcf0a8e6b1fc23936f5cc7ef7045b5935850f Mon Sep 17 00:00:00 2001 From: Eddy Date: Tue, 12 May 2026 22:31:37 +0200 Subject: [PATCH 5/7] feat: add node-cron daily summary job at 10:00 --- package-lock.json | 12 +++++++++++- package.json | 3 ++- src/index.js | 21 ++++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8698c05..aa4225f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "axios": "^1.6.0", "dotenv": "^16.4.0", - "express": "^4.18.0" + "express": "^4.18.0", + "node-cron": "^4.2.1" } }, "node_modules/accepts": { @@ -658,6 +659,15 @@ "node": ">= 0.6" } }, + "node_modules/node-cron": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", + "integrity": "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", diff --git a/package.json b/package.json index 1e07eb6..f99a483 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dependencies": { "axios": "^1.6.0", "dotenv": "^16.4.0", - "express": "^4.18.0" + "express": "^4.18.0", + "node-cron": "^4.2.1" } } diff --git a/src/index.js b/src/index.js index 8a9c0c7..a8d9901 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,10 @@ require('dotenv').config() +const cron = require('node-cron') const express = require('express') const { handleWebhook } = require('./messageHandler') const { loadDiensten } = require('./dienstService') +const { resetDay, getYesterdayTotal } = require('./dailyStats') +const { sendTextMessage } = require('./whatsappApi') const app = express() app.use(express.json({ limit: '10mb' })) @@ -24,8 +27,24 @@ const PORT = process.env.WEBHOOK_PORT || 3002 loadDiensten() +cron.schedule('0 10 * * *', () => { + const adminNumber = process.env.ADMIN_NUMBER + if (!adminNumber) { + console.error('ADMIN_NUMBER not configured, skipping daily summary') + return + } + + resetDay() + const total = getYesterdayTotal() + + sendTextMessage(adminNumber, `Aantal dienstaanvragen gisteren: ${total}`) + .then(() => console.log(`Daily summary sent: ${total} requests`)) + .catch(err => console.error('Failed to send daily summary:', err.message)) +}) + app.listen(PORT, () => { console.log(`dienst-bot listening on port ${PORT}`) console.log(`Allowed group: ${process.env.ALLOWED_GROUP}`) console.log(`Bot name: ${process.env.BOT_NAME}`) -}) + console.log('Daily summary cron job scheduled for 10:00') +}) \ No newline at end of file From 2e19540db46b9a6f361a67d2946a23fe342b1604 Mon Sep 17 00:00:00 2001 From: Eddy Date: Tue, 12 May 2026 22:45:17 +0200 Subject: [PATCH 6/7] docs: add daily summary feature to README --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91b3b16..23ab96a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ > De feature ratelimiting is gemaakt door middel van vibecoding met [OpenCode 1.14.40](https://opencode.ai) en Deepseek v4 Pro -WhatsApp bot die dienstschema's (PNG) verstuurt op basis van een dienstnummer in een WhatsApp groep. +WhatsApp bot die dienstschema's (PNG) verstuurt op basis van een dienstnummer in een WhatsApp groep, en dagelijks een samenvatting stuurt. ## Hoe het werkt @@ -114,9 +114,10 @@ npx pm2 logs dienst-bot ``` src/ - index.js # Express webhook server + 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) ``` @@ -131,6 +132,16 @@ src/ 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 dienstaanvragen 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 Privé project — niet voor distributie. From b98ef0b71414841e8422ca9499929ec9cc533de5 Mon Sep 17 00:00:00 2001 From: Eddy Date: Tue, 12 May 2026 22:48:48 +0200 Subject: [PATCH 7/7] docs: add spam protection implementation plan --- .../plans/2026-05-11-spam-protection.md | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-11-spam-protection.md diff --git a/docs/superpowers/plans/2026-05-11-spam-protection.md b/docs/superpowers/plans/2026-05-11-spam-protection.md new file mode 100644 index 0000000..b25c1a3 --- /dev/null +++ b/docs/superpowers/plans/2026-05-11-spam-protection.md @@ -0,0 +1,174 @@ +# Spam Protection Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Limit each user to max 5 dienst requests per day, with automatic daily reset. + +**Architecture:** In-memory rate limiter module that tracks request counts per user per day. Integrated into the message handler pipeline before dienst lookup. Sends a friendly warning when limit is exceeded. + +**Tech Stack:** Node.js, no new dependencies needed + +--- + +### Task 1: Create rate limiter module + +**Files:** +- Create: `src/rateLimiter.js` + +- [ ] **Step 1: Create `src/rateLimiter.js`** + +```js +const MAX_REQUESTS_PER_DAY = parseInt(process.env.MAX_REQUESTS_PER_DAY, 10) || 5 + +const userCounts = new Map() +let currentDate = new Date().toDateString() + +function resetIfNeeded() { + const today = new Date().toDateString() + if (today !== currentDate) { + userCounts.clear() + currentDate = today + console.log('Rate limiter: daily counters reset') + } +} + +function checkLimit(userId) { + resetIfNeeded() + const count = userCounts.get(userId) || 0 + return { + allowed: count < MAX_REQUESTS_PER_DAY, + count, + max: MAX_REQUESTS_PER_DAY + } +} + +function increment(userId) { + resetIfNeeded() + const count = (userCounts.get(userId) || 0) + 1 + userCounts.set(userId, count) + return count +} + +module.exports = { checkLimit, increment, MAX_REQUESTS_PER_DAY } +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/rateLimiter.js +git commit -m "feat: add rate limiter module" +``` + +--- + +### Task 2: Integrate rate limiter into message handler + +**Files:** +- Modify: `src/messageHandler.js:1,25-36` + +- [ ] **Step 1: Add rate limiter import at top of `src/messageHandler.js`** + +Add after line 2: + +```js +const { checkLimit, increment, MAX_REQUESTS_PER_DAY } = require('./rateLimiter') +``` + +- [ ] **Step 2: Add rate limit check in `handleWebhook` after dienstnummer extraction** + +Insert between the `dienstnummer` null check (line 36) and the `console.log` (line 38), replacing lines 38-40 with: + +```js + console.log(`Processing dienst ${dienstnummer} for ${message.author}`) + + const author = message.author + if (!author) return + + const limit = checkLimit(author) + if (!limit.allowed) { + await sendTextMessage(author, `Je hebt vandaag het maximum van ${limit.max} diensten bereikt. Probeer het morgen opnieuw.`) + console.log(`Rate limit reached for ${author} (${limit.count}/${limit.max})`) + return + } + + increment(author) +``` + +- [ ] **Step 3: Verify the file looks correct** + +Run: `node -e "require('./src/messageHandler')"` (should not throw) + +- [ ] **Step 4: Commit** + +```bash +git add src/messageHandler.js +git commit -m "feat: integrate rate limiter into message handler" +``` + +--- + +### Task 3: Add config to .env and restart + +**Files:** +- Modify: `.env` +- Modify: `.env.example` + +- [ ] **Step 1: Add `MAX_REQUESTS_PER_DAY` to `.env`** + +Append to `.env`: + +``` +# Rate limiting +MAX_REQUESTS_PER_DAY=5 +``` + +- [ ] **Step 2: Add `MAX_REQUESTS_PER_DAY` to `.env.example`** + +Append to `.env.example`: + +``` +# Rate limiting +MAX_REQUESTS_PER_DAY=5 +``` + +- [ ] **Step 3: Restart bot and verify** + +```bash +npx pm2 restart dienst-bot && npx pm2 save +npx pm2 logs dienst-bot --lines 5 --nostream +``` + +Expected: bot starts without errors, logs show listening on port 3002. + +- [ ] **Step 4: Commit and push** + +```bash +git add .env.example +git commit -m "feat: add rate limiting config option" +git push +``` + +--- + +### Task 4: Update README + +**Files:** +- Modify: `README.md` + +- [ ] **Step 1: Add rate limiting section to README.md** + +Add after the "Foutafhandeling" section: + +```markdown +## 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). +``` + +- [ ] **Step 2: Commit and push** + +```bash +git add README.md +git commit -m "docs: add spam protection section to README" +git push +```