Python實現機器人語音聊天


一、前言說明

  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")
server.py

 

 

  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:")))
TrainChat.py

 

  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,均分別輕松創下了數百萬銷量的佳績,一舉奠定了華為在國內領先、國際一流的品牌地位
phone.yml

 

  (3)說明:評論區讀者朋友【逾行】說這里會報錯,原因應該是包chatbot升級后,修改了寫法,畢竟我這個博客寫的有兩年的時間了,讀者朋友可以按評論區的修改試試  

  chatbot = ChatBot('Training demo')
  trainer = ListTrainer(chatbot)
  trainer.train([
    "text1",
    "text.2",
  ])
 
  博主最近更新Python3.6到Python3.8,chatterbot也同步更新,證實上述寫法是OK的,需要刪掉之前生成的db.sqlite3,否則會報錯,改后的文件內容如下
#!/usr/bin/python
# -*- coding: utf-8 -*-
from chatterbot import ChatBot
from chatterbot.trainers import ListTrainer


my_bot = ChatBot("Training demo", database_uri='sqlite:///db.sqlite3')

trainer = ListTrainer(my_bot)
# 直接寫語句訓練
trainer.train(["你叫什么名字?", "我叫小白兔!", ])
trainer.train([
"Test1",
"Test2",
"Test3",
"Test4",
])
# 使用自定義語句訓練它
trainer.train("chatterbot.corpus.mytrain")

 

   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")
recorder.py

 

  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
chatbot.py

  

  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()
client.py

  

  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=%"
config.py

 

 三、總結

  此文在本地語音保存解析過程有時間差問題,讀者可自行優化。

  修改日志:將整個源碼整合成一個項目ChatRoom,並傳到GitHub:https://github.com/Vrapile/ChatRoom.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM