一、前言說明
1.功能簡述
登錄后進入聊天界面,如果服務器都在同一個地址,則都進入同一個房間

進入/離開/發消息同一房間用戶都可以看到,輸入“tuling”或“chatbot”可以切換為和Tuling機器人或者ChatBot聊天
按住Say說話,自動錄音並轉為文本發送
如果是跟機器人聊天,則自動回復文本並播放文本語音

Tuling,是圖靈,已經錄入大量中文對話,直接調用接口即可實現自動回復,實用於開發聊天軟件
ChatBot,可自行訓練機器人,讓機器人擁有自己的語料庫,實用於企業智能聊天個性化
2.需要的核心技術
a. 輸入語音,識別后轉換為輸入文字
b. Tuling或ChatBot兩種機器人,回復輸出文字
c. 輸出文字,轉換為輸出語音並播放
以上a和c主要調用Baidu提供的API進行轉換,如果理解本文,可自行嘗試調用Google提供的API實現,Google技術強大但是否對中文支持良好,博主不曾嘗試不妄自揣度
3.環境說明
系統環境Win10,運行環境Python3.6,運行工具Pycharm
二、源碼設計(貼上完整源碼自己去理解)
1.運行順序
(1)TrainChat.py訓練本地chatbot機器人(每次更新訓練內容,運行一次即可)
(2) server.py開啟服務器
(3)client.py運行n次,每次運行都可登陸一個用戶

2.服務器server.py
主要處理用戶的登錄校驗,房間的人員消息處理
此處通過config.py中配置的列表PORT = range(1, 3)生成兩個房間,地址分別是127.0.0.1:1和127.0.0.1:2(實用時可以無限個)
啟用客戶端前,這個服務器要先運行,代碼中CommandHandler類拆解client客戶端發送的信息中的命令,並綁定函數
1 import asynchat 2 import asyncore 3 from config import PORT 4 5 6 # 定義結束異常類 7 class EndSession(Exception): 8 pass 9 10 11 class ChatServer(asyncore.dispatcher): 12 """ 13 聊天服務器 14 """ 15 def __init__(self, port): 16 asyncore.dispatcher.__init__(self) 17 # 創建socket 18 self.create_socket() 19 # 設置 socket 為可重用 20 self.set_reuse_addr() 21 # 監聽端口 22 self.bind(('', port)) 23 self.listen(5) 24 self.users = {} 25 self.main_room = ChatRoom(self) 26 27 def handle_accept(self): 28 conn, addr = self.accept() 29 ChatSession(self, conn) 30 31 32 class ChatSession(asynchat.async_chat): 33 """ 34 負責和客戶端通信 35 """ 36 def __init__(self, server, sock): 37 asynchat.async_chat.__init__(self, sock) 38 self.server = server 39 self.set_terminator(b'\n') 40 self.data = [] 41 self.name = None 42 self.enter(LoginRoom(server)) 43 44 def enter(self, room): 45 # 從當前房間移除自身,然后添加到指定房間 46 try: 47 cur = self.room 48 except AttributeError: 49 pass 50 else: 51 cur.remove(self) 52 self.room = room 53 room.add(self) 54 55 def collect_incoming_data(self, data): 56 # 接收客戶端的數據 57 self.data.append(data.decode("utf-8")) 58 59 def found_terminator(self): 60 # 當客戶端的一條數據結束時的處理 61 line = ''.join(self.data) 62 self.data = [] 63 try: 64 self.room.handle(self, line.encode("utf-8")) 65 # 退出聊天室的處理 66 except EndSession: 67 self.handle_close() 68 69 def handle_close(self): 70 # 當 session 關閉時,將進入 LogoutRoom 71 asynchat.async_chat.handle_close(self) 72 self.enter(LogoutRoom(self.server)) 73 74 75 class CommandHandler: 76 """ 77 命令處理類 78 """ 79 def unknown(self, session, cmd): 80 # 響應未知命令 81 # 通過 aynchat.async_chat.push 方法發送消息 82 session.push(('Unknown command {} \n'.format(cmd)).encode("utf-8")) 83 84 def handle(self, session, line): 85 line = line.decode() 86 # 命令處理 87 if not line.strip(): 88 return 89 parts = line.split(' ', 1) 90 cmd = parts[0] 91 try: 92 line = parts[1].strip() 93 except IndexError: 94 line = '' 95 # 通過協議代碼執行相應的方法 96 method = getattr(self, 'do_' + cmd, None) 97 try: 98 method(session, line) 99 except TypeError: 100 self.unknown(session, cmd) 101 102 103 class Room(CommandHandler): 104 """ 105 包含多個用戶的環境,負責基本的命令處理和廣播 106 """ 107 def __init__(self, server): 108 self.server = server 109 self.sessions = [] 110 111 def add(self, session): 112 # 一個用戶進入房間 113 self.sessions.append(session) 114 115 def remove(self, session): 116 # 一個用戶離開房間 117 self.sessions.remove(session) 118 119 def broadcast(self, line): 120 # 向所有的用戶發送指定消息 121 # 使用 asynchat.asyn_chat.push 方法發送數據 122 for session in self.sessions: 123 session.push(line) 124 125 def do_logout(self, session, line): 126 # 退出房間 127 raise EndSession 128 129 130 class LoginRoom(Room): 131 """ 132 處理登錄用戶 133 """ 134 def add(self, session): 135 # 用戶連接成功的回應 136 Room.add(self, session) 137 # 使用 asynchat.asyn_chat.push 方法發送數據 138 session.push(b'Connect Success') 139 140 def do_login(self, session, line): 141 # 用戶登錄邏輯 142 name = line.strip() 143 # 獲取用戶名稱 144 if not name: 145 session.push(b'UserName Empty') 146 # 檢查是否有同名用戶 147 elif name in self.server.users: 148 session.push(b'UserName Exist') 149 # 用戶名檢查成功后,進入主聊天室 150 else: 151 session.name = name 152 session.enter(self.server.main_room) 153 154 155 class LogoutRoom(Room): 156 """ 157 處理退出用戶 158 """ 159 def add(self, session): 160 # 從服務器中移除 161 try: 162 del self.server.users[session.name] 163 except KeyError: 164 pass 165 166 167 class ChatRoom(Room): 168 """ 169 聊天用的房間 170 """ 171 def add(self, session): 172 # 廣播新用戶進入 173 session.push(b'Login Success') 174 self.broadcast((session.name + ' has entered the room.\n').encode("utf-8")) 175 self.server.users[session.name] = session 176 Room.add(self, session) 177 178 def remove(self, session): 179 # 廣播用戶離開 180 Room.remove(self, session) 181 self.broadcast((session.name + ' has left the room.\n').encode("utf-8")) 182 183 def do_say(self, session, line): 184 # 客戶端發送消息 185 self.broadcast((session.name + ': ' + line + '\n').encode("utf-8")) 186 187 def do_noone_say(self, session, line): 188 # 圖靈回復消息 189 self.broadcast((line + '\n').encode("utf-8")) 190 191 def do_chatbot_say(self, session, line): 192 # 圖靈回復消息 193 self.broadcast(('ChatBot: ' + line + '\n').encode("utf-8")) 194 195 def do_tuling_say(self, session, line): 196 # 圖靈回復消息 197 self.broadcast(('Tuling: ' + line + '\n').encode("utf-8")) 198 199 def do_look(self, session, line): 200 # 查看在線用戶 201 session.push(b'All Online Users Are:\n') 202 for other in self.sessions: 203 session.push((other.name + '\n').encode("utf-8")) 204 205 206 if __name__ == '__main__': 207 for i in range(len(PORT)): 208 ChatServer(PORT[i]) 209 print("Chat server run at '127.0.0.1:{0}'".format(PORT[i])) 210 try: 211 asyncore.loop() 212 except KeyboardInterrupt: 213 print("Chat server exit")
3.訓練chatbot的TrainChat.py
主要用來訓練chatbot機器人,數據保存在本地sqlite數據庫(如果沒有數據庫自動創建)
個人學習此數據足以,作為企業可改為mongodb保存數據,速度會有保障
1 #!/usr/bin/python 2 # -*- coding: utf-8 -*- 3 from chatterbot import ChatBot 4 from chatterbot.trainers import ListTrainer 5 from chatterbot.trainers import ChatterBotCorpusTrainer 6 7 8 my_bot = ChatBot("Training demo", 9 database="./db.sqlite3") 10 11 # 直接寫語句訓練 12 my_bot.set_trainer(ListTrainer) 13 my_bot.train(["你叫什么名字?", "我叫小白兔!", ]) 14 my_bot.train([ 15 "Test1", 16 "Test2", 17 "Test3", 18 "Test4", 19 ]) 20 21 # 使用自定義語句訓練它 22 my_bot.set_trainer(ChatterBotCorpusTrainer) 23 my_bot.train("chatterbot.corpus.mytrain") 24 # while True: 25 # print(my_bot.get_response(input("user:")))
4.訓練chatbot的語料庫
提供了兩種語料訓練方法
(1)TrainChat.py里面可以直接寫訓練語句,也可開啟通過聊天時候的語句自動訓練
(2)自定義語料庫訓練,自定義語料格式,直接參照chatbot提供的一些寫就行
找到安裝chatbot后默認提供的中文語料格式D:\Python\Lib\site-packages\chatterbot_corpus\data\chinese
打開后格式就有了,這里我們按照格式新增一個mytrain文件夾,寫入自己的語料文件,如我寫的phone.yml

1 categories: 2 - phone 3 conversations: 4 - - iPhoneX 5 - iPhone X是Apple(蘋果公司)於北京時間2017年9月13日凌晨1點,在Apple Park新總部的史蒂夫·喬布斯劇院會上發布的新機型。其中“X”是羅馬數字“10”的意思,代表着蘋果向iPhone問世十周年致敬。iPhone X屬於高端版機型,采用全新設計,搭載色彩銳利的OLED屏幕,配備升級后的相機,使用3D面部識別(Face ID)傳感器解鎖手機,支持AirPower(空中能量)無線充電。分為64GB、256GB兩個版本,中國大陸起售價8388人民幣,美國起售價999美元,2017年10月27日預售,11月3號正式開賣。 6 - - 三星Galaxy S6 7 - 三星Galaxy S6是三星公司(SAMSUNG)在2015年3月2日推出的一款手機,已於2015年4月11日正式上市。\n三星Galaxy S6采用5.1英寸屏幕,2560×1440像素,像素密度高達573ppi,內置Exynos 7420八核64位處理器,能夠提供更強的性能以及更低的功耗;采用前500W像素+后1600W像素的雙鏡頭設計,均支持F1.9大光圈,感光元件是索尼IMX 240,支持OIS光學防抖和自動HDR技術。 8 - - 華為P8 9 - P系列是華為手機中的旗艦系列,到2017年1月,共有6款機型:P1、P2、P6、P7、P8、P8 MAX、P9、P9 Plus。從2012年1月11日在美國拉斯維加斯發布全球最薄6.68毫米的P1開始,P系列便創立了以驚艷ID設計融合強大均衡軟硬件配置為主的旗艦產品地位。之后,華為於2013年6月18日發布P6,2014年5月7日發布P7,均分別輕松創下了數百萬銷量的佳績,一舉奠定了華為在國內領先、國際一流的品牌地位
(3)說明:評論區讀者朋友【逾行】說這里會報錯,原因應該是包chatbot升級后,修改了寫法,畢竟我這個博客寫的有兩年的時間了,讀者朋友可以按評論區的修改試試
5.錄音並保存文件recorder.py
提供錄音功能並將錄音文件保存在本地
1 #!/usr/bin/python3 2 # -*- coding: utf-8 -*- 3 4 from pyaudio import PyAudio, paInt16 5 import numpy as np 6 from datetime import datetime 7 import wave 8 import sys 9 import time 10 11 12 class Recoder: 13 NUM_SAMPLES = 2000 # py audio內置緩沖大小 14 SAMPLING_RATE = 8000 # 取樣頻率 15 LEVEL = 500 # 聲音保存的閾值 16 COUNT_NUM = 20 # NUM_SAMPLES個取樣之內出現COUNT_NUM個大於LEVEL的取樣則記錄聲音 17 SAVE_LENGTH = 8 # 聲音記錄的最小長度:SAVE_LENGTH * NUM_SAMPLES 個取樣 18 TIME_COUNT = 10 # 錄音時間,單位s 19 20 Voice_String = [] 21 22 def savewav(self, filename): 23 wf = wave.open(filename, 'wb') 24 wf.setnchannels(1) 25 wf.setsampwidth(2) 26 wf.setframerate(self.SAMPLING_RATE) 27 wf.writeframes(np.array(self.Voice_String).tostring()) 28 # wf.writeframes(self.Voice_String.decode()) 29 wf.close() 30 31 def recoder(self): 32 pa = PyAudio() 33 stream = pa.open(format=paInt16, channels=1, rate=self.SAMPLING_RATE, input=True, 34 frames_per_buffer=self.NUM_SAMPLES) 35 save_count = 0 36 save_buffer = [] 37 time_count = self.TIME_COUNT 38 39 while True: 40 time_count -= 1 41 # print time_count 42 # 讀入NUM_SAMPLES個取樣 43 string_audio_data = stream.read(self.NUM_SAMPLES) 44 # 將讀入的數據轉換為數組 45 audio_data = np.fromstring(string_audio_data, dtype=np.short) 46 # 計算大於LEVEL的取樣的個數 47 large_sample_count = np.sum( audio_data > self.LEVEL ) 48 print(np.max(audio_data)) 49 # 如果個數大於COUNT_NUM,則至少保存SAVE_LENGTH個塊 50 if large_sample_count > self.COUNT_NUM: 51 save_count = self.SAVE_LENGTH 52 else: 53 save_count -= 1 54 55 if save_count < 0: 56 save_count = 0 57 58 if save_count > 0: 59 # 將要保存的數據存放到save_buffer中 60 # print save_count > 0 and time_count >0 61 save_buffer.append(string_audio_data ) 62 else: 63 # print save_buffer 64 # 將save_buffer中的數據寫入WAV文件,WAV文件的文件名是保存的時刻 65 # print "debug" 66 if len(save_buffer) > 0 : 67 self.Voice_String = save_buffer 68 save_buffer = [] 69 print("Recode a piece of voice successfully!") 70 return True 71 if time_count == 0: 72 if len(save_buffer)>0: 73 self.Voice_String = save_buffer 74 save_buffer = [] 75 print("Recode a piece of voice successfully!") 76 return True 77 else: 78 return False 79 80 81 def recording(): 82 r = Recoder() 83 r.recoder() 84 r.savewav(r"E:\Python_Doc\voice_say\say_voice.wav")
6. chatbot.py
提供播放音頻文件
調用圖靈Tuling接口返回文本信息
調用chatbot返回文本信息
調用百度api語音識別
調用百度api轉文本為語音(有兩個百度api都可用,第一個不用密匙),其中chatbot的數據庫配置要和TrainChat.py中配置的名稱一致
1 import pygame 2 from chatterbot import ChatBot 3 import requests 4 import json 5 from config import * 6 import time 7 import os 8 import random 9 import urllib.request 10 import base64 11 12 13 # 初始化百度返回的音頻文件地址,后面會變為全局變量,隨需改變 14 mp3_url = 'E:\Python_Doc\\voice_du\\voice_ss.mp3' 15 16 17 # 播放Mp3文件 18 def play_mp3(): 19 # 接受服務器的消息 20 pygame.mixer.init() 21 pygame.mixer.music.load(mp3_url) 22 pygame.mixer.music.play() 23 while pygame.mixer.music.get_busy(): 24 time.sleep(1) 25 pygame.mixer.music.stop() 26 pygame.mixer.quit() 27 28 29 # 刪除聲音文件 30 def remove_voice(): 31 path = r"E:\Python_Doc\voice_du" 32 for i in os.listdir(path): 33 path_file = os.path.join(path, i) 34 try: 35 os.remove(path_file) 36 except: 37 continue 38 39 40 # 圖靈自動回復 41 def tuling(info): 42 url = tuling_url + "?key=%s&info=%s" % (tuling_app_key, info) 43 content = requests.get(url, headers=headers) 44 answer = json.loads(content.text) 45 return answer['text'] 46 47 48 # 聊天機器人回復 49 def chatbot(info): 50 my_bot = ChatBot("", read_only=True, 51 database="./db.sqlite3") 52 res = my_bot.get_response(info) 53 return str(res) 54 55 56 # 百度講文本轉為聲音文件保存在本地 tts地址,無需token實時認證 57 def baidu_api(answer): 58 api_url = '{11}?idx={0}&tex={1}&cuid={2}&cod={3}&lan={4}&ctp={5}&pdt={6}&spd={7}&per={8}&vol={9}&pit={10}'\ 59 .format(baidu_api_set["idx"], answer, baidu_api_set["cuid"], baidu_api_set["cod"], baidu_api_set["lan"], 60 baidu_api_set["ctp"], baidu_api_set["pdt"], baidu_api_set["spd"], baidu_api_set["per"], 61 baidu_api_set["vol"], baidu_api_set["pit"], baidu_api_url) 62 res = requests.get(api_url, headers=headers2) 63 # 本地Mp3語音文件保存位置 64 iname = random.randrange(1, 99999) 65 global mp3_url 66 mp3_url = 'E:\Python_Doc\\voices\\voice_tts' + str(iname) + '.mp3' 67 with open(mp3_url, 'wb') as f: 68 f.write(res.content) 69 70 71 # 百度講文本轉為聲音文件保存在本地 方法2 tsn地址 72 def baidu_api2(answer): 73 # 獲取access_token 74 token = getToken() 75 get_url = baidu_api_url2 % (urllib.parse.quote(answer), "test", token) 76 voice_data = urllib.request.urlopen(get_url).read() 77 # 本地Mp3語音文件保存位置 78 name = random.randrange(1, 99999) 79 global mp3_url 80 mp3_url = 'E:\Python_Doc\\voice_du\\voice_tsn' + str(name) + '.mp3' 81 voice_fp = open(mp3_url, 'wb+') 82 voice_fp.write(voice_data) 83 voice_fp.close() 84 return 85 86 87 # 百度語音轉文本 88 def getText(filename): 89 # 獲取access_token 90 token = getToken() 91 data = {} 92 data['format'] = 'wav' 93 data['rate'] = 16000 94 data['channel'] = 1 95 data['cuid'] = str(random.randrange(123456, 999999)) 96 data['token'] = token 97 wav_fp = open(filename, 'rb') 98 voice_data = wav_fp.read() 99 data['len'] = len(voice_data) 100 data['speech'] = base64.b64encode(voice_data).decode('utf-8') 101 post_data = json.dumps(data) 102 # 語音識別的api url 103 upvoice_url = 'http://vop.baidu.com/server_api' 104 r_data = urllib.request.urlopen(upvoice_url, data=bytes(post_data, encoding="utf-8")).read() 105 print(json.loads(r_data)) 106 err = json.loads(r_data)['err_no'] 107 if err == 0: 108 return json.loads(r_data)['result'][0] 109 else: 110 return json.loads(r_data)['err_msg'] 111 112 113 # 獲取百度API調用的認證,實時生成,因為有時間限制 114 def getToken(): 115 # token認證的url 116 api_url = "https://openapi.baidu.com/oauth/2.0/token?" \ 117 "grant_type=client_credentials&client_id=%s&client_secret=%s" 118 token_url = api_url % (BaiDu_API_Key_GetVoi, BaiDu_Secret_Key_GetVoi) 119 r_str = urllib.request.urlopen(token_url).read() 120 token_data = json.loads(r_str) 121 token_str = token_data['access_token'] 122 return token_str
7.client.py
提供登錄窗口,聊天窗口,已及響應事件
say按鈕綁定sayDown錄音和sayUp獲取語音文本並發送兩個事件
Users顯示當前房間所有用戶...
1 import wx 2 import telnetlib 3 from time import sleep 4 import _thread as thread 5 import time 6 import os 7 from chatbot import baidu_api2, chatbot, tuling, play_mp3, remove_voice, getText 8 from config import BOTS, BOT, default_server, VOICE_SWITCH 9 from recorder import recording 10 11 12 bot_use = BOT 13 14 15 class LoginFrame(wx.Frame): 16 """ 17 登錄窗口 18 """ 19 def __init__(self, parent, id, title, size): 20 # 初始化,添加控件並綁定事件 21 wx.Frame.__init__(self, parent, id, title) 22 self.SetSize(size) 23 self.Center() 24 self.serverAddressLabel = wx.StaticText(self, label="Server Address", pos=(15, 40), size=(120, 25)) 25 self.userNameLabel = wx.StaticText(self, label="UserName", pos=(45, 90), size=(120, 25)) 26 self.serverAddress = wx.TextCtrl(self, value=default_server, 27 pos=(120, 37), size=(150, 25), style=wx.TE_PROCESS_ENTER) 28 self.userName = wx.TextCtrl(self, pos=(120, 87), size=(150, 25), style=wx.TE_PROCESS_ENTER) 29 self.loginButton = wx.Button(self, label='Login', pos=(50, 145), size=(90, 30)) 30 self.exitButton = wx.Button(self, label='Exit', pos=(180, 145), size=(90, 30)) 31 # 綁定登錄方法 32 self.loginButton.Bind(wx.EVT_BUTTON, self.login) 33 # 綁定退出方法 34 self.exitButton.Bind(wx.EVT_BUTTON, self.exit) 35 # 服務器輸入框Tab事件 36 self.serverAddress.SetFocus() 37 self.Bind(wx.EVT_TEXT_ENTER, self.usn_focus, self.serverAddress) 38 # 用戶名回車登錄 39 self.Bind(wx.EVT_TEXT_ENTER, self.login, self.userName) 40 self.Show() 41 42 # 回車調到用戶名輸入欄 43 def usn_focus(self, event): 44 self.userName.SetFocus() 45 46 def login(self, event): 47 # 登錄處理 48 try: 49 serverAddress = self.serverAddress.GetLineText(0).split(':') 50 con.open(serverAddress[0], port=int(serverAddress[1]), timeout=10) 51 response = con.read_some() 52 if response != b'Connect Success': 53 self.showDialog('Error', 'Connect Fail!', (200, 100)) 54 return 55 con.write(('login ' + str(self.userName.GetLineText(0)) + '\n').encode("utf-8")) 56 response = con.read_some() 57 if response == b'UserName Empty': 58 self.showDialog('Error', 'UserName Empty!', (200, 100)) 59 elif response == b'UserName Exist': 60 self.showDialog('Error', 'UserName Exist!', (200, 100)) 61 else: 62 self.Close() 63 ChatFrame(None, 2, title='當前用戶:'+str(self.userName.GetLineText(0)), size=(515, 400)) 64 except Exception: 65 self.showDialog('Error', 'Connect Fail!', (95, 20)) 66 67 def exit(self, event): 68 self.Close() 69 70 # 顯示錯誤信息對話框 71 def showDialog(self, title, content, size): 72 dialog = wx.Dialog(self, title=title, size=size) 73 dialog.Center() 74 wx.StaticText(dialog, label=content) 75 dialog.ShowModal() 76 77 78 class ChatFrame(wx.Frame): 79 """ 80 聊天窗口 81 """ 82 def __init__(self, parent, id, title, size): 83 # 初始化,添加控件並綁定事件 84 wx.Frame.__init__(self, parent, id, title, style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | 85 wx.DEFAULT_FRAME_STYLE) 86 self.SetSize(size) 87 self.Center() 88 self.chatFrame = wx.TextCtrl(self, pos=(5, 5), size=(490, 310), style=wx.TE_MULTILINE | wx.TE_READONLY) 89 self.sayButton = wx.Button(self, label="Say", pos=(5, 320), size=(58, 25)) 90 self.message = wx.TextCtrl(self, pos=(65, 320), size=(240, 25), style=wx.TE_PROCESS_ENTER) 91 self.sendButton = wx.Button(self, label="Send", pos=(310, 320), size=(58, 25)) 92 self.usersButton = wx.Button(self, label="Users", pos=(373, 320), size=(58, 25)) 93 self.closeButton = wx.Button(self, label="Close", pos=(436, 320), size=(58, 25)) 94 # 發送按鈕綁定發送消息方法 95 self.sendButton.Bind(wx.EVT_BUTTON, self.send) 96 # 輸入框回車發送信息 97 self.message.SetFocus() 98 # 發送消息 99 self.sayButton.Bind(wx.EVT_LEFT_DOWN, self.sayDown) 100 self.sayButton.Bind(wx.EVT_LEFT_UP, self.sayUp) 101 # 發送消息 102 self.Bind(wx.EVT_TEXT_ENTER, self.send, self.message) 103 # Users按鈕綁定獲取在線用戶數量方法 104 self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers) 105 # 關閉按鈕綁定關閉方法 106 self.closeButton.Bind(wx.EVT_BUTTON, self.close) 107 thread.start_new_thread(self.receive, ()) 108 # self.ShowFullScreen(True) 109 self.Show() 110 111 def sayDown(self, event): 112 thread.start_new_thread(recording, ()) 113 # print("ON") 114 115 def sayUp(self, event): 116 sayText = getText(r"E:\Python_Doc\voice_say\say_voice.wav") 117 self.message.AppendText(str(sayText)) 118 self.send(self) 119 120 def send(self, event): 121 # 發送消息 122 message = str(self.message.GetLineText(0)).strip() 123 global bot_use 124 if message != '': 125 if message == "chatbot": 126 bot_use = "ChatBot" 127 self.message.Clear() 128 con.write(('noone_say You have been changed ChatBot-Chat' + '\n').encode("utf-8")) 129 return 130 elif message == "tuling": 131 bot_use = "TuLing" 132 self.message.Clear() 133 con.write(('noone_say You have been changed TuLing-Chat' + '\n').encode("utf-8")) 134 return 135 elif message == "user": 136 bot_use = "User" 137 self.message.Clear() 138 con.write(('noone_say You have been changed User-Chat' + '\n').encode("utf-8")) 139 return 140 con.write(('say ' + message + '\n').encode("utf-8")) 141 self.message.Clear() 142 # 機器人回復 143 if bot_use == "ChatBot": 144 answer = chatbot(message) 145 con.write(('chatbot_say ' + answer + '\n').encode("utf-8")) 146 elif bot_use == "TuLing": 147 answer = tuling(message) 148 con.write(('tuling_say ' + answer + '\n').encode("utf-8")) 149 elif bot_use == "User": 150 return 151 152 if VOICE_SWITCH: 153 # 寫本地音樂文件 154 baidu_api2(answer) 155 # 新建線程播放音樂 156 thread.start_new_thread(play_mp3, ()) 157 return 158 159 def lookUsers(self, event): 160 # 查看當前在線用戶 161 con.write(b'look\n') 162 163 def close(self, event): 164 # 關閉窗口 165 thread.start_new_thread(remove_voice, ()) 166 con.write(b'logout\n') 167 con.close() 168 self.Close() 169 170 def receive(self): 171 # 接受服務器的消息 172 while True: 173 sleep(0.6) 174 result = con.read_very_eager() 175 if result != '': 176 self.chatFrame.AppendText(result) 177 178 179 if __name__ == '__main__': 180 app = wx.App() 181 con = telnetlib.Telnet() 182 LoginFrame(None, -1, title="Login", size=(320, 250)) 183 app.MainLoop()
8.config配置文件
百度API的KEY等內容也可自行去對應官網申請,本文提供僅供學習使用
1 # 默認輸入的服務器地址,測試時候使用,避免登錄總是輸入地址麻煩 2 default_server = "127.0.0.1:1" 3 4 # 定義服務器端口,一個端口一個房間 5 PORT = range(1, 3) 6 7 # 圖靈Tuling機器人還是ChatBot聊天機器人選擇 8 BOTS = ["TuLing", "ChatBot", "User"] 9 BOT = BOTS[2] 10 11 # 瀏覽器請求頭文件 12 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 ' 13 '(KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36', } 14 headers2 = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 ' 15 '(KHTML, like Gecko)Chrome/62.0.3202.94 Safari/537.36'} 16 17 # 圖靈密匙,自動回復地址,選擇的key不同,tuling機器人的回答也各不相同 18 tuling_app_key = "e5ccc9c7c8834ec3b08940e290ff1559" 19 tuling_app_key2 = "4bc32d41c10be18627438ae45eb839ac" 20 tuling_url = "http://www.tuling123.com/openapi/api" 21 22 # 語音保存播放開關 23 VOICE_SWITCH = True 24 25 # 百度文本轉語音地址和配置 tts地址 26 baidu_api_url = "http://tts.baidu.com/text2audio" 27 baidu_api_set = {"idx": 1, "cuid": "baidu_speech_demo", "cod": 2, 28 "lan": "zh", "ctp": 1, "pdt": 1, "spd": 4, "per": 4, "vol": 5, "pit": 5} 29 30 # 百度文字轉語音 tsn地址 31 baidu_api_url2 = "http://tsn.baidu.com/text2audio?tex=%s&lan=zh&cuid=%s&ctp=1&tok=%s" 32 BaiDu_API_Key_GetVoi = "2NagVAULCYCnOnamrc8MNUPc" 33 BaiDu_Secret_Key_GetVoi = "af4860b64e77d187643db05ccdb060e4" 34 35 # 百度語音識別 36 BaiDu_App_ID = "10623076" 37 BaiDu_API_Key = "2NagVAULCYCnOnamrc8MNUPc" 38 BaiDu_Secret_Key = "af4860b64e77d187643db05ccdb060e4" 39 BaiDu_OpenApi_Url = "https://openapi.baidu.com/oauth/2.0/token" \ 40 "?grant_type=client_credentials&client_id=%&client_secret=%"
三、總結
此文在本地語音保存解析過程有時間差問題,讀者可自行優化。
修改日志:將整個源碼整合成一個項目ChatRoom,並傳到GitHub:https://github.com/Vrapile/ChatRoom.git

trainer = ListTrainer(chatbot)
trainer.train([
"text1",
"text.2",
])
my_bot = ChatBot("Training demo", database_uri='sqlite:///db.sqlite3')