def twilio_ConfigurePracticeLocalNumber2010(practice, tw_number): """ Configures a practice's Twilio number to point to the correct Twilio IVR handler for the user, as well as sets the incoming phone number's friendly name appropriately. :param practice - practiceLocation which has mdcom_phone and mdcom_phone_sid set: 2010 version - need different status callback url """ auth, uri = client.auth, client.account_uri tw_number = _getUSNumber(tw_number) # assume US number, need to add country code norm_number = '+1' + tw_number abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Main_New') statusurl = reverse('MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Status') smsurl = reverse('MHLogin.DoctorCom.SMS.views.twilioSMS_incoming') d = { 'PhoneNumber': norm_number, 'FriendlyName': _('%s\'s DCom Number') % (practice.practice_name), 'VoiceUrl': urljoin(abs_uri, url), 'VoiceMethod': 'POST', 'StatusCallback': urljoin(abs_uri, statusurl), 'StatusCallbackMethod': 'POST', 'SmsUrl': urljoin(abs_uri, smsurl), } # TODO: add resp = , Verify the resulting data from Twilio, in JSON format try: make_twilio_request('POST', uri + '/IncomingPhoneNumbers/' + practice.mdcom_phone_sid, auth=auth, data=d) except TwilioRestException: pass return True
def twilio_ConfigurePracticeLocalNumber(practice, tw_number): """ DEPRECATED - in the future, it will be replaced by twilio_ConfigurePracticeLocalNumber2010 Any changes here should be replicated to twilio_ConfigurePracticeLocalNumber2010 Configures a practice's Twilio number to point to the correct Twilio IVR handler for the user, as well as sets the incoming phone number's friendly name appropriately. :param practice - PracticeLocation with mdcom_phone and mdcom_phone_sid set to the number whose urls is being set """ if (settings.TWILIO_PHASE == 2): return twilio_ConfigurePracticeLocalNumber2010(practice, tw_number) else: auth, uri = client2008.auth, client2008.account_uri abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_Main') smsurl = reverse('MHLogin.DoctorCom.SMS.views.twilioSMS_incoming') d = { 'FriendlyName': _('%s\'s DCom Number') % (practice.practice_name), 'Url': urljoin(abs_uri, url), 'SmsUrl': urljoin(abs_uri, smsurl), } # TODO: add resp = , Verify the resulting data from Twilio, in JSON format try: make_twilio_request('POST', uri + '/IncomingPhoneNumbers/' + practice.mdcom_phone_sid, auth=auth, data=d) except TwilioRestException: pass return True
def test_make_twilio_request_bad_data(mock): resp = Mock() resp.ok = False mock.return_value = resp url = "http://random/url" make_twilio_request("POST", url) mock.assert_called_with("POST", "http://random/url.json", headers=post_headers)
def request(self, method, uri, **kwargs): """ Send an HTTP request to the resource. :raises: a :exc:`~twilio.TwilioRestException` """ if 'timeout' not in kwargs and self.timeout is not UNSET_TIMEOUT: kwargs['timeout'] = self.timeout data = kwargs.get('data') if data is not None: udata = {} for k, v in six.iteritems(data): key = k.encode('utf-8') if isinstance(v, (list, tuple, set)): udata[key] = [encode_atom(x) for x in v] elif isinstance(v, (six.integer_types, six.binary_type, six.string_types)): udata[key] = encode_atom(v) else: raise ValueError('data should be an integer, ' 'binary, or string, or sequence ') data = urlencode(udata, doseq=True) event = HttpEvent(method, uri, data) self.events.append(event) resp = make_twilio_request(method, uri, auth=self.auth, **kwargs) event.url = resp.url event.status_code = resp.status_code event.response_body = six.text_type(resp.content) if method == "DELETE": return resp, {} else: return resp, json.loads(resp.content)
def twilio_old_get_available_number(area_code, incountry="US", intype="local"): """ request for number in area_code - DOES NOT RESPECT AREACODE restriction TO BE REMOVED - return only phone # """ auth, uri, = client.auth, client.account_uri abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) d = { 'AreaCode': area_code, } num_tup = None try: resp = make_twilio_request('GET', uri + '/AvailablePhoneNumbers/US/Local', auth=auth, data=d) content = json.loads(resp.content) # get the first phone number entry that matches - phone # only number_info = content['available_phone_numbers'][0]['phone_number'] # if we don't get any number, retry...? logger.debug('twilio_ProvisionNewLocalNumber2010 areacode %s got %s number %s' % ( area_code, str(content), number_info)) us_number_info = _getUSNumber(number_info) except TwilioRestException as re: # This error indicates that no number was available. if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED, TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE): raise Exception(_('Provision Local number fail: %s' % re.msg)) except KeyError: pass # return None if json fields not in resp return us_number_info
def twilio_old_get_available_number(area_code, incountry="US", intype="local"): """ request for number in area_code - DOES NOT RESPECT AREACODE restriction TO BE REMOVED - return only phone # """ auth, uri, = client.auth, client.account_uri abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) d = { 'AreaCode': area_code, } num_tup = None try: resp = make_twilio_request('GET', uri + '/AvailablePhoneNumbers/US/Local', auth=auth, data=d) content = json.loads(resp.content) # get the first phone number entry that matches - phone # only number_info = content['available_phone_numbers'][0]['phone_number'] # if we don't get any number, retry...? logger.debug( 'twilio_ProvisionNewLocalNumber2010 areacode %s got %s number %s' % (area_code, str(content), number_info)) us_number_info = _getUSNumber(number_info) except TwilioRestException as re: # This error indicates that no number was available. if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED, TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE): raise Exception(_('Provision Local number fail: %s' % re.msg)) except KeyError: pass # return None if json fields not in resp return us_number_info
def PracticeIVR_LeaveRegularMsg(request): """ Sends office manager text message, in attachment there is voice file of recording """ if 'CallStatus' in request.POST: logger.debug('%s: Into LeaveTextMsg with call status %s' % ( request.session.session_key, request.POST['CallStatus'])) if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'): try: callSID = request.POST['CallSid'] auth, uri, = client.auth, client.account_uri resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth) content = json.loads(resp.content) log = callLog.objects.get(callSID=callSID) log.call_duration = content['TwilioResponse']['Call']['Duration'] log.save() except TwilioRestException as tre: logger.critical('Unable to get call status: %s' % tre.msg) except ObjectDoesNotExist as odne: logger.warning('Call log does not exist for sid: %s. Caller may have ' 'hung up shortly after Twilio starts call process.' % str(odne)) #if already got recording - this must be second ittiration if ('ivr_makeRecording_recording' in request.session or request.session['ivr_only_callbacknumber'] == True): # get a list of all office managers for this practice mgrs = get_all_practice_managers(request.session['practice_id']) # after unique'ifying save_answering_service_message() expects recips # as a list, see https://redmine.mdcom.com/issues/1374 for details save_answering_service_message(request, False, list(set(m.user for m in mgrs))) r = twilio.Response() r.append(tts(_('Your message have been sent. Good Buy'))) r.append(twilio.Hangup()) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) else: # first or second iteration, get call back number and message recoding if ('ivr_makeRecording_callbacknumber' in request.session): # get recording for non urgent message request.session['ivr_makeRecording_maxLength'] = 600 # 10 minutes request.session['ivr_makeRecording_leadSilence'] = 2 request.session['ivr_makeRecording_promptOnce'] = True request.session['ivr_makeRecording_prompt'] = tts(_('Please say your ' 'non urgent message after the beep. Please state your name and ' 'speak clearly. Press pound when finished.')) request.session['ivr_makeRecording_returnOnHangup'] = \ 'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveRegularMsg' request.session['ivr_call_stack'].append('PracticeIVR_LeaveRegularMsg') request.session.modified = True # Pass off the recording action to the getRecording function. return getQuickRecording(request) else: #get call back number request.session['ivr_call_stack'].append('PracticeIVR_LeaveRegularMsg') request.session['ivr_callback_returnOnHangup'] = \ 'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveRegularMsg' return getCallBackNumber(request)
def Twilio_call_initiate(request, type, url): # type is the model type that we're going to use. # 1: TwilioCallGatherTest # 2: TwilioRecordTest caller = MHLUser.objects.get(id=request.user.id) called = caller if (not caller.mobile_phone): return errlib.err500(request, err_msg="You do not have a mobile phone number " "in your profile. Please enter one to use this function.") if (not called.mobile_phone): return errlib.err500(request, err_msg="The person you are trying to call " "doesn't have a mobile phone number listed.") if (type == 1): log = TwilioCallGatherTest() elif (type == 2): log = TwilioRecordTest() log.tester = caller log.save() # TODO: When confirmation gets figured out, generate an appropriate error message here. if (not caller.mobile_confirmed): pass if (not called.mobile_confirmed): pass response_args = '' if (request.GET and request.GET.has_key()): response_args = '?record=True' d = { 'Caller': settings.TWILIO_CALLER_ID, 'Called': caller.mobile_phone, 'Url': url, 'Method': 'POST', 'Timeout': 60, } auth, uri = client.auth, client.account_uri call_info = make_twilio_request('POST', uri + '/Calls', auth=auth, data=d) sid = json.loads(call_info.content).get('sid', '') if not validate_sid(sid): # This only occurs if the call SID is invalid or has changed formats. return errlib.err500(request) log.callid = sid log.save() if (type == 2): return sid context = get_context(request) context['called'] = called context['test_type'] = 10 context['test_id'] = log.id context['form_action'] = reverse('MHLogin.tests.views.confirm_test',) return render_to_response("tests/confirm_test.html", context)
def get_call_by_sid(callSid): """ Helper to get call info based on SID """ if (settings.TWILIO_PHASE == 2): auth, uri, call = client.auth, client.account_uri, None try: params = '/Calls/%s.json' % (callSid) # twilio req appends .json in wrong place when params and no headers resp = make_twilio_request( 'GET', uri + params, auth=auth, **{'headers': { 'Accept': 'application/json' }}) content = json.loads(resp.content) call = content['call'] logger.debug('get_call_by_sid content %s' % (call)) except (TwilioRestException, ValueError, KeyError) as err: logger.critical("Problems querying call sid %s: %s" % (callSid, str(err))) else: auth, uri, call = client2008.auth, client2008.account_uri, None try: params = '/Calls/%s' % (callSid) # twilio req appends .json in wrong place when params and no headers resp = make_twilio_request( 'GET', uri + params, auth=auth, **{'headers': { 'Accept': 'application/json' }}) content = json.loads(resp.content) logger.debug('get_call_by_sid content %s ' % content) call = content['TwilioResponse']['Call'] except (TwilioRestException, ValueError, KeyError) as err: logger.critical("Problems querying call sid %s: %s" % (callSid, str(err))) return call
def twilio_ProvisionNewLocalNumber(area_code): """ DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewLocalNumber2010 Any changes here should be replicated to twilio_ProvisionNewLocalNumber2010 :param area_code: area_code may be any valid three-digit string with the first digit being 2-9 (inclusive), second digit being 0-8 (inclusive) and last digit being 0-9 (inclusive). Toll-free numbers are excluded. :returns: The newly provisioned number on success, or None if Twilio doesn't have any available numbers for the requested area code. :raises: A generic exception if an invalid area code is requested. Raises an HTTPError if the Twilio request results in an error other than listed above. Note: twilio 2010 uses 2 phase call: 1 to call to AvailablePhoneNumbers and with the resulting number, call to IncomingPhoneNumbers """ if (settings.TWILIO_PHASE == 2): return twilio_ProvisionNewLocalNumber2010(area_code) else: auth, uri, = client2008.auth, client2008.account_uri abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber') d = { #'From' : settings.TWILIO_CALLER_ID, 'AreaCode': area_code, 'FriendlyName': 'Unassigned Number', 'Url': urljoin(abs_uri, url), } num_tup = None try: resp = make_twilio_request('POST', uri + '/IncomingPhoneNumbers/Local', auth=auth, data=d) content = json.loads(resp.content) number_info = content['TwilioResponse']['IncomingPhoneNumber'][ 'PhoneNumber'] sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid'] num_tup = (number_info, sid) except TwilioRestException as re: # This error indicates that no number was available. if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED, TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE): raise Exception(_('Provision Local number fail: %s' % re.msg)) except KeyError: pass # return None if json fields not in resp return num_tup
def twilio_ConfigurePracticeLocalNumber2010(practice, tw_number): """ Configures a practice's Twilio number to point to the correct Twilio IVR handler for the user, as well as sets the incoming phone number's friendly name appropriately. :param practice - practiceLocation which has mdcom_phone and mdcom_phone_sid set: 2010 version - need different status callback url """ auth, uri = client.auth, client.account_uri tw_number = _getUSNumber(tw_number) # assume US number, need to add country code norm_number = '+1' + tw_number abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse( 'MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Main_New') statusurl = reverse( 'MHLogin.DoctorCom.IVR.views_practice_v2.PracticeIVR_Status') smsurl = reverse('MHLogin.DoctorCom.SMS.views.twilioSMS_incoming') d = { 'PhoneNumber': norm_number, 'FriendlyName': _('%s\'s DCom Number') % (practice.practice_name), 'VoiceUrl': urljoin(abs_uri, url), 'VoiceMethod': 'POST', 'StatusCallback': urljoin(abs_uri, statusurl), 'StatusCallbackMethod': 'POST', 'SmsUrl': urljoin(abs_uri, smsurl), } # TODO: add resp = , Verify the resulting data from Twilio, in JSON format try: make_twilio_request('POST', uri + '/IncomingPhoneNumbers/' + practice.mdcom_phone_sid, auth=auth, data=d) except TwilioRestException: pass return True
def twilio_ProvisionNewTollFreeNumber(area_code): """ DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewTollFreeNumber2010 Any changes here should be replicated to twilio_ProvisionNewTollFreeNumber2010 :param area_code: area_code may be one of 800, 888, 877 or 866, as per Twilio's documentation. If it isn't one of these values, this function will raise an Exception. :returns: The newly provisioned number on success, or None if Twilio doesn't have any available numbers for the requested area code. :raises: Raises a generic exception if an invalid area code is requested. Raises an HTTPError if the Twilio request results in an error other than listed above. """ if (settings.TWILIO_PHASE == 2): return twilio_ProvisionNewTollFreeNumber2010(area_code) else: auth, uri = client2008.auth, client2008.account_uri abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber') d = { #'From' : settings.TWILIO_CALLER_ID, 'AreaCode': area_code, 'FriendlyName': _('Unassigned Number'), 'Url': urljoin(abs_uri, url), } num_tup = None try: resp = make_twilio_request('POST', uri + '/IncomingPhoneNumbers/TollFree', auth=auth, data=d) content = json.loads(resp.content) number_info = content['TwilioResponse']['IncomingPhoneNumber'][ 'PhoneNumber'] sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid'] num_tup = (number_info, sid) except TwilioRestException as re: # This error indicates that no number was available. if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED, TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE): raise Exception(_('TollFree number fail: %s' % re.msg)) except KeyError: pass # return None if json fields not in resp return num_tup
def twilio_ProvisionNewLocalNumber(area_code): """ DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewLocalNumber2010 Any changes here should be replicated to twilio_ProvisionNewLocalNumber2010 :param area_code: area_code may be any valid three-digit string with the first digit being 2-9 (inclusive), second digit being 0-8 (inclusive) and last digit being 0-9 (inclusive). Toll-free numbers are excluded. :returns: The newly provisioned number on success, or None if Twilio doesn't have any available numbers for the requested area code. :raises: A generic exception if an invalid area code is requested. Raises an HTTPError if the Twilio request results in an error other than listed above. Note: twilio 2010 uses 2 phase call: 1 to call to AvailablePhoneNumbers and with the resulting number, call to IncomingPhoneNumbers """ if (settings.TWILIO_PHASE == 2): return twilio_ProvisionNewLocalNumber2010(area_code) else: auth, uri, = client2008.auth, client2008.account_uri abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber') d = { #'From' : settings.TWILIO_CALLER_ID, 'AreaCode': area_code, 'FriendlyName': 'Unassigned Number', 'Url': urljoin(abs_uri, url), } num_tup = None try: resp = make_twilio_request('POST', uri + '/IncomingPhoneNumbers/Local', auth=auth, data=d) content = json.loads(resp.content) number_info = content['TwilioResponse']['IncomingPhoneNumber']['PhoneNumber'] sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid'] num_tup = (number_info, sid) except TwilioRestException as re: # This error indicates that no number was available. if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED, TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE): raise Exception(_('Provision Local number fail: %s' % re.msg)) except KeyError: pass # return None if json fields not in resp return num_tup
def twilio_ProvisionNewTollFreeNumber(area_code): """ DEPRECATED - in the future, it will be replaced by twilio_ProvisionNewTollFreeNumber2010 Any changes here should be replicated to twilio_ProvisionNewTollFreeNumber2010 :param area_code: area_code may be one of 800, 888, 877 or 866, as per Twilio's documentation. If it isn't one of these values, this function will raise an Exception. :returns: The newly provisioned number on success, or None if Twilio doesn't have any available numbers for the requested area code. :raises: Raises a generic exception if an invalid area code is requested. Raises an HTTPError if the Twilio request results in an error other than listed above. """ if (settings.TWILIO_PHASE == 2): return twilio_ProvisionNewTollFreeNumber2010(area_code) else: auth, uri = client2008.auth, client2008.account_uri abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.DoctorCom.IVR.views_generic.UnaffiliatedNumber') d = { #'From' : settings.TWILIO_CALLER_ID, 'AreaCode': area_code, 'FriendlyName': _('Unassigned Number'), 'Url': urljoin(abs_uri, url), } num_tup = None try: resp = make_twilio_request('POST', uri + '/IncomingPhoneNumbers/TollFree', auth=auth, data=d) content = json.loads(resp.content) number_info = content['TwilioResponse']['IncomingPhoneNumber']['PhoneNumber'] sid = content['TwilioResponse']['IncomingPhoneNumber']['Sid'] num_tup = (number_info, sid) except TwilioRestException as re: # This error indicates that no number was available. if re.code not in (TWILIO_AREACODE_PARAMETER_NOT_SUPPORTED, TWILIO_INVALID_AREA_CODE, TWILIO_NO_PHONE_NUMBERS_IN_AREA_CODE): raise Exception(_('TollFree number fail: %s' % re.msg)) except KeyError: pass # return None if json fields not in resp return num_tup
def ProviderIVR_LeaveMsg(request): """ Records a voicemail message. Arguments: request - The standard Django request argument request.session Keys: provider_id - The ID of the Provider user who owns this voicemail box. """ # TODO: Update this code so that users can hit the pound key to # pop back and log into their own voicemail box. provider = Provider.objects.get(id=request.session['provider_id']) config = None config_complete = provider.vm_config.count( ) == 1 and provider.vm_config.get().config_complete if (config_complete): config = provider.vm_config.get() if ('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'): try: callSID = request.POST['CallSid'] auth, uri, = client.auth, client.account_uri resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth) content = json.loads(resp.content) log = callLog.objects.get(callSID=callSID) log.call_duration = content['TwilioResponse']['Call']['Duration'] log.save() except: pass # this is really ugly, but letting this exception fall through # destroys a message analytics will pick up the duration later on if it's missing if ('ivr_makeRecording_recording' in request.session): provider_qs = Provider.objects.filter( mobile_phone=request.session['Caller']) if (provider_qs): request.session['ivr_makeRecording_callbacknumber'] = provider_qs[ 0].mdcom_phone subject = "Voice Mail from %s %s" % (provider_qs[0].first_name, provider_qs[0].last_name) else: request.session[ 'ivr_makeRecording_callbacknumber'] = request.session['Caller'] subject = "Voice Mail from %s" % request.session[ 'ivr_makeRecording_callbacknumber'] save_message(request, subject, [provider.user], None, "VM", False) request.session.pop('ivr_makeRecording_recording') r = twilio.Response() r.append(tts('Good bye')) r.append(twilio.Hangup()) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) # Get the configuration file for this Provider if (not config_complete): # FIXME: # Probably not the best way to create the spoken number. We need to # break the number down so that there are spaces between each digit, and # so that there are commas after the third and sixth digits. if (not 'Called' in request.session or (not request.session['Called'])): # This will occur if click2call calls this function request.session['ivr_makeRecording_prompt'] = tts( 'The user is not available. ' 'Please leave a message after the beep. Press pound when finished for options.' ) else: number = request.session['Called'] spoken_number = [] [spoken_number.extend([i, ' ']) for i in number] spoken_number.pop() # drop the last element spoken_number.insert(5, ',') spoken_number.insert(12, ',') request.session['ivr_makeRecording_prompt'] = tts( 'The person at %s ' 'is not available. Please leave a message after the beep. Press pound ' 'when finished for options.' % (''.join(spoken_number), )) else: p = re.compile('http') if (p.match(config.greeting)): # This is a Twilio recording. request.session['ivr_makeRecording_prompt'] = twilio.Play( config.greeting) else: # TODO: raise Exception('Unimplemented playback of local files') request.session['ivr_makeRecording_maxLength'] = 120 # 2 minutes request.session['ivr_makeRecording_leadSilence'] = 2 request.session['ivr_makeRecording_promptOnce'] = True request.session['ivr_makeRecording_returnOnHangup'] = \ 'MHLogin.DoctorCom.IVR.views_provider.ProviderIVR_LeaveMsg' request.session['ivr_call_stack'].append('ProviderIVR_LeaveMsg') request.session.modified = True # Pass off the recording action to the getRecording function. return getRecording(request)
def PracticeIVR_LeaveUrgentMsg(request): """ Records a voicemail message for the doctor on call and leave notification to the doctor based on preferences. """ # TODO: Update this code so that users can hit the pound key to pop # back and log into their own voicemail box. if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'): try: callSID = request.POST['CallSid'] auth, uri, = client.auth, client.account_uri resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth) content = json.loads(resp.content) log = callLog.objects.get(callSID=callSID) log.call_duration = content['TwilioResponse']['Call']['Duration'] log.save() except TwilioRestException as tre: logger.critical('Unable to get call status: %s' % tre.msg) except ObjectDoesNotExist as odne: logger.warning('Call log does not exist for sid: %s.' % str(odne)) logger.debug('%s: Into PracticeIVR_LeaveUrgentMsg with call status %s' % (request.session.session_key, request.POST['CallStatus'])) if ('ivr_makeRecording_callbacknumber' in request.session): callback = request.session['ivr_makeRecording_callbacknumber'] provider = _getRecentOncallProvider(request) # TODO: we do nothing with config config = None config_complete = provider.vm_config.count() == 1 and provider.vm_config.get().config_complete if (config_complete): config = provider.vm_config.get() if ('ivr_makeRecording_recording' in request.session or request.session['ivr_only_callbacknumber'] == True): if (request.session['ivr_only_callbacknumber'] == False): mgrs = get_all_practice_managers(request.session['practice_id']) # after unique'ifying save_answering_service_message() expects recips # as a list, see https://redmine.mdcom.com/issues/1374 for details save_answering_service_message(request, True, [provider], list(set(m.user for m in mgrs))) #this is calling doctor on call we keep events for that even if called #via answering service, just like doctor com does # Cleanup del request.session['ivr_makeRecording_recording'] else: if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'): mgrs = get_all_practice_managers(request.session['practice_id']) # after unique'ifying save_answering_service_message() expects recips # as a list, see https://redmine.mdcom.com/issues/1374 for details save_answering_service_message(request, True, [provider], list(set(m.user for m in mgrs))) # Notify the user, always by sms, but provider better have mobile phone if (provider.mobile_phone): if (request.session['ivr_only_callbacknumber'] == False): body = render_to_string('DoctorCom/IVR/voicemail_sms.txt', { #'caller': '(%s) %s-%s'%(request.session['Caller'][:3], #request.session['Caller'][3:6], request.session['Caller'][6:]), 'caller': '%s' % (request.session['ivr_makeRecording_callbacknumber']), }) else: if ('ivr_no_pound' in request.session and request.session['ivr_no_pound'] == True): body = _("You have a new call from DoctorCom. Caller hung up from " "CONFIRMED number %s") % (request.session['ivr_makeRecording_callbacknumber']) else: body = _("You have a new call from DoctorCom. Caller hung up " "from unconfirmed number %s") % \ (request.session['ivr_makeRecording_callbacknumber']) # TODO: we do nothing with body, sms send? # if pager number is entered, also page call back number r = twilio.Response() r.append(tts(_('Your message has been sent. Good bye'))) r.append(twilio.Hangup()) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) if ('ivr_makeRecording_callbacknumber' in request.session): request.session['ivr_makeRecording_prompt'] = tts(_('Please say your message ' 'for the doctor on call after the beep. Press pound when finished.')) request.session['ivr_makeRecording_maxLength'] = 600 # 10 minutes request.session['ivr_makeRecording_leadSilence'] = 2 request.session['ivr_makeRecording_promptOnce'] = True request.session['ivr_makeRecording_returnOnHangup'] = \ 'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveUrgentMsg' request.session['ivr_call_stack'].append('PracticeIVR_LeaveUrgentMsg') request.session.modified = True # Pass off the recording action to the getRecording function. return getQuickRecording(request) #get call back number request.session['ivr_call_stack'].append('PracticeIVR_LeaveUrgentMsg') request.session['ivr_callback_returnOnHangup'] = \ 'MHLogin.DoctorCom.IVR.views_practice.PracticeIVR_LeaveUrgentMsg' return getCallBackNumber(request)
def test_make_twilio_request_headers(mock): url = "http://random/url" make_twilio_request("POST", url) mock.assert_called_with("POST", "http://random/url.json", headers=post_headers)
def get_active_call(mhphone, ctype=TYPE_CALLED): """ Helper to get active call, defaults to in_progress state. for ref: http://www.twilio.com/docs/api/2008-08-01/rest/call :param mhphone: the Doctorcom number we are querying :param ctype: call type is called, caller, or either :param status: 0 = Not Yet Dialed, 1 = In Progress, 2 = Complete, 3 = Busy, 4 = Application Error, 5 = No Answer :returns: call or None if no match found * note: status is changed to string in 2010 twilio API, since this is active calls, I've changed status to 'in_progress'. Flags does not exist; Called = To; Caller = From; """ if (settings.TWILIO_PHASE == 2): auth, uri, call = client.auth, client.account_uri, None status = "in-progress" usmhphone = _makeUSNumber(mhphone) try: if (ctype == TYPE_EITHER): params = '/Calls.json?Status=%s' % (status) else: if (ctype == TYPE_CALLED): params = '/Calls.json?Status=%s&To=%s' % (status, str(usmhphone)) else: params = '/Calls.json?Status=%s&From=%s' % (status, str(usmhphone)) # twilio req appends .json in wrong place when params and no headers resp = make_twilio_request( 'GET', uri + params, auth=auth, **{'headers': { 'Accept': 'application/json' }}) content = json.loads(resp.content) calls = content['calls'] logger.debug('get_active_call new params %s content %s calls %s' % (params, content, calls)) call = calls[0] if calls else None except (TwilioRestException, ValueError, KeyError) as err: logger.critical("Problems querying active call list: %s" % str(err)) else: auth, uri, call = client2008.auth, client2008.account_uri, None status = 1 try: params = '/Calls.json?Status=%d&Flags=4' % (status) if ctype == TYPE_EITHER \ else '/Calls.json?Status=%d&%s=%s' % (status, CTYPES[ctype], str(mhphone)) # twilio req appends .json in wrong place when params and no headers resp = make_twilio_request( 'GET', uri + params, auth=auth, **{'headers': { 'Accept': 'application/json' }}) content = json.loads(resp.content) logger.debug('get_active_call content %s ' % content) calls = content['TwilioResponse']['Calls'] fn = lambda call: mhphone in (call['Call']['Called'], call['Call']['Caller']) \ if ctype == TYPE_EITHER else mhphone in (call['Call'][CTYPES[ctype]], ) results = filter(fn, calls) if results: # get 1st match but should be only 1 if len(results) > 1: logger.error("Unexpected additional results for: %s" % str(mhphone)) call = results[0]['Call'] except (TwilioRestException, ValueError, KeyError) as err: logger.critical("Problems querying active call list: %s" % str(err)) return call
def PracticeIVR_Main(request): """ DEPRECATED - to be replaced by PracticeIVR_Main_New in views_practice_v2.py entry point when call comes in to doctor.com number associated with Practice """ #if the practice's answering service is not available called = request.POST['Called'] try: practice = PracticeLocation.objects.get(mdcom_phone=called) can_have_answering_service = practice.get_setting_attr('can_have_answering_service') if (not can_have_answering_service): r = twilio.Response() r.append(tts(_("I'm sorry, answering service is not available. Good bye."))) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) except MultipleObjectsReturned: raise Exception(_('Multiple Practice Locations have mdcom_phone %s') % (called,)) except ObjectDoesNotExist: raise Exception(_('No Practice Location have mdcom_phone %s') % (called,)) if (request.COOKIES and 'CallStatus' not in request.POST or request.POST['CallStatus'] == 'completed'): callSID = request.POST['CallSid'] try: if 'Duration' in request.POST: # if getrecording starts redirecting here on call completed, this becomes wrong # the 2008 api overloads 'Duration' for both call duration (minutes), and recording duration (seconds) log = callLog.objects.get(callSID=callSID) log.duration = int(request.POST['Duration']) log.save() else: auth, uri, = client.auth, client.account_uri resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth) content = json.loads(resp.content) log = callLog.objects.get(callSID=callSID) log.call_duration = content['TwilioResponse']['Call']['Duration'] log.save() except TwilioRestException as tre: logger.critical('Unable to get call status: %s' % tre.msg) except ObjectDoesNotExist as odne: logger.warning('Call log does not exist for sid: %s. Caller may have ' 'hung up shortly after Twilio starts call process.' % str(odne)) r = twilio.Response() return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) # ready to initialize this call, request.session[practice_id] in request stack practice = PracticeIVR_Init(request) ############################################################################ #see if this is office manager calling, if yes - set up #TO DO - MANAGERS CELL ALSO NEED TO BE ADDED, also if not set up yet if (request.session['practice_phone'] == request.REQUEST['Caller'] or practice.accessnumber_set.filter(number=request.REQUEST['Caller'])): # Great, now we know that this is the owner calling in. First up, check to # see if the user has a configuration file and that they've completed setup # it is stored with practice location, for now act like first time # Now, check to ensure that user voicemail configuration was completed successfully. if (not practice.config_complete): request.session['ivr_call_stack'].append('PracticeIVR_Main') request.session.modified = True return PracticeIVR_Setup(request) else: # config was complete, but call is made, ask if want to change settings # make sure they have pin for setting if (not 'authenticated' in request.session): request.session['ivr_call_stack'].append('PracticeIVR_Main') request.session.modified = True r = twilio.Response() return authenticateSession(request, r) request.session['ivr_call_stack'].append('PracticeIVR_Main') request.session.modified = True return PracticeIVR_TreeRoot(request) ############################################################################ #phone call from outside called, they want to call answering service #lets see if we open/close/or lunch - based on that we decide on call tree #get current time in timezone of our practice, use gmt and then make it into local, #calltime_local is the time we use. gmttz = timezone('GMT') mytz = timezone(practice.time_zone) calltime_gmt = datetime.now(gmttz) calltime_local = calltime_gmt.astimezone(mytz) request.session['calltime_local_string'] = calltime_local.strftime("%Y-%m-%d %H:%M:%S") request.session['calltime_local'] = calltime_local #if this is newly V2 and later set up practice, call new version of getting input if (practice.uses_original_answering_serice()): return PracticeIVR_CallerResponse(request) # old way else: return PracticeIVR_V2_CallerResponse(request) # V2 way
def ProviderIVR_LeaveMsg(request): """ Records a voicemail message. Arguments: request - The standard Django request argument request.session Keys: provider_id - The ID of the Provider user who owns this voicemail box. """ # TODO: Update this code so that users can hit the pound key to # pop back and log into their own voicemail box. provider = Provider.objects.get(id=request.session['provider_id']) config = None config_complete = provider.vm_config.count() == 1 and provider.vm_config.get().config_complete if (config_complete): config = provider.vm_config.get() if('CallStatus' in request.POST and request.POST['CallStatus'] == 'completed'): try: callSID = request.POST['CallSid'] auth, uri, = client.auth, client.account_uri resp = make_twilio_request('GET', uri + '/Calls/%s' % callSID, auth=auth) content = json.loads(resp.content) log = callLog.objects.get(callSID=callSID) log.call_duration = content['TwilioResponse']['Call']['Duration'] log.save() except: pass # this is really ugly, but letting this exception fall through # destroys a message analytics will pick up the duration later on if it's missing if ('ivr_makeRecording_recording' in request.session): provider_qs = Provider.objects.filter(mobile_phone=request.session['Caller']) if(provider_qs): request.session['ivr_makeRecording_callbacknumber'] = provider_qs[0].mdcom_phone subject = "Voice Mail from %s %s" % (provider_qs[0].first_name, provider_qs[0].last_name) else: request.session['ivr_makeRecording_callbacknumber'] = request.session['Caller'] subject = "Voice Mail from %s" % request.session['ivr_makeRecording_callbacknumber'] save_message(request, subject, [provider.user], None, "VM", False) request.session.pop('ivr_makeRecording_recording') r = twilio.Response() r.append(tts('Good bye')) r.append(twilio.Hangup()) return HttpResponse(str(r), mimetype=settings.TWILIO_RESPONSE_MIMETYPE) # Get the configuration file for this Provider if (not config_complete): # FIXME: # Probably not the best way to create the spoken number. We need to # break the number down so that there are spaces between each digit, and # so that there are commas after the third and sixth digits. if (not 'Called' in request.session or (not request.session['Called'])): # This will occur if click2call calls this function request.session['ivr_makeRecording_prompt'] = tts('The user is not available. ' 'Please leave a message after the beep. Press pound when finished for options.') else: number = request.session['Called'] spoken_number = [] [spoken_number.extend([i, ' ']) for i in number] spoken_number.pop() # drop the last element spoken_number.insert(5, ',') spoken_number.insert(12, ',') request.session['ivr_makeRecording_prompt'] = tts('The person at %s ' 'is not available. Please leave a message after the beep. Press pound ' 'when finished for options.' % (''.join(spoken_number),)) else: p = re.compile('http') if (p.match(config.greeting)): # This is a Twilio recording. request.session['ivr_makeRecording_prompt'] = twilio.Play(config.greeting) else: # TODO: raise Exception('Unimplemented playback of local files') request.session['ivr_makeRecording_maxLength'] = 120 # 2 minutes request.session['ivr_makeRecording_leadSilence'] = 2 request.session['ivr_makeRecording_promptOnce'] = True request.session['ivr_makeRecording_returnOnHangup'] = \ 'MHLogin.DoctorCom.IVR.views_provider.ProviderIVR_LeaveMsg' request.session['ivr_call_stack'].append('ProviderIVR_LeaveMsg') request.session.modified = True # Pass off the recording action to the getRecording function. return getRecording(request)