Plugin to interact with the Tropo Cloud platform
Dependency :
compile ":tropo-webapi-grails:0.2.1"
Summary
Installation
grails install tropo-webapi-grails
Description
Description
Tropo is an Open Source massively scalable developer platform that makes it simple to quickly and easily build phone, SMS and Instant Messaging applications - or applications that handle all three - using the web technologies you already know and Tropo's powerful cloud API.This plugin implements the Tropo WebApi which is a server-side API that allows developers to create with very few lines of code applications that can send and receive SMSs and calls, build instant messaging powered applications, build conferences, transfer calls, record conversations, play and send sound files to other people and many other cool things. Apart from the Tropo WebApi, this plugin also implements the Tropo REST API which is a provisioning API that lets you configure, launch and manage all your Tropo applications.To run your Tropo applications you will need a Tropo powered application server. Voxeo (Tropo authors) offers the Tropo Cloud platform which is totally free for developers and which has very competitive rates for production deployment.Requires
Grails 1.0.5 or higher.Includes
- commons-codec-1.3.jar
- ezmorph-1.0.6.jar
- http-builder-0.5.1.jar
- httpclient-4.0.3.jar
- httpcore-4.0.1.jar
- json-lib-2.4-jdk15.jar
- xml-resolver-1.2.jar
Installation
You can install this plugin by running the following command:grails install-plugin tropo-webapi-grails
Screencast
Be sure to check out this screencast on how to create your first project with Tropo and Grails: http://blog.tropo.com/2011/04/20/voice-powered-applications-with-grails-screencast5 minutes tutorial
In the following sections we will create a sample Grails application that uses Tropo services. It will take you less than 5 minutes.Creating your Tropo application
Once you have the plugin installed you will find that creating voice and SMS powered application becomes incredibly easy. But first of all you need to sign up with Tropo. Once you have registered and signed in you can proceed and create your first application. As we are creating an application that uses the WebApi, you need to click on the plus button and then choose WebApi application like in the picture below:
Tropo applications can be executed in very different ways. Whenever a tropo application is launched the Tropo Cloud platform will create a new session and will invoke an URL at your server. Tropo applications can be triggered in many different ways like for example:
- Calling a phone number that you have set up
- Calling a skype number
- Sending a SMS
- Sending a message through an IM network
- Using the REST API
- From Grails itself using the TropoService object (effectively it runs a GET request to the REST API)
Once the application is created you will find that there is already several phone numbers linked to it. There is already a Skype phone number and you can link your application with several IM networks or even your Twitter account. On that same screen you will find links to attach new US national and international phone numbers to your application. More than 40 countries are supported. A mesage or phone call to any of these phone numbers or services will trigger your Grails controller logic. In the screenshot below you will find that I have attached an US national number and an UK phone number to our sample application:
Creating your Controller
Once your application is created adding voice support to it is really simple. You do not need any specific artifacts so you can add voice and messaging support to any of your Grails controllers, services or Taglibs. However, you will need to provide some implementation for the Grails controller that you have linked to your application in the previous steps.The Tropo WebApi is based in JSON and defines many different methods. This plugin provides a groovy builder that makes really easy to interact with the Tropo platform. Below you can find an example for our TropoController.groovy:import com.tropo.grails.TropoBuilder;class TropoController { def index = { def tropo = new TropoBuilder() tropo.tropo { say("Hello World. Hello Tropo. We are going to play a song.") say("http://ccmixter.org/content/copperhead/copperhead_-_When_We_Do.mp3") hangup() } tropo.render(response) } }
import com.tropo.grails.TropoBuilder;class TropoController { def index = { def tropo = new TropoBuilder() tropo.say("Hello World. Hello Tropo. We are going to play a song.") tropo.say("http://ccmixter.org/content/copperhead/copperhead_-_When_We_Do.mp3") tropo.hangup() tropo.render(response) } }
Testing your application
If you have created some landline phone numbers you can test your application just by calling those phone numbers. For example, in this tutorial you could call either +44 1259340253 or (407) 680-0744 and you would hear a welcome message and a mp3 song would be played until either the song stops or you hang up. You can also use Skype to call your application:
And that is it. As you have seen, we have created a Grails application that answers a phone call and plays some song to the user with almost no effort. This is a very naive example but it shows how easy this plugin makes to add voice and messaging to your apps. Now it would be a nice moment to stop and learn more about the Tropo WebApi reference and all the possibilities that brings up to developers.In the sections below we would expand on this plugin and will provide samples about how to call all the different methods.Source Code
Although the plugin is available from Grails SVN, the latest source code for this plugin is hosted at GitHub. You can find it here: https://github.com/tropo/tropo-webapi-grailsUsing Tests as a reference
At the source code there is several unit tests that show how to use the Groovy builder. Check them out here.Configuration
This plugin does not require specific configuration parameters by default.If you plan to use the REST API then you should provide your Tropo username and password as most of the REST API methods are secured by default. In this case you need to add the following lines to your Config.groovy file providing your Tropo username and password:tropo.username = "yourtropousername" tropo.password = "yourtropopassword"
Interacting with Tropo REST API
Tropo REST API allows you to interact with Tropo platform through a REST based service. This API can be used to directly invoke your applications (for example you could provide a phone image link in your webapp GUI that will invoke your application and trigger the Controller that we saw in the 5 minutes tutorial). Another usage for this API is to administer your account. You can use this API to create new applications, create and delete phone numbers, create and delete IM accounts, send signals to your application (like hanging up), etc. In summary, it is an admin and provisioning API.Although you could send the REST requests yourself, this plugin provides a handy Grails service named TropoService that is injected in your applications and that you may use to execute all the different Tropo REST API calls.Here is an example on how simple would be to launch your Tropo application using the TropoService:class TropoController { def tropoService def index = { tropoService.launchSession(token:
'72979191d971e344b46a0e4a3485571844250e689bb13548a75f1cce2ce9a53dde82c3fe944479bcb650500e')
}IMPORTANT: Transcription issues
The interaction with Tropo is based on the interchange of JSON based documents. There is a known bug where Tropo sends transcription POST requests with a Content-Type header set to 'application/x-www-form-urlencoded' which basically will make the JSON coverter to fail with a message pretty similar to "org.codehaus.groovy.grails.web.json.JSONException: Missing value. at character 0 of "If you face this issue the workaround is to disable Grails' automcatic content handling and parse the POST body yourself with the JSON converter. This is actually pretty easy. Here is an example:import grails.converters.JSONclass TestController { static allowedMethods = [add:'POST'] def index = { } def transcription = { def raw = request.reader.text def json = JSON.parse(raw) render "ok" } }
Examples using the Tropo Builder
As you could see in the 5 minutes tutorial above, this plugin provides a groovy builder that makes really easy to interact with the Tropo platform. The following examples showcase some of the actions that you could do in your Grails applications.Saying something to the user
def builder = new TropoBuilder()
builder.tropo {
say('Hello Mr. User')
}Asking a question to the user
def builder = new TropoBuilder() builder.tropo { ask(name : 'foo', bargein: true, timeout: 30, required: true, choices: '[5 DIGITS]') { say('Please say your account number') } }
Asking a question and redirecting the user to a different controller
Simply asking a question wasn't really a very useful action. Lets ask a question and redirect the user to a new action in our controller:def builder = new TropoBuilder() builder.tropo { ask(name : 'foo', bargein: true, timeout: 30, required: true) { say('Please say your account number') choices(value: '[5 DIGITS]') } on(event:'continue',next:'/tropo/zipcode') }
Displaying the outcome of your application actions
In the example above we've seen how we can redirect to a different controller to handle the user's input like for example when entering a number or saying something. How can we get the user input from our controllers?It is really easy. Tropo will send a POST request to our controller containing the result of the action that we have executed. That includes any input from the user:def zipcode = { def tropoRequest = request.JSON
def zipcode = tropoRequest.result.actions.value def builder = new TropoBuilder()
builder.tropo {
say(value: "Your zipcode is ${zipcode}. Thank you.")
hangup()
} builder.render(response)
}Creating a conference
The conference action allows multiple lines in separate sessions to be conferenced together so that the parties on each line can talk to each other simultaneously.def builder = new TropoBuilder() builder.tropo { conference(name: 'foo', id: '1234', mute: false, send_tones: false, exit_tone: '#') { on(event: 'join') { say(value: 'Welcome to the conference') } on(event:'leave') { say(value: 'Someone has left the conference') } } }
Hanging up
As it name says, the hangup action will simply hang up the call.def builder = new TropoBuilder()
builder.hangup()Recording a call
The record action plays a prompt (audio file or text to speech) then optionally waits for a response from the caller and records it.def builder = new TropoBuilder()builder.record(name: 'foo', url: 'http://sendme.com/tropo', beep: true, sendTones: true, exitTone: '#') { transcription(id: 'bling', url:'mailto:jose@voxeo.com', emailFormat: 'encoded') say('Please say your account number') choices(value: '[5 DIGITS]') }
Redirecting a call
Redirect is used to deflect the call to a third party SIP address. This function must be called before the call is answered; for active calls, consider using transfer.def builder = new TropoBuilder()
builder.tropo {
redirect(to: 'sip:1234', from: '4155551212')
}Rejecting a call
This action rejects the incoming call. For example, an application could inspect the callerID variable to determine if the user is known, then reject the call accordingly.def builder = new TropoBuilder()
builder.reject()Starting a recording
Allows your plugin to begin recording the current session. The resulting recording may then be sent via FTP or an HTTP POST/Multipart Form.builder.tropo {
startRecording(url:'http://postrecording.com/tropo')
}Stopping some recording
This stops the recording of the current call after startCallRecording has been called.def builder = new TropoBuilder()
builder.stopRecording()Transfering a call
The transfer action will transfer an already answered call to another destination / phone numberbuilder.tropo {
transfer(to: 'tel:+14157044517') {
on(event: 'unbounded', next: '/error')
choices(value: '[5 DIGITS]')
}
}Calling someone
The call method initiates an outbound call or a text conversation. Note that this verb is only valid when there is no active WebAPI call.builder.call(to: 'foo', from: 'bar', network: 'SMS', channel: 'TEXT', timeout: 10, answerOnMedia: false) {
headers(foo: 'foo', bar: 'bar')
startRecording(url: 'http://foobar', method: 'POST', format: 'audio/mp3', username: 'jose', password: 'passwd')
}Sending messages
The message action creates a call, says something and then hangs up, all in one step. This is particularly useful for sending out a quick SMS or IM.def builder = new TropoBuilder()builder.message(to: 'foo', from: 'bar', network: 'SMS', channel: 'TEXT', timeout: 10, answerOnMedia: false) { headers(foo: 'foo', bar: 'bar') startRecording(url: 'http://foobar', method: 'POST', format: 'audio/mp3', username: 'jose', password: 'passwd') say('Please say your account number') }
Event handling
Some actions may respond to different events. You can specify the different events as parameters. Refer to the WebApi reference to get more information.def builder = new TropoBuilder()def help_stop_choices = '0(0,help,i do not know, agent, operator, assistance, representative, real person, human), 9(9,quit,stop,shut up)' def yes_no_choices = 'true(1,yes,sure,affirmative), false(2,no,no thank you,negative),' + help_stop_choices builder.ask(name: 'donate_to_id', bargein: true, timeout: 10, silenceTimeout: 10, attempts: 4) { say([[event: 'timeout', value: 'Sorry, I did not hear anything.'], [event: 'nomatch:1 nomatch:2 nomatch:3', value: "Sorry, that wasn't a valid answer. You can press or say 1 for 'yes', or 2 for 'no'."], [value: 'You chose organization foobar. Are you ready to donate to them? If you say no, I will tell you a little more about the organization.'], [event: 'nomatch:3', value: 'This is your last attempt.']]) choices(value: yes_no_choices) }
Embedding builders
Sometimes you may need to create a builder in one method then run some logic and append extra content to the builder. This plugin lets you append builders within other builders. Here is an example:def builder1 = new TropoBuilder() builder1.tropo { on(event:'continue',next:'/result.json') } def builder2 = new TropoBuilder() builder2.tropo { ask(name : 'foo', bargein: true, timeout: 30, required: true) { say('Please say your account number') choices(value: '[5 DIGITS]') } append(builder1) }
Author
Martín Pérez (mpermar@gmail.com)Please report any issues to the guys at Voxeo support. You can also use the Grails User mailing list and/or write up an issue in JIRA at http://jira.codehaus.org/browse/GRAILSPLUGINS under the Tropo-Webapi-Grails component.History
October 4, 2011- Fixed: public/private mixed warning from STS2.8.0M1 and grails snapshot
- Released version 0.2.1
- Added a new isEmpty method to TropoBuilder
- Bug fixed. Append could only be used at the end of a closure.
- Bug fixed. Recordings use a choices element not exit_tone attribute.
- Added support for the new interdigitTimeout element in WebApi
- Released version 0.2 of the plugin
- Added the possibility to embed builders within other builders
- released version 0.1.2
- Fixed TropoBuilder's toString
- released patched version 0.1.1
- released initial version 0.1