diff --git a/.env.example b/.env.example index 1c22b3f..dda7ccc 100644 --- a/.env.example +++ b/.env.example @@ -21,17 +21,6 @@ DIENSTEN_DIR=../Whatsapp_Dienst/diensten # Rate limiting MAX_REQUESTS_PER_DAY=5 -# Pauze tussen pagina-sends (ms). Staged-ghost-race-mitigatie: maakt elke pagina -# een effectief losse send (enkele sends falen nooit). Gemeten transmissie-latentie -# 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/AGENTS.md b/AGENTS.md index 87bc6e8..cd1ca25 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -188,20 +188,16 @@ getransmitteerd was) — ze zijn géén bezorgbewijs en gaven verkeerde signalen **`MEDIA_SEND_DELAY_MS`** (bridge-`.env`: `/home/aiaiaiaiagent/wwebjs-api/.env`, nu `500` sinds 2026-09-28 teruggezet van 1000; delay lost de race niet op, zie RCA) ná elke media-send. -- **MERGE = de standaard leverroute sinds 2026-10-05 (`src/messageHandler.js`)**: - een meerpagina-dienst wordt door de bot samengevoegd tot **EEN lange afbeelding** - (`magick … -append`, `MERGE_PAGES=true`, `MAGICK_BIN=/home/linuxbrew/.linuxbrew/bin/magick` - in bot-`.env`) en als één enkele image gestuurd. Elimineert de staged-ghost - (2e pagina van een paar verliest) deterministisch: enkele sends komen aan, gepaarde - sends niet — bewezen (4027: pair verliest blz 2 al dagen; 1 lange afbeelding kwam wél - aan). Bij merge-falen (bv. magick ontbreekt) valt de bot terug op per-pagina-sends - met `PAGE_SEND_DELAY_MS`. Enkele-pagina-diensten worden as-is gestuurd (geen merge). - UX: één scrollbaar beeld i.p.v. twee losse; bewuste keuze boven `sendMediaAsDocument` - (document moet in extern programma geopend worden). -- **Bot-side pagina-pacing (`PAGE_SEND_DELAY_MS`, bot-`.env`, `3000`)** is sindsdien - alleen nog een fallback (bij merge-falen). Gemeten transmissie-latentie naar - ontvanger-sessie ≈ 0.83 s (848/833/827 ms); 3000 = ×3.5 marge. De brug-delay zelf - NIET verhogen (delay loss effect, RCA 26-09). +- **Pre-render = de standaard sinds 2026-10-05.** De sœur-repo **whatsappdienst** + rendert álle dienstschema's vooraf als **één lange JPEG per dienst** (meerpagina = + `magick … -append` tot één beeld; zie https://git.eddydevink.nl/eddy/whatsappdienst/issues/2) + en `dienst-tijden.json` verwijst naar één bestand per dienst. `dienst-bot` (deze repo) + stuurt **gewoon de bestanden uit `paginas[].bestandsnaam`** zoals voorheen — geen + runtime-merge, geen pacing. Single send = geen staged-ghost (2e pagina van een paar + verliest). UX: één scrollbaar beeld per dienst. +- Tijdelijk had de bot een runtime-merge als fallback (`MERGE_PAGES`/`MAGICK_BIN`/ + `PAGE_SEND_DELAY_MS`); dat is verwijderd zodra whatsappdienst single-file levert. De + historie (ack-gate-falsificatie, JPEG-conversie, 4027-casus) staat hieronder. - **Bridge-`.env`-wijzigingen**: `env_file` wordt bij container-create gelezen → een `MEDIA_SEND_DELAY_MS`-wijziging werkt pas na een **recreate** (`cd ~/wwebjs-api && sudo -A docker compose up -d`); een kale `docker restart` (ook de diff --git a/README.md b/README.md index f4e970f..3fd3798 100644 --- a/README.md +++ b/README.md @@ -151,14 +151,15 @@ gaven verkeerde signalen. Enige betrouwbare bronnen: het apparaat en `getInfo` (`message.getInfo()` → `read`/`readRemaining`). De bridge-`delivery`/acs van de store zijn nooit leveringsbewijs. -> **🛡️ Eén lange afbeelding per dienst (2026-10-05) — de standaard.** Meerpagina-diensten -> worden door de bot samengevoegd tot **één enkele image** (`magick -append`, -> `MERGE_PAGES=true`) en als één send verstuurd. Elimineert de staged-ghost (2e pagina van -> een paar verliest): gepaarde media-sends overlappen en de 2e verliest (in store + ack2 -> maar nooit verzonden); één enkele send komt deterministisch aan — verificatie 2026-10-05 -> (ook JPEG, delay en ack-gate losten het niet op). Bij merge-falen valt terug op -> per-pagina-sends met `PAGE_SEND_DELAY_MS`. Keuze vóór `sendMediaAsDocument` (geen externe -> opener nodig). Details/config: zie AGENTS.md en vault "Whatsapp rest-api.md". +> **🛡️ Pre-render = de standaard (2026-10-05).** De sœur-repo **whatsappdienst** +> rendert álle dienstschema's vooraf als **één lange JPEG per dienst** (meerpagina = +> verticaal samengevoegd) en `dienst-tijden.json` wijst naar één bestand. Deze bot stuurt +> gewoon de bestanden uit `paginas[].bestandsnaam` (één send per dienst) — geen +> runtime-merge. Single send elimineert de staged-ghost ("2e pagina van een paar +> verliest"): gepaarde media-sends overlappen en de 2e verliest; één enkele send komt +> deterministisch aan (verificatie 2026-10-05, incl. dat JPEG, delay en ack-gate het niet +> oplosten). Details: zie AGENTS.md — *Whatsapp rest-api.md* en whatsappdienst +> issue #2. ## Spam protectie diff --git a/src/messageHandler.js b/src/messageHandler.js index 4263616..bd5edf2 100644 --- a/src/messageHandler.js +++ b/src/messageHandler.js @@ -2,46 +2,6 @@ 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, [...filePaths, '-append', 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 -// wacht per pagina alleen op het optimistische brug-signaal (staging), en de -// bridge-delay (MEDIA_SEND_DELAY_MS) wacht óók op staging (delay loss effect). -// Een bot-side wacht maakt van elke pagina een effectief losse send: die komen -// empirisch wél aan (enkele sends falen nooit). -const PAGE_SEND_DELAY_MS = (() => { - const parsed = parseInt(process.env.PAGE_SEND_DELAY_MS, 10) - return Number.isFinite(parsed) && parsed >= 0 ? parsed : 15000 -})() - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)) -} const ALLOWED_GROUP = process.env.ALLOWED_GROUP const ALLOWED_GROUP_NAMES = (process.env.ALLOWED_GROUP_NAME || '') @@ -100,27 +60,17 @@ async function handleWebhook(payload) { return } - // Strategie: meerpagina-diensten als EEN lange afbeelding (merge) sturen — zie - // onder. pngPaths = de bronbestanden (nu .jpg, bestandsnaam uit json). + // Media-pacing voor opeenvolgende sends gebeurt bridge-breed (wwebjs-api + // serialiseert + throttle de media-sends; MEDIA_SEND_DELAY_MS in bridge-.env). const pngPaths = getPngFilePaths(dienst) if (!pngPaths || pngPaths.length === 0) { await sendTextMessage(author, `Geen pagina's gevonden voor dienst ${dienstnummer}.`) return } - // 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() + for (let i = 0; i < pngPaths.length; i++) { + const pngPath = pngPaths[i] + const filename = pngPath.split('/').pop() try { await sendImageMessage(author, pngPath, filename) } catch (err) { @@ -128,15 +78,6 @@ async function handleWebhook(payload) { console.error(errorMsg) await notifyAdmin(errorMsg) } - // 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, () => {}) } }