Przeglądaj źródła

feat: 新增实时语音asr

余尚辉 5 miesięcy temu
rodzic
commit
11d21101b8
100 zmienionych plików z 6791 dodań i 350 usunięć
  1. BIN
      .DS_Store
  2. 13 0
      .idea/easycode.ignore
  3. 3 0
      .idea/easycode/codebase-v2.xml
  4. 6 0
      .idea/inspectionProfiles/profiles_settings.xml
  5. 12 0
      .idea/material_theme_project_new.xml
  6. 7 0
      .idea/misc.xml
  7. 6 0
      .idea/vcs.xml
  8. 10 0
      .idea/voice-gateway.iml
  9. 119 0
      .idea/workspace.xml
  10. 7 7
      build.mak
  11. 2 2
      build/os-auto.mak
  12. 102 309
      config.log
  13. 18 18
      config.status
  14. 1 2
      docker/Dockerfile
  15. 7 0
      docker/docker-compose.yml
  16. 1 1
      pjlib/build/os-auto.mak
  17. 1 1
      pjlib/include/pj/compat/m_auto.h
  18. 1 1
      pjlib/include/pj/compat/os_auto.h
  19. 4 4
      pjmedia/build/os-auto.mak
  20. BIN
      pjsip-apps/src/swig/python/.DS_Store
  21. BIN
      pjsip-apps/src/swig/python/dist/pjsua2-2.14.dev0-py3.9-linux-aarch64.egg
  22. BIN
      pjsip-apps/src/swig/python/incoming_call.wav
  23. 5 5
      third_party/build/os-auto.mak
  24. BIN
      voip/__pycache__/asr.cpython-39.pyc
  25. 13 0
      voip/alibabacloud-nls-python-sdk-1.0.2/LICENSE
  26. 13 0
      voip/alibabacloud-nls-python-sdk-1.0.2/README.md
  27. 770 0
      voip/alibabacloud-nls-python-sdk-1.0.2/docs/SDK_README.md
  28. 31 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/PKG-INFO
  29. 36 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/SOURCES.txt
  30. 1 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/dependency_links.txt
  31. 3 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/requires.txt
  32. 2 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/top_level.txt
  33. 8 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__init__.py
  34. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/__init__.cpython-39.pyc
  35. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/_logger.cpython-36.pyc
  36. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/_logging.cpython-36.pyc
  37. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/core.cpython-39.pyc
  38. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/exception.cpython-39.pyc
  39. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/logging.cpython-39.pyc
  40. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/speech_recognizer.cpython-39.pyc
  41. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/speech_synthesizer.cpython-39.pyc
  42. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/speech_transcriber.cpython-39.pyc
  43. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/token.cpython-39.pyc
  44. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/util.cpython-39.pyc
  45. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/version.cpython-39.pyc
  46. 183 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/core.py
  47. 28 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/exception.py
  48. 65 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/logging.py
  49. 315 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/speech_recognizer.py
  50. 288 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/speech_synthesizer.py
  51. 374 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/speech_transcriber.py
  52. 49 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/token.py
  53. 44 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/util.py
  54. 2 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/version.py
  55. 26 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__init__.py
  56. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/__init__.cpython-39.pyc
  57. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_abnf.cpython-39.pyc
  58. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_app.cpython-39.pyc
  59. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_cookiejar.cpython-39.pyc
  60. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_core.cpython-39.pyc
  61. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_exceptions.cpython-39.pyc
  62. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_handshake.cpython-39.pyc
  63. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_http.cpython-39.pyc
  64. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_logging.cpython-39.pyc
  65. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_socket.cpython-39.pyc
  66. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_ssl_compat.cpython-39.pyc
  67. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_url.cpython-39.pyc
  68. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_utils.cpython-39.pyc
  69. 423 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_abnf.py
  70. 423 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_app.py
  71. 67 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_cookiejar.py
  72. 607 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_core.py
  73. 84 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_exceptions.py
  74. 191 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_handshake.py
  75. 335 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_http.py
  76. 90 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_logging.py
  77. 182 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_socket.py
  78. 44 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_ssl_compat.py
  79. 176 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_url.py
  80. 104 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_utils.py
  81. 0 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/__init__.py
  82. 6 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/data/header01.txt
  83. 6 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/data/header02.txt
  84. 7 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/data/header03.txt
  85. 21 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/echo-server.py
  86. 89 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_abnf.py
  87. 179 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_app.py
  88. 119 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_cookiejar.py
  89. 177 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_http.py
  90. 301 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_url.py
  91. 458 0
      voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_websocket.py
  92. 4 0
      voip/alibabacloud-nls-python-sdk-1.0.2/requirements.txt
  93. 49 0
      voip/alibabacloud-nls-python-sdk-1.0.2/setup.py
  94. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/tests/__pycache__/test_utils.cpython-39.pyc
  95. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/tests/test0.wav
  96. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/tests/test1.pcm
  97. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/tests/test1.wav
  98. BIN
      voip/alibabacloud-nls-python-sdk-1.0.2/tests/test1_1.pcm
  99. 0 0
      voip/alibabacloud-nls-python-sdk-1.0.2/tests/test2.pcm
  100. 73 0
      voip/alibabacloud-nls-python-sdk-1.0.2/tests/test_sr.py

BIN
.DS_Store


+ 13 - 0
.idea/easycode.ignore

@@ -0,0 +1,13 @@
+node_modules/
+dist/
+vendor/
+cache/
+.*/
+*.min.*
+*.test.*
+*.spec.*
+*.bundle.*
+*.bundle-min.*
+*.*.js
+*.*.ts
+*.log

Plik diff jest za duży
+ 3 - 0
.idea/easycode/codebase-v2.xml


+ 6 - 0
.idea/inspectionProfiles/profiles_settings.xml

@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>

+ 12 - 0
.idea/material_theme_project_new.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MaterialThemeProjectNewConfig">
+    <option name="metadata">
+      <MTProjectMetadataState>
+        <option name="migrated" value="true" />
+        <option name="pristineConfig" value="false" />
+        <option name="userId" value="21186f27:1920810b1c3:-7ff8" />
+      </MTProjectMetadataState>
+    </option>
+  </component>
+</project>

+ 7 - 0
.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Black">
+    <option name="sdkName" value="Python 3.9" />
+  </component>
+  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 10 - 0
.idea/voice-gateway.iml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4">
+  <component name="PyDocumentationSettings">
+    <option name="format" value="PLAIN" />
+    <option name="myDocStringFormat" value="Plain" />
+  </component>
+  <component name="TestRunnerService">
+    <option name="PROJECT_TEST_RUNNER" value="py.test" />
+  </component>
+</module>

+ 119 - 0
.idea/workspace.xml

@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AutoImportSettings">
+    <option name="autoReloadType" value="SELECTIVE" />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" id="fa326de8-c293-4edc-aea7-f522062e444a" name="更改" comment="">
+      <change afterPath="$PROJECT_DIR$/voip/asr.py" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/voip/hello_back.py" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/voip/realtimevoice2.py" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/voip/realtimevoice3.py" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/build.mak" beforeDir="false" afterPath="$PROJECT_DIR$/build.mak" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/build/os-auto.mak" beforeDir="false" afterPath="$PROJECT_DIR$/build/os-auto.mak" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/config.log" beforeDir="false" afterPath="$PROJECT_DIR$/config.log" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/config.status" beforeDir="false" afterPath="$PROJECT_DIR$/config.status" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/docker/Dockerfile" beforeDir="false" afterPath="$PROJECT_DIR$/docker/Dockerfile" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pjlib/build/os-auto.mak" beforeDir="false" afterPath="$PROJECT_DIR$/pjlib/build/os-auto.mak" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pjlib/include/pj/compat/m_auto.h" beforeDir="false" afterPath="$PROJECT_DIR$/pjlib/include/pj/compat/m_auto.h" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pjlib/include/pj/compat/os_auto.h" beforeDir="false" afterPath="$PROJECT_DIR$/pjlib/include/pj/compat/os_auto.h" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pjmedia/build/os-auto.mak" beforeDir="false" afterPath="$PROJECT_DIR$/pjmedia/build/os-auto.mak" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pjsip-apps/src/swig/python/incoming_call.wav" beforeDir="false" afterPath="$PROJECT_DIR$/pjsip-apps/src/swig/python/incoming_call.wav" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/third_party/build/os-auto.mak" beforeDir="false" afterPath="$PROJECT_DIR$/third_party/build/os-auto.mak" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/voip/gateway.py" beforeDir="false" afterPath="$PROJECT_DIR$/voip/gateway.py" afterDir="false" />
+    </list>
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="ProjectColorInfo">{
+  &quot;associatedIndex&quot;: 2
+}</component>
+  <component name="ProjectId" id="2mHNVadX2PyYmFevxLfAjefsM5B" />
+  <component name="ProjectViewState">
+    <option name="hideEmptyMiddlePackages" value="true" />
+    <option name="showLibraryContents" value="true" />
+  </component>
+  <component name="PropertiesComponent"><![CDATA[{
+  "keyToString": {
+    "ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
+    "Python 测试.pytest (realtimevoice.py 内).executor": "Run",
+    "Python.gateway.executor": "Run",
+    "Python.hello.executor": "Run",
+    "RunOnceActivity.ShowReadmeOnStart": "true",
+    "SHARE_PROJECT_CONFIGURATION_FILES": "true",
+    "git-widget-placeholder": "master",
+    "last_opened_file_path": "/Users/yushanghui/hongshantianping/git/voice-gateway/voip",
+    "node.js.detected.package.eslint": "true",
+    "node.js.detected.package.tslint": "true",
+    "node.js.selected.package.eslint": "(autodetect)",
+    "node.js.selected.package.tslint": "(autodetect)",
+    "nodejs_package_manager_path": "npm",
+    "settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
+    "vue.rearranger.settings.migration": "true"
+  }
+}]]></component>
+  <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/voip" />
+    </key>
+  </component>
+  <component name="SharedIndexes">
+    <attachedChunks>
+      <set>
+        <option value="bundled-js-predefined-d6986cc7102b-7c0b70fcd90d-JavaScript-PY-242.21829.153" />
+        <option value="bundled-python-sdk-464836ebc622-b74155a9e76b-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-242.21829.153" />
+      </set>
+    </attachedChunks>
+  </component>
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="fa326de8-c293-4edc-aea7-f522062e444a" name="更改" comment="" />
+      <created>1726711326540</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1726711326540</updated>
+      <workItem from="1726711327601" duration="10000" />
+      <workItem from="1726729566997" duration="471000" />
+      <workItem from="1726730040611" duration="136000" />
+      <workItem from="1726730191330" duration="336000" />
+      <workItem from="1726731225103" duration="16000" />
+      <workItem from="1726731896153" duration="30000" />
+      <workItem from="1726732088722" duration="578000" />
+      <workItem from="1726732801011" duration="23000" />
+      <workItem from="1726732880962" duration="17365000" />
+      <workItem from="1726813755505" duration="5045000" />
+      <workItem from="1726885942235" duration="7084000" />
+      <workItem from="1726899240242" duration="1211000" />
+      <workItem from="1726901494388" duration="1841000" />
+      <workItem from="1726903397653" duration="3718000" />
+      <workItem from="1726912128235" duration="4396000" />
+      <workItem from="1727060980209" duration="15357000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="3" />
+  </component>
+  <component name="Vcs.Log.Tabs.Properties">
+    <option name="TAB_STATES">
+      <map>
+        <entry key="MAIN">
+          <value>
+            <State />
+          </value>
+        </entry>
+      </map>
+    </option>
+  </component>
+  <component name="com.intellij.coverage.CoverageDataManagerImpl">
+    <SUITE FILE_PATH="coverage/voice_gateway$gateway.coverage" NAME="gateway 覆盖结果" MODIFIED="1727083376530" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/voip" />
+    <SUITE FILE_PATH="coverage/voice_gateway$pytest__realtimevoice_py__.coverage" NAME="pytest (realtimevoice.py 内) 覆盖结果" MODIFIED="1727062688965" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/voip" />
+    <SUITE FILE_PATH="coverage/voice_gateway$hello.coverage" NAME="hello 覆盖结果" MODIFIED="1727083155294" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/voip" />
+  </component>
+</project>

+ 7 - 7
build.mak

@@ -9,7 +9,7 @@ export HOST_NAME := unix
 export CC_NAME := gcc
 export TARGET_ARCH := 
 export STD_CPP_LIB := 
-export TARGET_NAME := x86_64-unknown-linux-gnu
+export TARGET_NAME := aarch64-unknown-linux-gnu
 export CROSS_COMPILE := 
 export LINUX_POLL := select 
 export SHLIB_SUFFIX := so
@@ -187,8 +187,8 @@ FFMPEG_CFLAGS =   -DPJMEDIA_USE_OLD_FFMPEG=1
 FFMPEG_LDFLAGS =   
 
 # Video4Linux2
-V4L2_CFLAGS = 
-V4L2_LDFLAGS = 
+V4L2_CFLAGS = -DPJMEDIA_VIDEO_DEV_HAS_V4L2=1
+V4L2_LDFLAGS = -lv4l2
 
 # OPENH264 flags
 OPENH264_CFLAGS =  
@@ -239,7 +239,7 @@ export APP_CFLAGS := -DPJ_AUTOCONF=1\
         -I$(PJDIR)/pjnath/include\
         -I$(PJDIR)/pjmedia/include\
         -I$(PJDIR)/pjsip/include
-export APP_CXXFLAGS := -g -O2 $(APP_CFLAGS)
+export APP_CXXFLAGS := -fPIC $(APP_CFLAGS)
 export APP_LDFLAGS := -L$(PJDIR)/pjlib/lib\
         -L$(PJDIR)/pjlib-util/lib\
         -L$(PJDIR)/pjnath/lib\
@@ -247,7 +247,7 @@ export APP_LDFLAGS := -L$(PJDIR)/pjlib/lib\
         -L$(PJDIR)/pjsip/lib\
         -L$(PJDIR)/third_party/lib\
         $(PJ_VIDEO_LDFLAGS) \
-        
+        -fPIC
 export APP_LDXXFLAGS := $(APP_LDFLAGS)
 
 export APP_LIB_FILES := \
@@ -329,7 +329,7 @@ export APP_LDLIBS := $(PJSUA_LIB_LDLIB) \
         $(APP_THIRD_PARTY_LIBS)\
         $(APP_THIRD_PARTY_EXT)\
         $(PJLIB_LDLIB) \
-        -lssl -lcrypto -luuid -lm -lrt -lpthread   
+        -lssl -lcrypto -luuid -lm -lrt -lpthread  -lasound   -lv4l2
 export APP_LDXXLIBS := $(PJSUA2_LIB_LDLIB) \
         -lstdc++ \
         $(APP_LDLIBS)
@@ -353,5 +353,5 @@ export PJ_INSTALL_DIR := /usr/local
 export PJ_INSTALL_INC_DIR := ${prefix}/include
 export PJ_INSTALL_LIB_DIR := ${exec_prefix}/lib
 export PJ_INSTALL_CFLAGS := -I$(PJ_INSTALL_INC_DIR) -DPJ_AUTOCONF=1  -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1
-export PJ_INSTALL_LDFLAGS_PRIVATE := $(APP_THIRD_PARTY_LIBS) $(APP_THIRD_PARTY_EXT) -lssl -lcrypto -luuid -lm -lrt -lpthread   
+export PJ_INSTALL_LDFLAGS_PRIVATE := $(APP_THIRD_PARTY_LIBS) $(APP_THIRD_PARTY_EXT) -lssl -lcrypto -luuid -lm -lrt -lpthread  -lasound   -lv4l2
 export PJ_INSTALL_LDFLAGS := -L$(PJ_INSTALL_LIB_DIR) $(filter-out $(PJ_INSTALL_LDFLAGS_PRIVATE),$(APP_LDXXLIBS))

+ 2 - 2
build/os-auto.mak

@@ -2,9 +2,9 @@
 
 export OS_CFLAGS   := $(CC_DEF)PJ_AUTOCONF=1 -fPIC -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1
 
-export OS_CXXFLAGS := $(CC_DEF)PJ_AUTOCONF=1 -g -O2
+export OS_CXXFLAGS := $(CC_DEF)PJ_AUTOCONF=1 -fPIC
 
-export OS_LDFLAGS  :=  -lssl -lcrypto -luuid -lm -lrt -lpthread   
+export OS_LDFLAGS  := -fPIC -lssl -lcrypto -luuid -lm -lrt -lpthread  -lasound   -lv4l2
 
 export OS_SOURCES  := 
 

Plik diff jest za duży
+ 102 - 309
config.log


+ 18 - 18
config.status

@@ -416,7 +416,7 @@ $config_headers
 
 Report bugs to the package provider."
 
-ac_cs_config='CFLAGS=-fPIC'
+ac_cs_config='CFLAGS=-fPIC CXXFLAGS=-fPIC LDFLAGS=-fPIC'
 ac_cs_version="\
 pjproject config.status 2.x
 configured by ./aconfigure, generated by GNU Autoconf 2.71,
@@ -505,7 +505,7 @@ if $ac_cs_silent; then
 fi
 
 if $ac_cs_recheck; then
-  set X /bin/bash './aconfigure'  'CFLAGS=-fPIC' $ac_configure_extra_args --no-create --no-recursion
+  set X /bin/bash './aconfigure'  'CFLAGS=-fPIC' 'CXXFLAGS=-fPIC' 'LDFLAGS=-fPIC' $ac_configure_extra_args --no-create --no-recursion
   shift
   \printf "%s\n" "running CONFIG_SHELL=/bin/bash $*" >&6
   CONFIG_SHELL='/bin/bash'
@@ -613,8 +613,8 @@ S["ac_webrtc_aec3_cflags"]=""
 S["ac_webrtc_aec3_instset"]=""
 S["ac_no_webrtc_aec3"]="1"
 S["ac_webrtc_ldflags"]=""
-S["ac_webrtc_cflags"]=""
-S["ac_webrtc_instset"]="sse2"
+S["ac_webrtc_cflags"]="-DWEBRTC_ARCH_ARM64"
+S["ac_webrtc_instset"]="neon"
 S["ac_no_webrtc"]=""
 S["ac_no_yuv"]=""
 S["ac_no_srtp"]=""
@@ -648,8 +648,8 @@ S["ac_vpx_ldflags"]=""
 S["ac_vpx_cflags"]=""
 S["ac_openh264_ldflags"]=""
 S["ac_openh264_cflags"]=""
-S["ac_v4l2_ldflags"]=""
-S["ac_v4l2_cflags"]=""
+S["ac_v4l2_ldflags"]="-lv4l2"
+S["ac_v4l2_cflags"]="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1"
 S["PKG_CONFIG"]="pkg-config"
 S["SAVED_PKG_CONFIG_PATH"]=""
 S["ac_ffmpeg_ldflags"]=" "
@@ -687,7 +687,7 @@ S["ac_oboe_ldflags"]=""
 S["ac_oboe_cflags"]=""
 S["ac_pa_cflags"]=" -DHAVE_SYS_SOUNDCARD_H -DHAVE_LINUX_SOUNDCARD_H -DPA_LITTLE_ENDIAN"
 S["ac_external_pa"]="0"
-S["ac_pjmedia_snd"]="null"
+S["ac_pjmedia_snd"]="alsa"
 S["ac_pjmedia_resample"]="libresample"
 S["ac_external_webrtc_aec3"]="0"
 S["ac_external_webrtc"]="0"
@@ -723,31 +723,31 @@ S["ac_ct_AR"]="ar"
 S["AR"]="ar"
 S["RANLIB"]="ranlib"
 S["ac_ct_CXX"]="g++"
-S["CXXFLAGS"]="-g -O2"
+S["CXXFLAGS"]="-fPIC"
 S["CXX"]="g++"
 S["OBJEXT"]="o"
 S["EXEEXT"]=""
 S["ac_ct_CC"]="gcc"
 S["CPPFLAGS"]=""
-S["LDFLAGS"]=""
+S["LDFLAGS"]="-fPIC"
 S["CFLAGS"]="-fPIC -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1"
 S["CC"]="gcc"
 S["target_os"]="linux-gnu"
 S["target_vendor"]="unknown"
-S["target_cpu"]="x86_64"
-S["target"]="x86_64-unknown-linux-gnu"
+S["target_cpu"]="aarch64"
+S["target"]="aarch64-unknown-linux-gnu"
 S["host_os"]="linux-gnu"
 S["host_vendor"]="unknown"
-S["host_cpu"]="x86_64"
-S["host"]="x86_64-unknown-linux-gnu"
+S["host_cpu"]="aarch64"
+S["host"]="aarch64-unknown-linux-gnu"
 S["build_os"]="linux-gnu"
 S["build_vendor"]="unknown"
-S["build_cpu"]="x86_64"
-S["build"]="x86_64-unknown-linux-gnu"
+S["build_cpu"]="aarch64"
+S["build"]="aarch64-unknown-linux-gnu"
 S["target_alias"]=""
 S["host_alias"]=""
 S["build_alias"]=""
-S["LIBS"]="-lssl -lcrypto -luuid -lm -lrt -lpthread   "
+S["LIBS"]="-lssl -lcrypto -luuid -lm -lrt -lpthread  -lasound   -lv4l2"
 S["ECHO_T"]=""
 S["ECHO_N"]="-n"
 S["ECHO_C"]=""
@@ -834,7 +834,7 @@ D["HAVE_LIBPTHREAD"]=" 1"
 D["HAVE_LIBRT"]=" 1"
 D["HAVE_LIBM"]=" 1"
 D["HAVE_LIBUUID"]=" 1"
-D["PJ_M_NAME"]=" \"x86_64\""
+D["PJ_M_NAME"]=" \"aarch64\""
 D["PJ_POOL_ALIGNMENT"]=" 8"
 D["HAVE_STDIO_H"]=" 1"
 D["HAVE_STDLIB_H"]=" 1"
@@ -883,7 +883,7 @@ D["PJ_HAS_UNISTD_H"]=" 1"
 D["PJ_HAS_EXECINFO_H"]=" 1"
 D["PJ_HAS_NET_IF_H"]=" 1"
 D["PJ_HAS_LOCALTIME_R"]=" 1"
-D["PJ_OS_NAME"]=" \"x86_64-unknown-linux-gnu\""
+D["PJ_OS_NAME"]=" \"aarch64-unknown-linux-gnu\""
 D["PJ_HAS_ERRNO_VAR"]=" 1"
 D["PJ_HAS_HIGH_RES_TIMER"]=" 1"
 D["PJ_HAS_MALLOC"]=" 1"

+ 1 - 2
docker/Dockerfile

@@ -1,6 +1,5 @@
 FROM python:3.9
-RUN pip3 install cython pip setuptools --upgrade
-
+RUN pip3 install swig cython pip setuptools --upgrade
 WORKDIR /code
 
 CMD ["tail", "-f", "/dev/null"]

+ 7 - 0
docker/docker-compose.yml

@@ -0,0 +1,7 @@
+version: '3'
+services:
+  pjsua:
+    image: pjsua:v2.14.0921_ip
+    container_name: pjsua
+    volumes:
+      - /Users/yushanghui/hongshantianping/git/voice-gateway:/code

+ 1 - 1
pjlib/build/os-auto.mak

@@ -24,7 +24,7 @@ export TEST_OBJS +=     main.o
 # Additional LDFLAGS for pjlib-test
 #
 # Disabled, as this causes duplicated LDFLAGS, which may raise linking errors
-#export TEST_LDFLAGS +=  -lssl -lcrypto -luuid -lm -lrt -lpthread   
+#export TEST_LDFLAGS += -fPIC -lssl -lcrypto -luuid -lm -lrt -lpthread  -lasound   -lv4l2
 
 #
 # TARGETS are make targets in the Makefile, to be executed for this given

+ 1 - 1
pjlib/include/pj/compat/m_auto.h

@@ -26,7 +26,7 @@
  */
 
 /* Machine name, filled in by autoconf script */
-#define PJ_M_NAME "x86_64"
+#define PJ_M_NAME "aarch64"
 
 /* Endianness. It's reported on pjsip list on 09/02/13 that autoconf
  * endianness detection failed for universal build, so special case

+ 1 - 1
pjlib/include/pj/compat/os_auto.h

@@ -27,7 +27,7 @@
  */
 
 /* Canonical OS name */
-#define PJ_OS_NAME "x86_64-unknown-linux-gnu"
+#define PJ_OS_NAME "aarch64-unknown-linux-gnu"
 
 /* Legacy macros */
 /* #undef PJ_WIN64 */

+ 4 - 4
pjmedia/build/os-auto.mak

@@ -15,8 +15,8 @@ FFMPEG_CFLAGS =   -DPJMEDIA_USE_OLD_FFMPEG=1
 FFMPEG_LDFLAGS =   
 
 # Video4Linux2
-V4L2_CFLAGS = 
-V4L2_LDFLAGS = 
+V4L2_CFLAGS = -DPJMEDIA_VIDEO_DEV_HAS_V4L2=1
+V4L2_LDFLAGS = -lv4l2
 
 # Directshow
 DSHOW_CFLAGS = 
@@ -58,7 +58,7 @@ export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) $(DSHOW_LDFLA
 #   - alsa:             Unix ALSA (alsa_dev.c)
 #   - null:             Null sound device (nullsound.c)
 #   - external:         Link with no sounddev (app will provide)
-AC_PJMEDIA_SND=null
+AC_PJMEDIA_SND=alsa
 
 #
 # Codecs
@@ -227,7 +227,7 @@ ifeq (,1)
 export CFLAGS += -DPJMEDIA_HAS_WEBRTC_AEC=0
 else
 export CFLAGS += -DPJMEDIA_HAS_WEBRTC_AEC=1
-ifneq ($(findstring arm,$(sse2)),)
+ifneq ($(findstring arm,$(neon)),)
 export CFLAGS += -DPJMEDIA_WEBRTC_AEC_USE_MOBILE=1
 endif
 

BIN
pjsip-apps/src/swig/python/.DS_Store


BIN
pjsip-apps/src/swig/python/dist/pjsua2-2.14.dev0-py3.9-linux-aarch64.egg


BIN
pjsip-apps/src/swig/python/incoming_call.wav


+ 5 - 5
third_party/build/os-auto.mak

@@ -31,7 +31,7 @@ ifneq (,1)
 DIRS += g7221
 endif
 
-ifneq ($(findstring pa,null),)
+ifneq ($(findstring pa,alsa),)
 ifeq (0,1)
 # External PA
 else
@@ -84,15 +84,15 @@ ifeq (0,1)
 # External webrtc
 else
 DIRS += webrtc
-WEBRTC_OTHER_CFLAGS = -fexceptions -DWEBRTC_POSIX=1 
-ifneq ($(findstring sse2,sse2),)
+WEBRTC_OTHER_CFLAGS = -fexceptions -DWEBRTC_POSIX=1 -DWEBRTC_ARCH_ARM64
+ifneq ($(findstring sse2,neon),)
     WEBRTC_SRC = \
     	      modules/audio_processing/aec/aec_core_sse2.o		 \
 	      modules/audio_processing/aec/aec_rdft_sse2.o	         \
 	      modules/audio_processing/aecm/aecm_core_c.o	         \
 	      modules/audio_processing/ns/nsx_core_c.o	                 \
 	      system_wrappers/source/cpu_features.o
-else ifneq ($(findstring neon,sse2),)
+else ifneq ($(findstring neon,neon),)
     WEBRTC_SRC = \
        	      modules/audio_processing/aec/aec_core_neon.o               \
 	      modules/audio_processing/aec/aec_rdft_neon.o               \
@@ -104,7 +104,7 @@ else ifneq ($(findstring neon,sse2),)
 	      common_audio/signal_processing/downsample_fast_neon.o      \
 	      common_audio/signal_processing/min_max_operations_neon.o
     WEBRTC_OTHER_CFLAGS += -DWEBRTC_HAS_NEON
-else ifneq ($(findstring mips,sse2),)
+else ifneq ($(findstring mips,neon),)
     WEBRTC_SRC = \
               modules/audio_processing/aec/aec_core_mips.o               \
 	      modules/audio_processing/aec/aec_rdft_mips.o               \

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


+ 13 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/LICENSE

@@ -0,0 +1,13 @@
+Copyright 1999-present Alibaba Group Holding Ltd.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 13 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/README.md

@@ -0,0 +1,13 @@
+# alibaba-nls-python-sdk
+
+This is Python SDK for NLS. It supports
+SPEECH-RECOGNIZER/SPEECH-SYNTHESIZER/SPEECH-TRANSLATOR/COMMON-REQUESTS-PROTO.
+
+This module works on Python versions:
+> 3.6 and greater
+
+install requirements:
+> python -m pip install -r requirements.txt
+
+install package:
+> python -m pip install .

+ 770 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/docs/SDK_README.md

@@ -0,0 +1,770 @@
+# NLS Python SDK说明
+
+
+
+本文介绍如何使用阿里云智能语音服务提供的Python SDK,包括SDK的安装方法及SDK代码示例。
+
+> 说明
+>
+> * 当前版本:0.0.1,支持python3
+
+
+
+## 安装
+
+解压SDK压缩包后进入SDK根目录使用如下命令安装SDK依赖:
+
+> python -m pip install -r requirements.txt
+
+依赖安装完成后使用如下命令安装SDK:
+
+> python -m pip install .
+
+> 注意:
+>
+> 上述命令需要在SDK根目录中执行
+
+安装完成后通过以下代码可以导入SDK:
+
+> #!/bin/python
+>
+> import nls
+
+
+
+## 多线程和多并发
+
+根据Python官方文档:
+
+> **CPython implementation detail:** 在 CPython 中,由于存在 [全局解释器锁](https://docs.python.org/zh-cn/3/glossary.html#term-global-interpreter-lock),同一时刻只有一个线程可以执行 Python 代码(虽然某些性能导向的库可能会去除此限制)。 如果你想让你的应用更好地利用多核心计算机的计算资源,推荐你使用 [`multiprocessing`](https://docs.python.org/zh-cn/3/library/multiprocessing.html#module-multiprocessing) 或 [`concurrent.futures.ProcessPoolExecutor`](https://docs.python.org/zh-cn/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor)。 但是,如果你想要同时运行多个 I/O 密集型任务,则多线程仍然是一个合适的模型。
+
+如果单解释器有太多线程,那么线程间切换的消耗会非常客观,有可能会导致SDK出现错误,不建议超过200线程的使用,如果有必要使用multiprocessing技术或者手动使用脚本创建多个解释器
+
+
+
+## 一句话识别
+
+一句话识别对应的类为NlsSpeechRecognizer,其核心方法如下:
+
+### 1. 初始化(\_\_init\_\_)
+
+参数说明:
+
+| 参数              | 类型     | 参数说明                                                     |
+| ----------------- | -------- | ------------------------------------------------------------ |
+| url               | str      | 网关websocket url,默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
+| akid              | str      | 账号access id,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| aksecret          | str      | 账号access secret key,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| token             | str      | 访问token,参考开发指南—获取Token及获取Token协议说明相关内容 |
+| on_start          | function | 当一句话识别就绪时的回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_result_changed | function | 当一句话识别返回中间结果时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_completed      | function | 当一句话识别返回最终识别结果时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_error          | function | 当SDK或云端出现错误时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_close          | function | 当和云端连接断开时回调,回调参数有一个——用户自定义参数,见后续callback_args参数说明 |
+| callback_args     | list     | 用户自定义参数列表,里面的内容会pack成list传递给各个回调的最后一个参数 |
+
+返回值:
+
+无
+
+### 2. start
+
+> 同步开始一句话识别,该方法会阻塞当前线程直到一句话识别就绪(on_start回调返回)
+
+参数说明:
+
+| 参数                              | 类型 | 参数说明                                                     |
+| --------------------------------- | ---- | ------------------------------------------------------------ |
+| aformat                           | str  | 要识别音频格式,支持pcm,opus,opu,默认pcm,**SDK不会自动将pcm编码成opus或opu,如果需要使用opus或opu,需要用户自行编码** |
+| sample_rate                       | int  | 识别音频采样率,默认16000                                    |
+| ch                                | int  | 音频通道数,默认为1,不需要提供                              |
+| enable_intermediate_result        | bool | 是否返回中间结果,默认False                                  |
+| enable_punctuation_prediction    | bool | 是否进行识别结果标点预测,默认False                          |
+| enable_inverse_text_normalization | bool | 是否进行ITN,默认False                                       |
+| timeout                           | int  | 阻塞超时,默认10秒                                           |
+| ping_interval                     | int  | ping包发送间隔,默认8,不需要可以设置为0或None               |
+| ping_timeout                      | int  | 是否检查pong包超时,默认为None,None为不检查pong包超时       |
+| ex                                | dict | 用户提供的额外参数,该字典内容会以key:value形式合并进请求的payload段中,具体可用参数见一句话语音识别接口说明 |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 3. stop
+
+> 停止一句话识别,并同步等待on_completed回调结束
+
+参数说明:
+
+| 参数    | 类型 | 参数说明           |
+| ------- | ---- | ------------------ |
+| timeout | int  | 阻塞超时,默认10秒 |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 4. shutdown
+
+> 强行关闭当前请求,重复调用无副作用
+
+参数说明:
+
+无
+
+返回值:
+
+无
+
+### 5. send_audio
+
+> 发送二进制音频数据,发送数据的格式需要和start中的aformat对应
+
+参数说明:
+
+| 参数     | 类型  | 参数说明                                                     |
+| -------- | ----- | ------------------------------------------------------------ |
+| pcm_data | bytes | 要发送的二进制音频数据,格式需要和上一次start中的aformat相对应。**SDK不会自动将pcm编码成opus或opu,如果需要使用opus或opu,需要用户自行编码** |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+
+
+### 代码示例:
+
+```python
+import time
+import threading
+import sys
+
+import nls
+
+
+URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
+AKID="Your AKID"
+AKKEY="Your AKSECRET"
+APPKEY="Your APPKEY"
+
+#以下代码会根据音频文件内容反复进行一句话识别
+class TestSr:
+    def __init__(self, tid, test_file):
+        self.__th = threading.Thread(target=self.__test_run)
+        self.__id = tid
+        self.__test_file = test_file
+   
+    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_start(self, message, *args):
+        print("test_on_start:{}".format(message))
+
+    def test_on_error(self, message, *args):
+        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("test_on_chg:{}".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))
+        
+        sr = nls.NlsSpeechRecognizer(
+                    url=URL,
+                    akid=AKID,
+                    aksecret=AKKEY,
+                    appkey=APPKEY,
+                    on_start=self.test_on_start,
+                    on_result_changed=self.test_on_result_chg,
+                    on_completed=self.test_on_completed,
+                    on_error=self.test_on_error,
+                    on_close=self.test_on_close,
+                    callback_args=[self.__id]
+                )
+        while True:
+            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(1)
+
+def multiruntest(num=500):
+    for i in range(0, num):
+        name = "thread" + str(i)
+        t = TestSr(name, "tests/test1.pcm")
+        t.start()
+
+nls.enableTrace(True)
+multiruntest(1)
+
+
+
+```
+
+
+
+## 实时语音识别
+
+实时语音识别对应的类为NlsSpeechTranscriber,其核心方法如下:
+
+### 1. 初始化(\_\_init\_\_)
+
+参数说明:
+
+| 参数              | 类型     | 参数说明                                                     |
+| ----------------- | -------- | ------------------------------------------------------------ |
+| url               | str      | 网关websocket url,默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
+| akid              | str      | 账号access id,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| aksecret          | str      | 账号access secret key,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| token             | str      | 访问token,参考开发指南—获取Token及获取Token协议说明相关内容 |
+| on_start          | function | 当实时识别就绪时的回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_sentence_begin | function | 当实时识别一句话开始时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_sentence_end   | function | 当实时识别一句话结束时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_result_changed | function | 当实时识别返回中间结果时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_completed      | function | 当实时识别返回最终识别结果时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_error          | function | 当SDK或云端出现错误时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_close          | function | 当和云端连接断开时回调,回调参数有一个——用户自定义参数,见后续callback_args参数说明 |
+| callback_args     | list     | 用户自定义参数列表,里面的内容会pack成list传递给各个回调的最后一个参数 |
+
+返回值:
+
+无
+
+### 2. start
+
+> 同步开始实时识别,该方法会阻塞当前线程直到实时识别就绪(on_start回调返回)
+
+参数说明:
+
+| 参数                              | 类型 | 参数说明                                                     |
+| --------------------------------- | ---- | ------------------------------------------------------------ |
+| aformat                           | str  | 要识别音频格式,支持pcm,opus,opu,默认pcm,**SDK不会自动将pcm编码成opus或opu,如果需要使用opus或opu,需要用户自行编码** |
+| sample_rate                       | int  | 识别音频采样率,默认16000                                    |
+| ch                                | int  | 音频通道数,默认为1,不需要提供                              |
+| enable_intermediate_result        | bool | 是否返回中间结果,默认False                                  |
+| enable_punctuation_prediction    | bool | 是否进行识别结果标点预测,默认False                          |
+| enable_inverse_text_normalization | bool | 是否进行ITN,默认False                                       |
+| timeout                           | int  | 阻塞超时,默认10秒                                           |
+| ping_interval                     | int  | ping包发送间隔,默认8,不需要可以设置为0或None               |
+| ping_timeout                      | int  | 是否检查pong包超时,默认为None,None为不检查pong包超时       |
+| ex                                | dict | 用户提供的额外参数,该字典内容会以key:value形式合并进请求的payload段中,具体可用参数见实时语音识别接口说明 |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 3. stop
+
+> 停止实时识别,并同步等待on_completed回调结束
+
+参数说明:
+
+| 参数    | 类型 | 参数说明           |
+| ------- | ---- | ------------------ |
+| timeout | int  | 阻塞超时,默认10秒 |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 4. shutdown
+
+> 强行关闭当前请求,重复调用无副作用
+
+参数说明:
+
+无
+
+返回值:
+
+无
+
+### 5. send_audio
+
+> 发送二进制音频数据,发送数据的格式需要和start中的aformat对应
+
+参数说明:
+
+| 参数     | 类型  | 参数说明                                                     |
+| -------- | ----- | ------------------------------------------------------------ |
+| pcm_data | bytes | 要发送的二进制音频数据,格式需要和上一次start中的aformat相对应。**SDK不会自动将pcm编码成opus或opu,如果需要使用opus或opu,需要用户自行编码** |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 6. ctrl
+
+> 发送控制命令,先阅读实时语音识别接口说明
+
+参数说明:
+
+| 参数 | 类型 | 参数说明                                                     |
+| ---- | ---- | ------------------------------------------------------------ |
+| ex   | dict | 自定义控制命令,该字典内容会以key:value形式合并进请求的payload段中 |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+
+
+### 代码示例
+
+```python
+import time
+import threading
+import sys
+
+import nls
+
+
+URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
+AKID="Your AKID"
+AKKEY="Your AKSECRET"
+APPKEY="Your APPKEY"
+
+#以下代码会根据音频文件内容反复进行实时识别(文件转写)
+class TestSt:
+    def __init__(self, tid, test_file):
+        self.__th = threading.Thread(target=self.__test_run)
+        self.__id = tid
+        self.__test_file = test_file
+   
+    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 args=>{}".format(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_completed(self, message, *args):
+        print("on_completed:args=>{} message=>{}".format(args, message))
+
+
+    def __test_run(self):
+        print("thread:{} start..".format(self.__id))
+        sr = nls.NlsSpeechTranscriber(
+                    url=URL,
+                    akid=AKID,
+                    aksecret=AKKEY,
+                    appkey=APPKEY,
+                    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_error=self.test_on_error,
+                    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(1)
+
+def multiruntest(num=500):
+    for i in range(0, num):
+        name = "thread" + str(i)
+        t = TestSt(name, "tests/test1.pcm")
+        t.start()
+
+nls.enableTrace(True)
+multiruntest(1)
+
+```
+
+
+
+## 语音合成
+
+语音合成对应的类为NlsSpeechSynthesizer,其核心方法如下:
+
+### 1. 初始化(\_\_init\_\_)
+
+参数说明:
+
+| 参数          | 类型     | 参数说明                                                     |
+| ------------- | -------- | ------------------------------------------------------------ |
+| url           | str      | 网关websocket url,默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
+| akid          | str      | 账号access id,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| aksecret      | str      | 账号access secret key,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| token         | str      | 访问token,参考开发指南—获取Token及获取Token协议说明相关内容 |
+| on_metainfo   | function | 如果start中通过ex参数传递enable_subtitle,则会返回对应字幕信息,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_data       | function | 当存在合成数据后回调,回调参数有两个,一个是对应start方法aformat的二进制音频数据,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_completed  | function | 当合成完毕时候回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_error      | function | 当SDK或云端出现错误时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_close      | function | 当和云端连接断开时回调,回调参数有一个——用户自定义参数,见后续callback_args参数说明 |
+| callback_args | list     | 用户自定义参数列表,里面的内容会pack成list传递给各个回调的最后一个参数 |
+
+返回值:
+
+无
+
+### 2. start
+
+> 同步开始语音合成,如果wait_complete为True(默认)则会阻塞直到所有音频合成完毕(on_completed返回之后)返回,否则会立即返回
+
+参数说明:
+
+| 参数              | 类型 | 参数说明                                                     |
+| ----------------- | ---- | ------------------------------------------------------------ |
+| text              | str  | 要合成的文字                                                 |
+| aformat           | str  | 合成出来音频的格式,默认为"pcm"                              |
+| voice             | str  | 发音人,默认为”xiaoyun“                                      |
+| sample_rate       | int  | 识别音频采样率,默认16000                                    |
+| volume            | int  | 音量大小,0~100,默认为50                                    |
+| speech_rate       | int  | 语速,-500~500,默认为0                                      |
+| pitch_rate        | int  | 语调,-500~500,默认为0                                      |
+| wait_complete     | bool | 是否阻塞到合成完成                                           |
+| start_timeout     | int  | 和云端连接建立超时,默认10秒                                 |
+| completed_timeout | int  | 从连接建立到合成完成超时,默认60秒                           |
+| ping_interval                     | int  | ping包发送间隔,默认8,不需要可以设置为0或None               |
+| ping_timeout                      | int  | 是否检查pong包超时,默认为None,None为不检查pong包超时       |
+| ex                | dict | 用户提供的额外参数,该字典内容会以key:value形式合并进请求的payload段中,具体可用参数见实时语音识别接口说明 |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 3. shutdown
+
+> 强行关闭当前请求,重复调用无副作用
+
+参数说明:
+
+无
+
+返回值:
+
+无
+
+### 代码示例:
+
+```python
+import time
+import threading
+import sys
+
+import nls
+
+URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
+AKID="Your AKID"
+AKKEY="Your AKSECRET"
+APPKEY="Your APPKEY"
+
+
+
+TEXT='大壮正想去摘取花瓣,谁知阿丽和阿强突然内讧,阿丽拿去手枪向树干边的阿强射击,两声枪响,阿强直接倒入水中'
+
+#以下代码会根据上述TEXT文本反复进行语音合成
+class TestTts:
+    def __init__(self, tid, test_file):
+        self.__th = threading.Thread(target=self.__test_run)
+        self.__id = tid
+        self.__test_file = test_file
+   
+    def start(self, text):
+        self.__text = text
+        self.__f = open(self.__test_file, "wb")
+        self.__th.start()
+    
+    def test_on_metainfo(self, message, *args):
+        print("on_metainfo message=>{}".format(message))  
+
+    def test_on_error(self, message, *args):
+        print("on_error args=>{}".format(args))
+
+    def test_on_close(self, *args):
+        print("on_close: args=>{}".format(args))
+        try:
+            self.__f.close()
+        except Exception as e:
+            print("close file failed since:", e)
+
+    def test_on_data(self, data, *args):
+        try:
+            self.__f.write(data)
+        except Exception as e:
+            print("write data failed:", e)
+
+    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))
+        tts = nls.NlsSpeechSynthesizer(
+                    url=URL,
+                    akid=AKID,
+                    aksecret=AKKEY,
+                    appkey=APPKEY,
+                    on_metainfo=self.test_on_metainfo,
+                    on_data=self.test_on_data,
+                    on_completed=self.test_on_completed,
+                    on_error=self.test_on_error,
+                    on_close=self.test_on_close,
+                    callback_args=[self.__id]
+                )
+
+        while True:
+            print("{}: session start".format(self.__id))
+            r = tts.start(self.__text, voice="ailun")
+            print("{}: tts done with result:{}".format(self.__id, r))
+            time.sleep(1)
+
+def multiruntest(num=500):
+    for i in range(0, num):
+        name = "thread" + str(i)
+        t = TestTts(name, "tests/test_tts.pcm")
+        t.start(TEXT)
+
+nls.enableTrace(True)
+multiruntest(1)
+
+```
+
+
+
+## 高级用法——使用CommonProto实现任意接口
+
+nls包下面的NlsCommonProto对象可以用来实现智能语音交互的任意接口,其核心方法如下:
+
+### 1. 初始化(\_\_init\_\_)
+
+参数说明:
+
+| 参数          | 类型     | 参数说明                                                     |
+| ------------- | -------- | ------------------------------------------------------------ |
+| url           | str      | 网关websocket url,默认为wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1 |
+| akid          | str      | 账号access id,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| aksecret      | str      | 账号access secret key,默认为None,如果需要获取token,则需要提供,如果已有token,则不需要提供 |
+| token         | str      | 访问token,参考开发指南—获取Token及获取Token协议说明相关内容 |
+| on_open       | function | 当和云端建连完成后回调,回调参数有一个——用户自定义参数,见后续callback_args参数说明 |
+| on_error      | function | 当SDK或云端出现错误时回调,回调参数有两个,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| on_close      | function | 当和云端连接断开时回调,回调参数有一个——用户自定义参数,见后续callback_args参数说明 |
+| on_data       | function | 当云端返回二进制数据时回调,回调参数有两个,一个是二进制数据,一个是用户自定义参数,见后续callback_args参数说明 |
+| user_callback | dict     | 用户自定义回调字典,key为str类型,value为function类型,当SDK收到云端应答后,会根据应答内部header字段下面name字段来索引该字典,如果对应item存在则会回调对应function。该回调包括两个参数,一个是json形式的字符串,一个是用户自定义参数,见后续callback_args参数说明 |
+| callback_args | list     | 用户自定义参数列表,里面的内容会pack成list传递给各个回调的最后一个参数 |
+
+返回值:
+
+无
+
+### 2. start
+
+> **异步**和云端建连并发起一次请求
+
+参数说明:
+
+| 参数    | 类型 | 参数说明                                 |
+| ------- | ---- | ---------------------------------------- |
+| name    | str  | 对应header字段中的name,用于指明请求类型 |
+| payload | dict | 请求中的payload,默认为{}                |
+| context | dict | 请求中的context,默认为{}                |
+| ping_interval                     | int  | ping包发送间隔,默认8,不需要可以设置为0或None               |
+| ping_timeout                      | int  | 是否检查pong包超时,默认为None,None为不检查pong包超时       |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 3. shutdown
+
+> 强行关闭当前请求,重复调用无副作用
+
+参数说明:
+
+无
+
+返回值:
+
+无
+
+### 4. send_text
+
+> 发送文本请求
+
+参数说明:
+
+| 参数    | 类型 | 参数说明                                 |
+| ------- | ---- | ---------------------------------------- |
+| name    | str  | 对应header字段中的name,用于指明请求类型 |
+| payload | dict | 请求中的payload,默认为{}                |
+| context | dict | 请求中的context,默认为{}                |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+### 5. send_binary
+
+> 发送二进制数据
+
+参数说明:
+
+| 参数     | 类型  | 参数说明           |
+| -------- | ----- | ------------------ |
+| pcm_data | bytes | 要发送的二进频数据 |
+
+返回值:
+
+bool类型,False为失败,True为成功
+
+
+
+### 示例代码:
+
+```python
+import time
+import threading
+import sys
+
+import nls
+
+URL="wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"
+AKID="Your AKID"
+AKKEY="Your AKSECRET"
+APPKEY="Your APPKEY"
+
+#以下代码会通过文件反复进行一句话识别
+class TestSrCommon:
+    def __init__(self, tid, test_file):
+        self.__th = threading.Thread(target=self.__test_run)
+        self.__id = tid
+        self.__test_file = test_file
+   
+    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_open(self, *args):
+        print("test_on_start")
+
+    def test_on_start(self, message, *args):
+        print("test_on_start:{}".format(message))
+
+    def test_on_data(self, data, *args):
+        pass
+
+    def test_on_error(self, message, *args):
+        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("test_on_chg:{}".format(message))
+
+    def test_on_completed(self, message, *args):
+        print("on_completed:args=>{} message=>{}".format(args, message))
+        self.__sr.shutdown() 
+
+    def __test_run(self):
+        print("thread:{} start..".format(self.__id))
+        callbacks = {
+                "RecognitionStarted":self.test_on_start,
+                "RecognitionResultChanged":self.test_on_result_chg,
+                "RecognitionCompleted":self.test_on_completed
+                }
+        self.__sr = nls.NlsCommonProto(
+                    url=URL,
+                    akid=AKID,
+                    aksecret=AKKEY,
+                    appkey=APPKEY,
+                    namespace="SpeechRecognizer",
+                    on_open=self.test_on_open,
+                    on_data=self.test_on_data,
+                    on_error=self.test_on_error,
+                    on_close=self.test_on_close,
+                    user_callback=callbacks,
+                    callback_args=[self.__id]
+                )
+
+        while True:
+            print("{}: session start".format(self.__id))
+            r = self.__sr.start(name="StartRecognition",
+                    payload={
+                        "format":"pcm",
+                        "sample_rate":16000
+                        }
+                    )
+            if not r:
+                print("start failed")
+                break
+            time.sleep(1)
+           
+            self.__slices = zip(*(iter(self.__data),) * 640)
+            for i in self.__slices:
+                self.__sr.send_binary(bytes(i))
+                time.sleep(0.01)
+
+            self.__sr.send_text(name="StopRecognition") 
+            time.sleep(1)
+
+def multiruntest(num=500):
+    for i in range(0, num):
+        name = "thread" + str(i)
+        t = TestSrCommon(name, "tests/test1.pcm")
+        t.start()
+
+nls.enableTrace(True)
+multiruntest(1)
+
+```
+

+ 31 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/PKG-INFO

@@ -0,0 +1,31 @@
+Metadata-Version: 2.1
+Name: nls
+Version: 1.0.0
+Summary: python sdk for nls
+Home-page: https://github.com/..
+Author: jiaqi.sjq
+Author-email: jiaqi.sjq@alibaba-inc.com
+License: Apache License 2.0
+Keywords: nls,sdk
+Classifier: Programming Language :: Python :: 3
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Description-Content-Type: text/markdown
+License-File: LICENSE
+Requires-Dist: oss2
+Requires-Dist: aliyun-python-sdk-core>=2.13.3
+Requires-Dist: matplotlib>=3.3.4
+
+# alibaba-nls-python-sdk
+
+This is Python SDK for NLS. It supports
+SPEECH-RECOGNIZER/SPEECH-SYNTHESIZER/SPEECH-TRANSLATOR/COMMON-REQUESTS-PROTO.
+
+This module works on Python versions:
+> 3.6 and greater
+
+install requirements:
+> python -m pip install -r requirements.txt
+
+install package:
+> python -m pip install .

+ 36 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/SOURCES.txt

@@ -0,0 +1,36 @@
+LICENSE
+README.md
+setup.py
+nls/__init__.py
+nls/core.py
+nls/exception.py
+nls/logging.py
+nls/speech_recognizer.py
+nls/speech_synthesizer.py
+nls/speech_transcriber.py
+nls/token.py
+nls/util.py
+nls/version.py
+nls.egg-info/PKG-INFO
+nls.egg-info/SOURCES.txt
+nls.egg-info/dependency_links.txt
+nls.egg-info/requires.txt
+nls.egg-info/top_level.txt
+nls/websocket/__init__.py
+nls/websocket/_abnf.py
+nls/websocket/_app.py
+nls/websocket/_cookiejar.py
+nls/websocket/_core.py
+nls/websocket/_exceptions.py
+nls/websocket/_handshake.py
+nls/websocket/_http.py
+nls/websocket/_logging.py
+nls/websocket/_socket.py
+nls/websocket/_ssl_compat.py
+nls/websocket/_url.py
+nls/websocket/_utils.py
+tests/test_sr.py
+tests/test_st.py
+tests/test_token.py
+tests/test_tts.py
+tests/test_utils.py

+ 1 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/dependency_links.txt

@@ -0,0 +1 @@
+

+ 3 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/requires.txt

@@ -0,0 +1,3 @@
+oss2
+aliyun-python-sdk-core>=2.13.3
+matplotlib>=3.3.4

+ 2 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls.egg-info/top_level.txt

@@ -0,0 +1,2 @@
+nls
+nls/websocket

+ 8 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__init__.py

@@ -0,0 +1,8 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+from .logging import *
+from .speech_recognizer import *
+from .speech_transcriber import *
+from .speech_synthesizer import *
+from .util import *
+from .version import __version__

BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/__init__.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/_logger.cpython-36.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/_logging.cpython-36.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/core.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/exception.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/logging.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/speech_recognizer.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/speech_synthesizer.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/speech_transcriber.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/token.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/util.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/__pycache__/version.cpython-39.pyc


+ 183 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/core.py

@@ -0,0 +1,183 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+import logging
+import threading
+
+from enum import Enum, unique
+from queue import Queue
+
+from . import logging, token, websocket
+from .exception import InvalidParameter, ConnectionTimeout, ConnectionUnavailable
+
+__URL__ = 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1'
+__HEADER__ = [
+    'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==',
+    'Sec-WebSocket-Version: 13',
+]
+
+__FORMAT__ = '%(asctime)s - %(levelname)s - %(message)s'
+#__all__ = ['NlsCore']
+
+def core_on_msg(ws, message, args):
+    logging.debug('core_on_msg:{}'.format(message))
+    if not args:
+        logging.error('callback core_on_msg with null args')
+        return
+    nls = args[0]
+    nls._NlsCore__issue_callback('on_message', [message])
+
+def core_on_error(ws, message, args):
+    logging.debug('core_on_error:{}'.format(message))
+    if not args:
+        logging.error('callback core_on_error with null args')
+        return
+    nls = args[0]
+    nls._NlsCore__issue_callback('on_error', [message])
+
+def core_on_close(ws, close_status_code, close_msg, args):
+    logging.debug('core_on_close')
+    if not args:
+        logging.error('callback core_on_close with null args')
+        return
+    nls = args[0]
+    nls._NlsCore__issue_callback('on_close')
+
+def core_on_open(ws, args):
+    logging.debug('core_on_open:{}'.format(args))
+    if not args:
+        logging.debug('callback with null args')
+        ws.close()
+    elif len(args) != 2:
+        logging.debug('callback args not 2')
+        ws.close()
+    nls = args[0]
+    nls._NlsCore__notify_on_open()
+    nls.start(args[1], nls._NlsCore__ping_interval, nls._NlsCore__ping_timeout)
+    nls._NlsCore__issue_callback('on_open')
+
+def core_on_data(ws, data, opcode, flag, args):
+    logging.debug('core_on_data opcode={}'.format(opcode))
+    if not args:
+        logging.error('callback core_on_data with null args')
+        return
+    nls = args[0]
+    nls._NlsCore__issue_callback('on_data', [data, opcode, flag])
+
+@unique
+class NlsConnectionStatus(Enum):
+    Disconnected = 0
+    Connected = 1
+
+
+class NlsCore:
+    """
+    NlsCore
+    """
+    def __init__(self, 
+                 url=__URL__,
+                 token=None,
+                 on_open=None, on_message=None, on_close=None,
+                 on_error=None, on_data=None, asynch=False, callback_args=[]):
+        self.__url = url
+        self.__async = asynch
+        if not token:
+            raise InvalidParameter('Must provide a valid token!')
+        else:
+            self.__token = token
+        self.__callbacks = {}
+        if on_open:
+            self.__callbacks['on_open'] = on_open
+        if on_message:
+            self.__callbacks['on_message'] = on_message
+        if on_close:
+            self.__callbacks['on_close'] = on_close
+        if on_error:
+            self.__callbacks['on_error'] = on_error
+        if on_data:
+            self.__callbacks['on_data'] = on_data
+        if not on_open and not on_message and not on_close and not on_error:
+            raise InvalidParameter('Must provide at least one callback')
+        logging.debug('callback args:{}'.format(callback_args))
+        self.__callback_args = callback_args
+        self.__header = __HEADER__ + ['X-NLS-Token: {}'.format(self.__token)]
+        websocket.enableTrace(True)
+        self.__ws = websocket.WebSocketApp(self.__url,
+                                           self.__header,
+                                           on_message=core_on_msg,
+                                           on_data=core_on_data,
+                                           on_error=core_on_error,
+                                           on_close=core_on_close,
+                                           callback_args=[self])
+        self.__ws.on_open = core_on_open
+        self.__lock = threading.Lock()
+        self.__cond = threading.Condition()
+        self.__connection_status = NlsConnectionStatus.Disconnected
+
+    def start(self, msg, ping_interval, ping_timeout):
+        self.__lock.acquire()
+        self.__ping_interval = ping_interval
+        self.__ping_timeout = ping_timeout
+        if self.__connection_status == NlsConnectionStatus.Disconnected:
+            self.__ws.update_args(self, msg)
+            self.__lock.release()
+            self.__connect_before_start(ping_interval, ping_timeout)
+        else:
+            self.__lock.release()
+            self.__ws.send(msg)
+
+    def __notify_on_open(self):
+        logging.debug('notify on open')
+        with self.__cond:
+            self.__connection_status = NlsConnectionStatus.Connected
+            self.__cond.notify()
+
+    def __issue_callback(self, which, exargs=[]):
+        if which not in self.__callbacks:
+            logging.error('no such callback:{}'.format(which))
+            return
+        if which is 'on_close':
+            with self.__cond:
+                self.__connection_status = NlsConnectionStatus.Disconnected
+                self.__cond.notify()
+        args = exargs+self.__callback_args
+        self.__callbacks[which](*args)
+
+    def send(self, msg, binary):
+        self.__lock.acquire()
+        if self.__connection_status == NlsConnectionStatus.Disconnected:
+            self.__lock.release()
+            logging.error('start before send')
+            raise ConnectionUnavailable('Must call start before send!')
+        else:
+            self.__lock.release()
+            if binary:
+                self.__ws.send(msg, opcode=websocket.ABNF.OPCODE_BINARY)
+            else:
+                logging.debug('send {}'.format(msg))
+                self.__ws.send(msg)
+    
+    def shutdown(self):
+        self.__ws.close()
+
+    def __run(self, ping_interval, ping_timeout):
+        logging.debug('ws run...')
+        self.__ws.run_forever(ping_interval=ping_interval,
+                ping_timeout=ping_timeout)
+        with self.__lock:
+            self.__connection_status = NlsConnectionStatus.Disconnected
+        logging.debug('ws exit...')
+
+    def __connect_before_start(self, ping_interval, ping_timeout):
+        with self.__cond:
+            self.__th = threading.Thread(target=self.__run,
+                    args=[ping_interval, ping_timeout])
+            self.__th.start()
+            if self.__connection_status == NlsConnectionStatus.Disconnected:
+                logging.debug('wait cond wakeup')
+                if not self.__async:
+                    if self.__cond.wait(timeout=10):
+                        logging.debug('wakeup without timeout')
+                        return self.__connection_status == NlsConnectionStatus.Connected
+                    else:
+                        logging.debug('wakeup with timeout')
+                        raise ConnectionTimeout('Wait response timeout! Please check local network!')

+ 28 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/exception.py

@@ -0,0 +1,28 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+
+class InvalidParameter(Exception):
+    pass
+
+# Token
+class GetTokenFailed(Exception):
+    pass
+
+# Connection
+class ConnectionTimeout(Exception):
+    pass
+
+class ConnectionUnavailable(Exception):
+    pass
+
+class StartTimeoutException(Exception):
+    pass
+
+class StopTimeoutException(Exception):
+    pass
+
+class NotStartException(Exception):
+    pass
+
+class CompleteTimeoutException(Exception):
+    pass

+ 65 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/logging.py

@@ -0,0 +1,65 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+import logging
+
+_logger = logging.getLogger('nls')
+
+try:
+    from logging import NullHandler
+except ImportError:
+    class NullHandler(logging.Handler):
+        def emit(self, record):
+            pass
+
+_logger.addHandler(NullHandler())
+_traceEnabled = False
+__LOG_FORMAT__ = '%(asctime)s - %(levelname)s - %(message)s'
+
+__all__=['enableTrace', 'dump', 'error', 'warning', 'debug', 'trace',
+        'isEnabledForError', 'isEnabledForDebug', 'isEnabledForTrace']
+
+def enableTrace(traceable, handler=logging.StreamHandler()):
+    """
+    enable log print
+
+    Parameters
+    ----------
+    traceable: bool
+        whether enable log print, default log level is logging.DEBUG
+    handler: Handler object
+        handle how to print out log, default to stdio
+    """
+    global _traceEnabled
+    _traceEnabled = traceable
+    if traceable:
+        _logger.addHandler(handler)
+        _logger.setLevel(logging.DEBUG)
+        handler.setFormatter(logging.Formatter(__LOG_FORMAT__))
+
+def dump(title, message):
+    if _traceEnabled:
+        _logger.debug('### ' + title + ' ###')
+        _logger.debug(message)
+        _logger.debug('########################################')
+
+def error(msg):
+    _logger.error(msg)
+
+def warning(msg):
+    _logger.warning(msg)
+
+def debug(msg):
+    _logger.debug(msg)
+
+def trace(msg):
+    if _traceEnabled:
+        _logger.debug(msg)
+
+def isEnabledForError():
+    return _logger.isEnabledFor(logging.ERROR)
+
+def isEnabledForDebug():
+    return _logger.isEnabledFor(logging.Debug)
+
+def isEnabledForTrace():
+    return _traceEnabled

+ 315 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/speech_recognizer.py

@@ -0,0 +1,315 @@
+# 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 .exception import (StartTimeoutException,
+                        StopTimeoutException,
+                        NotStartException,
+                        InvalidParameter)
+
+__SPEECH_RECOGNIZER_NAMESPACE__ = 'SpeechRecognizer'
+
+__SPEECH_RECOGNIZER_REQUEST_CMD__ = {
+    'start': 'StartRecognition',
+    'stop': 'StopRecognition'
+}
+
+__URL__ = 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1'
+
+__all__ = ['NlsSpeechRecognizer']
+
+
+class NlsSpeechRecognizer:
+    """
+    Api for short sentence speech recognition
+    """    
+    def __init__(self,
+                 url=__URL__,
+                 token=None,
+                 appkey=None,
+                 on_start=None,
+                 on_result_changed=None,
+                 on_completed=None,
+                 on_error=None, on_close=None,
+                 callback_args=[]):
+        """
+        NlsSpeechRecognizer initialization
+
+        Parameters:
+        -----------
+        url: str
+            websocket url.
+        token: str
+            access token. if you do not have a token, provide access id and key
+            secret from your aliyun account.
+        appkey: str
+            appkey from aliyun
+        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_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_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__ = {
+            'RecognitionStarted': self.__recognition_started,
+            'RecognitionResultChanged': self.__recognition_result_changed,
+            'RecognitionCompleted': self.__recognition_completed,
+            'TaskFailed': self.__task_failed
+        }
+        self.__callback_args = callback_args
+        self.__appkey = appkey
+        self.__url = url
+        self.__token = token
+        self.__start_cond = threading.Condition()
+        self.__start_flag = False
+        self.__on_start = on_start
+        self.__on_result_changed = on_result_changed
+        self.__on_completed = on_completed
+        self.__on_error = on_error
+        self.__on_close = on_close
+        self.__allow_aformat = (
+            'pcm', 'opus', 'opu', 'wav', 'mp3', 'speex', 'aac', 'amr'
+        )
+
+    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 __sr_core_on_open(self):
+        logging.debug('__sr_core_on_open')
+
+    def __sr_core_on_msg(self, msg, *args):
+        logging.debug('__sr_core_on_msg:msg={} args={}'.format(msg, args))
+        self.__handle_message(msg)
+
+    def __sr_core_on_error(self, msg, *args):
+        logging.debug('__sr_core_on_error:msg={} args={}'.format(msg, args))
+
+    def __sr_core_on_close(self):
+        logging.debug('__sr_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 __recognition_started(self, message):
+        logging.debug('__recognition_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 __recognition_result_changed(self, message):
+        logging.debug('__recognition_result_changed')
+        if self.__on_result_changed:
+            self.__on_result_changed(message, *self.__callback_args)
+
+    def __recognition_completed(self, message):
+        logging.debug('__recognition_completed')
+        self.__nls.shutdown()
+        logging.debug('__recognition_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, aformat='pcm', sample_rate=16000, ch=1,
+              enable_intermediate_result=False,
+              enable_punctuation_prediction=False,
+              enable_inverse_text_normalization=False,
+              timeout=10,
+              ping_interval=8,
+              ping_timeout=None,
+              ex:dict=None):
+        """
+        Recognition start 
+
+        Parameters:
+        -----------
+        aformat: str
+            audio binary format, support: 'pcm', 'opu', 'opus', default is 'pcm'
+        sample_rate: int
+            audio sample rate, default is 16000
+        ch: int
+            audio channels, only support mono which is 1
+        enable_intermediate_result: bool
+            whether enable return intermediate recognition result, default is False
+        enable_punctuation_prediction: bool
+            whether enable punctuation prediction, default is False
+        enable_inverse_text_normalization: bool
+            whether enable ITN, default is False
+        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=self.__token,
+            on_open=self.__sr_core_on_open,
+            on_message=self.__sr_core_on_msg,
+            on_close=self.__sr_core_on_close,
+            on_error=self.__sr_core_on_error,
+            callback_args=[])
+
+        if ch != 1:
+            raise InvalidParameter(f'Not support channel {ch}')
+        if aformat not in self.__allow_aformat:
+            raise InvalidParameter(f'Format {aformat} not support')
+
+        __id4 = uuid.uuid4().hex
+        self.__task_id = uuid.uuid4().hex
+        __header = {
+            'message_id': __id4,
+            'task_id': self.__task_id,
+            'namespace': __SPEECH_RECOGNIZER_NAMESPACE__,
+            'name': __SPEECH_RECOGNIZER_REQUEST_CMD__['start'],
+            'appkey': self.__appkey
+        }
+        __payload = {
+            'format': aformat,
+            'sample_rate': sample_rate,
+            'enable_intermediate_result': enable_intermediate_result,
+            'enable_punctuation_prediction': enable_punctuation_prediction,
+            'enable_inverse_text_normalization': enable_inverse_text_normalization
+        }
+
+        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=timeout):
+                    return
+                else:
+                    raise StartTimeoutException(f'Waiting Start over {timeout}s')
+
+    def stop(self, timeout=10):
+        """
+        Stop recognition 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': __SPEECH_RECOGNIZER_NAMESPACE__,
+            'name': __SPEECH_RECOGNIZER_REQUEST_CMD__['stop'],
+            'appkey': self.__appkey
+        }
+        __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 is 'aformat' in start method 
+        """
+        if not pcm_data:
+            raise InvalidParameter('data empty!')
+        __data = pcm_data
+        with self.__start_cond:
+            if not self.__start_flag:
+                raise NotStartException('Need start before send!')
+        try:
+            self.__nls.send(__data, True)
+        except ConnectionResetError as __e:
+            logging.error('connection reset')
+            self.__start_flag = False
+            self.__nls.shutdown()
+            raise __e

+ 288 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/speech_synthesizer.py

@@ -0,0 +1,288 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+import logging
+from re import I
+import uuid
+import json
+import threading
+
+from nls.core import NlsCore
+from . import logging
+from . import util
+from .exception import (StartTimeoutException,
+                        CompleteTimeoutException,
+                        InvalidParameter)
+
+__SPEECH_SYNTHESIZER_NAMESPACE__ = 'SpeechSynthesizer'
+__SPEECH_LONG_SYNTHESIZER_NAMESPACE__ = 'SpeechLongSynthesizer'
+
+__SPEECH_SYNTHESIZER_REQUEST_CMD__ = {
+    'start': 'StartSynthesis'
+}
+
+__URL__ = 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1'
+
+__all__ = ['NlsSpeechSynthesizer']
+
+
+class NlsSpeechSynthesizer:
+    """
+    Api for text-to-speech 
+    """
+    def __init__(self,
+                 url=__URL__,
+                 token=None,
+                 appkey=None,
+                 long_tts=False,
+                 on_metainfo=None,
+                 on_data=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
+        long_tts: bool
+            whether using long-text synthesis support, default is False. long-text synthesis
+            can support longer text but more expensive.
+        on_metainfo: 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_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_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__ = {
+            'MetaInfo': self.__metainfo,
+            'SynthesisCompleted': self.__synthesis_completed,
+            'TaskFailed': self.__task_failed
+        }
+        self.__callback_args = callback_args
+        self.__url = url
+        self.__appkey = appkey
+        self.__token = token
+        self.__long_tts = long_tts
+        self.__start_cond = threading.Condition()
+        self.__start_flag = False
+        self.__on_metainfo = on_metainfo
+        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
+                )
+
+    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')
+        with self.__start_cond:
+            self.__start_flag = True
+            self.__start_cond.notify()
+
+    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)
+        with self.__start_cond:
+            self.__start_flag = False
+            self.__start_cond.notify()
+
+    def __metainfo(self, message):
+        logging.debug('__metainfo')
+        if self.__on_metainfo:
+            self.__on_metainfo(message, *self.__callback_args)
+
+    def __synthesis_completed(self, message):
+        logging.debug('__synthesis_completed')
+        self.__nls.shutdown()
+        logging.debug('__synthesis_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,
+              text=None,
+              voice='xiaoyun',
+              aformat='pcm',
+              sample_rate=16000,
+              volume=50,
+              speech_rate=0,
+              pitch_rate=0,
+              wait_complete=True,
+              start_timeout=10,
+              completed_timeout=60,
+              ex:dict=None):
+        """
+        Synthesis start 
+
+        Parameters:
+        -----------
+        text: str
+            utf-8 text
+        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 16000, 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
+        wait_complete: bool
+            whether block until syntheis completed or timeout for completed timeout
+        start_timeout: int
+            timeout for connection established
+        completed_timeout: int
+            timeout for waiting synthesis completed from connection established
+        ex: dict
+            dict which will merge into 'payload' field in request
+        """
+        if text is None:
+            raise InvalidParameter('Text cannot be None')
+        
+        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))
+
+        __id4 = uuid.uuid4().hex
+        self.__task_id = uuid.uuid4().hex
+        __namespace = __SPEECH_SYNTHESIZER_NAMESPACE__
+        if self.__long_tts:
+            __namespace = __SPEECH_LONG_SYNTHESIZER_NAMESPACE__
+        __header = {
+            'message_id': __id4,
+            'task_id': self.__task_id,
+            'namespace': __namespace,
+            'name': __SPEECH_SYNTHESIZER_REQUEST_CMD__['start'],
+            'appkey': self.__appkey
+        }
+        __payload = {
+            'text': text,
+            'voice': voice,
+            'format': aformat,
+            'sample_rate': sample_rate,
+            'volume': volume,
+            'speech_rate': speech_rate,
+            'pitch_rate': pitch_rate
+        }
+        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=0, ping_timeout=None)
+            if self.__start_flag == False:
+                if not self.__start_cond.wait(start_timeout):
+                    logging.debug('syn start timeout')
+                    raise StartTimeoutException(f'Waiting Start over {start_timeout}s')
+            if self.__start_flag and wait_complete:
+                if not self.__start_cond.wait(completed_timeout):
+                    raise CompleteTimeoutException(f'Waiting Complete over {completed_timeout}s')
+
+    def shutdown(self):
+        """
+        Shutdown connection immediately
+        """
+        self.__nls.shutdown()

+ 374 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/speech_transcriber.py

@@ -0,0 +1,374 @@
+# 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,
+                        NotStartException,
+                        InvalidParameter)
+
+__SPEECH_TRANSCRIBER_NAMESPACE__ = 'SpeechTranscriber'
+
+__SPEECH_TRANSCRIBER_REQUEST_CMD__ = {
+    'start': 'StartTranscription',
+    'stop': 'StopTranscription',
+    'control': 'ControlTranscriber'
+}
+
+__URL__ = 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1'
+__all__ = ['NlsSpeechTranscriber']
+
+
+class NlsSpeechTranscriber:
+    """
+    Api for realtime speech transcription
+    """
+
+    def __init__(self, 
+                 url=__URL__,
+                 token=None,
+                 appkey=None,
+                 on_start=None,
+                 on_sentence_begin=None,
+                 on_sentence_end=None,
+                 on_result_changed=None,
+                 on_completed=None,
+                 on_error=None,
+                 on_close=None,
+                 callback_args=[]):
+        '''
+        NlsSpeechTranscriber initialization
+
+        Parameters:
+        -----------
+        url: str
+            websocket url.
+        token: str
+            access token. if you do not have a token, provide access id and key
+            secret from your aliyun account.
+        appkey: str
+            appkey from aliyun
+        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_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__ = {
+            'SentenceBegin': self.__sentence_begin,
+            'SentenceEnd': self.__sentence_end,
+            'TranscriptionStarted': self.__transcription_started,
+            'TranscriptionResultChanged': self.__transcription_result_changed,
+            'TranscriptionCompleted': self.__transcription_completed,
+            'TaskFailed': self.__task_failed
+        }
+        self.__callback_args = callback_args
+        self.__url = url
+        self.__appkey = appkey
+        self.__token = token
+        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_completed = on_completed
+        self.__on_error = on_error
+        self.__on_close = on_close
+        self.__allow_aformat = (
+            'pcm', 'opus', 'opu', 'wav', 'amr', 'speex', 'mp3', 'aac'
+        )
+
+    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 __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))
+
+    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_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, aformat='pcm', sample_rate=16000, ch=1,
+              enable_intermediate_result=False,
+              enable_punctuation_prediction=False,
+              enable_inverse_text_normalization=False,
+              timeout=10,
+              ping_interval=8,
+              ping_timeout=None,
+              ex:dict=None):
+        """
+        Transcription start 
+
+        Parameters:
+        -----------
+        aformat: str
+            audio binary format, support: 'pcm', 'opu', 'opus', default is 'pcm'
+        sample_rate: int
+            audio sample rate, default is 16000
+        ch: int
+            audio channels, only support mono which is 1
+        enable_intermediate_result: bool
+            whether enable return intermediate recognition result, default is False
+        enable_punctuation_prediction: bool
+            whether enable punctuation prediction, default is False
+        enable_inverse_text_normalization: bool
+            whether enable ITN, default is False
+        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=self.__token,
+            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=[])
+
+        if ch != 1:
+            raise ValueError('not support channel: {}'.format(ch))
+        if aformat not in self.__allow_aformat:
+            raise ValueError('format {} not support'.format(aformat))
+        __id4 = uuid.uuid4().hex
+        self.__task_id = uuid.uuid4().hex
+        __header = {
+            'message_id': __id4,
+            'task_id': self.__task_id,
+            'namespace': __SPEECH_TRANSCRIBER_NAMESPACE__,
+            'name': __SPEECH_TRANSCRIBER_REQUEST_CMD__['start'],
+            'appkey': self.__appkey
+        }
+        __payload = {
+            'format': aformat,
+            'sample_rate': sample_rate,
+            'enable_intermediate_result': enable_intermediate_result,
+            'enable_punctuation_prediction': enable_punctuation_prediction,
+            'enable_inverse_text_normalization': enable_inverse_text_normalization
+        }
+
+        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 transcription 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': __SPEECH_TRANSCRIBER_NAMESPACE__,
+            'name': __SPEECH_TRANSCRIBER_REQUEST_CMD__['stop'],
+            'appkey': self.__appkey
+        }
+        __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 ctrl(self, **kwargs):
+        """
+        Send control message to cloud
+
+        Parameters:
+        -----------
+        kwargs: dict
+            dict which will merge into 'payload' field in request
+        """
+        if not kwargs:
+            raise InvalidParameter('Empty kwargs not allowed!')
+        __id4 = uuid.uuid4().hex
+        __header = {
+            'message_id': __id4,
+            'task_id': self.__task_id,
+            'namespace': __SPEECH_TRANSCRIBER_NAMESPACE__,
+            'name': __SPEECH_TRANSCRIBER_REQUEST_CMD__['control'],
+            'appkey': self.__appkey
+        }
+        payload = {}
+        payload.update(kwargs)
+        __msg = {
+            'header': __header,
+            'payload': payload,
+            '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)
+
+    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 is 'aformat' in start method 
+        """
+
+        __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

+ 49 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/token.py

@@ -0,0 +1,49 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+from aliyunsdkcore.client import AcsClient
+from aliyunsdkcore.request import CommonRequest
+from .exception import GetTokenFailed
+
+import json
+
+__all__ = ['getToken']
+
+def getToken(akid, aksecret, domain='cn-shanghai',
+             version='2019-02-28',
+             url='nls-meta.cn-shanghai.aliyuncs.com'):
+    """
+    Help methods to get token from aliyun by giving access id and access secret
+    key
+
+    Parameters:
+    -----------
+    akid: str
+        access id from aliyun
+    aksecret: str
+        access secret key from aliyun
+    domain: str:
+        default is cn-shanghai
+    version: str:
+        default is 2019-02-28
+    url: str
+        full url for getting token, default is
+        nls-meta.cn-shanghai.aliyuncs.com
+    """
+    if akid is None or aksecret is None:
+        raise GetTokenFailed('No akid or aksecret')
+    client = AcsClient(akid, aksecret, domain)
+    request = CommonRequest()
+    request.set_method('POST')
+    request.set_domain(url)
+    request.set_version(version)
+    request.set_action_name('CreateToken')
+    response = client.do_action_with_exception(request)
+    response_json = json.loads(response)
+    if 'Token' in response_json:
+        token = response_json['Token']
+        if 'Id' in token:
+            return token['Id']
+        else:
+            raise GetTokenFailed(f'Missing id field in token:{token}') 
+    else:
+        raise GetTokenFailed(f'Token not in response:{response_json}')

+ 44 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/util.py

@@ -0,0 +1,44 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+
+from struct import *
+
+__all__=['wav2pcm', 'GetDefaultContext']
+
+def GetDefaultContext():
+    """
+    Return Default Context Object
+    """
+    return {
+        'sdk': {
+            'name': 'nls-python-sdk',
+            'version': '0.0.1',
+            'language': 'python'
+        }
+    }
+
+
+def wav2pcm(wavfile, pcmfile):
+    """
+    Turn wav into pcm
+    
+    Parameters
+    ----------
+    wavfile: str
+        wav file path
+    pcmfile: str
+        output pcm file path
+    """
+    with open(wavfile, 'rb') as i, open(pcmfile, 'wb') as o:
+        i.seek(0)
+        _id = i.read(4)
+        _id = unpack('>I', _id)
+        _size = i.read(4)
+        _size = unpack('<I', _size)
+        _type = i.read(4)
+        _type = unpack('>I', _type)
+        if _id[0] != 0x52494646 or _type[0] != 0x57415645:
+            raise ValueError('not a wav!')
+        i.read(32)
+        result = i.read()
+        o.write(result)
+

+ 2 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/version.py

@@ -0,0 +1,2 @@
+# Copyright (c) Alibaba, Inc. and its affiliates.
+__version__ = '1.0.0'

+ 26 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__init__.py

@@ -0,0 +1,26 @@
+"""
+__init__.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+from ._abnf import *
+from ._app import WebSocketApp
+from ._core import *
+from ._exceptions import *
+from ._logging import *
+from ._socket import *
+
+__version__ = "1.2.1"

BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/__init__.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_abnf.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_app.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_cookiejar.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_core.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_exceptions.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_handshake.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_http.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_logging.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_socket.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_ssl_compat.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_url.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/__pycache__/_utils.cpython-39.pyc


+ 423 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_abnf.py

@@ -0,0 +1,423 @@
+"""
+
+"""
+
+"""
+_abnf.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import array
+import os
+import struct
+import sys
+
+from ._exceptions import *
+from ._utils import validate_utf8
+from threading import Lock
+
+try:
+    # If wsaccel is available, use compiled routines to mask data.
+    # wsaccel only provides around a 10% speed boost compared
+    # to the websocket-client _mask() implementation.
+    # Note that wsaccel is unmaintained.
+    from wsaccel.xormask import XorMaskerSimple
+
+    def _mask(_m, _d):
+        return XorMaskerSimple(_m).process(_d)
+
+except ImportError:
+    # wsaccel is not available, use websocket-client _mask()
+    native_byteorder = sys.byteorder
+
+    def _mask(mask_value, data_value):
+        datalen = len(data_value)
+        data_value = int.from_bytes(data_value, native_byteorder)
+        mask_value = int.from_bytes(mask_value * (datalen // 4) + mask_value[: datalen % 4], native_byteorder)
+        return (data_value ^ mask_value).to_bytes(datalen, native_byteorder)
+
+
+__all__ = [
+    'ABNF', 'continuous_frame', 'frame_buffer',
+    'STATUS_NORMAL',
+    'STATUS_GOING_AWAY',
+    'STATUS_PROTOCOL_ERROR',
+    'STATUS_UNSUPPORTED_DATA_TYPE',
+    'STATUS_STATUS_NOT_AVAILABLE',
+    'STATUS_ABNORMAL_CLOSED',
+    'STATUS_INVALID_PAYLOAD',
+    'STATUS_POLICY_VIOLATION',
+    'STATUS_MESSAGE_TOO_BIG',
+    'STATUS_INVALID_EXTENSION',
+    'STATUS_UNEXPECTED_CONDITION',
+    'STATUS_BAD_GATEWAY',
+    'STATUS_TLS_HANDSHAKE_ERROR',
+]
+
+# closing frame status codes.
+STATUS_NORMAL = 1000
+STATUS_GOING_AWAY = 1001
+STATUS_PROTOCOL_ERROR = 1002
+STATUS_UNSUPPORTED_DATA_TYPE = 1003
+STATUS_STATUS_NOT_AVAILABLE = 1005
+STATUS_ABNORMAL_CLOSED = 1006
+STATUS_INVALID_PAYLOAD = 1007
+STATUS_POLICY_VIOLATION = 1008
+STATUS_MESSAGE_TOO_BIG = 1009
+STATUS_INVALID_EXTENSION = 1010
+STATUS_UNEXPECTED_CONDITION = 1011
+STATUS_BAD_GATEWAY = 1014
+STATUS_TLS_HANDSHAKE_ERROR = 1015
+
+VALID_CLOSE_STATUS = (
+    STATUS_NORMAL,
+    STATUS_GOING_AWAY,
+    STATUS_PROTOCOL_ERROR,
+    STATUS_UNSUPPORTED_DATA_TYPE,
+    STATUS_INVALID_PAYLOAD,
+    STATUS_POLICY_VIOLATION,
+    STATUS_MESSAGE_TOO_BIG,
+    STATUS_INVALID_EXTENSION,
+    STATUS_UNEXPECTED_CONDITION,
+    STATUS_BAD_GATEWAY,
+)
+
+
+class ABNF:
+    """
+    ABNF frame class.
+    See http://tools.ietf.org/html/rfc5234
+    and http://tools.ietf.org/html/rfc6455#section-5.2
+    """
+
+    # operation code values.
+    OPCODE_CONT = 0x0
+    OPCODE_TEXT = 0x1
+    OPCODE_BINARY = 0x2
+    OPCODE_CLOSE = 0x8
+    OPCODE_PING = 0x9
+    OPCODE_PONG = 0xa
+
+    # available operation code value tuple
+    OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
+               OPCODE_PING, OPCODE_PONG)
+
+    # opcode human readable string
+    OPCODE_MAP = {
+        OPCODE_CONT: "cont",
+        OPCODE_TEXT: "text",
+        OPCODE_BINARY: "binary",
+        OPCODE_CLOSE: "close",
+        OPCODE_PING: "ping",
+        OPCODE_PONG: "pong"
+    }
+
+    # data length threshold.
+    LENGTH_7 = 0x7e
+    LENGTH_16 = 1 << 16
+    LENGTH_63 = 1 << 63
+
+    def __init__(self, fin=0, rsv1=0, rsv2=0, rsv3=0,
+                 opcode=OPCODE_TEXT, mask=1, data=""):
+        """
+        Constructor for ABNF. Please check RFC for arguments.
+        """
+        self.fin = fin
+        self.rsv1 = rsv1
+        self.rsv2 = rsv2
+        self.rsv3 = rsv3
+        self.opcode = opcode
+        self.mask = mask
+        if data is None:
+            data = ""
+        self.data = data
+        self.get_mask_key = os.urandom
+
+    def validate(self, skip_utf8_validation=False):
+        """
+        Validate the ABNF frame.
+
+        Parameters
+        ----------
+        skip_utf8_validation: skip utf8 validation.
+        """
+        if self.rsv1 or self.rsv2 or self.rsv3:
+            raise WebSocketProtocolException("rsv is not implemented, yet")
+
+        if self.opcode not in ABNF.OPCODES:
+            raise WebSocketProtocolException("Invalid opcode %r", self.opcode)
+
+        if self.opcode == ABNF.OPCODE_PING and not self.fin:
+            raise WebSocketProtocolException("Invalid ping frame.")
+
+        if self.opcode == ABNF.OPCODE_CLOSE:
+            l = len(self.data)
+            if not l:
+                return
+            if l == 1 or l >= 126:
+                raise WebSocketProtocolException("Invalid close frame.")
+            if l > 2 and not skip_utf8_validation and not validate_utf8(self.data[2:]):
+                raise WebSocketProtocolException("Invalid close frame.")
+
+            code = 256 * self.data[0] + self.data[1]
+            if not self._is_valid_close_status(code):
+                raise WebSocketProtocolException("Invalid close opcode.")
+
+    @staticmethod
+    def _is_valid_close_status(code):
+        return code in VALID_CLOSE_STATUS or (3000 <= code < 5000)
+
+    def __str__(self):
+        return "fin=" + str(self.fin) \
+            + " opcode=" + str(self.opcode) \
+            + " data=" + str(self.data)
+
+    @staticmethod
+    def create_frame(data, opcode, fin=1):
+        """
+        Create frame to send text, binary and other data.
+
+        Parameters
+        ----------
+        data: <type>
+            data to send. This is string value(byte array).
+            If opcode is OPCODE_TEXT and this value is unicode,
+            data value is converted into unicode string, automatically.
+        opcode: <type>
+            operation code. please see OPCODE_XXX.
+        fin: <type>
+            fin flag. if set to 0, create continue fragmentation.
+        """
+        if opcode == ABNF.OPCODE_TEXT and isinstance(data, str):
+            data = data.encode("utf-8")
+        # mask must be set if send data from client
+        return ABNF(fin, 0, 0, 0, opcode, 1, data)
+
+    def format(self):
+        """
+        Format this object to string(byte array) to send data to server.
+        """
+        if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]):
+            raise ValueError("not 0 or 1")
+        if self.opcode not in ABNF.OPCODES:
+            raise ValueError("Invalid OPCODE")
+        length = len(self.data)
+        if length >= ABNF.LENGTH_63:
+            raise ValueError("data is too long")
+
+        frame_header = chr(self.fin << 7 |
+                           self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 |
+                           self.opcode).encode('latin-1')
+        if length < ABNF.LENGTH_7:
+            frame_header += chr(self.mask << 7 | length).encode('latin-1')
+        elif length < ABNF.LENGTH_16:
+            frame_header += chr(self.mask << 7 | 0x7e).encode('latin-1')
+            frame_header += struct.pack("!H", length)
+        else:
+            frame_header += chr(self.mask << 7 | 0x7f).encode('latin-1')
+            frame_header += struct.pack("!Q", length)
+
+        if not self.mask:
+            return frame_header + self.data
+        else:
+            mask_key = self.get_mask_key(4)
+            return frame_header + self._get_masked(mask_key)
+
+    def _get_masked(self, mask_key):
+        s = ABNF.mask(mask_key, self.data)
+
+        if isinstance(mask_key, str):
+            mask_key = mask_key.encode('utf-8')
+
+        return mask_key + s
+
+    @staticmethod
+    def mask(mask_key, data):
+        """
+        Mask or unmask data. Just do xor for each byte
+
+        Parameters
+        ----------
+        mask_key: <type>
+            4 byte string.
+        data: <type>
+            data to mask/unmask.
+        """
+        if data is None:
+            data = ""
+
+        if isinstance(mask_key, str):
+            mask_key = mask_key.encode('latin-1')
+
+        if isinstance(data, str):
+            data = data.encode('latin-1')
+
+        return _mask(array.array("B", mask_key), array.array("B", data))
+
+
+class frame_buffer:
+    _HEADER_MASK_INDEX = 5
+    _HEADER_LENGTH_INDEX = 6
+
+    def __init__(self, recv_fn, skip_utf8_validation):
+        self.recv = recv_fn
+        self.skip_utf8_validation = skip_utf8_validation
+        # Buffers over the packets from the layer beneath until desired amount
+        # bytes of bytes are received.
+        self.recv_buffer = []
+        self.clear()
+        self.lock = Lock()
+
+    def clear(self):
+        self.header = None
+        self.length = None
+        self.mask = None
+
+    def has_received_header(self):
+        return self.header is None
+
+    def recv_header(self):
+        header = self.recv_strict(2)
+        b1 = header[0]
+        fin = b1 >> 7 & 1
+        rsv1 = b1 >> 6 & 1
+        rsv2 = b1 >> 5 & 1
+        rsv3 = b1 >> 4 & 1
+        opcode = b1 & 0xf
+        b2 = header[1]
+        has_mask = b2 >> 7 & 1
+        length_bits = b2 & 0x7f
+
+        self.header = (fin, rsv1, rsv2, rsv3, opcode, has_mask, length_bits)
+
+    def has_mask(self):
+        if not self.header:
+            return False
+        return self.header[frame_buffer._HEADER_MASK_INDEX]
+
+    def has_received_length(self):
+        return self.length is None
+
+    def recv_length(self):
+        bits = self.header[frame_buffer._HEADER_LENGTH_INDEX]
+        length_bits = bits & 0x7f
+        if length_bits == 0x7e:
+            v = self.recv_strict(2)
+            self.length = struct.unpack("!H", v)[0]
+        elif length_bits == 0x7f:
+            v = self.recv_strict(8)
+            self.length = struct.unpack("!Q", v)[0]
+        else:
+            self.length = length_bits
+
+    def has_received_mask(self):
+        return self.mask is None
+
+    def recv_mask(self):
+        self.mask = self.recv_strict(4) if self.has_mask() else ""
+
+    def recv_frame(self):
+
+        with self.lock:
+            # Header
+            if self.has_received_header():
+                self.recv_header()
+            (fin, rsv1, rsv2, rsv3, opcode, has_mask, _) = self.header
+
+            # Frame length
+            if self.has_received_length():
+                self.recv_length()
+            length = self.length
+
+            # Mask
+            if self.has_received_mask():
+                self.recv_mask()
+            mask = self.mask
+
+            # Payload
+            payload = self.recv_strict(length)
+            if has_mask:
+                payload = ABNF.mask(mask, payload)
+
+            # Reset for next frame
+            self.clear()
+
+            frame = ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload)
+            frame.validate(self.skip_utf8_validation)
+
+        return frame
+
+    def recv_strict(self, bufsize):
+        shortage = bufsize - sum(map(len, self.recv_buffer))
+        while shortage > 0:
+            # Limit buffer size that we pass to socket.recv() to avoid
+            # fragmenting the heap -- the number of bytes recv() actually
+            # reads is limited by socket buffer and is relatively small,
+            # yet passing large numbers repeatedly causes lots of large
+            # buffers allocated and then shrunk, which results in
+            # fragmentation.
+            bytes_ = self.recv(min(16384, shortage))
+            self.recv_buffer.append(bytes_)
+            shortage -= len(bytes_)
+
+        unified = bytes("", 'utf-8').join(self.recv_buffer)
+
+        if shortage == 0:
+            self.recv_buffer = []
+            return unified
+        else:
+            self.recv_buffer = [unified[bufsize:]]
+            return unified[:bufsize]
+
+
+class continuous_frame:
+
+    def __init__(self, fire_cont_frame, skip_utf8_validation):
+        self.fire_cont_frame = fire_cont_frame
+        self.skip_utf8_validation = skip_utf8_validation
+        self.cont_data = None
+        self.recving_frames = None
+
+    def validate(self, frame):
+        if not self.recving_frames and frame.opcode == ABNF.OPCODE_CONT:
+            raise WebSocketProtocolException("Illegal frame")
+        if self.recving_frames and \
+                frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
+            raise WebSocketProtocolException("Illegal frame")
+
+    def add(self, frame):
+        if self.cont_data:
+            self.cont_data[1] += frame.data
+        else:
+            if frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
+                self.recving_frames = frame.opcode
+            self.cont_data = [frame.opcode, frame.data]
+
+        if frame.fin:
+            self.recving_frames = None
+
+    def is_fire(self, frame):
+        return frame.fin or self.fire_cont_frame
+
+    def extract(self, frame):
+        data = self.cont_data
+        self.cont_data = None
+        frame.data = data[1]
+        if not self.fire_cont_frame and data[0] == ABNF.OPCODE_TEXT and not self.skip_utf8_validation and not validate_utf8(frame.data):
+            raise WebSocketPayloadException(
+                "cannot decode: " + repr(frame.data))
+
+        return [data[0], frame]

+ 423 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_app.py

@@ -0,0 +1,423 @@
+"""
+
+"""
+
+"""
+_app.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import selectors
+import sys
+import threading
+import time
+import traceback
+from ._abnf import ABNF
+from ._core import WebSocket, getdefaulttimeout
+from ._exceptions import *
+from . import _logging
+
+
+__all__ = ["WebSocketApp"]
+
+
+class Dispatcher:
+    """
+    Dispatcher
+    """
+    def __init__(self, app, ping_timeout):
+        self.app = app
+        self.ping_timeout = ping_timeout
+
+    def read(self, sock, read_callback, check_callback):
+        while self.app.keep_running:
+            sel = selectors.DefaultSelector()
+            sel.register(self.app.sock.sock, selectors.EVENT_READ)
+
+            r = sel.select(self.ping_timeout)
+            if r:
+                if not read_callback():
+                    break
+            check_callback()
+            sel.close()
+
+
+class SSLDispatcher:
+    """
+    SSLDispatcher
+    """
+    def __init__(self, app, ping_timeout):
+        self.app = app
+        self.ping_timeout = ping_timeout
+
+    def read(self, sock, read_callback, check_callback):
+        while self.app.keep_running:
+            r = self.select()
+            if r:
+                if not read_callback():
+                    break
+            check_callback()
+
+    def select(self):
+        sock = self.app.sock.sock
+        if sock.pending():
+            return [sock,]
+
+        sel = selectors.DefaultSelector()
+        sel.register(sock, selectors.EVENT_READ)
+
+        r = sel.select(self.ping_timeout)
+        sel.close()
+
+        if len(r) > 0:
+            return r[0][0]
+
+
+class WebSocketApp:
+    """
+    Higher level of APIs are provided. The interface is like JavaScript WebSocket object.
+    """
+
+    def __init__(self, url, header=None,
+                 on_open=None, on_message=None, on_error=None,
+                 on_close=None, on_ping=None, on_pong=None,
+                 on_cont_message=None,
+                 keep_running=True, get_mask_key=None, cookie=None,
+                 subprotocols=None,
+                 on_data=None, callback_args=[]):
+        """
+        WebSocketApp initialization
+
+        Parameters
+        ----------
+        url: str
+            Websocket url.
+        header: list or dict
+            Custom header for websocket handshake.
+        on_open: function
+            Callback object which is called at opening websocket.
+            on_open has one argument.
+            The 1st argument is this class object.
+        on_message: function
+            Callback object which is called when received data.
+            on_message has 2 arguments.
+            The 1st argument is this class object.
+            The 2nd argument is utf-8 data received from the server.
+        on_error: function
+            Callback object which is called when we get error.
+            on_error has 2 arguments.
+            The 1st argument is this class object.
+            The 2nd argument is exception object.
+        on_close: function
+            Callback object which is called when connection is closed.
+            on_close has 3 arguments.
+            The 1st argument is this class object.
+            The 2nd argument is close_status_code.
+            The 3rd argument is close_msg.
+        on_cont_message: function
+            Callback object which is called when a continuation
+            frame is received.
+            on_cont_message has 3 arguments.
+            The 1st argument is this class object.
+            The 2nd argument is utf-8 string which we get from the server.
+            The 3rd argument is continue flag. if 0, the data continue
+            to next frame data
+        on_data: function
+            Callback object which is called when a message received.
+            This is called before on_message or on_cont_message,
+            and then on_message or on_cont_message is called.
+            on_data has 4 argument.
+            The 1st argument is this class object.
+            The 2nd argument is utf-8 string which we get from the server.
+            The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came.
+            The 4th argument is continue flag. If 0, the data continue
+        keep_running: bool
+            This parameter is obsolete and ignored.
+        get_mask_key: function
+            A callable function to get new mask keys, see the
+            WebSocket.set_mask_key's docstring for more information.
+        cookie: str
+            Cookie value.
+        subprotocols: list
+            List of available sub protocols. Default is None.
+        """
+        self.url = url
+        self.header = header if header is not None else []
+        self.cookie = cookie
+
+        self.on_open = on_open
+        self.on_message = on_message
+        self.on_data = on_data
+        self.on_error = on_error
+        self.on_close = on_close
+        self.on_ping = on_ping
+        self.on_pong = on_pong
+        self.on_cont_message = on_cont_message
+        self.keep_running = False
+        self.get_mask_key = get_mask_key
+        self.sock = None
+        self.last_ping_tm = 0
+        self.last_pong_tm = 0
+        self.subprotocols = subprotocols
+        self.callback_args = callback_args
+
+    def update_args(self, *args):
+        self.callback_args = args
+        #print(self.callback_args)
+
+    def send(self, data, opcode=ABNF.OPCODE_TEXT):
+        """
+        send message
+
+        Parameters
+        ----------
+        data: str
+            Message to send. If you set opcode to OPCODE_TEXT,
+            data must be utf-8 string or unicode.
+        opcode: int
+            Operation code of data. Default is OPCODE_TEXT.
+        """
+
+        if not self.sock or self.sock.send(data, opcode) == 0:
+            raise WebSocketConnectionClosedException(
+                "Connection is already closed.")
+
+    def close(self, **kwargs):
+        """
+        Close websocket connection.
+        """
+        self.keep_running = False
+        if self.sock:
+            self.sock.close(**kwargs)
+            self.sock = None
+
+    def _send_ping(self, interval, event, payload):
+        while not event.wait(interval):
+            self.last_ping_tm = time.time()
+            if self.sock:
+                try:
+                    self.sock.ping(payload)
+                except Exception as ex:
+                    _logging.warning("send_ping routine terminated: {}".format(ex))
+                    break
+
+    def run_forever(self, sockopt=None, sslopt=None,
+                    ping_interval=0, ping_timeout=None,
+                    ping_payload="",
+                    http_proxy_host=None, http_proxy_port=None,
+                    http_no_proxy=None, http_proxy_auth=None,
+                    skip_utf8_validation=False,
+                    host=None, origin=None, dispatcher=None,
+                    suppress_origin=False, proxy_type=None):
+        """
+        Run event loop for WebSocket framework.
+
+        This loop is an infinite loop and is alive while websocket is available.
+
+        Parameters
+        ----------
+        sockopt: tuple
+            Values for socket.setsockopt.
+            sockopt must be tuple
+            and each element is argument of sock.setsockopt.
+        sslopt: dict
+            Optional dict object for ssl socket option.
+        ping_interval: int or float
+            Automatically send "ping" command
+            every specified period (in seconds).
+            If set to 0, no ping is sent periodically.
+        ping_timeout: int or float
+            Timeout (in seconds) if the pong message is not received.
+        ping_payload: str
+            Payload message to send with each ping.
+        http_proxy_host: str
+            HTTP proxy host name.
+        http_proxy_port: int or str
+            HTTP proxy port. If not set, set to 80.
+        http_no_proxy: list
+            Whitelisted host names that don't use the proxy.
+        skip_utf8_validation: bool
+            skip utf8 validation.
+        host: str
+            update host header.
+        origin: str
+            update origin header.
+        dispatcher: Dispatcher object
+            customize reading data from socket.
+        suppress_origin: bool
+            suppress outputting origin header.
+
+        Returns
+        -------
+        teardown: bool
+            False if caught KeyboardInterrupt, True if other exception was raised during a loop
+        """
+
+        if ping_timeout is not None and ping_timeout <= 0:
+            raise WebSocketException("Ensure ping_timeout > 0")
+        if ping_interval is not None and ping_interval < 0:
+            raise WebSocketException("Ensure ping_interval >= 0")
+        if ping_timeout and ping_interval and ping_interval <= ping_timeout:
+            raise WebSocketException("Ensure ping_interval > ping_timeout")
+        if not sockopt:
+            sockopt = []
+        if not sslopt:
+            sslopt = {}
+        if self.sock:
+            raise WebSocketException("socket is already opened")
+        thread = None
+        self.keep_running = True
+        self.last_ping_tm = 0
+        self.last_pong_tm = 0
+
+        def teardown(close_frame=None):
+            """
+            Tears down the connection.
+
+            Parameters
+            ----------
+            close_frame: ABNF frame
+                If close_frame is set, the on_close handler is invoked
+                with the statusCode and reason from the provided frame.
+            """
+
+            if thread and thread.is_alive():
+                event.set()
+                thread.join()
+            self.keep_running = False
+            if self.sock:
+                self.sock.close()
+            close_status_code, close_reason = self._get_close_args(
+                close_frame if close_frame else None)
+            self.sock = None
+
+            # Finally call the callback AFTER all teardown is complete
+            self._callback(self.on_close, close_status_code, close_reason,
+                    self.callback_args)
+
+        try:
+            self.sock = WebSocket(
+                self.get_mask_key, sockopt=sockopt, sslopt=sslopt,
+                fire_cont_frame=self.on_cont_message is not None,
+                skip_utf8_validation=skip_utf8_validation,
+                enable_multithread=True)
+            self.sock.settimeout(getdefaulttimeout())
+            self.sock.connect(
+                self.url, header=self.header, cookie=self.cookie,
+                http_proxy_host=http_proxy_host,
+                http_proxy_port=http_proxy_port, http_no_proxy=http_no_proxy,
+                http_proxy_auth=http_proxy_auth, subprotocols=self.subprotocols,
+                host=host, origin=origin, suppress_origin=suppress_origin,
+                proxy_type=proxy_type)
+            if not dispatcher:
+                dispatcher = self.create_dispatcher(ping_timeout)
+
+            self._callback(self.on_open, self.callback_args)
+
+            if ping_interval:
+                event = threading.Event()
+                thread = threading.Thread(
+                    target=self._send_ping, args=(ping_interval, event, ping_payload))
+                thread.daemon = True
+                thread.start()
+
+            def read():
+                if not self.keep_running:
+                    return teardown()
+
+                op_code, frame = self.sock.recv_data_frame(True)
+                if op_code == ABNF.OPCODE_CLOSE:
+                    return teardown(frame)
+                elif op_code == ABNF.OPCODE_PING:
+                    self._callback(self.on_ping, frame.data, self.callback_args)
+                elif op_code == ABNF.OPCODE_PONG:
+                    self.last_pong_tm = time.time()
+                    self._callback(self.on_pong, frame.data, self.callback_args)
+                elif op_code == ABNF.OPCODE_CONT and self.on_cont_message:
+                    self._callback(self.on_data, frame.data,
+                                   frame.opcode, frame.fin, self.callback_args)
+                    self._callback(self.on_cont_message,
+                                   frame.data, frame.fin, self.callback_args)
+                else:
+                    data = frame.data
+                    if op_code == ABNF.OPCODE_TEXT:
+                        data = data.decode("utf-8")
+                        self._callback(self.on_message, data, self.callback_args)
+                    else:
+                        self._callback(self.on_data, data, frame.opcode, True,
+                            self.callback_args)
+
+                return True
+
+            def check():
+                if (ping_timeout):
+                    has_timeout_expired = time.time() - self.last_ping_tm > ping_timeout
+                    has_pong_not_arrived_after_last_ping = self.last_pong_tm - self.last_ping_tm < 0
+                    has_pong_arrived_too_late = self.last_pong_tm - self.last_ping_tm > ping_timeout
+
+                    if (self.last_ping_tm and
+                            has_timeout_expired and
+                            (has_pong_not_arrived_after_last_ping or has_pong_arrived_too_late)):
+                        raise WebSocketTimeoutException("ping/pong timed out")
+                return True
+
+            dispatcher.read(self.sock.sock, read, check)
+        except (Exception, KeyboardInterrupt, SystemExit) as e:
+            self._callback(self.on_error, e, self.callback_args)
+            if isinstance(e, SystemExit):
+                # propagate SystemExit further
+                raise
+            teardown()
+            return not isinstance(e, KeyboardInterrupt)
+        else:
+            teardown()
+            return True
+
+    def create_dispatcher(self, ping_timeout):
+        timeout = ping_timeout or 10
+        if self.sock.is_ssl():
+            return SSLDispatcher(self, timeout)
+
+        return Dispatcher(self, timeout)
+
+    def _get_close_args(self, close_frame):
+        """
+        _get_close_args extracts the close code and reason from the close body
+        if it exists (RFC6455 says WebSocket Connection Close Code is optional)
+        """
+        # Need to catch the case where close_frame is None
+        # Otherwise the following if statement causes an error
+        if not self.on_close or not close_frame:
+            return [None, None]
+
+        # Extract close frame status code
+        if close_frame.data and len(close_frame.data) >= 2:
+            close_status_code = 256 * close_frame.data[0] + close_frame.data[1]
+            reason = close_frame.data[2:].decode('utf-8')
+            return [close_status_code, reason]
+        else:
+            # Most likely reached this because len(close_frame_data.data) < 2
+            return [None, None]
+
+    def _callback(self, callback, *args):
+        if callback:
+            try:
+                callback(self, *args)
+
+            except Exception as e:
+                _logging.error("error from callback {}: {}".format(callback, e))
+                if self.on_error:
+                    self.on_error(self, e)

+ 67 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_cookiejar.py

@@ -0,0 +1,67 @@
+"""
+
+"""
+
+"""
+_cookiejar.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import http.cookies
+
+
+class SimpleCookieJar:
+    def __init__(self):
+        self.jar = dict()
+
+    def add(self, set_cookie):
+        if set_cookie:
+            simpleCookie = http.cookies.SimpleCookie(set_cookie)
+
+            for k, v in simpleCookie.items():
+                domain = v.get("domain")
+                if domain:
+                    if not domain.startswith("."):
+                        domain = "." + domain
+                    cookie = self.jar.get(domain) if self.jar.get(domain) else http.cookies.SimpleCookie()
+                    cookie.update(simpleCookie)
+                    self.jar[domain.lower()] = cookie
+
+    def set(self, set_cookie):
+        if set_cookie:
+            simpleCookie = http.cookies.SimpleCookie(set_cookie)
+
+            for k, v in simpleCookie.items():
+                domain = v.get("domain")
+                if domain:
+                    if not domain.startswith("."):
+                        domain = "." + domain
+                    self.jar[domain.lower()] = simpleCookie
+
+    def get(self, host):
+        if not host:
+            return ""
+
+        cookies = []
+        for domain, simpleCookie in self.jar.items():
+            host = host.lower()
+            if host.endswith(domain) or host == domain[1:]:
+                cookies.append(self.jar.get(domain))
+
+        return "; ".join(filter(
+            None, sorted(
+                ["%s=%s" % (k, v.value) for cookie in filter(None, cookies) for k, v in cookie.items()]
+            )))

+ 607 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_core.py

@@ -0,0 +1,607 @@
+"""
+_core.py
+====================================
+WebSocket Python client
+"""
+
+"""
+_core.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import socket
+import struct
+import threading
+import time
+
+# websocket modules
+from ._abnf import *
+from ._exceptions import *
+from ._handshake import *
+from ._http import *
+from ._logging import *
+from ._socket import *
+from ._ssl_compat import *
+from ._utils import *
+
+__all__ = ['WebSocket', 'create_connection']
+
+
+class WebSocket:
+    """
+    Low level WebSocket interface.
+
+    This class is based on the WebSocket protocol `draft-hixie-thewebsocketprotocol-76 <http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76>`_
+
+    We can connect to the websocket server and send/receive data.
+    The following example is an echo client.
+
+    >>> import websocket
+    >>> ws = websocket.WebSocket()
+    >>> ws.connect("ws://echo.websocket.org")
+    >>> ws.send("Hello, Server")
+    >>> ws.recv()
+    'Hello, Server'
+    >>> ws.close()
+
+    Parameters
+    ----------
+    get_mask_key: func
+        A callable function to get new mask keys, see the
+        WebSocket.set_mask_key's docstring for more information.
+    sockopt: tuple
+        Values for socket.setsockopt.
+        sockopt must be tuple and each element is argument of sock.setsockopt.
+    sslopt: dict
+        Optional dict object for ssl socket options. See FAQ for details.
+    fire_cont_frame: bool
+        Fire recv event for each cont frame. Default is False.
+    enable_multithread: bool
+        If set to True, lock send method.
+    skip_utf8_validation: bool
+        Skip utf8 validation.
+    """
+
+    def __init__(self, get_mask_key=None, sockopt=None, sslopt=None,
+                 fire_cont_frame=False, enable_multithread=True,
+                 skip_utf8_validation=False, **_):
+        """
+        Initialize WebSocket object.
+
+        Parameters
+        ----------
+        sslopt: dict
+            Optional dict object for ssl socket options. See FAQ for details.
+        """
+        self.sock_opt = sock_opt(sockopt, sslopt)
+        self.handshake_response = None
+        self.sock = None
+
+        self.connected = False
+        self.get_mask_key = get_mask_key
+        # These buffer over the build-up of a single frame.
+        self.frame_buffer = frame_buffer(self._recv, skip_utf8_validation)
+        self.cont_frame = continuous_frame(
+            fire_cont_frame, skip_utf8_validation)
+
+        if enable_multithread:
+            self.lock = threading.Lock()
+            self.readlock = threading.Lock()
+        else:
+            self.lock = NoLock()
+            self.readlock = NoLock()
+
+    def __iter__(self):
+        """
+        Allow iteration over websocket, implying sequential `recv` executions.
+        """
+        while True:
+            yield self.recv()
+
+    def __next__(self):
+        return self.recv()
+
+    def next(self):
+        return self.__next__()
+
+    def fileno(self):
+        return self.sock.fileno()
+
+    def set_mask_key(self, func):
+        """
+        Set function to create mask key. You can customize mask key generator.
+        Mainly, this is for testing purpose.
+
+        Parameters
+        ----------
+        func: func
+            callable object. the func takes 1 argument as integer.
+            The argument means length of mask key.
+            This func must return string(byte array),
+            which length is argument specified.
+        """
+        self.get_mask_key = func
+
+    def gettimeout(self):
+        """
+        Get the websocket timeout (in seconds) as an int or float
+
+        Returns
+        ----------
+        timeout: int or float
+             returns timeout value (in seconds). This value could be either float/integer.
+        """
+        return self.sock_opt.timeout
+
+    def settimeout(self, timeout):
+        """
+        Set the timeout to the websocket.
+
+        Parameters
+        ----------
+        timeout: int or float
+            timeout time (in seconds). This value could be either float/integer.
+        """
+        self.sock_opt.timeout = timeout
+        if self.sock:
+            self.sock.settimeout(timeout)
+
+    timeout = property(gettimeout, settimeout)
+
+    def getsubprotocol(self):
+        """
+        Get subprotocol
+        """
+        if self.handshake_response:
+            return self.handshake_response.subprotocol
+        else:
+            return None
+
+    subprotocol = property(getsubprotocol)
+
+    def getstatus(self):
+        """
+        Get handshake status
+        """
+        if self.handshake_response:
+            return self.handshake_response.status
+        else:
+            return None
+
+    status = property(getstatus)
+
+    def getheaders(self):
+        """
+        Get handshake response header
+        """
+        if self.handshake_response:
+            return self.handshake_response.headers
+        else:
+            return None
+
+    def is_ssl(self):
+        try:
+            return isinstance(self.sock, ssl.SSLSocket)
+        except:
+            return False
+
+    headers = property(getheaders)
+
+    def connect(self, url, **options):
+        """
+        Connect to url. url is websocket url scheme.
+        ie. ws://host:port/resource
+        You can customize using 'options'.
+        If you set "header" list object, you can set your own custom header.
+
+        >>> ws = WebSocket()
+        >>> ws.connect("ws://echo.websocket.org/",
+                ...     header=["User-Agent: MyProgram",
+                ...             "x-custom: header"])
+
+        Parameters
+        ----------
+        header: list or dict
+            Custom http header list or dict.
+        cookie: str
+            Cookie value.
+        origin: str
+            Custom origin url.
+        connection: str
+            Custom connection header value.
+            Default value "Upgrade" set in _handshake.py
+        suppress_origin: bool
+            Suppress outputting origin header.
+        host: str
+            Custom host header string.
+        timeout: int or float
+            Socket timeout time. This value is an integer or float.
+            If you set None for this value, it means "use default_timeout value"
+        http_proxy_host: str
+            HTTP proxy host name.
+        http_proxy_port: str or int
+            HTTP proxy port. Default is 80.
+        http_no_proxy: list
+            Whitelisted host names that don't use the proxy.
+        http_proxy_auth: tuple
+            HTTP proxy auth information. Tuple of username and password. Default is None.
+        redirect_limit: int
+            Number of redirects to follow.
+        subprotocols: list
+            List of available subprotocols. Default is None.
+        socket: socket
+            Pre-initialized stream socket.
+        """
+        self.sock_opt.timeout = options.get('timeout', self.sock_opt.timeout)
+        self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
+                                   options.pop('socket', None))
+
+        try:
+            self.handshake_response = handshake(self.sock, *addrs, **options)
+            for attempt in range(options.pop('redirect_limit', 3)):
+                if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES:
+                    url = self.handshake_response.headers['location']
+                    self.sock.close()
+                    self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
+                                               options.pop('socket', None))
+                    self.handshake_response = handshake(self.sock, *addrs, **options)
+            self.connected = True
+        except:
+            if self.sock:
+                self.sock.close()
+                self.sock = None
+            raise
+
+    def send(self, payload, opcode=ABNF.OPCODE_TEXT):
+        """
+        Send the data as string.
+
+        Parameters
+        ----------
+        payload: str
+            Payload must be utf-8 string or unicode,
+            If the opcode is OPCODE_TEXT.
+            Otherwise, it must be string(byte array).
+        opcode: int
+            Operation code (opcode) to send.
+        """
+        
+        frame = ABNF.create_frame(payload, opcode)
+        return self.send_frame(frame)
+
+    def send_frame(self, frame):
+        """
+        Send the data frame.
+
+        >>> ws = create_connection("ws://echo.websocket.org/")
+        >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT)
+        >>> ws.send_frame(frame)
+        >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0)
+        >>> ws.send_frame(frame)
+        >>> cont_frame = ABNF.create_frame("Foo Bar", ABNF.OPCODE_CONT, 1)
+        >>> ws.send_frame(frame)
+
+        Parameters
+        ----------
+        frame: ABNF frame
+            frame data created by ABNF.create_frame
+        """
+        if self.get_mask_key:
+            frame.get_mask_key = self.get_mask_key
+        data = frame.format()
+        length = len(data)
+        #if (isEnabledForTrace() and f):
+            #trace("++Sent raw: " + repr(data))
+            #trace("++Sent decoded: " + frame.__str__())
+        with self.lock:
+            while data:
+                l = self._send(data)
+                data = data[l:]
+
+        return length
+
+    def send_binary(self, payload):
+        """
+        Send a binary message (OPCODE_BINARY).
+
+        Parameters
+        ----------
+        payload: bytes
+            payload of message to send.
+        """
+        return self.send(payload, ABNF.OPCODE_BINARY)
+
+    def ping(self, payload=""):
+        """
+        Send ping data.
+
+        Parameters
+        ----------
+        payload: str
+            data payload to send server.
+        """
+        if isinstance(payload, str):
+            payload = payload.encode("utf-8")
+        self.send(payload, ABNF.OPCODE_PING)
+
+    def pong(self, payload=""):
+        """
+        Send pong data.
+
+        Parameters
+        ----------
+        payload: str
+            data payload to send server.
+        """
+        if isinstance(payload, str):
+            payload = payload.encode("utf-8")
+        self.send(payload, ABNF.OPCODE_PONG)
+
+    def recv(self):
+        """
+        Receive string data(byte array) from the server.
+
+        Returns
+        ----------
+        data: string (byte array) value.
+        """
+        with self.readlock:
+            opcode, data = self.recv_data()
+        if opcode == ABNF.OPCODE_TEXT:
+            return data.decode("utf-8")
+        elif opcode == ABNF.OPCODE_TEXT or opcode == ABNF.OPCODE_BINARY:
+            return data
+        else:
+            return ''
+
+    def recv_data(self, control_frame=False):
+        """
+        Receive data with operation code.
+
+        Parameters
+        ----------
+        control_frame: bool
+            a boolean flag indicating whether to return control frame
+            data, defaults to False
+
+        Returns
+        -------
+        opcode, frame.data: tuple
+            tuple of operation code and string(byte array) value.
+        """
+        opcode, frame = self.recv_data_frame(control_frame)
+        return opcode, frame.data
+
+    def recv_data_frame(self, control_frame=False):
+        """
+        Receive data with operation code.
+
+        If a valid ping message is received, a pong response is sent.
+
+        Parameters
+        ----------
+        control_frame: bool
+            a boolean flag indicating whether to return control frame
+            data, defaults to False
+
+        Returns
+        -------
+        frame.opcode, frame: tuple
+            tuple of operation code and string(byte array) value.
+        """
+        while True:
+            frame = self.recv_frame()
+            #if (isEnabledForTrace()):
+                #trace("++Rcv raw: " + repr(frame.format()))
+                #trace("++Rcv decoded: " + frame.__str__())
+            if not frame:
+                # handle error:
+                # 'NoneType' object has no attribute 'opcode'
+                raise WebSocketProtocolException(
+                    "Not a valid frame %s" % frame)
+            elif frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY, ABNF.OPCODE_CONT):
+                self.cont_frame.validate(frame)
+                self.cont_frame.add(frame)
+
+                if self.cont_frame.is_fire(frame):
+                    return self.cont_frame.extract(frame)
+
+            elif frame.opcode == ABNF.OPCODE_CLOSE:
+                self.send_close()
+                return frame.opcode, frame
+            elif frame.opcode == ABNF.OPCODE_PING:
+                if len(frame.data) < 126:
+                    self.pong(frame.data)
+                else:
+                    raise WebSocketProtocolException(
+                        "Ping message is too long")
+                if control_frame:
+                    return frame.opcode, frame
+            elif frame.opcode == ABNF.OPCODE_PONG:
+                if control_frame:
+                    return frame.opcode, frame
+
+    def recv_frame(self):
+        """
+        Receive data as frame from server.
+
+        Returns
+        -------
+        self.frame_buffer.recv_frame(): ABNF frame object
+        """
+        return self.frame_buffer.recv_frame()
+
+    def send_close(self, status=STATUS_NORMAL, reason=bytes('', encoding='utf-8')):
+        """
+        Send close data to the server.
+
+        Parameters
+        ----------
+        status: int
+            Status code to send. See STATUS_XXX.
+        reason: str or bytes
+            The reason to close. This must be string or bytes.
+        """
+        if status < 0 or status >= ABNF.LENGTH_16:
+            raise ValueError("code is invalid range")
+        self.connected = False
+        self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
+
+    def close(self, status=STATUS_NORMAL, reason=bytes('', encoding='utf-8'), timeout=3):
+        """
+        Close Websocket object
+
+        Parameters
+        ----------
+        status: int
+            Status code to send. See STATUS_XXX.
+        reason: bytes
+            The reason to close.
+        timeout: int or float
+            Timeout until receive a close frame.
+            If None, it will wait forever until receive a close frame.
+        """
+        if self.connected:
+            if status < 0 or status >= ABNF.LENGTH_16:
+                raise ValueError("code is invalid range")
+
+            try:
+                self.connected = False
+                self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
+                sock_timeout = self.sock.gettimeout()
+                self.sock.settimeout(timeout)
+                start_time = time.time()
+                while timeout is None or time.time() - start_time < timeout:
+                    try:
+                        frame = self.recv_frame()
+                        if frame.opcode != ABNF.OPCODE_CLOSE:
+                            continue
+                        if isEnabledForError():
+                            recv_status = struct.unpack("!H", frame.data[0:2])[0]
+                            if recv_status >= 3000 and recv_status <= 4999:
+                                debug("close status: " + repr(recv_status))
+                            elif recv_status != STATUS_NORMAL:
+                                error("close status: " + repr(recv_status))
+                        break
+                    except:
+                        break
+                self.sock.settimeout(sock_timeout)
+                self.sock.shutdown(socket.SHUT_RDWR)
+            except:
+                pass
+
+            self.shutdown()
+
+    def abort(self):
+        """
+        Low-level asynchronous abort, wakes up other threads that are waiting in recv_*
+        """
+        if self.connected:
+            self.sock.shutdown(socket.SHUT_RDWR)
+
+    def shutdown(self):
+        """
+        close socket, immediately.
+        """
+        if self.sock:
+            self.sock.close()
+            self.sock = None
+            self.connected = False
+
+    def _send(self, data):
+        return send(self.sock, data)
+
+    def _recv(self, bufsize):
+        try:
+            return recv(self.sock, bufsize)
+        except WebSocketConnectionClosedException:
+            if self.sock:
+                self.sock.close()
+            self.sock = None
+            self.connected = False
+            raise
+
+
+def create_connection(url, timeout=None, class_=WebSocket, **options):
+    """
+    Connect to url and return websocket object.
+
+    Connect to url and return the WebSocket object.
+    Passing optional timeout parameter will set the timeout on the socket.
+    If no timeout is supplied,
+    the global default timeout setting returned by getdefaulttimeout() is used.
+    You can customize using 'options'.
+    If you set "header" list object, you can set your own custom header.
+
+    >>> conn = create_connection("ws://echo.websocket.org/",
+         ...     header=["User-Agent: MyProgram",
+         ...             "x-custom: header"])
+
+    Parameters
+    ----------
+    class_: class
+        class to instantiate when creating the connection. It has to implement
+        settimeout and connect. It's __init__ should be compatible with
+        WebSocket.__init__, i.e. accept all of it's kwargs.
+    header: list or dict
+        custom http header list or dict.
+    cookie: str
+        Cookie value.
+    origin: str
+        custom origin url.
+    suppress_origin: bool
+        suppress outputting origin header.
+    host: str
+        custom host header string.
+    timeout: int or float
+        socket timeout time. This value could be either float/integer.
+        If set to None, it uses the default_timeout value.
+    http_proxy_host: str
+        HTTP proxy host name.
+    http_proxy_port: str or int
+        HTTP proxy port. If not set, set to 80.
+    http_no_proxy: list
+        Whitelisted host names that don't use the proxy.
+    http_proxy_auth: tuple
+        HTTP proxy auth information. tuple of username and password. Default is None.
+    enable_multithread: bool
+        Enable lock for multithread.
+    redirect_limit: int
+        Number of redirects to follow.
+    sockopt: tuple
+        Values for socket.setsockopt.
+        sockopt must be a tuple and each element is an argument of sock.setsockopt.
+    sslopt: dict
+        Optional dict object for ssl socket options. See FAQ for details.
+    subprotocols: list
+        List of available subprotocols. Default is None.
+    skip_utf8_validation: bool
+        Skip utf8 validation.
+    socket: socket
+        Pre-initialized stream socket.
+    """
+    sockopt = options.pop("sockopt", [])
+    sslopt = options.pop("sslopt", {})
+    fire_cont_frame = options.pop("fire_cont_frame", False)
+    enable_multithread = options.pop("enable_multithread", True)
+    skip_utf8_validation = options.pop("skip_utf8_validation", False)
+    websock = class_(sockopt=sockopt, sslopt=sslopt,
+                     fire_cont_frame=fire_cont_frame,
+                     enable_multithread=enable_multithread,
+                     skip_utf8_validation=skip_utf8_validation, **options)
+    websock.settimeout(timeout if timeout is not None else getdefaulttimeout())
+    websock.connect(url, **options)
+    return websock

+ 84 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_exceptions.py

@@ -0,0 +1,84 @@
+"""
+Define WebSocket exceptions
+"""
+
+"""
+_exceptions.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+
+class WebSocketException(Exception):
+    """
+    WebSocket exception class.
+    """
+    pass
+
+
+class WebSocketProtocolException(WebSocketException):
+    """
+    If the WebSocket protocol is invalid, this exception will be raised.
+    """
+    pass
+
+
+class WebSocketPayloadException(WebSocketException):
+    """
+    If the WebSocket payload is invalid, this exception will be raised.
+    """
+    pass
+
+
+class WebSocketConnectionClosedException(WebSocketException):
+    """
+    If remote host closed the connection or some network error happened,
+    this exception will be raised.
+    """
+    pass
+
+
+class WebSocketTimeoutException(WebSocketException):
+    """
+    WebSocketTimeoutException will be raised at socket timeout during read/write data.
+    """
+    pass
+
+
+class WebSocketProxyException(WebSocketException):
+    """
+    WebSocketProxyException will be raised when proxy error occurred.
+    """
+    pass
+
+
+class WebSocketBadStatusException(WebSocketException):
+    """
+    WebSocketBadStatusException will be raised when we get bad handshake status code.
+    """
+
+    def __init__(self, message, status_code, status_message=None, resp_headers=None):
+        msg = message % (status_code, status_message)
+        super().__init__(msg)
+        self.status_code = status_code
+        self.resp_headers = resp_headers
+
+
+class WebSocketAddressException(WebSocketException):
+    """
+    If the websocket address info cannot be found, this exception will be raised.
+    """
+    pass

+ 191 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_handshake.py

@@ -0,0 +1,191 @@
+"""
+_handshake.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import hashlib
+import hmac
+import os
+from base64 import encodebytes as base64encode
+from http import client as HTTPStatus
+from ._cookiejar import SimpleCookieJar
+from ._exceptions import *
+from ._http import *
+from ._logging import *
+from ._socket import *
+
+__all__ = ["handshake_response", "handshake", "SUPPORTED_REDIRECT_STATUSES"]
+
+# websocket supported version.
+VERSION = 13
+
+SUPPORTED_REDIRECT_STATUSES = (HTTPStatus.MOVED_PERMANENTLY, HTTPStatus.FOUND, HTTPStatus.SEE_OTHER,)
+SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS,)
+
+CookieJar = SimpleCookieJar()
+
+
+class handshake_response:
+
+    def __init__(self, status, headers, subprotocol):
+        self.status = status
+        self.headers = headers
+        self.subprotocol = subprotocol
+        CookieJar.add(headers.get("set-cookie"))
+
+
+def handshake(sock, hostname, port, resource, **options):
+    headers, key = _get_handshake_headers(resource, hostname, port, options)
+
+    header_str = "\r\n".join(headers)
+    send(sock, header_str)
+    dump("request header", header_str)
+    #print("request header:", header_str)
+
+    status, resp = _get_resp_headers(sock)
+    if status in SUPPORTED_REDIRECT_STATUSES:
+        return handshake_response(status, resp, None)
+    success, subproto = _validate(resp, key, options.get("subprotocols"))
+    if not success:
+        raise WebSocketException("Invalid WebSocket Header")
+
+    return handshake_response(status, resp, subproto)
+
+
+def _pack_hostname(hostname):
+    # IPv6 address
+    if ':' in hostname:
+        return '[' + hostname + ']'
+
+    return hostname
+
+
+def _get_handshake_headers(resource, host, port, options):
+    headers = [
+        "GET %s HTTP/1.1" % resource,
+        "Upgrade: websocket"
+    ]
+    if port == 80 or port == 443:
+        hostport = _pack_hostname(host)
+    else:
+        hostport = "%s:%d" % (_pack_hostname(host), port)
+    if "host" in options and options["host"] is not None:
+        headers.append("Host: %s" % options["host"])
+    else:
+        headers.append("Host: %s" % hostport)
+
+    if "suppress_origin" not in options or not options["suppress_origin"]:
+        if "origin" in options and options["origin"] is not None:
+            headers.append("Origin: %s" % options["origin"])
+        else:
+            headers.append("Origin: http://%s" % hostport)
+
+    key = _create_sec_websocket_key()
+
+    # Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified
+    if 'header' not in options or 'Sec-WebSocket-Key' not in options['header']:
+        key = _create_sec_websocket_key()
+        headers.append("Sec-WebSocket-Key: %s" % key)
+    else:
+        key = options['header']['Sec-WebSocket-Key']
+
+    if 'header' not in options or 'Sec-WebSocket-Version' not in options['header']:
+        headers.append("Sec-WebSocket-Version: %s" % VERSION)
+
+    if 'connection' not in options or options['connection'] is None:
+        headers.append('Connection: Upgrade')
+    else:
+        headers.append(options['connection'])
+
+    subprotocols = options.get("subprotocols")
+    if subprotocols:
+        headers.append("Sec-WebSocket-Protocol: %s" % ",".join(subprotocols))
+
+    if "header" in options:
+        header = options["header"]
+        if isinstance(header, dict):
+            header = [
+                ": ".join([k, v])
+                for k, v in header.items()
+                if v is not None
+            ]
+        headers.extend(header)
+
+    server_cookie = CookieJar.get(host)
+    client_cookie = options.get("cookie", None)
+
+    cookie = "; ".join(filter(None, [server_cookie, client_cookie]))
+
+    if cookie:
+        headers.append("Cookie: %s" % cookie)
+
+    headers.append("")
+    headers.append("")
+
+    return headers, key
+
+
+def _get_resp_headers(sock, success_statuses=SUCCESS_STATUSES):
+    status, resp_headers, status_message = read_headers(sock)
+    if status not in success_statuses:
+        raise WebSocketBadStatusException("Handshake status %d %s", status, status_message, resp_headers)
+    return status, resp_headers
+
+
+_HEADERS_TO_CHECK = {
+    "upgrade": "websocket",
+    "connection": "upgrade",
+}
+
+
+def _validate(headers, key, subprotocols):
+    subproto = None
+    for k, v in _HEADERS_TO_CHECK.items():
+        r = headers.get(k, None)
+        if not r:
+            return False, None
+        r = [x.strip().lower() for x in r.split(',')]
+        if v not in r:
+            return False, None
+
+    if subprotocols:
+        subproto = headers.get("sec-websocket-protocol", None)
+        if not subproto or subproto.lower() not in [s.lower() for s in subprotocols]:
+            error("Invalid subprotocol: " + str(subprotocols))
+            return False, None
+        subproto = subproto.lower()
+
+    result = headers.get("sec-websocket-accept", None)
+    if not result:
+        return False, None
+    result = result.lower()
+
+    if isinstance(result, str):
+        result = result.encode('utf-8')
+
+    value = (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode('utf-8')
+    hashed = base64encode(hashlib.sha1(value).digest()).strip().lower()
+    success = hmac.compare_digest(hashed, result)
+
+    if success:
+        return True, subproto
+    else:
+        return False, None
+
+
+def _create_sec_websocket_key():
+    randomness = os.urandom(16)
+    return base64encode(randomness).decode('utf-8').strip()

+ 335 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_http.py

@@ -0,0 +1,335 @@
+"""
+_http.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import errno
+import os
+import socket
+import sys
+
+from ._exceptions import *
+from ._logging import *
+from ._socket import*
+from ._ssl_compat import *
+from ._url import *
+
+from base64 import encodebytes as base64encode
+
+__all__ = ["proxy_info", "connect", "read_headers"]
+
+try:
+    from python_socks.sync import Proxy
+    from python_socks._errors import *
+    from python_socks._types import ProxyType
+    HAVE_PYTHON_SOCKS = True
+except:
+    HAVE_PYTHON_SOCKS = False
+
+    class ProxyError(Exception):
+        pass
+
+    class ProxyTimeoutError(Exception):
+        pass
+
+    class ProxyConnectionError(Exception):
+        pass
+
+
+class proxy_info:
+
+    def __init__(self, **options):
+        self.proxy_host = options.get("http_proxy_host", None)
+        if self.proxy_host:
+            self.proxy_port = options.get("http_proxy_port", 0)
+            self.auth = options.get("http_proxy_auth", None)
+            self.no_proxy = options.get("http_no_proxy", None)
+            self.proxy_protocol = options.get("proxy_type", "http")
+            # Note: If timeout not specified, default python-socks timeout is 60 seconds
+            self.proxy_timeout = options.get("timeout", None)
+            if self.proxy_protocol not in ['http', 'socks4', 'socks4a', 'socks5', 'socks5h']:
+                raise ProxyError("Only http, socks4, socks5 proxy protocols are supported")
+        else:
+            self.proxy_port = 0
+            self.auth = None
+            self.no_proxy = None
+            self.proxy_protocol = "http"
+
+
+def _start_proxied_socket(url, options, proxy):
+    if not HAVE_PYTHON_SOCKS:
+        raise WebSocketException("Python Socks is needed for SOCKS proxying but is not available")
+
+    hostname, port, resource, is_secure = parse_url(url)
+
+    if proxy.proxy_protocol == "socks5":
+        rdns = False
+        proxy_type = ProxyType.SOCKS5
+    if proxy.proxy_protocol == "socks4":
+        rdns = False
+        proxy_type = ProxyType.SOCKS4
+    # socks5h and socks4a send DNS through proxy
+    if proxy.proxy_protocol == "socks5h":
+        rdns = True
+        proxy_type = ProxyType.SOCKS5
+    if proxy.proxy_protocol == "socks4a":
+        rdns = True
+        proxy_type = ProxyType.SOCKS4
+
+    ws_proxy = Proxy.create(
+        proxy_type=proxy_type,
+        host=proxy.proxy_host,
+        port=int(proxy.proxy_port),
+        username=proxy.auth[0] if proxy.auth else None,
+        password=proxy.auth[1] if proxy.auth else None,
+        rdns=rdns)
+
+    sock = ws_proxy.connect(hostname, port, timeout=proxy.proxy_timeout)
+
+    if is_secure and HAVE_SSL:
+        sock = _ssl_socket(sock, options.sslopt, hostname)
+    elif is_secure:
+        raise WebSocketException("SSL not available.")
+
+    return sock, (hostname, port, resource)
+
+
+def connect(url, options, proxy, socket):
+    # Use _start_proxied_socket() only for socks4 or socks5 proxy
+    # Use _tunnel() for http proxy
+    # TODO: Use python-socks for http protocol also, to standardize flow
+    if proxy.proxy_host and not socket and not (proxy.proxy_protocol == "http"):
+        return _start_proxied_socket(url, options, proxy)
+
+    hostname, port, resource, is_secure = parse_url(url)
+
+    if socket:
+        return socket, (hostname, port, resource)
+
+    addrinfo_list, need_tunnel, auth = _get_addrinfo_list(
+        hostname, port, is_secure, proxy)
+    if not addrinfo_list:
+        raise WebSocketException(
+            "Host not found.: " + hostname + ":" + str(port))
+
+    sock = None
+    try:
+        sock = _open_socket(addrinfo_list, options.sockopt, options.timeout)
+        if need_tunnel:
+            sock = _tunnel(sock, hostname, port, auth)
+
+        if is_secure:
+            if HAVE_SSL:
+                sock = _ssl_socket(sock, options.sslopt, hostname)
+            else:
+                raise WebSocketException("SSL not available.")
+
+        return sock, (hostname, port, resource)
+    except:
+        if sock:
+            sock.close()
+        raise
+
+
+def _get_addrinfo_list(hostname, port, is_secure, proxy):
+    phost, pport, pauth = get_proxy_info(
+        hostname, is_secure, proxy.proxy_host, proxy.proxy_port, proxy.auth, proxy.no_proxy)
+    try:
+        # when running on windows 10, getaddrinfo without socktype returns a socktype 0.
+        # This generates an error exception: `_on_error: exception Socket type must be stream or datagram, not 0`
+        # or `OSError: [Errno 22] Invalid argument` when creating socket. Force the socket type to SOCK_STREAM.
+        if not phost:
+            addrinfo_list = socket.getaddrinfo(
+                hostname, port, 0, socket.SOCK_STREAM, socket.SOL_TCP)
+            return addrinfo_list, False, None
+        else:
+            pport = pport and pport or 80
+            # when running on windows 10, the getaddrinfo used above
+            # returns a socktype 0. This generates an error exception:
+            # _on_error: exception Socket type must be stream or datagram, not 0
+            # Force the socket type to SOCK_STREAM
+            addrinfo_list = socket.getaddrinfo(phost, pport, 0, socket.SOCK_STREAM, socket.SOL_TCP)
+            return addrinfo_list, True, pauth
+    except socket.gaierror as e:
+        raise WebSocketAddressException(e)
+
+
+def _open_socket(addrinfo_list, sockopt, timeout):
+    err = None
+    for addrinfo in addrinfo_list:
+        family, socktype, proto = addrinfo[:3]
+        sock = socket.socket(family, socktype, proto)
+        sock.settimeout(timeout)
+        for opts in DEFAULT_SOCKET_OPTION:
+            sock.setsockopt(*opts)
+        for opts in sockopt:
+            sock.setsockopt(*opts)
+
+        address = addrinfo[4]
+        err = None
+        while not err:
+            try:
+                sock.connect(address)
+            except socket.error as error:
+                error.remote_ip = str(address[0])
+                try:
+                    eConnRefused = (errno.ECONNREFUSED, errno.WSAECONNREFUSED)
+                except:
+                    eConnRefused = (errno.ECONNREFUSED, )
+                if error.errno == errno.EINTR:
+                    continue
+                elif error.errno in eConnRefused:
+                    err = error
+                    continue
+                else:
+                    if sock:
+                        sock.close()
+                    raise error
+            else:
+                break
+        else:
+            continue
+        break
+    else:
+        if err:
+            raise err
+
+    return sock
+
+
+def _wrap_sni_socket(sock, sslopt, hostname, check_hostname):
+    context = ssl.SSLContext(sslopt.get('ssl_version', ssl.PROTOCOL_TLS))
+
+    if sslopt.get('cert_reqs', ssl.CERT_NONE) != ssl.CERT_NONE:
+        cafile = sslopt.get('ca_certs', None)
+        capath = sslopt.get('ca_cert_path', None)
+        if cafile or capath:
+            context.load_verify_locations(cafile=cafile, capath=capath)
+        elif hasattr(context, 'load_default_certs'):
+            context.load_default_certs(ssl.Purpose.SERVER_AUTH)
+    if sslopt.get('certfile', None):
+        context.load_cert_chain(
+            sslopt['certfile'],
+            sslopt.get('keyfile', None),
+            sslopt.get('password', None),
+        )
+    # see
+    # https://github.com/liris/websocket-client/commit/b96a2e8fa765753e82eea531adb19716b52ca3ca#commitcomment-10803153
+    context.verify_mode = sslopt['cert_reqs']
+    if HAVE_CONTEXT_CHECK_HOSTNAME:
+        context.check_hostname = check_hostname
+    if 'ciphers' in sslopt:
+        context.set_ciphers(sslopt['ciphers'])
+    if 'cert_chain' in sslopt:
+        certfile, keyfile, password = sslopt['cert_chain']
+        context.load_cert_chain(certfile, keyfile, password)
+    if 'ecdh_curve' in sslopt:
+        context.set_ecdh_curve(sslopt['ecdh_curve'])
+
+    return context.wrap_socket(
+        sock,
+        do_handshake_on_connect=sslopt.get('do_handshake_on_connect', True),
+        suppress_ragged_eofs=sslopt.get('suppress_ragged_eofs', True),
+        server_hostname=hostname,
+    )
+
+
+def _ssl_socket(sock, user_sslopt, hostname):
+    sslopt = dict(cert_reqs=ssl.CERT_REQUIRED)
+    sslopt.update(user_sslopt)
+
+    certPath = os.environ.get('WEBSOCKET_CLIENT_CA_BUNDLE')
+    if certPath and os.path.isfile(certPath) \
+            and user_sslopt.get('ca_certs', None) is None:
+        sslopt['ca_certs'] = certPath
+    elif certPath and os.path.isdir(certPath) \
+            and user_sslopt.get('ca_cert_path', None) is None:
+        sslopt['ca_cert_path'] = certPath
+
+    if sslopt.get('server_hostname', None):
+        hostname = sslopt['server_hostname']
+
+    check_hostname = sslopt["cert_reqs"] != ssl.CERT_NONE and sslopt.pop(
+        'check_hostname', True)
+    sock = _wrap_sni_socket(sock, sslopt, hostname, check_hostname)
+
+    if not HAVE_CONTEXT_CHECK_HOSTNAME and check_hostname:
+        match_hostname(sock.getpeercert(), hostname)
+
+    return sock
+
+
+def _tunnel(sock, host, port, auth):
+    debug("Connecting proxy...")
+    connect_header = "CONNECT %s:%d HTTP/1.1\r\n" % (host, port)
+    connect_header += "Host: %s:%d\r\n" % (host, port)
+
+    # TODO: support digest auth.
+    if auth and auth[0]:
+        auth_str = auth[0]
+        if auth[1]:
+            auth_str += ":" + auth[1]
+        encoded_str = base64encode(auth_str.encode()).strip().decode().replace('\n', '')
+        connect_header += "Proxy-Authorization: Basic %s\r\n" % encoded_str
+    connect_header += "\r\n"
+    dump("request header", connect_header)
+
+    send(sock, connect_header)
+
+    try:
+        status, resp_headers, status_message = read_headers(sock)
+    except Exception as e:
+        raise WebSocketProxyException(str(e))
+
+    if status != 200:
+        raise WebSocketProxyException(
+            "failed CONNECT via proxy status: %r" % status)
+
+    return sock
+
+
+def read_headers(sock):
+    status = None
+    status_message = None
+    headers = {}
+    trace("--- response header ---")
+
+    while True:
+        line = recv_line(sock)
+        line = line.decode('utf-8').strip()
+        if not line:
+            break
+        trace(line)
+        if not status:
+
+            status_info = line.split(" ", 2)
+            status = int(status_info[1])
+            if len(status_info) > 2:
+                status_message = status_info[2]
+        else:
+            kv = line.split(":", 1)
+            if len(kv) == 2:
+                key, value = kv
+                if key.lower() == "set-cookie" and headers.get("set-cookie"):
+                    headers["set-cookie"] = headers.get("set-cookie") + "; " + value.strip()
+                else:
+                    headers[key.lower()] = value.strip()
+            else:
+                raise WebSocketException("Invalid header")
+
+    trace("-----------------------")
+
+    return status, headers, status_message

+ 90 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_logging.py

@@ -0,0 +1,90 @@
+"""
+
+"""
+
+"""
+_logging.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import logging
+
+_logger = logging.getLogger('websocket')
+try:
+    from logging import NullHandler
+except ImportError:
+    class NullHandler(logging.Handler):
+        def emit(self, record):
+            pass
+
+_logger.addHandler(NullHandler())
+
+_traceEnabled = False
+
+__all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace",
+           "isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"]
+
+
+def enableTrace(traceable, handler=logging.StreamHandler()):
+    """
+    Turn on/off the traceability.
+
+    Parameters
+    ----------
+    traceable: bool
+        If set to True, traceability is enabled.
+    """
+    global _traceEnabled
+    _traceEnabled = traceable
+    if traceable:
+        _logger.addHandler(handler)
+        _logger.setLevel(logging.ERROR)
+
+
+def dump(title, message):
+    if _traceEnabled:
+        _logger.debug("--- " + title + " ---")
+        _logger.debug(message)
+        _logger.debug("-----------------------")
+
+
+def error(msg):
+    _logger.error(msg)
+
+
+def warning(msg):
+    _logger.warning(msg)
+
+
+def debug(msg):
+    _logger.debug(msg)
+
+
+def trace(msg):
+    if _traceEnabled:
+        _logger.debug(msg)
+
+
+def isEnabledForError():
+    return _logger.isEnabledFor(logging.ERROR)
+
+
+def isEnabledForDebug():
+    return _logger.isEnabledFor(logging.DEBUG)
+
+
+def isEnabledForTrace():
+    return _traceEnabled

+ 182 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_socket.py

@@ -0,0 +1,182 @@
+"""
+
+"""
+
+"""
+_socket.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import errno
+import selectors
+import socket
+
+from ._exceptions import *
+from ._ssl_compat import *
+from ._utils import *
+
+DEFAULT_SOCKET_OPTION = [(socket.SOL_TCP, socket.TCP_NODELAY, 1)]
+#if hasattr(socket, "SO_KEEPALIVE"):
+#    DEFAULT_SOCKET_OPTION.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1))
+#if hasattr(socket, "TCP_KEEPIDLE"):
+#    DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPIDLE, 30))
+#if hasattr(socket, "TCP_KEEPINTVL"):
+#    DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPINTVL, 10))
+#if hasattr(socket, "TCP_KEEPCNT"):
+#    DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPCNT, 3))
+
+_default_timeout = None
+
+__all__ = ["DEFAULT_SOCKET_OPTION", "sock_opt", "setdefaulttimeout", "getdefaulttimeout",
+           "recv", "recv_line", "send"]
+
+
+class sock_opt:
+
+    def __init__(self, sockopt, sslopt):
+        if sockopt is None:
+            sockopt = []
+        if sslopt is None:
+            sslopt = {}
+        self.sockopt = sockopt
+        self.sslopt = sslopt
+        self.timeout = None
+
+
+def setdefaulttimeout(timeout):
+    """
+    Set the global timeout setting to connect.
+
+    Parameters
+    ----------
+    timeout: int or float
+        default socket timeout time (in seconds)
+    """
+    global _default_timeout
+    _default_timeout = timeout
+
+
+def getdefaulttimeout():
+    """
+    Get default timeout
+
+    Returns
+    ----------
+    _default_timeout: int or float
+        Return the global timeout setting (in seconds) to connect.
+    """
+    return _default_timeout
+
+
+def recv(sock, bufsize):
+    if not sock:
+        raise WebSocketConnectionClosedException("socket is already closed.")
+
+    def _recv():
+        try:
+            return sock.recv(bufsize)
+        except SSLWantReadError:
+            pass
+        except socket.error as exc:
+            error_code = extract_error_code(exc)
+            if error_code is None:
+                raise
+            if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
+                raise
+
+        sel = selectors.DefaultSelector()
+        sel.register(sock, selectors.EVENT_READ)
+
+        r = sel.select(sock.gettimeout())
+        sel.close()
+
+        if r:
+            return sock.recv(bufsize)
+
+    try:
+        if sock.gettimeout() == 0:
+            bytes_ = sock.recv(bufsize)
+        else:
+            bytes_ = _recv()
+    except socket.timeout as e:
+        message = extract_err_message(e)
+        raise WebSocketTimeoutException(message)
+    except SSLError as e:
+        message = extract_err_message(e)
+        if isinstance(message, str) and 'timed out' in message:
+            raise WebSocketTimeoutException(message)
+        else:
+            raise
+
+    if not bytes_:
+        raise WebSocketConnectionClosedException(
+            "Connection to remote host was lost.")
+
+    return bytes_
+
+
+def recv_line(sock):
+    line = []
+    while True:
+        c = recv(sock, 1)
+        line.append(c)
+        if c == b'\n':
+            break
+    return b''.join(line)
+
+
+def send(sock, data):
+    if isinstance(data, str):
+        data = data.encode('utf-8')
+
+    if not sock:
+        raise WebSocketConnectionClosedException("socket is already closed.")
+
+    def _send():
+        try:
+            return sock.send(data)
+        except SSLWantWriteError:
+            pass
+        except socket.error as exc:
+            error_code = extract_error_code(exc)
+            if error_code is None:
+                raise
+            if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
+                raise
+
+        sel = selectors.DefaultSelector()
+        sel.register(sock, selectors.EVENT_WRITE)
+
+        w = sel.select(sock.gettimeout())
+        sel.close()
+
+        if w:
+            return sock.send(data)
+
+    try:
+        if sock.gettimeout() == 0:
+            return sock.send(data)
+        else:
+            return _send()
+    except socket.timeout as e:
+        message = extract_err_message(e)
+        raise WebSocketTimeoutException(message)
+    except Exception as e:
+        message = extract_err_message(e)
+        if isinstance(message, str) and "timed out" in message:
+            raise WebSocketTimeoutException(message)
+        else:
+            raise

+ 44 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_ssl_compat.py

@@ -0,0 +1,44 @@
+"""
+_ssl_compat.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+__all__ = ["HAVE_SSL", "ssl", "SSLError", "SSLWantReadError", "SSLWantWriteError"]
+
+try:
+    import ssl
+    from ssl import SSLError
+    from ssl import SSLWantReadError
+    from ssl import SSLWantWriteError
+    HAVE_CONTEXT_CHECK_HOSTNAME = False
+    if hasattr(ssl, 'SSLContext') and hasattr(ssl.SSLContext, 'check_hostname'):
+        HAVE_CONTEXT_CHECK_HOSTNAME = True
+
+    __all__.append("HAVE_CONTEXT_CHECK_HOSTNAME")
+    HAVE_SSL = True
+except ImportError:
+    # dummy class of SSLError for environment without ssl support
+    class SSLError(Exception):
+        pass
+
+    class SSLWantReadError(Exception):
+        pass
+
+    class SSLWantWriteError(Exception):
+        pass
+
+    ssl = None
+    HAVE_SSL = False

+ 176 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_url.py

@@ -0,0 +1,176 @@
+"""
+
+"""
+"""
+_url.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import os
+import socket
+import struct
+
+from urllib.parse import unquote, urlparse
+
+
+__all__ = ["parse_url", "get_proxy_info"]
+
+
+def parse_url(url):
+    """
+    parse url and the result is tuple of
+    (hostname, port, resource path and the flag of secure mode)
+
+    Parameters
+    ----------
+    url: str
+        url string.
+    """
+    if ":" not in url:
+        raise ValueError("url is invalid")
+
+    scheme, url = url.split(":", 1)
+
+    parsed = urlparse(url, scheme="http")
+    if parsed.hostname:
+        hostname = parsed.hostname
+    else:
+        raise ValueError("hostname is invalid")
+    port = 0
+    if parsed.port:
+        port = parsed.port
+
+    is_secure = False
+    if scheme == "ws":
+        if not port:
+            port = 80
+    elif scheme == "wss":
+        is_secure = True
+        if not port:
+            port = 443
+    else:
+        raise ValueError("scheme %s is invalid" % scheme)
+
+    if parsed.path:
+        resource = parsed.path
+    else:
+        resource = "/"
+
+    if parsed.query:
+        resource += "?" + parsed.query
+
+    return hostname, port, resource, is_secure
+
+
+DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"]
+
+
+def _is_ip_address(addr):
+    try:
+        socket.inet_aton(addr)
+    except socket.error:
+        return False
+    else:
+        return True
+
+
+def _is_subnet_address(hostname):
+    try:
+        addr, netmask = hostname.split("/")
+        return _is_ip_address(addr) and 0 <= int(netmask) < 32
+    except ValueError:
+        return False
+
+
+def _is_address_in_network(ip, net):
+    ipaddr = struct.unpack('!I', socket.inet_aton(ip))[0]
+    netaddr, netmask = net.split('/')
+    netaddr = struct.unpack('!I', socket.inet_aton(netaddr))[0]
+
+    netmask = (0xFFFFFFFF << (32 - int(netmask))) & 0xFFFFFFFF
+    return ipaddr & netmask == netaddr
+
+
+def _is_no_proxy_host(hostname, no_proxy):
+    if not no_proxy:
+        v = os.environ.get("no_proxy", os.environ.get("NO_PROXY", "")).replace(" ", "")
+        if v:
+            no_proxy = v.split(",")
+    if not no_proxy:
+        no_proxy = DEFAULT_NO_PROXY_HOST
+
+    if '*' in no_proxy:
+        return True
+    if hostname in no_proxy:
+        return True
+    if _is_ip_address(hostname):
+        return any([_is_address_in_network(hostname, subnet) for subnet in no_proxy if _is_subnet_address(subnet)])
+    for domain in [domain for domain in no_proxy if domain.startswith('.')]:
+        if hostname.endswith(domain):
+            return True
+    return False
+
+
+def get_proxy_info(
+        hostname, is_secure, proxy_host=None, proxy_port=0, proxy_auth=None,
+        no_proxy=None, proxy_type='http'):
+    """
+    Try to retrieve proxy host and port from environment
+    if not provided in options.
+    Result is (proxy_host, proxy_port, proxy_auth).
+    proxy_auth is tuple of username and password
+    of proxy authentication information.
+
+    Parameters
+    ----------
+    hostname: str
+        Websocket server name.
+    is_secure: bool
+        Is the connection secure? (wss) looks for "https_proxy" in env
+        before falling back to "http_proxy"
+    proxy_host: str
+        http proxy host name.
+    http_proxy_port: str or int
+        http proxy port.
+    http_no_proxy: list
+        Whitelisted host names that don't use the proxy.
+    http_proxy_auth: tuple
+        HTTP proxy auth information. Tuple of username and password. Default is None.
+    proxy_type: str
+        Specify the proxy protocol (http, socks4, socks4a, socks5, socks5h). Default is "http".
+        Use socks4a or socks5h if you want to send DNS requests through the proxy.
+    """
+    if _is_no_proxy_host(hostname, no_proxy):
+        return None, 0, None
+
+    if proxy_host:
+        port = proxy_port
+        auth = proxy_auth
+        return proxy_host, port, auth
+
+    env_keys = ["http_proxy"]
+    if is_secure:
+        env_keys.insert(0, "https_proxy")
+
+    for key in env_keys:
+        value = os.environ.get(key, os.environ.get(key.upper(), "")).replace(" ", "")
+        if value:
+            proxy = urlparse(value)
+            auth = (unquote(proxy.username), unquote(proxy.password)) if proxy.username else None
+            return proxy.hostname, proxy.port, auth
+
+    return None, 0, None

+ 104 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/_utils.py

@@ -0,0 +1,104 @@
+"""
+_url.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+__all__ = ["NoLock", "validate_utf8", "extract_err_message", "extract_error_code"]
+
+
+class NoLock:
+
+    def __enter__(self):
+        pass
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        pass
+
+
+try:
+    # If wsaccel is available we use compiled routines to validate UTF-8
+    # strings.
+    from wsaccel.utf8validator import Utf8Validator
+
+    def _validate_utf8(utfbytes):
+        return Utf8Validator().validate(utfbytes)[0]
+
+except ImportError:
+    # UTF-8 validator
+    # python implementation of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+
+    _UTF8_ACCEPT = 0
+    _UTF8_REJECT = 12
+
+    _UTF8D = [
+        # The first part of the table maps bytes to character classes that
+        # to reduce the size of the transition table and create bitmasks.
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+        8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+        10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+        # The second part is a transition table that maps a combination
+        # of a state of the automaton and a character class to a state.
+        0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+        12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+        12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+        12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+        12,36,12,12,12,12,12,12,12,12,12,12, ]
+
+    def _decode(state, codep, ch):
+        tp = _UTF8D[ch]
+
+        codep = (ch & 0x3f) | (codep << 6) if (
+            state != _UTF8_ACCEPT) else (0xff >> tp) & ch
+        state = _UTF8D[256 + state + tp]
+
+        return state, codep
+
+    def _validate_utf8(utfbytes):
+        state = _UTF8_ACCEPT
+        codep = 0
+        for i in utfbytes:
+            state, codep = _decode(state, codep, i)
+            if state == _UTF8_REJECT:
+                return False
+
+        return True
+
+
+def validate_utf8(utfbytes):
+    """
+    validate utf8 byte string.
+    utfbytes: utf byte string to check.
+    return value: if valid utf8 string, return true. Otherwise, return false.
+    """
+    return _validate_utf8(utfbytes)
+
+
+def extract_err_message(exception):
+    if exception.args:
+        return exception.args[0]
+    else:
+        return None
+
+
+def extract_error_code(exception):
+    if exception.args and len(exception.args) > 1:
+        return exception.args[0] if isinstance(exception.args[0], int) else None

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


+ 6 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/data/header01.txt

@@ -0,0 +1,6 @@
+HTTP/1.1 101 WebSocket Protocol Handshake
+Connection: Upgrade 
+Upgrade: WebSocket
+Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
+some_header: something
+

+ 6 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/data/header02.txt

@@ -0,0 +1,6 @@
+HTTP/1.1 101 WebSocket Protocol Handshake
+Connection: Upgrade
+Upgrade WebSocket
+Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
+some_header: something
+

+ 7 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/data/header03.txt

@@ -0,0 +1,7 @@
+HTTP/1.1 101 WebSocket Protocol Handshake
+Connection: Upgrade, Keep-Alive
+Upgrade: WebSocket
+Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0=
+Set-Cookie: Token=ABCDE
+some_header: something
+

+ 21 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/echo-server.py

@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# From https://github.com/aaugustin/websockets/blob/main/example/echo.py
+
+import asyncio
+import websockets
+import os
+
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '8765')
+
+
+async def echo(websocket, path):
+    async for message in websocket:
+        await websocket.send(message)
+
+
+async def main():
+    async with websockets.serve(echo, "localhost", LOCAL_WS_SERVER_PORT):
+        await asyncio.Future()  # run forever
+
+asyncio.run(main())

+ 89 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_abnf.py

@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+#
+"""
+test_abnf.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import websocket as ws
+from websocket._abnf import *
+import unittest
+
+
+class ABNFTest(unittest.TestCase):
+
+    def testInit(self):
+        a = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING)
+        self.assertEqual(a.fin, 0)
+        self.assertEqual(a.rsv1, 0)
+        self.assertEqual(a.rsv2, 0)
+        self.assertEqual(a.rsv3, 0)
+        self.assertEqual(a.opcode, 9)
+        self.assertEqual(a.data, '')
+        a_bad = ABNF(0,1,0,0, opcode=77)
+        self.assertEqual(a_bad.rsv1, 1)
+        self.assertEqual(a_bad.opcode, 77)
+
+    def testValidate(self):
+        a_invalid_ping = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING)
+        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_invalid_ping.validate, skip_utf8_validation=False)
+        a_bad_rsv_value = ABNF(0,1,0,0, opcode=ABNF.OPCODE_TEXT)
+        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_rsv_value.validate, skip_utf8_validation=False)
+        a_bad_opcode = ABNF(0,0,0,0, opcode=77)
+        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_opcode.validate, skip_utf8_validation=False)
+        a_bad_close_frame = ABNF(0,0,0,0, opcode=ABNF.OPCODE_CLOSE, data=b'\x01')
+        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_close_frame.validate, skip_utf8_validation=False)
+        a_bad_close_frame_2 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_CLOSE, data=b'\x01\x8a\xaa\xff\xdd')
+        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_close_frame_2.validate, skip_utf8_validation=False)
+        a_bad_close_frame_3 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_CLOSE, data=b'\x03\xe7')
+        self.assertRaises(ws._exceptions.WebSocketProtocolException, a_bad_close_frame_3.validate, skip_utf8_validation=True)
+
+    def testMask(self):
+        abnf_none_data = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING, mask=1, data=None)
+        bytes_val = bytes("aaaa", 'utf-8')
+        self.assertEqual(abnf_none_data._get_masked(bytes_val), bytes_val)
+        abnf_str_data = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING, mask=1, data="a")
+        self.assertEqual(abnf_str_data._get_masked(bytes_val), b'aaaa\x00')
+
+    def testFormat(self):
+        abnf_bad_rsv_bits = ABNF(2,0,0,0, opcode=ABNF.OPCODE_TEXT)
+        self.assertRaises(ValueError, abnf_bad_rsv_bits.format)
+        abnf_bad_opcode = ABNF(0,0,0,0, opcode=5)
+        self.assertRaises(ValueError, abnf_bad_opcode.format)
+        abnf_length_10 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_TEXT, data="abcdefghij")
+        self.assertEqual(b'\x01', abnf_length_10.format()[0].to_bytes(1, 'big'))
+        self.assertEqual(b'\x8a', abnf_length_10.format()[1].to_bytes(1, 'big'))
+        self.assertEqual("fin=0 opcode=1 data=abcdefghij", abnf_length_10.__str__())
+        abnf_length_20 = ABNF(0,0,0,0, opcode=ABNF.OPCODE_BINARY, data="abcdefghijabcdefghij")
+        self.assertEqual(b'\x02', abnf_length_20.format()[0].to_bytes(1, 'big'))
+        self.assertEqual(b'\x94', abnf_length_20.format()[1].to_bytes(1, 'big'))
+        abnf_no_mask = ABNF(0,0,0,0, opcode=ABNF.OPCODE_TEXT, mask=0, data=b'\x01\x8a\xcc')
+        self.assertEqual(b'\x01\x03\x01\x8a\xcc', abnf_no_mask.format())
+
+    def testFrameBuffer(self):
+        fb = frame_buffer(0, True)
+        self.assertEqual(fb.recv, 0)
+        self.assertEqual(fb.skip_utf8_validation, True)
+        fb.clear
+        self.assertEqual(fb.header, None)
+        self.assertEqual(fb.length, None)
+        self.assertEqual(fb.mask, None)
+        self.assertEqual(fb.has_mask(), False)
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 179 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_app.py

@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+#
+"""
+test_app.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import os
+import os.path
+import websocket as ws
+import ssl
+import unittest
+
+# Skip test to access the internet unless TEST_WITH_INTERNET == 1
+TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
+# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
+TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
+TRACEABLE = True
+
+
+class WebSocketAppTest(unittest.TestCase):
+
+    class NotSetYet:
+        """ A marker class for signalling that a value hasn't been set yet.
+        """
+
+    def setUp(self):
+        ws.enableTrace(TRACEABLE)
+
+        WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
+
+    def tearDown(self):
+        WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
+        WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
+
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+    def testKeepRunning(self):
+        """ A WebSocketApp should keep running as long as its self.keep_running
+        is not False (in the boolean context).
+        """
+
+        def on_open(self, *args, **kwargs):
+            """ Set the keep_running flag for later inspection and immediately
+            close the connection.
+            """
+            self.send("hello!")
+            WebSocketAppTest.keep_running_open = self.keep_running
+            self.keep_running = False
+
+        def on_message(wsapp, message):
+            print(message)
+            self.close()
+
+        def on_close(self, *args, **kwargs):
+            """ Set the keep_running flag for the test to use.
+            """
+            WebSocketAppTest.keep_running_close = self.keep_running
+
+        app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, on_open=on_open, on_close=on_close, on_message=on_message)
+        app.run_forever()
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testSockMaskKey(self):
+        """ A WebSocketApp should forward the received mask_key function down
+        to the actual socket.
+        """
+
+        def my_mask_key_func():
+            return "\x00\x00\x00\x00"
+
+        app = ws.WebSocketApp('wss://stream.meetup.com/2/rsvps', get_mask_key=my_mask_key_func)
+
+        # if numpy is installed, this assertion fail
+        # Note: We can't use 'is' for comparing the functions directly, need to use 'id'.
+        self.assertEqual(id(app.get_mask_key), id(my_mask_key_func))
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testInvalidPingIntervalPingTimeout(self):
+        """ Test exception handling if ping_interval < ping_timeout
+        """
+
+        def on_ping(app, msg):
+            print("Got a ping!")
+            app.close()
+
+        def on_pong(app, msg):
+            print("Got a pong! No need to respond")
+            app.close()
+
+        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1', on_ping=on_ping, on_pong=on_pong)
+        self.assertRaises(ws.WebSocketException, app.run_forever, ping_interval=1, ping_timeout=2, sslopt={"cert_reqs": ssl.CERT_NONE})
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testPingInterval(self):
+        """ Test WebSocketApp proper ping functionality
+        """
+
+        def on_ping(app, msg):
+            print("Got a ping!")
+            app.close()
+
+        def on_pong(app, msg):
+            print("Got a pong! No need to respond")
+            app.close()
+
+        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1', on_ping=on_ping, on_pong=on_pong)
+        app.run_forever(ping_interval=2, ping_timeout=1, sslopt={"cert_reqs": ssl.CERT_NONE})
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testOpcodeClose(self):
+        """ Test WebSocketApp close opcode
+        """
+
+        app = ws.WebSocketApp('wss://tsock.us1.twilio.com/v3/wsconnect')
+        app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload")
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testOpcodeBinary(self):
+        """ Test WebSocketApp binary opcode
+        """
+
+        app = ws.WebSocketApp('streaming.vn.teslamotors.com/streaming/')
+        app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload")
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testBadPingInterval(self):
+        """ A WebSocketApp handling of negative ping_interval
+        """
+        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1')
+        self.assertRaises(ws.WebSocketException, app.run_forever, ping_interval=-5, sslopt={"cert_reqs": ssl.CERT_NONE})
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testBadPingTimeout(self):
+        """ A WebSocketApp handling of negative ping_timeout
+        """
+        app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1')
+        self.assertRaises(ws.WebSocketException, app.run_forever, ping_timeout=-3, sslopt={"cert_reqs": ssl.CERT_NONE})
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testCloseStatusCode(self):
+        """ Test extraction of close frame status code and close reason in WebSocketApp
+        """
+        def on_close(wsapp, close_status_code, close_msg):
+            print("on_close reached")
+
+        app = ws.WebSocketApp('wss://tsock.us1.twilio.com/v3/wsconnect', on_close=on_close)
+        closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b'\x03\xe8no-init-from-client')
+        self.assertEqual([1000, 'no-init-from-client'], app._get_close_args(closeframe))
+
+        closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b'')
+        self.assertEqual([None, None], app._get_close_args(closeframe))
+
+        app2 = ws.WebSocketApp('wss://tsock.us1.twilio.com/v3/wsconnect')
+        closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b'')
+        self.assertEqual([None, None], app2._get_close_args(closeframe))
+
+        self.assertRaises(ws.WebSocketConnectionClosedException, app.send, data="test if connection is closed")
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 119 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_cookiejar.py

@@ -0,0 +1,119 @@
+"""
+
+"""
+
+"""
+test_cookiejar.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+import unittest
+from websocket._cookiejar import SimpleCookieJar
+
+
+class CookieJarTest(unittest.TestCase):
+    def testAdd(self):
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("")
+        self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("a=b")
+        self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("a=b; domain=.abc")
+        self.assertTrue(".abc" in cookie_jar.jar)
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("a=b; domain=abc")
+        self.assertTrue(".abc" in cookie_jar.jar)
+        self.assertTrue("abc" not in cookie_jar.jar)
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("a=b; c=d; domain=abc")
+        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
+        self.assertEqual(cookie_jar.get(None), "")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("a=b; c=d; domain=abc")
+        cookie_jar.add("e=f; domain=abc")
+        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("a=b; c=d; domain=abc")
+        cookie_jar.add("e=f; domain=.abc")
+        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.add("a=b; c=d; domain=abc")
+        cookie_jar.add("e=f; domain=xyz")
+        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
+        self.assertEqual(cookie_jar.get("xyz"), "e=f")
+        self.assertEqual(cookie_jar.get("something"), "")
+
+    def testSet(self):
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b")
+        self.assertFalse(cookie_jar.jar, "Cookie with no domain should not be added to the jar")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b; domain=.abc")
+        self.assertTrue(".abc" in cookie_jar.jar)
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b; domain=abc")
+        self.assertTrue(".abc" in cookie_jar.jar)
+        self.assertTrue("abc" not in cookie_jar.jar)
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b; c=d; domain=abc")
+        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b; c=d; domain=abc")
+        cookie_jar.set("e=f; domain=abc")
+        self.assertEqual(cookie_jar.get("abc"), "e=f")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b; c=d; domain=abc")
+        cookie_jar.set("e=f; domain=.abc")
+        self.assertEqual(cookie_jar.get("abc"), "e=f")
+
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b; c=d; domain=abc")
+        cookie_jar.set("e=f; domain=xyz")
+        self.assertEqual(cookie_jar.get("abc"), "a=b; c=d")
+        self.assertEqual(cookie_jar.get("xyz"), "e=f")
+        self.assertEqual(cookie_jar.get("something"), "")
+
+    def testGet(self):
+        cookie_jar = SimpleCookieJar()
+        cookie_jar.set("a=b; c=d; domain=abc.com")
+        self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d")
+        self.assertEqual(cookie_jar.get("x.abc.com"), "a=b; c=d")
+        self.assertEqual(cookie_jar.get("abc.com.es"), "")
+        self.assertEqual(cookie_jar.get("xabc.com"), "")
+
+        cookie_jar.set("a=b; c=d; domain=.abc.com")
+        self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d")
+        self.assertEqual(cookie_jar.get("x.abc.com"), "a=b; c=d")
+        self.assertEqual(cookie_jar.get("abc.com.es"), "")
+        self.assertEqual(cookie_jar.get("xabc.com"), "")
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 177 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_http.py

@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+#
+"""
+test_http.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import os
+import os.path
+import websocket as ws
+from websocket._http import proxy_info, read_headers, _start_proxied_socket, _tunnel, _get_addrinfo_list, connect
+import unittest
+import ssl
+import websocket
+import socket
+
+try:
+    from python_socks._errors import ProxyError, ProxyTimeoutError, ProxyConnectionError
+except:
+    from websocket._http import ProxyError, ProxyTimeoutError, ProxyConnectionError
+
+# Skip test to access the internet unless TEST_WITH_INTERNET == 1
+TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
+TEST_WITH_PROXY = os.environ.get('TEST_WITH_PROXY', '0') == '1'
+# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
+TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
+
+
+class SockMock:
+    def __init__(self):
+        self.data = []
+        self.sent = []
+
+    def add_packet(self, data):
+        self.data.append(data)
+
+    def gettimeout(self):
+        return None
+
+    def recv(self, bufsize):
+        if self.data:
+            e = self.data.pop(0)
+            if isinstance(e, Exception):
+                raise e
+            if len(e) > bufsize:
+                self.data.insert(0, e[bufsize:])
+            return e[:bufsize]
+
+    def send(self, data):
+        self.sent.append(data)
+        return len(data)
+
+    def close(self):
+        pass
+
+
+class HeaderSockMock(SockMock):
+
+    def __init__(self, fname):
+        SockMock.__init__(self)
+        path = os.path.join(os.path.dirname(__file__), fname)
+        with open(path, "rb") as f:
+            self.add_packet(f.read())
+
+
+class OptsList():
+
+    def __init__(self):
+        self.timeout = 1
+        self.sockopt = []
+        self.sslopt = {"cert_reqs": ssl.CERT_NONE}
+
+
+class HttpTest(unittest.TestCase):
+
+    def testReadHeader(self):
+        status, header, status_message = read_headers(HeaderSockMock("data/header01.txt"))
+        self.assertEqual(status, 101)
+        self.assertEqual(header["connection"], "Upgrade")
+        # header02.txt is intentionally malformed
+        self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt"))
+
+    def testTunnel(self):
+        self.assertRaises(ws.WebSocketProxyException, _tunnel, HeaderSockMock("data/header01.txt"), "example.com", 80, ("username", "password"))
+        self.assertRaises(ws.WebSocketProxyException, _tunnel, HeaderSockMock("data/header02.txt"), "example.com", 80, ("username", "password"))
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testConnect(self):
+        # Not currently testing an actual proxy connection, so just check whether proxy errors are raised. This requires internet for a DNS lookup
+        if ws._http.HAVE_PYTHON_SOCKS:
+            # Need this check, otherwise case where python_socks is not installed triggers
+            # websocket._exceptions.WebSocketException: Python Socks is needed for SOCKS proxying but is not available
+            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4", timeout=1))
+            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4a", timeout=1))
+            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5", timeout=1))
+            self.assertRaises(ProxyTimeoutError, _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5h", timeout=1))
+            self.assertRaises(ProxyConnectionError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=9999, proxy_type="socks4", timeout=1), None)
+
+        self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http"))
+        self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http"))
+        self.assertRaises(socket.timeout, connect, "wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=9999, proxy_type="http", timeout=1), None)
+        self.assertEqual(
+            connect("wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=8080, proxy_type="http"), True),
+            (True, ("google.com", 443, "/")))
+        # The following test fails on Mac OS with a gaierror, not an OverflowError
+        # self.assertRaises(OverflowError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=99999, proxy_type="socks4", timeout=2), False)
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    @unittest.skipUnless(TEST_WITH_PROXY, "This test requires a HTTP proxy to be running on port 8899")
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+    def testProxyConnect(self):
+        ws = websocket.WebSocket()
+        ws.connect("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT, http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http")
+        ws.send("Hello, Server")
+        server_response = ws.recv()
+        self.assertEqual(server_response, "Hello, Server")
+        # self.assertEqual(_start_proxied_socket("wss://api.bitfinex.com/ws/2", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http"))[1], ("api.bitfinex.com", 443, '/ws/2'))
+        self.assertEqual(_get_addrinfo_list("api.bitfinex.com", 443, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http")),
+                         (socket.getaddrinfo("127.0.0.1", 8899, 0, socket.SOCK_STREAM, socket.SOL_TCP), True, None))
+        self.assertEqual(connect("wss://api.bitfinex.com/ws/2", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=8899, proxy_type="http"), None)[1], ("api.bitfinex.com", 443, '/ws/2'))
+        # TODO: Test SOCKS4 and SOCK5 proxies with unit tests
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testSSLopt(self):
+        ssloptions = {
+            "cert_reqs": ssl.CERT_NONE,
+            "check_hostname": False,
+            "server_hostname": "ServerName",
+            "ssl_version": ssl.PROTOCOL_TLS,
+            "ciphers": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:\
+                        TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\
+                        ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:\
+                        ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
+                        DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\
+                        ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:\
+                        ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\
+                        DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA256:\
+                        ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:\
+                        ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA",
+            "ecdh_curve": "prime256v1"
+        }
+        ws_ssl1 = websocket.WebSocket(sslopt=ssloptions)
+        ws_ssl1.connect("wss://api.bitfinex.com/ws/2")
+        ws_ssl1.send("Hello")
+        ws_ssl1.close()
+
+        ws_ssl2 = websocket.WebSocket(sslopt={"check_hostname": True})
+        ws_ssl2.connect("wss://api.bitfinex.com/ws/2")
+        ws_ssl2.close
+
+    def testProxyInfo(self):
+        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").proxy_protocol, "http")
+        self.assertRaises(ProxyError, proxy_info, http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="badval")
+        self.assertEqual(proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="http").proxy_host, "example.com")
+        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").proxy_port, "8080")
+        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").auth, None)
+        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http", http_proxy_auth=("my_username123", "my_pass321")).auth[0], "my_username123")
+        self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http", http_proxy_auth=("my_username123", "my_pass321")).auth[1], "my_pass321")
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 301 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_url.py

@@ -0,0 +1,301 @@
+# -*- coding: utf-8 -*-
+#
+"""
+test_url.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import os
+import unittest
+from websocket._url import get_proxy_info, parse_url, _is_address_in_network, _is_no_proxy_host
+
+
+class UrlTest(unittest.TestCase):
+
+    def test_address_in_network(self):
+        self.assertTrue(_is_address_in_network('127.0.0.1', '127.0.0.0/8'))
+        self.assertTrue(_is_address_in_network('127.1.0.1', '127.0.0.0/8'))
+        self.assertFalse(_is_address_in_network('127.1.0.1', '127.0.0.0/24'))
+
+    def testParseUrl(self):
+        p = parse_url("ws://www.example.com/r")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com/r/")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/r/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com/")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com:8080/r")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com:8080/")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://www.example.com:8080")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("wss://www.example.com:8080/r")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], True)
+
+        p = parse_url("wss://www.example.com:8080/r?key=value")
+        self.assertEqual(p[0], "www.example.com")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r?key=value")
+        self.assertEqual(p[3], True)
+
+        self.assertRaises(ValueError, parse_url, "http://www.example.com/r")
+
+        p = parse_url("ws://[2a03:4000:123:83::3]/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 80)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("ws://[2a03:4000:123:83::3]:8080/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], False)
+
+        p = parse_url("wss://[2a03:4000:123:83::3]/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 443)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], True)
+
+        p = parse_url("wss://[2a03:4000:123:83::3]:8080/r")
+        self.assertEqual(p[0], "2a03:4000:123:83::3")
+        self.assertEqual(p[1], 8080)
+        self.assertEqual(p[2], "/r")
+        self.assertEqual(p[3], True)
+
+
+class IsNoProxyHostTest(unittest.TestCase):
+    def setUp(self):
+        self.no_proxy = os.environ.get("no_proxy", None)
+        if "no_proxy" in os.environ:
+            del os.environ["no_proxy"]
+
+    def tearDown(self):
+        if self.no_proxy:
+            os.environ["no_proxy"] = self.no_proxy
+        elif "no_proxy" in os.environ:
+            del os.environ["no_proxy"]
+
+    def testMatchAll(self):
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['*']))
+        self.assertTrue(_is_no_proxy_host("192.168.0.1", ['*']))
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['other.websocket.org', '*']))
+        os.environ['no_proxy'] = '*'
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
+        self.assertTrue(_is_no_proxy_host("192.168.0.1", None))
+        os.environ['no_proxy'] = 'other.websocket.org, *'
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
+
+    def testIpAddress(self):
+        self.assertTrue(_is_no_proxy_host("127.0.0.1", ['127.0.0.1']))
+        self.assertFalse(_is_no_proxy_host("127.0.0.2", ['127.0.0.1']))
+        self.assertTrue(_is_no_proxy_host("127.0.0.1", ['other.websocket.org', '127.0.0.1']))
+        self.assertFalse(_is_no_proxy_host("127.0.0.2", ['other.websocket.org', '127.0.0.1']))
+        os.environ['no_proxy'] = '127.0.0.1'
+        self.assertTrue(_is_no_proxy_host("127.0.0.1", None))
+        self.assertFalse(_is_no_proxy_host("127.0.0.2", None))
+        os.environ['no_proxy'] = 'other.websocket.org, 127.0.0.1'
+        self.assertTrue(_is_no_proxy_host("127.0.0.1", None))
+        self.assertFalse(_is_no_proxy_host("127.0.0.2", None))
+
+    def testIpAddressInRange(self):
+        self.assertTrue(_is_no_proxy_host("127.0.0.1", ['127.0.0.0/8']))
+        self.assertTrue(_is_no_proxy_host("127.0.0.2", ['127.0.0.0/8']))
+        self.assertFalse(_is_no_proxy_host("127.1.0.1", ['127.0.0.0/24']))
+        os.environ['no_proxy'] = '127.0.0.0/8'
+        self.assertTrue(_is_no_proxy_host("127.0.0.1", None))
+        self.assertTrue(_is_no_proxy_host("127.0.0.2", None))
+        os.environ['no_proxy'] = '127.0.0.0/24'
+        self.assertFalse(_is_no_proxy_host("127.1.0.1", None))
+
+    def testHostnameMatch(self):
+        self.assertTrue(_is_no_proxy_host("my.websocket.org", ['my.websocket.org']))
+        self.assertTrue(_is_no_proxy_host("my.websocket.org", ['other.websocket.org', 'my.websocket.org']))
+        self.assertFalse(_is_no_proxy_host("my.websocket.org", ['other.websocket.org']))
+        os.environ['no_proxy'] = 'my.websocket.org'
+        self.assertTrue(_is_no_proxy_host("my.websocket.org", None))
+        self.assertFalse(_is_no_proxy_host("other.websocket.org", None))
+        os.environ['no_proxy'] = 'other.websocket.org, my.websocket.org'
+        self.assertTrue(_is_no_proxy_host("my.websocket.org", None))
+
+    def testHostnameMatchDomain(self):
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['.websocket.org']))
+        self.assertTrue(_is_no_proxy_host("my.other.websocket.org", ['.websocket.org']))
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", ['my.websocket.org', '.websocket.org']))
+        self.assertFalse(_is_no_proxy_host("any.websocket.com", ['.websocket.org']))
+        os.environ['no_proxy'] = '.websocket.org'
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
+        self.assertTrue(_is_no_proxy_host("my.other.websocket.org", None))
+        self.assertFalse(_is_no_proxy_host("any.websocket.com", None))
+        os.environ['no_proxy'] = 'my.websocket.org, .websocket.org'
+        self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
+
+
+class ProxyInfoTest(unittest.TestCase):
+    def setUp(self):
+        self.http_proxy = os.environ.get("http_proxy", None)
+        self.https_proxy = os.environ.get("https_proxy", None)
+        self.no_proxy = os.environ.get("no_proxy", None)
+        if "http_proxy" in os.environ:
+            del os.environ["http_proxy"]
+        if "https_proxy" in os.environ:
+            del os.environ["https_proxy"]
+        if "no_proxy" in os.environ:
+            del os.environ["no_proxy"]
+
+    def tearDown(self):
+        if self.http_proxy:
+            os.environ["http_proxy"] = self.http_proxy
+        elif "http_proxy" in os.environ:
+            del os.environ["http_proxy"]
+
+        if self.https_proxy:
+            os.environ["https_proxy"] = self.https_proxy
+        elif "https_proxy" in os.environ:
+            del os.environ["https_proxy"]
+
+        if self.no_proxy:
+            os.environ["no_proxy"] = self.no_proxy
+        elif "no_proxy" in os.environ:
+            del os.environ["no_proxy"]
+
+    def testProxyFromArgs(self):
+        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost"), ("localhost", 0, None))
+        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128),
+                         ("localhost", 3128, None))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost"), ("localhost", 0, None))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128),
+                         ("localhost", 3128, None))
+
+        self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_auth=("a", "b")),
+                         ("localhost", 0, ("a", "b")))
+        self.assertEqual(
+            get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
+            ("localhost", 3128, ("a", "b")))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_auth=("a", "b")),
+                         ("localhost", 0, ("a", "b")))
+        self.assertEqual(
+            get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")),
+            ("localhost", 3128, ("a", "b")))
+
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128,
+                                        no_proxy=["example.com"], proxy_auth=("a", "b")),
+                         ("localhost", 3128, ("a", "b")))
+        self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128,
+                                        no_proxy=["echo.websocket.org"], proxy_auth=("a", "b")),
+                         (None, 0, None))
+
+    def testProxyFromEnv(self):
+        os.environ["http_proxy"] = "http://localhost/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
+        os.environ["http_proxy"] = "http://localhost:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
+
+        os.environ["http_proxy"] = "http://localhost/"
+        os.environ["https_proxy"] = "http://localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None))
+        os.environ["http_proxy"] = "http://localhost:3128/"
+        os.environ["https_proxy"] = "http://localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None))
+
+        os.environ["http_proxy"] = "http://localhost/"
+        os.environ["https_proxy"] = "http://localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, None))
+        os.environ["http_proxy"] = "http://localhost:3128/"
+        os.environ["https_proxy"] = "http://localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, None))
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        os.environ["https_proxy"] = "http://a:b@localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b")))
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        os.environ["https_proxy"] = "http://a:b@localhost2/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("a", "b")))
+
+        os.environ["http_proxy"] = "http://john%40example.com:P%40SSWORD@localhost:3128/"
+        os.environ["https_proxy"] = "http://john%40example.com:P%40SSWORD@localhost2:3128/"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("john@example.com", "P@SSWORD")))
+
+        os.environ["http_proxy"] = "http://a:b@localhost/"
+        os.environ["https_proxy"] = "http://a:b@localhost2/"
+        os.environ["no_proxy"] = "example1.com,example2.com"
+        self.assertEqual(get_proxy_info("example.1.com", True), ("localhost2", None, ("a", "b")))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        os.environ["no_proxy"] = "example1.com,example2.com, echo.websocket.org"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None))
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        os.environ["no_proxy"] = "example1.com,example2.com, .websocket.org"
+        self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None))
+
+        os.environ["http_proxy"] = "http://a:b@localhost:3128/"
+        os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
+        os.environ["no_proxy"] = "127.0.0.0/8, 192.168.0.0/16"
+        self.assertEqual(get_proxy_info("127.0.0.1", False), (None, 0, None))
+        self.assertEqual(get_proxy_info("192.168.1.1", False), (None, 0, None))
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 458 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/nls/websocket/tests/test_websocket.py

@@ -0,0 +1,458 @@
+# -*- coding: utf-8 -*-
+#
+"""
+
+"""
+
+"""
+test_websocket.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import os
+import os.path
+import socket
+import websocket as ws
+from websocket._handshake import _create_sec_websocket_key, \
+    _validate as _validate_header
+from websocket._http import read_headers
+from websocket._utils import validate_utf8
+from base64 import decodebytes as base64decode
+
+import unittest
+
+try:
+    import ssl
+    from ssl import SSLError
+except ImportError:
+    # dummy class of SSLError for ssl none-support environment.
+    class SSLError(Exception):
+        pass
+
+# Skip test to access the internet unless TEST_WITH_INTERNET == 1
+TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
+# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
+TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
+TRACEABLE = True
+
+
+def create_mask_key(_):
+    return "abcd"
+
+
+class SockMock:
+    def __init__(self):
+        self.data = []
+        self.sent = []
+
+    def add_packet(self, data):
+        self.data.append(data)
+
+    def gettimeout(self):
+        return None
+
+    def recv(self, bufsize):
+        if self.data:
+            e = self.data.pop(0)
+            if isinstance(e, Exception):
+                raise e
+            if len(e) > bufsize:
+                self.data.insert(0, e[bufsize:])
+            return e[:bufsize]
+
+    def send(self, data):
+        self.sent.append(data)
+        return len(data)
+
+    def close(self):
+        pass
+
+
+class HeaderSockMock(SockMock):
+
+    def __init__(self, fname):
+        SockMock.__init__(self)
+        path = os.path.join(os.path.dirname(__file__), fname)
+        with open(path, "rb") as f:
+            self.add_packet(f.read())
+
+
+class WebSocketTest(unittest.TestCase):
+    def setUp(self):
+        ws.enableTrace(TRACEABLE)
+
+    def tearDown(self):
+        pass
+
+    def testDefaultTimeout(self):
+        self.assertEqual(ws.getdefaulttimeout(), None)
+        ws.setdefaulttimeout(10)
+        self.assertEqual(ws.getdefaulttimeout(), 10)
+        ws.setdefaulttimeout(None)
+
+    def testWSKey(self):
+        key = _create_sec_websocket_key()
+        self.assertTrue(key != 24)
+        self.assertTrue(str("¥n") not in key)
+
+    def testNonce(self):
+        """ WebSocket key should be a random 16-byte nonce.
+        """
+        key = _create_sec_websocket_key()
+        nonce = base64decode(key.encode("utf-8"))
+        self.assertEqual(16, len(nonce))
+
+    def testWsUtils(self):
+        key = "c6b8hTg4EeGb2gQMztV1/g=="
+        required_header = {
+            "upgrade": "websocket",
+            "connection": "upgrade",
+            "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0="}
+        self.assertEqual(_validate_header(required_header, key, None), (True, None))
+
+        header = required_header.copy()
+        header["upgrade"] = "http"
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+        del header["upgrade"]
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+
+        header = required_header.copy()
+        header["connection"] = "something"
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+        del header["connection"]
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+
+        header = required_header.copy()
+        header["sec-websocket-accept"] = "something"
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+        del header["sec-websocket-accept"]
+        self.assertEqual(_validate_header(header, key, None), (False, None))
+
+        header = required_header.copy()
+        header["sec-websocket-protocol"] = "sub1"
+        self.assertEqual(_validate_header(header, key, ["sub1", "sub2"]), (True, "sub1"))
+        # This case will print out a logging error using the error() function, but that is expected
+        self.assertEqual(_validate_header(header, key, ["sub2", "sub3"]), (False, None))
+
+        header = required_header.copy()
+        header["sec-websocket-protocol"] = "sUb1"
+        self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (True, "sub1"))
+
+        header = required_header.copy()
+        # This case will print out a logging error using the error() function, but that is expected
+        self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (False, None))
+
+    def testReadHeader(self):
+        status, header, status_message = read_headers(HeaderSockMock("data/header01.txt"))
+        self.assertEqual(status, 101)
+        self.assertEqual(header["connection"], "Upgrade")
+
+        status, header, status_message = read_headers(HeaderSockMock("data/header03.txt"))
+        self.assertEqual(status, 101)
+        self.assertEqual(header["connection"], "Upgrade, Keep-Alive")
+
+        HeaderSockMock("data/header02.txt")
+        self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt"))
+
+    def testSend(self):
+        # TODO: add longer frame data
+        sock = ws.WebSocket()
+        sock.set_mask_key(create_mask_key)
+        s = sock.sock = HeaderSockMock("data/header01.txt")
+        sock.send("Hello")
+        self.assertEqual(s.sent[0], b'\x81\x85abcd)\x07\x0f\x08\x0e')
+
+        sock.send("こんにちは")
+        self.assertEqual(s.sent[1], b'\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc')
+
+#        sock.send("x" * 5000)
+#        self.assertEqual(s.sent[1], b'\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc")
+
+        self.assertEqual(sock.send_binary(b'1111111111101'), 19)
+
+    def testRecv(self):
+        # TODO: add longer frame data
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        something = b'\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc'
+        s.add_packet(something)
+        data = sock.recv()
+        self.assertEqual(data, "こんにちは")
+
+        s.add_packet(b'\x81\x85abcd)\x07\x0f\x08\x0e')
+        data = sock.recv()
+        self.assertEqual(data, "Hello")
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testIter(self):
+        count = 2
+        for _ in ws.create_connection('wss://stream.meetup.com/2/rsvps'):
+            count -= 1
+            if count == 0:
+                break
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testNext(self):
+        sock = ws.create_connection('wss://stream.meetup.com/2/rsvps')
+        self.assertEqual(str, type(next(sock)))
+
+    def testInternalRecvStrict(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        s.add_packet(b'foo')
+        s.add_packet(socket.timeout())
+        s.add_packet(b'bar')
+        # s.add_packet(SSLError("The read operation timed out"))
+        s.add_packet(b'baz')
+        with self.assertRaises(ws.WebSocketTimeoutException):
+            sock.frame_buffer.recv_strict(9)
+        #     with self.assertRaises(SSLError):
+        #         data = sock._recv_strict(9)
+        data = sock.frame_buffer.recv_strict(9)
+        self.assertEqual(data, b'foobarbaz')
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.frame_buffer.recv_strict(1)
+
+    def testRecvTimeout(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        s.add_packet(b'\x81')
+        s.add_packet(socket.timeout())
+        s.add_packet(b'\x8dabcd\x29\x07\x0f\x08\x0e')
+        s.add_packet(socket.timeout())
+        s.add_packet(b'\x4e\x43\x33\x0e\x10\x0f\x00\x40')
+        with self.assertRaises(ws.WebSocketTimeoutException):
+            sock.recv()
+        with self.assertRaises(ws.WebSocketTimeoutException):
+            sock.recv()
+        data = sock.recv()
+        self.assertEqual(data, "Hello, World!")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testRecvWithSimpleFragmentation(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Brevity is "
+        s.add_packet(b'\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
+        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
+        s.add_packet(b'\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17')
+        data = sock.recv()
+        self.assertEqual(data, "Brevity is the soul of wit")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testRecvWithFireEventOfFragmentation(self):
+        sock = ws.WebSocket(fire_cont_frame=True)
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Brevity is "
+        s.add_packet(b'\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
+        # OPCODE=CONT, FIN=0, MSG="Brevity is "
+        s.add_packet(b'\x00\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
+        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
+        s.add_packet(b'\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17')
+
+        _, data = sock.recv_data()
+        self.assertEqual(data, b'Brevity is ')
+        _, data = sock.recv_data()
+        self.assertEqual(data, b'Brevity is ')
+        _, data = sock.recv_data()
+        self.assertEqual(data, b'the soul of wit')
+
+        # OPCODE=CONT, FIN=0, MSG="Brevity is "
+        s.add_packet(b'\x80\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C')
+
+        with self.assertRaises(ws.WebSocketException):
+            sock.recv_data()
+
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testClose(self):
+        sock = ws.WebSocket()
+        sock.connected = True
+        sock.close
+
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        sock.connected = True
+        s.add_packet(b'\x88\x80\x17\x98p\x84')
+        sock.recv()
+        self.assertEqual(sock.connected, False)
+
+    def testRecvContFragmentation(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        # OPCODE=CONT, FIN=1, MSG="the soul of wit"
+        s.add_packet(b'\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17')
+        self.assertRaises(ws.WebSocketException, sock.recv)
+
+    def testRecvWithProlongedFragmentation(self):
+        sock = ws.WebSocket()
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, "
+        s.add_packet(b'\x01\x9babcd.\x0c\x00\x01A\x0f\x0c\x16\x04B\x16\n\x15\rC\x10\t\x07C\x06\x13\x07\x02\x07\tNC')
+        # OPCODE=CONT, FIN=0, MSG="dear friends, "
+        s.add_packet(b'\x00\x8eabcd\x05\x07\x02\x16A\x04\x11\r\x04\x0c\x07\x17MB')
+        # OPCODE=CONT, FIN=1, MSG="once more"
+        s.add_packet(b'\x80\x89abcd\x0e\x0c\x00\x01A\x0f\x0c\x16\x04')
+        data = sock.recv()
+        self.assertEqual(
+            data,
+            "Once more unto the breach, dear friends, once more")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+
+    def testRecvWithFragmentationAndControlFrame(self):
+        sock = ws.WebSocket()
+        sock.set_mask_key(create_mask_key)
+        s = sock.sock = SockMock()
+        # OPCODE=TEXT, FIN=0, MSG="Too much "
+        s.add_packet(b'\x01\x89abcd5\r\x0cD\x0c\x17\x00\x0cA')
+        # OPCODE=PING, FIN=1, MSG="Please PONG this"
+        s.add_packet(b'\x89\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17')
+        # OPCODE=CONT, FIN=1, MSG="of a good thing"
+        s.add_packet(b'\x80\x8fabcd\x0e\x04C\x05A\x05\x0c\x0b\x05B\x17\x0c\x08\x0c\x04')
+        data = sock.recv()
+        self.assertEqual(data, "Too much of a good thing")
+        with self.assertRaises(ws.WebSocketConnectionClosedException):
+            sock.recv()
+        self.assertEqual(
+            s.sent[0],
+            b'\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17')
+
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+    def testWebSocket(self):
+        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
+        self.assertNotEqual(s, None)
+        s.send("Hello, World")
+        result = s.next()
+        s.fileno()
+        self.assertEqual(result, "Hello, World")
+
+        s.send("こにゃにゃちは、世界")
+        result = s.recv()
+        self.assertEqual(result, "こにゃにゃちは、世界")
+        self.assertRaises(ValueError, s.send_close, -1, "")
+        s.close()
+
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+    def testPingPong(self):
+        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
+        self.assertNotEqual(s, None)
+        s.ping("Hello")
+        s.pong("Hi")
+        s.close()
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testSupportRedirect(self):
+        s = ws.WebSocket()
+        self.assertRaises(ws._exceptions.WebSocketBadStatusException, s.connect, "ws://google.com/")
+        # Need to find a URL that has a redirect code leading to a websocket
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testSecureWebSocket(self):
+        import ssl
+        s = ws.create_connection("wss://api.bitfinex.com/ws/2")
+        self.assertNotEqual(s, None)
+        self.assertTrue(isinstance(s.sock, ssl.SSLSocket))
+        self.assertEqual(s.getstatus(), 101)
+        self.assertNotEqual(s.getheaders(), None)
+        s.settimeout(10)
+        self.assertEqual(s.gettimeout(), 10)
+        self.assertEqual(s.getsubprotocol(), None)
+        s.abort()
+
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+    def testWebSocketWithCustomHeader(self):
+        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT,
+                                 headers={"User-Agent": "PythonWebsocketClient"})
+        self.assertNotEqual(s, None)
+        s.send("Hello, World")
+        result = s.recv()
+        self.assertEqual(result, "Hello, World")
+        self.assertRaises(ValueError, s.close, -1, "")
+        s.close()
+
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+    def testAfterClose(self):
+        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
+        self.assertNotEqual(s, None)
+        s.close()
+        self.assertRaises(ws.WebSocketConnectionClosedException, s.send, "Hello")
+        self.assertRaises(ws.WebSocketConnectionClosedException, s.recv)
+
+
+class SockOptTest(unittest.TestCase):
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+    def testSockOpt(self):
+        sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),)
+        s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT, sockopt=sockopt)
+        self.assertNotEqual(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0)
+        s.close()
+
+
+class UtilsTest(unittest.TestCase):
+    def testUtf8Validator(self):
+        state = validate_utf8(b'\xf0\x90\x80\x80')
+        self.assertEqual(state, True)
+        state = validate_utf8(b'\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\xed\xa0\x80edited')
+        self.assertEqual(state, False)
+        state = validate_utf8(b'')
+        self.assertEqual(state, True)
+
+
+class HandshakeTest(unittest.TestCase):
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def test_http_SSL(self):
+        websock1 = ws.WebSocket(sslopt={"cert_chain": ssl.get_default_verify_paths().capath}, enable_multithread=False)
+        self.assertRaises(ValueError,
+                          websock1.connect, "wss://api.bitfinex.com/ws/2")
+        websock2 = ws.WebSocket(sslopt={"certfile": "myNonexistentCertFile"})
+        self.assertRaises(FileNotFoundError,
+                          websock2.connect, "wss://api.bitfinex.com/ws/2")
+
+    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+    def testManualHeaders(self):
+        websock3 = ws.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE,
+                                        "ca_certs": ssl.get_default_verify_paths().capath,
+                                        "ca_cert_path": ssl.get_default_verify_paths().openssl_cafile})
+        self.assertRaises(ws._exceptions.WebSocketBadStatusException,
+                          websock3.connect, "wss://api.bitfinex.com/ws/2", cookie="chocolate",
+                          origin="testing_websockets.com",
+                          host="echo.websocket.org/websocket-client-test",
+                          subprotocols=["testproto"],
+                          connection="Upgrade",
+                          header={"CustomHeader1":"123",
+                                  "Cookie":"TestValue",
+                                  "Sec-WebSocket-Key":"k9kFAUWNAMmf5OEMfTlOEA==",
+                                  "Sec-WebSocket-Protocol":"newprotocol"})
+
+    def testIPv6(self):
+        websock2 = ws.WebSocket()
+        self.assertRaises(ValueError, websock2.connect, "2001:4860:4860::8888")
+
+    def testBadURLs(self):
+        websock3 = ws.WebSocket()
+        self.assertRaises(ValueError, websock3.connect, "ws//example.com")
+        self.assertRaises(ws.WebSocketAddressException, websock3.connect, "ws://example")
+        self.assertRaises(ValueError, websock3.connect, "example.com")
+
+
+if __name__ == "__main__":
+    unittest.main()

+ 4 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/requirements.txt

@@ -0,0 +1,4 @@
+oss2
+aliyun-python-sdk-core>=2.13.3
+matplotlib>=3.3.4
+

+ 49 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/setup.py

@@ -0,0 +1,49 @@
+#!/usr/bin/python
+'''
+Licensed to the Apache Software Foundation(ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+http: // www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations under
+the License.
+'''
+import os
+import setuptools
+
+with open("README.md", "r") as f:
+    long_description = f.read()
+
+requires = [
+    "oss2",
+    "aliyun-python-sdk-core>=2.13.3",
+    "matplotlib>=3.3.4"
+]
+
+setup_args = {
+    'version': "1.0.0",
+    'author': "jiaqi.sjq",
+    'author_email': "jiaqi.sjq@alibaba-inc.com",
+    'description': "python sdk for nls",
+    'license':"Apache License 2.0",
+    'long_description': long_description,
+    'long_description_content_type': "text/markdown",
+    'keywords': ["nls", "sdk"],
+    'url': "https://github.com/..",
+    'packages': ["nls", "nls/websocket"],
+    'install_requires': requires,
+    'classifiers': [
+        "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: Apache Software License",
+        "Operating System :: OS Independent",
+    ],
+}
+
+setuptools.setup(name='nls', **setup_args)

BIN
voip/alibabacloud-nls-python-sdk-1.0.2/tests/__pycache__/test_utils.cpython-39.pyc


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/tests/test0.wav


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/tests/test1.pcm


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/tests/test1.wav


BIN
voip/alibabacloud-nls-python-sdk-1.0.2/tests/test1_1.pcm


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


+ 73 - 0
voip/alibabacloud-nls-python-sdk-1.0.2/tests/test_sr.py

@@ -0,0 +1,73 @@
+import time
+import threading
+import sys
+
+import nls
+from tests.test_utils import (TEST_ACCESS_TOKEN, TEST_ACCESS_APPKEY)
+
+class TestSr:
+    def __init__(self, tid, test_file):
+        self.__th = threading.Thread(target=self.__test_run)
+        self.__id = tid
+        self.__test_file = test_file
+   
+    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_start(self, message, *args):
+        print('test_on_start:{}'.format(message))
+
+    def test_on_error(self, message, *args):
+        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('test_on_chg:{}'.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))
+        
+        sr = nls.NlsSpeechRecognizer(
+                    token=TEST_ACCESS_TOKEN,
+                    appkey=TEST_ACCESS_APPKEY,
+                    on_start=self.test_on_start,
+                    on_result_changed=self.test_on_result_chg,
+                    on_completed=self.test_on_completed,
+                    on_error=self.test_on_error,
+                    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)
+
+            r = sr.stop()
+            print('{}: sr stopped:{}'.format(self.__id, r))
+            time.sleep(5)
+
+def multiruntest(num=500):
+    for i in range(0, num):
+        name = 'thread' + str(i)
+        t = TestSr(name, 'tests/test1.pcm')
+        t.start()
+
+nls.enableTrace(True)
+multiruntest(1)
+
+

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików