Created
June 1, 2015 22:54
-
-
Save iL3D/7e3b82ecef0d4471dfed to your computer and use it in GitHub Desktop.
Python/Flask port for menu and callme twimlets originally in PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'''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