Skip to content

Commit

Permalink
Major updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kyleconroy committed Oct 12, 2012
1 parent f271dd5 commit 9ecac9a
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 44 deletions.
2 changes: 1 addition & 1 deletion app.yaml
Expand Up @@ -6,4 +6,4 @@ threadsafe: true

handlers:
- url: /.*
script: main_other.app
script: callcenter.app
171 changes: 171 additions & 0 deletions callcenter.py
@@ -0,0 +1,171 @@
import webapp2
import util
from twilio import twiml
from twilio import rest

NUMBER = ""

# Change this to a sub account
ACCOUNT_SID = ""
AUTH_TOKEN = ""
APP_SID = ""

client = rest.TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN)

class BossHandler(webapp2.RequestHandler):

def get(self):
"""Enqueue a caller into the entry queue"""
self.response.headers['Content-Type'] = 'application/xml'

resp = twiml.Response()
d = resp.dial()
d.conference("boss")

self.response.write(str(resp))


class WaitHandler(webapp2.RequestHandler):

def get(self):
"""Tell a caller information about how long they have waited"""
self.response.headers['Content-Type'] = 'application/xml'

resp = twiml.Response()

messages = [
"You are number %s in line." % self.request.get('QueuePosition'),
"You've been in line for %s seconds." % self.request.get('QueueTime'),
"Average wait time is %s seconds." % self.request.get('AverageQueueTime'),
]

for message in messages:
resp.say(message)

resp.play("http://com.twilio.music.rock.s3.amazonaws.com/nickleus_-_"
"original_guitar_song_200907251723.mp3")

self.response.out.write(str(resp))


class EnqueueHandler(webapp2.RequestHandler):

def get(self):
"""Enqueue a caller into the entry queue"""
self.response.headers['Content-Type'] = 'application/xml'

resp = twiml.Response()
resp.say("You are being enqueued now.")
resp.enqueue(self.request.params.get('queue', 'support'),
waitUrl='/twiml/wait', waitMethod='GET')
resp.sms("Thanks for calling into today. How was your call?")
self.response.write(str(resp))


class DequeueHandler(webapp2.RequestHandler):

def get(self):
"""Enqueue a caller into the entry queue"""
self.response.headers['Content-Type'] = 'application/xml'

resp = twiml.Response()
resp.say("Looking for a caller")
d = resp.dial(record=True)
d.queue(self.request.params.get('queue', 'support'),
url="/twiml/record-consent")
resp.pause(length=10)
resp.redirect()
self.response.write(str(resp))


class ConsentHandler(webapp2.RequestHandler):

def get(self):
"""Inform the caller that the call may be recorded"""
self.response.headers['Content-Type'] = 'application/xml'

resp = twiml.Response()
resp.say("This call may be recorded for quality purposes")

self.response.write(str(resp))


class MenuHandler(webapp2.RequestHandler):

def post(self):
choice = self.request.params('Digits')

if choice == '1':
self.redirect('/twiml/enqueue?queue=support')
elif choice == '2':
self.redirect('/twiml/enqueue?queue=sales')
elif choice == '3':
self.redirect('/twiml/enqueue?queue=marketing')
else:
self.redirect('/twiml/menu')

def get(self):
"""Let the caller choose what department to queue into"""
self.response.headers['Content-Type'] = 'application/xml'

resp = twiml.Response()
gather = resp.gather()
gather.say("For support, press 1")
gather.say("For sales, press 2")
gather.say("For marketing, press 3")
resp.redirect()

self.response.write(str(resp))


class SupportHandler(webapp2.RequestHandler):

def get(self):
params = {
"token": util.generate_token(ACCOUNT_SID, AUTH_TOKEN, APP_SID)
}
self.response.out.write(render_template("index.html", params))


class CallHandler(webapp2.RequestHandler):

def get(self):
"""Show a list of all the current calls"""
params = {
"calls": client.calls.iter(status="in-progress"),
}

self.response.out.write(render_template("calls.html", params))

def post(self):
sid = self.request.params.get('sid')

if sid == '':
return

call = client.calls.get(sid)
call.redirect('/twiml/boss')


class FeedbackHandler(webapp2.RequestHandler):

def get(self):
"""Show a list of all the text messages to this number"""
params = {
"messages": client.sms.messages.iter(),
}

self.response.out.write(render_template("messages.html", params))


app = webapp2.WSGIApplication([
('/twiml/boss', BossHandler),
('/twiml/wait', WaitHandler),
('/twiml/menu', MenuHandler),
('/twiml/dequeue', DequeueHandler),
('/twiml/enqueue', EnqueueHandler),
('/twiml/record-consent', ConsentHandler),
('/web/support', SupportHandler),
('/web/calls', SupportHandler),
('/web/feedback', FeedbackHandler),
], debug=True)
3 changes: 3 additions & 0 deletions docs/callcenter.rst
Expand Up @@ -8,3 +8,6 @@ Bridging and Fair Queuing

Build a UI to manage queues
---------------------------

Escalting Calls
---------------
101 changes: 62 additions & 39 deletions docs/callin.rst
Expand Up @@ -3,8 +3,20 @@
Radio Call In
=============

In this workshop we'll be designing a radio call in application
using Twilio's new <Queue> functionality.
In this workshop we'll be designing a radio call in application using Twilio's
<Queue> functionality. While we'll be using a radio show as our target, this
style of queue managment can be used for any phone number where many people may
call at the same time.

Prerequisties
-------------

The next sections assume a working knowledge of Twilio. You should be familiar
with TwiML, configuring Twilio phone numbers, and Twilio application model.

Also, we're assuming you're comfortable writing web applications. For
reference, we'll be developing the application along the way using Python
and Google App Engine.

Using the Twilio Helper Libraries
---------------------------------
Expand All @@ -20,13 +32,14 @@ Reference`_ helpful for this workshop.
.. _here: http://www.twilio.com/docs/libraries
.. _Queue API Reference: https://twilio-python.readthedocs.org/en/latest/api/rest/resources.html#queues

Using Queue (TwiML)
-------------------
Using <Queue>
-------------

We'll need two Twilio phone numbers to work with Queue - one for the DJ to
dequeue calls from, and one for the queue that the listener will call into.
dequeue calls from, and one for the listener to call into.

First, we'll enqueue some calls via TwiML. In the example below, we enqueue
to a queue named ``radio-callin-queue``. Note that queues are created on
First, we'll enqueue calls via TwiML. In the example below, we enqueue calls
into a queue named ``radio-callin-queue``. Note that queues are created on
<Enqueue> if they do not already exist.

.. code-block:: xml
Expand All @@ -48,8 +61,9 @@ We can spice it up by adding some wait music, using the ``waitUrl`` parameter.
<Enqueue waitUrl="/wait-loop">radio-callin-queue</Enqueue>
</Response>
The ``/wait-loop`` endpoint goes to some TwiML that plays music. The ``waitUrl``
TwiML document supports a `subset of TwiML verbs`_.
Twilio will request the ``/wait-loop`` and process the TwiML that plays music.
The ``waitUrl`` TwiML document only supports a `subset of TwiML verbs`_, which
includes ``<Say>`` and ``<Play>``.

.. code-block:: xml
Expand All @@ -60,10 +74,9 @@ TwiML document supports a `subset of TwiML verbs`_.
</Response>
For the DJ dequeuing number, we use some TwiML that bridges the current call
to the queue. Note that <Dial>ing into a queue represents dequeuing a caller
on the queue, while the only way to get onto a queue is to be <Enqueue>d.
For the DJ dequeuing number, we use TwiML that bridges the current call to the
queue. Note that <Dial>ing into a queue dequeues the front on the queue, while
the only way to get onto a queue is by using the <Enqueue> verb.

.. code-block:: xml
Expand All @@ -81,16 +94,29 @@ to the first member on the queue.

Dynamic Queue Information
-------------------------

Twilio's Queue exposes dynamic inforrmation about the queue state that
you can use to build rich applications. In this section, we'll move past
static TwiML applications and start using the data Queue gives you to
create dynamic TwiML through a web application.

We'll start by working on our hold music. Wouldn't it be cool if we could
tell users where they were in the queue, how long they've been there, or
even the average wait time for their queue? Twilio exposes `all these
parameters`_ when invoking your application's waiting logic via HTTP so that
you can pass it along in your dynamic TwiML!
We'll start by working on our hold music. Wouldn't it be cool if we could tell
users where they were in the queue, how long they've been there, or even the
average wait time for their queue? Twilio sends these parameters via POST data
when invoking your application's waiting logic via HTTP.

================ ===========
Parameter Description
================ ===========
QueuePosition The current queue position for the enqueued call.
QueueSid The SID of the Queue that the caller is in.
QueueTime The time in seconds that the caller has been in the queue.
AverageQueueTime An average of how long time the current enqueued callers has been in the queue.
CurrentQueueSize The current number of enqueued calls in this queue.
================ ===========

Utilizing this information, we can inform our users what position they are in
the queue and how long they can expect to wait before an answer.

.. code-block:: python
Expand Down Expand Up @@ -123,10 +149,9 @@ through the ``action`` parameter when enqueuing.
# save to db, ping analytics, whatever you want!
.. _all these parameters: http://www.twilio.com/docs/api/twiml/enqueue#attributes-waiturl-parameters
Handling Long Queue Times
-------------------------

Queue Times Are Too Long! - A Call to Action
--------------------------------------------
We can use the ``action`` parameter to collect all sorts of useful metrics
on the backend, or even issue hasty apologies for long queue wait times.

Expand All @@ -135,11 +160,8 @@ let our users know we care. Using the `action URL parameters`_, we can
send an SMS apology if the wait time exceeded 30 seconds, or if their
call was rejected from a full queue.

You may find the `helper library documentation`_ for your `language of choice`_
helpful in sending SMS.

Here is some stub code that may help, if you are taking the Python / Google
App Engine route...
App Engine route.

.. code-block:: xml
Expand All @@ -165,17 +187,18 @@ App Engine route...
.. _language of choice: http://www.twilio.com/docs/libraries


See You Next Time - Closing Out the Queue
-----------------------------------------
Unfortunately, all good things must come to an end. It's time for our
radio show to close down until next time - but what about the people
still on the waiting queue?
Closing Out the Queue
---------------------

Unfortunately, all good things must come to an end. It's time for our radio
show to close down until next time - but what about the people still on the
waiting queue?

We can use `Queue`_ and `Member`_ REST API resources to programmatically
look at all of our account's queues and active members on those queues.
We can use `Queue`_ and `Member`_ REST API resources to programmatically look
at all of our account's queues and active members on those queues.

Let's write a quick script that will find our queue, loop through its
members, and dequeue each of them with a thank you message.
Let's write a quick script that will find our queue, loop through its members,
and dequeue each of them with a thank you message.

.. code-block:: python
Expand All @@ -193,12 +216,12 @@ First, we need to `find our queue`_.
my_queue = queue
Then, we can iterate over its members and dequeue with some static thank
you TwiML. Try it yourself! Hint: issuing `an HTTP POST to a Member instance`_
will dequeue that member.
Then, we can iterate over its members and dequeue with some static thank you
TwiML. Try it yourself! Hint: issuing `an HTTP POST to a Member instance`_ will
dequeue that member.

As a bonus, try allowing the callers being dequeued to record a message
for the DJs to listen to at the beginning of the next show.
As a bonus, try allowing the callers being dequeued to record a message for the
DJs to listen to at the beginning of the next show.

Finally, we can delete the queue using a REST API call.

Expand Down
1 change: 0 additions & 1 deletion docs/contents.rst.inc
Expand Up @@ -17,7 +17,6 @@ Idea Factory
custom_twiml
static_apps
dynamic_apps
deploy
voting

Unlocking the Box
Expand Down
2 changes: 2 additions & 0 deletions docs/custom_twiml.rst
Expand Up @@ -64,6 +64,8 @@ Your final application should look like this.

.. image:: _static/twimlbin.png

.. _configure-number:

Configuring your phone number
------------------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/dynamic_apps.rst
Expand Up @@ -256,6 +256,6 @@ deployemnt. It should take less than a minute to deploy.

Once it's deployed, take the url for your application and set it as the voice
number for your Twilio phone number. Configuring Twilio numbers is covered in
more detail in :ref:``
more detail in :ref:`configure-number`

Now give it a call. You should hear your custom message. Hooray!

0 comments on commit 9ecac9a

Please sign in to comment.