Create an SMS-Based Stock Quote Service with Twilio

With the advent of cloud-based telephony services such as Twilio, writing an application that works with SMS messages and telephone calls has never been easier. In addition to allowing you to send SMS messages via their simple API, Twilio also allows you to process incoming messages which opens up a whole world of possible applications.

In this article, I’ll show you how you can use Twilio to build a simple SMS-based stock quote service which handles both incoming SMS messages and sending responses. The application will allow the user to request a stock price for a given company via SMS and receive it a few moments later via the same method.

Before You Start

First – if you haven’t done so already – you’ll need to create an account with Twilio.

Once you’re signed up, you need to requisition (hire, in essence) a phone number which is what people will send messages to in order to use the service. Go to twilio.com/user/account/phone-numbers/incoming and click “Buy a number” to pick an available number. You may wish to pick one specific to your region, but you’re free to pick any of the available numbers – the only variable is cost.

Then go to the number you’ve selected, check the SMS box, and enter the URL you’re going to use for your application (it has to be somewhere publicly accessible) followed by /callback. For example:

http://example.com/callback

Take note of your account SID and the corresponding auth token (they appear at the top of your account page) – you’ll need them later.

The Application

The user will request a stock price via SMS and the application will process the incoming message, extract the relevant data, use an external web service to grab the requested information, and send back the response to the user in a second SMS.

The process will be as follows:

  • the user sends a command via SMS to a specified number
  • the SMS is handled by Twilio, which “pings” the configured end-point (our application)
  • our application parses the command and extracts the company code
  • the application pings an external web service, passing the company code as a parameter
  • that web service returns a price
  • the application pings the Twilio API with a response and the number to send the SMS to
  • the user receives an SMS with the requested stock price

This process is illustrated in the following diagram:

twilio-stock-01

Sound complicated? Don’t worry, it’s really not! Let’s get started.

Building the Foundation

The easiest way to get started is to use composer. Create a composer.json file in your project root with the following:

{
    "require": {
        "slim/slim": "2.3.0",   
        "slim/extras": "dev-develop",
        "twilio/sdk": "dev-master"
    }
}

I’m using the Slim framework; however, much of the code will be re-usable across whatever framework you favor (if indeed you wish to use a framework at all).

Now run:

php composer.phar install

You’ll find that the Slim framework now resides in the newly-created vendor folder, along with the official Twilio SDK, and some extra goodies (we’re going to use some logging functionality from Slim Extras later).

Create an includes folder, and in that a file called config.php:

return array(
    'twilio' => array(
        'account_sid'  => 'YOUR-ACCOUNT-SID',
        'auth_token'   => 'YOUR-AUTH-TOKEN',
        'phone_number' => 'YOUR-PHONE-NUMBER',
    ),
);

Of course you’ll need to replace these values with the ones you got from Twilio earlier.

Create a public directory in the project root and point your Apache configuration to serve files from this folder.

Now create an .htaccess file in the public folder:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

This will route all requests for non-existent files to a central controller file, index.php.

Now for the index.php file:

<?php
require '../vendor/autoload.php';

$config = require '../includes/config.php';

$app = new SlimSlim();

$app->get('/', function () use ($app) {
  print 'Installation successful';
});

$app->run();

Try browsing to the application – you should see the “Installation successful” message.

Because we’re building an application that’s not going to render content for a browser, it will be very helpful to implement some logging. Slim logs to STDERR by default, but the Slim Extras library referred to in the composer.json file contains a file-based log writer.

Change the instantiation of the new Slim object to:

$app = new SlimSlim(array(
        'log.writer' => new SlimExtrasLogDateTimeFileWriter(array(
            'path' => '../logs',
            'name_format' => 'Y-m-d',
            'message_format' => '%label% - %date% - %message%'
        ))
));

Then, create the logs folder in the project root and make it writable by the web server.

Now you can access the logger as follows:

$log = $app->getLog();

And log messages like this:

$log->info('This is a message');

Handling the Incoming SMS

We’ve specified our callback which gets pinged when an incoming SMS is received. Specifically, by “pinged” I mean that a POST request is made to the specified URL with two key pieces of information:

  • the sender’s number
  • the content of the SMS

The sender’s number and message are sent as POST variables – From and Body respectively.

So, let’s create the callback – I’ll keep it simple for now and just log the information:

$app->post('/callback', function () use ($app) {
    $log = $app->getLog();

    $sender = $app->request()->post('From');
    $message = $app->request()->post('Body');

    $log->info(sprintf('Message received from %s: %s', $sender, 	$message));
});

Try sending an SMS to your configured number and then check the logs – the log entry should show up within a few minutes. If you don’t see anything, Twilio keeps a record of activity so you may be able to debug the process from there.

The format of the message the application will accept is: COMMAND ARGUMENT. It will only accept one command – PRICE – as in, “give me the share price for..”, and a single argument, the company code.

Here’s an example:

PRICE AAPL

What the command means is “Send me Apple, Inc.’s current share price”.

By incorporating a command, we’re leaving open the option of adding additional functionality later. For example, perhaps you may want to offer the facility to SUBSCRIBE to regular updates.

To parse the incoming message we simply need to extract the command and the argument – we can use a simple regular expression for this:

$pattern = '/^([A-Z]+) ([A-Z]+)/';
preg_match($pattern, $subject, $matches);
print_r($matches);

If you try this with the example earlier, you should see this:

Array
(
    [0] => PRICE AAPL
    [1] => PRICE
    [2] => AAPL
)

The relevant parts are in positions 1 and 2.

$command = $matches[1];
$argument = $matches[2];

Although there’s only one valid command, there’s no harm in thinking ahead and using a switch to determine what action to take based on the command.

switch ($command) {
    case 'PRICE':
        // perform action
        break;
    default:
        print 'Invalid command!';
}

Getting the Stock Price

I’m going to use a web service to get the latest share price for a given company. Free stock services are few and far between – and even then there are often delays, but there’s one here.

Create a directory called src in the project root, and in that a directory called SitePoint, and in that a file called StockService.php:

<?php
namespace SitePoint;

class StockService
{
}

In order for the autoloader to pick up the location of this class, add an autoloader declaration in your composer.json:

"autoload": {
    "psr-0": {"SitePoint\": "src/"}
}

Now run:

php composer.phar update

To implement a method for grabbing the stock quote for a given company, create a public method in the StockService class like this:

public static function GetQuote($symbol) {
    $client = new SoapClient('http://www.webservicex.net/stockquote.asmx?WSDL');

    $params = array(
        'symbol' => $symbol,
    );
    $response = $client->__soapCall('GetQuote', array($params));
    $quotes = simplexml_load_string($response->GetQuoteResult);
	
    return $quotes->Stock[0];
}

All I’ve done is create an instance of SoapClient and passed it the URL to the service’s WSDL file, and then call the GetQuote method, process the XML, and return the first (and only) Stock element.

You can add a new route to test it:

$app->get('/quote', function () use ($app) {
    $symbol = $app->request()->get('symbol');
    $quote = SitePointStockService::GetQuote($symbol);
    var_dump($quote);
});

If you call http://localhost/quote?symbol=AAPL, you should see something like this:

object(SimpleXMLElement)#39 (16) {
  ["Symbol"]=>
  string(4) "AAPL"
  ["Last"]=>
  string(6) "431.31"
  ["Date"]=>
  string(9) "7/18/2013"
  ["Time"]=>
  string(6) "1:03pm"
  ["Change"]=>
  string(5) "+1.00"
  ["Open"]=>
  string(6) "433.55"
  ["High"]=>
  string(6) "434.87"
  ["Low"]=>
  string(6) "430.61"
  ["Volume"]=>
  string(7) "4878034"
  ["MktCap"]=>
  string(6) "404.8B"
  ["PreviousClose"]=>
  string(6) "430.31"
  ["PercentageChange"]=>
  string(6) "+0.23%"
  ["AnnRange"]=>
  string(15) "385.10 - 705.07"
  ["Earns"]=>
  string(6) "41.896"
  ["P-E"]=>
  string(5) "10.27"
  ["Name"]=>
  string(10) "Apple Inc."
}

In the next section, we’ll finish off by collating this information into an SMS message and send it to the requesting user’s mobile phone.

Responding via SMS

The first thing to do here is construct the message the application is going to reply with:

// construct the response
$response = sprintf('%s: %s (%s) [Hi: %s Lo: %s]',
	$quote->Name,
	$quote->Last,
	$quote->Change,
	$quote->High,
	$quote->Low
);

Based on the information earlier, you’d end up with a message like this:

Apple Inc.: 431.31 (+1.000) [Hi: 434.87 Lo: 430.61]

To send it as an SMS, instantiate the Twilio class and call the method which corresponds to the required REST endpoint:

// instantiate the Twilio client	
$twilio = new Services_Twilio(				
    $config['twilio']['account_sid'],
    $config['twilio']['auth_token']
);
			 
// send the SMS
$sms = $twilio->account->sms_messages->create(
    $config['twilio']['phone_number'],  // the number to send from
    $sender,
    $response
);

… and that’s it!

There’s still some work to be done, such as performing proper error handling, and the command parsing is a little unforgiving. Feel free to download the code from GitHub and play around!

Summary

In this article I’ve demonstrated how you can use Twilio to both receive and send SMS messages and how you can build a simple application from scratch to deliver a piece of requested information directly to someone’s mobile phone. In practice our example isn’t incredibly useful; stock prices fluctuate far quicker than you can send an SMS – but perhaps you can think of other uses?

You may also wish to play around with adding new commands. Perhaps instead of a service with an instant response, you subscribe to regular updates? Or even set some thresholds, for example “send me an SMS if the share price drops below x”). Feel free to pop any other ideas in the comments below!

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.