@@ -6,15 +6,58 @@ import {
66} from "../type" ;
77import { readEnv } from "../utils/env" ;
88
9+ const DEFAULT_API_BASE_URL = "https://api.twilio.com" ;
10+
911function resolveConfig ( config ?: TwilioConfig ) : TwilioConfig {
1012 return {
1113 accountSid : config ?. accountSid ?? readEnv ( "TWILIO_ACCOUNT_SID" ) ,
1214 authToken : config ?. authToken ?? readEnv ( "TWILIO_AUTH_TOKEN" ) ,
1315 from : config ?. from ?? readEnv ( "TWILIO_FROM" ) ,
14- apiBaseUrl : config ?. apiBaseUrl ?? readEnv ( "TWILIO_API_BASE_URL" ) ,
16+ apiBaseUrl :
17+ config ?. apiBaseUrl ?? readEnv ( "TWILIO_API_BASE_URL" ) ?? DEFAULT_API_BASE_URL ,
1518 } ;
1619}
1720
21+ function buildAuthHeader ( accountSid : string , authToken : string ) : string {
22+ return `Basic ${ Buffer . from ( `${ accountSid } :${ authToken } ` ) . toString ( "base64" ) } ` ;
23+ }
24+
25+ async function sendSingleMessage (
26+ config : Required < Pick < TwilioConfig , "accountSid" | "authToken" | "from" | "apiBaseUrl" > > ,
27+ to : string ,
28+ body : string
29+ ) : Promise < { ok : boolean ; status : number ; data : unknown } >
30+ {
31+ const url = new URL (
32+ `/2010-04-01/Accounts/${ config . accountSid } /Messages.json` ,
33+ config . apiBaseUrl
34+ ) ;
35+
36+ const form = new URLSearchParams ( {
37+ To : to ,
38+ From : config . from ,
39+ Body : body ,
40+ } ) ;
41+
42+ const response = await fetch ( url . toString ( ) , {
43+ method : "POST" ,
44+ headers : {
45+ Authorization : buildAuthHeader ( config . accountSid , config . authToken ) ,
46+ "Content-Type" : "application/x-www-form-urlencoded" ,
47+ } ,
48+ body : form . toString ( ) ,
49+ } ) ;
50+
51+ let data : unknown ;
52+ try {
53+ data = await response . json ( ) ;
54+ } catch {
55+ data = await response . text ( ) ;
56+ }
57+
58+ return { ok : response . ok , status : response . status , data } ;
59+ }
60+
1861export const twilioAdapter : ProviderAdapter < TwilioConfig > = {
1962 id : "twilio" ,
2063 async send ( options : ProviderSendOptions < TwilioConfig > ) : Promise < SmsResponse > {
@@ -29,11 +72,78 @@ export const twilioAdapter: ProviderAdapter<TwilioConfig> = {
2972 } ;
3073 }
3174
75+ if ( ! config . from ) {
76+ return {
77+ success : false ,
78+ provider : "twilio" ,
79+ error :
80+ "Twilio sender is missing. Provide from or set TWILIO_FROM." ,
81+ } ;
82+ }
83+
84+ const recipients = options . message . to ;
85+ if ( recipients . length === 0 ) {
86+ return {
87+ success : false ,
88+ provider : "twilio" ,
89+ error : "At least one recipient is required." ,
90+ } ;
91+ }
92+
93+ const resolvedConfig = {
94+ accountSid : config . accountSid ,
95+ authToken : config . authToken ,
96+ from : config . from ,
97+ apiBaseUrl : config . apiBaseUrl ?? DEFAULT_API_BASE_URL ,
98+ } ;
99+
100+ if ( recipients . length === 1 ) {
101+ const result = await sendSingleMessage (
102+ resolvedConfig ,
103+ recipients [ 0 ] ,
104+ options . message . message
105+ ) ;
106+
107+ if ( ! result . ok ) {
108+ return {
109+ success : false ,
110+ provider : "twilio" ,
111+ statusCode : result . status ,
112+ error : "Twilio request failed." ,
113+ data : result . data ,
114+ } ;
115+ }
116+
117+ return {
118+ success : true ,
119+ provider : "twilio" ,
120+ statusCode : result . status ,
121+ data : result . data ,
122+ } ;
123+ }
124+
125+ const results = [ ] as Array < { to : string ; ok : boolean ; status : number ; data : unknown } > ;
126+ let hasFailure = false ;
127+
128+ for ( const to of recipients ) {
129+ const result = await sendSingleMessage (
130+ resolvedConfig ,
131+ to ,
132+ options . message . message
133+ ) ;
134+ results . push ( { to, ok : result . ok , status : result . status , data : result . data } ) ;
135+ if ( ! result . ok ) {
136+ hasFailure = true ;
137+ }
138+ }
139+
32140 return {
33- success : false ,
141+ success : ! hasFailure ,
34142 provider : "twilio" ,
35- error :
36- "Twilio adapter is scaffolded. Add request details in src/providers/twilio.ts." ,
143+ statusCode : hasFailure ? 207 : 200 ,
144+ data : results ,
145+ error : hasFailure ? "One or more Twilio requests failed." : undefined ,
37146 } ;
147+
38148 } ,
39149} ;
0 commit comments