刘威 пре 5 месеци
родитељ
комит
49acfff3b2
59 измењених фајлова са 297 додато и 126 уклоњено
  1. BIN
      src/.DS_Store
  2. BIN
      src/__pycache__/__init__.cpython-39.pyc
  3. BIN
      src/core/.DS_Store
  4. BIN
      src/core/__pycache__/__init__.cpython-39.pyc
  5. BIN
      src/core/__pycache__/datasource.cpython-39.pyc
  6. BIN
      src/core/__pycache__/server.cpython-39.pyc
  7. BIN
      src/core/callcenter/.DS_Store
  8. BIN
      src/core/callcenter/__pycache__/__init__.cpython-39.pyc
  9. BIN
      src/core/callcenter/__pycache__/acd.cpython-39.pyc
  10. BIN
      src/core/callcenter/__pycache__/cache.cpython-39.pyc
  11. BIN
      src/core/callcenter/__pycache__/call.cpython-39.pyc
  12. BIN
      src/core/callcenter/__pycache__/constant.cpython-39.pyc
  13. BIN
      src/core/callcenter/__pycache__/enumeration.cpython-39.pyc
  14. BIN
      src/core/callcenter/__pycache__/model.cpython-39.pyc
  15. BIN
      src/core/callcenter/__pycache__/snowflake.cpython-39.pyc
  16. BIN
      src/core/callcenter/__pycache__/web.cpython-39.pyc
  17. BIN
      src/core/callcenter/__pycache__/ws.cpython-39.pyc
  18. 1 1
      src/core/callcenter/acd.py
  19. 21 19
      src/core/callcenter/agent.py
  20. 47 11
      src/core/callcenter/call.py
  21. BIN
      src/core/callcenter/esl/.DS_Store
  22. BIN
      src/core/callcenter/esl/__pycache__/client.cpython-39.pyc
  23. BIN
      src/core/callcenter/esl/annotation/__pycache__/__init__.cpython-39.pyc
  24. 3 2
      src/core/callcenter/esl/client.py
  25. BIN
      src/core/callcenter/esl/constant/__pycache__/__init__.cpython-39.pyc
  26. BIN
      src/core/callcenter/esl/constant/__pycache__/esl_constant.cpython-39.pyc
  27. BIN
      src/core/callcenter/esl/constant/__pycache__/event_names.cpython-39.pyc
  28. BIN
      src/core/callcenter/esl/constant/__pycache__/sip_header_constant.cpython-39.pyc
  29. BIN
      src/core/callcenter/esl/handler/.DS_Store
  30. BIN
      src/core/callcenter/esl/handler/__pycache__/__init__.cpython-39.pyc
  31. BIN
      src/core/callcenter/esl/handler/__pycache__/channel_answer_handler.cpython-39.pyc
  32. BIN
      src/core/callcenter/esl/handler/__pycache__/channel_bridge_handler.cpython-39.pyc
  33. BIN
      src/core/callcenter/esl/handler/__pycache__/channel_hangup_complete_handler.cpython-39.pyc
  34. BIN
      src/core/callcenter/esl/handler/__pycache__/channel_hangup_handler.cpython-39.pyc
  35. BIN
      src/core/callcenter/esl/handler/__pycache__/channel_park_handler.cpython-39.pyc
  36. BIN
      src/core/callcenter/esl/handler/__pycache__/channel_progress_media_handler.cpython-39.pyc
  37. BIN
      src/core/callcenter/esl/handler/__pycache__/default_esl_event_handler.cpython-39.pyc
  38. BIN
      src/core/callcenter/esl/handler/__pycache__/detected_tone_handler.cpython-39.pyc
  39. BIN
      src/core/callcenter/esl/handler/__pycache__/dtmf_handler.cpython-39.pyc
  40. BIN
      src/core/callcenter/esl/handler/__pycache__/esl_event_handler.cpython-39.pyc
  41. BIN
      src/core/callcenter/esl/handler/__pycache__/playback_stop_handler.cpython-39.pyc
  42. BIN
      src/core/callcenter/esl/handler/__pycache__/record_stop_handler.cpython-39.pyc
  43. 1 1
      src/core/callcenter/esl/handler/channel_park_handler.py
  44. BIN
      src/core/callcenter/esl/utils/__pycache__/esl_event_util.cpython-39.pyc
  45. 107 0
      src/core/callcenter/model.py
  46. 18 5
      src/core/callcenter/web.py
  47. 1 1
      src/core/datasource.py
  48. 0 22
      src/core/logs/flask.log
  49. BIN
      src/core/voip/.DS_Store
  50. BIN
      src/core/voip/__pycache__/__init__.cpython-39.pyc
  51. BIN
      src/core/voip/__pycache__/asr.cpython-39.pyc
  52. BIN
      src/core/voip/__pycache__/bot.cpython-39.pyc
  53. 33 7
      src/core/voip/asr.py
  54. 65 57
      src/core/voip/bot.py
  55. BIN
      src/core/voip/incoming_call.wav
  56. BIN
      src/core/voip/scripts/1_00.wav
  57. BIN
      src/core/voip/scripts/2_00.wav
  58. BIN
      src/core/voip/scripts/3_00.wav
  59. BIN
      src/core/voip/scripts/4_00.wav

BIN
src/__pycache__/__init__.cpython-39.pyc


BIN
src/core/.DS_Store


BIN
src/core/__pycache__/__init__.cpython-39.pyc


BIN
src/core/__pycache__/datasource.cpython-39.pyc


BIN
src/core/__pycache__/server.cpython-39.pyc


BIN
src/core/callcenter/.DS_Store


BIN
src/core/callcenter/__pycache__/__init__.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/acd.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/cache.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/call.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/constant.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/enumeration.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/model.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/snowflake.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/web.cpython-39.pyc


BIN
src/core/callcenter/__pycache__/ws.cpython-39.pyc


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

@@ -11,7 +11,7 @@ class AcdService:
     def __init__(self, client, logger):
         self.client = client
         self.logger = logger
-        # self.call_service = CallService(client, logger)
+        self.call_service = CallService(client, logger)
         # scheduler = BackgroundScheduler()
         # scheduler.add_job(self.try_transfer_agent, 'interval', seconds=5, max_instances=1)
         # scheduler.start()

+ 21 - 19
src/core/callcenter/agent.py

@@ -1,63 +1,65 @@
 #!/usr/bin/env python3
 # encoding:utf-8
+from src.core.callcenter.model import AgentActionRequest, AgentInfo, AgentQueryRequest, AgentRequest
 
 
 class AgentService:
 
-    def __init__(self):
-        pass
+    def __init__(self, client, logger):
+        self.inbound_client = client
+        self.logger = logger
 
-    def enable(self):
+    def enable(self, request: AgentActionRequest):
         pass
 
-    def disable(self):
+    def disable(self, request: AgentActionRequest):
         pass
 
-    def checkin(self):
+    def checkin(self, request: AgentActionRequest):
         pass
 
-    def checkout(self):
+    def checkout(self, request: AgentActionRequest):
         pass
 
-    def busy(self):
+    def busy(self, request: AgentActionRequest):
         pass
 
-    def idle(self):
+    def idle(self, request: AgentActionRequest):
         pass
 
-    def assign(self):
+    def assign(self, request: AgentActionRequest):
         pass
 
-    def idle_agent_exist(self):
+    def idle_agent_exist(self, request: AgentActionRequest):
         pass
 
-    def turn_on(self):
+    def turn_on(self, request: AgentActionRequest):
         pass
 
-    def hangup(self):
+    def hangup(self, request: AgentActionRequest):
         pass
 
-    def listen(self):
+    def listen(self, request: AgentActionRequest):
         pass
 
-    def get_and_check_phone(self):
+    def get_and_check_phone(self, request: AgentActionRequest):
         pass
 
-    def add(self):
+    def add(self, agent: AgentRequest):
         pass
 
-    def update(self):
+    def update(self, agent: AgentRequest):
         pass
 
     def detail(self, saas_id, agent_number):
         pass
 
-    def count(self):
+    def count(self, request: AgentQueryRequest):
         pass
 
-    def query_page(self):
+    def query_page(self, request: AgentQueryRequest):
         pass
 
-    def delete(self):
+    def delete(self, sass_id, agent_number):
         pass
 

+ 47 - 11
src/core/callcenter/call.py

@@ -5,7 +5,7 @@ import time
 
 import src.core.callcenter.cache as Cache
 from src.core.callcenter.constant import saasId, HOLD_MUSIC_PATH
-from src.core.callcenter.enumeration import CallCause, Direction, NextType, DeviceType
+from src.core.callcenter.enumeration import CallCause, Direction, NextType, DeviceType, CdrType
 from src.core.callcenter.model import MakeCallRequest, AgentInfo, CallInfo, HangupCallRequest, CheckInCallRequest, \
     DeviceInfo, NextCommand
 from src.core.callcenter.snowflake import Snowflake
@@ -14,11 +14,9 @@ from src.core.callcenter.snowflake import Snowflake
 class CallService:
 
     def __init__(self, client, logger):
-        worker_id = 1
-        data_center_id = 1
         self.client = client
         self.logger = logger
-        self.snowflake = Snowflake(worker_id, data_center_id)
+        self.snowflake = Snowflake(worker_id=1, data_center_id=1)
 
     def call(self, request: MakeCallRequest, agent: AgentInfo):
         call_id = 'C' + self.snowflake.next_id()
@@ -45,21 +43,59 @@ class CallService:
         self.client.bridge_break(devices.get(0))
         self.client.hold_play(devices.get(0), HOLD_MUSIC_PATH)
 
+    def cancel_hold(self, call_info: CallInfo, device_id):
+        self.client.bridge_call(call_info.call_id, call_info.device_list[0], call_info.device_list[1])
+
     def transfer(self, call_info: CallInfo, agent_number, service_id):
+        caller = call_info.called
+        call_id = call_info.call_id
         agent = Cache.get_agent_info(call_info.agent_key)
         device_id = 'D' + self.snowflake.next_id()
         now = lambda: int(round(time.time() * 1000))
 
-        device = DeviceInfo(device_id=device_id, caller=call_info.called, called=agent_number)
+        device = DeviceInfo(device_id=device_id, caller=caller, display=caller, called=agent_number, call_id=call_id,
+                            call_time=now, cdr_type=CdrType.TRANSFER.code, device_type=DeviceType.AGENT.code)
+        call_info.device_list.append(device_id)
+        call_info.caller = agent_number
+        call_info.device_info_map[device_id] = device
+        call_info.next_commands.append(NextCommand(device.device_id, NextType.NEXT_TRANSFER_CALL, call_info.device_list[0]))
+        call_info.agent_key = agent_number
+        # agent.sip_server
+        route_gateway = Cache.get_route_gateway(saasId)
+        Cache.add_call_info(call_info)
+        self.client.make_call(route_gateway, caller, agent_number, call_id, device_id)
 
     def hangup(self, request: HangupCallRequest):
-        pass
+        call_info = Cache.get_call_info(request.call_id)
+        if not call_info:
+            self.logger.info('hangup call not exist callId: %s', request.call_id)
+            return
+        devices = call_info.device_list
+        if not devices:
+            self.logger.info('hangup deviceList is null callId: %s', request.call_id)
+            return
+        self.hangup_all(call_info, CallCause.AGENT_HANGUP_CALL)
 
-    def hangup_all(self, call_info: CallInfo, case_enum: CallCause):
-        pass
+    def hangup_all(self, call_info: CallInfo, case_enum=CallCause.DEFAULT):
+        devices = call_info.device_list
+        if not devices:
+            self.logger.info('hangupCallAll skip 已全部挂断 callId: %s', call_info.call_id)
+            return
+        for device in devices:
+            self.client.hangup_call(call_info.call_id, device, case_enum)
 
-    def hangup_by_call_id(self, call_id):
-        pass
+    def hangup_call(self, call_id):
+        call_info = Cache.get_call_info(call_id)
+        if not call_info:
+            self.logger.info('hangup call not exist callId: %s', call_id)
+            return
+        devices = call_info.device_list
+        if not devices:
+            self.logger.info('hangup deviceList is null callId: %s', call_id)
+            return
+        for device in devices:
+            self.client.hangup_call(call_info.call_id, device, CallCause.RESTART)
 
     def checkin_call(self, request: CheckInCallRequest):
-        pass
+        agent = Cache.get_agent_info(request.agent_number)
+        return self.client.show_channel(agent.device_id)

BIN
src/core/callcenter/esl/.DS_Store


BIN
src/core/callcenter/esl/__pycache__/client.cpython-39.pyc


BIN
src/core/callcenter/esl/annotation/__pycache__/__init__.cpython-39.pyc


+ 3 - 2
src/core/callcenter/esl/client.py

@@ -75,8 +75,8 @@ class InboundClient:
                     time.sleep(3)
                     self.start()
                 else:
-                    threading.Thread(target=self.process_esl_event, args=(e,)).start()
-                    # self.choose_thread_pool_executor(e).submit(self.process_esl_event, e)
+                    # threading.Thread(target=self.process_esl_event, args=(e,)).start()
+                    self.choose_thread_pool_executor(e).submit(self.process_esl_event, e)
 
     def choose_thread_pool_executor(self, e):
         call_id = EslEventUtil.getCallId(e)
@@ -356,6 +356,7 @@ class InboundClient:
     def show_channel(self, device_id):
         msg = self.con.api("show", " channels like " + device_id + " as json")
         print('show_channel::', msg)
+        return msg
 
     def expression(self, template, params):
         for key, value in params.items():

BIN
src/core/callcenter/esl/constant/__pycache__/__init__.cpython-39.pyc


BIN
src/core/callcenter/esl/constant/__pycache__/esl_constant.cpython-39.pyc


BIN
src/core/callcenter/esl/constant/__pycache__/event_names.cpython-39.pyc


BIN
src/core/callcenter/esl/constant/__pycache__/sip_header_constant.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/.DS_Store


BIN
src/core/callcenter/esl/handler/__pycache__/__init__.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/channel_answer_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/channel_bridge_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/channel_hangup_complete_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/channel_hangup_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/channel_park_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/channel_progress_media_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/default_esl_event_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/detected_tone_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/dtmf_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/esl_event_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/playback_stop_handler.cpython-39.pyc


BIN
src/core/callcenter/esl/handler/__pycache__/record_stop_handler.cpython-39.pyc


+ 1 - 1
src/core/callcenter/esl/handler/channel_park_handler.py

@@ -23,7 +23,7 @@ class ChannelParkHandler(EslEventHandler):
     def process_fxo_calling(self, event):
         kwargs = json.loads(event.serialize('json'))
         destination = self.bot_agent.register(**kwargs)
-        #destination = '1001@pbx.fuxicarbon.com'
+        # destination = '1001'
         # 获取通话的 UUID
         call_uuid = event.getHeader("Unique-ID")
         service = event.getHeader("variable_service")

BIN
src/core/callcenter/esl/utils/__pycache__/esl_event_util.cpython-39.pyc


+ 107 - 0
src/core/callcenter/model.py

@@ -41,6 +41,113 @@ class MakeCallRequest:
         return cls(**data)
 
 
+class AgentRequest:
+    def __init__(self, saas_id, agent_number, agent_name, out_id, agent_password, agent_type, phone_number, distribute,
+                 agent_state, identity_type):
+        # 租户隔离
+        self.saas_id = saas_id
+        # 坐席工号, 生成返回
+        self.agent_number = agent_number
+        # 坐席名称
+        self.agent_name = agent_name
+        # 外部展示Id
+        self.out_id = out_id
+        # 坐席密码
+        self.agent_password = agent_password
+        # 坐席类型 0:普通坐席 ;1:组长:2:主管
+        self.agent_type = agent_type
+        # 分机号
+        self.phone_number = phone_number
+        # 分配标志  0:不参与排队;1:参与排队
+        self.distribute = distribute
+        # 账号状态 0:可用;1:禁用
+        self.agent_state = agent_state
+        # 身份状态
+        self.identity_type = identity_type
+
+
+class AgentQueryRequest:
+    def __init__(self, saas_id, agent_number, out_id, agent_type, agent_name):
+        # 租户隔离
+        self.saas_id = saas_id
+        # 坐席工号
+        self.agent_number = agent_number
+        # 坐席名称
+        self.agent_name = agent_name
+        # 外部展示Id
+        self.out_id = out_id
+        # 坐席类型 0:普通坐席 ;1:组长:2:主管
+        self.agent_type = agent_type
+
+
+class AgentActionRequest:
+    """
+    坐席操作
+    """
+
+    def __init__(self, saas_id, agent_id, agent_number, out_id, identity_type, scene):
+        self.saas_id =saas_id
+        # 坐席工号
+        self.agent_id = agent_id
+        # # 坐席工号
+        self.agent_number = agent_number
+        # 外部展示Id
+        self.out_id = out_id,
+        # 身份状态
+        self.identity_type = identity_type
+        # 场景 manual:手动外呼; robot:机器人外呼; monitor:监听
+        self.scene = scene
+
+    def from_json(self, data):
+        return self(**data)
+
+
+class AgentMonitor:
+    def __init__(self, id=None, saas_id=None, agent_num=None, out_id=None,
+                 check_state=1, check_scene=None, check_in_time =None,
+                 check_out_time=None, service_state=0, busy_time=None,
+                 idle_time=None, call_time=None, hang_time=None,
+                 heart_state=None, heart_time=None, session_id = None,
+                 is_delete=0, update_time=None, create_time=None):
+        self.id = id
+        # 租户隔离
+        self.saas_id = saas_id
+        # 坐席工号
+        self.agent_num = agent_num
+        # 使用方id
+        self.out_id = out_id
+        # 是否签入 0:是 1: 否  默认未签入
+        self.check_state = check_state
+        # 迁入时scene
+        self.check_scene = check_scene
+        # 签入时间
+        self.check_in_time = check_in_time
+        # 签出时间
+        self.check_out_time = check_out_time
+        # 坐席服务状态 0:签出 1:忙碌 2:空闲
+        self.service_state = service_state
+        # 置忙时间
+        self.busy_time = busy_time
+        # 置闲时间
+        self.idle_time = idle_time
+        # 拨通时间
+        self.call_time = call_time
+        # 挂断时间
+        self.hang_time = hang_time
+        # 心跳状态
+        self.heart_state = heart_state
+        # 上次正常心跳时间
+        self.heart_time = heart_time
+        #
+        self.session_id = session_id
+        # 删除标识
+        self.is_delete = is_delete
+        # 更新时间
+        self.update_time = update_time
+        # 创建时间
+        self.create_time = create_time
+
+
 class HangupCallRequest:
     def __init__(self, saas_id, call_id, agent_number):
         # saasId(必填)

+ 18 - 5
src/core/callcenter/web.py

@@ -4,12 +4,12 @@
 import threading
 from logging.config import dictConfig
 
+from src.core.callcenter.agent import AgentService
 from src.core.callcenter.esl.client import InboundClient
-from flask import Flask, render_template_string
+from flask import Flask, request, render_template_string
 
 from src.core.callcenter.call import CallService
-from src.core.callcenter.model import MakeCallRequest, AgentInfo
-
+from src.core.callcenter.model import MakeCallRequest, AgentInfo, AgentActionRequest
 
 dictConfig({
         "version": 1,
@@ -48,7 +48,7 @@ app.config['SECRET_KEY'] = ''
 
 client = InboundClient(app.logger)
 call_service = CallService(client, app.logger)
-
+agent_service = AgentService(client, app.logger)
 
 @app.route('/')
 def index():
@@ -78,12 +78,14 @@ def index():
 
 @app.route('/open/agent/get-cdn-url', methods=['POST'])
 def get_cdn_url():
+
     """获取cdn地址"""
     return 'Hello World!'
 
 
 @app.route('/open/agent/get-init-config', methods=['POST'])
 def get_init_config():
+
     """获取初始化配置"""
     return 'Hello World!'
 
@@ -91,36 +93,47 @@ def get_init_config():
 @app.route('/open/agent/check-in', methods=['POST'])
 def check_in():
     """坐席签入"""
-    return 'Hello World!'
+    param = AgentActionRequest.from_json(json_object=request.json())
+    return agent_service.checkin(param)
 
 
 @app.route('/open/agent/check-out', methods=['POST'])
 def check_out():
     """坐席签出"""
+    param = AgentActionRequest.from_json(json_object=request.json())
+    return agent_service.checkin(param)
     return 'Hello World!'
 
 
 @app.route('/open/agent/busy', methods=['POST'])
 def busy():
     """坐席置忙"""
+    param = AgentActionRequest.from_json(json_object=request.json())
+    return agent_service.checkin(param)
     return 'Hello World!'
 
 
 @app.route('/open/agent/idle', methods=['POST'])
 def idle():
     """坐席置闲"""
+    param = AgentActionRequest.from_json(json_object=request.json())
+    return agent_service.checkin(param)
     return 'Hello World!'
 
 
 @app.route('/open/agent/turn-on', methods=['POST'])
 def turn_on():
     """接通"""
+    param = AgentActionRequest.from_json(json_object=request.json())
+    return agent_service.checkin(param)
     return 'Hello World!'
 
 
 @app.route('/open/agent/hang-up', methods=['POST'])
 def hang_up():
     """挂断"""
+    param = AgentActionRequest.from_json(json_object=request.json())
+    return agent_service.checkin(param)
     return 'Hello World!'
 
 

+ 1 - 1
src/core/datasource.py

@@ -117,7 +117,7 @@ class MysqlHandler:
 @singleton
 class RedisHandler:
 
-    def __init__(self, host='192.168.100.159', port=6379, db=0, password='^YHN&UJM'):
+    def __init__(self, host='192.168.100.195', port=6379, db=0, password='^YHN&UJM'):
         try:
             # host = '10.0.0.24'
             # host = RADIS_HOST

Разлика између датотеке није приказан због своје велике величине
+ 0 - 22
src/core/logs/flask.log


BIN
src/core/voip/.DS_Store


BIN
src/core/voip/__pycache__/__init__.cpython-39.pyc


BIN
src/core/voip/__pycache__/asr.cpython-39.pyc


BIN
src/core/voip/__pycache__/bot.cpython-39.pyc


+ 33 - 7
src/core/voip/asr.py

@@ -48,7 +48,8 @@ class TestSt:
 
     @classmethod
     def get_cached_token(cls):
-        # 检查是否已有缓存的Token且未过期
+        # 检查是否已有缓存的Token且未过期s):
+        #         # 检查是否已有缓存的Token且未
         if cls.token_cache["token"] and cls.token_cache["expire_time"]:
             current_time = int(time.time())
             if current_time < cls.token_cache["expire_time"]:
@@ -65,9 +66,11 @@ class TestSt:
         else:
             print("无法获取Token")
             return None
-    def __init__(self, tid):
+
+    def __init__(self, tid, message_receiver=None):
         self.__th = threading.Thread(target=self.__test_run)
         self.__id = tid
+        self.message_receiver = message_receiver
         self._Token = self.get_cached_token()
         self.sr = None
         print("开始")
@@ -87,9 +90,14 @@ class TestSt:
             url=URL,
             token=self._Token,
             appkey=APPKEY,
-            on_error=self.test_on_error,
+            on_sentence_begin=self.test_on_sentence_begin,
+            on_sentence_end=self.test_on_sentence_end,
+            on_start=self.test_on_start,
             on_result_changed=self.test_on_result_chg,
-            on_completed=self.test_on_completed
+            on_completed=self.test_on_completed,
+            on_error=self.test_on_error,
+            on_close=self.test_on_close,
+            callback_args=[self.__id]
         )
         self.sr.start(
             aformat="pcm",
@@ -99,10 +107,28 @@ class TestSt:
         )
         print("ASR session started.")
 
+    def test_on_sentence_begin(self, message, *args):
+        pass
+        # print("test_on_sentence_begin:{}".format(message))
+        # if self.message_receiver:
+        #     self.message_receiver(message, *args)
+
+    def test_on_sentence_end(self, message, *args):
+        # print("test_on_sentence_end:{}".format(message))
+        if self.message_receiver:
+            self.message_receiver(message, *args)
+
+    def test_on_start(self, message, *args):
+        print("test_on_start:{}".format(message))
+
     def test_on_error(self, message, *args):
-        print(f"Error occurred: {message}")
+        print("on_error args=>{}".format(args))
+
+    def test_on_close(self, *args):
+        print("on_close: args=>{}".format(args))
+
     def test_on_result_chg(self, message, *args):
-        print(f"Result changed: {message}")
+        print("test_on_chg:{}".format(message))
 
     def test_on_completed(self, message, *args):
-        print(f"Recognition completed: {message}")
+        print("on_completed:args=>{} message=>{}".format(args, message))

+ 65 - 57
src/core/voip/bot.py

@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 # encoding:utf-8
-
+import os
 import time
 import json
 import wave
@@ -12,7 +12,8 @@ from enum import Enum
 
 calls = {}
 recording_file = '/code/src/core/voip/incoming_call.wav'
-player_file = '/code/src/core/voip/test111.wav'
+player_file = '/code/src/core/voip/test222.wav'
+player_script_dir = '/code/src/core/voip/scripts/'
 
 
 class BotStatus(Enum):
@@ -36,13 +37,14 @@ class UserStatus(Enum):
 
 
 class MyAudioMediaPort(pj.AudioMediaPort):
-    def __init__(self, asr=None):
+    def __init__(self, call, asr=None):
         pj.AudioMediaPort.__init__(self)
         # 打开一个 .pcm 文件来保存音频流(可选:保存为 .wav)
         self.wav = wave.open(f"{recording_file}", "wb")
         self.wav.setnchannels(1)
         self.wav.setsampwidth(2)  # 假设每个样本是 16 位(2 字节)
         self.wav.setframerate(16000)
+        self.call = call
         self.asr = asr
 
     def onFrameRequested(self, frame):
@@ -53,17 +55,26 @@ class MyAudioMediaPort(pj.AudioMediaPort):
         # print("Received audio frame:", frame.buf, frame.size)
         if self.asr:  # 如果ASR实例存在,则发送音频数据
             self.asr.send_audio(frame.buf)
+        try:
+            asr_text = self.call.user_asr_text_queue.get(block=False)
+            print(asr_text)
+            if asr_text:
+                self.call.send_bot_speaker(self.call.scripts.get())
+        except:
+            pass
 
 
 class MyAudioMediaPlayer(pj.AudioMediaPlayer):
 
-    def __init__(self, sink):
+    def __init__(self, sink, on_complete=None):
         pj.AudioMediaPlayer.__init__(self)
         self.sink = sink
+        self.on_complete = on_complete
 
     def onEof2(self):
-        print('player complete')
         self.stopTransmit(self.sink)
+        if self.on_complete:
+            self.on_complete()
 
 
 # Subclass to extend the Account and get notifications etc.
@@ -99,13 +110,24 @@ class MyCall(pj.Call):
         self.call_id = call_id
         self.kwargs = kwargs
         self.audio_port = None
-        self.aud_med = None
         self.recorder = None
+        self.aud_med = None
         self.player = None  # 用于播放录音的媒体播放器
         self.asr = None
-        # from src.core.voip.asr import TestSt
-        # self.asr = TestSt(call_id)  # 创建ASR实例
-        # self.asr.start()  # 启动ASR线程
+        self.user_asr_text_queue = queue.Queue(maxsize=100)
+        self.scripts = self.build_demo_script()
+
+        from src.core.voip.asr import TestSt
+        self.asr = TestSt(call_id, message_receiver=self.on_receiver_asr_result)  # 创建ASR实例
+        self.asr.start()  # 启动ASR线程
+
+    def build_demo_script(self):
+        res = queue.Queue(maxsize=10)
+        for file in os.listdir(player_script_dir):
+            file = os.path.join(player_script_dir, file)
+            print('build_demo_script::', file)
+            res.put(file)
+        return res
 
     def onDtmfDigit(self, prm):
         digit = prm.digit
@@ -139,30 +161,33 @@ class MyCall(pj.Call):
         for media in call_info.media:
             if media.type == pj.PJMEDIA_TYPE_AUDIO and \
                (media.status == pj.PJSUA_CALL_MEDIA_ACTIVE):
+                print("Call Media state 111: ", call_info.stateText)
                 self.aud_med = self.getAudioMedia(media.index)
                 try:
-                    self.audio_port = MyAudioMediaPort(self.asr)
-                    self.audio_port.createPort("Incoming Call Port", self.build_audio_format())
-                    self.aud_med.startTransmit(self.audio_port)
-
-                    # 录制来电声音
-                    # self.recorder = pj.AudioMediaRecorder()
-                    # self.recorder.createRecorder(recording_file)
-                    # self.aud_med.startTransmit(self.recorder)
-                    # self.send_audio_to_asr()
-
-                    # 播放其它录音文件
-                    self.player = MyAudioMediaPlayer(self.aud_med)
-                    #self.player = pj.AudioMediaPlayer()
-                    self.player.createPlayer(player_file)
-                    self.player.startTransmit(self.aud_med)
-
-                    # 显示播放进度
-                    #threading.Thread(target=self.display_playback_progress, args=()).start()
-                    # self.display_playback_progress()
+                    # 建立双向通道
+                    self.receive_user_speaker()
+                    self.send_bot_speaker(self.scripts.get())
                 except Exception as e:
                     traceback.print_exc()
 
+    def receive_user_speaker(self):
+        self.audio_port = MyAudioMediaPort(self, self.asr)
+        self.audio_port.createPort("Incoming Call Port", self.build_audio_format())
+        self.aud_med.startTransmit(self.audio_port)
+
+    def send_bot_speaker(self, player_file):
+        if not player_file or not os.path.exists(player_file):
+            return
+        self.player = MyAudioMediaPlayer(self.aud_med, on_complete=self.on_media_player_complete)
+        self.player.createPlayer(player_file)
+        self.player.startTransmit(self.aud_med)
+
+    def on_receiver_asr_result(self, message, *args):
+        self.user_asr_text_queue.put(message)
+
+    def on_media_player_complete(self):
+        print('player complete')
+
     def build_audio_format(self):
         fmt = pj.MediaFormatAudio()
         fmt.type = pj.PJMEDIA_TYPE_AUDIO
@@ -173,31 +198,13 @@ class MyCall(pj.Call):
         fmt.bitsPerSample = 16  # 每个采样的位数
         return fmt
 
-    def display_playback_progress(self):
-        while self.player:
-            # 获取当前播放位置和总时长
-            current_pos = self.player.getPos()
-            player_info = self.player.getInfo()
-            # print(current_pos, player_info.sizeBytes, player_info.sizeSamples)
-            total_duration = player_info.sizeBytes
-            if current_pos >= total_duration:
-                self.player.stopTransmit(self.aud_med)
-            # if total_duration > 0:
-            #     print(current_pos, total_duration)
-                # progress = (current_pos / total_duration) * 100
-                # print(f"播放进度: {progress:.2f}%")
-            # if current_pos >= total_duration:
-            #     self.player.stop()
-
-            # time.sleep(0.1)  # 每隔1秒更新一次进度
-
 
 class BotAgent:
 
     def __init__(self, logger, user_part_range=range(1001, 1011), host="192.168.100.159", port="5060", password="slibra@#123456"):
         self.logger = logger
         self.user_part_range, self.host, self.port, self.password = user_part_range, host, port, password
-        self.pool = queue.Queue(maxsize=len(user_part_range))
+        self.user_part_pool = queue.Queue(maxsize=len(user_part_range))
         self.accounts = {}
         self.calls = {}
         self.ep = pj.Endpoint()
@@ -207,10 +214,11 @@ class BotAgent:
     def create_pjsua2(self):
         # Create and initialize the library
         ep_cfg = pj.EpConfig()
-        ep_cfg.uaConfig.threadCnt = 4
-        ep_cfg.uaConfig.mainThreadOnly = False
+        ep_cfg.uaConfig.threadCnt = 0
+        ep_cfg.uaConfig.mainThreadOnly = True
         ep_cfg.uaConfig.maxCalls = 10
         ep_cfg.uaConfig.maxAccounts = 10
+        ep_cfg.medConfig.noVad = True
         ep_cfg.logConfig.level = 5
         ep_cfg.logConfig.consoleLevel = 5
         self.ep.libCreate()
@@ -240,14 +248,14 @@ class BotAgent:
             acfg.natConfig.iceEnabled = True
             acfg.natConfig.turnEnabled = True
             acfg.natConfig.turnServer = "stun:192.168.100.159:3478"
-            #acfg.natConfig.turnUsername = "username"
-            #acfg.natConfig.turnPassword = "password"
+            # acfg.natConfig.turnUsername = "username"
+            # acfg.natConfig.turnPassword = "password"
 
             # Create the account
             acc = Account(self, user_part)
             acc.create(acfg)
 
-            self.pool.put(user_part)
+            self.user_part_pool.put(user_part)
             self.accounts[user_part] = acc
 
         while not self.is_stopping:
@@ -267,9 +275,9 @@ class BotAgent:
             self.release(user_part)
 
     def register(self, **kwargs):
-        user_part = self.pool.get()
+        user_part = self.user_part_pool.get()
         acc = self.accounts.get(user_part)
-        self.logger.info('register, user_part :%d, pool.size :%d', user_part, self.pool.qsize())
+        self.logger.info('register, user_part :%d, pool.size :%d', user_part, self.user_part_pool.qsize())
         if acc:
             print('register==========>', acc.getId())
             # ps = pj.PresenceStatus()
@@ -296,10 +304,10 @@ class BotAgent:
                         return True
             return False
 
-        if element_in_queue(self.pool, user_part):
+        if element_in_queue(self.user_part_pool, user_part):
             return
-        self.pool.put(user_part)
-        self.logger.info("release, user_part :%d, pool.size :%d", user_part, self.pool.qsize())
+        self.user_part_pool.put(user_part)
+        self.logger.info("release, user_part :%d, pool.size :%d", user_part, self.user_part_pool.qsize())
 
     def destroy(self):
         self.is_stopping = True

BIN
src/core/voip/incoming_call.wav


BIN
src/core/voip/scripts/1_00.wav


BIN
src/core/voip/scripts/2_00.wav


BIN
src/core/voip/scripts/3_00.wav


BIN
src/core/voip/scripts/4_00.wav


Неке датотеке нису приказане због велике количине промена