Why such an application? Well, I have a Python script that pings my web sites every half hour (a simple HTTP GET request to the site's entry point). But sometimes the latency is so high that the script believes the site is down. So I receive an SMS on my cell phone. If I'm outside the office or home, I cannot check whether there is a real problem or not. So I decided to write a very simple voice application I could call from anywhere.
The VUI (voice user interface) of the application is simple: it asks me for a site name and, on a valid choice, makes an HTTP request to ping the site again. If the response code is 200, it tells me that the site is up and running, otherwise it tells me the site is down. The application loops and asks me for a site name again. At any time, I can ask for help by saying "what are my options" or "help me", or I can quit by either hanging up or say "quit".
The main application
The main part of the application begins with the initialization of a number of global variables:
var sitesUrl = "http://schemeway.dyndns.org/voiceapps/sites.json";
var sites = JSON.parse(http_request(sitesUrl, 'GET'));
var grammar = buildGrammar(sites);
var helpOption = sites.length;
var quitOption = sites.length + 1;
var helpPrompt = buildHelpPrompt(sites);
The
sitesUrl variable holds the URL of a JSON document giving the names and URLs of the sites I can ping. The grammar variable holds a string representation of the grammar used for speech recognition (in a Tropo-specific syntax). Finally, helpPrompt holds a TTS (text-to-speech) string for the help message.Once the variables are initialized, the application answers the call and play a welcome message (calls to the Tropo API are in bold):
answer();
say("Hi! Welcome to the web site monitoring application.");
It then enters a loop that repeatedly asks for a site name and handles the outcome of the interaction:
var exit = false;
while (!exit) {
ask("Choose a site.", {
choices: grammar,
maxTime: 10,
onChoice: function(choice) {
if (choice.value == helpOption) {
say (helpPrompt);
return;
}
else if (choice.value == quitOption) {
say("Thanks for calling!");
exit = true;
return;
}
var site = new Site(sites[choice.value]);
say("The application " + site.name + " is ");
var responseCode = site.test().status;
if (responseCode == 200) {
say("up and running.");
}
else {
say("not reachable. The response code is " + responseCode + ".");
}
},
onBadChoice: function() {
say("Please try again.");
say(helpPrompt);
},
onTimeout: function() {
say(helpPrompt);
}
});
}
The
ask function takes as its second argument a set of options, like the grammar, various timeouts, and callback functions. For example, when a valid choice has been made, the onChoice property is called with a single argument, the selected choice. The onBadChoice callback is called when the caller says something that is not covered by the grammar (a "no match"), while the onTimeout callback is called when the caller says nothing (what we usually call a "no input").Some helper functions
The functions used to create the grammar and the help prompt are given here:
function buildGrammar(sites) {
var grammar = "";
var index = 0;
for(index in sites) {
var site = sites[index];
grammar += (", " + index + "(" + index + ", " + site.name + ")");
}
grammar += (", " + (++index) + "(what are my options, help, help me)");
grammar += (", " + (++index) + "(bye, quit)");
grammar = grammar.substring(1);
return grammar;
}
function buildHelpPrompt(sites) {
var prompt = "Your options are: ";
var index = 0;
for(index in sites) {
var site = sites[index];
prompt += (site.name + ", ");
}
prompt += "help me, or quit.";
return prompt;
}Finally, the
Site constructor function and the test property are given here:function Site(obj) {
this.name = obj.name;
this.url = obj.url;
}
Site.prototype.test = function () {
var connection = (new java.net.URL(this.url)).openConnection();
connection.setConnectTimeout(3000);
var responseCode = 500;
try {
responseCode = connection.getResponseCode();
connection.disconnect();
}
catch (e) {
}
return {status: responseCode};
}The whole source code can be accessed here. Feel free to comment, suggest enhancements, or even steal my code!
(2006-05-26 11AM EST) Update: My home internet access seems flacky today, so the above link may not work. Stay tuned, I am working on getting it back to normal.
(2006-05-26 1PM EST) Update: The link should be working now.



