diff --git a/.env.example b/.env.example index 952158d..168e1aa 100644 --- a/.env.example +++ b/.env.example @@ -11,5 +11,8 @@ BOT_NAME=EdGpt DIENSTEN_JSON=./diensten/dienst-tijden.json DIENSTEN_PNG=./diensten/png +# Rate limiting +MAX_REQUESTS_PER_DAY=5 + # Admin notifications ADMIN_NUMBER=31612345678@c.us diff --git a/README.md b/README.md index 15cf63d..d6f8366 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ npx pm2 logs dienst-bot src/ index.js # Express webhook server messageHandler.js # Berichtverwerking en filtering + rateLimiter.js # Spam protectie (max 5 per dag) dienstService.js # JSON lookup en PNG bestandspaden whatsappApi.js # WhatsApp API client (bericht + afbeelding) ``` @@ -124,6 +125,10 @@ src/ - **Fout bij verzenden PNG**: de admin krijgt een WhatsApp melding - **Webhook fout**: wordt gelogd in PM2 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). + ## Licentie Privé project — niet voor distributie. diff --git a/src/messageHandler.js b/src/messageHandler.js index d57d9f7..789a84b 100644 --- a/src/messageHandler.js +++ b/src/messageHandler.js @@ -1,5 +1,6 @@ const { findDienst, getPngFilePaths } = require('./dienstService') const { sendTextMessage, sendImageMessage, notifyAdmin } = require('./whatsappApi') +const { checkLimit, increment, MAX_REQUESTS_PER_DAY } = require('./rateLimiter') const BOT_NAME = (process.env.BOT_NAME || 'EdGpt').toLowerCase() const ALLOWED_GROUP = process.env.ALLOWED_GROUP @@ -40,6 +41,15 @@ async function handleWebhook(payload) { 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) + const dienst = findDienst(dienstnummer) if (!dienst) { await sendTextMessage(author, `Dienstnummer ${dienstnummer} niet gevonden.`) diff --git a/src/rateLimiter.js b/src/rateLimiter.js new file mode 100644 index 0000000..311dce4 --- /dev/null +++ b/src/rateLimiter.js @@ -0,0 +1,32 @@ +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 }