Sunday, March 9, 2014

Multi Channel Chat with Twilio and Google App Engine

Today’s post comes from Kevin Whinnery, Developer Evangelist at Twilio. In this post, Kevin describes how to build a multi-channel chat application using Google App Engine and Twilio. You can follow Kevin on Twitter at @kevinwhinnery or on Google+.







Google App Engine enables developers to focus on their application’s logic by providing a scalable infrastructure and high-level APIs for persistence, file management, and other common web app needs. XMPP and Channels are among these APIs, making it ridiculously easy to write awesome real-time communications apps in the browser.



Today, we’re going to break down an example application (view it live, source code) that integrates these two App Engine services (plus SMS messaging from Twilio) in a group chat application that connects users via SMS, XMPP, and browser-based chat clients.



We won’t go through every line of code, but at a high level, this application is about receiving inbound messages and sending outbound messages. Let’s see how we do this via SMS, XMPP, and Channels.



Twilio SMS

Sending SMS text messages with the Twilio API requires signing up for a Twilio account. Once you’ve signed up for an account, you can use your account SID and auth token to make authenticated requests against the Twilio REST API. You could just use App Engine’s built-in URL fetch service to interact with the Twilio API, but our official helper library for Java makes authenticating requests and serializing data much easier, providing a POJO interface to Twilio resources and functionality. We’ll be using the Twilio helper in this example. If you’re looking for App Engine specific reference examples, our friends at Google included this reference documentation in their doc site.



In our chat application, all outbound communication and message dispatching is handled by the MultichannelChatManager class. In this application, we add subscribers to the chat room to an in-memory set. When it’s time to send out a message, we iterate over the members of this set and send out messages to all subscribers. We send out messages to SMS subscribers using the Twilio helper on line #56:

TwilioRestClient client = new TwilioRestClient("ACCOUNT_SID", "AUTH_TOKEN");

Map params = new HashMap();
params.put("Body", messageBody);
params.put("To", sub);
params.put("From", "+16122948105");

SmsFactory messageFactory = client.getAccount().getSmsFactory();

try {
Sms message = messageFactory.create(params);
System.out.println(message.getSid());
} catch (TwilioRestException e) {
e.printStackTrace();
}
To receive inbound communication, you will need to purchase a Twilio phone number or use the one given to you when you signed up for a Twilio account. You can configure this phone number to send an HTTP POST to a URL that you choose when an SMS message is received (this callback pattern is called a webhook). In this sample application, we have a Java servlet with a web.xml file configured to accept inbound SMS. In your Twilio number configuration, you would enter https://yourappname.appspot.com/sms, as below:



In the actual servlet, we handle inbound SMS messages first by looking for a “STOP” command, which will indicate that this user no longer wants to receive text messages from the app. Then, we confirm that the user is subscribed (by looking for their telephone number). Finally, we send out a message using our MultichannelChatManager class.



When Twilio sends your app the details of an SMS message with an HTTP request, it expects your app to respond with an XML format called TwiML. TwiML is a simple set of fewer than 20 XML tags that tells Twilio how to respond to inbound communication. The output of our SMS servlet is an XML (TwiML) document, which will send an SMS back to a user if they unsubscribe:

public class TwilioSmsServlet extends HttpServlet {
// Handle Incoming SMS
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
TwiMLResponse twiml = new TwiMLResponse();

// parse the body, looking for a command
String smsBody = request.getParameter("Body");
String smsFrom = request.getParameter("From");

// Unsubscribe, if requested
if (smsBody.startsWith("STOP")) {
MultichannelChatManager.removeSub(smsFrom);
com.twilio.sdk.verbs.Sms bye = new com.twilio.sdk.verbs.Sms("You have been unsubscribed. Thank You!");
twiml.append(bye);
} else {
// If they arent subscribed, subscribe them
if (!MultichannelChatManager.isSubscribed(smsFrom)) {
MultichannelChatManager.addSub(smsFrom);
}
MultichannelChatManager.sendMessage(smsBody, "sms");
}

response.setContentType("application/xml");
response.getWriter().print(twiml.toXML());

} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
}

App Engine XMPP Integration

App Engine provides a simple API for sending and receiving XMPP chat messages. Our chat application can receive new messages over XMPP and send them back out to all subscribed clients, similar to how our app behaves for SMS.



App Engine applications have an XMPP username associated with them by default, which takes the form of “appname@appspot.com”. The former part of the username is your unique App Engine app ID and the latter is the appspot domain that your app runs on. To send a message via XMPP to our chat app we need to send a chat message to “twiliosandbox@appspot.com” from a chat client that supports XMPP. If you used the desktop chat client Adium for Google Talk, the interaction might look something like this:



For our application to receive inbound XMPP messages, we need to configure an inbound message handler servlet in our web.xml configuration file. This webhook callback design is the same type of event mechanism used by Twilio to deliver SMS messages to our application. In this servlet, we receive an inbound POST request with information about an inbound chat message:

public class XMPPReceiverServlet extends HttpServlet {
// Handle Incoming XMPP Chat messages
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
XMPPService xmpp = XMPPServiceFactory.getXMPPService();
Message msg = xmpp.parseMessage(req);

// The "JID" is the unique ID of this chat client, which we use to subscribe
JID fromJid = msg.getFromJid();
String body = msg.getBody();

// Unsubscribe, if requested
if (body.startsWith("STOP")) {
MultichannelChatManager.removeSub(fromJid.getId());
} else {
// If they arent subscribed, subscribe them
if (!MultichannelChatManager.isSubscribed(fromJid.getId())) {
MultichannelChatManager.addSub(fromJid.getId());
}
MultichannelChatManager.sendMessage(body, "xmpp");
}
}
}
To send outbound messages, we use the App Engine platform APIs to send an outbound message to a specific JID, which uniquely identifies a connected XMPP client:

JID jid = new JID(sub);
Message msg = new MessageBuilder().withRecipientJids(jid).withBody(messageBody).build();
XMPPService xmpp = XMPPServiceFactory.getXMPPService();
xmpp.sendMessage(msg);

Channel API

The Channel API allows server-side push to connected clients in an App Engine application. In our chat application, we will utilize this API to push new chat messages to browser-based clients.



In order for our server to push chat messages to a browser, the client needs to be issued an ID by our server. We configure a servlet to handle issuing these IDs (and to handle incoming chat messages created by the browser in JavaScript) in web.xml. The servlet generates a unique ID for a connected client, based on the current system time:

//Generate a client token for the GAE Channel API
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
ChannelService channelService = ChannelServiceFactory.getChannelService();

//The token must be based on some unique identifier for the client - I am using the current time
//for demo purposes...
String clientId = String.valueOf(System.currentTimeMillis());
String token = channelService.createChannel(clientId);

//Subscribe this client
MultichannelChatManager.addSub(clientId);

//Reply with the token
res.setContentType("text/plain");
res.getWriter().print(token);
}
In the browser, we get an ID for the current user via XHR. First, we include the Channel client JavaScript library by requesting a special URL on the App Engine server. Then we use jQuery to issue a GET request to our server to obtain a client ID:

//Get a client token to use with the channel API
$.ajax(/chat,{
method:GET,
dataType:text,
success: function(token) {
console.log(token);
var channel = new goog.appengine.Channel(token);
var socket = channel.open();

//Assign our handler function to the open socket
socket.onmessage = onMessage;
}
});
When we get our client ID, we use that to configure the App Engine channel service in the browser for data pushed from the server. Data pushed from the server is handled in a callback function, which updates the textarea on the page. When the user enters a chat message in the browser, we issue a POST request to our ChatServlet, which uses the MultichannelChatManager class to publish a message to all connected clients. This is where we use the channel API to push data to connected web browsers:

ChannelService channelService = ChannelServiceFactory.getChannelService();
channelService.sendMessage(new ChannelMessage(sub,messageBody));

Wrapping Up

In this walkthrough, we explored three messaging APIs that work nicely on App Engine: Twilio SMS, XMPP, and Channels. Our example used Java, but all three APIs will work with Python and Go as well (Twilio has a helper library you might use for Python also).



Using platforms like Twilio and App Engine, developers can create communications applications, which previously would have required expert knowledge and infrastructure to build, in a fraction of the time. I hope you’ll be able to use these APIs to engage with your users wherever they happen to be.



Application source code is available on GitHub here.



- Contributed by Kevin Whinnery, Developer Evangelist, Twilio

Related Posts by Categories

0 comments:

Post a Comment