Browse Source

Merge branch 'develop' into jms_20250106_prod

Davidliu 1 month ago
parent
commit
29e17ff9a6

BIN
src.tar.gz


+ 1 - 1
src/core/callcenter/acd.py

@@ -96,6 +96,6 @@ class AcdService:
         for call_id in tmp_arr:
             call_info_queue.put_nowait(call_id)
 
-    def wait_timeout(self, call_id, timeouts=120):
+    def wait_timeout(self, call_id, timeouts=55):
         delay_action = DelayAction(call_id=call_id)
         self.cache.add_delay_message(DelayActionEnum.ACD_TIMEOUT_PLAY.name, delay_action, timeouts)

+ 14 - 7
src/core/callcenter/agent.py

@@ -21,7 +21,7 @@ from src.core.callcenter.constant import CENTER_AGENT_HEARTBEAT, SAAS_ID, CENTER
 from src.core.callcenter.dao import *
 from src.core.callcenter.data_handler import DataHandleServer
 from src.core.callcenter.enumeration import AgentState, AgentCheck, AgentHeartState, AgentServiceState, AgentLogState, \
-    AgentScene, BizErrorCode, WorkStatus, DownEvent, HumanState, DeviceType, ServiceDirect
+    AgentScene, BizErrorCode, WorkStatus, DownEvent, HumanState, DeviceType, ServiceDirect, HangupDir
 from src.core.callcenter.esl.constant.event_names import *
 from src.core.callcenter.exception import BizException
 from src.core.callcenter.push import PushHandler
@@ -91,7 +91,7 @@ class AgentEventService:
 
         start_time = time.time()
         try:
-            self.logger.info('agent_event_channel, event_name=%s, agent_num=%s, device_id=%s, is_agent=%s', event_name, agent_num, device_id, is_agent)
+            self.logger.info('agent_event_channel, event_name=%s, agent_num=%s, device_id=%s, is_agent=%s, hangup_dir=%s, hangup_count=%s, answer_count=%s', event_name, agent_num, device_id, is_agent, call_info.hangup_dir, call_info.hangup_count, call_info.answer_count)
             agent = self.data_handle_server.get_agent(saas_id, agent_num)
             if not agent:
                 # self.logger.warn("event service channel agent is null %s %s %s %s %s", saas_id, event_name, caller, called, json.loads(event.serialize('json')))
@@ -103,6 +103,9 @@ class AgentEventService:
 
             # 信道发起事件,触发完成发起(或桥)&& 坐席侧
             if CHANNEL_ORIGINATE == event_name and is_agent:
+                if call_info.hangup_dir and call_info.hangup_dir == HangupDir.CUSTOMER_HANGUP.code:
+                    self.logger.info('agent_event_channel:already:hangup, event_name=%s, call_id=%s, device_id=%s, is_agent=%s, agent_num=%s, hangup_dir=%s, hangup_count=%s, answer_count=%s', event_name, call_id, device_id, is_agent, agent_num, call_info.hangup_dir, call_info.hangup_count, call_info.answer_count)
+                    return
                 self.push_handler.push_on_agent_work_report(saas_id, flow_id, agent_num, call_id, AgentScene.MANUAL, WorkStatus.AGENT_RINGING,phone=call_info.caller)
 
             # 进度事件,外呼时对方提醒。或者入呼时提醒 && 坐席侧
@@ -146,7 +149,7 @@ class AgentEventService:
                 self.agent_monitor_service.update_processing(agent_monitor)
                 self.logger.info('挂断更新:%s', agent)
                 # self.data_handle_server.update_record(call_id, user_id=agent.user_id, user_name=agent.agent_name)
-                self.reprocessing_idle(AgentDelayStateData(saas_id, flow_id, agent_num, AgentServiceState.REPROCESSING, AgentScene.MANUAL))
+                self.reprocessing_idle(AgentDelayStateData(saas_id, flow_id, call_id, agent_num, AgentServiceState.REPROCESSING, AgentScene.MANUAL))
                 self.push_handler.push_on_call_end(saas_id, flow_id, agent_num, AgentScene.MANUAL, ServiceDirect.MANUAL_CALL.service_direct, '0')
                 self.push_handler.push_on_agent_work_report(saas_id, flow_id, agent_num, call_id, AgentScene.MANUAL, WorkStatus.AGENT_HANG_REPROCESSING)
                 self.push_handler.push_on_agent_report(saas_id, agent_num, AgentScene.MANUAL, AgentServiceState.REPROCESSING)
@@ -189,7 +192,7 @@ class AgentEventService:
 
         start_time = time.time()
         try:
-            self.logger.info('bot_event_channel, event_name=%s, call_id=%s, device_id=%s, is_agent=%s, agent_num=%s', event_name, call_id, device_id, is_agent, agent_num)
+            self.logger.info('bot_event_channel, event_name=%s, call_id=%s, device_id=%s, is_agent=%s, agent_num=%s, hangup_dir=%s, hangup_count=%s, answer_count=%s', event_name, call_id, device_id, is_agent, agent_num, call_info.hangup_dir, call_info.hangup_count, call_info.answer_count)
             agent = self.data_handle_server.get_agent(saas_id, agent_num)
             if not agent:
                 # self.logger.warn("bot event service channel agent is null %s %s %s %s %s", saas_id, event_name, caller, called,
@@ -203,6 +206,9 @@ class AgentEventService:
 
             # 信道发起事件,触发完成发起(或桥)&& 坐席侧
             if CHANNEL_ORIGINATE == event_name and is_agent:
+                if call_info.hangup_dir and call_info.hangup_dir == HangupDir.CUSTOMER_HANGUP.code:
+                    self.logger.info('bot_event_channel:already:hangup, event_name=%s, call_id=%s, device_id=%s, is_agent=%s, agent_num=%s, hangup_dir=%s, hangup_count=%s, answer_count=%s', event_name, call_id, device_id, is_agent, agent_num, call_info.hangup_dir, call_info.hangup_count, call_info.answer_count)
+                    return
                 self.push_handler.push_on_call_ring(saas_id, flow_id, agent_num, AgentScene.ROBOT, call_id, ServiceDirect.ROBOT_CALL.service_direct, called, caller, human_service_id)
                 self.push_handler.push_on_agent_work_report(saas_id, flow_id, agent_num, call_id, AgentScene.ROBOT, WorkStatus.AGENT_RINGING,phone=call_info.caller)
                 self.data_handle_server.update_record(call_id, transfer_user_id=agent.user_id,transfer_user_name=agent.agent_name)  #转接给客服以后更新转接人
@@ -223,7 +229,7 @@ class AgentEventService:
 
             if CHANNEL_HANGUP == event_name and is_agent:
                 self.agent_monitor_service.update_processing(agent_monitor)
-                self.reprocessing_idle(AgentDelayStateData(saas_id, flow_id, agent_num, AgentServiceState.REPROCESSING, AgentScene.ROBOT))
+                self.reprocessing_idle(AgentDelayStateData(saas_id, flow_id, call_id, agent_num, AgentServiceState.REPROCESSING, AgentScene.ROBOT))
                 self.push_handler.push_on_call_end(saas_id, flow_id, agent_num, AgentScene.ROBOT, ServiceDirect.ROBOT_CALL.service_direct, "0")
                 self.push_handler.push_on_agent_work_report(saas_id, flow_id, agent_num, call_id, AgentScene.ROBOT, WorkStatus.AGENT_HANG_REPROCESSING)
                 self.push_handler.push_on_agent_report(saas_id, agent_num, AgentScene.ROBOT, AgentServiceState.REPROCESSING)
@@ -250,7 +256,7 @@ class AgentEventService:
         self.agent_state_service.idle(agent.saas_id, agent.out_id, agent.phone_num)
         self.logger.info('reprocessing_idle_end')
         self.agent_monitor_service.update_idle(agent_monitor)
-        self.push_handler.push_on_agent_work_report(state_data.saas_id, state_data.flow_id, state_data.agent_num, "", state_data.scene, WorkStatus.AGENT_HANG_IDLE)
+        self.push_handler.push_on_agent_work_report(state_data.saas_id, state_data.flow_id, state_data.agent_num, state_data.call_id, state_data.scene, WorkStatus.AGENT_HANG_IDLE)
         self.agent_actionlog_service.insert_service_state(agent_monitor, AgentServiceState.IDLE, AgentLogState.REPROCESSING_IDLE)
 
 
@@ -910,7 +916,8 @@ class AgentStateService:
 
     def handle_release_agent_lock(self, choose_phone_num, saas_id, service_id='00000000000000000'):
         key = self._lock_key(saas_id, service_id, choose_phone_num)
-        self.redis_handler.redis.delete(key)
+        # self.redis_handler.redis.delete(key)
+        self.redis_handler.redis.expire(key, 3)
         self.logger.info('releaseAgent %s %s %s'% (saas_id, service_id, choose_phone_num))
 
     def handle_assign_time(self, saas_id, service_id, choose_phone_num):

+ 4 - 3
src/core/callcenter/api.py

@@ -177,9 +177,10 @@ class AgentStateData(BaseApi):
         self.phone_num = phone_num
 
 class AgentDelayStateData(BaseApi):
-    def __init__(self, saas_id=None, flow_id=None, agent_num=None, service_state=None, scene=None):
+    def __init__(self, saas_id=None, flow_id=None, call_id=None, agent_num=None, service_state=None, scene=None):
         self.saas_id = saas_id
         self.flow_id = flow_id
+        self.call_id = call_id
         self.agent_num = agent_num
         self.service_state = service_state
         self.scene = scene
@@ -217,8 +218,8 @@ class MakeCallContext(BaseApi):
                  device_id: Optional[str] = None,
                  eavesdrop: Optional[str] = None,
                  device_type: Optional[int] = None,
-                 timeout: Optional[int] = 60*2,
-                 originate_timeout: Optional[int] = 60*2,
+                 timeout: Optional[int] = 90,
+                 originate_timeout: Optional[int] = 90,
                  sip_header_map: Optional[Dict[str, str]] = {},
                  called_prefix: Optional[str] = "",
                  service_id: Optional[str] = None,

+ 1 - 0
src/core/callcenter/constant.py

@@ -43,6 +43,7 @@ READY_TIMES = "readyTimes"
 SEREVICE_TIMES = "serviceTimes"
 
 HOLD_MUSIC_PATH = '/freeswitch/music/hold.wav'
+AGENT_BUSY_MUSIC_PATH = '/freeswitch/music/agent_busy.wav'
 WaitingHangupMusicPath = '/freeswitch/music/sorry.wav'
 
 BASE_RECORD_PATH = '/freeswitch/record/'

+ 24 - 10
src/core/callcenter/esl/client.py

@@ -17,7 +17,8 @@ from src.core.callcenter import BizException
 from src.core.callcenter.cache import Cache
 from src.core.callcenter.api import MakeCallContext, DelayAction, CallInfo, DeviceInfo, NextCommand
 from src.core.callcenter.callback import Callback
-from src.core.callcenter.constant import SK, EMPTY, WaitingHangupMusicPath, SAAS_ID, HOLD_MUSIC_PATH
+from src.core.callcenter.constant import SK, EMPTY, WaitingHangupMusicPath, SAAS_ID, HOLD_MUSIC_PATH, \
+    AGENT_BUSY_MUSIC_PATH
 from src.core.callcenter.esl.constant.esl_constant import BRIDGE_VARIABLES, BRIDGE, HANGUP, NORMAL_CLEARING, SIP_HEADER, \
     SPACE, SOFIA, \
     ORIGINATE, PARK, SET, EAVESDROP, SMF_ALEG, EXECUTE, PLAYBACK, PAUSE, TRANSFER, UUID_TRANSFER, UUID_BROADCAST, \
@@ -163,6 +164,7 @@ class InboundClient:
     def exec_when_call_timeout(self, call_id, device_id):
         call_info = self.cache.get_call_info(call_id)
         if not call_info or not (device_id in call_info.device_list):
+            self.logger.info("do_delay_action:exec_when_call_timeout:return, device_id=%s, call_info=%s", device_id, call_info)
             return
         device_info = call_info.device_info_map.get(device_id)
         if device_info and device_info.answer_time is None:
@@ -201,21 +203,33 @@ class InboundClient:
         if not call_info:
             self.logger.info("exec_when_acd_timeout callInfo为空 callId: %s", call_id)
             return
+
+        def play_sorry(music_file):
+            self.hold_play(device_id, music_file)
+            self.play_timeout(call_id, timeout=30)
+            next_command = NextCommand(device_id = device_id, next_type=NextType.NEXT_HANGUP.code)
+            call_info.next_commands = [next_command]
+            self.cache.add_call_info(call_info)
+            # self.dataHandleServer.update_record(call_id, status= 0)
+            self.logger.info("waitingTimeOut 开始播放结束音乐 callId:%s customerDeviceId:%s playFile:%s", call_id,
+                             device_id, music_file)
+
+        next_cmd = call_info.next_commands[0] if call_info.next_commands and len(call_info.next_commands) >0 else None
+        self.logger.info("do_delay_action:exec_when_acd_timeout:next_cmd=%s, call_info=%s", next_cmd, call_info)
+        if next_cmd and next_cmd.next_type == NextType.NEXT_TRANSFER_CALL.code:
+            device_id = next_cmd.next_value
+            self.break0(device_id)
+            play_sorry(AGENT_BUSY_MUSIC_PATH)
+
         device_list = [v for k,v in call_info.device_info_map.items() if v.device_type == DeviceType.CUSTOMER]
+        self.logger.info("do_delay_action:exec_when_acd_timeout:device_list=%s, call_info=%s", device_list, call_info)
         if device_list and len(device_list) == 1:
             device_id = device_list[0].device_id
             self.break0(device_id)
             if not WaitingHangupMusicPath:
                 self.hangup_call(call_id, device_id, CallCause.WAITING_TIMEOUT)
                 return
-            self.hold_play(device_id, WaitingHangupMusicPath)
-            self.play_timeout(call_id, timeout=30)
-            next_command = NextCommand(device_id = device_id, next_type=NextType.NEXT_HANGUP.code)
-            call_info.next_commands = [next_command]
-            self.cache.add_call_info(call_info)
-            # self.dataHandleServer.update_record(call_id, status= 0)
-            self.logger.info("waitingTimeOut 开始播放结束音乐 callId:%s customerDeviceId:%s playFile:%s", call_id,
-                             device_id, WaitingHangupMusicPath)
+            play_sorry(WaitingHangupMusicPath)
 
     def make_call(self, context: MakeCallContext):
         # self.logger.info("拨打测试context:%s", context.__dict__)
@@ -603,7 +617,7 @@ class OutboundClient:
 
             return None
 
-        def answer(self, con, call_id, device_id, timeouts=120):
+        def answer(self, con, call_id, device_id, timeouts=55):
             con.execute("answer", "", device_id)
             # con.execute("bgapi", f"uuid_setvar {device_id} {sipHeaderCallId} {call_id}", device_id)
             con.execute("playback", HOLD_MUSIC_PATH, device_id)

+ 13 - 1
src/core/callcenter/esl/handler/playback_stop_handler.py

@@ -2,7 +2,8 @@
 # encoding:utf-8
 
 import src.core.callcenter.esl.utils.esl_event_util as EslEventUtil
-from src.core.callcenter.constant import HOLD_MUSIC_PATH
+from src.core.callcenter.api import NextCommand
+from src.core.callcenter.constant import HOLD_MUSIC_PATH, WaitingHangupMusicPath
 from src.core.callcenter.data_handler import *
 from src.core.callcenter.enumeration import NextType, CallCause
 from src.core.callcenter.esl.annotation import EslEventName
@@ -40,8 +41,19 @@ class PlaybackStopHandler(EslEventHandler):
             self.logger.info("PLAYBACK_STOP next_command is null, call_info:%s", call_info)
             return
 
+        # self.logger.info("PLAYBACK_STOP next_command:%s", next_command)
+        # if next_command and next_command.next_type == NextType.NEXT_TRANSFER_CALL.code:
+        #     device_id = next_command.next_value
+        #     self.inbound_client.break0(device_id)
+        #     self.inbound_client.hold_play(device_id, WaitingHangupMusicPath)
+        #     self.inbound_client.play_timeout(call_id, timeout=30)
+        #     next_command = NextCommand(device_id = device_id, next_type=NextType.NEXT_HANGUP.code)
+        #     call_info.next_commands = [next_command]
+        #     self.logger.info("PLAYBACK_STOP 开始播放结束音乐 callId:%s customerDeviceId:%s playFile:%s", call_id, device_id, WaitingHangupMusicPath)
+
         if NextType.NEXT_HANGUP == next_command.next_type:
             call_info.end_time = device_info.end_time
             for _device_id in call_info.device_list:
                 self.inbound_client.hangup_call(call_id, _device_id, CallCause.PLAYBACK_STOP)
+
         self.cache.add_call_info(call_info)

+ 1 - 1
src/core/callcenter/views.py

@@ -197,7 +197,7 @@ def track_event():
     try:
         data = request.get_json()
         # 存入日志文件
-        app.logger.info(json.dumps(data))
+        # app.logger.info(json.dumps(data))
     except Exception as e:
         traceback.print_exc()
         app.logger.error('track_event:exception', e)

+ 3 - 0
src/core/voip/bot.py

@@ -631,6 +631,9 @@ class BotAgent:
                 acfg.natConfig.turnServer = f"stun:{self.host}:3478"
                 # acfg.natConfig.turnUsername = "username"
                 # acfg.natConfig.turnPassword = "password"
+                acfg.natConfig.udpKaIntervalSec = 30
+                acfg.natConfig.contactRewriteUse = 2
+                acfg.natConfig.sdpNatRewriteUse = 2
 
                 # Create the account
                 acc = Account(self, user_part=user_part)