diff --git a/src/dienstService.js b/src/dienstService.js new file mode 100644 index 0000000..6cf42a8 --- /dev/null +++ b/src/dienstService.js @@ -0,0 +1,24 @@ +const fs = require('fs') +const path = require('path') + +let dienstenCache = null + +function loadDiensten() { + if (dienstenCache) return dienstenCache + const jsonPath = process.env.DIENSTEN_JSON + const raw = fs.readFileSync(jsonPath, 'utf-8') + dienstenCache = JSON.parse(raw) + return dienstenCache +} + +function findDienst(dienstnummer) { + const diensten = loadDiensten() + return diensten.find(d => d.dienstnummer === dienstnummer) || null +} + +function getPngFilePaths(dienst) { + const pngDir = process.env.DIENSTEN_PNG + return dienst.paginas.map(p => path.join(pngDir, p.bestandsnaam)) +} + +module.exports = { loadDiensten, findDienst, getPngFilePaths } diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..8a9c0c7 --- /dev/null +++ b/src/index.js @@ -0,0 +1,31 @@ +require('dotenv').config() +const express = require('express') +const { handleWebhook } = require('./messageHandler') +const { loadDiensten } = require('./dienstService') + +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() + +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}`) +}) diff --git a/src/messageHandler.js b/src/messageHandler.js new file mode 100644 index 0000000..e947a3b --- /dev/null +++ b/src/messageHandler.js @@ -0,0 +1,62 @@ +const { findDienst, getPngFilePaths } = require('./dienstService') +const { sendTextMessage, sendImageMessage, notifyAdmin } = require('./whatsappApi') + +const BOT_NAME = (process.env.BOT_NAME || 'EdGpt').toLowerCase() +const ALLOWED_GROUP = process.env.ALLOWED_GROUP + +function extractDienstnummer(body) { + const match = body.match(/\b(\d{4})\b/) + return match ? match[1] : null +} + +function isBotMentioned(message) { + if (message._data?.mentionedJidList?.length > 0) { + return true + } + if (message.body && message.body.toLowerCase().includes(`@${BOT_NAME}`)) { + return true + } + return false +} + +async function handleWebhook(payload) { + if (payload.dataType !== 'message') return + if (payload.sessionId !== process.env.WHATSAPP_SESSION) return + + const message = payload.data?.message + if (!message) return + if (message.fromMe) return + if (message.from !== ALLOWED_GROUP) return + if (!isBotMentioned(message)) return + + const dienstnummer = extractDienstnummer(message.body || '') + if (!dienstnummer) return + + const author = message.author + if (!author) return + + const dienst = findDienst(dienstnummer) + if (!dienst) { + await sendTextMessage(author, `Dienstnummer ${dienstnummer} niet gevonden.`) + return + } + + const pngPaths = getPngFilePaths(dienst) + if (!pngPaths || pngPaths.length === 0) { + await sendTextMessage(author, `Geen pagina's gevonden voor dienst ${dienstnummer}.`) + return + } + + for (const pngPath of pngPaths) { + const filename = pngPath.split('/').pop() + try { + await sendImageMessage(author, pngPath, filename) + } catch (err) { + const errorMsg = `Fout bij verzenden ${filename} naar ${author}: ${err.message}` + console.error(errorMsg) + await notifyAdmin(errorMsg) + } + } +} + +module.exports = { handleWebhook } diff --git a/src/whatsappApi.js b/src/whatsappApi.js new file mode 100644 index 0000000..d8299d3 --- /dev/null +++ b/src/whatsappApi.js @@ -0,0 +1,46 @@ +const axios = require('axios') +const fs = require('fs') + +const API_BASE = process.env.WHATSAPP_API_ENDPOINT +const SESSION = process.env.WHATSAPP_SESSION + +async function sendTextMessage(chatId, text) { + const url = `${API_BASE}/client/sendMessage/${SESSION}` + const response = await axios.post(url, { + chatId, + contentType: 'string', + content: text + }) + return response.data +} + +async function sendImageMessage(chatId, filePath, filename) { + const fileBuffer = fs.readFileSync(filePath) + const base64Data = fileBuffer.toString('base64') + const url = `${API_BASE}/client/sendMessage/${SESSION}` + const response = await axios.post(url, { + chatId, + contentType: 'MessageMedia', + content: { + mimetype: 'image/png', + data: base64Data, + filename: filename + } + }) + return response.data +} + +async function notifyAdmin(errorText) { + const adminNumber = process.env.ADMIN_NUMBER + if (!adminNumber) { + console.error('ADMIN_NUMBER not configured, skipping admin notification') + return + } + try { + await sendTextMessage(adminNumber, `[dienst-bot fout] ${errorText}`) + } catch (err) { + console.error('Failed to notify admin:', err.message) + } +} + +module.exports = { sendTextMessage, sendImageMessage, notifyAdmin }