Created
April 8, 2024 17:02
-
-
Save kevboutin/67b2d879910ef91d77246d6387fbc517 to your computer and use it in GitHub Desktop.
SMS Utility using Twilio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const twilio = require('twilio'); | |
const TAG = 'smsUtility'; | |
/** | |
* Send an SMS text message. | |
* | |
* Example response from Twilio: | |
* { | |
* "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", | |
* "api_version": "2010-04-01", | |
* "body": "McAvoy or Stewart? These timelines can get so confusing.", | |
* "date_created": "Thu, 30 Jul 2015 20:12:31 +0000", | |
* "date_sent": "Thu, 30 Jul 2015 20:12:33 +0000", | |
* "date_updated": "Thu, 30 Jul 2015 20:12:33 +0000", | |
* "direction": "outbound-api", | |
* "error_code": null, | |
* "error_message": null, | |
* "from": "+15017122661", | |
* "messaging_service_sid": "MGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", | |
* "num_media": "0", | |
* "num_segments": "1", | |
* "price": null, | |
* "price_unit": null, | |
* "sid": "MMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", | |
* "status": "sent", | |
* "subresource_uris": { | |
* "media": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/SMXXXXXXXXXXXXXXXXXXXXXXXX/Media.json" | |
* }, | |
* "to": "+15558675310", | |
* "uri": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/SMXXXXXXXXXXXXXXXXXXXX.json" | |
* } | |
* | |
* @param {string} locale The recipient user ISO standard locale. | |
* @param {string} msg The message to send. The character limit for a single SMS message is 160 characters; however, | |
* most modern phones and networks support concatenation and segmenting and rebuild messages up | |
* to 1600 characters. Messages not using GSM-7 encoding are limited to 67 characters. Large | |
* messages are segmented into 153 character segments and sent individually then rebuilt by the | |
* recipients device. For example, a 161 character message will be sent as two messages, one with | |
* 153 characters and the second with 8 characters. Messages containing any UCS-2 characters are | |
* limited to 67 characters and will be concatenated into 67 character message segments, even if | |
* the messages contain less than 160 characters. Use the following tool for testing message | |
* splitting: http://chadselph.github.io/smssplit/ | |
* @param {string} recipient The recipient phone number. The format should be like this: +15558675310, which is | |
* standard E.164 format (https://support.twilio.com/hc/en-us/articles/223183008-Formatting-International-Phone-Numbers). | |
* @param {string} sender The sender phone number or null to use the default from the configuration. This is the Twilio | |
* number purchased (https://www.twilio.com/console/phone-numbers/incoming). Use the E.164 | |
* format. | |
* @param {object} config The Twilio configuration that consists of the default sender, account SID and auth token. | |
* @return {Promise} A promise with the result or error. | |
*/ | |
exports.send = async (locale, msg, recipient, sender, config) => { | |
if (!sender) { | |
sender = config.fromNumber ? config.fromNumber : null; | |
} | |
if (msg && recipient) { | |
console.log(`${TAG}::Sending SMS text message to ${recipient} from ${sender}.`); | |
const countryCode = this.getCountryCode(locale); | |
const client = twilio(config.accountSid, config.authToken); | |
const recipientPhoneNumber = await this.normalizePhoneNumber(client, recipient, countryCode); | |
const message = await client.messages.create({ | |
body: msg, | |
to: recipientPhoneNumber, | |
from: sender, | |
}); | |
return message; | |
} | |
throw new Error(`Message or recipient is missing.`, msg, recipient); | |
}; | |
/** | |
* Get the country code from the locale. | |
* | |
* @param {string} locale The ISO standard locale. | |
* @return {*} The country code or null if there was a problem. | |
*/ | |
exports.getCountryCode = (locale) => { | |
if (!locale || locale.length < 2) { | |
return null; | |
} | |
return locale.slice(-2); | |
}; | |
/** | |
* Normalize a phone number. | |
* | |
* @param {object} client The Twilio client. | |
* @param {string} phoneNumber The phone number to normalize. | |
* @param {string} countryCode The ISO standard country code. | |
* @return {Promise} A promise with the normalized phone number or error. | |
*/ | |
exports.normalizePhoneNumber = (client, phoneNumber, countryCode) => | |
new Promise((resolve, reject) => { | |
const hasPlus = phoneNumber.indexOf('+') !== -1; | |
// Remove non-digits, which includes spaces. | |
let result = phoneNumber.replace(/\D/g, ''); | |
// Add plus back if originally existed. | |
if (hasPlus) result = `+${result}`; | |
client.lookups.v2 | |
.phoneNumbers(result) | |
.fetch({ countryCode: countryCode.toUpperCase() }) | |
.then((phoneInfo) => { | |
console.log(`${TAG}::Twilio response for phone number validation: ${JSON.stringify(phoneInfo)}`); | |
console.log(`${TAG}::The phone number, ${phoneInfo.phoneNumber} is valid.`); | |
resolve(result); | |
}) | |
.catch((error) => { | |
console.error( | |
`${TAG}::Failed to validate phone number ${phoneNumber} for country ${countryCode}.`, | |
error | |
); | |
reject(error); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment