Tag Archives: vxml

Analog remix part 2: Tropo

Tropo, a project of Voxeo Corporation, adds voice and short-message-communications functions to PHP, JavaScript, Ruby, Python, and Groovy through a set of classes. They host the platform and your scripts, which are then triggered through the web or by PSTN/SIP, SMS, Twitter, or chat events. You can use the system as long as you want for free for development, or pay reasonable hosting rates to go production. Oh, and their support team is terrific, even for developers who haven’t yet paid them a dime.
So what can you do with it?
  • Interactive voice-response (IVR) systems
  • Chat or Twitter robot
  • Dialer
  • Call routing based on logic in the script
  • Text-to-speech
  • Speech-to-text
  • Audio recording
  • Whatever you can normally do with the aforementioned scripting languages, but note that the stdout will be a communications outlet such as a SIP channel, a Twitter feed or conversation, a chat, or an SMS–not a web page
My first Tropo application, the “local operator,” is the topic of part two of the analog remix. The platform has so many features, though, that I look forward to spending more time developing in it.
I chose to use PHP because it’s the language with which I am most familiar and also has Curl libraries built in, which I am using to make a CGI callback to my own Asterisk server. The purpose should become clear when you read the code, but you may ask, “why not just send a SIP REFER to do the call transfer?” The answer is that you can’t with the Tropo platform. All transfers go through Tropo; there’s no option for a REFER. So if you want to use your own dialplan and not have your call going out to Tropo and back to use local resources, you have to do the transfer through the Asterisk Manager, thus the CGI call.
Number, Please?
The script answers the call (which will come in by SIP URI), asks, “Number, please?”, validates and confirms the spoken number with the caller, then uses the Curl functions to go back to the calling Asterisk server (running a web server) and transfer the call.
Getting There From Asterisk
Once you’ve written a script and uploaded it to  Tropo (or typed it directly into Tropo’s file-editing interface), you go to the Applications section and choose your script as the source. Once you’ve submitted it, you’ll be given a SIP URI (amongst other methods) by which to access the application. So as part of a FreePBX Custom Extension or in your extensions.conf file, you can make an extension for your new application by referencing SIP/somenumber@sip.tropo.com. I set mine up as extension 1100, which I mentioned in the previous post.
Now, if I go off hook from one of my internal extensions and dial 1100, or simply pick up the analog extension on which I configured the Warm Line/Hot Line, I’ve got the operator ready to take my number and transfer my call. Only one piece left after this: the Asterisk Manager CGI script.

Appendix: the PHP code (Number, Please?)

<?php

// Main

// SSML tags needed for advanced text-to-speech manipulation
$ssml_start = "<?xml version='1.0'?><speak>";
$sayas_end="</say-as>";
$sayas_digits ="<say-as interpret-as='vxml:digits'>";
$ssml_end = "</speak>";

answer();

// These IDs are used to identify the caller and the Asterisk box
$callid = $currentCall->getHeader('x-sbc-call-id');
$hostid = trim((strstr($callid, '@')),'@');

_log("Call-ID is $callid");
_log("Host is $hostid");

wait(1000);

$haveNumber = false;

while (!$haveNumber) {
$number = ask($ssml_start . "Number <emphasis level='reduced'>please?</emphasis>" .
$ssml_end,
array(voice => "allison",
attempts => 3,
bargein => false,
choices => "[4-16 DIGITS]",
minConfidence => 0.4,
timeout => 8,
onBadChoice => "handlerBadChoice",
onError => "handlerError",
onHangup => "handlerHangup",
onTimeout => "handlerTimeout"
));

// Confirm the number with the caller
// 1 and 0 are options for testing with DTMF
$yesno = ask($ssml_start . $sayas_digits . $number->value . $sayas_end .
", is this correct?" . $ssml_end,
array(voice => "allison",
attempts => 2,
bargein => false,
choices => "yes, no, 1, 0",
minConfidence => 0.5,
timeout => 3,
onBadChoice => "handlerBadChoice",
onError => "handlerError",
onHangup => "handlerHangup",
onTimeout => "handlerTimeout"
));

_log("Caller said $yesno->value.");

if ($yesno->value == "yes" || $yesno->value == "1") {
$haveNumber = true;
} else {
say("OK, let's try again.");
}
}

// The CGI callback. Refer to a future posting for the design
// Customize for your server. Use SSL for security if you can.
$url = "http://$hostid/tropo.cgi?f=xfer&callid=" . urlencode($callid) .
"&dest=" . $number->value;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, "USERNAME:PASSWORD");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

_log("Received from CGI: " . $result = curl_exec($ch));

if (!$result) {
_log(curl_error($ch));
}
curl_close($ch);

// If the transfer didn't happen, the caller is still here...

if ($currentCall->isActive()) {
_log("Transfer failed and caller still on the line.");
say("I'm sorry, something went wrong. Please try again later.");
hangup();
}

return;
// END OF MAIN

// Function definitions

function handlerBadChoice($event) {
say("I'm sorry, I didn't understand you.");
if($event->attempt >= 3) {
say("Goodbye.");
hangup();
}
return;
}

function handlerError($event) {
_log("Error occurred: $event->value");
say("I'm sorry, an error occurred. Goodbye");
hangup();
return;
}

function handlerHangup($event) {
_log("Caller hung up.");
exit;
}

function handlerTimeout($event) {
say("I'm sorry, I didn't hear you.");
if($event->attempt >= 3) {
say("Goodbye.");
hangup();
}
return;
}

?>

Analog remix part 1: a VoIP manual service exchange

Recently, I decided to revisit the analog side of my home Asterisk VoIP system, specifically, the Northern Electric 302 that I keep hooked up and on display. It’s a conversation piece, for when folks spot the rotary-dial phone, they ask, “Does that thing work?” At that point I explain that I can answer calls on it, but because of the rotary dial (and because I haven’t fashioned or purchased a rotary-to-DTMF converter) I can’t place a call from it.

Until now. With a little tweaking of the analog adapter, a fresh rediscovery of VoiceXML through the Tropo application platform, and some straightforward CGI programming, I have a robotic operator ready to connect me to whatever number I tell her. No dialing necessary!
Speech recognition is not new technology, but I think that using it here is a clever way to get around the limitations of my old technology (the rotary dial) by using the computerized equivalent of even-older “technology” (the human operator). So this discussion on implementing a manual service exchange in VoIP is dedicated to Almon Strowger, inventor of the automatic telephone switch.
There are three components to this:
  • configuring the analog adapter as an automatic ringdown (hot-line) or warm-line
  • programming the speech-to-text “operator” service on Tropo
  • programming the call-back CGI on a web server that can connect to the Asterisk Manager and perform call-handling functions
listed in order of difficulty.
Part 1: the automatic ringdown
Most VoIP-analog adapters (and probably most IP phones for that matter) have the ability to act as a hot-line, which rings a set destination when the phone goes off-hook, or a warm-line, which does the same after a timeout period (for example, six seconds without any digits entered). If you’ve got your vintage analog gear on a dedicated ATA, go for the hot-line, because you’ll never actually be “dialing” anything. As for me, I’ve got my 302 bridged with a Panasonic cordless system, so I need to maintain the ability to dial. But after six seconds of no dialing, the ATA assumes I want the operator and connects the call appropriately.
The hot-line/warm-line is configured in the endpoint’s dial plan. Refer to your device’s manual. The common Cisco/Linksys/Sipura SPA adapters all seem to have the same dial plan syntax, documented here. Here are the key items from that document:

The following implements a Hot Line phone, which automatically calls 1 212 5551234.

The following provides a Warm Line to a local office operator (1000) after five seconds, unless a four-digit extension is dialed by the user.

In my case, my “operator” extension is going to be 1100, so part of my dial plan will contain | P6 <:1100> |, which tells the ATA to automatically connect the call to extension 1100 after six seconds of no input.
Now that the need to dial has been eliminated, we’ll need something useful at extension 1100, which will be the Tropo-powered speech synthesis and recognition system, discussed in a future post.

Make your own TellMe for free

First of all, if you’re not familiar with TellMe, pick up a phone and dial 1-800-555-8355 and interact with it a little.

TellMe Studio, a free, online development environment for creating VoiceXML applications, allows you to create your own TellMe-like application. It’s been around for a while but this is the first I’ve heard of it.

With TellMe you can make all kinds of voice-activated applications like IVR menu systems, information systems and voice dialers. They host the VoiceXML interpreter and provide SIP access with up to four simultaneous calls. That’s not bad. All you have to do to go live with a voice app is provide a means to gateway calls over SIP to their system. With Asterisk it’s a piece of cake.

It has a fairly rich feature set with Javascript available as a means for more advanced logic than what the VXML can do natively. I just started playing with it recently and quickly got a simple menu system running. Try it out, and leave a comment if you write any interesting code.