背景介绍
itchat是一个开源的微信个人号接口,使用python调用微信从未如此简单。
使用不到三十行的代码,你就可以完成一个能够处理所有信息的微信机器人。
官方文档参考https://itchat.readthedocs.io/zh/latest/
最近要做一个自动应答机器人,获得用户消息GUI+语义分析+机器学习给出答案。
准备工作
需要安装ffmpeg(百度搜索官网,下载windows版解压后把bin目录添加到系统变量的path中)
pip安装 pydub,SpeechRecognition
pip install pydub pip install SpeechRecognition
绑定消息
GUI这部分使用微信的itchat接口,安装和新手教程可以自己参考官方文档。
绑定语音消息回复的方式为:
@itchat.msg_register(RECORDING) def tuling_reply(msg):
其中用的是RECORDING是因为之前代码最开始有from itchat.content import *,否则就要使用itchat.content.RECORDING
关于@修饰符的作用,网上百度就有,说下自己的思考:
@de
def func1:
----- 等价于 ------
func1 = de( func1 )
Python解释器读到函数修饰符“@”的时候,后面步骤会是这样了:
1. 去调用de函数,de函数的入口参数就是那个叫“func1”的函数;
2. de函数被执行,入口参数的(也就是func1函数)会被调用(执行);
换言之,修饰符带的那个函数的入口参数,就是下面的那个整个的函数。
参考https://blog.csdn.net/972301/article/details/59537712和 https://blog.csdn.net/fwenzhou/article/details/8733857
所以我们使用@的时候,itchat.msg_register这个函数就被执行了,我们定义的tuling_reply作为参数传了进去,所以才会读取到消息就用这个函数处理消息
语音识别
由于微信保存的语音消息都是mp3格式,看了一圈发现只有腾讯语音识别支持mp3,之前尝试过腾讯一句话识别语音API,但是官方没有最新的例程,并且居然不同部分用的是不同版本的文档说明,导致我鉴权一直失败。到后来仔细研读了下,自己写了代码,鉴权应该是通过了,但是返回的消息是x‘\98'这样的一个中文字符,并且解码会失败,这才发现可能是因为腾讯的只支持中文,虽然我在这个随笔的例子是中文语音识别,但我实际项目要做的是英文语音识别。不过在这中间也学到了一些东西,比如加密算法的使用,还有python3的二进制和字符串消息的转换关系。
1 import binascii 2 import hashlib 3 import hmac 4 import urllib.parse 5 import urllib.request 6 import time 7 import random 8 import base64 9 10 def asr(msg): 11 msg['Text'](msg['FileName'])#保存mp3语音 12 timeData = str(int(time.time())) # 时间戳 13 nonceData = int(random.random()*10000) # Nonce,官网给的信息:随机正整数,与 Timestamp 联合起来, 用于防止重放攻击 14 with open(msg['FileName'], 'rb') as f: 15 voiceData = f.read()#读取mp3语音,获得byte数据,格式是b'\x..' 16 os.remove(msg['FileName'])#删除mp3语音 17 DataLenData = len(voiceData)#读取未base64编码之前的文件长度 18 tmp = int(timeData)#time stamp 19 signDictData = {#需要注意的是字典的key值要按照ascii码升序排序,并不一定是字典序,可以使用sorted(signDictData.keys())来查看ascii码排序结果 20 'Action' : actionData, 21 'Data': base64.b64encode(voiceData).decode('utf8'),#base64编码,编码后是二进制,再用decode解码 22 # 'Data': voiceData, 23 'DataLen': DataLenData, 24 'EngSerViceType': EngSerViceTypeData, 25 'Nonce' : nonceData, 26 'ProjectId':0, 27 'Region': 'ap-shanghai', 28 'SecretId' : secretId, 29 # 'SignatureMethod': 'HmacSHA256',#加密算法可选,不指定这个参数默认是HmacSHA1加密 30 'SourceType': SourceTypeData, 31 'SubServiceType': SubServiceTypeData, 32 'Timestamp' : tmp, 33 'UsrAudioKey': UsrAudioKeyData, 34 'Version': versionData, 35 'VoiceFormat': VoiceFormatData 36 } 37 # 请求方法 + 请求主机 +请求路径 + ? + 请求字符串 38 requestStr = "%s%s%s%s%s"%(requestMethod,uriData,"/","?",dictToStr(signDictData)) 39 # signData = urllib.parse.quote(sign(secretKey,requestStr,'HmacSHA1')) 40 #生成签名字符的时候一定是使用的没有经过urlencode编码的requestStr字符串,下面的加了encode的就是把字符串变成byte,sha1是算法,decode是把二进制解码为字符串。digest()是把hmac.new()的结果解析成字符串,然后经过base64编码为byte,再解码为字符串 41 signData = binascii.b2a_base64(hmac.new(secretKey.encode('utf-8'), requestStr.encode('utf-8'), hashlib.sha1).digest())[:-1].decode() 42 # 上述操作是实现签名,下面即进行请求 43 # 先建立请求参数, 此处参数只在签名时多了一个Signature 44 actionArgs = { 45 'Action' : actionData, 46 'Data': base64.b64encode(voiceData).decode('utf8'), 47 # 'Data': voiceData, 48 'DataLen': DataLenData, 49 'EngSerViceType': EngSerViceTypeData, 50 'Nonce' : nonceData, 51 'ProjectId':0, 52 'Region': 'ap-shanghai', 53 'SecretId' : secretId, 54 'SourceType': SourceTypeData, 55 'SubServiceType': SubServiceTypeData, 56 'Timestamp' : tmp, 57 'UsrAudioKey': UsrAudioKeyData, 58 'Version': versionData, 59 'VoiceFormat': VoiceFormatData, 60 "Signature": signData 61 } 62 # 根据uri构建请求的url 63 requestUrl = "https://%s/?"%(uriData) 64 # 将请求的url和参数进行拼接,使用urlencode会修改掉参数中的/和=等符号的表示方式 65 requestUrlWithArgs = requestUrl + urllib.parse.urlencode(actionArgs) 66 67 # actionArgs = signDictData #这是深复制,两个字典就是一个字典 68 # actionArgs["Signature"] = signData 69 70 # # 根据uri构建请求的url 71 # requestUrl = "https://%s/?"%(uriData) 72 # # 将请求的url和参数进行拼接 73 # requestUrlWithArgs = requestUrl + dictToStr(actionArgs) 74 75 # 获得response 76 responseData = urllib.request.urlopen(requestUrlWithArgs).read().decode("utf-8")# 根据uri构建 77 # return json.loads(responseData)["Response"]["Error"]["Message"] #处理错误消息 78 return json.loads(responseData)["Response"]["Result"]#处理正确消息