Skip to content

Instantly share code, notes, and snippets.

@iL3D
Created June 1, 2015 22:54
Show Gist options
  • Save iL3D/7e3b82ecef0d4471dfed to your computer and use it in GitHub Desktop.
Save iL3D/7e3b82ecef0d4471dfed to your computer and use it in GitHub Desktop.
Python/Flask port for menu and callme twimlets originally in PHP
'''Copyright (c) 2012 Twilio, Inc.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.'''
import re
from flask import Flask
from flask import render_template
from flask import url_for
from flask import request
from urllib.parse import urlencode
from twilio import twiml
from twilio.util import TwilioCapability
# Declare and configure application
app = Flask(__name__, static_url_path='/static')
app.config.from_pyfile('local_settings.py')
# Voice Request URL
@app.route('/voice', methods=['GET', 'POST'])
def voice():
response = twiml.Response()
response.say("Congratulations! You deployed the Twilio Hackpack "
"for Heroku and Flask.")
return str(response)
# SMS Request URL
@app.route('/sms', methods=['GET', 'POST'])
def sms():
response = twiml.Response()
response.sms("Congratulations! You deployed the Twilio Hackpack "
"for Heroku and Flask.")
return str(response)
# Twilio Client demo template
@app.route('/client')
def client():
configuration_error = None
for key in ('TWILIO_ACCOUNT_SID', 'TWILIO_AUTH_TOKEN', 'TWILIO_APP_SID',
'TWILIO_CALLER_ID'):
if not app.config.get(key, None):
configuration_error = "Missing from local_settings.py: " \
"{0}".format(key)
token = None
if not configuration_error:
capability = TwilioCapability(app.config['TWILIO_ACCOUNT_SID'],
app.config['TWILIO_AUTH_TOKEN'])
capability.allow_client_incoming("joey_ramone")
capability.allow_client_outgoing(app.config['TWILIO_APP_SID'])
token = capability.generate()
params = {'token': token}
return render_template('client.html', params=params,
configuration_error=configuration_error)
@app.route('/client/incoming', methods=['POST'])
def client_incoming():
try:
from_number = request.values.get('PhoneNumber', None)
resp = twiml.Response()
if not from_number:
resp.say("Your app is missing a Phone Number. "
"Make a request with a Phone Number to make outgoing "
"calls with the Twilio hack pack.")
return str(resp)
if 'TWILIO_CALLER_ID' not in app.config:
resp.say(
"Your app is missing a Caller ID parameter. "
"Please add a Caller ID to make outgoing calls with Twilio "
"Client")
return str(resp)
with resp.dial(callerId=app.config['TWILIO_CALLER_ID']) as r:
# If we have a number, and it looks like a phone number:
if from_number and re.search('^[\d\(\)\- \+]+$', from_number):
r.number(from_number)
else:
r.say("We couldn't find a phone number to dial. Make sure "
"you are sending a Phone Number when you make a "
"request with Twilio Client")
return str(resp)
except:
resp = twiml.Response()
resp.say("An error occurred. Check your debugger at twilio dot com "
"for more information.")
return str(resp)
# Callme twimlet-equivalent URL
@app.route('/callme', methods=['GET','POST'])
def callme():
response = twiml.Response()
if request.values.get('AccountSid') != app.config['TWILIO_ACCOUNT_SID']:
return str(response)
# Second pass only, after initial call
dial_call_status = request.values.get('DialCallStatus', None)
dial_status = request.values.get('DialStatus' , None)
dial = request.values.get('Dial' , None)
if dial and (dial_status or dial_call_status):
url_next = request.values.get('FailUrl', None)
if dial_call_status=='completed' or dial_status=='answered' or not url_next:
response.hangup()
else:
response.redirect(url_next)
# First pass, making initial call
else:
fail_param = request.values.get('FailUrl','')
message_param = request.values.get('Message','')
timeout_param = request.values.get('Timeout','20')
number_to_dial = request.values.get('PhoneNumber', None)
# To trigger the proper fail-over or hangup, rrepeat with Dial flag set in order
url_after_call = '{}?{}'.format( url_for('.callme', _external=True),
urlencode({'Dial':'true','FailUrl':fail_param}))
# Do a whisper on "my" end to prevent unintended pickup by greedy voicemail of, say, a powered-off mobile phone
url_upon_pickup = '{}?{}'.format( 'http://twimlets.com/whisper',
urlencode({'Message':message_param}))
with response.dial(action=url_after_call,timeout=timeout_param) as d:
d.number(number_to_dial, url=url_upon_pickup)
return str(response)
# Menu twimlet-equivalent URL
@app.route('/menu', methods=['GET','POST'])
def menu():
response = twiml.Response()
if request.values.get('AccountSid') != app.config['TWILIO_ACCOUNT_SID']:
return str(response)
digits = request.values.get('Digits', None)
if digits:
url_submenu = request.values.get('Options[' + digits + ']', None)
if url_submenu:
response.redirect(url_submenu)
else:
response.say("I'm sorry, that is not a valid option.")
response.hangup()
else:
max_digits = 1
# for gathering multiple digits, uncomment the following
#keycodes = re.findall(r'Options\[(\S+)\]',str(request.values))
#for keycode in keycodes:
# max_digits = max(max_digits, len(keycode))
with response.gather(numDigits=max_digits) as g:
message = request.values.get('Message','')
if ( re.match(r'^http*',message) ): g.play(message)
elif ( len(message) > 0 ): g.say( message)
# uncomment the next two lines to run once without repeat
#response.say("You did not make a selection. Good-bye.")
#response.hangup()
# OR repeat until selection made
response.redirect()
return str(response)
# Installation success page
@app.route('/')
def index():
params = {
'Voice Request URL': url_for('.voice', _external=True),
'SMS Request URL': url_for('.sms', _external=True),
'Client URL': url_for('.client', _external=True)}
return render_template('index.html', params=params,
configuration_error=None)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment