def update_callrequest(callrequest, opt_hangup_cause): #Only the aleg will update the subscriber status / Bleg is only recorded #Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause callrequest.save() callrequest.subscriber.save() debug_query(24)
def update_callrequest(callrequest, opt_hangup_cause): # Only the aleg will update the subscriber status / Bleg is only recorded # Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause callrequest.save() callrequest.subscriber.save() debug_query(24)
def process_callevent(record): """ Process the callevent, this tasks will: - Retrieve the callrequest using either callrequest_id or request_uuid - create the voipcall, and save different data """ # TODO: add method in utils parse_callevent app_type = 'campaign' event_name = record[1] body = record[2] job_uuid = record[3] call_uuid = record[4] # used_gateway_id = record[5] callrequest_id = record[6] alarm_request_id = record[7] callerid = record[8] phonenumber = record[9] duration = record[10] billsec = record[11] hangup_cause = record[12] hangup_cause_q850 = record[13] starting_date = record[14] amd_status = record[17] leg = record[18] if event_name == 'BACKGROUND_JOB': # hangup cause come from body hangup_cause = body[5:] if hangup_cause == '': hangup_cause = body[5:] request_uuid = job_uuid opt_hangup_cause = hangup_cause debug_query(22) try: if callrequest_id == 0: callrequest = Callrequest.objects \ .select_related('aleg_gateway', 'subscriber', 'campaign') \ .get(request_uuid=request_uuid.strip(' \t\n\r')) else: # mainly coming here callrequest = Callrequest.objects \ .select_related('aleg_gateway', 'subscriber', 'campaign') \ .get(id=callrequest_id) except: logger.error("Cannot find Callrequest job_uuid : %s" % job_uuid) return True if callrequest.alarm_request_id: app_type = 'alarm' alarm_req = AlarmRequest.objects.get(pk=callrequest.alarm_request_id) # Overwrite alarm_request_id as this is equal to 0 when call fails alarm_request_id = callrequest.alarm_request_id logger.debug("Find Callrequest id : %d" % callrequest.id) debug_query(23) if leg == 'aleg' and app_type == 'campaign': # Update callrequest # update_callrequest.delay(callrequest, opt_hangup_cause) # Disabled above tasks to reduce amount of tasks # Only the aleg will update the subscriber status / Bleg is only recorded # Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause # ... callrequest.save() callrequest.subscriber.save() debug_query(24) elif leg == 'aleg' and app_type == 'alarm': try: caluser_profile = CalendarUserProfile.objects.get( user=alarm_req.alarm.event.creator) except CalendarUserProfile.DoesNotExist: logger.error("Error retrieving CalendarUserProfile") return False if opt_hangup_cause == 'NORMAL_CLEARING' and \ amd_status == 'machine' and \ caluser_profile.calendar_setting.voicemail and \ caluser_profile.calendar_setting.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY: # Call reached AMD callrequest.status = CALLREQUEST_STATUS.FAILURE alarm_req.status = ALARMREQUEST_STATUS.FAILURE alarm_req.alarm.status = ALARM_STATUS.FAILURE elif opt_hangup_cause == 'NORMAL_CLEARING': # Call is successful callrequest.status = CALLREQUEST_STATUS.SUCCESS alarm_req.status = ALARMREQUEST_STATUS.SUCCESS alarm_req.duration = duration alarm_req.alarm.status = ALARM_STATUS.SUCCESS else: # Call failed callrequest.status = CALLREQUEST_STATUS.FAILURE alarm_req.status = ALARMREQUEST_STATUS.FAILURE alarm_req.alarm.status = ALARM_STATUS.FAILURE callrequest.hangup_cause = opt_hangup_cause callrequest.save() alarm_req.save() alarm_req.alarm.save() debug_query(24) if call_uuid == '': call_uuid = job_uuid if callerid == '': callerid = callrequest.callerid if phonenumber == '': phonenumber = callrequest.phone_number # Create those in Bulk - add in a buffer until reach certain number # buff_voipcall.save( # obj_callrequest=callrequest, # request_uuid=request_uuid, # leg=leg, # hangup_cause=opt_hangup_cause, # hangup_cause_q850=hangup_cause_q850, # callerid=callerid, # phonenumber=phonenumber, # starting_date=starting_date, # call_uuid=call_uuid, # duration=duration, # billsec=billsec, # amd_status=amd_status) # debug_query(25) voipcall_save(callrequest=callrequest, request_uuid=request_uuid, leg=leg, hangup_cause=opt_hangup_cause, hangup_cause_q850=hangup_cause_q850, callerid=callerid, phonenumber=phonenumber, starting_date=starting_date, call_uuid=call_uuid, duration=duration, billsec=billsec, amd_status=amd_status) # If the call failed we will check if we want to make a retry call # Add condition to retry when it s machine and we want to reach a human if (app_type == 'campaign' and opt_hangup_cause != 'NORMAL_CLEARING' and callrequest.call_type == CALLREQUEST_TYPE.ALLOW_RETRY) or \ (app_type == 'campaign' and amd_status == 'machine' and callrequest.campaign.voicemail and callrequest.campaign.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): # Update to Retry Done callrequest.call_type = CALLREQUEST_TYPE.RETRY_DONE callrequest.save() debug_query(26) # check if we are allowed to retry on failure if ((callrequest.subscriber.count_attempt - 1) >= callrequest.campaign.maxretry or not callrequest.campaign.maxretry): logger.error("Not allowed retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Check here if we should try for completion check_retrycall_completion(callrequest) debug_query(28) else: # Allowed Retry logger.error("Allowed Retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Create new callrequest, Assign parent_callrequest, # Change callrequest_type & num_attempt new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, timeout=callrequest.timeout, subscriber_id=callrequest.subscriber_id) new_callrequest.save() # NOTE : implement a PID algorithm second_towait = callrequest.campaign.intervalretry debug_query(29) logger.debug("Init Retry CallRequest in %d seconds" % second_towait) init_callrequest.apply_async(args=[ new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration ], countdown=second_towait) elif app_type == 'campaign': # The Call is Answered and it's a campaign call logger.info("Check for completion call") # Check if we should relaunch a new call to achieve completion check_retrycall_completion(callrequest) elif (opt_hangup_cause != 'NORMAL_CLEARING' and app_type == 'alarm') or \ (amd_status == 'machine' and app_type == 'alarm' and caluser_profile.calendar_setting.voicemail and caluser_profile.calendar_setting.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): # Alarm callrequest failed or Alarm callrequest reached voicemail logger.info("Check to retry alarm") check_retry_alarm(alarm_request_id) else: logger.info("Retry: No matching conditions")
def run(self, campaign_id): """ This task retrieves the next outbound call to be made for a given campaign, and will create a new callrequest and schedule a task to process those calls **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: pending_call_processing = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects\ .select_related('user__userprofile__dialersetting', 'aleg_gateway', 'content_type')\ .get(id=campaign_id) except: logger.error("Can't find this campaign") return False # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) #TODO: move this logic of setting call_type after CallRequest post_save # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry try: obj_campaign.user.userprofile.dialersetting if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY except ObjectDoesNotExist: logger.error("Can't find user's dialersetting") return False debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records if settings.HEARTBEAT_MIN == 1: # 1 task per minute callfrequency = frequency # task run only once per minute, so we can assign frequency else: callfrequency = int(frequency / settings.HEARTBEAT_MIN) + 1 # 1000 per minutes #callfrequency = int(frequency) + 1 # 1000 per minutes (list_subscriber, no_subscriber) = obj_campaign\ .get_pending_subscriber_update(callfrequency, SUBSCRIBER_STATUS.IN_PROCESS) logger.info("##subscriber=%d campaign_id=%d callfreq=%d freq=%d" % (no_subscriber, campaign_id, callfrequency, frequency)) debug_query(3) if no_subscriber == 0: return False # Set time to wait for balanced dispatching of calls time_to_wait = (60.0 / settings.HEARTBEAT_MIN) / no_subscriber count = 0 loopnow = datetime.utcnow() loopnow + timedelta(seconds=1.55) for elem_camp_subscriber in list_subscriber: # Loop on Subscriber and start the initcall's task count = count + 1 second_towait = floor(count * time_to_wait) # ms_addtowait now used anymore, replaced by async eta ms_addtowait = (count * time_to_wait) - second_towait eta_delta = loopnow + timedelta(seconds=(count * time_to_wait)) # as we use eta_delta ms_addtowait is set to 0 ms_addtowait = 0 logger.info("Init CallRequest in %d seconds (cmpg:%d,subscr:%d:eta_delta:%s)" % (second_towait, campaign_id, elem_camp_subscriber.id, eta_delta)) phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) #Verify that the contact is authorized if not obj_campaign.is_authorized_contact(obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue #Verify that the contact is not in the DNC list if obj_campaign.dnc: res_dnc = DNCContact.objects.filter(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) if res_dnc: logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue else: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) #TODO: idea to speed up, create bluck of 10(Y) and then send a list # of callrequest_id to init_callrequest # Create Callrequest new_callrequest = Callrequest( status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.utcnow().replace(tzinfo=utc), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, caller_name=obj_campaign.caller_name, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber) new_callrequest.save() debug_query(6) second_towait = second_towait + settings.DELAY_OUTBOUND init_callrequest.apply_async( args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait], # countdown=second_towait) eta=eta_delta) # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async(args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait], countdown=1) debug_query(7) return True
def run(self, campaign_id): """ This will execute the outbound calls in the campaign **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: spool_pending_call = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects.select_related('user__userprofile__dialersetting', 'aleg_gateway', 'content_type').get(id=campaign_id) except: logger.error("Can't find this campaign") return False # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry if obj_campaign.user.userprofile.dialersetting: if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records if frequency >= 10: callfrequency = int(frequency / DIV_MIN) + 1 # 1000 per minutes 101 #callfrequency = int(frequency) + 1 # 1000 per minutes 101 else: callfrequency = frequency (list_subscriber, no_subscriber) = obj_campaign.get_pending_subscriber_update(callfrequency, SUBSCRIBER_STATUS.IN_PROCESS) logger.info("##subscriber=%d campaign_id=%d callfrequency=%d frequency=%d" % (no_subscriber, campaign_id, callfrequency, frequency)) debug_query(3) if no_subscriber == 0: return False # Set time to wait for balanced dispatching of calls #time_to_wait = int(60 / DIV_MIN) / no_subscriber time_to_wait = 6.0 / no_subscriber count = 0 for elem_camp_subscriber in list_subscriber: """Loop on Subscriber and start the initcall task""" count = count + 1 second_towait = floor(count * time_to_wait) ms_addtowait = (count * time_to_wait) - second_towait logger.info("Init CallRequest in %d seconds (cmpg:%d,subscriber:%d)" % (second_towait, campaign_id, elem_camp_subscriber.id)) phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) #Verify that the contact is authorized if not obj_campaign.is_authorized_contact(obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True #Verify that the contact is not in the DNC list if obj_campaign.dnc: res_dnc = DNCContact.objects.filter(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) if res_dnc: logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True else: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) #TODO: idea to speed up, create bluck of 10(Y) and then send a list of callrequest_id to init_callrequest # Create a Callrequest Instance to track the call task new_callrequest = Callrequest( status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.now(), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber) new_callrequest.save() debug_query(6) init_callrequest.apply_async( args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait], countdown=second_towait) # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async(args=[new_callrequest_id, obj_campaign_id], countdown=1) debug_query(7) return True
def init_callrequest(callrequest_id, campaign_id, callmaxduration, ms_addtowait=0, alarm_request_id=None): """ This task read the callrequest, update it as 'In Process' then proceed on the call outbound, using the different call engine supported **Attributes**: * ``callrequest_id`` - Callrequest ID * ``campaign_id`` - Campaign ID * ``callmaxduration`` - Max duration * ``ms_addtowait`` - Milliseconds to wait before outbounding the call """ outbound_failure = False subscriber_id = None contact_id = None debug_query(8) if ms_addtowait > 0: sleep(ms_addtowait) #Survey Call or Alarm Call if campaign_id: #TODO: use only https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.only obj_callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'user__userprofile', 'subscriber', 'campaign').get(id=callrequest_id) subscriber_id = obj_callrequest.subscriber_id contact_id = obj_callrequest.subscriber.contact_id elif alarm_request_id: obj_callrequest = Callrequest.objects.select_related('aleg_gateway', 'user__userprofile').get(id=callrequest_id) alarm_request_id = obj_callrequest.alarm_request_id else: logger.info("TASK :: init_callrequest, wrong campaign_id & alarm_request_id") return False debug_query(9) logger.info("TASK :: init_callrequest - status:%s;cmpg:%s;alarm:%s" % (obj_callrequest.status, campaign_id, alarm_request_id)) # TODO: move method prepare_phonenumber into the model gateway #obj_callrequest.aleg_gatewayprepare_phonenumber() dialout_phone_number = prepare_phonenumber( obj_callrequest.phone_number, obj_callrequest.aleg_gateway.addprefix, obj_callrequest.aleg_gateway.removeprefix, obj_callrequest.aleg_gateway.status) if not dialout_phone_number: logger.info("Error with dialout_phone_number - phone_number:%s" % (obj_callrequest.phone_number)) return False else: logger.debug("dialout_phone_number : %s" % dialout_phone_number) debug_query(10) if settings.DIALERDEBUG: dialout_phone_number = settings.DIALERDEBUG_PHONENUMBER #Retrieve the Gateway for the A-Leg gateways = obj_callrequest.aleg_gateway.gateways gateway_id = obj_callrequest.aleg_gateway.id #gateway_codecs / gateway_retries gateway_timeouts = obj_callrequest.aleg_gateway.gateway_timeouts if not gateway_timeouts: gateway_timeouts = '60' originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string debug_query(11) #Sanitize gateways gateways = gateways.strip() if gateways[-1] != '/': gateways = gateways + '/' originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string if obj_callrequest.user.userprofile and obj_callrequest.user.userprofile.accountcode: originate_dial_string = originate_dial_string + ',accountcode=' + \ str(obj_callrequest.user.userprofile.accountcode) debug_query(12) if settings.NEWFIES_DIALER_ENGINE.lower() == 'esl': try: args_list = [] send_digits = False time_limit = callmaxduration # To wait before sending DTMF to the extension, you can add leading 'w' # characters. # Each 'w' character waits 0.5 seconds instead of sending a digit. # Each 'W' character waits 1.0 seconds instead of sending a digit. # You can also add the tone duration in ms by appending @[duration] after string. # Eg. 1w2w3@1000 check_senddigit = dialout_phone_number.partition('w') if check_senddigit[1] == 'w': send_digits = check_senddigit[1] + check_senddigit[2] dialout_phone_number = check_senddigit[0] args_list.append("origination_caller_id_number=%s" % obj_callrequest.callerid) args_list.append("origination_caller_id_name='%s'" % obj_callrequest.caller_name) #Add App Vars args_list.append("campaign_id=%s,subscriber_id=%s,alarm_request_id=%s,used_gateway_id=%s,callrequest_id=%s,contact_id=%s" % (campaign_id, subscriber_id, alarm_request_id, gateway_id, obj_callrequest.id, contact_id)) args_list.append(originate_dial_string) #Call Vars callvars = "bridge_early_media=true,originate_timeout=%s,newfiesdialer=true,leg_type=1" % \ (gateway_timeouts, ) args_list.append(callvars) #Default Test hangup_on_ring = '' send_preanswer = False # set hangup_on_ring try: hangup_on_ring = int(hangup_on_ring) except ValueError: hangup_on_ring = -1 exec_on_media = 1 if hangup_on_ring >= 0: args_list.append("execute_on_media_%d='sched_hangup +%d ORIGINATOR_CANCEL'" % (exec_on_media, hangup_on_ring)) exec_on_media += 1 #TODO: look and test http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_queue_dtmf # Send digits if send_digits: if send_preanswer: args_list.append("execute_on_media_%d='send_dtmf %s'" % (exec_on_media, send_digits)) exec_on_media += 1 else: args_list.append("execute_on_answer='send_dtmf %s'" % send_digits) # Set time_limit try: time_limit = int(time_limit) except ValueError: time_limit = -1 #TODO : Fix time_limit - maybe implement this into Lua # if time_limit > 0: # # create sched_hangup_id # sched_hangup_id = str(uuid1()) # # create a new request uuid # request_uuid = str(uuid1()) # args_list.append("api_on_answer_1='sched_api +%d %s hupall ALLOTTED_TIMEOUT'" # % (time_limit, sched_hangup_id)) # build originate string args_str = ','.join(args_list) #DEBUG #settings.ESL_SCRIPT = '&playback(/usr/local/freeswitch/sounds/en/us/callie/voicemail/8000/vm-record_greeting.wav)' if settings.DIALERDEBUG: dial_command = "originate {%s}user/areski '%s'" % (args_str, settings.ESL_SCRIPT) else: dial_command = "originate {%s}%s%s '%s'" % \ (args_str, gateways, dialout_phone_number, settings.ESL_SCRIPT) # originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=10}user/areski &playback(/tmp/myfile.wav) # dial = "originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=,newfiesdialer=true,used_gateway_id=1,callrequest_id=38,leg_type=1,origination_caller_id_number=234234234,origination_caller_id_name=234234,effective_caller_id_number=234234234,effective_caller_id_name=234234,}user//1000 '&lua(/usr/share/newfies-lua/newfies.lua)'" logger.warn('dial_command : %s' % dial_command) request_uuid = dial_out(dial_command) debug_query(14) if request_uuid and len(request_uuid) > 0 and request_uuid[:5] == 'error': outbound_failure = True debug_query(13) except: raise logger.error('error : ESL') outbound_failure = True logger.debug('Received RequestUUID :> %s' % request_uuid) else: logger.error('No other method supported!') obj_callrequest.status = CALLREQUEST_STATUS.FAILURE obj_callrequest.save() #ADD if alarm_request_id update AlarmRequest return False #Survey Call or Alarm Call if campaign_id: #Update Subscriber if not obj_callrequest.subscriber.count_attempt: obj_callrequest.subscriber.count_attempt = 1 else: obj_callrequest.subscriber.count_attempt = obj_callrequest.subscriber.count_attempt + 1 obj_callrequest.subscriber.last_attempt = datetime.utcnow().replace(tzinfo=utc) #check if the outbound call failed then update Subscriber if outbound_failure: obj_callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL obj_callrequest.subscriber.save() elif alarm_request_id: if outbound_failure: check_retry_alarm(alarm_request_id) #Update CallRequest Object obj_callrequest.request_uuid = request_uuid #check if the outbound call failed if outbound_failure: obj_callrequest.status = CALLREQUEST_STATUS.FAILURE else: obj_callrequest.status = CALLREQUEST_STATUS.CALLING obj_callrequest.save() debug_query(14) return True
def callevent_processing(): """ Retrieve callevents and process them call_event table is created by listener.lua CREATE TABLE if not exists call_event ( id serial NOT NULL PRIMARY KEY, event_name varchar(200) NOT NULL, body varchar(200) NOT NULL, job_uuid varchar(200), call_uuid varchar(200) NOT NULL, used_gateway_id integer, callrequest_id integer, alarm_request_id integer, callerid varchar(200), phonenumber varchar(200), duration integer DEFAULT 0, billsec integer DEFAULT 0, hangup_cause varchar(40), hangup_cause_q850 varchar(10), amd_status varchar(40), starting_date timestamp with time zone, status smallint, leg smallint, created_date timestamp with time zone NOT NULL ); CREATE INDEX call_event_idx_status ON call_event (status); --CREATE INDEX call_event_idx_date ON call_event (created_date); --CREATE INDEX call_event_idx_uuid ON call_event (call_uuid); """ debug_query(20) cursor = connection.cursor() #TODO (Areski) #Replace this for ORM with select_for_update or transaction sql_statement = "SELECT id, event_name, body, job_uuid, call_uuid, used_gateway_id, " \ "callrequest_id, alarm_request_id, callerid, phonenumber, duration, billsec, hangup_cause, " \ "hangup_cause_q850, starting_date, status, created_date, amd_status, leg " \ "FROM call_event WHERE status=1 LIMIT 1000 OFFSET 0" cursor.execute(sql_statement) row = cursor.fetchall() debug_query(21) # buff_voipcall = BufferVoIPCall() for record in row: call_event_id = record[0] event_name = record[1] #Update Call Event sql_statement = "UPDATE call_event SET status=2 WHERE id=%d" % call_event_id cursor.execute(sql_statement) logger.info("Processing Event : %s" % event_name) process_callevent.delay(record) debug_query(30) # buff_voipcall.commit() # debug_query(31) logger.debug('End Loop : callevent_processing')
def run(self, campaign_id): """ This will execute the outbound calls in the campaign **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: CheckPendingcall = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects.select_related('user__userprofile__dialersetting', 'aleg_gateway', 'content_type').get(id=campaign_id) except: logger.error('Can\'t find this campaign') return False # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry if obj_campaign.user.userprofile.dialersetting: if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records list_subscriber = obj_campaign.get_pending_subscriber_update( frequency, SUBSCRIBER_STATUS.IN_PROCESS ) if list_subscriber: no_subscriber = list_subscriber.count() else: no_subscriber = 0 logger.info("campaign_id=%d #Subscriber: %d" % (campaign_id, no_subscriber)) debug_query(3) if no_subscriber == 0: return False if no_subscriber == 1: # Not many subscriber do a fast dial time_to_wait = 1.0 elif no_subscriber <= 10: # Not many subscriber do a fast dial time_to_wait = 6.0 else: # Set time to wait for balanced dispatching of calls time_to_wait = 60.0 / no_subscriber count = 0 for elem_camp_subscriber in list_subscriber: """Loop on Subscriber and start the initcall task""" count = count + 1 logger.info("Add CallRequest for Subscriber (%d) & wait (%s) " % (elem_camp_subscriber.id, str(time_to_wait))) phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) #Verify that the contact is authorized if not obj_campaign.is_authorized_contact(obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True #Verify that the contact is not in the DNC list if obj_campaign.dnc: try: DNCContact.objects.get(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True except DNCContact.DoesNotExist: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) # Create a Callrequest Instance to track the call task new_callrequest = Callrequest( status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.now(), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber) new_callrequest.save() debug_query(6) second_towait = ceil(count * time_to_wait) logger.info("Init CallRequest in %d seconds (cmpg:%d)" % (second_towait, campaign_id)) init_callrequest.apply_async( args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration], countdown=second_towait) # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async(args=[new_callrequest_id, obj_campaign_id], countdown=1) debug_query(7) return True
def run(self, campaign_id): """ This task retrieves the next outbound call to be made for a given campaign, and will create a new callrequest and schedule a task to process those calls **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: pending_call_processing = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects\ .select_related('user__userprofile__dialersetting', 'aleg_gateway', 'content_type')\ .get(id=campaign_id) except: logger.error("Can't find this campaign") return False # Ensure the content_type become "survey" when campagin starts if not obj_campaign.has_been_started: # change has_been_started flag obj_campaign.has_been_started = True obj_campaign.save() if obj_campaign.content_type.model == 'survey_template': # Copy survey survey_template = Survey_template.objects.get( user=obj_campaign.user, pk=obj_campaign.object_id) survey_template.copy_survey_template(obj_campaign.id) collect_subscriber.delay(obj_campaign.id) # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) # TODO: move this logic of setting call_type after CallRequest post_save # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry try: obj_campaign.user.userprofile.dialersetting if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY except ObjectDoesNotExist: logger.error("Can't find user's dialersetting") return False debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records if settings.HEARTBEAT_MIN == 1: # 1 task per minute callfrequency = frequency # task run only once per minute, so we can assign frequency else: callfrequency = int( frequency / settings.HEARTBEAT_MIN) + 1 # 1000 per minutes # callfrequency = int(frequency) + 1 # 1000 per minutes (list_subscriber, no_subscriber) = obj_campaign\ .get_pending_subscriber_update(callfrequency, SUBSCRIBER_STATUS.IN_PROCESS) logger.info("##subscriber=%d campaign_id=%d callfreq=%d freq=%d" % (no_subscriber, campaign_id, callfrequency, frequency)) debug_query(3) if no_subscriber == 0: return False list_cr = [] bulk_record = [] # this is used to tag and retrieve the id that are inserted bulk_uuid = str(uuid1()) for elem_camp_subscriber in list_subscriber: phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) # Verify that the contact is authorized if not obj_campaign.is_authorized_contact( obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue # Verify that the contact is not in the DNC list if obj_campaign.dnc: res_dnc = DNCContact.objects.filter(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) if res_dnc: logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue else: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) bulk_record.append( Callrequest(status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.utcnow().replace(tzinfo=utc), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, caller_name=obj_campaign.caller_name, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber, request_uuid=bulk_uuid)) debug_query(6) # Create Callrequests in Bulk logger.info("Bulk Create CallRequest => %d" % (len(bulk_record))) Callrequest.objects.bulk_create(bulk_record) # Set time to wait for balanced dispatching of calls time_to_wait = (60.0 / settings.HEARTBEAT_MIN) / no_subscriber count = 0 loopnow = datetime.utcnow() loopnow + timedelta(seconds=1.55) # Retrienve the one we just created list_cr = Callrequest.objects.filter(request_uuid=bulk_uuid).all() for cr in list_cr: # Loop on Subscriber and start the initcall's task count = count + 1 second_towait = floor(count * time_to_wait) # ms_addtowait now used anymore, replaced by async eta ms_addtowait = (count * time_to_wait) - second_towait eta_delta = loopnow + timedelta(seconds=(count * time_to_wait)) # as we use eta_delta ms_addtowait is set to 0 ms_addtowait = 0 logger.info( "Init CallRequest in %d seconds (cmpg:%d,subscr:%d:eta_delta:%s)" % (second_towait, campaign_id, elem_camp_subscriber.id, eta_delta)) init_callrequest.apply_async( args=[ cr.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait ], # countdown=second_towait) eta=eta_delta) second_towait = second_towait + settings.DELAY_OUTBOUND # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async( # args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait], # countdown=1) debug_query(7) return True
def init_callrequest(callrequest_id, campaign_id, callmaxduration, ms_addtowait=0): """ This task read the callrequest, update it as 'In Process' then proceed on the call outbound, using the different call engine supported **Attributes**: * ``callrequest_id`` - Callrequest ID * ``campaign_id`` - Campaign ID * ``callmaxduration`` - Max duration * ``ms_addtowait`` - Milliseconds to wait before outbounding the call """ debug_query(8) if ms_addtowait > 0: sleep(ms_addtowait) #Get CallRequest object #use only https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.only # test2 = Callrequest.objects.only('id', 'callerid', 'phone_number', 'status', \ # 'request_uuid', 'campaign__id', 'subscriber__id', \ # 'aleg_gateway__id', 'aleg_gateway__addprefix', 'aleg_gateway__removeprefix', 'aleg_gateway__status', \ # 'aleg_gateway__gateways', 'aleg_gateway__gateway_timeouts', 'aleg_gateway__originate_dial_string', \ # 'user__userprofile__accountcode', 'campaign__caller_name', \ # 'subscriber__id', 'subscriber__contact__id', 'subscriber__count_attempt', 'subscriber__last_attempt')\ # .select_related('aleg_gateway', 'user__userprofile', 'subscriber', 'campaign').get(id=4767) obj_callrequest = Callrequest.objects.select_related( 'aleg_gateway', 'user__userprofile', 'subscriber', 'campaign').get(id=callrequest_id) debug_query(9) logger.info("TASK :: init_callrequest - status:%s;cmpg:%d" % (str(obj_callrequest.status), campaign_id)) debug_query(10) if obj_callrequest.aleg_gateway: dialout_phone_number = prepare_phonenumber( obj_callrequest.phone_number, obj_callrequest.aleg_gateway.addprefix, obj_callrequest.aleg_gateway.removeprefix, obj_callrequest.aleg_gateway.status) else: dialout_phone_number = obj_callrequest.phone_number logger.debug("dialout_phone_number : %s" % dialout_phone_number) if not dialout_phone_number: logger.info( "Error with dialout_phone_number - phone_number:%s;cmpg:%d" % (str(obj_callrequest.phone_number), campaign_id)) return False debug_query(11) if settings.DIALERDEBUG: dialout_phone_number = settings.DIALERDEBUG_PHONENUMBER #Retrieve the Gateway for the A-Leg gateways = obj_callrequest.aleg_gateway.gateways gateway_id = obj_callrequest.aleg_gateway.id #gateway_codecs = obj_callrequest.aleg_gateway.gateway_codecs #gateway_retries = obj_callrequest.aleg_gateway.gateway_retries gateway_timeouts = obj_callrequest.aleg_gateway.gateway_timeouts originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string debug_query(12) #Sanitize gateways gateways = gateways.strip() if gateways[-1] != '/': gateways = gateways + '/' originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string if (obj_callrequest.user.userprofile.accountcode and obj_callrequest.user.userprofile.accountcode > 0): originate_dial_string = originate_dial_string + \ ',accountcode=' + str(obj_callrequest.user.userprofile.accountcode) debug_query(13) outbound_failure = False #Send Call to API #http://ask.github.com/celery/userguide/remote-tasks.html """ #this could be needed if we want to call a different API / Twilio import httplib, urllib params = urllib.urlencode({'From': '900900000', 'To': '1000',}) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} conn = httplib.HTTPConnection("127.0.0.1:8000") conn.request("POST", "/api/dialer_cdr/testcall/", params, headers) response = conn.getresponse() print response.status, response.reason data = response.read() conn.close() """ if settings.NEWFIES_DIALER_ENGINE.lower() == 'esl': try: args_list = [] send_digits = False time_limit = callmaxduration # To wait before sending DTMF to the extension, you can add leading 'w' # characters. # Each 'w' character waits 0.5 seconds instead of sending a digit. # Each 'W' character waits 1.0 seconds instead of sending a digit. # You can also add the tone duration in ms by appending @[duration] after string. # Eg. 1w2w3@1000 check_senddigit = dialout_phone_number.partition('w') if check_senddigit[1] == 'w': send_digits = check_senddigit[1] + check_senddigit[2] dialout_phone_number = check_senddigit[0] args_list.append("origination_caller_id_number=%s" % obj_callrequest.callerid) if obj_callrequest.campaign.caller_name: args_list.append("origination_caller_id_name='%s'" % obj_callrequest.campaign.caller_name) #Add App Vars args_list.append( "campaign_id=%d,subscriber_id=%d,used_gateway_id=%s,callrequest_id=%s,contact_id=%s" % (obj_callrequest.campaign_id, obj_callrequest.subscriber_id, gateway_id, obj_callrequest.id, obj_callrequest.subscriber.contact_id)) args_list.append(originate_dial_string) #Call Vars callvars = "bridge_early_media=true,originate_timeout=%s,newfiesdialer=true,leg_type=1" % \ (gateway_timeouts, ) args_list.append(callvars) #Default Test hangup_on_ring = '' send_preanswer = False # set hangup_on_ring try: hangup_on_ring = int(hangup_on_ring) except ValueError: hangup_on_ring = -1 exec_on_media = 1 if hangup_on_ring >= 0: args_list.append( "execute_on_media_%d='sched_hangup +%d ORIGINATOR_CANCEL'" % (exec_on_media, hangup_on_ring)) exec_on_media += 1 #TODO: look and test http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_queue_dtmf # Send digits if send_digits: if send_preanswer: args_list.append("execute_on_media_%d='send_dtmf %s'" % (exec_on_media, send_digits)) exec_on_media += 1 else: args_list.append("execute_on_answer='send_dtmf %s'" % send_digits) # set time_limit try: time_limit = int(time_limit) except ValueError: time_limit = -1 #TODO : Fix time_limit - maybe implement this into Lua # if time_limit > 0: # # create sched_hangup_id # sched_hangup_id = str(uuid1()) # # create a new request uuid # request_uuid = str(uuid1()) # args_list.append("api_on_answer_1='sched_api +%d %s hupall ALLOTTED_TIMEOUT'" # % (time_limit, sched_hangup_id)) # build originate string args_str = ','.join(args_list) #DEBUG #settings.ESL_SCRIPT = '&playback(/usr/local/freeswitch/sounds/en/us/callie/voicemail/8000/vm-record_greeting.wav)' dial_command = "originate {%s}%s%s '%s'" % \ (args_str, gateways, dialout_phone_number, settings.ESL_SCRIPT) # originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=10}user/areski &playback(/tmp/myfile.wav) # dial = "originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=,newfiesdialer=true,used_gateway_id=1,callrequest_id=38,leg_type=1,origination_caller_id_number=234234234,origination_caller_id_name=234234,effective_caller_id_number=234234234,effective_caller_id_name=234234,}user//1000 '&lua(/usr/share/newfies-lua/newfies.lua)'" request_uuid = esl_dialout(dial_command) debug_query(14) except: raise logger.error('error : ESL') outbound_failure = True return False logger.debug('Received RequestUUID :> ' + request_uuid) else: logger.error('No other method supported!') obj_callrequest.status = CALLREQUEST_STATUS.FAILURE obj_callrequest.save() return False #Update Subscriber if not obj_callrequest.subscriber.count_attempt: obj_callrequest.subscriber.count_attempt = 1 else: obj_callrequest.subscriber.count_attempt = obj_callrequest.subscriber.count_attempt + 1 obj_callrequest.subscriber.last_attempt = datetime.now() #check if the outbound call failed then update Subscriber if outbound_failure: obj_callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL obj_callrequest.subscriber.save() #Update CallRequest Object obj_callrequest.request_uuid = request_uuid #check if the outbound call failed if outbound_failure: obj_callrequest.status = CALLREQUEST_STATUS.FAILURE else: obj_callrequest.status = CALLREQUEST_STATUS.CALLING obj_callrequest.save() #lock to limit running process, do so per campaign #http://ask.github.com/celery/cookbook/tasks.html debug_query(15) return True
def init_callrequest(callrequest_id, campaign_id, callmaxduration): """ This task read the callrequest, update it as 'In Process' then proceed on the call outbound, using the different call engine supported **Attributes**: * ``callrequest_id`` - Callrequest ID """ debug_query(8) #Get CallRequest object # .only('id', 'callerid', 'phone_number', 'status', # 'request_uuid', 'campaign__id', 'subscriber__id', # 'aleg_gateway__id', 'aleg_gateway__addprefix', 'aleg_gateway__removeprefix', 'aleg_gateway__status', # 'aleg_gateway__gateways', 'aleg_gateway__gateway_timeouts', 'aleg_gateway__originate_dial_string', # 'user__userprofile__accountcode', 'campaign__caller_name', # 'subscriber__id', 'subscriber__contact__id', 'subscriber__count_attempt', 'subscriber__last_attempt')\ obj_callrequest = Callrequest.objects.select_related('aleg_gateway', 'user__userprofile', 'subscriber', 'campaign').get(id=callrequest_id) debug_query(9) logger.info("TASK :: init_callrequest - status:%s;cmpg:%d" % (str(obj_callrequest.status), campaign_id)) debug_query(10) if obj_callrequest.aleg_gateway: dialout_phone_number = prepare_phonenumber( obj_callrequest.phone_number, obj_callrequest.aleg_gateway.addprefix, obj_callrequest.aleg_gateway.removeprefix, obj_callrequest.aleg_gateway.status) else: dialout_phone_number = obj_callrequest.phone_number logger.debug("dialout_phone_number : %s" % dialout_phone_number) if not dialout_phone_number: logger.info("Error with dialout_phone_number - phone_number:%s;cmpg:%d" % (str(obj_callrequest.phone_number), campaign_id)) return False debug_query(11) if settings.DIALERDEBUG: dialout_phone_number = settings.DIALERDEBUG_PHONENUMBER #Retrieve the Gateway for the A-Leg gateways = obj_callrequest.aleg_gateway.gateways gateway_id = obj_callrequest.aleg_gateway.id #gateway_codecs = obj_callrequest.aleg_gateway.gateway_codecs #gateway_retries = obj_callrequest.aleg_gateway.gateway_retries gateway_timeouts = obj_callrequest.aleg_gateway.gateway_timeouts originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string debug_query(12) #Sanitize gateways gateways = gateways.strip() if gateways[-1] != '/': gateways = gateways + '/' originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string if (obj_callrequest.user.userprofile.accountcode and obj_callrequest.user.userprofile.accountcode > 0): originate_dial_string = originate_dial_string + \ ',accountcode=' + str(obj_callrequest.user.userprofile.accountcode) debug_query(13) outbound_failure = False #Send Call to API #http://ask.github.com/celery/userguide/remote-tasks.html """ #this could be needed if we want to call a different API / Twilio import httplib, urllib params = urllib.urlencode({'From': '900900000', 'To': '1000',}) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} conn = httplib.HTTPConnection("127.0.0.1:8000") conn.request("POST", "/api/dialer_cdr/testcall/", params, headers) response = conn.getresponse() print response.status, response.reason data = response.read() conn.close() """ if settings.NEWFIES_DIALER_ENGINE.lower() == 'esl': try: args_list = [] send_digits = False time_limit = callmaxduration # To wait before sending DTMF to the extension, you can add leading 'w' # characters. # Each 'w' character waits 0.5 seconds instead of sending a digit. # Each 'W' character waits 1.0 seconds instead of sending a digit. # You can also add the tone duration in ms by appending @[duration] after string. # Eg. 1w2w3@1000 check_senddigit = dialout_phone_number.partition('w') if check_senddigit[1] == 'w': send_digits = check_senddigit[1] + check_senddigit[2] dialout_phone_number = check_senddigit[0] args_list.append("origination_caller_id_number=%s" % obj_callrequest.callerid) if obj_callrequest.campaign.caller_name: args_list.append("origination_caller_id_name='%s'" % obj_callrequest.campaign.caller_name) #Add App Vars args_list.append("campaign_id=%d,subscriber_id=%d,used_gateway_id=%s,callrequest_id=%s,contact_id=%s" % (obj_callrequest.campaign_id, obj_callrequest.subscriber_id, gateway_id, obj_callrequest.id, obj_callrequest.subscriber.contact_id)) args_list.append(originate_dial_string) #Call Vars callvars = "bridge_early_media=true,originate_timeout=%s,newfiesdialer=true,leg_type=1" % \ (gateway_timeouts, ) args_list.append(callvars) #Default Test hangup_on_ring = '' send_preanswer = False # set hangup_on_ring try: hangup_on_ring = int(hangup_on_ring) except ValueError: hangup_on_ring = -1 exec_on_media = 1 if hangup_on_ring >= 0: args_list.append("execute_on_media_%d='sched_hangup +%d ORIGINATOR_CANCEL'" % (exec_on_media, hangup_on_ring)) exec_on_media += 1 #TODO: look and test http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_queue_dtmf # Send digits if send_digits: if send_preanswer: args_list.append("execute_on_media_%d='send_dtmf %s'" % (exec_on_media, send_digits)) exec_on_media += 1 else: args_list.append("execute_on_answer='send_dtmf %s'" % send_digits) # set time_limit try: time_limit = int(time_limit) except ValueError: time_limit = -1 #TODO : Fix time_limit - maybe implement this into Lua # if time_limit > 0: # # create sched_hangup_id # sched_hangup_id = str(uuid1()) # # create a new request uuid # request_uuid = str(uuid1()) # args_list.append("api_on_answer_1='sched_api +%d %s hupall ALLOTTED_TIMEOUT'" # % (time_limit, sched_hangup_id)) # build originate string args_str = ','.join(args_list) #DEBUG #settings.ESL_SCRIPT = '&playback(/usr/local/freeswitch/sounds/en/us/callie/voicemail/8000/vm-record_greeting.wav)' dial = "originate {%s}%s%s '%s'" % \ (args_str, gateways, dialout_phone_number, settings.ESL_SCRIPT) # originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=10}user/areski &playback(/tmp/myfile.wav) # dial = "originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=,newfiesdialer=true,used_gateway_id=1,callrequest_id=38,leg_type=1,origination_caller_id_number=234234234,origination_caller_id_name=234234,effective_caller_id_number=234234234,effective_caller_id_name=234234,}user//1000 '&lua(/usr/share/newfies-lua/newfies.lua)'" print dial import ESL c = ESL.ESLconnection(settings.ESL_HOSTNAME, settings.ESL_PORT, settings.ESL_SECRET) c.connected() ev = c.api("bgapi", str(dial)) c.disconnect() debug_query(14) if ev: result = ev.serialize() logger.debug(result) pos = result.find('Job-UUID:') if pos: request_uuid = result[pos + 10:pos + 46] else: request_uuid = 'error' else: request_uuid = 'error' except: raise logger.error('error : ESL') outbound_failure = True return False logger.info('Received RequestUUID :> ' + request_uuid) else: logger.error('No other method supported!') obj_callrequest.status = CALLREQUEST_STATUS.FAILURE obj_callrequest.save() return False #Update Subscriber if not obj_callrequest.subscriber.count_attempt: obj_callrequest.subscriber.count_attempt = 1 else: obj_callrequest.subscriber.count_attempt = obj_callrequest.subscriber.count_attempt + 1 obj_callrequest.subscriber.last_attempt = datetime.now() #check if the outbound call failed then update Subscriber if outbound_failure: obj_callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL obj_callrequest.subscriber.save() #Update CallRequest Object obj_callrequest.request_uuid = request_uuid #check if the outbound call failed if outbound_failure: obj_callrequest.status = CALLREQUEST_STATUS.FAILURE else: obj_callrequest.status = CALLREQUEST_STATUS.CALLING obj_callrequest.save() #lock to limit running process, do so per campaign #http://ask.github.com/celery/cookbook/tasks.html debug_query(15) return True
def check_callevent(): """ Check callevent call_event table is created by listener.lua CREATE TABLE if not exists call_event ( id serial NOT NULL PRIMARY KEY, event_name varchar(200) NOT NULL, body varchar(200) NOT NULL, job_uuid varchar(200), call_uuid varchar(200) NOT NULL, used_gateway_id integer, callrequest_id integer, callerid varchar(200), phonenumber varchar(200), duration integer DEFAULT 0, billsec integer DEFAULT 0, hangup_cause varchar(40), hangup_cause_q850 varchar(10), amd_status varchar(40), starting_date timestamp with time zone, status smallint, created_date timestamp with time zone NOT NULL ); CREATE INDEX call_event_idx_status ON call_event (status); --CREATE INDEX call_event_idx_date ON call_event (created_date); --CREATE INDEX call_event_idx_uuid ON call_event (call_uuid); """ debug_query(20) cursor = connection.cursor() #TODO (Areski) #Replace this for ORM with select_for_update or transaction sql_statement = "SELECT id, event_name, body, job_uuid, call_uuid, used_gateway_id, "\ "callrequest_id, callerid, phonenumber, duration, billsec, hangup_cause, "\ "hangup_cause_q850, starting_date, status, created_date, amd_status, leg "\ "FROM call_event WHERE status=1 LIMIT 1000 OFFSET 0" cursor.execute(sql_statement) row = cursor.fetchall() debug_query(21) # buff_voipcall = BufferVoIPCall() for record in row: call_event_id = record[0] event_name = record[1] #Update Call Event sql_statement = "UPDATE call_event SET status=2 WHERE id=%d" % call_event_id cursor.execute(sql_statement) logger.info("Processing Event : %s" % event_name) process_callevent.delay(record) debug_query(30) # buff_voipcall.commit() # debug_query(31) logger.debug('End Loop : check_callevent')
def handle_callevent(record): """ Handle the callevent, create the voipcall, and save different data """ event_name = record[1] body = record[2] job_uuid = record[3] call_uuid = record[4] #used_gateway_id = record[5] callrequest_id = record[6] callerid = record[7] phonenumber = record[8] duration = record[9] billsec = record[10] hangup_cause = record[11] hangup_cause_q850 = record[12] starting_date = record[13] amd_status = record[16] leg = record[17] if event_name == 'BACKGROUND_JOB': #hangup cause come from body hangup_cause = body[5:] # if event_name == 'CHANNEL_HANGUP_COMPLETE': # #hangup cause come from body # print(event_name) if hangup_cause == '': hangup_cause = body[5:] request_uuid = job_uuid opt_hangup_cause = hangup_cause debug_query(22) try: if callrequest_id == 0: callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'subscriber', 'campaign')\ .get(request_uuid=request_uuid.strip(' \t\n\r')) else: #mainly coming here callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'subscriber', 'campaign')\ .get(id=callrequest_id) except: logger.error("Cannot find Callrequest job_uuid : %s" % job_uuid) return True logger.debug("Find Callrequest id : %d" % callrequest.id) debug_query(23) if leg == 'aleg': #Update callrequest #update_callrequest.delay(callrequest, opt_hangup_cause) #Disabled above tasks to reduce amount of tasks #Only the aleg will update the subscriber status / Bleg is only recorded #Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause callrequest.save() callrequest.subscriber.save() debug_query(24) if call_uuid == '': call_uuid = job_uuid if callerid == '': callerid = callrequest.callerid if phonenumber == '': phonenumber = callrequest.phone_number #Create those in Bulk - add in a buffer until reach certain number # buff_voipcall.save( # obj_callrequest=callrequest, # request_uuid=request_uuid, # leg=leg, # hangup_cause=opt_hangup_cause, # hangup_cause_q850=hangup_cause_q850, # callerid=callerid, # phonenumber=phonenumber, # starting_date=starting_date, # call_uuid=call_uuid, # duration=duration, # billsec=billsec, # amd_status=amd_status) # debug_query(25) voipcall_save.delay(callrequest=callrequest, request_uuid=request_uuid, leg=leg, hangup_cause=opt_hangup_cause, hangup_cause_q850=hangup_cause_q850, callerid=callerid, phonenumber=phonenumber, starting_date=starting_date, call_uuid=call_uuid, duration=duration, billsec=billsec, amd_status=amd_status) #TODO: Move this to tasks? #If the call failed we will check if we want to make a retry call #Add condition to retry when it s machine and we want to reach a human if (opt_hangup_cause != 'NORMAL_CLEARING' and callrequest.call_type == CALLREQUEST_TYPE.ALLOW_RETRY) or \ (amd_status == 'machine' and callrequest.campaign.voicemail and callrequest.campaign.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): #Update to Retry Done callrequest.call_type = CALLREQUEST_TYPE.RETRY_DONE callrequest.save() debug_query(26) #check if we are allowed to retry on failure if ((callrequest.subscriber.count_attempt - 1) >= callrequest.campaign.maxretry or not callrequest.campaign.maxretry): logger.error("Not allowed retry - Maxretry (%d)" % callrequest.campaign.maxretry) #Check here if we should try for completion check_retrycall_completion(callrequest) debug_query(28) else: #Allowed Retry logger.error("Allowed Retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Create new callrequest, Assign parent_callrequest, # Change callrequest_type & num_attempt new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, timeout=callrequest.timeout, subscriber_id=callrequest.subscriber_id) new_callrequest.save() #NOTE : implement a PID algorithm second_towait = callrequest.campaign.intervalretry debug_query(29) logger.debug("Init Retry CallRequest in %d seconds" % second_towait) init_callrequest.apply_async(args=[ new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration ], countdown=second_towait) else: #The Call is Answered logger.debug("Check for completion call") #Check if we should relaunch a new call to achieve completion check_retrycall_completion(callrequest)
def check_callevent(): """ Check callevent call_event table is created by listener.lua CREATE TABLE if not exists call_event ( id serial NOT NULL PRIMARY KEY, event_name varchar(200) NOT NULL, body varchar(200) NOT NULL, job_uuid varchar(200), call_uuid varchar(200) NOT NULL, used_gateway_id integer, callrequest_id integer, callerid varchar(200), phonenumber varchar(200), duration integer DEFAULT 0, billsec integer DEFAULT 0, hangup_cause varchar(40), hangup_cause_q850 varchar(10), amd_status varchar(40), starting_date timestamp with time zone, status integer, created_date timestamp with time zone NOT NULL ); CREATE INDEX call_event_idx_uuid ON call_event (call_uuid); CREATE INDEX call_event_idx_status ON call_event (status); CREATE INDEX call_event_idx_date ON call_event (created_date); """ debug_query(20) cursor = connection.cursor() #TODO (Areski) #Replace this for ORM with select_for_update or transaction sql_statement = "SELECT id, event_name, body, job_uuid, call_uuid, used_gateway_id, "\ "callrequest_id, callerid, phonenumber, duration, billsec, hangup_cause, "\ "hangup_cause_q850, starting_date, status, created_date, amd_status, leg FROM call_event WHERE status=1 LIMIT 2000 OFFSET 0" cursor.execute(sql_statement) row = cursor.fetchall() debug_query(21) buff_voipcall = BufferVoIPCall() for record in row: call_event_id = record[0] event_name = record[1] body = record[2] job_uuid = record[3] call_uuid = record[4] #used_gateway_id = record[5] callrequest_id = record[6] callerid = record[7] phonenumber = record[8] duration = record[9] billsec = record[10] hangup_cause = record[11] hangup_cause_q850 = record[12] starting_date = record[13] amd_status = record[16] leg = record[17] #Update Call Event sql_statement = "UPDATE call_event SET status=2 WHERE id=%d" % call_event_id cursor.execute(sql_statement) logger.info("Processing Event : %s" % event_name) if event_name == 'BACKGROUND_JOB': #hangup cause come from body hangup_cause = body[5:] # if event_name == 'CHANNEL_HANGUP_COMPLETE': # #hangup cause come from body # print(event_name) if hangup_cause == '': hangup_cause = body[5:] opt_request_uuid = job_uuid opt_hangup_cause = hangup_cause debug_query(22) try: if callrequest_id == 0: callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'subscriber', 'campaign')\ .get(request_uuid=opt_request_uuid.strip(' \t\n\r')) else: #mainly coming here callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'subscriber', 'campaign')\ .get(id=callrequest_id) except: logger.error("Cannot find Callrequest job_uuid : %s" % job_uuid) continue logger.debug("Find Callrequest id : %d" % callrequest.id) debug_query(23) if leg == 'aleg': #Only the aleg will update the subscriber status / Bleg is only recorded #Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause callrequest.save() callrequest.subscriber.save() debug_query(24) if call_uuid == '': call_uuid = job_uuid if callerid == '': callerid = callrequest.callerid if phonenumber == '': phonenumber = callrequest.phone_number #TODO: Create those in Bulk - add in a buffer until reach certain number buff_voipcall.save( obj_callrequest=callrequest, request_uuid=opt_request_uuid, leg=leg, hangup_cause=opt_hangup_cause, hangup_cause_q850=hangup_cause_q850, callerid=callerid, phonenumber=phonenumber, starting_date=starting_date, call_uuid=call_uuid, duration=duration, billsec=billsec, amd_status=amd_status) debug_query(25) #If the call failed we will check if we want to make a retry call #Add condition to retry when it s machine and we want to reach a human if (opt_hangup_cause != 'NORMAL_CLEARING' and callrequest.call_type == CALLREQUEST_TYPE.ALLOW_RETRY) or \ (amd_status == 'machine' and callrequest.campaign.voicemail and callrequest.campaign.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): #Update to Retry Done callrequest.call_type = CALLREQUEST_TYPE.RETRY_DONE callrequest.save() debug_query(26) #check if we are allowed to retry on failure if ((callrequest.subscriber.count_attempt - 1) >= callrequest.campaign.maxretry or not callrequest.campaign.maxretry): logger.error("Not allowed retry - Maxretry (%d)" % callrequest.campaign.maxretry) #Check here if we should try for completion check_retrycall_completion(callrequest) debug_query(28) else: #Allowed Retry logger.error("Allowed Retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Create new callrequest, Assign parent_callrequest, # Change callrequest_type & num_attempt new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, timeout=callrequest.timeout, subscriber_id=callrequest.subscriber_id ) new_callrequest.save() #NOTE : implement a PID algorithm second_towait = callrequest.campaign.intervalretry debug_query(29) logger.debug("Init Retry CallRequest in %d seconds" % second_towait) init_callrequest.apply_async( args=[new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration], countdown=second_towait) else: #The Call is Answered logger.debug("Check for completion call") #Check if we should relaunch a new call to achieve completion check_retrycall_completion(callrequest) debug_query(30) buff_voipcall.commit() debug_query(31) logger.debug('End Loop : check_callevent')
def callevent_processing(): """ Retrieve callevents and process them call_event table is created by listener.lua CREATE TABLE if not exists call_event ( id serial NOT NULL PRIMARY KEY, event_name varchar(200) NOT NULL, body varchar(200) NOT NULL, job_uuid varchar(200), call_uuid varchar(200) NOT NULL, used_gateway_id integer, callrequest_id integer, alarm_request_id integer, callerid varchar(200), phonenumber varchar(200), duration integer DEFAULT 0, billsec integer DEFAULT 0, hangup_cause varchar(40), hangup_cause_q850 varchar(10), amd_status varchar(40), starting_date timestamp with time zone, status smallint, leg smallint, created_date timestamp with time zone NOT NULL ); CREATE INDEX call_event_idx_status ON call_event (status); --CREATE INDEX call_event_idx_date ON call_event (created_date); --CREATE INDEX call_event_idx_uuid ON call_event (call_uuid); """ debug_query(20) cursor = connection.cursor() # TODO (Areski) # Replace this for ORM with select_for_update or transaction try: sql_statement = "SELECT id, event_name, body, job_uuid, call_uuid, used_gateway_id, " \ "callrequest_id, alarm_request_id, callerid, phonenumber, duration, billsec, hangup_cause, " \ "hangup_cause_q850, starting_date, status, created_date, amd_status, leg " \ "FROM call_event WHERE status=1 LIMIT 1000 OFFSET 0" cursor.execute(sql_statement) row = cursor.fetchall() except: # Error on sql / Lua listener might not be on logger.error("Error Fetching call_event") else: debug_query(21) # buff_voipcall = BufferVoIPCall() call_event_list = [] for record in row: call_event_id = record[0] event_name = record[1] call_event_list.append(str(call_event_id)) logger.info("Processing Call_Event : %s" % event_name) process_callevent.delay(record) if call_event_list: # Update Call Event sql_statement = "UPDATE call_event SET status=2 WHERE id IN (%s)" % ','.join( call_event_list) cursor.execute(sql_statement) debug_query(30) # buff_voipcall.commit() # debug_query(31) logger.debug('End Loop : callevent_processing')
def process_callevent(record): """ Process the callevent, this tasks will: - Retrieve the callrequest using either callrequest_id or request_uuid - create the voipcall, and save different data """ #TODO: add method in utils parse_callevent app_type = 'campaign' event_name = record[1] body = record[2] job_uuid = record[3] call_uuid = record[4] #used_gateway_id = record[5] callrequest_id = record[6] alarm_request_id = record[7] callerid = record[8] phonenumber = record[9] duration = record[10] billsec = record[11] hangup_cause = record[12] hangup_cause_q850 = record[13] starting_date = record[14] amd_status = record[17] leg = record[18] if event_name == 'BACKGROUND_JOB': #hangup cause come from body hangup_cause = body[5:] if hangup_cause == '': hangup_cause = body[5:] request_uuid = job_uuid opt_hangup_cause = hangup_cause debug_query(22) try: if callrequest_id == 0: callrequest = Callrequest.objects \ .select_related('aleg_gateway', 'subscriber', 'campaign') \ .get(request_uuid=request_uuid.strip(' \t\n\r')) else: #mainly coming here callrequest = Callrequest.objects \ .select_related('aleg_gateway', 'subscriber', 'campaign') \ .get(id=callrequest_id) except: logger.error("Cannot find Callrequest job_uuid : %s" % job_uuid) return True logger.error(callrequest) if callrequest.alarm_request_id: app_type = 'alarm' alarm_req = AlarmRequest.objects.get(pk=callrequest.alarm_request_id) #Overwrite alarm_request_id as this is equal to 0 when call fails alarm_request_id = callrequest.alarm_request_id logger.debug("Find Callrequest id : %d" % callrequest.id) debug_query(23) if leg == 'aleg' and app_type == 'campaign': #Update callrequest #update_callrequest.delay(callrequest, opt_hangup_cause) #Disabled above tasks to reduce amount of tasks #Only the aleg will update the subscriber status / Bleg is only recorded #Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause # ... callrequest.save() callrequest.subscriber.save() debug_query(24) elif leg == 'aleg' and app_type == 'alarm': if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS alarm_req.status = ALARMREQUEST_STATUS.SUCCESS alarm_req.duration = duration alarm_req.alarm.status = ALARM_STATUS.SUCCESS else: callrequest.status = CALLREQUEST_STATUS.FAILURE alarm_req.status = ALARMREQUEST_STATUS.FAILURE alarm_req.alarm.status = ALARM_STATUS.FAILURE callrequest.hangup_cause = opt_hangup_cause callrequest.save() alarm_req.save() alarm_req.alarm.save() debug_query(24) if call_uuid == '': call_uuid = job_uuid if callerid == '': callerid = callrequest.callerid if phonenumber == '': phonenumber = callrequest.phone_number #Create those in Bulk - add in a buffer until reach certain number # buff_voipcall.save( # obj_callrequest=callrequest, # request_uuid=request_uuid, # leg=leg, # hangup_cause=opt_hangup_cause, # hangup_cause_q850=hangup_cause_q850, # callerid=callerid, # phonenumber=phonenumber, # starting_date=starting_date, # call_uuid=call_uuid, # duration=duration, # billsec=billsec, # amd_status=amd_status) # debug_query(25) voipcall_save( callrequest=callrequest, request_uuid=request_uuid, leg=leg, hangup_cause=opt_hangup_cause, hangup_cause_q850=hangup_cause_q850, callerid=callerid, phonenumber=phonenumber, starting_date=starting_date, call_uuid=call_uuid, duration=duration, billsec=billsec, amd_status=amd_status) #If the call failed we will check if we want to make a retry call #Add condition to retry when it s machine and we want to reach a human if (app_type == 'campaign' and opt_hangup_cause != 'NORMAL_CLEARING' and callrequest.call_type == CALLREQUEST_TYPE.ALLOW_RETRY) or \ (amd_status == 'machine' and callrequest.campaign.voicemail and callrequest.campaign.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): #Update to Retry Done callrequest.call_type = CALLREQUEST_TYPE.RETRY_DONE callrequest.save() debug_query(26) #check if we are allowed to retry on failure if ((callrequest.subscriber.count_attempt - 1) >= callrequest.campaign.maxretry or not callrequest.campaign.maxretry): logger.error("Not allowed retry - Maxretry (%d)" % callrequest.campaign.maxretry) #Check here if we should try for completion check_retrycall_completion(callrequest) debug_query(28) else: #Allowed Retry logger.error("Allowed Retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Create new callrequest, Assign parent_callrequest, # Change callrequest_type & num_attempt new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, timeout=callrequest.timeout, subscriber_id=callrequest.subscriber_id ) new_callrequest.save() #NOTE : implement a PID algorithm second_towait = callrequest.campaign.intervalretry debug_query(29) logger.debug("Init Retry CallRequest in %d seconds" % second_towait) init_callrequest.apply_async( args=[new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration], countdown=second_towait) elif app_type == 'campaign': #The Call is Answered and it's a campaign call logger.debug("Check for completion call") #Check if we should relaunch a new call to achieve completion check_retrycall_completion(callrequest) elif opt_hangup_cause != 'NORMAL_CLEARING' and app_type == 'alarm': # check_retry_alarm(alarm_request_id)
def init_callrequest(callrequest_id, campaign_id, callmaxduration, ms_addtowait=0, alarm_request_id=None): """ This task read the callrequest, update it as 'In Process' then proceed on the call outbound, using the different call engine supported **Attributes**: * ``callrequest_id`` - Callrequest ID * ``campaign_id`` - Campaign ID * ``callmaxduration`` - Max duration * ``ms_addtowait`` - Milliseconds to wait before outbounding the call """ outbound_failure = False subscriber_id = None contact_id = None debug_query(8) if ms_addtowait > 0: sleep(ms_addtowait) # Survey Call or Alarm Call if campaign_id: # TODO: use only # https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.only obj_callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'user__userprofile', 'subscriber', 'campaign').get(id=callrequest_id) subscriber_id = obj_callrequest.subscriber_id contact_id = obj_callrequest.subscriber.contact_id elif alarm_request_id: obj_callrequest = Callrequest.objects.select_related( 'aleg_gateway', 'user__userprofile').get(id=callrequest_id) alarm_request_id = obj_callrequest.alarm_request_id else: logger.info( "TASK :: init_callrequest, wrong campaign_id & alarm_request_id") return False debug_query(9) logger.info("TASK :: init_callrequest - status:%s;cmpg:%s;alarm:%s" % (obj_callrequest.status, campaign_id, alarm_request_id)) # TODO: move method prepare_phonenumber into the model gateway # Obj_callrequest.aleg_gatewayprepare_phonenumber() dialout_phone_number = prepare_phonenumber( obj_callrequest.phone_number, obj_callrequest.aleg_gateway.addprefix, obj_callrequest.aleg_gateway.removeprefix, obj_callrequest.aleg_gateway.status) if not dialout_phone_number: logger.info("Error with dialout_phone_number - phone_number:%s" % (obj_callrequest.phone_number)) return False else: logger.debug("dialout_phone_number : %s" % dialout_phone_number) debug_query(10) if settings.DIALERDEBUG: dialout_phone_number = settings.DIALERDEBUG_PHONENUMBER # Retrieve the Gateway for the A-Leg gateways = obj_callrequest.aleg_gateway.gateways gateway_id = obj_callrequest.aleg_gateway.id # Gateway_codecs / gateway_retries dialing_timeout = obj_callrequest.timeout # fraud protection on short calls try: dialing_timeout = int(dialing_timeout) if dialing_timeout < 10: dialing_timeout = 10 except ValueError: dialing_timeout = 45 originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string debug_query(11) # Sanitize gateways gateways = gateways.strip() if gateways[-1] != '/': gateways = gateways + '/' originate_dial_string = obj_callrequest.aleg_gateway.originate_dial_string if obj_callrequest.user.userprofile and obj_callrequest.user.userprofile.accountcode: originate_dial_string = originate_dial_string + ',accountcode=' + \ str(obj_callrequest.user.userprofile.accountcode) debug_query(12) if settings.NEWFIES_DIALER_ENGINE.lower() == 'esl': try: args_list = [] send_digits = False time_limit = callmaxduration # To wait before sending DTMF to the extension, you can add leading 'w' # characters. # Each 'w' character waits 0.5 seconds instead of sending a digit. # Each 'W' character waits 1.0 seconds instead of sending a digit. # You can also add the tone duration in ms by appending @[duration] after string. # Eg. 1w2w3@1000 check_senddigit = dialout_phone_number.partition('w') if check_senddigit[1] == 'w': send_digits = check_senddigit[1] + check_senddigit[2] dialout_phone_number = check_senddigit[0] if obj_callrequest.callerid and len(obj_callrequest.callerid) > 0: args_list.append("origination_caller_id_number='%s'" % obj_callrequest.callerid) if obj_callrequest.caller_name and len( obj_callrequest.caller_name) > 0: args_list.append("origination_caller_id_name='%s'" % obj_callrequest.caller_name) # Add App Vars args_list.append( "campaign_id=%s,subscriber_id=%s,alarm_request_id=%s,used_gateway_id=%s,callrequest_id=%s,contact_id=%s,dialout_phone_number=%s" % (campaign_id, subscriber_id, alarm_request_id, gateway_id, obj_callrequest.id, contact_id, obj_callrequest.phone_number)) args_list.append(originate_dial_string) # Call Vars callvars = "bridge_early_media=true,originate_timeout=%d,newfiesdialer=true,leg_type=1" % \ (dialing_timeout, ) args_list.append(callvars) # Default Test hangup_on_ring = '' send_preanswer = False # set hangup_on_ring try: hangup_on_ring = int(hangup_on_ring) except ValueError: hangup_on_ring = -1 exec_on_media = 1 if hangup_on_ring >= 10: # 0->10 fraud protection on short calls args_list.append( "execute_on_media_%d='sched_hangup +%d ORIGINATOR_CANCEL'" % (exec_on_media, hangup_on_ring)) exec_on_media += 1 # TODO: look and test http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_queue_dtmf # Send digits if send_digits: if send_preanswer: args_list.append("execute_on_media_%d='send_dtmf %s'" % (exec_on_media, send_digits)) exec_on_media += 1 else: args_list.append("execute_on_answer='send_dtmf %s'" % send_digits) # Set time_limit try: time_limit = int(time_limit) if time_limit > 0: args_list.append( "execute_on_answer='sched_hangup +%d ALLOTTED_TIMEOUT'" % time_limit) except ValueError: logger.error('ValueError time_limit :> %s' % time_limit) # build originate string args_str = ','.join(args_list) # DEBUG # settings.ESL_SCRIPT = '&playback(/usr/local/freeswitch/sounds/en/us/callie/voicemail/8000/vm-record_greeting.wav)' if settings.DIALERDEBUG: dial_command = "originate {%s}user/areski '%s'" % ( args_str, settings.ESL_SCRIPT) else: dial_command = "originate {%s}%s%s '%s'" % \ (args_str, gateways, dialout_phone_number, settings.ESL_SCRIPT) # originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=10}user/areski &playback(/tmp/myfile.wav) # dial = "originate {bridge_early_media=true,hangup_after_bridge=true,originate_timeout=,newfiesdialer=true,used_gateway_id=1,callrequest_id=38,leg_type=1,origination_caller_id_number=234234234,origination_caller_id_name=234234,effective_caller_id_number=234234234,effective_caller_id_name=234234,}user//1000 '&lua(/usr/share/newfies-lua/newfies.lua)'" # Load balance on testing # from random import randint, seed # seed() # randval = randint(1, 2) # if randval == 1: # dial_command = dial_command.replace('88.208.208.244', '88.208.208.209') # logger.warn('dial_command (%d): %s' % (randval, dial_command)) logger.warn('dial_command : %s' % dial_command) request_uuid = dial_out(dial_command, obj_callrequest.id) debug_query(14) if request_uuid and len( request_uuid) > 0 and request_uuid[:5] == 'error': outbound_failure = True debug_query(13) except: raise logger.error('error : ESL') outbound_failure = True logger.debug('Received RequestUUID :> %s' % request_uuid) else: logger.error('No other method supported!') obj_callrequest.status = CALLREQUEST_STATUS.FAILURE obj_callrequest.save() # ADD if alarm_request_id update AlarmRequest return False # Survey Call or Alarm Call if campaign_id: # Update Subscriber if not obj_callrequest.subscriber.count_attempt: obj_callrequest.subscriber.count_attempt = 1 else: obj_callrequest.subscriber.count_attempt = obj_callrequest.subscriber.count_attempt + 1 obj_callrequest.subscriber.last_attempt = datetime.utcnow().replace( tzinfo=utc) # check if the outbound call failed then update Subscriber if outbound_failure: obj_callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL obj_callrequest.subscriber.save() elif alarm_request_id: if outbound_failure: check_retry_alarm(alarm_request_id) # Update CallRequest Object obj_callrequest.request_uuid = request_uuid # check if the outbound call failed if outbound_failure: obj_callrequest.status = CALLREQUEST_STATUS.FAILURE else: obj_callrequest.status = CALLREQUEST_STATUS.CALLING obj_callrequest.save() debug_query(14) return True
def run(self, campaign_id): """ This will execute the outbound calls in the campaign **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: spool_pending_call = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects.select_related( 'user__userprofile__dialersetting', 'aleg_gateway', 'content_type').get(id=campaign_id) except: logger.error("Can't find this campaign") return False # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry if obj_campaign.user.userprofile.dialersetting: if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records if frequency >= 10: callfrequency = int( frequency / DIV_MIN) + 1 # 1000 per minutes 101 #callfrequency = int(frequency) + 1 # 1000 per minutes 101 else: callfrequency = frequency (list_subscriber, no_subscriber) = obj_campaign.get_pending_subscriber_update( callfrequency, SUBSCRIBER_STATUS.IN_PROCESS) logger.info( "##subscriber=%d campaign_id=%d callfrequency=%d frequency=%d" % (no_subscriber, campaign_id, callfrequency, frequency)) debug_query(3) if no_subscriber == 0: return False # Set time to wait for balanced dispatching of calls #time_to_wait = int(60 / DIV_MIN) / no_subscriber time_to_wait = 6.0 / no_subscriber count = 0 for elem_camp_subscriber in list_subscriber: """Loop on Subscriber and start the initcall task""" count = count + 1 second_towait = floor(count * time_to_wait) ms_addtowait = (count * time_to_wait) - second_towait logger.info( "Init CallRequest in %d seconds (cmpg:%d,subscriber:%d)" % (second_towait, campaign_id, elem_camp_subscriber.id)) phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) #Verify that the contact is authorized if not obj_campaign.is_authorized_contact( obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True #Verify that the contact is not in the DNC list if obj_campaign.dnc: res_dnc = DNCContact.objects.filter(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) if res_dnc: logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True else: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) #TODO: idea to speed up, create bluck of 10(Y) and then send a list of callrequest_id to init_callrequest # Create a Callrequest Instance to track the call task new_callrequest = Callrequest( status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.now(), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber) new_callrequest.save() debug_query(6) init_callrequest.apply_async(args=[ new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait ], countdown=second_towait) # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async(args=[new_callrequest_id, obj_campaign_id], countdown=1) debug_query(7) return True