From 80fc78e093eac1195d29a39d9bb551c32b4ed2ef Mon Sep 17 00:00:00 2001 From: Eddy de Vink Date: Mon, 5 Oct 2026 21:21:03 +0200 Subject: [PATCH] bot: meerpagina-diensten als 1 lange afbeelding (merge) - elimineert staged-ghost (2e pagina verliest) --- .env.example | 6 +++++ src/messageHandler.js | 55 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index 4bd9290..1c22b3f 100644 --- a/.env.example +++ b/.env.example @@ -26,6 +26,12 @@ MAX_REQUESTS_PER_DAY=5 # naar ontvanger ≈ 0.83 s (2026-10-05); default = 3 x ~3.5. 0 = geen pauze. PAGE_SEND_DELAY_MS=3000 +# Merge: meerpagina-diensten als EEN lange afbeelding sturen (elimineert de +# staged-ghost "2e pagina van een paar verliest"). 'false' = per-pagina sturen. +MERGE_PAGES=true +# Pad naar ImageMagick (magick -append). Server: /home/linuxbrew/.linuxbrew/bin/magick +MAGICK_BIN=magick + # Admin notifications ADMIN_NUMBER=31612345678@c.us diff --git a/src/messageHandler.js b/src/messageHandler.js index 688a0bf..365cdc3 100644 --- a/src/messageHandler.js +++ b/src/messageHandler.js @@ -2,6 +2,31 @@ const { findDienst, getPngFilePaths } = require('./dienstService') const { sendTextMessage, sendImageMessage, notifyAdmin, sendChatSeen } = require('./whatsappApi') const { checkLimit, increment, MAX_REQUESTS_PER_DAY } = require('./rateLimiter') const { increment: incrementDaily } = require('./dailyStats') +const { execFile } = require('child_process') +const { promisify } = require('util') +const path = require('path') +const os = require('os') +const fs = require('fs') +const crypto = require('crypto') +const execFileP = promisify(execFile) + +// Merge-helper: combineer alle pagina's tot één lange afbeelding en stuur die als +// EÉN image. De staged-ghost is een "2e pagina van een paar verliest" (RCA +// 2026-09-26; gemeten 2026-10-05: ook JPEG, delay en ack-gate lossen het niet op); +// één enkele send van een samengevoegde afbeelding komt deterministisch aan. +const MAGICK_BIN = process.env.MAGICK_BIN || 'magick' +const MERGE_PAGES = process.env.MERGE_PAGES !== 'false' + +async function mergePages(filePaths, dienstnummer) { + const out = path.join(os.tmpdir(), `dienst-${dienstnummer}-${crypto.randomBytes(4).toString('hex')}.jpg`) + try { + await execFileP(MAGICK_BIN, ['-append', ...filePaths, out]) + return out + } catch (err) { + console.error(`magick merge failed for ${dienstnummer}: ${err.message}`) + return null + } +} // Pauze tussen opeenvolgende pagina-sends. De staged-ghost-race (RCA 2026-09-26) // treedt op als de 2e pagina start terwijl de 1e nog écht transmetteert — de bot @@ -75,17 +100,27 @@ async function handleWebhook(payload) { return } - // Media-pacing voor opeenvolgende sends gebeurt bridge-breed (wwebjs-api - // serialiseert + throttle de media-sends; MEDIA_SEND_DELAY_MS in bridge-.env). + // Strategie: meerpagina-diensten als EEN lange afbeelding (merge) sturen — zie + // onder. pngPaths = de bronbestanden (nu .jpg, bestandsnaam uit json). const pngPaths = getPngFilePaths(dienst) if (!pngPaths || pngPaths.length === 0) { await sendTextMessage(author, `Geen pagina's gevonden voor dienst ${dienstnummer}.`) return } - for (let i = 0; i < pngPaths.length; i++) { - const pngPath = pngPaths[i] - const filename = pngPath.split('/').pop() + // Merge alle pagina's tot één lange afbeelding (één send) — elimineert de + // staged-ghost (2e pagina van een paar verliest; zie RCA 2026-09-26 + verificatie + // 2026-10-05). 1 pagina = as-is sturen; bij merge-falen valt terug op per-pagina. + let sendPaths = pngPaths + let tempMerged = null + if (MERGE_PAGES && pngPaths.length > 1) { + tempMerged = await mergePages(pngPaths, dienstnummer) + if (tempMerged) sendPaths = [tempMerged] + } + + for (let i = 0; i < sendPaths.length; i++) { + const pngPath = sendPaths[i] + const filename = tempMerged ? `${dienstnummer}.jpg` : pngPath.split('/').pop() try { await sendImageMessage(author, pngPath, filename) } catch (err) { @@ -93,12 +128,16 @@ async function handleWebhook(payload) { console.error(errorMsg) await notifyAdmin(errorMsg) } - // Wacht vóór de volgende pagina zodat de echte transmissie van deze pagina - // klaar is (staged-ghost-race RCA) — 0 zet de pauze uit. - if (i < pngPaths.length - 1 && PAGE_SEND_DELAY_MS > 0) { + // Wacht vóór de volgende pagina (alleen bij niet-gemerge pagina-sends); + // 0 zet de pauze uit. + if (!tempMerged && i < sendPaths.length - 1 && PAGE_SEND_DELAY_MS > 0) { await sleep(PAGE_SEND_DELAY_MS) } } + + if (tempMerged) { + fs.unlink(tempMerged, () => {}) + } } module.exports = { handleWebhook, extractDienstnummer }