Monitoring Cryptocurrency Prices using JavaScript, Twilio Functions and SMS

June 27, 2017
Written by
Chris Hranj
Contributor
Opinions expressed by Twilio contributors are their own

Check Bitcoin prices with Twilio Functions

Twilio recently introduced Functions, a serverless environment to build and run Twilio apps. With Functions, you can build simple Twilio applications in JavaScript without worrying about configuring servers, finding a place to host TwiML or tunneling to localhost.

Let’s get acquainted with Functions by building an app that monitors the price of cryptocurrencies. The finished app will use CoinMarketCap to send us pricing information via SMS about any cryptocurrency we send (Bitcoin, Ethereum, Dogecoin, etc).

giphy.gif

If you’re already familiar with setting up a new Function, the finished code for this Function is available on Github here.

Getting Started

In order to build this application and use Twilio Functions you’ll need the following:

It may also benefit you to read the Intro to Functions blog post that explains a lot of the basics that this post will touch on.

Creating a New Function

Functions can be accessed from the Twilio Console by navigating to Runtime -> Functions. Click the red ‘ ’ circle and select the Hello SMS template.

functions.gif

You’ll notice the following Node.js code already in the Code section of your Function:

 

exports.handler = function(context, event, callback) {
	let twiml = new Twilio.twiml.MessagingResponse();
	twiml.message("Hello World");
	callback(null, twiml);
};

 

The code above determines what will happen when the Function is invoked. Every Function you write will follow this pattern.

The context parameter includes information about your runtime, such as configuration variables. The event object includes information about a specific invocation, including HTTP parameters from the HTTP request. Finally, callback is the method you call to return from the Function. You can read more about the above code in this Twilio support page.

The code inside of the function is creating a new TwiML response object containing a Message verb with a message of “Hello World”. These lines can be removed for now.

Configuring a Function

Give your Function a name and a path. The name should be something simple that describes what the Function does (“CryptoCurrencyLookup”, for example). The path serves as a publicly accessible URL which can be used to invoke the Function from anywhere, not just from a Twilio call or SMS.

Set the Event to “Incoming Messages”, and make sure that “Access Control” is checked. This ensures that only requests made via SMS through Twilio can run this Function. As a result, the path that was just set above will only be accessible by incoming messages to Twilio phone numbers.

configuration.PNG
Making HTTP Requests in a Function

Add the following to the top of the Function:

 

const got = require('got');

 

The above line will import the got module. got will be used to make an HTTP request to a 3rd-party API later on in the Function. Twilio Functions currently support only four 3rd-party libraries: twilio, got, lodash, and xmldom .

We are going to use CoinMarketCap and their simple API to obtain up-to-date pricing information on cryptocurrencies. Remove the boilerplate code and add the following to your Function:

 

const got = require('got');

exports.handler = function(context, event, callback) {
    got('https://api.coinmarketcap.com/v1/ticker/${event.Body.trim()}/')
    .then(response => {
        let result = JSON.parse(response.body)[0];
        let price_change = parseInt(result.percent_change_24h);
        message = result.symbol + "\t" + result.price_usd +
            (price_change > 0 ? '\u2191' : '\u2193') + "\t" +
            Math.abs(result.percent_change_24h);
    });
};

 

The code above uses got to make an HTTP GET request to CoinMarketCap. Notice that the URL being requested contains the expression, ${event.Body.trim()}. event.Body is the message contents of the SMS messages that will be sent to your Twilio number later on. The trim method removes any leading or trailing spaces. The entire expression itself is wrapped as a template literal that will evaluate event.Body.trim() and insert it into the the string to form a URL.

Since the HTTP request we’re making to the CoinMarketCap API happens asynchronously, got uses a promise to tell us when that HTTP request is complete, calling the function defined inside of the then function.  Asynchronous JavaScript can be very tough for a beginner, but luckily there are tons of resources to help understand it.

Inside that function we parse the JSON response from CoinMarketCap and obtain the currency’s value, symbol and percent-change in the last 24 hours, then piece everything together to form a message that resembles a stock ticker.

Screen Shot 2017-06-20 at 12.20.55 AM.png

The (price_change > 0 ? 'u2191' : 'u2193') expression is a conditional ternary operator which  determines if the message should contain an up arrow or down.

Creating a Response

The code above contains the bulk of the application logic. However, the Function will timeout when invoked because the callback parameter is never called. Let’s create a TwiML MessagingResponse object and pass it to the callback, similar to what was seen in the original template code. We should also refactor how the message is being constructed to something more readable:

 

const got = require('got');

exports.handler = function(context, event, callback) {
    let message = "";
    let twiml = new Twilio.twiml.MessagingResponse();
    got('https://api.coinmarketcap.com/v1/ticker/${event.Body.trim()}/')
    .then(response => {
        let result = JSON.parse(response.body)[0];
        let price_change = parseInt(result.percent_change_24h);
        let message_params = [
            result.symbol, result.price_usd,
            (price_change > 0 ? 'u2191' : 'u2193'),
            Math.abs(result.percent_change_24h)];
        message = message_params.join(" ");
        twiml.message(message);
        callback(null, twiml);
    });
};

 

Once the callback is invoked, the Function will ensure that the MessageResponse object is turned into XML and the HTTP response’s content-type is set to text/xml. Twilio will handle the rest by sending an outgoing SMS.

Configure a Finished Function

With this code in place, the next step is to link this Function to a Twilio number you own. Navigate to your Phone Numbers, select the number your own to that you want to configure, scroll down and configure the Messaging section like this:

number_config.PNG

Save these changes and test the Function by texting the name of any cryptocurrency to your Twilio number (Bitcoin, for example).

imageedit_5_6596497647.png
Error Handling

Our application is complete, but there is currently no behavior defined in our Function in the event that an error occurs. That means if the user texts in a non-existent cryptocurrency or CoinMarketCap is unreachable, the user will get no response.

This can be fixed by chaining a catch method to our promise. A catch is called whenever a promise is rejected (i.e. an error occurred). In order to add a catch we’ll need to chain one last then method to the Function so that the callback will be invoked on both successes and failures. Refactor your Function code as such:

 

const got = require('got');

got('https://api.coinmarketcap.com/v1/ticker/${event.Body.trim()}/')
    .then(response => {
        let result = JSON.parse(response.body)[0];
        let price_change = parseInt(result.percent_change_24h);
        let message_params = [
                result.symbol, result.price_usd,
                (price_change > 0 ? '\u2191' : '\u2193'),
                Math.abs(result.percent_change_24h)];
                message = message_params.join(" ");
    })
    .catch(error => {
        console.log(error);
        message = "Unable to determine price.";
    })
    .then(function() {
        twiml.message(message);
        callback(null, twiml);
    });

 

Save the changes. Now, if an error occurs the user will still receive an SMS response instead of nothing. Test this by texting in anything other than a valid cryptocurrency name (“Twiliocoin” or “asdfasdfas”, for example). The response you receive should state “Unable to determine price.”

imageedit_7_2579234510.png

The finished code for the Function should look like this:

 

const got = require('got');

exports.handler = function(context, event, callback) {
    let message = "";
    let twiml = new Twilio.twiml.MessagingResponse();
    got('https://api.coinmarketcap.com/v1/ticker/${event.Body.trim()}/')
    .then(response => {
        let result = JSON.parse(response.body)[0];
        let price_change = parseInt(result.percent_change_24h);
        let message_params = [
            result.symbol, result.price_usd,
            (price_change > 0 ? '\u2191' : '\u2193'),
            Math.abs(result.percent_change_24h)];
        message = message_params.join(" ");
    })
    .catch(error => {
        console.log(error);
        message = "Unable to determine price.";
    })
    .then(function() {
        twiml.message(message);
        callback(null, twiml);
    });
};

 

This is all of the code the Function will need. If you receive a lot of “Unable to determine price.” responses, the errors will show up in your logs and you can use Twilio’s built-in Debugger to assess the issue.

Wrapping Up

Congratulations on creating a simple but powerful Twilio application using nothing but ~25 lines of JavaScript. Now you can quickly monitor the price of any of the ever-fluctuating cryptocurrencies with nothing more than an SMS message.

giphy.gif

Stay tuned for more Functions posts on the Twilio blog and for additional Functions features. In the meantime, see what else you can build using Functions and the endless number of awesome APIs and services out there.

If you run into any issues with your Function or you have questions, please reach out to me on Twitter @brodan_ or on Github.