@xenterprises/fastify-xtwilio 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,268 @@
1
+ // src/services/sms.js
2
+ import Twilio from "twilio";
3
+
4
+ export async function setupSMS(fastify, options) {
5
+ if (options.active === false) return;
6
+
7
+ // Validate required credentials
8
+ if (!options.accountSid || !options.authToken) {
9
+ throw new Error("Twilio accountSid and authToken must be provided for SMS.");
10
+ }
11
+
12
+ if (!options.phoneNumber && !options.messagingServiceSid) {
13
+ throw new Error("Either phoneNumber or messagingServiceSid must be provided for SMS.");
14
+ }
15
+
16
+ // Initialize Twilio client
17
+ const twilioClient = Twilio(options.accountSid, options.authToken);
18
+
19
+ fastify.decorate("sms", {
20
+ /**
21
+ * Send an SMS message
22
+ * @param {string} to - Recipient phone number (E.164 format recommended)
23
+ * @param {string} body - Message content
24
+ * @param {object} options - Optional parameters (statusCallback, etc.)
25
+ * @returns {Promise<object>} Message result
26
+ */
27
+ send: async (to, body, extraOptions = {}) => {
28
+ try {
29
+ const message = await twilioClient.messages.create({
30
+ body,
31
+ to,
32
+ from: options.phoneNumber,
33
+ messagingServiceSid: options.messagingServiceSid,
34
+ ...extraOptions,
35
+ });
36
+ return message;
37
+ } catch (error) {
38
+ fastify.log.error("SMS send failed:", error);
39
+ throw new Error("Failed to send SMS.");
40
+ }
41
+ },
42
+
43
+ /**
44
+ * Send an MMS message with media
45
+ * @param {string} to - Recipient phone number
46
+ * @param {string} body - Message content
47
+ * @param {string|string[]} mediaUrl - Media URL(s)
48
+ * @param {object} options - Optional parameters
49
+ * @returns {Promise<object>} Message result
50
+ */
51
+ sendMMS: async (to, body, mediaUrl, extraOptions = {}) => {
52
+ try {
53
+ const message = await twilioClient.messages.create({
54
+ body,
55
+ to,
56
+ from: options.phoneNumber,
57
+ messagingServiceSid: options.messagingServiceSid,
58
+ mediaUrl: Array.isArray(mediaUrl) ? mediaUrl : [mediaUrl],
59
+ ...extraOptions,
60
+ });
61
+ return message;
62
+ } catch (error) {
63
+ fastify.log.error("MMS send failed:", error);
64
+ throw new Error("Failed to send MMS.");
65
+ }
66
+ },
67
+
68
+ /**
69
+ * Schedule an SMS message (requires Messaging Service)
70
+ * @param {string} to - Recipient phone number
71
+ * @param {string} body - Message content
72
+ * @param {Date|string} sendAt - When to send the message (ISO 8601 format)
73
+ * @returns {Promise<object>} Message result
74
+ */
75
+ schedule: async (to, body, sendAt) => {
76
+ try {
77
+ if (!options.messagingServiceSid) {
78
+ throw new Error("messagingServiceSid is required for scheduled messages.");
79
+ }
80
+
81
+ const message = await twilioClient.messages.create({
82
+ body,
83
+ to,
84
+ messagingServiceSid: options.messagingServiceSid,
85
+ scheduleType: "fixed",
86
+ sendAt: sendAt instanceof Date ? sendAt.toISOString() : sendAt,
87
+ });
88
+ return message;
89
+ } catch (error) {
90
+ fastify.log.error("SMS schedule failed:", error);
91
+ throw new Error("Failed to schedule SMS.");
92
+ }
93
+ },
94
+
95
+ /**
96
+ * Cancel a scheduled message
97
+ * @param {string} messageSid - Message SID
98
+ * @returns {Promise<object>} Updated message
99
+ */
100
+ cancelScheduled: async (messageSid) => {
101
+ try {
102
+ const message = await twilioClient.messages(messageSid).update({
103
+ status: "canceled",
104
+ });
105
+ return message;
106
+ } catch (error) {
107
+ fastify.log.error("SMS cancel scheduled failed:", error);
108
+ throw new Error("Failed to cancel scheduled SMS.");
109
+ }
110
+ },
111
+
112
+ /**
113
+ * Get message by SID
114
+ * @param {string} messageSid - Twilio message SID
115
+ * @returns {Promise<object>} Message object
116
+ */
117
+ get: async (messageSid) => {
118
+ try {
119
+ const message = await twilioClient.messages(messageSid).fetch();
120
+ return message;
121
+ } catch (error) {
122
+ fastify.log.error("SMS get failed:", error);
123
+ throw new Error("Failed to get SMS.");
124
+ }
125
+ },
126
+
127
+ /**
128
+ * Get message status
129
+ * @param {string} messageSid - Twilio message SID
130
+ * @returns {Promise<object>} Message status
131
+ */
132
+ getStatus: async (messageSid) => {
133
+ try {
134
+ const message = await twilioClient.messages(messageSid).fetch();
135
+ return {
136
+ sid: message.sid,
137
+ status: message.status,
138
+ errorCode: message.errorCode,
139
+ errorMessage: message.errorMessage,
140
+ dateCreated: message.dateCreated,
141
+ dateUpdated: message.dateUpdated,
142
+ dateSent: message.dateSent,
143
+ };
144
+ } catch (error) {
145
+ fastify.log.error("SMS getStatus failed:", error);
146
+ throw new Error("Failed to get SMS status.");
147
+ }
148
+ },
149
+
150
+ /**
151
+ * List messages
152
+ * @param {object} filters - Filter options (dateSent, from, to, limit)
153
+ * @returns {Promise<object[]>} List of messages
154
+ */
155
+ list: async (filters = {}) => {
156
+ try {
157
+ const messages = await twilioClient.messages.list({
158
+ from: filters.from,
159
+ to: filters.to,
160
+ dateSentAfter: filters.dateSentAfter,
161
+ dateSentBefore: filters.dateSentBefore,
162
+ limit: filters.limit || 50,
163
+ });
164
+ return messages;
165
+ } catch (error) {
166
+ fastify.log.error("SMS list failed:", error);
167
+ throw new Error("Failed to list SMS messages.");
168
+ }
169
+ },
170
+
171
+ /**
172
+ * Delete a message
173
+ * @param {string} messageSid - Message SID
174
+ * @returns {Promise<boolean>} Success status
175
+ */
176
+ delete: async (messageSid) => {
177
+ try {
178
+ await twilioClient.messages(messageSid).remove();
179
+ return true;
180
+ } catch (error) {
181
+ fastify.log.error("SMS delete failed:", error);
182
+ throw new Error("Failed to delete SMS.");
183
+ }
184
+ },
185
+
186
+ /**
187
+ * Get media from a message
188
+ * @param {string} messageSid - Message SID
189
+ * @returns {Promise<object[]>} List of media items
190
+ */
191
+ getMedia: async (messageSid) => {
192
+ try {
193
+ const media = await twilioClient.messages(messageSid).media.list();
194
+ return media;
195
+ } catch (error) {
196
+ fastify.log.error("SMS getMedia failed:", error);
197
+ throw new Error("Failed to get media.");
198
+ }
199
+ },
200
+
201
+ /**
202
+ * Validate a phone number using Lookup API
203
+ * @param {string} phoneNumber - Phone number to validate
204
+ * @param {object} options - Lookup options (type, addOns)
205
+ * @returns {Promise<object>} Validation result
206
+ */
207
+ validatePhoneNumber: async (phoneNumber, lookupOptions = {}) => {
208
+ try {
209
+ const validation = await twilioClient.lookups.v2
210
+ .phoneNumbers(phoneNumber)
211
+ .fetch(lookupOptions);
212
+
213
+ const isValid = validation.valid !== undefined ? validation.valid : true;
214
+
215
+ return {
216
+ phoneNumber: validation.phoneNumber,
217
+ nationalFormat: validation.nationalFormat,
218
+ countryCode: validation.countryCode,
219
+ valid: isValid,
220
+ validation,
221
+ };
222
+ } catch (error) {
223
+ fastify.log.error("Phone number validation failed:", error);
224
+ return {
225
+ phoneNumber,
226
+ valid: false,
227
+ validation: {},
228
+ };
229
+ }
230
+ },
231
+
232
+ /**
233
+ * Send bulk SMS messages
234
+ * @param {Array<{to: string, body: string}>} messages - Array of messages to send
235
+ * @returns {Promise<object[]>} Array of message results
236
+ */
237
+ sendBulk: async (messages) => {
238
+ try {
239
+ const promises = messages.map((msg) =>
240
+ twilioClient.messages.create({
241
+ body: msg.body,
242
+ to: msg.to,
243
+ from: options.phoneNumber,
244
+ messagingServiceSid: options.messagingServiceSid,
245
+ })
246
+ );
247
+
248
+ const results = await Promise.allSettled(promises);
249
+ return results.map((result, index) => {
250
+ if (result.status === "fulfilled") {
251
+ return { success: true, message: result.value };
252
+ } else {
253
+ return {
254
+ success: false,
255
+ error: result.reason.message,
256
+ to: messages[index].to,
257
+ };
258
+ }
259
+ });
260
+ } catch (error) {
261
+ fastify.log.error("SMS sendBulk failed:", error);
262
+ throw new Error("Failed to send bulk SMS.");
263
+ }
264
+ },
265
+ });
266
+
267
+ console.info(" ✅ SMS Service Enabled");
268
+ }
package/src/xTwilio.js ADDED
@@ -0,0 +1,37 @@
1
+ // src/xTwilio.js
2
+ import fp from "fastify-plugin";
3
+
4
+ /*
5
+ * Services
6
+ */
7
+ import { setupSMS } from "./services/sms.js";
8
+ import { setupConversations } from "./services/conversations.js";
9
+ import { setupRCS } from "./services/rcs.js";
10
+ import { setupEmail } from "./services/email.js";
11
+
12
+ async function xTwilio(fastify, options) {
13
+ const {
14
+ twilio: twilioOptions = {},
15
+ sendgrid: sendgridOptions = {},
16
+ } = options;
17
+
18
+ console.info("\n 📱 Starting xTwilio...\n");
19
+
20
+ /*
21
+ * Twilio Services
22
+ */
23
+ await setupSMS(fastify, twilioOptions);
24
+ await setupConversations(fastify, twilioOptions);
25
+ await setupRCS(fastify, twilioOptions);
26
+
27
+ /*
28
+ * SendGrid Email
29
+ */
30
+ await setupEmail(fastify, sendgridOptions);
31
+
32
+ console.info("\n 📱 xTwilio Ready!\n");
33
+ }
34
+
35
+ export default fp(xTwilio, {
36
+ name: "xTwilio",
37
+ });