WhatsApp AI Assistant
Module 20 · ai · intermediate
parse.js
// Turn a WhatsApp Cloud API webhook into one item per incoming message.
// Accepts the raw webhook body (Inbound API) or the WhatsApp Trigger's items (one per change.value).
const viaApi = $('Inbound API').isExecuted;
const q = viaApi ? $('Inbound API').first().json.query ?? {} : {};
const values = viaApi
? ($('Inbound API').first().json.body?.entry ?? []).flatMap((e) => e.changes ?? []).map((c) => c.value ?? {})
: $('WhatsApp Trigger').all().map((i) => i.json);
const textOf = (m) =>
m.text?.body ??
m.interactive?.button_reply?.title ??
m.interactive?.list_reply?.title ??
m.button?.text ??
m[m.type]?.caption ??
'';
const out = [];
for (const v of values) {
const names = Object.fromEntries((v.contacts ?? []).map((c) => [c.wa_id, c.profile?.name ?? '']));
for (const m of v.messages ?? []) {
out.push({
json: {
wa_id: String(m.from ?? ''),
profile_name: names[m.from] ?? '',
message_id: String(m.id ?? ''),
type: m.type ?? 'unknown',
text: String(textOf(m)).trim().slice(0, 2000),
received_at: m.timestamp ? new Date(Number(m.timestamp) * 1000).toISOString() : new Date().toISOString(),
phone_number_id: v.metadata?.phone_number_id ?? '',
dry_run: q.dry_run === 'true',
now: q.now || new Date().toISOString(), // tests inject the clock with ?now=
via_api: viaApi,
},
});
}
}
// Status updates (sent/delivered/read) have no messages: return one marker item so the API caller gets an answer.
return out.length ? out : [{ json: { ignored: true, reason: 'no messages (status update or other event)', via_api: viaApi } }];