|
@@ -15,7 +15,7 @@ import concurrent.futures
|
|
|
|
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
|
|
|
|
|
-import src.core.callcenter.cache as Cache
|
|
|
+from src.core.callcenter.cache import Cache
|
|
|
from src.core.callcenter.api import MakeCallContext, DelayAction, CallInfo, DeviceInfo, NextCommand
|
|
|
from src.core.callcenter.constant import SK, EMPTY, CTI_ENGINE_DELAY_ACTION_LOCK, HOLD_MUSIC_PATH, saasId
|
|
|
from src.core.callcenter.esl.constant.esl_constant import BRIDGE_VARIABLES, BRIDGE, HANGUP, NORMAL_CLEARING, SIP_HEADER, SPACE, SPLIT, SOFIA, \
|
|
@@ -30,18 +30,19 @@ from src.core.callcenter.esl.handler.default_esl_event_handler import DefaultEsl
|
|
|
from src.core.callcenter.snowflake import Snowflake
|
|
|
from src.core.datasource import SERVE_HOST
|
|
|
from src.core.voip.constant import *
|
|
|
-from src.core.callcenter.dao import *
|
|
|
-
|
|
|
+from src.core.callcenter.data_handler import *
|
|
|
class InboundClient:
|
|
|
|
|
|
- def __init__(self, agent, logger):
|
|
|
+ def __init__(self, agent, app):
|
|
|
self.con = None
|
|
|
self.thread_num = 12
|
|
|
self.is_stopping = False
|
|
|
- self.logger = logger
|
|
|
+ self.app = app
|
|
|
+ self.logger = app.logger
|
|
|
self.bot_agent = agent
|
|
|
+ self.cache = Cache(app)
|
|
|
self.handler_table = self.scan_esl_event_handlers()
|
|
|
- self.default_event_handler = DefaultEslEventHandler(self, self.bot_agent, self.logger)
|
|
|
+ self.default_event_handler = DefaultEslEventHandler(self, self.bot_agent)
|
|
|
self.host, self.port, self.password = SERVE_HOST, '8021', '4918257983818884358'
|
|
|
self.executors = {x: concurrent.futures.ThreadPoolExecutor(max_workers=1) for x in range(self.thread_num)}
|
|
|
self.delay_action_executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
|
|
@@ -52,7 +53,7 @@ class InboundClient:
|
|
|
|
|
|
def submit_delay_action(self):
|
|
|
for name, member in DelayActionEnum.__members__.items():
|
|
|
- action_messages = Cache.get_delay_message(name)
|
|
|
+ action_messages = self.cache.get_delay_message(name)
|
|
|
for action_message in action_messages:
|
|
|
self.delay_action_executor.submit(self.do_delay_action, name, action_message)
|
|
|
|
|
@@ -72,7 +73,7 @@ class InboundClient:
|
|
|
handlers = {}
|
|
|
for _cls in classes:
|
|
|
items = handlers.get(_cls._esl_event_name, [])
|
|
|
- items.append(_cls(self, self.bot_agent, self.logger))
|
|
|
+ items.append(_cls(self, self.bot_agent))
|
|
|
handlers[_cls._esl_event_name] = items
|
|
|
return handlers
|
|
|
|
|
@@ -129,7 +130,7 @@ class InboundClient:
|
|
|
|
|
|
def do_delay_action(self, action, message):
|
|
|
delay_action = DelayAction.from_json(message)
|
|
|
- flag = Cache.lock_delay_action(delay_action.uuid)
|
|
|
+ flag = self.cache.lock_delay_action(delay_action.uuid)
|
|
|
if not flag:
|
|
|
self.logger.info("异步延迟执行操作重复 action:%s msg:%s", action, message)
|
|
|
return
|
|
@@ -146,7 +147,7 @@ class InboundClient:
|
|
|
self.exec_when_acd_timeout(delay_action.call_id)
|
|
|
|
|
|
def exec_when_call_timeout(self, call_id, device_id):
|
|
|
- call_info = Cache.get_call_info(call_id)
|
|
|
+ call_info = self.cache.get_call_info(call_id)
|
|
|
if not call_info or not (device_id in call_info.device_list):
|
|
|
return
|
|
|
device_info = call_info.device_info_map.get(device_id)
|
|
@@ -164,12 +165,12 @@ class InboundClient:
|
|
|
channel = self.show_channel(device_id)
|
|
|
if channel:
|
|
|
delay_action = DelayAction(call_id=call_id, device_id=device_id)
|
|
|
- Cache.add_delay_message(DelayActionEnum.CALL_TIMEOUT_DECR, delay_action, timeouts=20)
|
|
|
- Cache.add_call_info(call_info)
|
|
|
+ self.cache.add_delay_message(DelayActionEnum.CALL_TIMEOUT_DECR, delay_action, timeouts=20)
|
|
|
+ self.cache.add_call_info(call_info)
|
|
|
self.hangup_call(call_id, device_id, CallCause.CALL_TIMEOUT)
|
|
|
|
|
|
def exec_when_play_timeout(self, call_id):
|
|
|
- call_info = Cache.get_call_info(call_id)
|
|
|
+ call_info = self.cache.get_call_info(call_id)
|
|
|
if not call_info or not call_info.next_commands:
|
|
|
return
|
|
|
self.logger.debug("播放结束音乐失败,进行挂机 callId:%s", call_id)
|
|
@@ -179,7 +180,7 @@ class InboundClient:
|
|
|
self.hangup_call(call_id, device_id, CallCause.PLAY_TIMEOUT)
|
|
|
|
|
|
def exec_when_acd_timeout(self, call_id):
|
|
|
- call_info = Cache.get_call_info(call_id)
|
|
|
+ call_info = self.cache.get_call_info(call_id)
|
|
|
if not call_info:
|
|
|
self.logger.info("exec_when_acd_timeout callInfo为空 callId: {}", call_id)
|
|
|
return
|
|
@@ -189,7 +190,7 @@ class InboundClient:
|
|
|
self.bridge_break(device_id)
|
|
|
self.hold_play(device_id, HOLD_MUSIC_PATH)
|
|
|
self.play_timeout(call_id, timeout=30)
|
|
|
- Cache.add_call_info(call_info)
|
|
|
+ self.cache.add_call_info(call_info)
|
|
|
self.logger.info("waitingTimeOut 开始播放结束音乐 callId:%s customerDeviceId:%s playFile:%s", call_id,
|
|
|
device_id, HOLD_MUSIC_PATH)
|
|
|
|
|
@@ -268,7 +269,7 @@ class InboundClient:
|
|
|
def call_timeout(self, call_id, device_id, timeout):
|
|
|
"""呼叫超时主动挂机"""
|
|
|
delay_action = DelayAction(call_id=call_id, device_id=device_id)
|
|
|
- Cache.add_delay_message(DelayActionEnum.CALL_TIMEOUT_HANGUP.code, delay_action, timeouts=timeout)
|
|
|
+ self.cache.add_delay_message(DelayActionEnum.CALL_TIMEOUT_HANGUP.code, delay_action, timeouts=timeout)
|
|
|
|
|
|
def send_args(self, device_id, name, arg, con=None):
|
|
|
msg = ESL.ESLevent("sendmsg", device_id)
|
|
@@ -490,7 +491,7 @@ class InboundClient:
|
|
|
def play_timeout(self, call_id, timeout):
|
|
|
"""播放超时主动挂机"""
|
|
|
delay_action = DelayAction(call_id=call_id)
|
|
|
- Cache.add_delay_message(DelayActionEnum.PLAY_TIMEOUT_HANGUP.code, delay_action, timeouts=timeout)
|
|
|
+ self.cache.add_delay_message(DelayActionEnum.PLAY_TIMEOUT_HANGUP.code, delay_action, timeouts=timeout)
|
|
|
|
|
|
def listen(self, device_id1, device_id2, aleg=True, bleg=True):
|
|
|
"""监听"""
|
|
@@ -519,17 +520,19 @@ class InboundClient:
|
|
|
|
|
|
class OutboundClient:
|
|
|
|
|
|
- def __init__(self, agent, logger,app):
|
|
|
- self.logger = logger
|
|
|
+ def __init__(self, agent, app):
|
|
|
self.app = app
|
|
|
+ self.logger = app.logger
|
|
|
self.whitelist = []
|
|
|
self.update_whitelist() # 初始化加载白名单
|
|
|
|
|
|
# 定时更新白名单
|
|
|
threading.Thread(target=self.refresh_whitelist, daemon=True).start()
|
|
|
|
|
|
+
|
|
|
+ self.dataHandleServer = DataHandleServer(app)
|
|
|
#threading.Thread(target=self.start, args=('0.0.0.0', 8084, agent, logger)).start()
|
|
|
- server_thread = threading.Thread(target=self.start, args=('0.0.0.0', 8084, agent, logger))
|
|
|
+ server_thread = threading.Thread(target=self.start, args=('0.0.0.0', 8084, agent))
|
|
|
server_thread.daemon = True # 设置守护线程
|
|
|
server_thread.start()
|
|
|
|
|
@@ -553,6 +556,7 @@ class OutboundClient:
|
|
|
agent_nums = [agent.agent_num for agent in agents]
|
|
|
return agent_nums
|
|
|
|
|
|
+
|
|
|
class ESLRequestHandler(socketserver.BaseRequestHandler):
|
|
|
def setup(self):
|
|
|
try:
|
|
@@ -569,30 +573,39 @@ class OutboundClient:
|
|
|
caller_number = info.getHeader("Caller-Caller-ID-Number") # 获取来电号码
|
|
|
whitelist = self.server.load_whitelist()
|
|
|
|
|
|
- # 检查白名单
|
|
|
- if caller_number in whitelist:
|
|
|
- # 直接转接到人工坐席
|
|
|
- agents = self.server.load_agent_monitor()
|
|
|
- # 随机取一个坐席号
|
|
|
- destination = random.choice(agents)
|
|
|
- # 直接转接到人工坐席
|
|
|
- self.server.logger.info("Caller %s is in whitelist, directly transferring call, agents: %s, destination: %s", caller_number, agents,destination)
|
|
|
- return
|
|
|
-
|
|
|
-
|
|
|
call_id = 'C' + str(Snowflake().next_id())
|
|
|
new_device_id = 'D' + str(Snowflake().next_id())
|
|
|
|
|
|
kwargs = json.loads(info.serialize('json'))
|
|
|
kwargs['variable_sip_h_P-LIBRA-CallId'] = call_id
|
|
|
kwargs['variable_sip_h_P-LIBRA-DeviceId'] = new_device_id
|
|
|
- destination = self.server.agent.register(**kwargs)
|
|
|
- self.server.logger.info("debugger::device_id=%s, destination=%s, new_device_id=%s", device_id, destination, new_device_id)
|
|
|
-
|
|
|
- self.build_call_info(call_id, device_id, new_device_id, str(destination), **kwargs)
|
|
|
- Cache.add_device_user_part(device_id, destination)
|
|
|
- con.execute("bridge", "{sip_h_P-LIBRA-CallId=%s,sip_h_P-LIBRA-DeviceId=%s,origination_uuid=%s}user/%s"%(call_id, new_device_id, new_device_id, destination), device_id)
|
|
|
-
|
|
|
+ call_info = {
|
|
|
+ "session_id": call_id,
|
|
|
+ "time_begin": datetime.utcnow(),
|
|
|
+ "category": 0,
|
|
|
+ "phone": caller_number
|
|
|
+ }
|
|
|
+ # 检查白名单
|
|
|
+ if caller_number in whitelist:
|
|
|
+ agents = self.server.load_agent_monitor()
|
|
|
+ destination = random.choice(agents) # 随机取一个坐席号
|
|
|
+ # 直接转接到人工坐席
|
|
|
+ self.server.logger.info( "Caller %s is in whitelist, agents: %s, destination: %s", caller_number, agents, destination)
|
|
|
+ call_info['type']= 0
|
|
|
+ call_info['agent_num'] = destination
|
|
|
+ else:
|
|
|
+ #转到ai机器人
|
|
|
+ destination = self.server.agent.register(**kwargs)
|
|
|
+ self.server.logger.info("debugger::device_id=%s, destination=%s, new_device_id=%s", device_id, destination, new_device_id)
|
|
|
+ call_info['type'] = 1
|
|
|
+ call_info['service_category'] = 1
|
|
|
+ call_info['user_id'] = destination
|
|
|
+ call_info['user_name'] = f"机器人{destination}"
|
|
|
+ self.build_call_info(call_id, device_id, new_device_id, str(destination), **kwargs)
|
|
|
+ self.server.cache.add_device_user_part(device_id, destination)
|
|
|
+ con.execute("bridge", "{sip_h_P-LIBRA-CallId=%s,sip_h_P-LIBRA-DeviceId=%s,origination_uuid=%s}user/%s"%(call_id, new_device_id, new_device_id, destination), device_id)
|
|
|
+
|
|
|
+ self.server.dataHandleServer.create_record(call_info)
|
|
|
# destination = "user/1001"
|
|
|
# msg = ESL.ESLevent("sendmsg", uuid)
|
|
|
# msg.addHeader("call-command", "execute")
|
|
@@ -635,20 +648,22 @@ class OutboundClient:
|
|
|
call_info.device_list.append(new_device_id)
|
|
|
# call_info.next_commands.append(NextCommand(device_id, NextType.NEXT_CALL_BRIDGE.code, new_device_id))
|
|
|
call_info.device_info_map = {device_id: device_custom, new_device_id: device_bot}
|
|
|
- Cache.add_call_info(call_info)
|
|
|
+ self.server.cache.add_call_info(call_info)
|
|
|
|
|
|
|
|
|
class CustomTCPServer(socketserver.TCPServer):
|
|
|
- def __init__(self, server_address, RequestHandlerClass, agent, logger,whitelist_loader,load_agent_monitor):
|
|
|
+ def __init__(self, server_address, RequestHandlerClass, agent, app,whitelist_loader,load_agent_monitor,dataHandleServer):
|
|
|
self.agent = agent
|
|
|
- self.logger = logger
|
|
|
+ self.cache = Cache(app)
|
|
|
+ self.logger = app.logger
|
|
|
self.load_whitelist = whitelist_loader
|
|
|
self.load_agent_monitor = load_agent_monitor
|
|
|
+ self.dataHandleServer = dataHandleServer
|
|
|
super().__init__(server_address, RequestHandlerClass)
|
|
|
|
|
|
- def start(self, HOST='0.0.0.0', PORT=8084, agent=None, logger=None):
|
|
|
+ def start(self, HOST='0.0.0.0', PORT=8084, agent=None):
|
|
|
# HOST, PORT = "0.0.0.0", 8084
|
|
|
# 创建一个 TCP 服务器
|
|
|
- with self.CustomTCPServer((HOST, PORT), self.ESLRequestHandler, agent, logger, self.load_whitelist, self.load_agent_monitor) as server:
|
|
|
+ with self.CustomTCPServer((HOST, PORT), self.ESLRequestHandler, agent, self.app, self.load_whitelist, self.load_agent_monitor,self.dataHandleServer) as server:
|
|
|
self.logger.info(f"ESL server listening on {HOST}:{PORT}")
|
|
|
server.serve_forever()
|