// src/bot/sendMessage.ts import type { WebexBot } from 'webex-node-bot-framework'; import prisma from '../services/PrismaService.ts'; import logger from '../services/Logger.ts'; import { TwilioService } from '../services/TwilioService.ts'; import { classifyTwilioError } from '../lib/twilioErrors.ts'; interface SpaceLite { roomId: string; smsPhone: string; smsName: string; wbxTmPhone: string; } /** Send an SMS via Twilio and persist the message log. */ export async function sendViaTwilio( text: string, space: SpaceLite, bot: WebexBot, mediaUrls: string[] = [], ): Promise { try { const message = await TwilioService.sendMessage({ to: space.smsPhone, from: space.wbxTmPhone, body: text, mediaUrl: mediaUrls.length > 0 ? mediaUrls : undefined, }); await prisma.message.create({ data: { sid: message.sid, roomId: space.roomId, from: space.wbxTmPhone, body: text, status: 'sent', mediaUrls: JSON.stringify(mediaUrls), }, }); const displayName = space.smsName && space.smsName !== 'Unknown' ? space.smsName : 'the contact'; await bot.say(`Message sent to **${displayName}** (${space.smsPhone})`); } catch (err) { const classified = classifyTwilioError(err); logger.error( { err, to: space.smsPhone, category: classified.category, kind: classified.kind, code: classified.code }, 'Twilio send failed', ); try { await prisma.message.create({ data: { roomId: space.roomId, from: space.wbxTmPhone, body: text, status: classified.kind === 'retryable' ? 'send_error_retryable' : 'send_error', mediaUrls: JSON.stringify(mediaUrls), }, }); } catch (persistErr) { logger.warn({ err: persistErr }, 'Failed to persist send failure'); } const prefix = classified.kind === 'retryable' ? 'Temporary send failure' : 'Send failed'; await bot.say(`${prefix}: ${classified.userMessage}`); } }