def testRecordActionMethod(self): """should record with an action and a get method""" r = Response() r.append(twiml.Record(action="example.com", method="GET")) r = self.strip(r) self.assertEquals( r, '<?xml version="1.0" encoding="UTF-8"?><Response><Record action="example.com" method="GET" /></Response>' )
def testRecordEmpty(self): """should record""" r = Response() r.append(twiml.Record()) r = self.strip(r) self.assertEquals( r, '<?xml version="1.0" encoding="UTF-8"?><Response><Record /></Response>' )
def testImproperNesting(self): """ bad nesting """ verb = twiml.Gather() self.assertRaises(TwimlException, verb.append, twiml.Gather()) self.assertRaises(TwimlException, verb.append, twiml.Record()) self.assertRaises(TwimlException, verb.append, twiml.Hangup()) self.assertRaises(TwimlException, verb.append, twiml.Redirect()) self.assertRaises(TwimlException, verb.append, twiml.Dial()) self.assertRaises(TwimlException, verb.append, twiml.Conference("")) self.assertRaises(TwimlException, verb.append, twiml.Sms(""))
def improperAppend(self, verb): self.assertRaises(TwimlException, verb.append, twiml.Say("")) self.assertRaises(TwimlException, verb.append, twiml.Gather()) self.assertRaises(TwimlException, verb.append, twiml.Play("")) self.assertRaises(TwimlException, verb.append, twiml.Record()) self.assertRaises(TwimlException, verb.append, twiml.Hangup()) self.assertRaises(TwimlException, verb.append, twiml.Reject()) self.assertRaises(TwimlException, verb.append, twiml.Redirect()) self.assertRaises(TwimlException, verb.append, twiml.Dial()) self.assertRaises(TwimlException, verb.append, twiml.Conference("")) self.assertRaises(TwimlException, verb.append, twiml.Sms("")) self.assertRaises(TwimlException, verb.append, twiml.Pause())
def Twilio_record(request): import cPickle # Save debugging data sid = request.POST['CallSid'] status = request.POST['CallStatus'] log = TwilioRecordTest.objects.get(callid=sid) if (not log.debug_data): debugData = [] else: debugData = cPickle.loads(log.debug_data.encode('ascii')) newEntry = ['Twilio_record', cPickle.dumps(request.POST)] debugData.append(newEntry) log.debug_data = cPickle.dumps(debugData) log.save() # We don't care about which session this is associated with as all # verification is the same across all sessions. r = twilio.Response() say = twilio.Say("After the tone, please press 1, 2, and 3, then pound to finish.", voice=twilio.Say.MAN, language=twilio.Say.ENGLISH) r.append(say) abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.tests.views.Twilio_record_complete') record = twilio.Record( action=urljoin(abs_uri, url), transcribe=False, finishOnKey='#', playBeep='true', timeout=30, ) r.append(record) debugData.append(str(r)) log.debug_data = cPickle.dumps(debugData) log.save() return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)
def testRecordBadAppend(self): """ should raise exceptions for wrong appending """ self.improperAppend(twiml.Record())
def testRecordAddAttribute(self): """ add attribute """ r = twiml.Record(foo="bar") r = self.strip(r) assert_equal(r, '<?xml version="1.0" encoding="UTF-8"?><Record foo="bar" />')
def testRecordTranscribeCallback(self): """ should record with a transcribe and transcribeCallback """ r = Response() r.append(twiml.Record(transcribeCallback="example.com")) r = self.strip(r) assert_equal(r, '<?xml version="1.0" encoding="UTF-8"?><Response><Record transcribeCallback="example.com" /></Response>')
def testRecordMaxlengthFinishTimeout(self): """ should record with an maxlength, finishonkey, and timeout """ r = Response() r.append(twiml.Record(timeout=4, finishOnKey="#", maxLength=30)) r = self.strip(r) assert_equal(r, '<?xml version="1.0" encoding="UTF-8"?><Response><Record finishOnKey="#" maxLength="30" timeout="4" /></Response>')
def getRecording(request, twilioResponse=None): """ Takes a recording from the user. This function uses the session dictionary at request.session for inputs and outputs. This is necessary because Twilio will make multiple calls to this function. This function differs from getQuickRecording in that it allows a user to confirm their recording. :param request: The standard Django request argument :returns: django.http.HttpResponse -- the result Required Session Keys: request.session['ivr_makeRecording_prompt'] - A Twilio verb object (pretty much always Say or Play) that is used to lead into the recording. e.g., tts('Leave a message.') Optional Session Keys: request.session['ivr_makeRecording_promptOnce'] - A flag that specifies if the prompt should be spoken only once, if True. The stored value must be one that can be tested against for truth. (i.e., the value must be able to be used in an if statement such as, "if (request.session['ivr_makeRecording_promptOnce']):...." request.session['ivr_makeRecording_maxLength'] - The Twilio gather max_length value. Default is 180: 3 minutes. request.session['ivr_makeRecording_timeout'] - The Twilio record timeout value. Default is 5. request.session['ivr_makeRecording_transcribe'] - The Twilio gather transcribe value. Default is False. request.session['ivr_makeRecording_finishOnKey'] - The Twilio gather finishOnKey value. Default is any key. request.session['ivr_makeRecording_playBeep'] - The Twilio gather playBeep value. Default is True. request.session['ivr_makeRecording_leadSilence'] - How many seconds of silence to give before starting any prompts. This is necessary because the first second or so of sound at the very beginning of any Twilio call is lost. Default is 0. request.session['ivr_makeRecording_returnOnHangup'] - The name of the function to return control flow to, should the user hang up on the recording (e.g., to terminate a voicemail message). The function gets called with a single argument -- the standard Django request object, default is None. Output Session Keys: request.session['ivr_makeRecording_recording'] - The Twilio URL of the recording Make sure you clear this key using the 'del' built-in function so that other fucntions can test against it to see if getRecording has succeeded. To "return" to your function, push it onto the end of request.session['ivr_call_stack'], as per the usual IVR philosophy. """ # FIXME: # There's gotta be a less complicated way of doing this. The biggest # complicating factor when writing a generic function for Twilio is that # every time we want to get anything from the user, we need to return and # wait for Twilio to establish a new HTTP connection. # # There are two design patterns that I've come up with so far. The first is # what you see implemented here, where Twilio accesses this function # directly via a REST API we provide. The two biggest problems here are that # it requires us to rely heavily on the session database, and that we become # reliant on Twilio to functionally return to the calling function. This # second problem is generally annoying, but becomes a meaningful problem # when Twilio won't redirect for us -- e.g., when the user hangs up on the # recording. The current solution is to use an eval() statement to directly # call the "caller" function, but this is kind of kludgey. # # The other design pattern is to have Twilio keep calling the "caller" # function, and have that call this function. The problem is that it makes # caller functions more complicated since they have to check the return # data from this function to determine what happened, and potentially keep # track of where this function is, in terms of its execution path across # HTTP connections. # # I'm not sure what we want to do about this. I'm inclined to say that the # latter implementation is going to be somewhat cleaner since we can # probably just pass a tuple of values with state and whatnot, as well as # the return value for the function at large? if 'CallStatus' in request.POST: logger.debug('%s: Into getRecording with call status %s' % ( request.session.session_key, request.POST['CallStatus'])) r = twilioResponse or twilio.Response() request.session.modified = True # First, check to see if the caller has hung up. #if (request.POST['CallStatus'] == 'completed'): if ('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'): if('RecordingUrl' in request.POST): request.session['ivr_makeRecording_recording'] = request.POST['RecordingUrl'] if 'ivr_makeRecording_recording' in request.session: view = request.session.get('ivr_makeRecording_returnOnHangup', None) if view: try: # TODO: no more exec() but validation on view is needed mod, func = view.rsplit('.', 1) mod = import_module(mod) getattr(mod, func)(request) # call the view function except Exception as e: mail_admins("Problem calling view in getRecording", str(e)) cleanup_recording_state(request) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) if 'RecordingUrl' in request.POST: recording_url = request.POST['RecordingUrl'] p1 = re.compile('http://api.twilio.com/\d{4}-\d{2}-\d{2}/Accounts/AC[0-9a-f]'\ '{32}/Recordings/RE[0-9a-f]{32}$') if (not p1.match(recording_url)): raise Exception(_('Recording url failed to match regex: %s') % (recording_url,)) request.session['ivr_makeRecording_recording'] = recording_url getRecording_playRecordingAndConfirmation(request, r) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) elif ('ivr_makeRecording_recording' in request.session): if 'Digits' in request.POST: digits = request.POST['Digits'] p2 = re.compile('[0-9*#]$') if (not p2.match(digits)): raise Exception('') if (digits == '1'): # User accepted the recording. cleanup_recording_state(request) r.append(twilio.Redirect(reverse(request.session['ivr_call_stack'].pop()))) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) if (digits == '3'): del request.session['ivr_makeRecording_recording'] # And just fall through. User wishes to record again, so pretty much start over. else: getRecording_playRecordingAndConfirmation(request, r) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) else: getRecording_getConfirmation(request, r) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) # Check for required values if (not 'ivr_makeRecording_prompt' in request.session): raise Exception(_('Error. Required session key \'ivr_makeRecording_prompt\' undefined.')) # Set up default values if (not 'ivr_makeRecording_promptOnce' in request.session): request.session['ivr_makeRecording_promptOnce'] = False if (not 'ivr_makeRecording_maxLength' in request.session): request.session['ivr_makeRecording_maxLength'] = 180 if (not 'ivr_makeRecording_timeout' in request.session): request.session['ivr_makeRecording_timeout'] = 5 if (not 'ivr_makeRecording_transcribe' in request.session): request.session['ivr_makeRecording_transcribe'] = False if (not 'ivr_makeRecording_finishOnKey' in request.session): request.session['ivr_makeRecording_finishOnKey'] = '1234567890*#' if (not 'ivr_makeRecording_playBeep' in request.session): request.session['ivr_makeRecording_playBeep'] = True if (not 'ivr_makeRecording_leadSilence' in request.session): request.session['ivr_makeRecording_leadSilence'] = 0 if (not 'ivr_makeRecording_returnOnHangup' in request.session): request.session['ivr_makeRecording_returnOnHangup'] = None if (request.session['ivr_makeRecording_promptOnce']): if (not 'ivr_makeRecording_promptOnce_played' in request.session): request.session['ivr_makeRecording_promptOnce_played'] = True r.append(request.session['ivr_makeRecording_prompt']) else: r.append(request.session['ivr_makeRecording_prompt']) if (request.session['ivr_makeRecording_leadSilence']): r.append(twilio.Pause(length=request.session['ivr_makeRecording_leadSilence'])) r.append(twilio.Record( action=reverse('getRecording'), maxLength=request.session['ivr_makeRecording_maxLength'], timeout=request.session['ivr_makeRecording_timeout'], finishOnKey=request.session['ivr_makeRecording_finishOnKey'], transcribe=request.session['ivr_makeRecording_transcribe'], playBeep=request.session['ivr_makeRecording_playBeep'], )) r.append(twilio.Redirect(reverse('getRecording'))) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)
def getQuickRecording(request): """Takes a recording from the user. This function uses the session dictionary at request.session for inputs and outputs. This is done to maintain code base flexibility with this and getRecording. This function differs from getRecording in that strictly gets a recording from the user, without allowing the user to confirm that they wish to keep the recording. :param request: The standard Django request argument :returns: django.http.HttpResponse -- the result Required Session Keys: request.session['ivr_makeRecording_prompt'] - A Twilio verb object (pretty much always Say or Play) that is used to lead into the recording. e.g., tts('Leave a message.') Optional Session Keys: request.session['ivr_makeRecording_maxLength'] - The Twilio gather max_length value. Default is 5: 5 seconds. request.session['ivr_makeRecording_timeout'] - The Twilio record timeout value. Default is 6. request.session['ivr_makeRecording_leadSilence'] - How many seconds of silence to give before starting any prompts. This is necessary because the first second or so of sound at the very beginning of any Twilio call is lost. Default is 1. request.session['ivr_makeRecording_playBeep'] - Set to True if you want the recording to be prompted with a tone. Default is True. request.session['ivr_makeRecording_finishOnKey'] - The Twilio gather finishOnKey value. Default is any key. Output Session Keys: request.session['ivr_makeRecording_recording'] - The Twilio URL of the recording Make sure you clear this key using the 'del' built-in function so that other fucntions can test against it to see if getRecording has succeeded. To "return" to your function, push it onto the end of request.session['ivr_call_stack'], as per the usual IVR philosophy. """ if 'CallStatus' in request.POST: logger.debug('%s: Into getQuickRecording with call status %s' % ( request.session.session_key, request.POST['CallStatus'])) r = twilio.Response() request.session.modified = True request.session['ivr_only_callbacknumber'] = True if 'CallStatus' in request.POST and request.POST['CallStatus'] == 'completed': view = request.session.get('ivr_makeRecording_returnOnHangup', None) if view: # The user hung up. Return out and tell twilio no message recorded. request.session['ivr_no_pound'] = True if 'RecordingUrl' in request.POST: request.session['ivr_makeRecording_recording'] = request.POST['RecordingUrl'] request.session['ivr_only_callbacknumber'] = False else: request.session['ivr_only_callbacknumber'] = True try: # TODO: no more exec() but validation on view is needed mod, func = view.rsplit('.', 1) mod = import_module(mod) getattr(mod, func)(request) # call the view function except Exception as e: mail_admins("Problem calling view in getQuickRecording", str(e)) cleanup_recording_state(request) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) if 'CallStatus' in request.POST and 'RecordingUrl' in request.POST: if request.POST['CallStatus'] == 'completed': # The user hung up. Return out and tell Twilio to do nothing. return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) recording_url = request.POST['RecordingUrl'] p1 = re.compile('http://api.twilio.com/\d{4}-\d{2}-\d{2}/Accounts/AC[0-9a-f]{32}'\ '/Recordings/RE[0-9a-f]{32}$') if (not p1.match(recording_url)): raise Exception(_('Recording url failed to match regex: %s') % (recording_url,)) request.session['ivr_makeRecording_recording'] = recording_url request.session['ivr_only_callbacknumber'] = False cleanup_recording_state(request) del request.session['getQuickRecording_subsequentExcecution'] r.append(twilio.Redirect(reverse(request.session['ivr_call_stack'].pop()))) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) # Check for required values if (not 'ivr_makeRecording_prompt' in request.session): raise Exception(_('Error. Required session key \'ivr_makeRecording_prompt\' ' 'undefined. Request.method is %s.') % (request.POST,)) # Set up default values if (not 'ivr_makeRecording_maxLength' in request.session): request.session['ivr_makeRecording_maxLength'] = 5 if (not 'ivr_makeRecording_timeout' in request.session): request.session['ivr_makeRecording_timeout'] = 6 if (not 'ivr_makeRecording_leadSilence' in request.session): request.session['ivr_makeRecording_leadSilence'] = 1 if (not 'ivr_makeRecording_playBeep' in request.session): request.session['ivr_makeRecording_playBeep'] = True if (not 'ivr_makeRecording_finishOnKey' in request.session): request.session['ivr_makeRecording_finishOnKey'] = '1234567890*#' # Use this to keep track of if we've been through here before. if ('getQuickRecording_subsequentExcecution' in request.session): r.append(tts('Sorry, I didn\'t get that.')) request.session['getQuickRecording_subsequentExcecution'] = True if (request.session['ivr_makeRecording_leadSilence']): r.append(twilio.Pause(length=request.session['ivr_makeRecording_leadSilence'])) r.append(request.session['ivr_makeRecording_prompt']) r.append(twilio.Record( action=reverse('getQuickRecording'), maxLength=request.session['ivr_makeRecording_maxLength'], finishOnKey=request.session['ivr_makeRecording_finishOnKey'], timeout=request.session['ivr_makeRecording_timeout'], playBeep=request.session['ivr_makeRecording_playBeep'], )) r.append(twilio.Redirect(reverse('getQuickRecording'))) #raise Exception(str(r)) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE)