Browse Source

fix: 状态

余尚辉 3 months ago
parent
commit
8fedc606ea
63 changed files with 997 additions and 65 deletions
  1. 1 1
      Dockerfile
  2. 4 0
      alibabacloud-nls-python-sdk-dev/.gitignore
  3. 0 0
      alibabacloud-nls-python-sdk-dev/LICENSE
  4. 0 0
      alibabacloud-nls-python-sdk-dev/README.md
  5. 0 0
      alibabacloud-nls-python-sdk-dev/docs/SDK_README.md
  6. 2 0
      alibabacloud-nls-python-sdk-dev/nls/__init__.py
  7. 0 0
      alibabacloud-nls-python-sdk-dev/nls/core.py
  8. 3 0
      alibabacloud-nls-python-sdk-dev/nls/exception.py
  9. 0 0
      alibabacloud-nls-python-sdk-dev/nls/logging.py
  10. 321 0
      alibabacloud-nls-python-sdk-dev/nls/realtime_meeting.py
  11. 0 0
      alibabacloud-nls-python-sdk-dev/nls/speech_recognizer.py
  12. 0 0
      alibabacloud-nls-python-sdk-dev/nls/speech_synthesizer.py
  13. 0 0
      alibabacloud-nls-python-sdk-dev/nls/speech_transcriber.py
  14. 439 0
      alibabacloud-nls-python-sdk-dev/nls/stream_input_tts.py
  15. 0 0
      alibabacloud-nls-python-sdk-dev/nls/token.py
  16. 1 1
      alibabacloud-nls-python-sdk-dev/nls/util.py
  17. 0 0
      alibabacloud-nls-python-sdk-dev/nls/version.py
  18. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/__init__.py
  19. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_abnf.py
  20. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_app.py
  21. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_cookiejar.py
  22. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_core.py
  23. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_exceptions.py
  24. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_handshake.py
  25. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_http.py
  26. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_logging.py
  27. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_socket.py
  28. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_ssl_compat.py
  29. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_url.py
  30. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/_utils.py
  31. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/__init__.py
  32. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/echo-server.py
  33. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_abnf.py
  34. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_app.py
  35. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_cookiejar.py
  36. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_http.py
  37. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_url.py
  38. 0 0
      alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_websocket.py
  39. 0 0
      alibabacloud-nls-python-sdk-dev/requirements.txt
  40. 1 1
      alibabacloud-nls-python-sdk-dev/setup.py
  41. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test0.wav
  42. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test1.pcm
  43. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test1.wav
  44. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test1_1.pcm
  45. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test2.pcm
  46. 84 0
      alibabacloud-nls-python-sdk-dev/tests/test_realtime_meeting.py
  47. 10 11
      alibabacloud-nls-python-sdk-dev/tests/test_sr.py
  48. 17 18
      alibabacloud-nls-python-sdk-dev/tests/test_st.py
  49. 63 0
      alibabacloud-nls-python-sdk-dev/tests/test_stream_input_tts.py
  50. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test_token.py
  51. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test_tts.pcm
  52. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test_tts.py
  53. 0 0
      alibabacloud-nls-python-sdk-dev/tests/test_utils.py
  54. 0 0
      alibabacloud-nls-python-sdk-dev/tests/tts_test.pcm
  55. 0 0
      alibabacloud-nls-python-sdk-dev/tests/tts_test.wav
  56. 4 4
      docker-compose.yml
  57. 4 1
      src/core/callcenter/call.py
  58. 2 2
      src/core/callcenter/esl/handler/channel_answer_handler.py
  59. 12 4
      src/core/callcenter/esl/handler/channel_bridge_handler.py
  60. 16 7
      src/core/callcenter/esl/handler/channel_hangup_handler.py
  61. 8 10
      src/core/callcenter/esl/handler/channel_originate_handler.py
  62. 3 3
      src/core/callcenter/push.py
  63. 2 2
      src/core/voip/bot.py

+ 1 - 1
Dockerfile

@@ -13,7 +13,7 @@ RUN cd /code/pjproject/pjsip-apps/src/swig/python && make && make install
 # pyESL
 RUN cd /code/python-ESL-1.4.18 && python setup.py install
 # nls
-RUN cd /code/alibabacloud-nls-python-sdk-1.0.2 && pip install -r requirements.txt && python setup.py install
+RUN cd /code/alibabacloud-nls-python-sdk-dev && python -m pip install -r requirements.txt && python -m pip install .
 # 安装项目依赖
 RUN pip install -r requirements.txt
 

+ 4 - 0
alibabacloud-nls-python-sdk-dev/.gitignore

@@ -0,0 +1,4 @@
+nls/__pycache__
+nls/websocket/__pycache__
+tests/__pycache__
+nls.egg-info/

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/LICENSE → alibabacloud-nls-python-sdk-dev/LICENSE


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/README.md → alibabacloud-nls-python-sdk-dev/README.md


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/docs/SDK_README.md → alibabacloud-nls-python-sdk-dev/docs/SDK_README.md


+ 2 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/__init__.py → alibabacloud-nls-python-sdk-dev/nls/__init__.py

@@ -4,5 +4,7 @@ from .logging import *
 from .speech_recognizer import *
 from .speech_transcriber import *
 from .speech_synthesizer import *
+from .stream_input_tts import *
+from .realtime_meeting import *
 from .util import *
 from .version import __version__

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/core.py → alibabacloud-nls-python-sdk-dev/nls/core.py


+ 3 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/exception.py → alibabacloud-nls-python-sdk-dev/nls/exception.py

@@ -25,4 +25,7 @@ class NotStartException(Exception):
     pass
 
 class CompleteTimeoutException(Exception):
+    pass
+
+class WrongStateException(Exception):
     pass

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/logging.py → alibabacloud-nls-python-sdk-dev/nls/logging.py


+ 321 - 0
alibabacloud-nls-python-sdk-dev/nls/realtime_meeting.py

@@ -0,0 +1,321 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+import logging
+import uuid
+import json
+import threading
+
+from nls.core import NlsCore
+from . import logging
+from . import util
+from nls.exception import (StartTimeoutException,
+                        StopTimeoutException,
+                        InvalidParameter)
+
+__REALTIME_MEETING_NAMESPACE__ = 'SpeechTranscriber'
+
+__REALTIME_MEETING_REQUEST_CMD__ = {
+    'start': 'StartTranscription',
+    'stop': 'StopTranscription'
+}
+
+__all__ = ['NlsRealtimeMeeting']
+
+
+class NlsRealtimeMeeting:
+    """
+    Api for realtime meeting
+    """
+
+    def __init__(self,
+                 url=None,
+                 on_start=None,
+                 on_sentence_begin=None,
+                 on_sentence_end=None,
+                 on_result_changed=None,
+                 on_result_translated=None,
+                 on_completed=None,
+                 on_error=None,
+                 on_close=None,
+                 callback_args=[]):
+        '''
+        NlsRealtimeMeeting initialization
+
+        Parameters:
+        -----------
+        url: str
+            meeting join url.
+        on_start: function
+            Callback object which is called when recognition started.
+            on_start has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_sentence_begin: function
+            Callback object which is called when one sentence started.
+            on_sentence_begin has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_sentence_end: function
+            Callback object which is called when sentence is end.
+            on_sentence_end has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_result_changed: function
+            Callback object which is called when partial recognition result
+            arrived.
+            on_result_changed has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_result_translated: function
+            Callback object which is called when partial translation result
+            arrived.
+            on_result_translated has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_completed: function
+            Callback object which is called when recognition is completed.
+            on_completed has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_error: function
+            Callback object which is called when any error occurs.
+            on_error has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_close: function
+            Callback object which is called when connection closed.
+            on_close has one arguments.
+            The 1st argument is *args which is callback_args.
+        callback_args: list
+            callback_args will return in callbacks above for *args.
+        '''
+        if not url:
+            raise InvalidParameter('Must provide url')
+        self.__response_handler__ = {
+            'SentenceBegin': self.__sentence_begin,
+            'SentenceEnd': self.__sentence_end,
+            'TranscriptionStarted': self.__transcription_started,
+            'TranscriptionResultChanged': self.__transcription_result_changed,
+            'ResultTranslated': self.__transcription_result_translated,
+            'TranscriptionCompleted': self.__transcription_completed,
+            'TaskFailed': self.__task_failed
+        }
+        self.__callback_args = callback_args
+        self.__url = url
+        self.__start_cond = threading.Condition()
+        self.__start_flag = False
+        self.__on_start = on_start
+        self.__on_sentence_begin = on_sentence_begin
+        self.__on_sentence_end = on_sentence_end
+        self.__on_result_changed = on_result_changed
+        self.__on_result_translated = on_result_translated
+        self.__on_completed = on_completed
+        self.__on_error = on_error
+        self.__on_close = on_close
+
+    def __handle_message(self, message):
+        logging.debug('__handle_message {}'.format(message))
+        try:
+            __result = json.loads(message)
+            if __result['header']['name'] in self.__response_handler__:
+                __handler = self.__response_handler__[
+                    __result['header']['name']]
+                __handler(message)
+            else:
+                logging.error('cannot handle cmd{}'.format(
+                    __result['header']['name']))
+                return
+        except json.JSONDecodeError:
+            logging.error('cannot parse message:{}'.format(message))
+            return
+
+    def __tr_core_on_open(self):
+        logging.debug('__tr_core_on_open')
+
+    def __tr_core_on_msg(self, msg, *args):
+        logging.debug('__tr_core_on_msg:msg={} args={}'.format(msg, args))
+        self.__handle_message(msg)
+
+    def __tr_core_on_error(self, msg, *args):
+        logging.debug('__tr_core_on_error:msg={} args={}'.format(msg, args))
+        with self.__start_cond:
+            self.__start_flag = False
+            self.__start_cond.notify()
+        if self.__on_error:
+            self.__on_error(msg, *self.__callback_args)
+
+    def __tr_core_on_close(self):
+        logging.debug('__tr_core_on_close')
+        if self.__on_close:
+            self.__on_close(*self.__callback_args)
+        with self.__start_cond:
+            self.__start_flag = False
+            self.__start_cond.notify()
+
+    def __sentence_begin(self, message):
+        logging.debug('__sentence_begin')
+        if self.__on_sentence_begin:
+            self.__on_sentence_begin(message, *self.__callback_args)
+
+    def __sentence_end(self, message):
+        logging.debug('__sentence_end')
+        if self.__on_sentence_end:
+            self.__on_sentence_end(message, *self.__callback_args)
+
+    def __transcription_started(self, message):
+        logging.debug('__transcription_started')
+        if self.__on_start:
+            self.__on_start(message, *self.__callback_args)
+        with self.__start_cond:
+            self.__start_flag = True
+            self.__start_cond.notify()
+
+    def __transcription_result_changed(self, message):
+        logging.debug('__transcription_result_changed')
+        if self.__on_result_changed:
+            self.__on_result_changed(message, *self.__callback_args)
+
+    def __transcription_result_translated(self, message):
+        logging.debug('__transcription_result_translated')
+        if self.__on_result_translated:
+            self.__on_result_translated(message, *self.__callback_args)
+
+    def __transcription_completed(self, message):
+        logging.debug('__transcription_completed')
+        self.__nls.shutdown()
+        logging.debug('__transcription_completed shutdown done')
+        if self.__on_completed:
+            self.__on_completed(message, *self.__callback_args)
+        with self.__start_cond:
+            self.__start_flag = False
+            self.__start_cond.notify()
+
+    def __task_failed(self, message):
+        logging.debug('__task_failed')
+        with self.__start_cond:
+            self.__start_flag = False
+            self.__start_cond.notify()
+        if self.__on_error:
+            self.__on_error(message, *self.__callback_args)
+
+    def start(self,
+              timeout=10,
+              ping_interval=8,
+              ping_timeout=None,
+              ex:dict=None):
+        """
+        Realtime meeting start
+
+        Parameters:
+        -----------
+        timeout: int
+            wait timeout for connection setup
+        ping_interval: int
+            send ping interval, 0 for disable ping send, default is 8
+        ping_timeout: int
+            timeout after send ping and recive pong, set None for disable timeout check and default is None
+        ex: dict
+            dict which will merge into 'payload' field in request
+        """
+        self.__nls = NlsCore(
+            url=self.__url,
+            token='default',
+            on_open=self.__tr_core_on_open,
+            on_message=self.__tr_core_on_msg,
+            on_close=self.__tr_core_on_close,
+            on_error=self.__tr_core_on_error,
+            callback_args=[])
+
+        __id4 = uuid.uuid4().hex
+        self.__task_id = uuid.uuid4().hex
+        __header = {
+            'message_id': __id4,
+            'task_id': self.__task_id,
+            'namespace': __REALTIME_MEETING_NAMESPACE__,
+            'name': __REALTIME_MEETING_REQUEST_CMD__['start'],
+            'appkey': 'default'
+        }
+        __payload = {
+        }
+
+        if ex:
+            __payload.update(ex)
+
+        __msg = {
+            'header': __header,
+            'payload': __payload,
+            'context': util.GetDefaultContext()
+        }
+        __jmsg = json.dumps(__msg)
+        with self.__start_cond:
+            if self.__start_flag:
+                logging.debug('already start...')
+                return
+            self.__nls.start(__jmsg, ping_interval, ping_timeout)
+            if self.__start_flag == False:
+                if self.__start_cond.wait(timeout):
+                    return
+                else:
+                    raise StartTimeoutException(f'Waiting Start over {timeout}s')
+
+    def stop(self, timeout=10):
+        """
+        Stop realtime meeting and mark session finished
+
+        Parameters:
+        -----------
+        timeout: int
+            timeout for waiting completed message from cloud
+        """
+        __id4 = uuid.uuid4().hex
+        __header = {
+            'message_id': __id4,
+            'task_id': self.__task_id,
+            'namespace': __REALTIME_MEETING_NAMESPACE__,
+            'name': __REALTIME_MEETING_REQUEST_CMD__['stop'],
+            'appkey': 'default'
+        }
+        __msg = {
+            'header': __header,
+            'context': util.GetDefaultContext()
+        }
+        __jmsg = json.dumps(__msg)
+        with self.__start_cond:
+            if not self.__start_flag:
+                logging.debug('not start yet...')
+                return
+            self.__nls.send(__jmsg, False)
+            if self.__start_flag == True:
+                logging.debug('stop wait..')
+                if self.__start_cond.wait(timeout):
+                    return
+                else:
+                    raise StopTimeoutException(f'Waiting stop over {timeout}s')
+
+    def shutdown(self):
+        """
+        Shutdown connection immediately
+        """
+        self.__nls.shutdown()
+
+    def send_audio(self, pcm_data):
+        """
+        Send audio binary, audio size prefer 20ms length
+
+        Parameters:
+        -----------
+        pcm_data: bytes
+            audio binary which format created by CreateTask
+        """
+
+        __data = pcm_data
+        with self.__start_cond:
+            if not self.__start_flag:
+                return
+        try:
+            self.__nls.send(__data, True)
+        except ConnectionResetError as __e:
+            logging.error('connection reset')
+            self.__start_flag = False
+            self.__nls.shutdown()
+            raise __e

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/speech_recognizer.py → alibabacloud-nls-python-sdk-dev/nls/speech_recognizer.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/speech_synthesizer.py → alibabacloud-nls-python-sdk-dev/nls/speech_synthesizer.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/speech_transcriber.py → alibabacloud-nls-python-sdk-dev/nls/speech_transcriber.py


+ 439 - 0
alibabacloud-nls-python-sdk-dev/nls/stream_input_tts.py

@@ -0,0 +1,439 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+import logging
+import uuid
+import json
+import threading
+from enum import IntEnum
+
+from nls.core import NlsCore
+from . import logging
+from .exception import StartTimeoutException, WrongStateException, InvalidParameter
+
+__STREAM_INPUT_TTS_NAMESPACE__ = "FlowingSpeechSynthesizer"
+
+__STREAM_INPUT_TTS_REQUEST_CMD__ = {
+    "start": "StartSynthesis",
+    "send": "RunSynthesis",
+    "stop": "StopSynthesis",
+}
+__STREAM_INPUT_TTS_REQUEST_NAME__ = {
+    "started": "SynthesisStarted",
+    "sentence_begin": "SentenceBegin",
+    "sentence_synthesis": "SentenceSynthesis",
+    "sentence_end": "SentenceEnd",
+    "completed": "SynthesisCompleted",
+    "task_failed": "TaskFailed",
+}
+
+__URL__ = "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
+
+__all__ = ["NlsStreamInputTtsSynthesizer"]
+
+
+class NlsStreamInputTtsRequest:
+    def __init__(self, task_id, session_id, appkey):
+        self.task_id = task_id
+        self.appkey = appkey
+        self.session_id = session_id
+
+    def getStartCMD(self, voice, format, sample_rate, volumn, speech_rate, pitch_rate, ex):
+        self.voice = voice
+        self.format = format
+        self.sample_rate = sample_rate
+        self.volumn = volumn
+        self.speech_rate = speech_rate
+        self.pitch_rate = pitch_rate
+        cmd = {
+            "header": {
+                "message_id": uuid.uuid4().hex,
+                "task_id": self.task_id,
+                "name": __STREAM_INPUT_TTS_REQUEST_CMD__["start"],
+                "namespace": __STREAM_INPUT_TTS_NAMESPACE__,
+                "appkey": self.appkey,
+            },
+            "payload": {
+                "session_id": self.session_id,
+                "voice": self.voice,
+                "format": self.format,
+                "sample_rate": self.sample_rate,
+                "volumn": self.volumn,
+                "speech_rate": self.speech_rate,
+                "pitch_rate": self.pitch_rate,
+            },
+        }
+        if ex:
+            cmd["payload"].update(ex)        
+        return json.dumps(cmd)
+
+    def getSendCMD(self, text):
+        cmd = {
+            "header": {
+                "message_id": uuid.uuid4().hex,
+                "task_id": self.task_id,
+                "name": __STREAM_INPUT_TTS_REQUEST_CMD__["send"],
+                "namespace": __STREAM_INPUT_TTS_NAMESPACE__,
+                "appkey": self.appkey,
+            },
+            "payload": {"text": text},
+        }
+        return json.dumps(cmd)
+
+    def getStopCMD(self):
+        cmd = {
+            "header": {
+                "message_id": uuid.uuid4().hex,
+                "task_id": self.task_id,
+                "name": __STREAM_INPUT_TTS_REQUEST_CMD__["stop"],
+                "namespace": __STREAM_INPUT_TTS_NAMESPACE__,
+                "appkey": self.appkey,
+            },
+        }
+        return json.dumps(cmd)
+
+
+class NlsStreamInputTtsStatus(IntEnum):
+    Begin = 1
+    Start = 2
+    Started = 3
+    WaitingComplete = 3
+    Completed = 4
+    Failed = 5
+    Closed = 6
+
+class ThreadSafeStatus:
+    def __init__(self, state: NlsStreamInputTtsStatus):
+        self._state = state
+        self._lock = threading.Lock()
+    
+    def get(self) -> NlsStreamInputTtsStatus:
+        with self._lock:
+            return self._state
+    
+    def set(self, state: NlsStreamInputTtsStatus):
+        with self._lock:
+            self._state = state
+
+
+class NlsStreamInputTtsSynthesizer:
+    """
+    Api for text-to-speech
+    """
+
+    def __init__(
+        self,
+        url=__URL__,
+        token=None,
+        appkey=None,
+        session_id=None,
+        on_data=None,
+        on_sentence_begin=None,
+        on_sentence_synthesis=None,
+        on_sentence_end=None,
+        on_completed=None,
+        on_error=None,
+        on_close=None,
+        callback_args=[],
+    ):
+        """
+        NlsSpeechSynthesizer initialization
+
+        Parameters:
+        -----------
+        url: str
+            websocket url.
+        akid: str
+            access id from aliyun. if you provide a token, ignore this argument.
+        appkey: str
+            appkey from aliyun
+        session_id: str
+            32-character string, if empty, sdk will generate a random string.
+        on_data: function
+            Callback object which is called when partial synthesis result arrived
+            arrived.
+            on_result_changed has two arguments.
+            The 1st argument is binary data corresponding to aformat in start
+            method.
+            The 2nd argument is *args which is callback_args.
+        on_sentence_begin: function
+            Callback object which is called when detected sentence start.
+            on_start has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_sentence_synthesis: function
+            Callback object which is called when detected sentence synthesis.
+            The incremental timestamp is returned within payload.
+            on_start has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_sentence_end: function
+            Callback object which is called when detected sentence end.
+            The timestamp of the whole sentence is returned within payload.
+            on_start has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_completed: function
+            Callback object which is called when recognition is completed.
+            on_completed has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_error: function
+            Callback object which is called when any error occurs.
+            on_error has two arguments.
+            The 1st argument is message which is a json format string.
+            The 2nd argument is *args which is callback_args.
+        on_close: function
+            Callback object which is called when connection closed.
+            on_close has one arguments.
+            The 1st argument is *args which is callback_args.
+        callback_args: list
+            callback_args will return in callbacks above for *args.
+        """
+        if not token or not appkey:
+            raise InvalidParameter("Must provide token and appkey")
+        self.__response_handler__ = {
+            __STREAM_INPUT_TTS_REQUEST_NAME__["started"]: self.__synthesis_started,
+            __STREAM_INPUT_TTS_REQUEST_NAME__["sentence_begin"]: self.__sentence_begin,
+            __STREAM_INPUT_TTS_REQUEST_NAME__[
+                "sentence_synthesis"
+            ]: self.__sentence_synthesis,
+            __STREAM_INPUT_TTS_REQUEST_NAME__["sentence_end"]: self.__sentence_end,
+            __STREAM_INPUT_TTS_REQUEST_NAME__["completed"]: self.__synthesis_completed,
+            __STREAM_INPUT_TTS_REQUEST_NAME__["task_failed"]: self.__task_failed,
+        }
+        self.__callback_args = callback_args
+        self.__url = url
+        self.__appkey = appkey
+        self.__token = token
+        self.__session_id = session_id
+        self.start_sended = threading.Event()
+        self.started_event = threading.Event()
+        self.complete_event = threading.Event()
+        self.__on_sentence_begin = on_sentence_begin
+        self.__on_sentence_synthesis = on_sentence_synthesis
+        self.__on_sentence_end = on_sentence_end
+        self.__on_data = on_data
+        self.__on_completed = on_completed
+        self.__on_error = on_error
+        self.__on_close = on_close
+        self.__allow_aformat = ("pcm", "wav", "mp3")
+        self.__allow_sample_rate = (
+            8000,
+            11025,
+            16000,
+            22050,
+            24000,
+            32000,
+            44100,
+            48000,
+        )
+        self.state = ThreadSafeStatus(NlsStreamInputTtsStatus.Begin)     
+        if not self.__session_id:
+            self.__session_id = uuid.uuid4().hex   
+        self.request = NlsStreamInputTtsRequest(
+            uuid.uuid4().hex, self.__session_id, self.__appkey
+        )
+
+    def __handle_message(self, message):
+        logging.debug("__handle_message")
+        try:
+            __result = json.loads(message)
+            if __result["header"]["name"] in self.__response_handler__:
+                __handler = self.__response_handler__[__result["header"]["name"]]
+                __handler(message)
+            else:
+                logging.error("cannot handle cmd{}".format(__result["header"]["name"]))
+                return
+        except json.JSONDecodeError:
+            logging.error("cannot parse message:{}".format(message))
+            return
+
+    def __syn_core_on_open(self):
+        logging.debug("__syn_core_on_open")
+        self.start_sended.set()
+
+    def __syn_core_on_data(self, data, opcode, flag):
+        logging.debug("__syn_core_on_data")
+        if self.__on_data:
+            self.__on_data(data, *self.__callback_args)
+
+    def __syn_core_on_msg(self, msg, *args):
+        logging.debug("__syn_core_on_msg:msg={} args={}".format(msg, args))
+        self.__handle_message(msg)
+
+    def __syn_core_on_error(self, msg, *args):
+        logging.debug("__sr_core_on_error:msg={} args={}".format(msg, args))
+
+    def __syn_core_on_close(self):
+        logging.debug("__sr_core_on_close")
+        if self.__on_close:
+            self.__on_close(*self.__callback_args)
+        self.state.set(NlsStreamInputTtsStatus.Closed)
+        self.start_sended.set()
+        self.started_event.set()
+        self.complete_event.set()
+
+    def __synthesis_started(self, message):
+        logging.debug("__synthesis_started")
+        self.started_event.set()
+
+    def __sentence_begin(self, message):
+        logging.debug("__sentence_begin")
+        if self.__on_sentence_begin:
+            self.__on_sentence_begin(message, *self.__callback_args)
+
+    def __sentence_synthesis(self, message):
+        logging.debug("__sentence_synthesis")
+        if self.__on_sentence_synthesis:
+            self.__on_sentence_synthesis(message, *self.__callback_args)
+
+    def __sentence_end(self, message):
+        logging.debug("__sentence_end")
+        if self.__on_sentence_end:
+            self.__on_sentence_end(message, *self.__callback_args)
+
+    def __synthesis_completed(self, message):
+        logging.debug("__synthesis_completed")
+        if self.__on_completed:
+            self.__on_completed(message, *self.__callback_args)
+        self.__nls.shutdown()
+        logging.debug("__synthesis_completed shutdown done")
+        self.complete_event.set()
+
+
+    def __task_failed(self, message):
+        logging.debug("__task_failed")
+        self.start_sended.set()
+        self.started_event.set()
+        self.complete_event.set()
+        if self.__on_error:
+            self.__on_error(message, *self.__callback_args)
+        self.state.set(NlsStreamInputTtsStatus.Failed)
+
+    def startStreamInputTts(
+        self,
+        voice="longxiaochun",
+        aformat="pcm",
+        sample_rate=24000,
+        volume=50,
+        speech_rate=0,
+        pitch_rate=0,
+        ex:dict=None,
+    ):
+        """
+        Synthesis start
+
+        Parameters:
+        -----------
+        voice: str
+            voice for text-to-speech, default is xiaoyun
+        aformat: str
+            audio binary format, support: 'pcm', 'wav', 'mp3', default is 'pcm'
+        sample_rate: int
+            audio sample rate, default is 24000, support:8000, 11025, 16000, 22050,
+            24000, 32000, 44100, 48000
+        volume: int
+            audio volume, from 0~100, default is 50
+        speech_rate: int
+            speech rate from -500~500, default is 0
+        pitch_rate: int
+            pitch for voice from -500~500, default is 0
+        ex: dict
+            dict which will merge into 'payload' field in request
+        """
+
+        self.__nls = NlsCore(
+            url=self.__url,
+            token=self.__token,
+            on_open=self.__syn_core_on_open,
+            on_message=self.__syn_core_on_msg,
+            on_data=self.__syn_core_on_data,
+            on_close=self.__syn_core_on_close,
+            on_error=self.__syn_core_on_error,
+            callback_args=[],
+        )
+
+        if aformat not in self.__allow_aformat:
+            raise InvalidParameter("format {} not support".format(aformat))
+        if sample_rate not in self.__allow_sample_rate:
+            raise InvalidParameter("samplerate {} not support".format(sample_rate))
+        if volume < 0 or volume > 100:
+            raise InvalidParameter("volume {} not support".format(volume))
+        if speech_rate < -500 or speech_rate > 500:
+            raise InvalidParameter("speech_rate {} not support".format(speech_rate))
+        if pitch_rate < -500 or pitch_rate > 500:
+            raise InvalidParameter("pitch rate {} not support".format(pitch_rate))
+
+        request = self.request.getStartCMD(
+            voice, aformat, sample_rate, volume, speech_rate, pitch_rate, ex
+        )
+        
+        last_state = self.state.get()
+        if last_state != NlsStreamInputTtsStatus.Begin:
+            logging.debug("start with wrong state {}".format(last_state))
+            self.state.set(NlsStreamInputTtsStatus.Failed)
+            raise WrongStateException("start with wrong state {}".format(last_state))
+
+        logging.debug("start with request: {}".format(request))
+        self.__nls.start(request, ping_interval=0, ping_timeout=None)
+        self.state.set(NlsStreamInputTtsStatus.Start)
+        if not self.start_sended.wait(timeout=10):
+            logging.debug("syn start timeout")
+            raise StartTimeoutException(f"Waiting Connection before Start over 10s")
+
+        if last_state != NlsStreamInputTtsStatus.Begin:
+            logging.debug("start with wrong state {}".format(last_state))
+            self.state.set(NlsStreamInputTtsStatus.Failed)
+            raise WrongStateException("start with wrong state {}".format(last_state))
+
+        if not self.started_event.wait(timeout=10):
+            logging.debug("syn started timeout")
+            self.state.set(NlsStreamInputTtsStatus.Failed)
+            raise StartTimeoutException(f"Waiting Started over 10s")
+        self.state.set(NlsStreamInputTtsStatus.Started)
+
+    def sendStreamInputTts(self, text):
+        """
+        send text to server
+
+        Parameters:
+        -----------
+        text: str
+            utf-8 text
+        """
+        last_state = self.state.get()
+        if last_state != NlsStreamInputTtsStatus.Started:
+            logging.debug("send with wrong state {}".format(last_state))
+            self.state.set(NlsStreamInputTtsStatus.Failed)
+            raise WrongStateException("send with wrong state {}".format(last_state))
+
+        request = self.request.getSendCMD(text)
+        logging.debug("send with request: {}".format(request))
+        self.__nls.send(request, None)
+
+    def stopStreamInputTts(self):
+        """
+        Synthesis end
+        """
+
+        last_state = self.state.get()
+        if last_state != NlsStreamInputTtsStatus.Started:
+            logging.debug("send with wrong state {}".format(last_state))
+            self.state.set(NlsStreamInputTtsStatus.Failed)
+            raise WrongStateException("stop with wrong state {}".format(last_state))
+
+
+        request = self.request.getStopCMD()
+        logging.debug("stop with request: {}".format(request))
+        self.__nls.send(request, None)
+        self.state.set(NlsStreamInputTtsStatus.WaitingComplete)
+        self.complete_event.wait()
+        self.state.set(NlsStreamInputTtsStatus.Completed)
+        self.shutdown()
+
+    def shutdown(self):
+        """
+        Shutdown connection immediately
+        """
+
+        self.__nls.shutdown()

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/token.py → alibabacloud-nls-python-sdk-dev/nls/token.py


+ 1 - 1
alibabacloud-nls-python-sdk-1.0.2/nls/util.py → alibabacloud-nls-python-sdk-dev/nls/util.py

@@ -11,7 +11,7 @@ def GetDefaultContext():
     return {
         'sdk': {
             'name': 'nls-python-sdk',
-            'version': '0.0.1',
+            'version': '1.1.0',
             'language': 'python'
         }
     }

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/version.py → alibabacloud-nls-python-sdk-dev/nls/version.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__init__.py → alibabacloud-nls-python-sdk-dev/nls/websocket/__init__.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_abnf.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_abnf.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_app.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_app.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_cookiejar.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_cookiejar.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_core.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_core.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_exceptions.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_exceptions.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_handshake.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_handshake.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_http.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_http.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_logging.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_logging.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_socket.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_socket.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_ssl_compat.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_ssl_compat.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_url.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_url.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_utils.py → alibabacloud-nls-python-sdk-dev/nls/websocket/_utils.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/__init__.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/__init__.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/echo-server.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/echo-server.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_abnf.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_abnf.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_app.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_app.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_cookiejar.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_cookiejar.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_http.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_http.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_url.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_url.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_websocket.py → alibabacloud-nls-python-sdk-dev/nls/websocket/tests/test_websocket.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/requirements.txt → alibabacloud-nls-python-sdk-dev/requirements.txt


+ 1 - 1
alibabacloud-nls-python-sdk-1.0.2/setup.py → alibabacloud-nls-python-sdk-dev/setup.py

@@ -28,7 +28,7 @@ requires = [
 ]
 
 setup_args = {
-    'version': "1.0.0",
+    'version': "1.1.0",
     'author': "jiaqi.sjq",
     'author_email': "jiaqi.sjq@alibaba-inc.com",
     'description': "python sdk for nls",

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test0.wav → alibabacloud-nls-python-sdk-dev/tests/test0.wav


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test1.pcm → alibabacloud-nls-python-sdk-dev/tests/test1.pcm


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test1.wav → alibabacloud-nls-python-sdk-dev/tests/test1.wav


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test1_1.pcm → alibabacloud-nls-python-sdk-dev/tests/test1_1.pcm


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test2.pcm → alibabacloud-nls-python-sdk-dev/tests/test2.pcm


+ 84 - 0
alibabacloud-nls-python-sdk-dev/tests/test_realtime_meeting.py

@@ -0,0 +1,84 @@
+import time
+import threading
+import sys
+import nls
+
+class TestRealtimeMeeting:
+    def __init__(self, tid, test_file, url):
+        self.__th = threading.Thread(target=self.__test_run)
+        self.__id = tid
+        self.__test_file = test_file
+        self.__url = url
+
+    def loadfile(self, filename):
+        with open(filename, "rb") as f:
+            self.__data = f.read()
+
+    def start(self):
+        self.loadfile(self.__test_file)
+        self.__th.start()
+
+    def test_on_sentence_begin(self, message, *args):
+        print("test_on_sentence_begin:{}".format(message))
+
+    def test_on_sentence_end(self, message, *args):
+        print("test_on_sentence_end:{}".format(message))
+
+    def test_on_start(self, message, *args):
+        print("test_on_start:{}".format(message))
+
+    def test_on_error(self, message, *args):
+        print("on_error message=>{} args=>{}".format(message, args))
+
+    def test_on_close(self, *args):
+        print("on_close: args=>{}".format(args))
+
+    def test_on_result_chg(self, message, *args):
+        print("test_on_chg:{}".format(message))
+
+    def test_on_result_translated(self, message, *args):
+        print("test_on_translated:{}".format(message))
+
+    def test_on_completed(self, message, *args):
+        print("on_completed:args=>{} message=>{}".format(args, message))
+
+
+    def __test_run(self):
+        print("thread:{} start..".format(self.__id))
+        rm = nls.NlsRealtimeMeeting(
+                    url=self.__url,
+                    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_result_translated=self.test_on_result_translated,
+                    on_completed=self.test_on_completed,
+                    on_error=self.test_on_error,
+                    on_close=self.test_on_close,
+                    callback_args=[self.__id]
+                )
+
+        print("{}: session start".format(self.__id))
+        r = rm.start()
+
+        self.__slices = zip(*(iter(self.__data),) * 640)
+        for i in self.__slices:
+            rm.send_audio(bytes(i))
+            time.sleep(0.01)
+
+        time.sleep(1)
+
+        r = rm.stop()
+        print("{}: rm stopped:{}".format(self.__id, r))
+        time.sleep(5)
+
+def multiruntest(num=1):
+    for i in range(0, num):
+        name = "thread" + str(i)
+        t = TestRealtimeMeeting(name, "tests/test1.pcm", "wss://tingwu-realtime-cn-hangzhou-pre.aliyuncs.com/api/ws/v1?")
+        t.start()
+
+nls.enableTrace(True)
+multiruntest(1)
+
+

+ 10 - 11
alibabacloud-nls-python-sdk-1.0.2/tests/test_sr.py → alibabacloud-nls-python-sdk-dev/tests/test_sr.py

@@ -48,18 +48,17 @@ class TestSr:
                     on_close=self.test_on_close,
                     callback_args=[self.__id]
                 )
-        while True:
-            print('{}: session start'.format(self.__id))
-            r = sr.start(ex={'format':'pcm', 'hello':123})
-           
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                sr.send_audio(bytes(i))
-                time.sleep(0.01)
+        print("{}: session start".format(self.__id))
+        r = sr.start(aformat="pcm", ex={"hello":123})
+            
+        self.__slices = zip(*(iter(self.__data),) * 640)
+        for i in self.__slices:
+            sr.send_audio(bytes(i))
+            time.sleep(0.01)
 
-            r = sr.stop()
-            print('{}: sr stopped:{}'.format(self.__id, r))
-            time.sleep(5)
+        r = sr.stop()
+        print("{}: sr stopped:{}".format(self.__id, r))
+        time.sleep(1)
 
 def multiruntest(num=500):
     for i in range(0, num):

+ 17 - 18
alibabacloud-nls-python-sdk-1.0.2/tests/test_st.py → alibabacloud-nls-python-sdk-dev/tests/test_st.py

@@ -55,24 +55,23 @@ class TestSt:
                     on_close=self.test_on_close,
                     callback_args=[self.__id]
                 )
-        while True:
-            print("{}: session start".format(self.__id))
-            r = sr.start(aformat="pcm",
-                    enable_intermediate_result=True,
-                    enable_punctuation_prediction=True,
-                    enable_inverse_text_normalization=True)
-
-            self.__slices = zip(*(iter(self.__data),) * 640)
-            for i in self.__slices:
-                sr.send_audio(bytes(i))
-                time.sleep(0.01)
-
-            sr.ctrl(ex={"test":"tttt"})
-            time.sleep(1)
-
-            r = sr.stop()
-            print("{}: sr stopped:{}".format(self.__id, r))
-            time.sleep(5)
+        print("{}: session start".format(self.__id))
+        r = sr.start(aformat="pcm",
+                enable_intermediate_result=True,
+                enable_punctuation_prediction=True,
+                enable_inverse_text_normalization=True)
+
+        self.__slices = zip(*(iter(self.__data),) * 640)
+        for i in self.__slices:
+            sr.send_audio(bytes(i))
+            time.sleep(0.01)
+
+        sr.ctrl(ex={"test":"tttt"})
+        time.sleep(1)
+
+        r = sr.stop()
+        print("{}: sr stopped:{}".format(self.__id, r))
+        time.sleep(5)
 
 def multiruntest(num=500):
     for i in range(0, num):

+ 63 - 0
alibabacloud-nls-python-sdk-dev/tests/test_stream_input_tts.py

@@ -0,0 +1,63 @@
+import nls
+import pyaudio
+import time
+from tests.test_utils import TEST_ACCESS_TOKEN, TEST_ACCESS_APPKEY
+
+
+test_text = [
+    "流式文本语音合成SDK,",
+    "可以将输入的文本",
+    "合成为语音二进制数据,",
+    "相比于非流式语音合成,",
+    "流式合成的优势在于实时性",
+    "更强。用户在输入文本的同时",
+    "可以听到接近同步的语音输出,",
+    "极大地提升了交互体验,",
+    "减少了用户等待时间。",
+    "适用于调用大规模",
+    "语言模型(LLM),以",
+    "流式输入文本的方式",
+    "进行语音合成的场景。",
+]
+
+if __name__ == "__main__":
+    player = pyaudio.PyAudio()
+    stream = player.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)
+
+    # 创建SDK实例
+    # 配置回调函数
+    def test_on_data(data, *args):
+        stream.write(data)
+
+    def test_on_message(message, *args):
+        print('on message=>{}'.format(message))
+
+    def test_on_close(*args):
+        print('on_close: args=>{}'.format(args))
+
+    def test_on_error(message, *args):
+        print('on_error args=>{}, message=>{}'.format(args, message))
+    
+    sdk = nls.NlsStreamInputTtsSynthesizer(
+        token=TEST_ACCESS_TOKEN,
+        appkey=TEST_ACCESS_APPKEY,
+        on_data=test_on_data,
+        on_sentence_begin=test_on_message,
+        on_sentence_synthesis=test_on_message,
+        on_sentence_end=test_on_message,
+        on_completed=test_on_message,
+        on_error=test_on_error,
+        on_close=test_on_close,
+        callback_args=[],
+    )
+
+    # 发送文本消息
+    sdk.startStreamInputTts()
+    for text in test_text:
+        sdk.sendStreamInputTts(text)
+        time.sleep(0.05)
+    sdk.stopStreamInputTts()
+
+    stream.stop_stream()
+    stream.close()
+    player.terminate()

+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test_token.py → alibabacloud-nls-python-sdk-dev/tests/test_token.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test_tts.pcm → alibabacloud-nls-python-sdk-dev/tests/test_tts.pcm


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test_tts.py → alibabacloud-nls-python-sdk-dev/tests/test_tts.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/test_utils.py → alibabacloud-nls-python-sdk-dev/tests/test_utils.py


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/tts_test.pcm → alibabacloud-nls-python-sdk-dev/tests/tts_test.pcm


+ 0 - 0
alibabacloud-nls-python-sdk-1.0.2/tests/tts_test.wav → alibabacloud-nls-python-sdk-dev/tests/tts_test.wav


+ 4 - 4
docker-compose.yml

@@ -1,13 +1,13 @@
 services:
   pjsua:
-    image: pjsua2:v2.14.1201
+    image: pjsua2:v2.14.1202
     container_name: pjsua
     restart: always
 #    network_mode: host
     volumes:
-#      - /Users/yushanghui/hongshantianping/git/voice-gateway-service:/code
-      - /home/hongshan/voice-gateway-service:/code
-      - /root/aibot/dm/voice:/root/aibot/dm/voice
+      - /Users/yushanghui/hongshantianping/git/voice-gateway-service:/code
+#      - /home/hongshan/voice-gateway-service:/code
+#      - /root/aibot/dm/voice:/root/aibot/dm/voice
     environment:
       - SERVE_HOST=192.168.100.159
     ports:

+ 4 - 1
src/core/callcenter/call.py

@@ -5,11 +5,12 @@ import time
 from datetime import datetime
 from src.core.callcenter.cache import Cache
 from src.core.callcenter.constant import saasId, HOLD_MUSIC_PATH
-from src.core.callcenter.enumeration import CallCause, Direction, NextType, DeviceType, CdrType, AgentServiceState
+from src.core.callcenter.enumeration import CallCause, Direction, NextType, DeviceType, CdrType, AgentServiceState,AgentScene,WorkStatus
 from src.core.callcenter.api import AgentCallRequest, CallInfo, HangupCallRequest, CheckInCallRequest, \
     DeviceInfo, NextCommand, MakeCallContext
 from src.core.callcenter.esl.constant.sip_header_constant import sipHeaderServiceId, sipHeaderCtiFlowId
 from src.core.callcenter.snowflake import Snowflake
+from src.core.callcenter.push import PushHandler
 from src.core.callcenter.data_handler import *
 
 class CallService:
@@ -20,6 +21,7 @@ class CallService:
         self.cache = Cache(client.app)
         self.snowflake = Snowflake()
         self.dataHandleServer=DataHandleServer(client.app)
+        self.push_handler = PushHandler(logger)
 
     def call(self, request: AgentCallRequest):
         call_id = 'C' + str(self.snowflake.next_id())
@@ -64,6 +66,7 @@ class CallService:
         })
         # 变更坐席状态为拨号中
         self.dataHandleServer.update_agent_monitor_service_state(request.agent_id, AgentServiceState.DIALING.code)
+        self.push_handler.push_on_agent_work_report(request.saas_id, request.cti_flow_id, request.agent_id, call_id,AgentScene.ROBOT, WorkStatus.AGENT_HANG_IDLE)
         return call_id
 
     def hold(self, call_info: CallInfo, device_id):

+ 2 - 2
src/core/callcenter/esl/handler/channel_answer_handler.py

@@ -39,8 +39,8 @@ class ChannelAnswerHandler(EslEventHandler):
         if not next_command:
             return
 
-        if device_info.device_type == DeviceType.CUSTOMER.code:
-            self.push_handler.push_on_ring_start(saas_id=call_info.saas_id, flow_id=call_info.cti_flow_id, user_id=call_info.agent_key, scene=AgentScene.MANUAL, call_id=call_info.call_id)
+        # if device_info.device_type == DeviceType.CUSTOMER.code:
+        #     self.push_handler.push_on_ring_start(saas_id=call_info.saas_id, flow_id=call_info.cti_flow_id, user_id=call_info.agent_key, scene=AgentScene.MANUAL, call_id=call_info.call_id)
 
         device_info.answer_time = EslEventUtil.getEventDateTimestamp(event)
         device_info.ring_end_time = EslEventUtil.getEventDateTimestamp(event)

+ 12 - 4
src/core/callcenter/esl/handler/channel_bridge_handler.py

@@ -1,10 +1,11 @@
 #!/usr/bin/env python3
 # encoding:utf-8
 from src.core.callcenter.esl.annotation import EslEventName
-from src.core.callcenter.enumeration import  DeviceType, AgentServiceState
+from src.core.callcenter.enumeration import  DeviceType, AgentServiceState,WorkStatus,AgentScene
 from src.core.callcenter.esl.constant.event_names import CHANNEL_BRIDGE
 from src.core.callcenter.esl.handler.esl_event_handler import EslEventHandler
 import src.core.callcenter.esl.utils.esl_event_util as EslEventUtil
+from src.core.callcenter.push import PushHandler
 from src.core.callcenter.data_handler import *
 
 @EslEventName(CHANNEL_BRIDGE)
@@ -13,13 +14,20 @@ class ChannelBridgeHandler(EslEventHandler):
     def __init__(self, inbound_client, bot_agent):
         super().__init__(inbound_client, bot_agent)
         self.dataHandleServer = DataHandleServer(inbound_client.app)
+        self.push_handler = PushHandler(inbound_client.logger)
 
     def handle(self, address, event, coreUUID):
         call_id = EslEventUtil.getCallId(event)
         device_id = EslEventUtil.getDeviceId(event)
         call = self.cache.get_call_info(call_id)
         device = call.device_info_map.get(device_id)
-        self.logger.info('device_id: %s, device:%s ', device_id, device)
+        if not call.answer_time:
+            call.answer_time = EslEventUtil.getEventDateTimestamp(event)
+            self.cache.add_call_info(call)
+
+        self.logger.info("bridge call_info.answer_time:%s,device_info.answer_time%s,device.device_type%s" % (call.answer_time, device.answer_time,device.device_type))
         if device.device_type == DeviceType.AGENT.code: # 如果是坐席接听 变更坐席状态
-            self.dataHandleServer.update_agent_monitor_service_state(device_id, AgentServiceState.IDLE.code)
-        pass
+            self.dataHandleServer.update_agent_monitor_service_state(call.agent_key, AgentServiceState.CALLING.code)
+            self.push_handler.push_on_agent_work_report(call.saas_id, call.cti_flow_id, call.agent_key, call.call_id, AgentScene.ROBOT, WorkStatus.AGENT_ANSWER_INCOMING)
+
+

+ 16 - 7
src/core/callcenter/esl/handler/channel_hangup_handler.py

@@ -8,7 +8,7 @@ import traceback
 from src.core.callcenter.acd import AcdService
 from src.core.callcenter.call import CallService
 from src.core.callcenter.enumeration import CallType, DeviceType, AnswerFlag, NextType, CdrType, HangupDir, \
-    CallCause,AgentServiceState,AgentScene
+    CallCause,AgentServiceState,AgentScene, WorkStatus
 from src.core.callcenter.esl.annotation import EslEventName
 import src.core.callcenter.esl.utils.esl_event_util as EslEventUtil
 from src.core.callcenter.esl.constant.event_names import CHANNEL_HANGUP
@@ -24,8 +24,8 @@ class ChannelHangupHandler(EslEventHandler):
         super().__init__(inbound_client, bot_agent)
         self.acd_service = AcdService(inbound_client,inbound_client.app)
         self.call_service = CallService(inbound_client,inbound_client.logger)
-        self.dataHandleServer=DataHandleServer(inbound_client.app)
         self.push_handler = PushHandler(inbound_client.logger)
+        self.dataHandleServer=DataHandleServer(inbound_client.app)
 
     def handle(self, address, event, coreUUID):
         self.logger.info(json.loads(event.serialize('json')))
@@ -83,9 +83,6 @@ class ChannelHangupHandler(EslEventHandler):
             call_info.device_info_map[device_info.device_id] = device_info
             skip_hangup_all = device_info.device_type == DeviceType.ROBOT.code
 
-            # 更新通话记录
-            # self.dataHandleServer.update_record(call_id, {"time_end": datetime.fromtimestamp(int(device_info.end_time)),"times":device_info.talk_time})
-
             self.logger.info('ChannelHangupHandler, hangup_reason=%s, device_type=%s, cdr_type=%s, end_time=%s, skip_hangup_all=%s' % (hangup_reason, device_info.device_type, device_info.cdr_type, call_info.end_time, skip_hangup_all))
             # 如果是转人工
             # if 'transferToAgent' == hangup_reason and DeviceType.ROBOT.code == device_info.device_type:
@@ -109,13 +106,25 @@ class ChannelHangupHandler(EslEventHandler):
                 self.call_service.hangup_all(call_info, CallCause.HANGUP_EVENT)
                 # self.inbound_client.hangup_call(call_id, device_id, CallCause.HANGUP_EVENT)
 
+            # 全部挂机以后推送挂机状态
+            self.logger.info('yushanghui::count %s', count)
+            if count == 0:
+                # 计算当前通话时长
+                if call_info.answer_time:
+                    call_info.end_time = timestamp
+                    call_info.talk_time = int(call_info.end_time) - int(call_info.answer_time)
+                    self.dataHandleServer.update_record(call_id, {"time_end": datetime.utcnow(), "times": call_info.talk_time // 1000})
+
             # 判断挂机方向 && 更新缓存
             self.hangup_dir(call_info, device_info, cause)
             self.cache.add_call_info(call_info)
 
+
+            # 更新坐席状态
             if device_info.device_type == DeviceType.AGENT.code:
-                self.dataHandleServer.update_agent_monitor_service_state(device_id, AgentServiceState.IDLE.code)
-                self.push_handler.push_on_call_end(call_info.cti_flow_id,call_info.agent_key,AgentScene.ROBOT,call_info.direction, device_info.device_type)
+                  self.push_handler.push_on_call_end(call_info.cti_flow_id, call_info.agent_key, AgentScene.ROBOT, call_info.direction, device_info.device_type)
+                  self.push_handler.push_on_agent_work_report(call_info.saas_id, call_info.cti_flow_id, call_info.agent_key,call_info.call_id, AgentScene.ROBOT,WorkStatus.AGENT_DIALING)
+                  self.dataHandleServer.update_agent_monitor_service_state(device_id, AgentServiceState.IDLE.code)
         except:
             traceback.print_exc()
 

+ 8 - 10
src/core/callcenter/esl/handler/channel_originate_handler.py

@@ -15,13 +15,11 @@ class ChannelOriginateHandler(EslEventHandler):
         self.push_handler = PushHandler(inbound_client.logger)
 
     def handle(self, address, event, coreUUID):
-        # call_id = EslEventUtil.getCallId(event)
-        # device_id = EslEventUtil.getDeviceId(event)
-        # call = self.cache.get_call_info(call_id)
-        # device = call.device_info_map.get(device_id)
-        # self.logger.info('ChannelOriginateHandler::device_id: %s, device:%s ', device_id, device)
-        # if device.device_type == DeviceType.AGENT.code: # 如果是坐席接听 变更坐席状态
-        #     self.push_handler.push_on_call_ring(call.saas_id,call.cti_flow_id,call.agent_key,AgentScene.ROBOT,call.call_id, call.called,call.caller, "00000000000000000")
-        #     self.push_handler.push_on_agent_work_report(call.saas_id, call.cti_flow_id,call.agent_key,call.call_id,AgentScene.ROBOT, WorkStatus.AGENT_RINGING)
-
-        pass
+        call_id = EslEventUtil.getCallId(event)
+        device_id = EslEventUtil.getDeviceId(event)
+        call = self.cache.get_call_info(call_id)
+        device = call.device_info_map.get(device_id)
+        self.logger.info('ChannelOriginateHandler::device_id: %s, device:%s ', device_id, device)
+        if device.device_type == DeviceType.AGENT.code: # 如果是坐席接听 变更坐席状态
+            self.push_handler.push_on_call_ring(call.saas_id,call.cti_flow_id,call.agent_key,AgentScene.ROBOT,call.call_id, call.called,call.caller, "00000000000000000")
+            self.push_handler.push_on_agent_work_report(call.saas_id, call.cti_flow_id,call.agent_key,call.call_id,AgentScene.ROBOT, WorkStatus.AGENT_RINGING)

+ 3 - 3
src/core/callcenter/push.py

@@ -78,7 +78,7 @@ class PushHandler:
         new_data = {'data': json.dumps(data)}
         self.push_to_socket_service(user_id, json.dumps(new_data))
 
-    def push_on_ring_start(self, saas_id, flow_id, user_id, scene, call_id=None):
+    def push_on_ring_start(self, saas_id, flow_id, user_id, scene: AgentScene, call_id=None):
         data = {
             'eventName': DownEvent.ON_RING_Start.code,
             'ext': {
@@ -91,7 +91,7 @@ class PushHandler:
         new_data = {'data': json.dumps(data)}
         self.push_to_socket_service(user_id, json.dumps(new_data))
 
-    def push_on_ring_end(self, saas_id, flow_id, user_id, scene, call_id):
+    def push_on_ring_end(self, saas_id, flow_id, user_id, scene: AgentScene, call_id):
         data = {
             'eventName': DownEvent.ON_RING_END.code,
             'ext': {
@@ -104,7 +104,7 @@ class PushHandler:
         new_data = {'data': json.dumps(data)}
         self.push_to_socket_service(user_id, json.dumps(new_data))
 
-    def push_answer_call(self, saas_id,flow_id, out_id, call_id, scene, service_direct,work_status,user_id ):
+    def push_answer_call(self, saas_id,flow_id, out_id, call_id, scene: AgentScene, service_direct,work_status,user_id ):
         data = {
             'eventName': DownEvent.ANSWER_CALL.code,
             'ext': {

+ 2 - 2
src/core/voip/bot.py

@@ -380,8 +380,8 @@ class ToTextBotAgent:
         )
         print("user_asr_text发送结果:", user_asr_text)
         # 发送请求并处理响应
-        # self.test_request(self.request_data)
-        self.to_quest(self.request_data)
+        self.test_request(self.request_data)
+        # self.to_quest(self.request_data)
 
 
     def to_quest(self, request: BotChatRequest):