import { success } from "../globals.js"; import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; import { withWhatsAppPrefix, sleep } from "../utils.js"; import { readEnv } from "../env.js"; import { createClient } from "./client.js"; import { logTwilioSendError } from "./utils.js"; const successTerminalStatuses = new Set(["delivered", "read"]); const failureTerminalStatuses = new Set(["failed", "undelivered", "canceled"]); // Send outbound WhatsApp message; exit non-zero on API failure. export async function sendMessage( to: string, body: string, runtime: RuntimeEnv = defaultRuntime, ) { const env = readEnv(runtime); const client = createClient(env); const from = withWhatsAppPrefix(env.whatsappFrom); const toNumber = withWhatsAppPrefix(to); try { const message = await client.messages.create({ from, to: toNumber, body, }); console.log( success( `✅ Request accepted. Message SID: ${message.sid} -> ${toNumber}`, ), ); return { client, sid: message.sid }; } catch (err) { logTwilioSendError(err, toNumber, runtime); } } // Poll message status until delivered/failed or timeout. export async function waitForFinalStatus( client: ReturnType, sid: string, timeoutSeconds: number, pollSeconds: number, runtime: RuntimeEnv = defaultRuntime, ) { const deadline = Date.now() + timeoutSeconds * 1000; while (Date.now() < deadline) { const m = await client.messages(sid).fetch(); const status = m.status ?? "unknown"; if (successTerminalStatuses.has(status)) { console.log(success(`✅ Delivered (status: ${status})`)); return; } if (failureTerminalStatuses.has(status)) { runtime.error( `❌ Delivery failed (status: ${status}${m.errorCode ? `, code ${m.errorCode}` : ""})${m.errorMessage ? `: ${m.errorMessage}` : ""}`, ); runtime.exit(1); } await sleep(pollSeconds * 1000); } console.log( "ℹ️ Timed out waiting for final status; message may still be in flight.", ); }