Posts Tagged ‘tropo samples’

Creating an internationalized Restaurants Finder application in minutes with Grails and Tropo

Wednesday, September 21st, 2011

Grails Logo In a previous blog post I already showed how we could pretty quickly create applications in Grails with the Tropo Grails Plugin and host them in Cloud Foundry with almost no effort. In this blog post I’m going to show a more ellaborated example. But, hey! In Tropo and Grails ellaborated examples don’t take more than 100 lines of code :)

One of the advantages of Tropo against its competitors is speech recognition and several internationalization features. Tropo understands 24 languages, so I thought that it would be pretty cool to create an application to find restaurants in a non-English speaking country as most of our blog posts are commonly focused on English. To implement my restaurants finder application I used the 11870 API service which is a Spanish web service that gives you the names, phone numbers and reviews of many services across any city in Spain (pretty cool).

So whats the idea around this restaurant finder? Basically imagine that you are on holidays with your wife in a city that you don’t know. And back in the hotel you made a reservation to a nice restaurant. Then you go through the day to visit the city and when it is time to go to the restaurant you realize that you don’t know where it is. Damn, you forgot the map! You don’t have any tourist service nearby, locals don’t know the restaurant location and you don’t have internet connection or an smartphone at hand. And of course your wife is starving! What do you do? Your marriage and peaceful holidays are at risk! Hopefully my restaurants finder could help you here :) Basically you dial a phone number, say the name of the city you are in, say the name of the restaurant and the application will tell you the phone number of the restaurant (it would be a one-liner change in the application to actually transfer your call to the restaurant’s phone).

But let’s look to some of the code. A first Grails action loads the name of all the cities with population bigger than 10.000. I’ve got a list from the Internet and just built a comma-separated list of cities. I use that list to create my choices statement that instructs Tropo to only accept one of those cities:


def index = {

    def citiesFile = applicationContext.getResource(
                            'classpath:cities.txt').file
    def citiesString = new String(IOUtils.toByteArray(
                            new FileInputStream(citiesFile)))
    def cities = citiesString.tokenize(',')

    def tropo = new TropoBuilder()
        tropo.tropo {
            say(value:"Bienvenido al servicio de información...", 
                  voice:"carmen")
            ask(name:'city',mode:"speech",
                         recognizer:"es-es", voice:"carmen") {
                say(value:"Diga el nombre de una ciudad")
                choices(value:citiesString)
            }
            on(event:'continue',next:'/tropo/restaurant')
        }
        tropo.render(response)	
}

That will prompt the user for the name of the city. Once the user says the name of a city a second Grails action will be triggered. In that action we call the 11870 API to fetch the list of available restaurants in that city. With that list I create another choices attribute that will instruct Tropo to only accept restaurants with a name in that list.

def restaurant = {

    def tropoRequest = request.JSON
    def place = tropoRequest.result.actions.value

    def map = restaurantMap(place)
    session["restaurants"] = map
    def restaurants = restaurantList(map)

    def tropo = new TropoBuilder()
    tropo.tropo {
        ask(name:'restaurante', mode:'speech', 
                      recognizer:'es-es', voice:'carmen') {
	    say(value:"Diganos el nombre de un restaurante en ${place}")
	    choices(value:restaurants)
	}
	on(event:'continue',next:'/tropo/info')
    }
    tropo.render(response)
}

In the code above, the method restaurantMap takes the name of the place and returns a map with all the restaurants in that place and their phone numbers. That map is stored in the HTTP session so later can be used to retrieve the phone number for the restaurant that the user has told. This is the actual code that invokes 11870 API and builds the map:

def restaurantMap(def place) {

    def query = "http://api.11870.com/api/v2/search?appToken=..."

    def feed = new XmlParser().parse(query)

    def restaurants = []
    def map=[:]
    feed.entry.each {
        restaurants << it.title.text()
        map.put(it.title.text(),it.'oos:telephone'.text())
    }
    map
}

Finally, once the user says the name of a restaurant, one last action will take the input and will use it to find the phone number of the restaurant:

def info = {

    def tropoRequest = request.JSON
    def restaurant = tropoRequest.result.actions.value

    def map = session["restaurants"]
    def phone = map.get(restaurant)

    def tropo = new TropoBuilder()
    tropo.tropo {
        say(value:"El teléfono de ${restaurant} es ${phone}...", 
              voice:"carmen")
        hangup()
    }
    tropo.render(response)		
}

That was pretty easy, wasn’t it? In very few lines of code we get a quite useful application. Provided you have an API to fetch restaurants from and a list of cities of a country then it would be fairly simple to add support for extra countries to this little example. There is many other things that could be done like handling nomatches and timeouts to make the application more reliable and user friendly, or dial the phone number of the restaurant and transfer the call. I hope you don’t mind if I leave these as exercises to the reader.

But wait, do you want to see the app in action? I’ve recorded a short video that goes through the source code and also shows the application working.

And that is it. I hope you liked it!

How to Add Phono and Tropo to CCXML

Thursday, November 4th, 2010

Dan York recently posted a really nice example of how to combine Phono – the new hotness in cloud telephony that turns any web browser into a phone – and good old fashioned enterprise-grade CCXML.

Dan’s piece inspired me to think about new ways to combine cloud telephony tools like Tropo and Phono with the bedrock of enterprise telephony – CCXML. Turns out, there are some very interesting and powerful combinations possible when you merge these different ways of building phone applications.

Connecting Phono to CCXML

Dan’s piece provided a nice summary of how use Phono as a web front-end to a CCXML application. The approach he describes is a great way to extend the reach of a CCXML application to a whole new group of users – those who are viewing your web site in a browser.

There is an equally exciting way of combining CCXML with the Tropo platform – using Tropo to send IM-based “screen pops” to service agents or others within your organization when a call comes in.

In the call center and customer service industries, screen pops are a way of providing notice to a customer service agent that a call is coming in. The actual content of a screen pop can range from simply displaying the caller ID of the incoming call, to actually pulling information out of a Customer Relationship Management (CRM) system about the caller to display it to the agent before they answer.

In this post, I want to show a simple example of how Tropo can be used to compliment CCXML applications to quickly and easily add screen pop capability. After we run through the details, I want to provide some additional insights on how both Phono and Tropo can be used to significantly enhance the power of enterprise CCXML.

A Simple Tropo Screen Pop Application

The first step in our example is to set up a very simple Tropo script that will send a screen pop message to the IM network and user of our choice. To keep things simple, we’ll use PHP for this example (but you could use any of the languages supported in Tropo Scripting to do this).

Here is the code for our example script:

<?php
message($screenPopMessage, 
	array( 'network'=>$network,'to'=>$sendTo));
?>

As you can see, this is a simple Tropo scripting application that does one thing – it sends an IM message to a user on a specified network. All of the variables in this script (everything preceded by a ‘$’) will be populated when we invoke it from CCXML.

For now, go to your Tropo account and set up a new Tropo Scripting application. Give your application a name, and when filling in the field “What URL powers your app?” select “Hosted File” and opt to create a new hosted file. Click “Create Application” when done.

Once this is done, you will notice your app has several different numbers automatically provisioned. Directly beneath that, you will see the Outbound Tokens provisioned for your new application. Make note of the token for messaging, as later you will need to copy it and paste it into your CCXML script.

The last step is to provision an instant messaging network for the application. This is the IM user that will send the screen pop when an incoming call is received in CCXML (i.e., the IM message will come appear to come from this user). To keep things simple, you can just set up a Jabber account using the tropo.im domain. If you do this, you can add the user you assign to your application to your buddy list in any Jabber client, including Google Chat.

Connecting Tropo to CCXML

In order to invoke our simple Tropo screen pop application, we need to make some changes to the CCXML introduced in the Dan’s post. Our revised CCXML looks like this:

<?xml version="1.0" encoding="UTF-8"?> 
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="inboundID"/> 
  <var name="outboundID"/> 
  <var name="initState" expr="'state1'" />

  <!-- Tropo API information -->
  <var name="tropoAPIToken" expr="'your-tropo-session-token'"/>
  <var name="tropoAPIEndPoint" expr="'http://api-internal.tropo.com/1.0/sessions?action=create&amp;token=' + tropoAPIToken"/>
  <var name="TropoSendURL"/>

  <var name="screenPopMessage" expr="encodeURIComponent('This is a screen pop!')"/>
  <!-- Any valid IM network name such as AIM, GTALK, MSN, JABBER, TWITTER and YAHOO. -->
  <var name="network" expr="'JABBER'"/>
  <var name="sendTo" expr="'your-im-user-name'"/>

  <eventprocessor statevariable="initState">

    <transition state="state1" event="connection.alerting"> 
      <log expr="'*** INBOUND CONNECTION ALERTING ***'"/> 
      <assign name="inboundID" expr="event$.connectionid" /> 
      <assign name="initState" expr="'state2'"/> 
      <assign name="TropoSendURL" expr="tropoAPIEndPoint + '&amp;screenPopMessage=' + screenPopMessage"/>
      <assign name="TropoSendURL" expr="TropoSendURL + '&amp;network=' + network"/>
      <assign name="TropoSendURL" expr="TropoSendURL + '&amp;sendTo=' + sendTo"/>
      <send name="'http.get'" target="TropoSendURL" targettype="'basichttp'"/>
    </transition>

    <transition event="send.successful" state="state2">
      <log expr="'*** Screen pop information sent to Tropo. Accept inbound call. ***'"/>   
      <assign name="initState" expr="'state3'"/>
      <accept connectionid="inboundID" /> 
    </transition>

    <transition event="error.send.failed" state="state2">
      <log expr="'*** ERROR: Could not send screen pop information. ***'"/>
      <assign name="initState" expr="'state3'"/>
      <accept connectionid="inboundID" /> 
    </transition>

    <transition state="state3" event="connection.connected"> 
      <assign name="initState" expr="'state4'"/> 
      <!-- 4079651112 is Voxeo Sales line -->
      <createcall dest="'tel:4079651112'" callerid="'1112223333'" connectionid="outboundID" timeout="'30s'"/> 
    </transition> 

    <transition state="state4" event="connection.progressing"> 
      <log expr="'*** CONNECTION.PROGRESSING ***'"/>  
      <assign name="initState" expr="'state5'"/> 
    </transition> 

    <transition state="state5" event="connection.connected"> 
      <assign name="initState" expr="'state6'"/> 
      <log expr="'*** CONNECTION.CONNECTED: INBOUND ***'"/>  
      <join id1="inboundID" id2="outboundID" duplex="'full'"/> 
    </transition> 

    <transition state="state6" event="conference.joined"> 
      <log expr="'*** CONFERENCE JOINED: INBOUND LEG TO OUTBOUND LEG ***'"/>  
    </transition> 

    <transition event="connection.disconnected"> 
      <log expr="'*** CONNECTION.DISCONNECTED ***'"/>  
      <exit/> 
    </transition> 

    <transition event="error.*">
      <log expr="'*** AN ERROR HAS OCCURED: ' + event$.reason  + ' ***'"/>
      <exit/>
    </transition>

  </eventprocessor> 
</ccxml>

You’ll notice a few differences from the CCXML example used in Dan’s piece. It includes a variable to hold a special URL to the Tropo Session API. There are also variables to hold the IM network and user that you want to send a screen pop to when a call comes into CCXML. Make sure you enter the details of the network on line 15 and the username on line 16 to whom you want to send the screen pop. Also, enter the messaging token from your Tropo application on line 9 in this CCXML script.

Set this script up in your Evolution account (as described in Dan’s post). Now, when a call comes into CCXML from Phono, the user that you have designated gets a screen pop with the value of the screenPopMessage variable in the CCXML script.

You can obviously change this message to anything you want, but it must be URL encoded before sending to the Tropo Session API.

<var name="screenPopMessage" 
       expr="encodeURIComponent('This is a screen pop!')"/>

Icing on the Cake!

Although this example is admittedly simple, coupled with Dan’s previous piece on connecting Phono to CCXML, it demonstrates how easy it is to augment CCXML with both Phono and Tropo. But that’s not all, it gets even better…

Phono allows developers to append information to a phone call, via the Phone API. in other words, before making a Phono call from your web page, you could add information to it via a header collection – this information could be obtained from prompting the user and getting input that way, or by examining what they did on a page (i.e., clicking a link). When the call connects to CCXML, this information is then available in standard SIP headers.

What this would let you do is to add inforamtion to a Phono call via client-side jQuery, and then use that inforamtion to craft the screen pop message in CCXML. This would allow you to customize the screen pop message to a customer service agent for each caller. You could even route calls differently based on the iformation added via Phono headers.

In addition, Tropo allows for the easy creation of IM applications (and other types of communication applications). In our simple example, we are just sending a message one way, from a CCXML script to a customer service agent through an instant message. But Tropo allows developers to build interactive IM applications. So with a little more work, we could create a screen pop application that asks an agent if they want to take an incoming call, send the call to another agent, or even send it to voice mail.

Because it’s possible to inject events into a running CCXML session, a Tropo application could control how a call is routed in CCXML based on an interaction with an agent. In this way it is possible to create more sophisticated, interactive screen pops using Tropo.

Hopefully, this post and the one it was inspired by give you a sense of how Phono and Tropo can compliment standards-based CCXML.

When it comes to getting more out of your CCXML applications, Phono and Tropo are the icing on the cake!

Tutorial: Using Loops & Functions in your Tropo applications

Monday, April 26th, 2010

Hello,

This afternoon I got a ticket over here at the Fortress of Solitude, err I mean Voxeo Support that I thought I would share with the class. This developer opened a ticket asking how to implement control document execution in his applications. In short he was looking to prompt his callers, in a loop, and stay in said loop until the user explicitly opts to hung up, or proceed with the call. This developer was stuck on whether or not he would have to explicitly repeat the ask() with each iteration.

The developer was also wondering how he could alternate prompts in hopes of giving his application a more natural feel for his users. I could immediately see how implementing this could be confusing for some of our developers, and also how this could be helpful on any channel not just Voice (SMS, Instant Message, Twitter,ect). This being the case I thought this most certainly warranted a blog posting to shared the example with our developers in hopes it may prove helpful; And now for our feature presentation….

<?php
answer();

//Global options for ask, declare once, use many =)
$globalOptions = array(
  "choices"     => "one( 1, one), two( 2, two),exit(3,exit)", 
  "repeat"      => 3,
  "onBadChoice" => create_function( '$event', 'say("I am sorry, I did not understand what you said.");' ),
  "onTimeout"   => create_function( '$event', 'say("I am sorry.  I didn\'t hear anything.");' )
	);

$promptArray = array(
    "Hello, to select the first option please press or say one, for the second option please press or say two.  To exit the application please say exit or just press 3",
    "For option one press or say one, or for option two press or say two"
   );
$promptIteratorVariable = 0;
/**
 * whichPromptToSay
 *
 * @return Array element of  our array ($promptArray) depending on count of $promptIteratorVariable
 */

function whichPromptToSay(){
	global $promptArray;
	if ($promptIteratorVariable==0){
		$promptIteratorVariable++;
		return $promptArray[0];
	}else{
		return $promptArray[1];
	}
}

/**
 * whichVerbToSay()
 *
 * @return element of our verb array
 */
$verbArray = array("selected","chose","picked","opted for");

function whichVerbToSay(){
	return $verbArray[rand(0,(count($verbArray)-1))];
}

//We will continue our loop until false, but first we need to set to true
$continueLoop = true;

while($continueLoop){
$result = ask(whichPromptToSay(),$globalOptions);
	if($result->name=='choice'){
		if ($result->value=="one"){
			say( "You " . whichVerbToSay() . " option one");
		}elseif ($result->value=="two"){
			say( "You " . whichVerbToSay() . " option two");
		}elseif ($result->value=="exit"){
			say( "peace out");
				$continueLoop = false;
		}
	}
}
hangup();
?>

The thing to take home from this example is that you can make use of functions to add a bit of flair to your applications, as well as loops to control application execution. These two techniques will hopefully not only help you have cleaner code, but also help you provide a better, more natural, user experience for your callers! If there are any questions on anything presented here please don’t hesitate to let us know, as our team is most certainly standing by to offer any help our developers may require!

Regards,

John Dyer
Customer Engineer
Voxeo Support

Tropo Samples Updated with New Call & Transfer Requirement

Tuesday, September 22nd, 2009

Recently we did an upgrade to the Tropo platform that included the requirement to include a ‘+’ whenever making an outbound call from the Tropo cloud. This means that when dialing a number, even in the US, you must now format as ‘+14155551212′ when using the call or transfer method.

For this we have updated all of the Tropo Samples at Github. You may see the changes themselves here.