Being able to send SMS text messages to our users is another way for us to connect with them
It is possible to connect our computer to a GSM modem, interact with specialized libraries (such as Asterisk, asterisk.org
, combined with ngSMS, ozekisms.com)
, and interface with the libraries and the telephony equipment to send SMS messages.
There are easier ways though. Services like Twilio provide gateway SMS services, where we contact them via an HTTP REST API and they handle the SMS sending for us.
In this recipe, we'll convert our newsletter mail out app into a blanket SMS service using the twilio
module.
This requires a Twilio account (https://www.twilio.com/try-twilio). Once signed up and logged in we should take note of our Account SID, Auth Token, and Sandbox phone number (we may have to select our country of residence to obtain the appropriate Sandbox number).
We'll need some phone numbers to send texts to for testing purposes. In the sandbox mode (which is what we'll be using for development), any number we want to text or call must go through a verification process. We do this by selecting the Numbers link from the Account section, and clicking Verify a Number. Twilio will then call that number and expect a PIN provided on screen to be entered for confirmation.
Let's create a new file, smsout.js
, and install the twilio
helper module:
npm install twilio
First, we require twilio
and put together some configuration settings:
var twilio = require('twilio'), var settings = { sid : 'Ad054bz5be4se5dd211295c38446da2ffd', token: '3e0345293rhebt45r6erta89xc89v103', hostname : 'dummyhost', phonenumber: '+14155992671' //sandbox number }
Twilio phone number
Before we can start interacting with the Twilio service, we have to specify a registered Twilio phone number in order to create our phone
. For development purposes, we can simply use the sandbox number, which can be found from the Twilio dashboard (http://www.twilio.com/user/account). In a production scenario, we would need to upgrade our account and purchase a unique phone number from Twilio.
With our settings
present and correct we're ready to create a Twilio client, using it to initialize a virtual phone:
var restClient = new (twilio.RestClient)(settings.sid, settings.token); var client = new (twilio.Client)(settings.sid, settings.token, settings.hostname); var phone = client.getPhoneNumber(settings.phonenumber);
We created restClient
here also, which offers API calls reflective of Twilio's raw REST API. We'll be using restClient
to check the status of our SMS message in order to determine if the message has been sent from Twilio to the target phone.
Now we define a list of numbers to text (we'll have to provide our own) to text, much like our maillist
array in the previous recipe:
var smslist = [ '+44770xxxxxx1', '+44770xxxxxx2', '+44770xxxxxx3', '+44770xxxxxx4', '+44770xxxxxx5' ];
Unless we have upgraded our account, any number on smslist
must be pre-verified with Twilio. This can be done through the Twilio Numbers account section (https://www.twilio.com/user/account/phone-numbers/).
Then we simply loop through smslist
and use phone
to send an SMS message to each recipient as follows:
var msg = 'SMS Ahoy!'; smslist.forEach(function (to) { phone.sendSms(to, msg, {}, function(sms) { console.log(sms); }); });
This should work fine, except that the process won't exit (because twilio
initializes a server to receive Twilio callbacks) and we don't know when an SMS is actually sent from Twilio to the recipient. One way to check is to make another request to the Twilio REST API asking for a status update. The twilio RestClient
makes this easy for us as follows:
phone.sendSms(to, msg, {}, function(sms) { restClient.getSmsInstance(sms.smsDetails.sid, function (presentSms) { //process presentSms using it's status property. }); });
If our SMS hasn't been sent on the first call, we need to wait and check it again. Let's make some final improvements as shown in the following code:
var msg = 'SMS Ahoy!', i = 0; smslist.forEach(function (to) { phone.sendSms(to, msg, {}, function (sms) { function checkStatus(smsInstance) { restClient.getSmsInstance(smsInstance.sid, function (presentSms) { if (presentSms.status === 'sent') { console.log('Sent to ' + presentSms.to); } else { if (isNaN(presentSms.status)) { //retry: if its not a number (like 404, 401), it's not an error setTimeout(checkStatus, 1000, presentSms); return; } //it seems to be a number, let's notify, but carry on console.log('API error: ', presentSms.message); } i += 1; if (i === smslist.length) { process.exit(); } }); }; checkStatus(sms.smsDetails); }); });
Now the console will output each time a number has been confirmed as sent. When all numbers have been messaged the process exits.
The twilio.RestClient
gives us access to low-level interactions with the Twitter API via the twilio
helper. This simply wraps generic API calls with our preset authorization settings for us, making the HTTP requests on our behalf.
twilio.Client
is a higher-level API presented by the twilio
helper and handles two-way interactions between Twilio and the client. The third parameter that must be provided to initialize a new client is the hostname
parameter. The twilio
module provides this to Twilio as part of a callback URL requested from the Twilio servers to confirm that an SMS message has been sent.
We ignored this behavior and supplied a dummy hostname, implementing our own method of confirming an SMS was sent. Our method doesn't require us to have a live server address accessible from the Web (see the There's more... section for a live server implementation that uses the hostname
property as intended).
We use the sendSms
method of our created phone
object to make an HTTP request of the Twilio API via the twilio
module, passing in the desired recipient, message, and a callback function.
Once the request is made, our callback is triggered with the initial sms
object. We use this to determine the ID that Twilio has given our sendSMS
request with smsInstance.sid
(which is sms.smsDetails.sid)
.
smsInstance.sid
is passed to restClient.getSmsInstance
which provides us with an updated instance of our smsIntance
object, we call it presentSms
. This call is made from within a custom, self-calling function expression called checkStatus
, which has our initial sms.smsDetails
object passed to it.
We are looking to see if Twilio has sent our text message yet. If it has, presentSms.status
will be sent
. If it's anything other than this, we want to wait a short while and then ask Twilio for another update on the status of our queued SMS message. That is, unless the returned status is a 404
, in which case there has been an issue and we need to notify the user, but continue on to process the next SMS message.
As in the Sending email recipe, we keep a count of how many messages are sent. Once they total the amount of recipients we exit the process, since smsout.js
is a command-line app (in a server scenario we wouldn't need or want to do this).
The Twilio module's versatility stretches beyond sending SMS messages. It can also transparently handle Twilio callbacks for us through emitting events.
With a live public server, we would be better off providing our hostname to twilio.Client
so it can manage callback URL requests.
For this code to work, it must be hosted on a live public server. For more information on hosting Node on live servers, seeChapter 10,Taking It Live
We would remove restClient
and change our settings
object to the following:
var settings = {
sid : 'Ad054bz5be4se5dd211295c38446da2ffd',
token: '3e0345293rhebt45r6erta89xc89v103',
hostname : 'nodecookbook.com',
phonenumber: '+14155992671' //sandbox number
};
Then our SMS sending code is simply:
var i = 0; smslist.forEach(function (to) { phone.sendSms(to, msg, {}, function(sms) { sms.on('processed', function(req) { i += 1; console.log('Message to ' + req.To + ' processed with status: ' + req.SmsStatus); if (i === smslist.length) {process.exit();} }); }); });
The twilio
client provides a statusCallback
URL to Twilio. Twilio will request this URL (something like http://nodecookbook.com:31337/autoprovision/0)
to confirm and the twilio
helper module will emit a processed
event to notify us of Twilio's confirmation. We listen out for this event, checking the given SmsStatus
via that req
object to confirm success.
For this next example to work, we would need to have a valid hostname and be running our app on a web-exposed server as in the previous section.
For this code to work, it must be hosted on a live public server. For more information on hosting Node on live servers, seeChapter 10,Taking It Live
To make a call we start with the usual setup.
var twilio = require('twilio'), var settings = { sid : 'Ad054bz5be4se5dd211295c38446da2ffd', token: '3e0345293rhebt45r6erta89xc89v103', hostname : 'nodecookbook.com', phonenumber: '+14155992671' //sandbox number }; var client = new (twilio.Client)(settings.sid, settings.token, settings.hostname); var phone = client.getPhoneNumber(settings.phonenumber);
Then we use the makeCall
method as follows:
phone.makeCall('+4477xxxxxxx1', {}, function(call) { call.on('answered', function (req, res) { console.log('answered'), res.append(new (twilio.Twiml).Say('Meet us in the abandoned factory')); res.append(new (twilio.Twiml).Say('Come alone', {voice: 'woman'})); res.send(); }).on('ended', function (req) { console.log('ended', req); process.exit(); }) });
If our account is not upgraded, whatever number we supply to makeCall
must be verified through the Twilio Numbers
area in the account section (https://www.twilio.com/user/account/phone-numbers/).
makeCall
invokes a callback with a call
parameter. call
is an event emitter, with answered
and ended
events. The twilio
module transparently converts Twilio confirmation callbacks into these events.
The answered
event feeds the req
and res
objects to its callback. req
holds information about the outgoing call, and res
has methods which allow us to interact with the call, namely res.append
and res.send
.
To send a computerized text-to-speech message to the recipient, we instantiate a new instance of the twilio
module's Twiml
class and use the Say
method (watch out for the unusual convention of giving a capital S to a non-class, using a small s will throw an error).
TwiML
is Twilio's markup language. It's simply API-specific XML. The twilio
module provides the Twiml
class to handle the ugly business of forming the necessary XML. We use it to create two Say
verbs. Behind the twilio
scenes, the two append
calls followed by the send invocation would create and issue the following TwiML
to twilio:
<?xml version="1.0" encoding="UTF-8" ?> <Response> <Say> Meet us in the abandoned factory </Say> <Say voice="woman"> Come alone </Say> </Response>
The TwiML
code is received by twilio
and converted into speech. After the woman's voice says Come alone
the phone call ends, triggering the ended
event in our app (which the twilio
module emits as a result of receiving an HTTP request from twilio
signifying the call has ended).
We listen to the ended
event determining when twilio
(or the recipient) have hung up. Once ended
has triggered we exit the process, outputting the req
object as an overview of the call.
18.217.187.19