zoukankan      html  css  js  c++  java
  • 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'
    ')
     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 {} 
    '.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.
    ').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.
    ').encode("utf-8"))
    182 
    183     def do_say(self, session, line):
    184         # 客户端发送消息
    185         self.broadcast((session.name + ': ' + line + '
    ').encode("utf-8"))
    186 
    187     def do_noone_say(self, session, line):
    188         # 图灵回复消息
    189         self.broadcast((line + '
    ').encode("utf-8"))
    190 
    191     def do_chatbot_say(self, session, line):
    192         # 图灵回复消息
    193         self.broadcast(('ChatBot: ' + line + '
    ').encode("utf-8"))
    194 
    195     def do_tuling_say(self, session, line):
    196         # 图灵回复消息
    197         self.broadcast(('Tuling: ' + line + '
    ').encode("utf-8"))
    198 
    199     def do_look(self, session, line):
    200         # 查看在线用户
    201         session.push(b'All Online Users Are:
    ')
    202         for other in self.sessions:
    203             session.push((other.name + '
    ').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:PythonLibsite-packageschatterbot_corpusdatachinese

          打开后格式就有了,这里我们按照格式新增一个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日正式上市。
    三星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_Docvoice_saysay_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_Docvoice_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)) + '
    ').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_Docvoice_saysay_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' + '
    ').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' + '
    ').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' + '
    ').encode("utf-8"))
    139                 return
    140             con.write(('say ' + message + '
    ').encode("utf-8"))
    141             self.message.Clear()
    142             # 机器人回复
    143             if bot_use == "ChatBot":
    144                 answer = chatbot(message)
    145                 con.write(('chatbot_say ' + answer + '
    ').encode("utf-8"))
    146             elif bot_use == "TuLing":
    147                 answer = tuling(message)
    148                 con.write(('tuling_say ' + answer + '
    ').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
    ')
    162 
    163     def close(self, event):
    164         # 关闭窗口
    165         thread.start_new_thread(remove_voice, ())
    166         con.write(b'logout
    ')
    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

  • 相关阅读:
    教你50招提升ASP.NET性能(二十一):避免使用会话状态
    教你50招提升ASP.NET性能(二十):7条便利的ViewState技巧
    教你50招提升ASP.NET性能(二十):认识你的循环
    教你50招提升ASP.NET性能(十九):静态集合
    教你50招提升ASP.NET性能(十八):在处理网站性能问题前,首先验证问题是否出在客户端
    教你50招提升ASP.NET性能(十七):不要认为问题只会从业务层产生
    教你50招提升ASP.NET性能(十六):把问题仍给硬件而不是开发人员
    教你50招提升ASP.NET性能(十五):解决性能问题时不要低估UI的价值
    教你50招提升ASP.NET性能(十四):使用startMode属性来减少ASP.NET站点加载时间
    Chrome谷歌浏览器书签排序后,重启浏览器导致排序无效的问题(完美解决)
  • 原文地址:https://www.cnblogs.com/Vrapile/p/8421403.html
Copyright © 2011-2022 走看看