一、前言说明

  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. class EndSession(Exception):
  7. pass
  8.  
  9. class ChatServer(asyncore.dispatcher):
  10. """
  11. 聊天服务器
  12. """
  13. def __init__(self, port):
  14. asyncore.dispatcher.__init__(self)
  15. # 创建socket
  16. self.create_socket()
  17. # 设置 socket 为可重用
  18. self.set_reuse_addr()
  19. # 监听端口
  20. self.bind(('', port))
  21. self.listen(5)
  22. self.users = {}
  23. self.main_room = ChatRoom(self)
  24.  
  25. def handle_accept(self):
  26. conn, addr = self.accept()
  27. ChatSession(self, conn)
  28.  
  29. class ChatSession(asynchat.async_chat):
  30. """
  31. 负责和客户端通信
  32. """
  33. def __init__(self, server, sock):
  34. asynchat.async_chat.__init__(self, sock)
  35. self.server = server
  36. self.set_terminator(b'\n')
  37. self.data = []
  38. self.name = None
  39. self.enter(LoginRoom(server))
  40.  
  41. def enter(self, room):
  42. # 从当前房间移除自身,然后添加到指定房间
  43. try:
  44. cur = self.room
  45. except AttributeError:
  46. pass
  47. else:
  48. cur.remove(self)
  49. self.room = room
  50. room.add(self)
  51.  
  52. def collect_incoming_data(self, data):
  53. # 接收客户端的数据
  54. self.data.append(data.decode("utf-8"))
  55.  
  56. def found_terminator(self):
  57. # 当客户端的一条数据结束时的处理
  58. line = ''.join(self.data)
  59. self.data = []
  60. try:
  61. self.room.handle(self, line.encode("utf-8"))
  62. # 退出聊天室的处理
  63. except EndSession:
  64. self.handle_close()
  65.  
  66. def handle_close(self):
  67. # 当 session 关闭时,将进入 LogoutRoom
  68. asynchat.async_chat.handle_close(self)
  69. self.enter(LogoutRoom(self.server))
  70.  
  71. class CommandHandler:
  72. """
  73. 命令处理类
  74. """
  75. def unknown(self, session, cmd):
  76. # 响应未知命令
  77. # 通过 aynchat.async_chat.push 方法发送消息
  78. session.push(('Unknown command {} \n'.format(cmd)).encode("utf-8"))
  79.  
  80. def handle(self, session, line):
  81. line = line.decode()
  82. # 命令处理
  83. if not line.strip():
  84. return
  85. parts = line.split(' ', 1)
  86. cmd = parts[0]
  87. try:
  88. line = parts[1].strip()
  89. except IndexError:
  90. line = ''
  91. # 通过协议代码执行相应的方法
  92. method = getattr(self, 'do_' + cmd, None)
  93. try:
  94. method(session, line)
  95. except TypeError:
  96. self.unknown(session, cmd)
  97.  
  98. class Room(CommandHandler):
  99. """
  100. 包含多个用户的环境,负责基本的命令处理和广播
  101. """
  102. def __init__(self, server):
  103. self.server = server
  104. self.sessions = []
  105.  
  106. def add(self, session):
  107. # 一个用户进入房间
  108. self.sessions.append(session)
  109.  
  110. def remove(self, session):
  111. # 一个用户离开房间
  112. self.sessions.remove(session)
  113.  
  114. def broadcast(self, line):
  115. # 向所有的用户发送指定消息
  116. # 使用 asynchat.asyn_chat.push 方法发送数据
  117. for session in self.sessions:
  118. session.push(line)
  119.  
  120. def do_logout(self, session, line):
  121. # 退出房间
  122. raise EndSession
  123.  
  124. class LoginRoom(Room):
  125. """
  126. 处理登录用户
  127. """
  128. def add(self, session):
  129. # 用户连接成功的回应
  130. Room.add(self, session)
  131. # 使用 asynchat.asyn_chat.push 方法发送数据
  132. session.push(b'Connect Success')
  133.  
  134. def do_login(self, session, line):
  135. # 用户登录逻辑
  136. name = line.strip()
  137. # 获取用户名称
  138. if not name:
  139. session.push(b'UserName Empty')
  140. # 检查是否有同名用户
  141. elif name in self.server.users:
  142. session.push(b'UserName Exist')
  143. # 用户名检查成功后,进入主聊天室
  144. else:
  145. session.name = name
  146. session.enter(self.server.main_room)
  147.  
  148. class LogoutRoom(Room):
  149. """
  150. 处理退出用户
  151. """
  152. def add(self, session):
  153. # 从服务器中移除
  154. try:
  155. del self.server.users[session.name]
  156. except KeyError:
  157. pass
  158.  
  159. class ChatRoom(Room):
  160. """
  161. 聊天用的房间
  162. """
  163. def add(self, session):
  164. # 广播新用户进入
  165. session.push(b'Login Success')
  166. self.broadcast((session.name + ' has entered the room.\n').encode("utf-8"))
  167. self.server.users[session.name] = session
  168. Room.add(self, session)
  169.  
  170. def remove(self, session):
  171. # 广播用户离开
  172. Room.remove(self, session)
  173. self.broadcast((session.name + ' has left the room.\n').encode("utf-8"))
  174.  
  175. def do_say(self, session, line):
  176. # 客户端发送消息
  177. self.broadcast((session.name + ': ' + line + '\n').encode("utf-8"))
  178.  
  179. def do_noone_say(self, session, line):
  180. # 图灵回复消息
  181. self.broadcast((line + '\n').encode("utf-8"))
  182.  
  183. def do_chatbot_say(self, session, line):
  184. # 图灵回复消息
  185. self.broadcast(('ChatBot: ' + line + '\n').encode("utf-8"))
  186.  
  187. def do_tuling_say(self, session, line):
  188. # 图灵回复消息
  189. self.broadcast(('Tuling: ' + line + '\n').encode("utf-8"))
  190.  
  191. def do_look(self, session, line):
  192. # 查看在线用户
  193. session.push(b'All Online Users Are:\n')
  194. for other in self.sessions:
  195. session.push((other.name + '\n').encode("utf-8"))
  196.  
  197. if __name__ == '__main__':
  198. for i in range(len(PORT)):
  199. ChatServer(PORT[i])
  200. print("Chat server run at '127.0.0.1:{0}'".format(PORT[i]))
  201. try:
  202. asyncore.loop()
  203. except KeyboardInterrupt:
  204. 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. my_bot = ChatBot("Training demo",
  8. database="./db.sqlite3")
  9.  
  10. # 直接写语句训练
  11. my_bot.set_trainer(ListTrainer)
  12. my_bot.train(["你叫什么名字?", "我叫小白兔!", ])
  13. my_bot.train([
  14. "Test1",
  15. "Test2",
  16. "Test3",
  17. "Test4",
  18. ])
  19.  
  20. # 使用自定义语句训练它
  21. my_bot.set_trainer(ChatterBotCorpusTrainer)
  22. my_bot.train("chatterbot.corpus.mytrain")
  23. # while True:
  24. # 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 XApple(苹果公司)于北京时间2017913日凌晨1点,在Apple Park新总部的史蒂夫·乔布斯剧院会上发布的新机型。其中“X”是罗马数字“10”的意思,代表着苹果向iPhone问世十周年致敬。iPhone X属于高端版机型,采用全新设计,搭载色彩锐利的OLED屏幕,配备升级后的相机,使用3D面部识别(Face ID)传感器解锁手机,支持AirPower(空中能量)无线充电。分为64GB256GB两个版本,中国大陆起售价8388人民币,美国起售价999美元,20171027日预售,113号正式开卖。
  6. - - 三星Galaxy S6
  7. - 三星Galaxy S6是三星公司(SAMSUNG)在201532日推出的一款手机,已于2015411日正式上市。\n三星Galaxy S6采用5.1英寸屏幕,2560×1440像素,像素密度高达573ppi,内置Exynos 7420八核64位处理器,能够提供更强的性能以及更低的功耗;采用前500W像素+后1600W像素的双镜头设计,均支持F1.9大光圈,感光元件是索尼IMX 240,支持OIS光学防抖和自动HDR技术。
  8. - - 华为P8
  9. - P系列是华为手机中的旗舰系列,到20171月,共有6款机型:P1P2P6P7P8P8 MAXP9P9 Plus。从2012111日在美国拉斯维加斯发布全球最薄6.68毫米的P1开始,P系列便创立了以惊艳ID设计融合强大均衡软硬件配置为主的旗舰产品地位。之后,华为于2013618日发布P6201457日发布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,否则会报错,改后的文件内容如下
  1. #!/usr/bin/python
    # -*- coding: utf-8 -*-
    from chatterbot import ChatBot
    from chatterbot.trainers import ListTrainer
  2.  
  1. my_bot = ChatBot("Training demo", database_uri='sqlite:///db.sqlite3')
  1. trainer = ListTrainer(my_bot)
  1. # 直接写语句训练
    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. class Recoder:
  12. NUM_SAMPLES = 2000 # py audio内置缓冲大小
  13. SAMPLING_RATE = 8000 # 取样频率
  14. LEVEL = 500 # 声音保存的阈值
  15. COUNT_NUM = 20 # NUM_SAMPLES个取样之内出现COUNT_NUM个大于LEVEL的取样则记录声音
  16. SAVE_LENGTH = 8 # 声音记录的最小长度:SAVE_LENGTH * NUM_SAMPLES 个取样
  17. TIME_COUNT = 10 # 录音时间,单位s
  18.  
  19. Voice_String = []
  20.  
  21. def savewav(self, filename):
  22. wf = wave.open(filename, 'wb')
  23. wf.setnchannels(1)
  24. wf.setsampwidth(2)
  25. wf.setframerate(self.SAMPLING_RATE)
  26. wf.writeframes(np.array(self.Voice_String).tostring())
  27. # wf.writeframes(self.Voice_String.decode())
  28. wf.close()
  29.  
  30. def recoder(self):
  31. pa = PyAudio()
  32. stream = pa.open(format=paInt16, channels=1, rate=self.SAMPLING_RATE, input=True,
  33. frames_per_buffer=self.NUM_SAMPLES)
  34. save_count = 0
  35. save_buffer = []
  36. time_count = self.TIME_COUNT
  37.  
  38. while True:
  39. time_count -= 1
  40. # print time_count
  41. # 读入NUM_SAMPLES个取样
  42. string_audio_data = stream.read(self.NUM_SAMPLES)
  43. # 将读入的数据转换为数组
  44. audio_data = np.fromstring(string_audio_data, dtype=np.short)
  45. # 计算大于LEVEL的取样的个数
  46. large_sample_count = np.sum( audio_data > self.LEVEL )
  47. print(np.max(audio_data))
  48. # 如果个数大于COUNT_NUM,则至少保存SAVE_LENGTH个块
  49. if large_sample_count > self.COUNT_NUM:
  50. save_count = self.SAVE_LENGTH
  51. else:
  52. save_count -= 1
  53.  
  54. if save_count < 0:
  55. save_count = 0
  56.  
  57. if save_count > 0:
  58. # 将要保存的数据存放到save_buffer中
  59. # print save_count > 0 and time_count >0
  60. save_buffer.append(string_audio_data )
  61. else:
  62. # print save_buffer
  63. # 将save_buffer中的数据写入WAV文件,WAV文件的文件名是保存的时刻
  64. # print "debug"
  65. if len(save_buffer) > 0 :
  66. self.Voice_String = save_buffer
  67. save_buffer = []
  68. print("Recode a piece of voice successfully!")
  69. return True
  70. if time_count == 0:
  71. if len(save_buffer)>0:
  72. self.Voice_String = save_buffer
  73. save_buffer = []
  74. print("Recode a piece of voice successfully!")
  75. return True
  76. else:
  77. return False
  78.  
  79. def recording():
  80. r = Recoder()
  81. r.recoder()
  82. 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. mp3_url = 'E:\Python_Doc\\voice_du\\voice_ss.mp3'
  14.  
  15. # 播放Mp3文件
  16. def play_mp3():
  17. # 接受服务器的消息
  18. pygame.mixer.init()
  19. pygame.mixer.music.load(mp3_url)
  20. pygame.mixer.music.play()
  21. while pygame.mixer.music.get_busy():
  22. time.sleep(1)
  23. pygame.mixer.music.stop()
  24. pygame.mixer.quit()
  25.  
  26. # 删除声音文件
  27. def remove_voice():
  28. path = r"E:\Python_Doc\voice_du"
  29. for i in os.listdir(path):
  30. path_file = os.path.join(path, i)
  31. try:
  32. os.remove(path_file)
  33. except:
  34. continue
  35.  
  36. # 图灵自动回复
  37. def tuling(info):
  38. url = tuling_url + "?key=%s&info=%s" % (tuling_app_key, info)
  39. content = requests.get(url, headers=headers)
  40. answer = json.loads(content.text)
  41. return answer['text']
  42.  
  43. # 聊天机器人回复
  44. def chatbot(info):
  45. my_bot = ChatBot("", read_only=True,
  46. database="./db.sqlite3")
  47. res = my_bot.get_response(info)
  48. return str(res)
  49.  
  50. # 百度讲文本转为声音文件保存在本地 tts地址,无需token实时认证
  51. def baidu_api(answer):
  52. 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}'\
  53. .format(baidu_api_set["idx"], answer, baidu_api_set["cuid"], baidu_api_set["cod"], baidu_api_set["lan"],
  54. baidu_api_set["ctp"], baidu_api_set["pdt"], baidu_api_set["spd"], baidu_api_set["per"],
  55. baidu_api_set["vol"], baidu_api_set["pit"], baidu_api_url)
  56. res = requests.get(api_url, headers=headers2)
  57. # 本地Mp3语音文件保存位置
  58. iname = random.randrange(1, 99999)
  59. global mp3_url
  60. mp3_url = 'E:\Python_Doc\\voices\\voice_tts' + str(iname) + '.mp3'
  61. with open(mp3_url, 'wb') as f:
  62. f.write(res.content)
  63.  
  64. # 百度讲文本转为声音文件保存在本地 方法2 tsn地址
  65. def baidu_api2(answer):
  66. # 获取access_token
  67. token = getToken()
  68. get_url = baidu_api_url2 % (urllib.parse.quote(answer), "test", token)
  69. voice_data = urllib.request.urlopen(get_url).read()
  70. # 本地Mp3语音文件保存位置
  71. name = random.randrange(1, 99999)
  72. global mp3_url
  73. mp3_url = 'E:\Python_Doc\\voice_du\\voice_tsn' + str(name) + '.mp3'
  74. voice_fp = open(mp3_url, 'wb+')
  75. voice_fp.write(voice_data)
  76. voice_fp.close()
  77. return
  78.  
  79. # 百度语音转文本
  80. def getText(filename):
  81. # 获取access_token
  82. token = getToken()
  83. data = {}
  84. data['format'] = 'wav'
  85. data['rate'] = 16000
  86. data['channel'] = 1
  87. data['cuid'] = str(random.randrange(123456, 999999))
  88. data['token'] = token
  89. wav_fp = open(filename, 'rb')
  90. voice_data = wav_fp.read()
  91. data['len'] = len(voice_data)
  92. data['speech'] = base64.b64encode(voice_data).decode('utf-8')
  93. post_data = json.dumps(data)
  94. # 语音识别的api url
  95. upvoice_url = 'http://vop.baidu.com/server_api'
  96. r_data = urllib.request.urlopen(upvoice_url, data=bytes(post_data, encoding="utf-8")).read()
  97. print(json.loads(r_data))
  98. err = json.loads(r_data)['err_no']
  99. if err == 0:
  100. return json.loads(r_data)['result'][0]
  101. else:
  102. return json.loads(r_data)['err_msg']
  103.  
  104. # 获取百度API调用的认证,实时生成,因为有时间限制
  105. def getToken():
  106. # token认证的url
  107. api_url = "https://openapi.baidu.com/oauth/2.0/token?" \
  108. "grant_type=client_credentials&client_id=%s&client_secret=%s"
  109. token_url = api_url % (BaiDu_API_Key_GetVoi, BaiDu_Secret_Key_GetVoi)
  110. r_str = urllib.request.urlopen(token_url).read()
  111. token_data = json.loads(r_str)
  112. token_str = token_data['access_token']
  113. 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. bot_use = BOT
  12.  
  13. class LoginFrame(wx.Frame):
  14. """
  15. 登录窗口
  16. """
  17. def __init__(self, parent, id, title, size):
  18. # 初始化,添加控件并绑定事件
  19. wx.Frame.__init__(self, parent, id, title)
  20. self.SetSize(size)
  21. self.Center()
  22. self.serverAddressLabel = wx.StaticText(self, label="Server Address", pos=(15, 40), size=(120, 25))
  23. self.userNameLabel = wx.StaticText(self, label="UserName", pos=(45, 90), size=(120, 25))
  24. self.serverAddress = wx.TextCtrl(self, value=default_server,
  25. pos=(120, 37), size=(150, 25), style=wx.TE_PROCESS_ENTER)
  26. self.userName = wx.TextCtrl(self, pos=(120, 87), size=(150, 25), style=wx.TE_PROCESS_ENTER)
  27. self.loginButton = wx.Button(self, label='Login', pos=(50, 145), size=(90, 30))
  28. self.exitButton = wx.Button(self, label='Exit', pos=(180, 145), size=(90, 30))
  29. # 绑定登录方法
  30. self.loginButton.Bind(wx.EVT_BUTTON, self.login)
  31. # 绑定退出方法
  32. self.exitButton.Bind(wx.EVT_BUTTON, self.exit)
  33. # 服务器输入框Tab事件
  34. self.serverAddress.SetFocus()
  35. self.Bind(wx.EVT_TEXT_ENTER, self.usn_focus, self.serverAddress)
  36. # 用户名回车登录
  37. self.Bind(wx.EVT_TEXT_ENTER, self.login, self.userName)
  38. self.Show()
  39.  
  40. # 回车调到用户名输入栏
  41. def usn_focus(self, event):
  42. self.userName.SetFocus()
  43.  
  44. def login(self, event):
  45. # 登录处理
  46. try:
  47. serverAddress = self.serverAddress.GetLineText(0).split(':')
  48. con.open(serverAddress[0], port=int(serverAddress[1]), timeout=10)
  49. response = con.read_some()
  50. if response != b'Connect Success':
  51. self.showDialog('Error', 'Connect Fail!', (200, 100))
  52. return
  53. con.write(('login ' + str(self.userName.GetLineText(0)) + '\n').encode("utf-8"))
  54. response = con.read_some()
  55. if response == b'UserName Empty':
  56. self.showDialog('Error', 'UserName Empty!', (200, 100))
  57. elif response == b'UserName Exist':
  58. self.showDialog('Error', 'UserName Exist!', (200, 100))
  59. else:
  60. self.Close()
  61. ChatFrame(None, 2, title='当前用户:'+str(self.userName.GetLineText(0)), size=(515, 400))
  62. except Exception:
  63. self.showDialog('Error', 'Connect Fail!', (95, 20))
  64.  
  65. def exit(self, event):
  66. self.Close()
  67.  
  68. # 显示错误信息对话框
  69. def showDialog(self, title, content, size):
  70. dialog = wx.Dialog(self, title=title, size=size)
  71. dialog.Center()
  72. wx.StaticText(dialog, label=content)
  73. dialog.ShowModal()
  74.  
  75. class ChatFrame(wx.Frame):
  76. """
  77. 聊天窗口
  78. """
  79. def __init__(self, parent, id, title, size):
  80. # 初始化,添加控件并绑定事件
  81. wx.Frame.__init__(self, parent, id, title, style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX |
  82. wx.DEFAULT_FRAME_STYLE)
  83. self.SetSize(size)
  84. self.Center()
  85. self.chatFrame = wx.TextCtrl(self, pos=(5, 5), size=(490, 310), style=wx.TE_MULTILINE | wx.TE_READONLY)
  86. self.sayButton = wx.Button(self, label="Say", pos=(5, 320), size=(58, 25))
  87. self.message = wx.TextCtrl(self, pos=(65, 320), size=(240, 25), style=wx.TE_PROCESS_ENTER)
  88. self.sendButton = wx.Button(self, label="Send", pos=(310, 320), size=(58, 25))
  89. self.usersButton = wx.Button(self, label="Users", pos=(373, 320), size=(58, 25))
  90. self.closeButton = wx.Button(self, label="Close", pos=(436, 320), size=(58, 25))
  91. # 发送按钮绑定发送消息方法
  92. self.sendButton.Bind(wx.EVT_BUTTON, self.send)
  93. # 输入框回车发送信息
  94. self.message.SetFocus()
  95. # 发送消息
  96. self.sayButton.Bind(wx.EVT_LEFT_DOWN, self.sayDown)
  97. self.sayButton.Bind(wx.EVT_LEFT_UP, self.sayUp)
  98. # 发送消息
  99. self.Bind(wx.EVT_TEXT_ENTER, self.send, self.message)
  100. # Users按钮绑定获取在线用户数量方法
  101. self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
  102. # 关闭按钮绑定关闭方法
  103. self.closeButton.Bind(wx.EVT_BUTTON, self.close)
  104. thread.start_new_thread(self.receive, ())
  105. # self.ShowFullScreen(True)
  106. self.Show()
  107.  
  108. def sayDown(self, event):
  109. thread.start_new_thread(recording, ())
  110. # print("ON")
  111.  
  112. def sayUp(self, event):
  113. sayText = getText(r"E:\Python_Doc\voice_say\say_voice.wav")
  114. self.message.AppendText(str(sayText))
  115. self.send(self)
  116.  
  117. def send(self, event):
  118. # 发送消息
  119. message = str(self.message.GetLineText(0)).strip()
  120. global bot_use
  121. if message != '':
  122. if message == "chatbot":
  123. bot_use = "ChatBot"
  124. self.message.Clear()
  125. con.write(('noone_say You have been changed ChatBot-Chat' + '\n').encode("utf-8"))
  126. return
  127. elif message == "tuling":
  128. bot_use = "TuLing"
  129. self.message.Clear()
  130. con.write(('noone_say You have been changed TuLing-Chat' + '\n').encode("utf-8"))
  131. return
  132. elif message == "user":
  133. bot_use = "User"
  134. self.message.Clear()
  135. con.write(('noone_say You have been changed User-Chat' + '\n').encode("utf-8"))
  136. return
  137. con.write(('say ' + message + '\n').encode("utf-8"))
  138. self.message.Clear()
  139. # 机器人回复
  140. if bot_use == "ChatBot":
  141. answer = chatbot(message)
  142. con.write(('chatbot_say ' + answer + '\n').encode("utf-8"))
  143. elif bot_use == "TuLing":
  144. answer = tuling(message)
  145. con.write(('tuling_say ' + answer + '\n').encode("utf-8"))
  146. elif bot_use == "User":
  147. return
  148.  
  149. if VOICE_SWITCH:
  150. # 写本地音乐文件
  151. baidu_api2(answer)
  152. # 新建线程播放音乐
  153. thread.start_new_thread(play_mp3, ())
  154. return
  155.  
  156. def lookUsers(self, event):
  157. # 查看当前在线用户
  158. con.write(b'look\n')
  159.  
  160. def close(self, event):
  161. # 关闭窗口
  162. thread.start_new_thread(remove_voice, ())
  163. con.write(b'logout\n')
  164. con.close()
  165. self.Close()
  166.  
  167. def receive(self):
  168. # 接受服务器的消息
  169. while True:
  170. sleep(0.6)
  171. result = con.read_very_eager()
  172. if result != '':
  173. self.chatFrame.AppendText(result)
  174.  
  175. if __name__ == '__main__':
  176. app = wx.App()
  177. con = telnetlib.Telnet()
  178. LoginFrame(None, -1, title="Login", size=(320, 250))
  179. 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 = ""
  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

Python实现机器人语音聊天的更多相关文章

  1. python 实现简单语音聊天机器人

    '''思路:使用百度的文本转音频API,将结果保存成mp3格式,并用mp3play库播放该文件.''' 1 # -*- coding:utf-8 -*- import sys import reque ...

  2. Python实现机器人聊天

    今天午休的时候,无意之中看了一篇博客,名字叫Python实现机器人,感觉挺有的意思的.于是用其写了一个简单的Python聊天,源码如下所示: # -*- coding: utf- -*- import ...

  3. 手把手教你用Python代码实现微信聊天机器人 -- Python wxpy

    关注我,每天都有优质技术文章推送,工作,学习累了的时候放松一下自己. 本篇文章同步微信公众号 欢迎大家关注我的微信公众号:「醉翁猫咪」 来学习了,微信聊天机器人. 环境要求: Windows / Li ...

  4. Python微信机器人

    Python微信机器人 本文目录 一 简介 二 登录微信 三 微信好友男女比例 四 微信好友地域分布 五 微信聊天机器人 一 简介 wxpy基于itchat,使用了 Web 微信的通讯协议,,通过大量 ...

  5. Unity3D 实现简单的语音聊天 [iOS版本]

    现在很多手机游戏中的聊天系统都加入语音聊天的功能,相比于传统的文字聊天,语音聊天在MMORPG中显得尤为重要,毕竟直接口头交流总比你码字快得多了,也更直观些. 实现语音聊天的方法很多,U3D中有不少第 ...

  6. Android 即时语音聊天工具 开发

    使用融云SDK 1. 功能需求分析 1.1 核心功能需求: * 即时通讯 * 文字聊天 * 语音聊天 1.2 辅助功能需求: * 注册.登录 * 好友添加功能 * 好友关系管理 2. 融云即时通讯平台 ...

  7. C#实现多人语音聊天

    在上一篇文章 实现一个简单的语音聊天室(多人语音聊天系统)中,我用C#实现了一个简单的语音聊天室,并给出了源代码下载.尽管有源代码,可是非常多朋友反映,理解起来还是有些模糊.不够清楚.如今想来,是由于 ...

  8. python写机器人玩僵尸骰子

    python写机器人玩僵尸骰子由Al Sweigart用python发布注意:我正在为我的僵尸骰子模拟器寻找反馈,以及这一套指令.如果你觉得有什么地方可以改进,请发邮件到al@inventwithpy ...

  9. cordova使用webrtc与网页端及移动端视频、语音聊天

    最近在做一个移动端与移动端.网页端文字.视频.语音聊天的功能.文字聊天使用websocket,在网上很多资料,也没什么难度.但是在视频.语音聊天上遇到了小小的难点.之前一直在找一些SDK想快速开发,例 ...

随机推荐

  1. android handler传递数据

    起因:在android使用get请求获取验证码时需要重开一个线程,这就造成了我无法获取到从服务器后台返回的数据 解决方法:创建全局变量,将返回的数据解析后返回给handler,再在handler中将数 ...

  2. 新手学python-Day4-进制,数据类型,编码转换,列表

    python3中字符串的编码转换 names=b'\xe2\x82\xac20'.decode('utf-8') print(names) names='€20'.encode('utf-8') pr ...

  3. SpringBoot+Maven聚合多项目打包成jar

    已我最近自己在玩的一个DEMO为例 taosir为pom.xml,其他子项目均为其modules,且为jar项目 eureka为注册中心.workflow为提供者.entrance为调用方 entra ...

  4. jquery中的jsonp跨域调用(接口)

                                                                           jquery jsonp跨域调用接口

  5. Nutch命令大全

    Nutch采用了一种命令的方式进行工作,其命令可以是对局域网方式的单一命令也可以是对整个Web进行爬取的分步命令.主要的命令如下: 1. Crawl Crawl是"org.apache.nu ...

  6. ASP.NET-dropdownlist默认值

    可以在第三个选项中定义一个默认值,但是返回数据的时候就不需要这种类似"请选择名称"之类的提示了,所以在构造seleclt option的时候,要在option中的属性中加上sele ...

  7. Winserver服务器-AD字段对照简图

    AD字段对照简图

  8. HDU 3625

    有点置换群的味道. 当撞开一个门后,能打开一连串的门,即是可以排成一个圈.求的是种数,于是,可以使用第一类斯特林数,求出撞了0~K次的种数. 但是,注意,当第一个门为独自一个圈时,是不可行的,因为这代 ...

  9. Webstorm快捷键整理

    Webstorm快捷键整理 F2/Shift F2  下一个/上一个高亮错误 Ctrl+Shift+BackSpace 回到刚刚编辑的地方 Alt+Insert 新建文件,还有其他功能 Ctrl+D ...

  10. [Struts2] No result defined for action ... and result input &amp; Invalid field value for field ...

    "No result defined for action ... and result input"错误一般发生在Struts2的拦截器拦截时遇到了问题时.Struts2会将跳转 ...