Posts Tagged ‘Twitter’

How to build an automated Twitter bot using JavaScript and Tropo

Friday, August 13th, 2010

We’re back with more how-to goodness!  While perusing our relatively extensive blogging history and existing documentation, we noticed the previous examples showing how to link Twitter and Tropo were moderately complex and potentially daunting, especially for someone just trying to get an initial feel for how it works.  So, in an attempt to remedy that, we endeavored to provide an easy, all-inclusive example from start to finish.  Hopefully we succeeded.

Our example today is a simple RSVP Twitter Bot; when someone tweets the Twitter account attached to your application, it responds back automatically with a confirmation indicating their RSVP was received.   In this case, we set it up to be password driven; if the application receives the Tweet “Tropo Rocks!”, it responds with an approval message.  Any other message will cause the Twitter bot to respond with a message indicating they sent the wrong password and to try again.

We’ll assume you have a Tropo account at this point and begin with creating your Twitter app.  If you don’t already have a account, you can click here to create one.

1) Login to your Tropo account, then go to Your Applications and click the Create New Application link:

2) Choose Tropo Scripting; this will bring up the New Application window:

3) Give the application a name; we chose ClubTropo to fit the example, but you can use whatever you’d like.  Next, click the link for Hosted File (next to What URL powers your app?) and choose Create a new hosted file for this application.  This will open the New Mapped File window:

4) You will need to give the file a name; as with the application name, it can be anything you like, but make sure this one ends with .js to identify it as Javascript .  In the box for File Text, enter in the following simple code:

answer();
message = currentCall.initialText;
if(message == "Tropo Rocks!"){
    say("You just learned how to make a Twitter bot with @Tropo for free!  Welcome to the club.");
}
else {
    say ("Say what?  Try it again - send us a tweet with the correct password to get on the list.");
}

Then click Create File to save it.

To explain what the code does:

  • The answer method “picks up” the tweet, like it would “pick up” a phone call.
  • We then define the variable message as currentCall.initialText, which contains the tweet sent by the user (go here for more on currentCall).
  • Next, we specify what to tweet back if the user’s tweet has the correct message (i.e., Tropo Rocks!) and when it does not.
  • That’s it!  That’s all the code you need.

5) Once the file has been created and you’re back on the New Application screen, you’ll see the path to your file has been automatically filled in for you.  Now click Create Application to save the application as a whole:

6) This will bring up the page in the next screenshot.  Here, we need to add our Twitter account to the application; this will be the Twitter account that will act as the bot.  Select the Twitter icon at the bottom of the screen and you’ll see a link that says Click to activate Twitter.  This will attempt to connect to whatever Twitter account you’re currently logged into, so make sure you’re in the right one before you click the link.

7) Twitter will display a webpage in your browser asking you to Deny or Allow Tropo access to your Twitter account.  If you’re definitely in the right account, go ahead and click Allow:

8) You will then return back to the Tropo New Application screen and your Twitter account name will be listed:

9) Click the Update Application button to save the change, and you’re ready to test!  Our Twitter bot is named ClubTropo and we sent it a message from two different accounts.  From TropoUser, we sent “@ClubTropo Tropo Rules!”, which was rejected.  From JD_Gryph, we sent “@ClubTropo Tropo Rocks!”, which was accepted as the correct password.  Check out the responses below:

Voila!  RSVP Twitter Bot achieved.  Feel free to test with @ClubTropo and/or create your own; the door is wide open!

For more information, check out our Tropo Scripting documentation, or check out our Tutorials and Samples.

(Sidenote:  You may want to check out this blogpost before testing any Twitter apps; Twitter can be touchy with multiple instances of identical messages)

Scaling Your Twitter Support, Part 1: Adding a “Night Service” via Tropo.com

Friday, May 7th, 2010

What do you do if your use of Twitter for customer interaction is wildly successful? How do you scale your support for using Twitter for customer service, customer support or other topics? Do you hire a bunch of extra people? like Staples did? Or do you look at using tools and services to help your existing staff through automation and/or augmentation of the staff’s efforts?

If you have been reading previous posts about Tropo and Twitter or if you follow me on Twitter, you’ll know I’m a wee bit passionate about the service… and this particular question continues to intrigue me:

How do you scale your Twitter support?

Obviously you can hire a bunch of people to help with responding to tweets, like Staples and Comcast (which once upon a time had only Frank Eliason on Twitter but now has a whole crew). You can have them work different shifts or be in different parts of the world to help with coverage. You can do all that, if you can afford to do so.

But for many companies, that may not be an option… and so ever since we introduced support for developing Twitter applications in Tropo.com, I’ve been thinking about how you could use Tropo to automate and augment your Twitter support. In a couple of presentations about Unified Self-Service, I’ve mentioned having a couple of people who support Twitter interaction and asked the question:

What do you do when those employees go home for the evening?

Do you:

  1. Wait to respond to all Twitter inquiries until the next business morning?
  2. Require your staff to check every few hours to respond?
  3. Require your staff to work off hours to have 24×7 coverage?

Many companies probably treat Twitter messages like email or phone… “we’ll get back to you tomorrow”… but what if you want to provide a higher level of service? What if you want to help people by pointing them to your website?

A SOLUTION?

One solution that came to my mind was to resurrect the “night service” idea from the telephony days… create an automated agent attached to your Twitter account that only replies to Twitter messages during certain hours.

This turns out to be ridiculously simple in Tropo! Here’s a VERY basic implementation in python:

from datetime import *
answer()

if datetime.now().hour not in range(12,21) :
    say("Our offices are currently closed. We will reply to your tweet as soon as we can but in the meantime, please visit http://www.tropo.com")

hangup()

That’s it.

NOTE: Time on Tropo servers is in UTC/GMT, so you need to adjust offset accordingly. US Eastern Daylight Savings Time is +4 hours, so 5pm is 17:00 EDT = 21:00 UTC. My example code, therefore, does NOT respond between the hours of 8:00 am and 5:00 pm US Eastern time.

MAKING IT SMARTER

Now, this example is exceedingly dumb. It simply fires back a single tweet as an “@” response to any mention of the Twitter account. This is probably not what you want, given that it doesn’t differentiate between a “reply” to your Twitter account (where the “@username” is at the very beginning) and just a reference to your Twitter account in the body of a tweet.

So how do you tell the difference?

It turns out that there is a simple way (and yes, we need to document this better). If the Twitter message is a “reply” with your “@username” at the beginning of the tweet, your Twitter username is removed before the tweet text is sent to your app. If the Twitter message mentions your Twitter username, it is just included in the message text. The rationale here is that a reply is pretty much like an instant message or SMS … and so we are making it easy for your app to treat IM, SMS and Twitter replies in the same way. Regardless, the “currentCall.initialText” variable is loaded with the text that was sent to your account – your Twitter username is just stripped out if it is a reply message.

The end result is that you can determine if the message is a “reply” (or “public message”) to your twitter account by testing for the absence of your Twitter name.

Modifying the code above to respond to only messages versus mentions, it looks like this in Python:

from datetime import *
answer()

if datetime.now().hour not in range(12,21) :
    if currentCall.initialText.find("@stratohelp") == -1:
        say("Our offices are currently closed. We will reply to your tweet as soon as we can but in the meantime, please visit our web site at www.tropo.com")

hangup()

In this case, the python string function “find” returns a “-1″ if the string is not found. The Twitter username I’m showing here is “@stratohelp”, which you obviously need to replace with your own account name.

Now, again, I’m still sending out only a very basic message. I could start adding more functionality to this to make it smarter. Features like:

  • Scanning the “initialText” for keywords and responding back with specific URLs. For instance, pointing people to a FAQ entry for particular phrases.
  • Performing other actions based on keywords in the tweet, like sending email or a SMS to a certain person within your company.
  • Using the Twitter ID (found in currentCall.callerID) to personalize the message back, perhaps by doing a dip into a database to retrieve info.

There are many more actions you can take… and I’ll look at some of those in the next posts in this series.

TRYING IT OUT

To try this yourself, just:

  1. Login to your Tropo.com account (or sign up for a free developer account).
  2. Create a new application and set it to only respond during certain hours (remembering the UTC offset). If you copy/paste my code above, name the file ending in “.py”.
  3. Follow these steps to link a Twitter account to your Tropo app.
  4. Try out sending tweets to that Twitter account during both the time the app should respond and when it shouldn’t.

Note: When you are developing and testing an app, please do remember the restrictions on duplicate tweets… your app will not be able to keep sending the identical response to the identical Twitter account, so you may want to have multiple Twitter IDs for your testing.

Have fun with it… and please do let me know if you do anything cool with a Twitter app. I’d love to write about other fun uses here. Also, if you have examples similar to the code I’ve done above in languages other than python, please let me know as a comment here or as an email and I’d be glad to add them here as additional examples.

Tropo and the Upcoming Twitter API Change

Monday, May 3rd, 2010

Following up on IMIfied’s post on the upcoming Twitter API changes, Tropo will not be impacted since we only support OAuth for authentication with Twitter. Therefore when Twitter shuts off their HTTP Authentication at the end of May 2010, you do not have to change anything for your Tropo apps using Twitter.

Testing a Tropo app with Twitter? Remember that Twitter rejects duplicate tweets…

Friday, March 26th, 2010

Just a quick note… if you have decided to try building a Tropo app that interacts with Twitter, perhaps based on our blog posts about how to add Twitter to a Tropo app and how to make a Tropo app react differently per channel, you may want to be aware in your testing that Twitter rejects duplicate tweets sent in a quick time period. This Twitter Support ticket has the info:

http://help.twitter.com/forums/10713/entries/68809

I got bit by this yesterday testing the weather app I outlined in those blog posts. As I was modifying the code, I was tweeting out the same ZIP code to test the differences:

@danweathertest 32801
@danweathertest 32801
@danweathertest 32801

and didn’t understand why the subsequent tweets just… disappeared. I was using TweetDeck and it was only when I tried tweeting from the actual Twitter web site that I saw a quick error message pop up that pointed me to that support ticket.

Obviously if you do not tweet the same exact text, you are okay. In my case, I just started using other ZIP codes… like “90210″ and “12345″. (the latter being Schenectady, NY, of all places!)

An Example of How to Make a Tropo App Respond Differently to Different Channels (including Twitter)

Thursday, March 25th, 2010

Would you like to have your Tropo.com app respond differently depending upon which communications channel is used to connect to the app? Would you like to, for instance, have the app provide more of an introduction to voice callers but not for text channels like IM, SMS or Twitter?

Earlier today, I wrote about how to add Twitter support to a Tropo app and at the end of the piece I mentioned that my Yahoo!Weather sample application responded with more messages that I really wanted it to send via Twitter. In this post, I will show how you can use the “currentCall” session variable to tweak your app to respond differently depending upon which channel is being used.

First, let’s look at how the app originally responded via IM to a ZIP code:

tropobyIM.jpg

After one IM to the app I received seven responses! Not good. Perhaps in IM it might be okay, but in SMS or Twitter it is a bit too verbose.

REMOVING EXTRA MESSAGES IN TEXT MODE (BUT NOT IN VOICE)

If you look at those messages, the first and last messages are “extra” messages that introduce the app to the user and then clue the user into the fact that the app is done. We could just eliminate them, but then, particularly with the final message, we’re being kind of rude to the person who is calling in. We are just giving the report and hanging up. Perfectly fine in a text-based interaction, but not great for a voice interaction.

To modify the app to respond differently according to channels, we need to use the “currentCall” session variable. With that variable you have access to the parameters of the “callObject, including, most relevant to this issue, “channel“. So in python, my personal language of choice, I can add this at the beginning:

if currentCall.channel == "VOICE":
    say( "Welcome to the Tropo dot com sample Yahoo weather app." )

and this at the end:

if currentCall.channel == "VOICE":
    say( "Thats all. Goodbye!" )

With those two additions, I’ve now made it so that if you call the application at +1 (407) 374-3994 you will hear those phrases but if you use a text channel (SMS, IM or Twitter) you will not see those messages.

I then went through the python code and eliminated the multiple “say” commands so that the location, time, temperature and condition were all written on one line. The result is that now the interaction is down to 2 messages:

tropobyIM2.jpg

MORE TWEAKING FOR VOICE

This is obviously better, but notice that first message “Enter the ZIP code...“. If you hear that in a voice call, will you realize that you could either enter the ZIP code in digits on your keypad or say the ZIP code? Probably not… you probably think you can only use DTMF digits. So let’s change the prompt a bit:

if currentCall.channel == "VOICE":
    say( "Welcome to the Tropo dot com sample Yahoo weather app." )
    result = ask( "Please say or enter the ZIP code for a weather check", { 'choices' : "[5 DIGITS]" })
else:
    result = ask( "Enter the ZIP code for a weather check", { 'choices' : "[5 DIGITS]" })

Now we have a more friendly prompt for voice that clues people in to the fact that they can either enter digits or use speech recognition.

REMOVING THE INITIAL PROMPT IF DATA IS PROVIDED

The next step is obviously to get rid of that initial “Enter the ZIP code...” prompt for a text channel if a ZIP code is sent in the initial message. This points to a major difference between voice and text channels. With voice, you need to have that initial prompt. With text, the user can already know what they need to send and just send that data along.

The currentCall session variable also has a “initialText” parameter that includes the text sent by the user. So I can see that the next step I need to do is something like:

If currentCall.initialText is a valid ZIP code, don’t send the first prompt. If currentCall.initialText is NOT a valid ZIP code, or is just general text, do send the initial prompt.

Unfortunately: 1) I have to leave in a few minutes to catch a plane to fly back to my home in NH; and 2) I’ve been spending more of my days writing in PowerPoint versus python, so my coding is a bit rusty. My first reaction is just to do a try / except but I know I’ll need to play with it a bit. So I’ll tweak it some more later.

PLAYING WITH THE CODE

If you are a pythonista, you’re welcome to play with my sample app. I uploaded it to Github at:

http://github.com/danyork/tropo-twitter-weather-sample

If you come up with an elegant way to get rid of that initial prompt, I’ll be glad to write about your solution here.

And if you create a similar app in another language, I’m also glad to write about that here as well.

USING OTHER currentCall OPTIONS

Beyond “channel” and “initialText” there are a number of other interesting parameters found on the callObject reference page. While currentCall.channel lets you broadly differentiate between voice and text channels, currentCall.network lets you get even more granular and respond to specific channels differently. And of course currentCall.callerID is always a useful variable to have around.

With that, I’ve got a plane to catch… stay tuned for more…

How to Add Twitter Support to a Tropo.com App – Step by Step

Thursday, March 25th, 2010

With our new announcement of support for Twitter in Tropo.com apps, I thought I’d walk through the steps of adding Twitter support to an app.

I’ve got a basic “Yahoo weather” app that I copied from the Tropo tutorials and sample apps. You can call it today at “(407) 374-3994″, send it SMS at the same number, (407) 374-3994, or use Jabber IM to contact it at “danyahooweather@tropo.im”. You give it a US ZIP code (try “32801″ for Orlando) and it gives you a summary of weather.

Now I want to add Twitter support.

STEP 1: Login to your Twitter Account (either a new or existing one).

Obviously you first need a Twitter account to link to your app. You can use an existing Twitter account or, like I did, create a new one: @danweathertest. The next steps work best if you login to this account on Twitter.com before proceeding.

STEP 2: Start the Twitter activation process in Tropo.com.

Next you login to Tropo.com, click on the “Account” link and open up the application you want to link to Twitter (or create a new app – try one of the samples if you don’t have an app yet). At the bottom of the application info, you’ll see the tabs for the different channels you can use – click the Twitter “t” icon:

tropotwitter1-s.jpg

Just click the link to “activate Twitter”:

tropotwitter2-1.jpg

STEP 3: Allow the Tropo app access to your Twitter account.

You will now be taken to Twitter.com to complete the OAuth authentication process. If you did login to your (new or existing) Twitter account back in Step 1, all you need to do is click the “Allow” button. If you didn’t, or are logged into the wrong Twitter account, you’ll need to sign out of Twitter and login with the correct account.

tropotwitter3-s.jpg

After you click “Allow”, you will be redirected back to Tropo.com where you will see the message after a moment that Twitter has been successfully activated:

tropotwitter4-s.jpg

STEP 4: CELEBRATE!

That’s it! You’re done!

Welllll… you might want to test your app a bit to make sure that it works well with Twitter. For instance, when I send a tweet to my Yahoo weather bot:

@danweathertest 03431

I get back a flow of multiple messages:

tropotwitter5-s.jpg

Now maybe that is what you want… maybe it’s not. I think ideally it would summarize that report into a single tweet… but that’s an application implementation detail that I just have to go back and work on in the python code for my app.

WRAPPING UP

In just a few steps I’ve added Twitter support to an existing Tropo.com app so that this one single Tropo application can be reached by any of these channels:

Voice:

+1 (407) 374-3994
Skype: +99000936 9991438833
SIP: sip:9991438833@sip.tropo.com
INum: +883510001814088

SMS: (407) 374-3994
Jabber IM: danyahooweather@tropo.im
Twitter: danweathertest

Now I could go on and add a MSN account, AOL account, etc., but for the purposes of this example I’ll leave it at that.

That’s all there is to it… take out a Twitter account, do the OAuth dance, and there you are! I look forward to seeing what kind of Twitter-related apps you all create!

Tropo Adds Twitter and goes International

Thursday, March 25th, 2010

Tropo is all about making it easy for you to interact with your customers. What if you could write one application and communicate with your customers via voice, instant messaging (IM), text message (SMS) and even Twitter? And what if they could call that application from anywhere in the world and talk with it in their own language?

Welcome to the new Tropo.com release today!

Tropo was already the easiest way to build voice-powered phone applications. Today, the Tropo cloud communications platform goes into full production launch with a suite of new features including:

  • TWITTER SUPPORT – You can now assign a Twitter name to your application and customers can interact with the app by simply sending a Twitter message to that name. Your app will see all messages to the Twitter account and can take action on messages that are received. Want to set up an app to do customer support over Twitter? Want to make your web application be able to service Twitter users? Now you can. Simple and easy and all secured by OAuth, too. Read more:
  • INTERNATIONAL PHONE NUMBERS – No longer are Tropo phone numbers limited to North America. Now for a low monthly fee you can get numbers in over 30 countries: Austria, Belgium, Brazil, Croatia, Czech Republic, Denmark, Estonia, Finland, France, Germany, Greece, Ireland, Israel, Japan, Latvia, Lithuania, Luxembourg, Malta, Mexico, Netherlands, New Zealand, Norway, Panama, Peru, Poland, Portugal, Slovakia, Slovenia, Spain, Switzerland and the United Kingdom.
  • INTERNATIONAL OUTBOUND DIALING – Naturally if you can accept inbound calls from other countries you can also make outbound calls to virtually any country around the world. Making international calls? Ask us for our international rates.
  • INTERNATIONAL TEXT-TO-SPEECH – All those fancy new international numbers deserves some fancy new language support, too. A simple flag in your code lets you change the voice used for text-to-speech to any of these languages: US English, UK English, Dutch, French, German, Italian, Mexican Spanish or Castilian Spanish. Read more:
  • INTERNATIONAL SPEECH RECOGNITION – Tropo.com has always supported automatic speech recognition (ASR) in English so that you can have your callers talk back to your app instead of having to punch their touchtone responses in… now we’ve added ASR support for more languages: UK English, Dutch, French, German, Italian, Mexican Spanish or Castilian Spanish.
  • SMS SUPPORT – Does your app have a US number? The exact same phone number you use to call your app can also be used to receive and reply to SMS messages. Want to broadcast an announcement or send a user some alerts? Your app can send SMS messages, too. Connect the millions of mobile users to your application now. It’s the same API as your voice application, so you don’t need to write two different apps. Read more:
  • IM and CHAT SUPPORT – You can add instant messaging accounts to your app and communicate via AOL’s AIM, Yahoo!Messenger, Microsoft’s Live Messenger, GoogleTalk or any XMPP or Jabber IM service. We’ll even give you a nifty “tropo.im” Jabber ID for your app. Like SMS, IM uses the same API as your voice apps. No extra coding required.
  • TOLL-FREE PHONE NUMBERS – Want to make your app accessible to everyone in North America at no cost to them? Add a toll-free number. And because you shouldn’t have to pay extra just to provide your customers with more convenience, calls to toll free numbers are priced the same as local numbers.
  • NEW REST/JSON WEB API – Beyond the hosted scripting Tropo has always offered, you can now host your application on your own web server, write it in whatever language you choose and communicate through our RESTful web API using JSON. Read more:
  • ONE-CLICK MOVE TO PRODUCTION – After you have developed your app and want it to go live, it’s just one click in the web interface to move it into production. No contracts, no calls into a salesperson. Simply go into the web management interface and flip the switch. That’s it.

Beyond the new features, Tropo continues to offer the following:

  • FREE DEVELOPER ACCOUNTS – No need to provide a credit card. No need to purchase credits just to try out your apps. We let you sign up completely for free… get free North American phone numbers, free outbound calling, free support… Just head over to Tropo.com to sign up now.
  • SKYPE, SIP and INUM NUMBERS – We know that the world of voice calling is moving beyond the legacy Public Switched Telephone Network (PSTN). When you create an app on Tropo, you immediately get (at no extra charge) a Skype number, a SIP address and an iNum number to let people call your app from all those newer networks, too.
  • FREE DEVELOPER SUPPORT – We don’t call our support team the “Customer Obsession Team” for nothing… they are obsessed with making sure you get the information you need to make your apps successful and they’re doing it around the clock.
  • TROPO SCRIPTING – If you don’t want to host your application on your own web server you can easily host it on our cloud in Ruby, JavaScript, Python, PHP or Groovy.
  • VOXEO’S REAL-TIME CLOUD INFRASTRUCTURE – Tropo.com runs on top of Voxeo’s worldwide cloud infrastructure that is optimized for real-time communications and will ensure your calls and messages go through.

We’re very excited that Tropo has gone live and we’re looking forward to seeing what you all do with it. Why not start now? Head over to Tropo.com and create an account. Or, if you are an existing user, login and check out all the new features you can add to all your existing applications.

Twitter mashups with Tropo

Wednesday, January 27th, 2010

This is a guest post by Mark Silverberg (@skram on Twitter). Mark acted as a guinea pig for the Tropo Web API and Ruby library in the weeks leading up to its release last week. He wrote a sample app mashing up Twitter and voice and provided this overview and code walk through.

With Tropo’s newly released web API, developers can create unified communications (telephone, cellular text message, instant message (Jabber, AIM, Yahoo, MSN) applications using traditional document-based web development flow. The code gets stored on the developer’s choice of host and is referenced each time a call comes in (or is initiated using the Session API for outgoing phone calls). This additional approach to creating voice and IM-enabled applications allows developers to integrate Tropo code into their existing and new applications seamlessly, using their own hosting and servers. (The existing, hosted Tropo is still available.)

Here’s a twitter-by-phone example that uses the standard Twitter RSS feed for content, Sinatra as a web daemon (you could use rails or something else too) and the Tropo web (JSON) API. Since Tropo code is stored and run on the server of your choice, you can use any gems, custom classes, etc. to connect your data and content with phones and chat clients globally and on your own terms.

Instead of being a flat-file script with loops to validate input and drive the call flow with blocks of scripting logic, the JSON API with this shiny ruby gem allows for a document-based approach – like traditional web development. You can think of http://example.com/index.json, the URL you set as your Tropo App URL, as your website’s landing page. It is the first page/document ‘hit’ when a user calls your application.

tweet flow chart

Once the instructions in that document are finished, one of two Tropo API events are triggered (more about the code for this below): continue or incomplete. The events are triggered after the requested input is received (or not received). Example: If we ask for someone’s zip code, as soon as they say it, we continue to the specified next document. It’s as if, by saying, entering, or texting 5 numbers, the user has pressed the “Submit” button and they continue through your app to hear the weather, traffic conditions, tweets in proximity, what-have-you.

Making it all work together

Note, you could even integrate this right into your existing Sinatra/Rails/etc. web app. Just add a responds_to or document view for index.json, and put your Tropo code right in there. In the context of this Twitter example, I could have index.html be a HTML form asking for how many tweets the user would like to see at once, process.html be an intermediary view, and then page_of_tweets.html be, like it is in our voice application, a page-view of the number of tweets they requested. Your HTML page would have a “next” button for page, unlike the code published here which automatically reads the next page.

Before diving into the code (the version described below is in full here or the latest copy is available on GitHub), you may want to try this out for yourself. Call the @voxeo tweets by phone line at (253) 217-4272 or with Skype at +99000936 9991429531 for a demo. You can also create a Tropo app and point it to wherever you have hosted this web service. We deployed on Heroku for our demo, but you can use any Ruby web hosting you like.

As you’ll see, this code sample presents some number of tweets per ‘page’, defined by the caller. It will continue on to infinity, or however many tweets are available. As you step through the code, it may also be helpful to take a look at the more basic examples included with the Ruby library.

The call flow for this web service is quite simple. A call comes in and index.json is rendered.

post '/index.json' do

Sessions and Variables (twitter.rb, lines 5-9)

v = Tropo::Generator.parse request.env["rack.input"].read
session[:id] = v[:session][:id]
session[:caller] = v[:session][:from]
session[:user] = "voxeo"
session[:page] = 1  # This shouldn't be tinkered with unless you don't want the most recent tweets to be heard.

At one fell swoop, this web route collects the information Tropo sent the web service (like session ID, caller information, and timestamp) and stores relevant information into a session object Sinatra provides us for variable storage which will last the duration of the session. For this application, we’re storing the call_id and caller information information for debugging purposes. We also store the user we want to hear the tweets for here. Sessions are a convenient way to store not only caller information, but data we will want to use later in the call flow such as the page number of tweets we are reading.

Events (lines 11-14)

t.on :event => 'continue', :next => '/process.json'
t.on :event => 'error', :next => '/error.json'     # For fatal programming errors. Log some details so we can fix it
t.on :event => 'hangup', :next => '/hangup.json'   # When a user hangs or call is done. We will want to log some details.
t.on :event => 'incomplete', :next => '/incomplete.json'

On line 10, we initiate our Tropo::Generator object to which will append what we want Tropo to do for us. Lines 11-14 define our events for our index.json route. These tell Tropo what to do next, if there’s an error, if the user hangs up, or if the user doesn’t do what we expect them to do. As you’ll see at the beginning of the other routes, we want the same routes of error.json and hangup.json to be triggered for their respective events, so we repeat those lines.

Say & Ask (lines 15-22)

t.say "You have reached @#{session[:user]}'s tweets-by-phone."

t.ask :name => 'count', :bargein => true, :timeout => 7, :required => true, :attempts => 4,
    :say => [{:event => "timeout", :value => "Sorry, I did not hear anything."},
             {:event => "nomatch:1 nomatch:2 nomatch:3", :value => "That wasn't a one digit number."},
             {:value => "How many tweets do you want to listen to at once? Enter or say a one digit number."},
             {:event => "nomatch:3", :value => "This is your last attempt. Watch it."}],
              :choices => { :value => "[1 DIGITS]"}

Line 22 is the first thing the user hears. We welcome to our app and dynamically (because we stored the twitter username into our session hash) let them know whose tweets they are about to hear. Lines 17-22 are all for one Tropo action: ask. This block of code, which will be latter referenced as the action “count” prompts the user for how many tweets they will want to hear per proverbial page. It may seem weird that the actual question is sandwiched by events within the :say => [] block, but it’s actually very intuitive and conversation-like. When an event is matched (for example timeout, nomatch, or nomatch:#), the corresponding value will be said to the user before or after the question being posed. In this example, we sternly warn the user after 3 unmatchable responses that it’s their last chance after the question is repeated.

Continue: If the user inputs (here, they can speak a one-digit number or enter it using their telephone keypad) a valid answer to our question, the continue event will be triggered and the call continues.

Incomplete: If the user fails all four attempts we allotted them, they are transferred to the “incomplete” event we defined which in this case notifies them of what happened and terminates the call. Because we defined the hangup event again inside of incomplete.json, hangup.json (lines 69-74) will be executed once the call is terminated and Sinatra will print the information on the screen. For actual applications, this is where you’d probably want to log information for historical reporting and/or billing fun.

process.json (lines 40-49)

post '/process.json' do
  v = Tropo::Generator.parse request.env["rack.input"].read
  t = Tropo::Generator.new
    t.on  :event => 'error', :next => '/error.json'     # For fatal programming errors. Log some details so we can fix it
    t.on  :event => 'hangup', :next => '/hangup.json'   # When a user hangs or call is done. We will want to log some details.
    t.on  :event => 'continue', :next => '/say_page_of_tweets.json'
    session[:count] = v[:result][:actions][:count][:value]
    # t.say "@#{session[:user]} tweets coming right up!"
  t.response <br />
end

For calls that give us a one-digit number for the question we asked on line 17, we store that number into our session hash and send them to the next document which will read the tweets they requested.

say_page_of_tweets.json (lines 51-67)

post '/say_page_of_tweets.json' do
  t = Tropo::Generator.new
    t.on  :event => 'error', :next => '/error.json'     # For fatal programming errors. Log some details so we can fix it
    t.on  :event => 'hangup', :next => '/hangup.json'   # When a user hangs or call is done. We will want to log some details.
    t.on  :event => 'continue', :next => '/say_page_of_tweets.json'
    t.say "I'm about to read you the"
    t.say say_as_ordinal(session[:page])
    t.say " #{session[:count]} tweets by #{session[:user]}."
    source = "http://twitter.com/statuses/user_timeline/#{session[:user]}.rss?count=#{session[:count]}&page=#{session[:page]}"
    rss = REXML::Document.new(open(source).read).root
    rss.root.elements.each("channel/item") { |element|
      t.say "Tweet from about #{Time.parse(element.get_text('pubDate').to_s).to_pretty}"
      t.say reformat_uris(element.get_text('title').to_s) + "," # comma for extra pause between tweets.
    }
    session[:page] += 1
  t.response
end

This is where our application loops and reads the requested tweets one-by-one. This is the moment the session hash has been waiting for. We use the information we collected in index.json and process.json to report the requested information to the user.

Lines 61 through 64 is where we render the Twitter RSS feed we requested in line 60, and go through each tweet received to read the age of the tweet, followed by its content. As you can see on lines 57, 62, and 63, we use helpers whose code can be found in the accompanying goodies.rb file to present the data in a more user-friendly way.

At the end of each execution of say_page_of_tweets.json, as seen on line 65, we increment the session variable for page, so the next X-number of tweets are read when the action is re-executed (because, you guessed it, line 55 tells Tropo to execute that same document again).

Goodies.rb

As you can see on the first line of the script, we require()’d the ruby file goodies.rb. In it, I’ve provided some helpful methods for this particular app. I simply put them in a separate file so our main file is cleaner.

reformat_uris() removes the http[s]:// from URLs found in twitter status updates. We need to do this because Tropo handily (though not in this case) tries to stream URLs as sound files. This is very helpful when you want to play a MP3 file over the phone, but if you provide it a regular HTML site, it’ll synthesize it into white-noise.

say_as_ordinal() returns a string of VoiceXML. Tropo is built upon Voxeo’s highly tested infrastructure which, until now, has been mostly used for power and good of VoiceXML. Thankfully to use, Tropo stays true to its roots and allows us to execute VoiceXML. It can do a lot of handy things like turn “1″ into “first”, saving us from coding that conversion ourself and making sure it scales and is in correct English grammar.

to_pretty() is similar to the DateTime helper time_ago_in_words() available to Rails users. I found this method addition for the Ruby Time class and like it. As you can see, it’s very easy to customize too.

Conclusion

As you can see, this is a fairly basic application of a voice technology but it could be vastly expanded to become exponentially more feature-rich. For example, we could ask the user for more input such as giving them a choice of which twitter username to query, much more input validation, and more.

Tropo is backed by Voxeo’s extreme support. Check out Tropo’s contact page for all the ways to get help with Tropo and making the web applications you already have, and the ones you are only just now dreaming up, come alive with voice and IM-technology.

Tweeter Phone uses Tropo to read you your latest tweets

Friday, March 20th, 2009

Here’s exactly the kind of rapid innovation we were hoping to see with Tropo. Last night, a developer attends the monthly meeting of the Orlando Ruby Users Group which we hosted and where we demonstrated Tropo. He goes home, spends a few hours working on a site and… ta da…

Tweeter Phone!

You login with your Twitter credentials (yes, you have to provide your Twitter username/password at least until Twitter finishes their OAuth implementation), enter in your phone number and then whenever you call the application phone number from you phone you will hear the latest Twitter updates from the people you follow. Works great!

Now, okay… sure, for those of us with smart phones with Twitter apps or mobile web clients it’s probably easier to check Twitter updates from those interfaces… but if you don’t have such a phone or you don’t have a great data connection, this provides another way to hear what people are posting.

More importantly, in our mind, was the fact that this developer could go from knowing nothing about Tropo to launching a new communication service in a few hours. We love his About page:

Tweeter Phone was built by Devoh.

The initial version was spent in a single 5-hour, late-night coding session.

The phone service is powered by Tropo, the new cloud-based telephony service from Voxeo.

Kudos to “Devoh” for bringing out this service – and we look forward to seeing what else he (and you all) do with Tropo.

Speaking of Twitter (Hmmm… which is actually what his service does :-), you can of course follow us on Twitter at twitter.com/voxeo


If you found this post interesting or helpful, please consider either subscribing via RSS or following us on Twitter.


Technorati Tags: , , ,