import { twilioRequest } from './client.ts'
import { mutationPreview } from './helpers.ts'
import { resolveMessagingServiceSid } from './config.ts'
import {
assertE164,
inputRecord,
optionalE164,
requiredString,
type TwilioAuthOptions,
} from './validation.ts'
/**
* Preview or send an SMS.
*
* Defaults to dry-run. A live send requires `confirm: true` after the user
* explicitly approves the destination and body. Never bakes in a from/to
* number — pass `from` or a Messaging Service SID (param or packageStorage).
*/
export async function sendMessage(
params: TwilioAuthOptions & {
to: string
body: string
from?: string
messagingServiceSid?: string
statusCallback?: string
dryRun?: boolean
confirm?: boolean
},
) {
const input = inputRecord(params)
const to = assertE164(requiredString(input, 'to'), 'to')
const body = requiredString(input, 'body')
const from = optionalE164(input, 'from')
const messagingServiceSid = await resolveMessagingServiceSid(input)
const statusCallback =
typeof input.statusCallback === 'string' && input.statusCallback.trim()
? input.statusCallback.trim()
: undefined
if (statusCallback && !/^https:\/\//i.test(statusCallback)) {
throw new Error('statusCallback must be an https URL when provided.')
}
if (!from && !messagingServiceSid) {
if (input.confirm === true && input.dryRun !== true) {
throw new Error(
'Sending SMS requires from (E.164) or messagingServiceSid (MG...). Pass one explicitly or store a Messaging Service SID in packageStorage after you fork. This package has no baked-in phone numbers.',
)
}
}
const payload = {
To: to,
Body: body,
From: from,
MessagingServiceSid: messagingServiceSid,
StatusCallback: statusCallback,
}
const preview = mutationPreview(input, '/Messages.json', payload)
if (preview) {
return {
...preview,
to,
from: from ?? null,
messagingServiceSid: messagingServiceSid ?? null,
bodyLength: body.length,
missingSender: !from && !messagingServiceSid,
}
}
const raw = await twilioRequest({
...input,
resource: '/Messages.json',
method: 'POST',
body: payload,
})
return {
ok: true,
sid: typeof raw.sid === 'string' ? raw.sid : null,
status: typeof raw.status === 'string' ? raw.status : null,
to: typeof raw.to === 'string' ? raw.to : to,
from: typeof raw.from === 'string' ? raw.from : from ?? null,
dateCreated: typeof raw.date_created === 'string' ? raw.date_created : null,
}
}
export default sendMessage