refactor: verwijder alle Appium-fallback (wa-appium-api afgedaan, nooit meer)
This commit is contained in:
parent
18f6eb41ff
commit
431d74ed70
3 changed files with 6 additions and 112 deletions
12
src/index.js
12
src/index.js
|
|
@ -18,17 +18,6 @@ async function notifyTelegramOnline() {
|
||||||
console.error('Telegram startup notification failed:', err.message)
|
console.error('Telegram startup notification failed:', err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function registerWhatsAppWebhook() {
|
|
||||||
const subscribeURL = process.env.WHATSAPP_WEBHOOK_SUBSCRIBE_URL
|
|
||||||
const targetURL = process.env.WHATSAPP_WEBHOOK_TARGET_URL
|
|
||||||
if (!subscribeURL || !targetURL) return
|
|
||||||
try {
|
|
||||||
await axios.post(subscribeURL, { url: targetURL }, { timeout: 5000 })
|
|
||||||
console.log(`Registered WhatsApp fallback webhook: ${targetURL}`)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('WhatsApp fallback webhook registration failed:', err.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
app.use(express.json({ limit: '10mb' }))
|
app.use(express.json({ limit: '10mb' }))
|
||||||
|
|
@ -96,7 +85,6 @@ async function startServer() {
|
||||||
console.log(`Bot name: ${process.env.BOT_NAME}`)
|
console.log(`Bot name: ${process.env.BOT_NAME}`)
|
||||||
console.log('Daily summary cron job scheduled for 10:00')
|
console.log('Daily summary cron job scheduled for 10:00')
|
||||||
notifyTelegramOnline()
|
notifyTelegramOnline()
|
||||||
registerWhatsAppWebhook()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function shutdown(signal) {
|
function shutdown(signal) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const API_BASE = process.env.WHATSAPP_API_ENDPOINT
|
|
||||||
const { findDienst, getPngFilePaths } = require('./dienstService')
|
const { findDienst, getPngFilePaths } = require('./dienstService')
|
||||||
const { sendTextMessage, sendImageMessage, notifyAdmin, sendChatSeen } = require('./whatsappApi')
|
const { sendTextMessage, sendImageMessage, notifyAdmin, sendChatSeen } = require('./whatsappApi')
|
||||||
const { checkLimit, increment, MAX_REQUESTS_PER_DAY } = require('./rateLimiter')
|
const { checkLimit, increment, MAX_REQUESTS_PER_DAY } = require('./rateLimiter')
|
||||||
|
|
@ -13,23 +10,6 @@ const ALLOWED_GROUP_NAMES = (process.env.ALLOWED_GROUP_NAME || '')
|
||||||
.map(name => name.trim())
|
.map(name => name.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
const ALLOWED_CHAT_IDS = new Set([ALLOWED_GROUP, ...ALLOWED_GROUP_NAMES].filter(Boolean))
|
const ALLOWED_CHAT_IDS = new Set([ALLOWED_GROUP, ...ALLOWED_GROUP_NAMES].filter(Boolean))
|
||||||
const appiumEventCooldown = 24 * 60 * 60 * 1000
|
|
||||||
const appiumEventStatePath = process.env.APPIUM_EVENT_STATE_PATH ||
|
|
||||||
path.join(__dirname, '..', 'appium-event-state.json')
|
|
||||||
let appiumEventState = { events: [] }
|
|
||||||
try {
|
|
||||||
appiumEventState = JSON.parse(fs.readFileSync(appiumEventStatePath, 'utf8'))
|
|
||||||
if (!Array.isArray(appiumEventState.events)) appiumEventState = { events: [] }
|
|
||||||
appiumEventState.events = appiumEventState.events.map(event => {
|
|
||||||
const parts = event.key.split('\u0000')
|
|
||||||
return {
|
|
||||||
key: [normalizeChatId(parts[0]), parts[1], parts[2]].join('\u0000'),
|
|
||||||
processedAt: event.processedAt || event.timestamp || 0,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
appiumEventState = { events: [] }
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeChatId(chatId) {
|
function normalizeChatId(chatId) {
|
||||||
return (chatId || '')
|
return (chatId || '')
|
||||||
|
|
@ -38,30 +18,6 @@ function normalizeChatId(chatId) {
|
||||||
.trim()
|
.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAppiumEventKey(payload) {
|
|
||||||
return [normalizeChatId(payload.chatId), payload.sender, payload.text].join('\u0000')
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDuplicateAppiumEvent(payload) {
|
|
||||||
const now = Date.now()
|
|
||||||
appiumEventState.events = appiumEventState.events.filter(event =>
|
|
||||||
now - (event.processedAt || event.timestamp || 0) < appiumEventCooldown
|
|
||||||
)
|
|
||||||
return appiumEventState.events.some(event =>
|
|
||||||
event.key === getAppiumEventKey(payload)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function markAppiumEventProcessed(payload) {
|
|
||||||
appiumEventState.events.push({
|
|
||||||
key: getAppiumEventKey(payload),
|
|
||||||
processedAt: Date.now(),
|
|
||||||
})
|
|
||||||
appiumEventState.events = appiumEventState.events.slice(-500)
|
|
||||||
const temporaryPath = `${appiumEventStatePath}.tmp`
|
|
||||||
fs.writeFileSync(temporaryPath, JSON.stringify(appiumEventState))
|
|
||||||
fs.renameSync(temporaryPath, appiumEventStatePath)
|
|
||||||
}
|
|
||||||
function extractDienstnummer(body) {
|
function extractDienstnummer(body) {
|
||||||
const match = body.match(/^\s*(\d{4,5})\s*$/)
|
const match = body.match(/^\s*(\d{4,5})\s*$/)
|
||||||
return match ? match[1] : null
|
return match ? match[1] : null
|
||||||
|
|
@ -75,19 +31,10 @@ async function handleWebhook(payload) {
|
||||||
if (id) handleAck(id, ack)
|
if (id) handleAck(id, ack)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const isWWebEvent = payload.dataType === 'message'
|
if (payload.dataType !== 'message') return
|
||||||
const isAppiumEvent = Boolean(payload.sender && payload.chatId)
|
if (payload.sessionId !== process.env.WHATSAPP_SESSION) return
|
||||||
if (!isWWebEvent && !isAppiumEvent) return
|
|
||||||
if (isAppiumEvent && isDuplicateAppiumEvent(payload)) return
|
const message = payload.data?.message
|
||||||
if (isWWebEvent && payload.sessionId !== process.env.WHATSAPP_SESSION) return
|
|
||||||
const message = isWWebEvent
|
|
||||||
? payload.data?.message
|
|
||||||
: {
|
|
||||||
fromMe: false,
|
|
||||||
from: payload.chatId,
|
|
||||||
author: payload.sender,
|
|
||||||
body: payload.text || '',
|
|
||||||
}
|
|
||||||
if (!message) return
|
if (!message) return
|
||||||
if (message.fromMe) return
|
if (message.fromMe) return
|
||||||
if (!ALLOWED_CHAT_IDS.has(normalizeChatId(message.from))) return
|
if (!ALLOWED_CHAT_IDS.has(normalizeChatId(message.from))) return
|
||||||
|
|
@ -103,9 +50,7 @@ async function handleWebhook(payload) {
|
||||||
|
|
||||||
console.log(`Processing dienst ${dienstnummer} for ${message.author}`)
|
console.log(`Processing dienst ${dienstnummer} for ${message.author}`)
|
||||||
|
|
||||||
const author = isWWebEvent
|
const author = message.author || message.from
|
||||||
? message.author || message.from
|
|
||||||
: message.author
|
|
||||||
if (!author) return
|
if (!author) return
|
||||||
|
|
||||||
const limit = checkLimit(author)
|
const limit = checkLimit(author)
|
||||||
|
|
@ -120,32 +65,25 @@ async function handleWebhook(payload) {
|
||||||
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.`)
|
||||||
if (isAppiumEvent) markAppiumEventProcessed(payload)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const pngPaths = getPngFilePaths(dienst)
|
const pngPaths = getPngFilePaths(dienst)
|
||||||
if (!pngPaths || pngPaths.length === 0) {
|
if (!pngPaths || pngPaths.length === 0) {
|
||||||
await sendTextMessage(author, `Geen pagina's gevonden voor dienst ${dienstnummer}.`)
|
await sendTextMessage(author, `Geen pagina's gevonden voor dienst ${dienstnummer}.`)
|
||||||
if (isAppiumEvent) markAppiumEventProcessed(payload)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let sendFailed = false
|
|
||||||
for (const pngPath of pngPaths) {
|
for (const pngPath of pngPaths) {
|
||||||
const filename = pngPath.split('/').pop()
|
const filename = pngPath.split('/').pop()
|
||||||
try {
|
try {
|
||||||
await sendImageMessage(author, pngPath, filename)
|
await sendImageMessage(author, pngPath, filename)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
sendFailed = true
|
|
||||||
const errorMsg = `Fout bij verzenden ${filename} naar ${author}: ${err.message}`
|
const errorMsg = `Fout bij verzenden ${filename} naar ${author}: ${err.message}`
|
||||||
console.error(errorMsg)
|
console.error(errorMsg)
|
||||||
if (API_BASE.includes(':3001')) {
|
|
||||||
await notifyAdmin(errorMsg)
|
await notifyAdmin(errorMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!sendFailed && isAppiumEvent) markAppiumEventProcessed(payload)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { handleWebhook, extractDienstnummer }
|
module.exports = { handleWebhook, extractDienstnummer }
|
||||||
|
|
|
||||||
|
|
@ -47,38 +47,7 @@ async function sendTextMessage(chatId, text) {
|
||||||
}, `sendTextMessage to ${chatId}`)
|
}, `sendTextMessage to ${chatId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitForAppiumJob(jobId) {
|
|
||||||
for (let attempt = 0; attempt < 120; attempt++) {
|
|
||||||
const response = await fetch(`${API_BASE}/jobs/${jobId}`)
|
|
||||||
if (!response.ok) throw new Error(`Appium job lookup failed: ${response.status}`)
|
|
||||||
const job = await response.json()
|
|
||||||
if (job.status === 'done') return job.result
|
|
||||||
if (job.status === 'failed') throw new Error(job.error || 'Appium job failed')
|
|
||||||
await sleep(1000)
|
|
||||||
}
|
|
||||||
throw new Error(`Appium job timed out: ${jobId}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendImageMessage(chatId, filePath, filename) {
|
async function sendImageMessage(chatId, filePath, filename) {
|
||||||
if (API_BASE.includes(':3010')) {
|
|
||||||
return withRetry(async () => {
|
|
||||||
const fileBuffer = fs.readFileSync(filePath)
|
|
||||||
const form = new FormData()
|
|
||||||
form.append('image', new Blob([fileBuffer], { type: 'image/png' }), filename)
|
|
||||||
form.append('chatName', chatId.replace(/@[cg]\.us$/, ''))
|
|
||||||
const response = await fetch(`${API_BASE}/messages/send-media`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: form
|
|
||||||
})
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`sendImageMessage failed: ${response.status} ${response.statusText}`)
|
|
||||||
}
|
|
||||||
const queued = await response.json()
|
|
||||||
if (!queued.jobId) throw new Error('Appium did not return a job ID')
|
|
||||||
return waitForAppiumJob(queued.jobId)
|
|
||||||
}, `sendImageMessage ${filename} to ${chatId}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return withRetry(async () => {
|
return withRetry(async () => {
|
||||||
const fileBuffer = fs.readFileSync(filePath)
|
const fileBuffer = fs.readFileSync(filePath)
|
||||||
const base64Data = fileBuffer.toString('base64')
|
const base64Data = fileBuffer.toString('base64')
|
||||||
|
|
@ -120,7 +89,6 @@ async function notifyAdmin(errorText) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendChatSeen(chatId) {
|
async function sendChatSeen(chatId) {
|
||||||
if (API_BASE.includes(':3010')) return null
|
|
||||||
const url = `${API_BASE}/client/sendSeen/${SESSION}`
|
const url = `${API_BASE}/client/sendSeen/${SESSION}`
|
||||||
const response = await axios.post(url, { chatId })
|
const response = await axios.post(url, { chatId })
|
||||||
return response.data
|
return response.data
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue