const WHATSAPP_API_VERSION = 'v18.0'; const WHATSAPP_BASE_URL = process.env['WHATSAPP_API_BASE_URL'] ?? 'https://graph.facebook.com'; function getEnvVar(prefix: string, account: string, key: string): string { const envKey = `WHATSAPP_${account.toUpperCase()}_${key}`; return process.env[envKey] ?? ''; } function getPhoneNumberId(account: string): string { return getEnvVar('WHATSAPP', account, 'PHONE_NUMBER_ID'); } function getAccessToken(account: string): string { return getEnvVar('WHATSAPP', account, 'ACCESS_TOKEN'); } function getBusinessAccountId(account: string): string { return getEnvVar('WHATSAPP', account, 'BUSINESS_ACCOUNT_ID'); } interface WhatsAppMessageResponse { messaging_product: string; contacts?: Array<{ wa_id: string; input: string }>; messages?: Array<{ id: string }>; } async function whatsappApiRequest( phoneId: string, accessToken: string, endpoint: string, method: 'GET' | 'POST' = 'POST', body?: unknown ) { const url = `${WHATSAPP_BASE_URL}/${WHATSAPP_API_VERSION}/${phoneId}/${endpoint}`; const res = await fetch(url, { method, headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(10000), }); if (!res.ok) { const error = await res.text(); throw new Error(`WhatsApp API error (${res.status}): ${error}`); } return res.json(); } export async function sendMessage(args: { to: string; message: string; account?: string }): Promise<{ success: boolean; message_id: string }> { const phoneId = getPhoneNumberId(args.account ?? 'default'); const accessToken = getAccessToken(args.account ?? 'default'); if (!phoneId || !accessToken) { throw new Error('Missing WhatsApp credentials. Set WHATSAPP_{ACCOUNT}_PHONE_NUMBER_ID and WHATSAPP_{ACCOUNT}_ACCESS_TOKEN'); } const body = { messaging_product: 'whatsapp', to: args.to, type: 'text', text: { body: args.message }, }; const data = await whatsappApiRequest(phoneId, accessToken, 'messages', 'POST', body); const response = data as WhatsAppMessageResponse; return { success: true, message_id: response.messages?.[0]?.id ?? '' }; } export async function sendTemplate(args: { to: string; template_name: string; language?: string; components?: unknown[]; account?: string }): Promise<{ success: boolean; message_id: string }> { const phoneId = getPhoneNumberId(args.account ?? 'default'); const accessToken = getAccessToken(args.account ?? 'default'); if (!phoneId || !accessToken) { throw new Error('Missing WhatsApp credentials. Set WHATSAPP_{ACCOUNT}_PHONE_NUMBER_ID and WHATSAPP_{ACCOUNT}_ACCESS_TOKEN'); } const body: Record = { messaging_product: 'whatsapp', to: args.to, type: 'template', template: { name: args.template_name, language: { code: args.language ?? 'en' }, }, }; if (args.components) { (body.template as Record).components = args.components; } const data = await whatsappApiRequest(phoneId, accessToken, 'messages', 'POST', body); const response = data as WhatsAppMessageResponse; return { success: true, message_id: response.messages?.[0]?.id ?? '' }; } export async function getMessageStatus(args: { message_id: string; account?: string }): Promise<{ message_id: string; status: string; timestamp?: string }> { const phoneId = getPhoneNumberId(args.account ?? 'default'); const accessToken = getAccessToken(args.account ?? 'default'); if (!phoneId || !accessToken) { throw new Error('Missing WhatsApp credentials. Set WHATSAPP_{ACCOUNT}_PHONE_NUMBER_ID and WHATSAPP_{ACCOUNT}_ACCESS_TOKEN'); } // Note: Meta Cloud API doesn't support polling message status via GET // Status updates are only available via webhooks (push-based) throw new Error('whatsapp_get_message_status is not supported. Meta Cloud API only provides delivery status via webhooks. Use POST /api/whatsapp/webhook to receive status updates.'); } export async function listTemplates(args: { account?: string }): Promise<{ templates: Array<{ name: string; language: string; status: string }> }> { const account = args.account ?? 'default'; const businessAccountId = getBusinessAccountId(account); const accessToken = getAccessToken(account); if (!businessAccountId || !accessToken) { throw new Error('Missing WhatsApp credentials. Set WHATSAPP_{ACCOUNT}_BUSINESS_ACCOUNT_ID and WHATSAPP_{ACCOUNT}_ACCESS_TOKEN'); } const url = `${WHATSAPP_BASE_URL}/${WHATSAPP_API_VERSION}/${businessAccountId}/message_templates?fields=name,language,status`; const res = await fetch(url, { headers: { 'Authorization': `Bearer ${accessToken}`, }, signal: AbortSignal.timeout(10000), }); if (!res.ok) { const error = await res.text(); throw new Error(`WhatsApp API error (${res.status}): ${error}`); } const data = await res.json(); return { templates: (data.data ?? []).map((t: { name: string; language: string | { code: string }; status: string }) => ({ name: t.name, language: typeof t.language === 'string' ? t.language : t.language?.code ?? 'en', status: t.status, })), }; }