'****************************************************************************** ' This is the weather by ZIP code demo in which the user enters the ZIP code ' via the keypad and the HTTP GET transaction is performed asynchronously. '******************************************************************************/ Imports System Imports System.IO Imports System.Net Imports System.Xml Imports System.Text Imports System.Threading Imports System.Reflection Imports System.Collections Imports IVRForBeginners '****************************************************************************** ' All .Net IVR applications are implemented as classes derived from our base '******************************************************************************/ Public Class VbWeather Inherits NetClientCall Private Shared prompt As String Private Shared welcome As String Private Shared regrets As String Private Shared baseQuery As String Private Shared dirs As Hashtable Private Shared states As Hashtable Private zip As String Private weather As String Private iar As IAsyncResult Private req As HttpWebRequest Private resp As HttpWebResponse '****************************************************************************** ' We set some constant variables '******************************************************************************/ Public Shared Shadows Function Initialize() As Boolean prompt = "Please enter a five digit ZIP code or press the pound key to quit." welcome = "Welcome to the weather demonstration application." regrets = "We are sorry but we had trouble determining the weather for that ZIP code. " + _ "If the ZIP code is valid you can try again later." baseQuery = "http://weather.yahooapis.com/forecastrss?p=" BuildStatesTable() Initialize = True End Function '****************************************************************************** ' The required Answer() method always comprises the entire Voice User ' Interface for an IVR application. '******************************************************************************/ Public Sub Answer() Dim i, millis As Integer Dim syn As Synthesizer Dim buffer As StringBuilder Try ' If the caller hangs up we want an exception hangupThrows(True) ' Get an instance of the synthesizer and greet the caller ' We should check for a null object which indicates failure syn = getSynthesizer() syn.speak(welcome) syn.wait() Do While (True) ' Tell the caller what we want ( 5 keystrokes ) syn.speak(prompt) ' And wait for him to give it to us If Not inputWait(5, "#", 20000) Then Exit Do ' We have no patience if we got fewer If inputAvailable() < 5 Then Exit Do ' If we have the input tell the user to be patient syn.speak("Please wait") ' Build a ZIP code from the key presses buffer = New StringBuilder For i = 1 To 5 buffer.Append(getChar()) Next i zip = buffer.ToString() ' Make an asynchronous request of the web service RequestWeather() Do While Not iar.IsCompleted ' Amuse the user with "music on hold" while we wait millis = playMessage("strumming") ' Wait for the request to complete, the music to stop, ' or for the user to hang up. When the music stops we ' just play it again but we could count the iterations ' of the loop to enforce a time out. wait(iar.AsyncWaitHandle, millis) Loop ' When the request completes we fetch the data and build the forecast CompleteRequest() ' That done we kill the music, add a bit of silence ' and then deliver the forecast to the caller stopPlaying() wait(500) syn.speak(weather) ' While speaking we add an entry to the call detail records cdrStatusMessage(0, zip) cdrStatusMessage(1, weather) ' Then we wait for the forecast to be completely spoken syn.wait() Loop ' Be polite and say good bye before we hang up syn.speak("Good bye.") syn.wait() ' If the caller hangs up we will cancel the web service request Catch e1 As NetClientCallTermination CancelRequest() Console.WriteLine(e1.Message) ' Shouldn't happen, but you never know Catch e2 As System.Exception CancelRequest() Console.WriteLine(e2.Message) End Try End Sub '****************************************************************************** ' We use this hash table to translate state abbreviations we get from the web ' service to more easily pronounceable stuff. '******************************************************************************/ Private Shared Sub BuildStatesTable() states = New Hashtable states.Add("AL", "Alabama") states.Add("AK", "Alaska") states.Add("AZ", "Arizona") states.Add("AR", "Arkansas") states.Add("CA", "California") states.Add("CO", "Colorado") states.Add("CT", "Connecticut") states.Add("DE", "Delaware") states.Add("DC", "D C") states.Add("FL", "Florida") states.Add("GA", "Georgia") states.Add("HI", "Hawaii") states.Add("ID", "Idaho") states.Add("IL", "Illinois") states.Add("IN", "Indiana") states.Add("IA", "Iowa") states.Add("KS", "Kansas") states.Add("KY", "Kentucky") states.Add("LA", "Louisiana") states.Add("ME", "Maine") states.Add("MD", "Maryland") states.Add("MA", "Massachusetts") states.Add("MI", "Michigan") states.Add("MN", "Minnesota") states.Add("MS", "Mississippi") states.Add("MO", "Missouri") states.Add("MT", "Montana") states.Add("NE", "Nebraska") states.Add("NV", "Nevada") states.Add("NH", "New Hampshire") states.Add("NJ", "New Jersey") states.Add("NM", "New Mexico") states.Add("NY", "New York") states.Add("NC", "North Carolina") states.Add("ND", "North Dakota") states.Add("OH", "Ohio") states.Add("OK", "Oklahoma") states.Add("OR", "Oregon") states.Add("PA", "Pennsylvania") states.Add("PR", "Puerto Rico") states.Add("RI", "Rhode Island") states.Add("SC", "South Carolina") states.Add("SD", "South Dakota") states.Add("TN", "Tennessee") states.Add("TX", "Texas") states.Add("UT", "Utah") states.Add("VT", "Vermont") states.Add("VA", "Virginia") states.Add("WA", "Washington") states.Add("WV", "West Virginia") states.Add("WI", "Wisconsin") states.Add("WY", "Wyoming") End Sub '****************************************************************************** ' Pulls the city and state from the XML document '******************************************************************************/ Private Function GetCity(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String Dim city, temp1, temp2 As String Dim node As XmlNode ' The city and the state reside in the same node ... node = doc.SelectSingleNode("//channel/yweather:location", mgr) ' ... as distinct attributes temp1 = node.Attributes.GetNamedItem("city").InnerText temp2 = node.Attributes.GetNamedItem("region").InnerText ' Expand the state abbreviation if we know it ... If states.Contains(temp2) Then GetCity = temp1 + " " + states.Item(temp2) ' ... or take it as is if we don't Else GetCity = temp1 + " " + temp2 End If End Function '****************************************************************************** ' Pulls the temperature from the XML document '******************************************************************************/ Private Function GetTemperature(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String Dim temp As String Dim node As XmlNode node = doc.SelectSingleNode("//channel/item/yweather:condition", mgr) temp = node.Attributes.GetNamedItem("temp").InnerText GetTemperature = temp + " degrees Fahrenheit" End Function '****************************************************************************** ' Pulls the current condition from the XML document '******************************************************************************/ Private Function GetOutlook(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String Dim node As XmlNode node = doc.SelectSingleNode("//channel/item/yweather:condition", mgr) GetOutlook = node.Attributes.GetNamedItem("text").InnerText End Function '****************************************************************************** ' Pulls the wind speed and direction from the XML document '******************************************************************************/ Private Function GetWind(ByVal doc As XmlDocument, ByVal mgr As XmlNamespaceManager) As String Dim degrees As Integer Dim wind, temp1, temp2 As String Dim node As XmlNode node = doc.SelectSingleNode("//channel/yweather:wind", mgr) temp1 = node.Attributes.GetNamedItem("direction").InnerText temp2 = node.Attributes.GetNamedItem("speed").InnerText degrees = Val(temp1) GetWind = "from the " + Direction(degrees) + " at " + temp2 + " miles per hour" End Function Function Direction(ByVal num As Integer) As String If num <= 11 Then Direction = "north" ElseIf num <= 33 Then Direction = "north northeast" ElseIf num <= 56 Then Direction = "northeast" ElseIf num <= 78 Then Direction = "east northeast" ElseIf num <= 101 Then Direction = "east" ElseIf num <= 123 Then Direction = "east southeast" ElseIf num <= 146 Then Direction = "southeast" ElseIf num <= 168 Then Direction = "south southeast" ElseIf num <= 191 Then Direction = "south" ElseIf num <= 213 Then Direction = "south southwest" ElseIf num <= 236 Then Direction = "southwest" ElseIf num <= 258 Then Direction = "west southwest" ElseIf num <= 281 Then Direction = "west" ElseIf num <= 303 Then Direction = "west northwest" ElseIf num <= 326 Then Direction = "northwest" ElseIf num <= 348 Then Direction = "north northwest" Else Direction = "north" End If End Function '****************************************************************************** ' If the user hangs up we use this function to cancel the outstanding web ' service(request) '******************************************************************************/ Private Sub CancelRequest() Try If Not req Is Nothing Then req.Abort() CompleteRequest() End If Catch e As System.Exception Console.WriteLine(e.Message) End Try End Sub '****************************************************************************** ' When the asynchronous HTTP transation completes we fetch the results '******************************************************************************/ Private Sub CompleteRequest() Dim isOpen As Boolean Dim city, outlook, temp, wind As String Dim doc As XmlDocument Dim msg As StringBuilder Dim mgr As XmlNamespaceManager doc = Nothing resp = Nothing isOpen = False weather = Nothing Try resp = req.EndGetResponse(iar) isOpen = True ' The response from the web service is XML so load it into a document doc = New XmlDocument doc.Load(resp.GetResponseStream()) ' Get a namespace manager and add the namespace mgr = New XmlNamespaceManager(doc.NameTable) mgr.AddNamespace("yweather", "http://xml.weather.yahoo.com/ns/rss/1.0") ' Then pull the city, temperature, current condition and wind speed from it city = GetCity(doc, mgr) temp = GetTemperature(doc, mgr) outlook = GetOutlook(doc, mgr) wind = GetWind(doc, mgr) ' We take those bits of information to build a two sentence forecast msg = New StringBuilder msg.Append("In ") msg.Append(city) msg.Append(", the weather is ") msg.Append(outlook) msg.Append(" with a temperature of ") msg.Append(temp) msg.Append(". The winds are ") msg.Append(wind) msg.Append(".") weather = msg.ToString() Catch e As InvalidOperationException isOpen = False Catch e As System.Exception Console.WriteLine(e.Message) ' Make sure we close the connection when we are done Finally If (isOpen) Then resp.Close() End Try ' If we failed to build a forecast we'll offer an apology instead If weather = Nothing Then weather = regrets End Sub '****************************************************************************** ' Starts an asynchronous HTTP GET transaction '******************************************************************************/ Private Sub RequestWeather() req = WebRequest.Create(baseQuery + zip) iar = req.BeginGetResponse(Nothing, Nothing) End Sub End Class