87 lines
2.8 KiB
TypeScript
87 lines
2.8 KiB
TypeScript
import { appendLog, readJsonFile, writeJsonFile } from './storage'
|
|
|
|
type FlowState = Record<string, { step: string; name?: string }>
|
|
|
|
function normalizeNumber(number?: string | null) {
|
|
return (number || '').replace(/\D+/g, '')
|
|
}
|
|
|
|
export async function sendTextMessage(to: string, message: string) {
|
|
const apiVersion = process.env.WHATSAPP_API_VERSION || 'v23.0'
|
|
const phoneNumberId = process.env.WHATSAPP_PHONE_NUMBER_ID || ''
|
|
const accessToken = process.env.WHATSAPP_ACCESS_TOKEN || ''
|
|
const url = `https://graph.facebook.com/${apiVersion}/${phoneNumberId}/messages`
|
|
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
messaging_product: 'whatsapp',
|
|
to,
|
|
type: 'text',
|
|
text: { preview_url: false, body: message },
|
|
}),
|
|
})
|
|
|
|
const payload = await response.json().catch(async () => ({ raw: await response.text() }))
|
|
|
|
appendLog({ type: 'outgoing_message', to, message, response: payload, success: response.ok, created_at: new Date().toISOString() })
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Gagal kirim pesan ke WhatsApp')
|
|
}
|
|
|
|
return payload
|
|
}
|
|
|
|
export async function handleWebhook(payload: any) {
|
|
appendLog({ type: 'webhook_received', payload, created_at: new Date().toISOString() })
|
|
|
|
for (const entry of payload.entry || []) {
|
|
for (const change of entry.changes || []) {
|
|
const value = change.value || {}
|
|
|
|
for (const message of value.messages || []) {
|
|
await handleIncomingMessage(message)
|
|
}
|
|
|
|
for (const status of value.statuses || []) {
|
|
appendLog({ type: 'message_status', status, created_at: new Date().toISOString() })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async function handleIncomingMessage(message: any) {
|
|
const from = message.from || ''
|
|
appendLog({ type: 'incoming_message', from, message, created_at: new Date().toISOString() })
|
|
|
|
if (normalizeNumber(from) !== normalizeNumber(process.env.WA_TEST_NUMBER)) {
|
|
appendLog({ type: 'ignored_message', reason: 'sender_not_test_number', from, created_at: new Date().toISOString() })
|
|
return
|
|
}
|
|
|
|
const body = (message.text?.body || '').trim()
|
|
const state = readJsonFile<FlowState>('flow-state.json', {})
|
|
const step = state[from]?.step || 'idle'
|
|
|
|
if (body.toLowerCase() === 'daftar' && step === 'idle') {
|
|
state[from] = { step: 'waiting_name' }
|
|
writeJsonFile('flow-state.json', state)
|
|
await sendTextMessage(from, 'Silakan kirim nama Anda.')
|
|
return
|
|
}
|
|
|
|
if (step === 'waiting_name' && body) {
|
|
state[from] = { step: 'completed', name: body }
|
|
writeJsonFile('flow-state.json', state)
|
|
await sendTextMessage(from, `Terima kasih ${body}, data testing diterima.`)
|
|
return
|
|
}
|
|
|
|
await sendTextMessage(from, 'Kirim "daftar" untuk mulai flow testing.')
|
|
}
|