-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.py
177 lines (158 loc) · 7.23 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import os
from flask import Flask
from flask import Response
from flask import request
from flask import render_template
from twilio import twiml
from twilio.rest import TwilioRestClient
from twilio.util import TwilioCapability
from twilio.access_token import AccessToken
# Required to connect to Twilio
TWILIO_ACCOUNT_SID = "AC662af494000000000000000000000000"
TWILIO_AUTH_TOKEN = "058d0000000000000000000000000000"
TWILIO_NUMBER = "+14155554963"
# create an authenticated client that can make requests to Twilio for your
# account.
client = TwilioRestClient(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
# Allows us to keep a track of how many times the agent has pressed the
# "transfer" button for this call
xferd = []
# Create a Flask web app
app = Flask(__name__)
# Render the Twilio Client as a web page
@app.route('/')
def index():
# Create a capability token for this client instance
capability = TwilioCapability(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
# If the client attempts an outgoing call, invoke a TwiML app
capability.allow_client_outgoing("AP17a745dc5cfc00000000000000000000")
# We can make incoming calls to this client with <dial><client>david</client></dial>
capability.allow_client_incoming("david")
token = capability.generate()
return render_template("client_browser.html",
capability_token=token)
# The Twilio Client has attempted to make an outbound call
# This function makes that call happen
@app.route('/client_call', methods=['GET', 'POST'])
def client_pstn():
# because we are calling out to the PSTN, we need to provide a phone
# number, associated with the same Twilio account as the client, as
# caller ID. It needs to be in e.164 format
callerID = "+14155554963"
# Get the dialed number from the parameters in the HTTP request
called_number = request.values.get('tocall')
# Generate a TwiML response...
response = twiml.Response()
# ...and actually place a call to the dialed number
# when the client initiates a transfer and puts this call
# on hold, the action url will be invoked, giving us an
# opportunity to update the Client leg
with response.dial(callerId=callerID, action="/agent_into_conf") as r:
r.number(called_number)
return str(response)
# Ooh, the person using the Twilio Client has pressed the "Transfer" button
# This allows them to start the process of transferring the called party to
# a colleague (for example)
@app.route('/transfer', methods=['POST', 'GET'])
def transfer():
# The Twilio Client has told us what the call sid is for it's connection
# to Twilio. We want to find out what the call sid is for the called
# party's connection to Twilio. This will be listed as a child call on
# the Client's call
call_sid = request.values.get('params[CallSid]')
child_calls = client.calls.list(parent_call_sid=call_sid)
# The transfer button has two similar but different functions
# 1) start the transfer process by putting the called party on hold
# and initiating a call to the colleague
# 2) take the called party off hold and connect them to the
# colleague
# The easy way of figuring out which the person using the Client
# wants to do is by figuring out whether they have already pressed
# the Transfer button on this call. If they have not, they want to
# do (1). If this is the second time they have pressed the button on
# this call, they want to do (2).
if call_sid not in xferd:
# For ease, the second agent, the colleague, is on a cell phone. Again,
# we provide a url so that we can provide TwiML to control this new call
# leg
second_agent_call = client.calls.create(to="+15105550555", from_="+15105553413",
url="http://2deedf27.ngrok.io/second_agent_in_conf")
for child_call in child_calls:
print call_sid
print child_call.sid
# To place the called party on hold, we make a REST request
# to update their call leg (identified via call sid) and
# Twilio will then send a request to the url specified
# so that we can provide instructions about how to update
# the call leg via TwiML
client.calls.update(child_call.sid, method="POST",
url="http://2deedf27.ngrok.io/called_on_hold")
xferd.append(call_sid)
else:
for child_call in child_calls:
# this is the second time the transfer button has been pressed
# for this call so we will put the called party into the
# conference with the colleague
client.calls.update(child_call.sid, method="POST",
url="http://2deedf27.ngrok.io/customer_to_conf")
return "hello"
# This is the URL we gave to Twilio when we asked to update
# the call leg of the called party at the start of the transfer
# process. We will use TwiML to put the called party into a
# conference that contains only them and some hold music
@app.route('/called_on_hold', methods=['POST', 'GET'])
def called_on_hold():
print request.values
response = twiml.Response()
with response.dial() as r:
# A nice, simple conference name. I can do this here
# because I only write this web app to handle one Client
# and that Client can only have one call going at a time
# To make this web app more general, we would need to
# come up with a process for giving each "Hold" conference
# a unique but meaningful name, so that we know how to
# reference it later
conference_name = "Hold"
r.conference(conference_name, endConferenceOnExit=True)
return str(response)
# Either the called party hung up before we managed to transfer
# them or we just put them on hold. Here, we will figure out
# which
@app.route('/agent_into_conf', methods=['POST', 'GET'])
def agent_into_conf():
print "in agent into conf"
print request.values
child_sid = request.values.get("DialCallSid")
child_call = client.calls.get(child_sid)
response = twiml.Response()
# if the customer hasn't hung up, it means we've
# put them on hold so we should update the Client
# call leg to put them into a conference with the
# colleague
if child_call.status == "in-progress":
with response.dial() as r:
conference_name = "xfer"
r.conference(conference_name)
# if they have hung up, we should follow suit
else:
response.hangup()
return str(response)
# we'll put our colleague into a converence so that they can
# await our arrival
@app.route("/second_agent_in_conf", methods=["POST", "GET"])
def second_agent_in_conf():
response = twiml.Response()
with response.dial() as r:
conference_name = "xfer"
r.conference(conference_name)
return str(response)
# Put the customer into the conference with our colleague
@app.route("/customer_to_conf", methods=['POST','GET'])
def customer_to_conf():
response=twiml.Response()
with response.dial() as r:
r.conference("xfer")
return str(response)
if __name__ == '__main__':
# Note that in production, you would want to disable debugging
app.run(debug=True)