I said to myself, “Hey self, Amit is right. That would be cool.”
Out With The Hold, In With The New York Times
Twilio’s Conference noun sports functionality that lets you customize the hold experience. The
In this post we’ll look into how to replace your humdrum hold music with today’s headlines from The New York Times, read to you by the soothing voice of Twilio’s new Alice text-to-speech voice.
Building The Conference Room
We’ll be using Python’s micro web framework Flask for this project. If you want to cut to the chase, the completed project with instructions on installation and configuration are available here. You can also give it a try before you code by calling (646) 430-9298.
Let’s hop to it! First, we need to create a Twilio Conference room with a Flask app. Simple enough.
# Import our dependencies from flask import Flask from twilio import twiml # Instantiate our Flask app app = Flask(__name__) # Create an endpoint '/conference' to serve as our # Voice Request URL @app.route('/conference', methods=['POST']) def voice(): # Use the Twilio Python Module's TwiML generator to # create a response response = twiml.Response() # Have that response dial into a conference room we call # "Today's Headlines" with response.dial() as dial: dial.conference("Today's Headlines") return str(response) # Add a little boilerplate to run our Flask app. if __name__ == '__main__': port = os.environ.get("PORT", 5000) if port == 5000: app.debug = True app.run(host='0.0.0.0', port=port)
If we set the Voice Request URL to the
/conference endpoint we created above – bliggity-blam – that caller is placed into a conference. Now we need to take that hold experience and customize it. To do this, we use the
waitUrl attribute of the Conference noun and connect it to a new endpoint called
from flask import Flask from twilio import twiml app = Flask(__name__) # Let's adjust this route to create a custom wait # experience for our conference call. @app.route('/conference', methods=['POST']) def voice(): response = twiml.Response() with response.dial() as dial: # Let's add a waitUrl attribute to this Conference # noun and point it to a new endpoint we will # create below. dial.conference("Today's Headlines", waitUrl="/wait") return str(response) # Here's a new TwiML endpoint we'll create to customize what # our callers hear when they wait to be connected to conference. @app.route('/wait', methods=['POST']) def wait(): response = twiml.Response() response.say("This is totally a custom waiting room experience.") return str(response) # Add a little boilerplate to run our Flask app. if __name__ == '__main__': port = os.environ.get("PORT", 5000) if port == 5000: app.debug = True app.run(host='0.0.0.0', port=port)
Nice! Our callers hear a custom message when they try to call into the Conference. Now we just need to get some more interesting information for them to hear – like today’s headlines from the New York Times.
Connecting to the New York Times API
Next we’ll slow down a bit and go step-by-step through how we get the latest headlines from the New York Times API and transform that data into something our callers can hear while they wait to be placed in conference.
Step two is to click the Get Keys button to register our application.
Boom! Now we can enter the information for our application which is step three. Fill out the name of the application, your web site and how you heard about the New York Times API. After this information is filled out we will scroll down.
As we are scrolling down, we see a multitude of rad APIs from the folks at NYT. The one we are looking for in step four is the one that will give us today’s most important stories – the Most Popular API. Select the checkbox for “Issue a new key for Most Popular API” and continue scrolling down to the bottom.
Step five, we agree to the New York Times Terms of Service and register our app.
And holy biscuits! We now have our API key. Copy this value – we will use it as we continue coding our informative new conference wait room.
Connecting Your Conference Room To The News
Excellent – now we’re ready to fill our conference wait room with all the news that is fit to print. To do this, we need to ask the New York Times API for the Most Popular articles for the day. For that we will use a favorite HTTP client of mine, Kenneth Reitz’s elegant Requests module.
Once we successfully retrieve with a Requests call, we’ll iterate through the results and pull out the NYT’s handy data field called
abstract. This field which will give us a string of that news story to read to our callers using the new Alice voice in a Say verb. Finally we’ll add a pause after each Say so our callers will know they are hearing another headline.
from flask import Flask from twilio import twiml # First, we'll add your favorite HTTP client and mine, Requests import requests app = Flask(__name__) # First, we give our app the key we retrieved from the # New York Times API developer dashboard app.config['NYTIMES_API_KEY'] = "key_we_registered" # Next, we'll define the API request we will use to # retrieve the most popular stories of the day to # read to our users. app.config['API_PATH'] = \ "http://api.nytimes.com/svc/mostpopular/v2/"\ "mostviewed/all-sections/1.json?api-key=" # Keep our conference room same as before. @app.route('/conference', methods=['POST']) def voice(): response = twiml.Response() with response.dial() as dial: dial.conference("Today's Headlines", waitUrl="/wait") return str(response) # Now we'll customize our wait room to read from the New York Times API. @app.route('/wait', methods=['POST']) def wait(): # Create a TwiML response, just like before. response = twiml.Response() # Make sure we set our API key in the app's config dictionary # up at the top of this snippet. if app.config['NYTIMES_API_KEY']: # With our API key set, we'll ask the NYT to give us the Most # Popular stories today. api_request = requests.get("%s%s" % (app.config['API_PATH'], app.config['NYTIMES_API_KEY'])) # If we get a successful response, we'll take a look at the # results. if api_request.status_code == 200: # Parse the data we received with the Requests' module's # handy built-in JSON parser. json_response = api_request.json() # If we have valid JSON, give the caller a quick introduction. if json_response: response.say("While you wait for your conference to connect," \ " here are today's headlines from the New York Times.", voice="alice") # Iterate through all the results to get the dictionary # for each news story. for result in json_response['results']: # Use the Alice voice with the Say verb to read the # summary of the story to the caller. response.say(result['abstract'], voice='alice') # Add a courteous pause so the caller knows Alice # has moved on to the next story. response.pause() else: # If we didn't get JSON, let the caller know. response.say("Unable to parse result from New York Times API.") else: # If we couldn't get a successful request, let the caller know. response.say("Unable to reach New York Times API.") response.say("Check your configuration and logs for the error.") else: # Finally, if we did not set the API key, also let the caller know # and point to some resources for help with fixing this # configuration error. response.say("Configuration error: You need to set your New York " \ "Times API Key environment variable. See the README for " \ "more information.") # Whatever result we have, turn it into TwiML and hand it over # to Twilio to process for the hold room. return str(response) if __name__ == '__main__': port = os.environ.get("PORT", 5000) if port == 5000: app.debug = True app.run(host='0.0.0.0', port=port)
News headlines are just the tip of the iceberg of what you can do with waitUrls. The same idea could be applied to callers entering a Twilio Queue. You could also reduce the stories down to sections of The New York Times that appeal more directly to your callers like Business or Technology. Or you can surprise and delight your users with something completely different.
To get a more fleshed out version of this project, check out this repo on Github with a full test suite and better error handling. Or you can try it out for yourself by calling (646) 430-9298.
The waitUrl attribute is one of my favorite features of the Twilio API and I can’t wait to see what creative hold experiences you create. Be sure to share them with me on Twitter @dN0t or by email at firstname.lastname@example.org