Weather By Phone lets a caller check the weather by phone. Twilio answers the call, the application asks the caller for their US zip code, the application looks up the weather using a remote web service and reads the response to the caller.
This demo is written in Python for Google App Engine. This code can be adapted for other Python web frameworks such as Django.
1
When a call is answered by Twilio, Twilio fetches the '/' page which is configures to query the GatherPage object.
weatherbyphone.py
application = webapp.WSGIApplication([ \
('/', GatherPage),
('/weather', WeatherPage)],
debug=True)
2
The GatherPage object contains two methods get() and post() which map to the same function and return a page generated by the template gather.xml
weatherbyphone.py
def xml_response(handler, page, templatevalues=None):
path = os.path.join(os.path.dirname(__file__), page)
handler.response.headers["Content-Type"] = "text/xml"
handler.response.out.write(template.render(path, templatevalues))
class GatherPage(webapp.RequestHandler):
def get(self):
self.post()
def post(self):
templatevalues = {
'postprefix': BASE_URL,
}
xml_response(self, 'gather.xml', templatevalues)
Here is the template used to render the initial TwiML greeting.
gather.xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather method="POST" numDigits="5"
action="{{ postprefix }}weather">
<Play>greeting.wav</Play>
<Say>Please enter your 5 digit zip code to hear the
weather.</Say>
</Gather>
</Response>
Click on this link to view the TwiML XML response as Twilio does:
3
Twilio then plays the greeting sound file, reads the "Please enter..." text to the caller and waits for 5 digits from the caller. When 5 digits have been entered by the caller, Twilio performs an HTTP POST with the results back to the 'action' handler. In this case, the hander is configured to be 'http://weatherbyphone.appspot.com/weather' Control then passes to the /weather portion of the application and the WeatherPage object.
weatherbyphone.py
def post(self):
zipcode = self.request.get('Digits')
if not zipcode:
self._error("Invalid zip code.", BASE_URL)
return
zipcode = zipcode.replace('#', '').replace('*', '')[:5]
weatherxml = self._fetch(zipcode)
if not weatherxml:
self._error("Error fetching weather. Good Bye.")
return
try:
xml_response(self, 'weather.xml', self._parse(weatherxml))
except:
self._error("Error parsing weather. Good Bye.")
Because we have mapped the get() handler in the WeatherPage object we can simulate digits pressed by the caller directly in a web browser. Twilio passes back digits pressed using the 'Digits' parameter. Here is link simulating the situation if someone entered the zip code 02138:
4
The final response is generated by fetching the weather conditions in XML format from the remote weather web service, parsing the XML, and feeding the values back in to the weather.xml template.
weather.xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>It is currently {{ temp }} degrees fahrenheit and
{{ conditions }} in {{ location }}.</Say>
</Response>
And that's it! The rest of the code is input validation and error handling.