Compare commits

...
Author SHA1 Message Date
Eddy
7176f09387 docs: add spam protection section and rateLimiter.js to README 2026-05-11 20:11:45 +02:00
Eddy
100b2af449 feat: add rate limiting config option 2026-05-11 20:10:42 +02:00
Eddy
3d933c96a6 feat: integrate rate limiter into message handler 2026-05-11 20:07:07 +02:00
Eddy
52c51fbf3f feat: add rate limiter module 2026-05-11 20:02:16 +02:00
4 changed files with 50 additions and 0 deletions

View file

@ -11,5 +11,8 @@ BOT_NAME=EdGpt
DIENSTEN_JSON=./diensten/dienst-tijden.json DIENSTEN_JSON=./diensten/dienst-tijden.json
DIENSTEN_PNG=./diensten/png DIENSTEN_PNG=./diensten/png
# Rate limiting
MAX_REQUESTS_PER_DAY=5
# Admin notifications # Admin notifications
ADMIN_NUMBER=31612345678@c.us ADMIN_NUMBER=31612345678@c.us

View file

@ -114,6 +114,7 @@ npx pm2 logs dienst-bot
src/ src/
index.js # Express webhook server index.js # Express webhook server
messageHandler.js # Berichtverwerking en filtering messageHandler.js # Berichtverwerking en filtering
rateLimiter.js # Spam protectie (max 5 per dag)
dienstService.js # JSON lookup en PNG bestandspaden dienstService.js # JSON lookup en PNG bestandspaden
whatsappApi.js # WhatsApp API client (bericht + afbeelding) whatsappApi.js # WhatsApp API client (bericht + afbeelding)
``` ```
@ -124,6 +125,10 @@ src/
- **Fout bij verzenden PNG**: de admin krijgt een WhatsApp melding - **Fout bij verzenden PNG**: de admin krijgt een WhatsApp melding
- **Webhook fout**: wordt gelogd in PM2 logs - **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 ## Licentie
Privé project — niet voor distributie. Privé project — niet voor distributie.

View file

@ -1,5 +1,6 @@
const { findDienst, getPngFilePaths } = require('./dienstService') const { findDienst, getPngFilePaths } = require('./dienstService')
const { sendTextMessage, sendImageMessage, notifyAdmin } = require('./whatsappApi') 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 BOT_NAME = (process.env.BOT_NAME || 'EdGpt').toLowerCase()
const ALLOWED_GROUP = process.env.ALLOWED_GROUP const ALLOWED_GROUP = process.env.ALLOWED_GROUP
@ -40,6 +41,15 @@ async function handleWebhook(payload) {
const author = message.author const author = message.author
if (!author) return 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) const dienst = findDienst(dienstnummer)
if (!dienst) { if (!dienst) {
await sendTextMessage(author, `Dienstnummer ${dienstnummer} niet gevonden.`) await sendTextMessage(author, `Dienstnummer ${dienstnummer} niet gevonden.`)

32
src/rateLimiter.js Normal file
View file

@ -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 }