def report_staffid(con, *args): global my_queue if not args: # 外联情况 info = con.getInfo() else: info = args[0] # 这里用info命名不太恰当但是历史原因就暂时info(时间上是ev--event) return caller = info.getHeader('caller-caller-id-number') callee = info.getHeader( 'variable_sip_to_user') # 或者caller-destination-number uuid = info.getHeader('variable_uuid') # 获取分机 member = info.getHeader('variable_MEMBERINTERFACE') ccutils.set_variable(con, 'CDR(Exten)', member) # 获取工号 sql = 'select StaffId from' + Conf.R_STAFFIDEXTEN_SHARE + 'where Exten = {}'.format( member) row = db.get(sql) StaffId = row.StaffId if row else 0 if StaffId: ccutils.multiset(con, ['StaffId', StaffId, 'CDR(StaffId)', StaffId]) # 插入录音记录时会用到 ccutils.multiset(con, [ 'CDR(DIALSTATUS)', 'ANSWER', 'QUEUETIME_START', ccutils.microtime(True) ]) return (StaffId, member)
def queue_answer(con, *args): # IsForeCSave = GLOBALS['IsForeCSave'] info = args[0] callee = info.getHeader('variable_sip_from_user') uuid = info.getHeader('variable_uuid') CustomId = info.getHeader('variable_CDR(customerid)') #获取客户ID CustomId = CustomId if CustomId else 0 QueueName = info.getHeader('variable_QueueName') ''' 这里要通过MEMBERINTERFACE获取坐席分机,但是在呼入情况下info是针对客户那一端的channel的不是针对坐席这一端的 所以通过info.getHeader()来获取是'牛头不对马尾',呼出应该没问题 ''' member = info.getHeader('variable_MEMBERINTERFACE') #获取分机 starttime = ccutils.microtime(True) # 获取工号 sql = 'select StaffId from' + Conf.R_STAFFIDEXTEN_SHARE + 'where Exten = {}'.format( member) row = db.get(sql) StaffId = row.StaffId if row else 0 vars = [ 'CDR(Exten)', member, 'IsAnswer', 1, 'QUEUETIME_START', ccutils.microtime(True) ] ccutils.multiset(con, vars) isIvrLogin = '******' if not StaffId: # 如果staffid为空有可能是座席F5刷新导致的,临时的解决为从登录日志表中获取最新的一次登录绑定 sql = "select UserName from {} where exten={} and IsSuccess=1 order by id desc limit 1;".format( Conf.l_CRM_LOGIN, member) row = db.get(sql) StaffId = row.StaffId if row else 0 if StaffId: ccutils.set_variable(con, 'StaffId', 0) online_time = 0 if StaffId: ccutils.multiset(con, ['StaffId', StaffId, 'CDR(StaffId)', StaffId]) sql = 'select UpdateTime from' + Conf.R_STAFFIDEXTEN_SHARE + 'where StaffId={} limit 1'.format( StaffId) row = db.get(sql) online_time = row.UpdateTime if row else 0 if online_time == 1: # 如果是语音登录则记录最后一次接通的被叫号码 isIvrLogin = 1 sql = "update {} set lastnum={} where queue_name='{}' and staffid={};".format( Conf.FORECAST_QUEUE, callee, QueueName, StaffId) db.execute(sql) # 摘机标识 if isIvrLogin and online_time == 1: ccutils.set_variable(con, 'SHARED(SRC_IsSave, {})', member, isIvrLogin) # 参数字符串 ccutils.set_variable(con, 'SHARED(SHARE_STRING, {})', member, int(CustomId) * int(StaffId)) ccutils.set_variable(con, 'CDR(customerid)', CustomId) # 号码呼叫状态置为通话中 sql = "update" + Conf.FORECAST_DATA_TEMP + "set calloutStatus=2,updatetime='{}' where id={};".format( now(), CustomId) db.execute(sql) return (StaffId, )
def call_transfer(con, *args): if not args: # 外联情况 info = con.getInfo() else: info = args[0] # 这里用info命名不太恰当但是历史原因就暂时info(时间上是ev--event) return # print('info.serialize() is {}'.format(info.serialize())) caller = info.getHeader('caller-caller-id-number') callee = info.getHeader( 'variable_sip_to_user') # 或者caller-destination-number uuid = info.getHeader('variable_uuid') domain_name = info.getHeader('variable_domain_name') if not domain_name: domain_name = info.getHeader('variable_sip_to_host') elif not domain_name: domain_name = info.getHeader('channel-network-addr') ccutils.multiset(con, ['CDR(calltype)', 1, 'CALLTYPE', 1]) sql = 'select StaffId from ' + Conf.R_STAFFIDEXTEN_SHARE + 'where Exten = {}'.format( callee) row = db.get(sql) trans_staffid = row.StaffId if row else 0 if trans_staffid: ccutils.multiset( con, ['StaffId', trans_staffid, 'CDR(StaffId)', trans_staffid]) sql = "select a.Id ,a.ClassifId from {} a join crm_department b on a.DepartmentId=b.Id " \ "join {} c on b.Id=c.DepartmentId where a.`Status`=1 and c.StaffId={};".format(Conf.K_PROJECTINFO, Conf.CRM_USERINFO, trans_staffid) row = db.get(sql) # 此外线号码没有绑定项目 if row: # 归属分类Id ClassifId = row.ClassifId # 归属项目Id ProjectId = row.Id ccutils.multiset(con, [ 'CDR(ClassifId)', ClassifId, 'CDR(projectid)', ProjectId, 'PROID', ProjectId, 'ClassifId', ClassifId ]) ccutils.set_variable(con, 'CDR(DIALSTATUS)', 'answered') con.executeAsync('bridge', 'sofia/internal/${}%${}/'.format(callee, domain_name), uuid) # $agi->agi_exec('Dial', "SIP/$callee,30"); return True
def queue_groupcall_answer(con, *args): if not args: # 外联情况 ev = con.getInfo() else: ev = args[0] # 这里用info命名不太恰当但是历史原因就暂时info(时间上是ev--event) return # $IsForeCSave = & $GLOBALS['IsForeCSave']; #todo 不知道这个变量何用?? callee = ev.getHeader( 'variable_sip_to_user') # 或者caller-destination-number CustomId = ev.getHeader('variable_CustomId') QueueName = ev.getHeader('QueueName') memberinterface = ev.getHeader('variable_MEMBERINTERFACE') #获取分机 member = memberinterface[:4] start_time = ccutils.microtime(True) # 获取工号 sql = 'select StaffId from {} where Exten={} limit 1'.format( Conf.R_STAFFIDEXTEN_SHARE, member) row = db.get(sql) StaffId = row.StaffId if row else 0 ccutils.multiset(con, ['CDR(Exten)', member, 'IsAnswer', 1]) isIvrLogin = '******' if not StaffId: # 如果staffid为空 有可能 是座席F5刷新导致的,临时的解决为从登录日志表中获取最新的一次登录绑定 sql = "select UserName from {} where exten='{}' and IsSuccess=1 order by id desc limit 1;".format( Conf.l_CRM_LOGIN, member) row = db.get(sql) StaffId = row.StaffId if row else 0 if not StaffId: ccutils.set_variable(con, 'StaffId', 0) if StaffId: ccutils.multiset(con, ['StaffId', StaffId, 'CDR(StaffId)', StaffId]) sql = 'select UpdateTime from {} where StaffId={} limit 1'.format( Conf.R_STAFFIDEXTEN_SHARE, StaffId) row = db.get(sql) online_time = row.UpdateTime if row else 0 ccutils.set_variable(con, 'CDR(customerid)', CustomId) return True
def call_in(con, *args): ''' :param info: :return: 此函数对应于agi_call_in.php ''' if not args: #外联情况 info = con.getInfo() else: info = args[0] #这里用info命名不太恰当但是历史原因就暂时info(时间上是ev--event) return caller = info.getHeader('variable_sip_from_user') callee = info.getHeader('channel-destination-number') if 'gw+' in callee: callee = callee[3:] uuid = info.getHeader('variable_uuid') domain_name = info.getHeader('variable_domain_hame') if not domain_name: domain_name = info.getHeader('variable_sip_to_host') elif not domain_name: domain_name = info.getHeader('channel-network-addr') dial_str = None # 通过外线号码获得其所属的项目(正常进行的项目) sql = 'select b.id as ClassifId, a.CompanyId, a.Type from {} a join {} b on a.CompanyId = b.CompanyId ' \ 'where a.Num = "{}" order by b.Id limit 1'.format(Conf.K_NUMINFO_SHARE, Conf.K_PROJECT_CLASSIFICATION, callee) row = db.get(sql) if not row: print('#########未绑定项目#############') return dial_str # 判断是否为语音识别的呼入号码 # row.Type = 3 if row.Type == 3: sql = 'select asrnumber, CompanyId from' + Conf.ASRCALL_CONFIG + 'where CallerID = "{}"'.format( callee) asrrow = db.get(sql) if asrrow: CustomerId = 0 #todo 原来php里直接出现此变量,python会报错如果没有实现定义的话,所以这里添加个定义,具体php为何这样有待进一步理解 vars = [ 'asrnumber', asrrow.asrnumber, 'CompanyId', asrrow.CompanyId, 'CDR(CompanyId)', asrrow.CompanyId, 'CDR(calltype)', 6, 'CUSID', CustomerId, 'CustomId', CustomerId, 'CDR(customerid)', CustomerId ] ccutils.multiset(con, vars) # ###todo:transfer好像执行完不会返回,这里似乎应该调用execute_extension更合适,待确认... con.executeAsync('transfer', 'from-asrcall', uuid) return dial_str # 归属分类id ClassifId = row.ClassifId # 队列名称:队列到名字和呼入到外线一致 QueueName = callee CompanyId = row.CompanyId vars = [ 'CompanyId', CompanyId, 'ClassifId', ClassifId, 'CDR(calltype)', 1, 'CDR(ClassifId)', ClassifId, 'CDR(CompanyId)', CompanyId ] ccutils.multiset(con, vars) # 黑名单 sql = 'select id from' + Conf.K_BLACKLIST_SHARE + 'where Num = "{}" and CompanyId = {} limit 1'.format( caller, CompanyId) blacklist = db.get(sql).id if db.get(sql) else None if blacklist: print('已经加入黑名单') return dial_str sql = "select IVR_IsStaffFirst,IsRecord from {} where Enabled=1 and Id={} ;".format( Conf.CRM_COMPANYINFO, CompanyId) row = db.get(sql) if not row: # 公司账户已经禁用 con.execute('playback', 'ccos_company_disabled') return dial_str staff_first = row.IVR_IsStaffFirst # $agi->set_variable('ISRECORD',$row['IsRecord']); # 检查应该客户是否为服务客户 如果是 则获取其相应资料 sql = "select Id,StaffId,ProjectId from" + Conf.CUSTOMER_BASEINFO + "where CompanyId={} and DelBatchId=0 and phone='{}' limit 1;".format( CompanyId, caller) row = db.get(sql) CustomerId = row.Id if row else 0 StaffId = row.StaffId if row else 0 ### if not CustomerId and staff_first: sql = "select id, StaffId,customerid from {} where ClassifId={} and dst={} and StaffId>0 union all select id, " \ "StaffId,customerid from {} where ClassifId={} and src ={} and StaffId>0 order by id desc limit 1;".format(Conf.CDR, ClassifId, caller, Conf.CDR, ClassifId, caller) row = db.get(sql) CustomerId = row.customerid if row else 0 StaffId = row.StaffId if row else 0 ### if not CustomerId: CustomerId = ccutils.get_generator_id(0, caller) vars = ['CUSID', CustomerId, 'CDR(customerid)', CustomerId] ccutils.multiset(con, vars) # 语音导航 ivr_exec(con, callee) # 直属号码 if not staff_first or not StaffId: sql = "select StaffId,count(*) as CountNum from" + Conf.R_STAFFIDNUM_SHARE + "where num='{}' and type=1 ;".format( callee) row = db.get(sql) # 如果没有呼入绑定 则挂机 if row and not row.CountNum: return None # 如果有呼入绑定 且只有一个座席 if row.CountNum == 1: StaffId = row.StaffId staff_first = 1 # 呼叫归属客服 if staff_first and StaffId: sql = 'select Exten from' + Conf.R_STAFFIDEXTEN_SHARE + 'where StaffId = "{}"'.format( StaffId) row = db.get(sql) Exten = row.Exten if row else '' vars = [ 'StaffId', StaffId, 'CDR(Exten)', Exten, 'CDR(StaffId)', StaffId ] ccutils.multiset(con, vars) ExtenStatus = -4 if Exten: ExtenStatus = get_extension_state(Exten) #取当前分机状态 # 分机空闲时进行呼叫 if not ExtenStatus: sql = "select c.id,b.IsHiddenNum from" + Conf.CRM_USERINFO + "a join crm_role b on a.RoleId=b.Id join " \ "k_projectinfo c on a.DepartmentId=c.DepartmentId and c.`Status`=1 where a.StaffId='{}';".format(StaffId) row = db.get(sql) isHiddenNum = row.IsHiddenNum if not isHiddenNum: ccutils.set_variable(con, 'CALLERID(num-pres)', 'prohib') # ccutils.set_variable(con,'CALL_STR','SIP/{}'.format(Exten)) # con.executeAsync('set', 'CALL_STR=SIP/{}'.format(Exten), uuid) # con.executeAsync('Goto', 'transfer', uuid) dial_str = 'sofia/internal/{}%{}'.format(Exten, domain_name) elif staff_first == 1: con.execute('playback', Conf.SOUND_PATH + 'Ivr_021.wav') # 坐席正忙请稍后再拨 return dial_str if not QueueName: return dial_str # 如果列队中有部门经理 且号码显示 没全部为可显示 sql = "select a.Id from" + Conf.R_STAFFIDNUM_SHARE + "a join {} b on a.StaffId=b.StaffId join {} c on b.RoleId=c.Id and c.`Type`=3 and c.IsHiddenNum=1 where a.Num ='{}' limit 1 ;".format( Conf.CRM_USERINFO, Conf.CRM_ROLE, callee) row = db.get(sql) isHiddenNum = row.Id if row else 0 if not isHiddenNum: # 查看座席权限是否 是隐藏 sql = "select Id from" + Conf.CRM_ROLE + "where CompanyId={} and type=4 and ISHiddenNum=0 limit 1;".format( CompanyId) row = db.get(sql) isHiddenNum = row.Id if row else 0 if isHiddenNum: ccutils.set_variable(con, 'CALLERID(num-pres)', 'prohib') ccutils.set_variable(con, 'U_QUEUE', QueueName) return (dial_str, QueueName)
def queue_hangup(con, ev): sys_mode = ccutils.read_configuration('sys_mode') caller = ev.getHeader('caller-caller-id-number') callee = ev.getHeader('channel-caller-id-number') uuid = ev.getHeader('Unique-ID') channel = ev.getHeader('Other-Leg-Channel-Name') # CustomId = ev.getHeader('variable_CustomId)') #todo 这是原来的php对应的但是获取不到这个变量所以用下面一句代替 CustomId = ev.getHeader('variable_CDR(customerid)') # 如果客户ID不存在 直接退出 if not CustomId: return True ClassifId = ev.getHeader('variable_ClassifId') StaffId = ev.getHeader('variable_StaffId') REASON = ev.getHeader('variable_REASON') REASON = REASON if REASON else '' CompanyId = ev.getHeader('variable_CompanyId') IsAnswer = ev.getHeader('variable_IsAnswer') calltype = ev.getHeader('variable_CALLTYPE') PROID = ev.getHeader('variable_PROID') recordfile = ev.getHeader('variable_record_file') now = datetime.datetime.now() endtime = now.strftime('%Y-%m-%d %H:%M:%S') if StaffId and not PROID: result = ccutils.getInfoByStaffId(StaffId) if result: CompanyId = result['CompanyId'] ClassifId = result['ClassifId'] PROID = result['ProjectId'] ccutils.multiset(con, ['CDR(CompanyId)',CompanyId,'CDR(ClassifId)',ClassifId,'CDR(projectid)', PROID]) if IsAnswer == 1 or not len(REASON): # 排队的 if not IsAnswer and not len(REASON): REASON = '100' ccutils.set_variable(con, 'CDR(customerid)', CustomId) # 呼叫记录插入 cdrs = ccutils.gen_cdrs(ev) ccutils.cdr_insert(ev, *cdrs) # 获取通话时常 answeredtime = 0 queue_start_time = int(float(ev.getHeader('variable_QUEUETIME_START'))) if queue_start_time: answeredtime = ccutils.microtime(True) - queue_start_time else: answeredtime = ev.getHeader('variable_CDR(billsec)') if not answeredtime: answeredtime = '0000-00-00 00:00:00' # 接通 # 号码呼叫状态置为通话结束 # 关机、停机、拒接、无效号码、无应答插入到回收池 sql = 'insert ignore into' + Conf.FORECAST_DATA_SHARE_LOG + '(Id,ClassifId,ProjectId,CompanyId,BatchId,StaffId,Name,Phone,Address,Sex,Email,CallOutStatus,CallResult,AnswerTime,UpdateTime,CreateTime)' sql += "select Id, ClassifId,ProjectId,CompanyId,BatchId,'{}',Name,Phone,Address,Sex,Email,3,'{}','{}','{}',CreateTime from {} where id='{}' ;".format(StaffId, REASON, answeredtime, endtime, Conf.FORECAST_DATA_TEMP, CustomId) db.execute(sql) # 从预测池中删除 sql = "delete from {} where id='{}';".format(Conf.FORECAST_DATA_TEMP, CustomId) db.execute(sql) if recordfile: extenNum = ev.getHeader('variable_CDR(Exten)') ast_recodefile = recordfile sql = 'insert into' + Conf.L_RECORD + '(Type,StaffId,CustomerId,ProjectId,ClassifId,CallType,CallerNum,CalleeNum,Uniqueid,FileName,Path,HoldSec,CreateTime,CompanyId,exten)values' sql += "(0,'{}','{}','{}','{}',{},'{}','{}',{},'','{}','{}','{}','{}','{}');".format(StaffId, CustomId, PROID, ClassifId, calltype, caller, callee, uuid, ast_recodefile, answeredtime, endtime, CompanyId, extenNum) db.execute(sql) else: sql = "select Id, ClassifId,ProjectId,CompanyId,BatchId,Name,Phone,Address,Sex,Email,CreateTime from"+Conf.FORECAST_DATA_TEMP + "where id={} ;".format(CustomId) row = db.get(sql) if not row: return True Id = row.Id ClassifId = row.ClassifId CompanyId = row.CompanyId ProjectId = row.ProjectId BatchId = row.BatchId Name = row.Name Phone = row.Phone Address = row.Address Sex = row.Sex Email = row.Email CreateTime = row.CreateTime # 关机。停机。拒接。无效号码。无应答 插入 到回收池 sql = 'insert ignore into'+Conf.FORECAST_DATA_SHARE_LOG + '( Id,ClassifId,ProjectId,CompanyId,BatchId,StaffId,Name,Phone,Address,Sex,Email,CallOutStatus,CallResult,AnswerTime,UpdateTime,CreateTime)values' sql += "({}, '{}','{}','{}','{}',0,'{}','{}','{}','{}','{}',0,'{}',0,'{}','{}');".format(Id, ClassifId, ProjectId, CompanyId, BatchId, Name, Phone, Address, Sex, Email, REASON, endtime, CreateTime) db.execute(sql) # 插入cdr # 呼叫发起时间戳 orig_time = ev.getHeader('variable_Origtime') duration = time.time() - orig_time call_date = orig_time.strftime('%Y-%m-%d %H:%M:%S') duration_int = math.ceil(duration) disposition = 'NO ANSWER' # 托管用户预测未接通不计话单 if not sys_mode: sql = 'INSERT INTO'+Conf.CDR+ '(`clid`, `src`, `dst`, `calldate`, `answertime`, `endtime`, `duration`, `billsec`,`duration_int`,`billsec_int`, `dcontext`, `disposition`, `uniqueid`, `linkedid`, `projectid`, `StaffId`, `customerid`, `ClassifId`, `calltype`,`channel`,`dstchannel`,CompanyId)values' sql += "('{}', '{}','{}','{}', '0000-00-00 00:00:00','{}', {}, 0,{},0, 'from-autocall', '{}', '{}', '{}','{}', 0, '{}', '{}', 3,'{}','','$CompanyId');".format(caller, caller, Phone, call_date, endtime, duration, duration_int, disposition, uuid, uuid, ProjectId, CustomId, ClassifId, channel) db.execute(sql) return True