fix(ack): geen sessie-herstart bij hangende media — alleen detectie+notificatie+bestandsflag
This commit is contained in:
parent
be3dde4908
commit
a6cfcfc219
3 changed files with 17 additions and 38 deletions
|
|
@ -21,6 +21,10 @@ DIENSTEN_DIR=../Whatsapp_Dienst/diensten
|
||||||
# Rate limiting
|
# Rate limiting
|
||||||
MAX_REQUESTS_PER_DAY=5
|
MAX_REQUESTS_PER_DAY=5
|
||||||
|
|
||||||
|
# Ack tracking (ghost-send detectie; vereist message_ack aangezet in wwebjs-api .env)
|
||||||
|
ACK_STUCK_TIMEOUT_MS=300000 # 5 min zonder ack 2 = stuck
|
||||||
|
ACK_CHECK_INTERVAL_MS=30000 # checkinterval
|
||||||
|
|
||||||
# Admin notifications
|
# Admin notifications
|
||||||
ADMIN_NUMBER=31612345678@c.us
|
ADMIN_NUMBER=31612345678@c.us
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
// ackTracker.js
|
// ackTracker.js
|
||||||
// Tracks delivery acks (message_ack) of outgoing media to detect "ghost" sends:
|
// Tracks delivery acks (message_ack) of outgoing media to detect sends that
|
||||||
// a message returns success + a message ID but never receives a delivery ack
|
// never reach the recipient: a message returns success + a message ID but
|
||||||
// (stuck below ack 2). Root cause is a stale "ghost" session — reported as
|
// stays below ack 2 (one grey tick).
|
||||||
// wwebjs #201849 (valid ID, no ACK, message not in own chat history).
|
//
|
||||||
|
// Findings (2026-09-24): an ack-1 hang is a bestand×ontvanger interaction, NOT
|
||||||
|
// the session. The same file hung (ack 1) for one recipient yet delivered
|
||||||
|
// (ack 3) to another in the same fresh session. A session restart does NOT fix
|
||||||
|
// an ack-1 hang (the same file kept hanging after restart) and needlessly
|
||||||
|
// disrupts production — so we deliberately do NOT auto-restart here. We only
|
||||||
|
// detect + notify + name the file, so it can be re-rendered and re-tested.
|
||||||
//
|
//
|
||||||
// Requires the bridge (wwebjs-api) to emit message_ack: remove 'message_ack'
|
// Requires the bridge (wwebjs-api) to emit message_ack: remove 'message_ack'
|
||||||
// from DISABLED_CALLBACKS in the wwebjs-api .env and restart the container.
|
// from DISABLED_CALLBACKS in the wwebjs-api .env and restart the container.
|
||||||
|
|
||||||
const STUCK_TIMEOUT_MS = parseInt(process.env.ACK_STUCK_TIMEOUT_MS, 10) || 5 * 60 * 1000
|
const STUCK_TIMEOUT_MS = parseInt(process.env.ACK_STUCK_TIMEOUT_MS, 10) || 5 * 60 * 1000
|
||||||
const AUTO_RESTART_SESSION = (process.env.ACK_AUTO_RESTART_SESSION || 'true').toLowerCase() === 'true'
|
|
||||||
const RESTART_COOLDOWN_MS = parseInt(process.env.ACK_RESTART_COOLDOWN_MS, 10) || 30 * 60 * 1000
|
|
||||||
|
|
||||||
// messageId -> { chatId, filename, sentAt, ack, notifiedStuck }
|
// messageId -> { chatId, filename, sentAt, ack, notifiedStuck }
|
||||||
const sends = new Map()
|
const sends = new Map()
|
||||||
let lastRestartAt = 0
|
|
||||||
|
|
||||||
function registerSend(messageId, chatId, filename) {
|
function registerSend(messageId, chatId, filename) {
|
||||||
if (!messageId) return
|
if (!messageId) return
|
||||||
|
|
@ -58,24 +61,10 @@ function pendingCount() {
|
||||||
return sends.size
|
return sends.size
|
||||||
}
|
}
|
||||||
|
|
||||||
function canRestart() {
|
|
||||||
return AUTO_RESTART_SESSION && Date.now() - lastRestartAt > RESTART_COOLDOWN_MS
|
|
||||||
}
|
|
||||||
|
|
||||||
function markRestarted() {
|
|
||||||
lastRestartAt = Date.now()
|
|
||||||
// After a session restart the old local IDs no longer ack; stop tracking them.
|
|
||||||
sends.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
registerSend,
|
registerSend,
|
||||||
handleAck,
|
handleAck,
|
||||||
stuckEntries,
|
stuckEntries,
|
||||||
markNotified,
|
markNotified,
|
||||||
pendingCount,
|
pendingCount
|
||||||
AUTO_RESTART_SESSION,
|
|
||||||
RESTART_COOLDOWN_MS,
|
|
||||||
canRestart,
|
|
||||||
markRestarted
|
|
||||||
}
|
}
|
||||||
18
src/index.js
18
src/index.js
|
|
@ -115,17 +115,7 @@ async function startServer() {
|
||||||
process.on('SIGINT', () => shutdown('SIGINT'))
|
process.on('SIGINT', () => shutdown('SIGINT'))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function restartSession() {
|
async function checkStuckAcks() {
|
||||||
const url = `${process.env.WHATSAPP_API_ENDPOINT}/session/restart/${process.env.WHATSAPP_SESSION}`
|
|
||||||
try {
|
|
||||||
await axios.get(url, { timeout: 15000 })
|
|
||||||
console.log(`[ack] Session ${process.env.WHATSAPP_SESSION} restarted (stuck ack detected)`)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('[ack] Session restart failed:', err.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkStuckAcks() {
|
|
||||||
const stuck = ackTracker.stuckEntries()
|
const stuck = ackTracker.stuckEntries()
|
||||||
if (stuck.length === 0) return
|
if (stuck.length === 0) return
|
||||||
for (const s of stuck) {
|
for (const s of stuck) {
|
||||||
|
|
@ -134,14 +124,10 @@ function checkStuckAcks() {
|
||||||
notifyAdmin(
|
notifyAdmin(
|
||||||
`[dienst-bot] hangend bericht: ${s.filename} → ${s.chatId} ` +
|
`[dienst-bot] hangend bericht: ${s.filename} → ${s.chatId} ` +
|
||||||
`(ack ${s.ack}, ${Math.round((Date.now() - s.sentAt) / 1000)}s oud). ` +
|
`(ack ${s.ack}, ${Math.round((Date.now() - s.sentAt) / 1000)}s oud). ` +
|
||||||
`Sessie ${process.env.WHATSAPP_SESSION} is mogelijk stale.`
|
`Bestand opnieuw renderen + bezorgtest, geen sessie-herstart (die helpt hier niet).`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ackTracker.canRestart()) {
|
|
||||||
ackTracker.markRestarted()
|
|
||||||
restartSession()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ackCheckIntervalMs = parseInt(process.env.ACK_CHECK_INTERVAL_MS, 10) || 30000
|
const ackCheckIntervalMs = parseInt(process.env.ACK_CHECK_INTERVAL_MS, 10) || 30000
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue