Making Your Inbox Intelligent with Context.IO and Twilio

October 10, 2013
Written by
Keith Casey
Contributor
Opinions expressed by Twilio contributors are their own

keith-casey-lo-res

Email is broken. We get too much of it. We get irrelevant stuff. Reply-all is the default and CC is the norm. Even worse, smart phones constantly interrupt our day with every single message with almost no intelligence involved. How do we sort through the email and make sure we get the important information in a timely manner in a way we can use? Recently I discovered Context.io API and saw a light at the end of the tunnel.

In brief, Context.io is an API for your email inbox. After creating an account and synchronizing it to your account, you can retrieve any email, sort through them by date, sending, content, or a variety of other things. More importantly, you can create webhooks to trigger events as things occur within your inbox. Just like Twilio, they abstract the complicated details away under a much simpler interface. More importantly, by combining Twilio and Context.io, you can have an intelligent inbox that enables you to filter out the important details and ignore the rest.

Configuring Your Webhook

Let’s start simple by creating a text notification whenever we get a new email. Yes, any smart phone will show a notification for a new message, but let’s walk before we run.

All of the following examples make heavy use of Context.io’s webhooks. To create your first webhook, create an account with Context.io, visit their web console, and connect at least one email account. By default, you only need a callback_url and a failure_notif_url. We’ll discuss the rest of the options later.

These webhooks work exactly like the Twilio Voice and SMS Urls. When a new email comes in, Context.io will perform a HTTP POST including a payload with information about the message. The full payload is detailed in their documentation but the primary fields of interest are the account_id and message_id. We can retrieve both with a simple PHP snippet:

<?php

$json = file_get_contents('php://input');
$data = json_decode($json);

SMS Notifications

For our first revision, we’ll use the Twilio PHP Helper library to generate the TwiML as seen here:

<?php

include 'credentials.php';
require 'vendor/Services/Twilio.php';

$json = file_get_contents('php://input');
$data = json_decode($json);

//use the from/subject from the webhook call to create text message
$sms_body  = $data->message_data->addresses->from->name;
$sms_body .= " just emailed: \n";
$sms_body .= $data->message_data->subject;

//send the text message
$client = new Services_Twilio($AccountSid, $AuthToken);
$message = $client->account->messages->sendMessage(
    $fromNumber, // From a Twilio number in your account
    $toNumber, // Text any number
    $sms_body
);

As seen above, this file receives in incoming payload and parses out the name of the sender and the subject line. We concatenate these together and generate a text message from our Twilio phone number to our cell phone. Now when I send a message to myself, I get a text message like this within a few moments:

Keith Casey just emailed:
This is my test subject line

Combining Email with SMS

Now that our proof of concept is complete, we’ll make the messages more useful. While Context.io’s webhooks include the message_id and all of the email participants in the payload, it does not include the body of the message itself. To get this, we’ll have to make a second API call:

<?php
//This is a partial snippet
$contextIO = new ContextIO($consumerKey, $consumerSecret);

//use the message id to get the message body
$message = $contextIO->getMessageBody($account_id,
    array('message_id' => $message_id, 'type' => 'text/plain'));

if (is_object($message)) {
    $content = $message->getData();
    $body = $content[0]['content'];
}

This request uses the message_id from the payload to request the entire body of the message. We can get the body in either html or plain text, but in this case we’ll get the plain text. It will be the easiest way to embed in a text message.

While the message body includes file attachments, we’re not going to use them in these examples. One area for improvement is to use a short code to send those attached images via our MMS-based Picture Messaging.

Once you combine the body retrieval with the previous script, you come up with this:

<?php

include 'credentials.php';
require 'vendor/Services/Twilio.php';
require 'vendor/PHP-ContextIO/class.contextio.php';

$json = file_get_contents('php://input');
$data = json_decode($json);

$message_id = $data->message_data->message_id;

$contextIO = new ContextIO($consumerKey, $consumerSecret);

//use the message id to get the message body
$message = $contextIO->getMessageBody($account_id,
    array('message_id' => $message_id, 'type' => 'text/plain'));

if (is_object($message)) {
    $content = $message->getData();
    $body = $content[0]['content'];
}

//use the from/subject/body to create text message
$sms_body  = $data->message_data->addresses->from->name;
$sms_body .= " just emailed: \n";
$sms_body .= $data->message_data->subject;
$sms_body .= "\n\n" . $body;

//send the text message
$client = new Services_Twilio($AccountSid, $AuthToken);
$message = $client->account->messages->sendMessage(
    $fromNumber, // From a Twilio number in your account
    $toNumber, // Text any number
    $sms_body
);

Now when I send myself a test message, I get this text message within a few minutes:
Keith Casey just emailed:
This is my test subject line

This is the body of my message.

Keith Casey
Sr Developer Evangelist – Austin, TX

Find me at an upcoming event here:
http://bit.ly/trackDKC

You’ll notice that this includes the signature from the email itself. One area for improvement would be to parse the message and remove the signature. This will be challenging at best unless you figure out how to cheat. We’ll cover that in our last segment.

Email By Phone

As long as we are including the body of the message in a text message, we can just as easily convert this to placing a phone call. In this case, we’ll use TwiML to generate a series of Say verbs to read each portion of the message. The first script looks much like the SMS script from earlier:

<?php

include 'credentials.php';
require 'vendor/Services/Twilio.php';

$json = file_get_contents('php://input');
$data = json_decode($json);

$message_id = $data->message_data->message_id;

//use the message id to create the reading url
$url = 'http://example.com/rev3-readbody.php?message_id=' . $message_id;

//send the text message
$client = new Services_Twilio($AccountSid, $AuthToken);
$message = $client->account->calls->create(
    $fromNumber, // From a Twilio number in your account
    $toNumber, // Text any number
    $url
);

On the other side, to generate the TwiML, we do much the same as how we created the SMS. First we get the message_id, retrieve both the message header and body information, and then put it all together with the Say:

<?php

include 'credentials.php';
require 'vendor/Services/Twilio.php';
require 'vendor/PHP-ContextIO/class.contextio.php';

$message_id = $_GET['message_id'];

$contextIO = new ContextIO($consumerKey, $consumerSecret);

//use the message id to get the message body
$message = $contextIO->getMessage($account_id,
    array('message_id' => $message_id, 'type' => 'text/plain'));

$welcome = '';
//use the message id to get the message information
if (is_object($message)) {
    $content = $message->getData();
    $welcome  = $content['addresses']['from']['name'];
    $welcome .= ' just emailed ';
    $welcome .= $content['subject'];
}

//use the message id to get the message body
$message = $contextIO->getMessageBody($account_id,
    array('message_id' => $message_id, 'type' => 'text/plain'));

if (is_object($message)) {
    $content = $message->getData();
    $body = $content[0]['content'];
}

// create the body of the Twiml
$response = new Services_Twilio_Twiml();
$response->say($welcome);
$response->say("Here's the message");
$response->say($body);

print $response;

While this example uses phrases to separate each of the segments, a better approach may be to use the different Twilio voices. It would give a clear distinction with minimal effort.

Manage You Inbox Via Phone

Of course now that we can receive our messages via voice or text, the next logical step is to allow you to manipulate them and a good parallel is voice mail. In our case, we’ll implement a way to delete the message with the default action being to keep the message.

In this case, we’ll replace our original Say structure with Gather to collect the input of a single key:

<?php
// this is just a snippet, not the full file

// create the body of the Twiml
$response = new Services_Twilio_Twiml();
$gather = $response->gather(array('numDigits' => 1, 'action' => 'rev4-process.php?message_id=' . $message_id));
$gather->say("Press 7 at any time to delete this message.");
$gather->say($welcome);
$gather->say("Here's the message");
$gather->say($body);

print $response;

And finally, we take that input and make one final call to the Context.io API to actually delete the message:

<?php

include 'credentials.php';
require 'vendor/Services/Twilio.php';

$digits = (int) $_POST['Digits'];
$message_id = $_GET['message_id'];

$contextIO = new ContextIO($consumerKey, $consumerSecret);

switch($digits) {
    case 7:
        $result = $contextIO->deleteMessage($accountId, array('message_id' => $message_id));
        $message = "This message has been deleted";
        break;
    default:
        $message = "I have no clue what you're doing. Stop it.";
        //do nothing
}

$response = new Services_Twilio_Twiml();
$response->say($message);
$response->hangup();

Advanced Webhooks & Callback Filters

Our final step goes back to the beginning with our webhooks. At present, the above scripts will generate a text message or voice call for each and every message received. To put it simply, that’s not scalable on one’s sanity. To make this a little more selective and therefore powerful, we’ll go back to the Context.io webhooks console and dive into the optional parameters.

By using the optional parameters, we can add functionality and even simplify what we’ve already written. For example, by setting “include_body” to 1, we can get the body in the original payload and completely eliminate our second API request. By using the filter_* parameters we can limit the webhook calls to messages to or from certain addresses, only those that have files, or even those that have files with specific names. At this point, we’re really only limited by the complexity of the rules we want to build.

This is also where our cheat from above comes into play. While detecting and removing everyone’s email signature is challenging at best, if you’re only forwarding messages from certain people, you can limit your parsing to address those specific cases.

While the above scripts take into account a number of basic scenarios, these scripts could easily be expanded to support moving messages between folders, archiving emails, or even using Twilio’s transcription to draft responses. Once you ponder text and voice as another user interface, a number of things become possible. If you take these scripts further, I’d love to hear about what you build and how.