Browse Source

Merge branch 'master' of ssh://gitlab.fuxicarbon.com:1111/client_service/voice-gateway-service

刘威 4 tháng trước cách đây
mục cha
commit
9afb42fa53
2 tập tin đã thay đổi với 118 bổ sung127 xóa
  1. 30 38
      src/core/callcenter/api.py
  2. 88 89
      src/core/voip/bot.py

+ 30 - 38
src/core/callcenter/api.py

@@ -491,15 +491,13 @@ class RouteGateway:
 
 #机器人外呼
 class BotChatRequest:
-    def __init__(self, node_id=None, user_id=None, session_id=None, record_id=None,
-                 task_id=None, asr_text=None, key_input=None, ext:Dict={}):
-        self.node_id = node_id  # 节点id
-        self.user_id = user_id  # 用户id
-        self.session_id = session_id  # 会话id
-        self.record_id = record_id  # 唯一标识
-        self.task_id = task_id  # 机器人任务id
-        self.event_type = None  # 1:电话接通,2:用户语音asr结果上传,3:用户按键输入,4:用户挂断电话,5:读取外呼结果(可在外呼过程中调用,不一定在结束之后),6:用户不说话超时,7:TTS或录音文件播放完毕或被打断,8:ASR错误导致挂断电话,9:TTS错误导致挂断电话,10:其它系统错误导致挂断电话
-        self.asr_text = asr_text  # asr识别的文本
+    def __init__(self, nodeId=None, userId=None, sessionId=None, taskId=None, asrText=None, recordId=None, ext:Dict={}):
+        self.nodeId = nodeId  # 节点id
+        self.userId = userId  # 用户id
+        self.sessionId = sessionId  # 会话id
+        self.recordId = recordId  # 唯一标识
+        self.taskId = taskId  # 机器人任务id
+        self.asrText = asrText  # asr识别的文本
         self.ext = ext
 
     def to_json_string(self):
@@ -518,9 +516,9 @@ class BotChatRequest:
 
 
 class ChatAction:
-    def __init__(self, action_code=None, action_content=None):
-        self.action_code = action_code         # normal:正常通话;hang:挂断;transfer:转人工
-        self.action_content = action_content   # 动作内容
+    def __init__(self, actionCode=None, actionContent=None):
+        self.actionCode = actionCode         # normal:正常通话;hang:挂断;transfer:转人工
+        self.actionContent = actionContent   # 动作内容
 
     def to_json_string(self):
         return json.dumps(self.__dict__, ensure_ascii=False)
@@ -528,17 +526,17 @@ class ChatAction:
     @classmethod
     def from_json(cls, json_data):
         return cls(
-            action_code=json_data.get("action_code"),
-            action_content=json_data.get("action_content")
+            actionCode=json_data.get("actionCode"),
+            actionContent=json_data.get("actionContent")
         )
 
 
 class ChatContent:
-    def __init__(self, content_type=None, content=None, voice_url=None, voice_content=None):
-        self.content_type = content_type   # 播放类型
+    def __init__(self, contentType=None, content=None, voiceUrl=None, voiceContent=None):
+        self.contentType = contentType   # 播放类型
         self.content = content             # 播放内容
-        self.voice_url = voice_url         # 语音地址
-        self.voice_content = voice_content # 语音文本
+        self.voiceUrl = voiceUrl         # 语音地址
+        self.voiceContent = voiceContent # 语音文本
 
     def to_json_string(self):
         return json.dumps(self.__dict__, ensure_ascii=False)
@@ -546,33 +544,29 @@ class ChatContent:
     @classmethod
     def from_json(cls, json_data):
         return cls(
-            content_type=json_data.get("content_type"),
+            contentType=json_data.get("contentType"),
             content=json_data.get("content"),
-            voice_url=json_data.get("voice_url"),
-            voice_content=json_data.get("voice_content")
+            voiceUrl=json_data.get("voiceUrl"),
+            voiceContent=json_data.get("voiceContent")
         )
 
 
 class ChatMessage:
-    def __init__(self, node_id=None, contents=None, interruptable=None, wait_time=None,
-                 action=None, talk_time_out=None,dtmf_type=None):
-        self.node_id = node_id   # 节点id
+    def __init__(self, nodeId=None, contents=None,  waitTime=None,
+                 action=None,inputType=None):
+        self.nodeId = nodeId   # 节点id
         self.contents = contents if contents is not None else []   # 内容列表
-        self.interruptable = interruptable   # 是否可打断
-        self.wait_time = wait_time   # 用户静默时长
+        self.waitTime = waitTime   # 用户静默时长
         self.action = action        # 动作代码
-        self.talk_time_out = talk_time_out     # 用户通话时长
-        self.dtmf_type = dtmf_type   # dtmf类型
+        self.inputType = inputType   # dtmf类型
 
     def to_json_string(self):
         return json.dumps({
-            "node_id": self.node_id,
+            "nodeId": self.nodeId,
             "contents": [content.__dict__ for content in self.contents],
-            "interruptable": self.interruptable,
-            "wait_time": self.wait_time,
+            "waitTime": self.waitTime,
             "action": self.action.__dict__ if self.action else None,
-            "talk_time_out": self.talk_time_out,
-            "dtmf_type": self.dtmf_type
+            "inputType": self.inputType
         }, ensure_ascii=False)
 
     @classmethod
@@ -580,13 +574,11 @@ class ChatMessage:
         contents = [ChatContent.from_json(item) for item in json_data.get("contents", [])]
         action = ChatAction.from_json(json_data.get("action", {})) if json_data.get("action") else None
         return cls(
-            node_id=json_data.get("node_id"),
+            nodeId=json_data.get("nodeId"),
             contents=contents,
-            interruptable=json_data.get("interruptable"),
-            wait_time=json_data.get("wait_time"),
+            waitTime=json_data.get("waitTime"),
             action=action,
-            talk_time_out=json_data.get("talk_time_out"),
-            dtmf_type=json_data.get("dtmf_type")
+            inputType=json_data.get("inputType")
         )
 
 

+ 88 - 89
src/core/voip/bot.py

@@ -15,7 +15,7 @@ from src.core.datasource import SIP_SERVER
 from src.core.voip.constant import *
 
 import requests
-from src.core.callcenter.api import BotChatRequest, ChatResponse,ChatMessage
+from src.core.callcenter.api import BotChatRequest,ChatMessage
 
 
 calls = {}
@@ -58,14 +58,10 @@ class MyAudioMediaPort(pj.AudioMediaPort):
         self.asr = asr
 
         self.user_asr_texts = []
-        self.cur_player_file = None
-
 
         self.play_start_time = None  # 记录播放开始时间
         self.play_complete_flag = False  # 播放完成标志
 
-
-        #self.dtmf_type = None   #记录按键类型 1为长按键类型
     def onFrameRequested(self, frame):
         print("Request audio frame:", frame)
 
@@ -81,27 +77,27 @@ class MyAudioMediaPort(pj.AudioMediaPort):
             if asr_text and not play_complete:
                 self.user_asr_texts.append(asr_text)
             if asr_text and play_complete:
-                self.cur_player_file = None
+                self.call.cur_player_file = None
                 self.user_asr_texts.append(asr_text)
                 user_asr_text = asr_text if len(self.user_asr_texts) == 1 else '###'.join(self.user_asr_texts)
                 self.user_asr_texts.clear()
-                self.call.chat("2",user_asr_text)
+                self.call.chat(user_asr_text)
 
             #超时处理
             current_time = time.time()
-            # print(f'onFrameReceived:self.wait_time={self.call.wait_time}, self.call.digit ={self.call.digit},asr_text:{asr_text},play_complete:{play_complete}')
-            if self.call.wait_time and self.call.wait_time != "0" and play_complete and not asr_text and self.call.digit == '':
+            print(f'onFrameReceived:self.wait_time={self.call.wait_time}, self.call.digit ={self.call.digit},asr_text:{asr_text},play_complete:{play_complete},self.call.inputType:{self.call.inputType}')
+            if self.call.wait_time and self.call.wait_time != "0" and play_complete and not asr_text and not self.call.digit :
                 self.wait_time_check(current_time, self.call.wait_time)
 
             # 如果为超长类型的按键服务 超过30s未输入完成执行超时操作
-            if self.call.dtmf_type=='1' and play_complete and not asr_text:
-                self.wait_time_check(current_time, 30)
+            if self.call.inputType == '1' and play_complete and not asr_text:
+                self.wait_time_check(current_time, 15)
 
             message_queue_size = self.call.message_queue.qsize()
-            if (message_queue_size > 0 and not self.cur_player_file) or (message_queue_size > 0 and play_complete):
+            if (message_queue_size > 0 and not self.call.cur_player_file) or (message_queue_size > 0 and play_complete):
                 print('onFrameReceived:message_queue_size=', message_queue_size, 'play_complete=', play_complete, asr_text)
-                self.cur_player_file, self.call.wait_time, self.call.dtmf_type, self.call.action = self.get_player_file()
-                self.call.send_bot_speaker(self.cur_player_file)
+                self.call.cur_player_file, self.call.wait_time, self.call.inputType,self.call.action, self.call.nodeId = self.get_player_file()
+                self.call.send_bot_speaker(self.call.cur_player_file)
 
                 # 重置播放完成标志和超时计时器,确保新的播放从头开始计时
                 self.play_complete_flag = False  # 重置播放完成标志
@@ -111,12 +107,11 @@ class MyAudioMediaPort(pj.AudioMediaPort):
                 #播放完毕执行的动作
                 self.call.say_end_action(self.call.action)
         except:
-            traceback.print_exc()
             pass
 
     def is_play_complete(self):
-        if self.cur_player_file:
-            player_id = murmur3_32(self.cur_player_file)
+        if self.call.cur_player_file:
+            player_id = murmur3_32(self.call.cur_player_file)
             return self.call.player_complete_dict.get(player_id)
 
     def get_asr_text(self):
@@ -130,12 +125,13 @@ class MyAudioMediaPort(pj.AudioMediaPort):
     def get_player_file(self):
         try:
             message = self.call.message_queue.get(block=False)
-            player_file = [item.voice_url for item in message.contents if item.content_type == 'voice']
-            wait_time = message.wait_time
-            dtmf_type = message.dtmf_type
-            action = message.action
-            print('get_player_file:', player_file, wait_time,dtmf_type, action)
-            return player_file, wait_time, dtmf_type, action
+            player_file = [item.voiceUrl for item in message.contents if item.contentType == 'voice']
+            # self.call.wait_time = message.waitTime
+            # self.call.inputType = message.inputType
+            # self.call.action = message.action
+            # self.call.nodeId = message.nodeId
+            print('get_player_file:', player_file,message.waitTime, message.inputType, message.action, message.nodeId)
+            return player_file, message.waitTime, message.inputType, message.action, message.nodeId
         except Exception as e:
             traceback.print_exc()
 
@@ -150,10 +146,14 @@ class MyAudioMediaPort(pj.AudioMediaPort):
         # 检查超时时间是否已到
         if current_time - self.play_start_time > int(wait_time):
             self.play_complete_flag = False  # 重置标志位,避免重复超时
-            if wait_time == 30:
-                self.call.chat("7", self.call.digit)
+            if wait_time == 15:
+                print("DTMF:超时",self.call.digit)
+                # self.call.chat(f"DTMF({self.call.digit})DTMF","1")
+                self.call.user_asr_text_queue.put(f"DTMF({self.call.digit})DTMF")
             else:
-                self.call.chat("6", "ASR408error")
+                # self.call.chat("6", "ASR408error")
+                print("DTMF:超时6shehehehehehhehhehehe")
+                self.call.user_asr_text_queue.put("ASR408error")
 
 
 
@@ -215,9 +215,12 @@ class MyCall(pj.Call):
         self.player_complete_dict = {}
 
         self.wait_time = None
-        self.dtmf_type = None #记录按键类型 1为长按键类型
+        self.inputType = None #记录按键类型 1为长按键类型
         self.digit = '' # 存储长按键内容
         self.action = None
+        self.nodeId= 'start'
+
+        self.cur_player_file = None   #当前播放的文件
 
         from src.core.voip.asr import TestSt
         self.asr = TestSt(call_id, message_receiver=self.on_receiver_asr_result)  # 创建ASR实例
@@ -228,18 +231,16 @@ class MyCall(pj.Call):
         digit = prm.digit
         # 假设为超长类型按键 把用户输入的按键进行拼接 如果为# 则把用户输入所有按键放入队列并发送文本机器人
         # 如果为非正常按键服务 输入以后直接发送文本机器人
-        if self.dtmf_type == '1':
+        if self.inputType == '1':
             if digit != '#':
                 self.digit += digit
                 print(f"Received DTMF digit12: {self.digit}")
             else:
                 self.user_asr_text_queue.put(f"DTMF({self.digit})DTMF")
-                self.chat('3', self.digit)
                 print(f"Received DTMF digit34: {self.digit}")
         else:
             print(f"Received DTMF digit: {digit}")
             self.user_asr_text_queue.put(f"DTMF({digit})DTMF")
-            # self.chat('3', digit)
 
     def onCallState(self, prm):
         call_info = self.getInfo()
@@ -312,38 +313,38 @@ class MyCall(pj.Call):
 
     def bot_say_hello(self):
         print('bot_say_hello, come in ')
-        self.chat('1', user_asr_text="SAY_HELLO")
-    def chat(self,event_type, user_asr_text=None):
+        self.chat(user_asr_text="start")
+
+    def chat(self, user_asr_text=None):
         # 调用文本机器人接口
-        ToTextBotAgent(event_type,user_asr_text,self)
+        ToTextBotAgent(user_asr_text,self)
 
     def say_end_action(self, action):
-        print('handling_release', action.action_code)
-        action_code = action.action_code
-        if action_code == 'hang':  # 挂断
-            action_content = action.action_content
-            print(f'todo 挂电话:{action_content}')
-        elif action_code == 'transfer':  # 转人工
+        print('handling_release', action.actionCode)
+        actionCode = action.actionCode
+        if actionCode == 'hang':  # 挂断
+            actionContent = action.actionContent
+            print(f'todo 挂电话:{actionContent}')
+        elif actionCode == 'transfer':  # 转人工
             print('todo 转人工')
 
 class ToTextBotAgent:
-    def __init__(self, event_type, user_asr_text, call_agent):
+    def __init__(self, user_asr_text, call_agent):
         if not user_asr_text:
             print("ASR文本为空,终止执行。")
             return
 
         self.call_agent = call_agent
         self.request_data = BotChatRequest(
-            node_id="1",
-            user_id="139311",
-            session_id="1",
-            record_id="2",
-            task_id="ceshi",
-            asr_text=user_asr_text
+            nodeId=self.call_agent.nodeId,
+            userId="139311",
+            sessionId="1",
+            recordId="2",
+            taskId="ceshi",
+            asrText=user_asr_text
         )
         # 发送请求并处理响应
-        self.test_request(self.request_data,event_type)
-
+        self.test_request(self.request_data)
 
 
     def to_quest(self, request: BotChatRequest):
@@ -356,7 +357,9 @@ class ToTextBotAgent:
             if response.status_code == 200:
                 # 成功,解析响应JSON
                 response_data = response.json()
-                return ChatResponse.from_json(json.dumps(response_data))  # 转换为JSON字符串并解析
+                # return ChatResponse.from_json(json.dumps(response_data))  # 转换为JSON字符串并解析
+                parsed_response = ChatMessage.from_json(response_data)
+                self.call_agent.message_queue.put(parsed_response)
             else:
                 # 错误处理
                 print(f"请求失败,状态码: {response.status_code}, 响应内容: {response.text}")
@@ -366,64 +369,60 @@ class ToTextBotAgent:
             return None
 
 # 模拟接口请求返回
-    def test_request(self, params: BotChatRequest,event_type):
+    def test_request(self, params: BotChatRequest):
         print("test_request::params=", params)
         response_data = {
-            "node_id": "1.0",
+            "nodeId": "1.0",
             "contents": [],
-            "interruptable": False,
-            "wait_time": "6",
+            "waitTime": "6",
             "action": {
-                "action_code": "normal",
-                "action_content": "正常通话"
+                "actionCode": "normal",
+                "actionContent": "正常通话"
             },
-            "talk_time_out": "",
-            "dtmf_type": '0'
+            "inputType": "0"
         }
 
-        print("event_type:", event_type)
-        if event_type == '1':
+        print("asrText:", params.asrText)
+        if params.asrText == 'start':                              #欢迎语
+            response_data['contents'].append({
+                "contentType": "voice",
+                "content": "",
+                "voiceUrl": '/code/src/core/voip/scripts/1_00.wav',
+                "voiceContent": "五一北京到上海的高铁票还有吗?"
+            })
+            response_data['inputType'] = '1'
+        elif params.asrText == 'ASR408error':                          #超时执行
             response_data['contents'].append({
-                "content_type": "voice",
+                "contentType": "voice",
                 "content": "",
-                "voice_url": '/code/src/core/voip/scripts/1_00.wav',
-                "voice_content": "五一北京到上海的高铁票还有吗?"
+                "voiceUrl": '/code/src/core/voip/scripts/4_00.wav',
+                "voiceContent": "waitTime超时"
             })
-            response_data['dtmf_type']= '1'
-        elif event_type == '2':
-            new_contents = [
+        elif "DTMF" in params.asrText and self.call_agent.inputType =='1':   #长按键超30s执行
+            response_data['contents'].append({
+                "contentType": "voice",
+                "content": "",
+                "voiceUrl": '/code/src/core/voip/scripts/2_00.wav',
+                "voiceContent": "sds"
+            })
+        else :
+            response_data['contents'] = [
                 {
-                    "content_type": "voice",
+                    "contentType": "voice",
                     "content": "",
-                    "voice_url": '/code/src/core/voip/test111.wav',
-                    "voice_content": "测试第二个录音文件"
+                    "voiceUrl": '/code/src/core/voip/test111.wav',
+                    "voiceContent": "测试第二个录音文件"
                 },
                 {
-                    "content_type": "voice",
+                    "contentType": "voice",
                     "content": "",
-                    "voice_url": '/code/src/core/voip/test222.wav',
-                    "voice_content": "五一北京到上海的高铁票还有吗?"
+                    "voiceUrl": '/code/src/core/voip/test222.wav',
+                    "voiceContent": "五一北京到上海的高铁票还有吗?"
                 }
             ]
-            response_data['contents']= new_contents
-        elif event_type == '6':
-            response_data['contents'].append({
-                "content_type": "voice",
-                "content": "",
-                "voice_url": '/code/src/core/voip/scripts/4_00.wav',
-                "voice_content": "sds"
-            })
-        elif event_type == '7':
-            response_data['contents'].append({
-                "content_type": "voice",
-                "content": "",
-                "voice_url": '/code/src/core/voip/scripts/2_00.wav',
-                "voice_content": "sds"
-            })
         try:
             print(json.dumps(response_data['contents']))
             parsed_response = ChatMessage.from_json(response_data)
-            # self.handling_message(parsed_response)
             self.call_agent.message_queue.put(parsed_response)
         except Exception as e:
             print(f"Error in test_request: {e}")
@@ -451,8 +450,8 @@ class BotAgent:
         ep_cfg.uaConfig.maxCalls = 20
         ep_cfg.uaConfig.maxAccounts = 20
         ep_cfg.medConfig.noVad = True
-        ep_cfg.logConfig.level = 3
-        ep_cfg.logConfig.consoleLevel = 3
+        ep_cfg.logConfig.level = 4
+        ep_cfg.logConfig.consoleLevel = 4
         self.ep.libCreate()
         self.ep.libInit(ep_cfg)