摘要:利用微信公众号开发一个聊天机器人。
前置条件
- Linux基础
- 一点儿CGI和Python基础
- 一台运行Linux系统的云主机或VPS虚拟主机
- 最好有个域名
原理
聊天机器人原理图上图给出了聊天机器人的基本原理。用户A在微信客户端中给公众号发送一条消息,这条消息会通过微信服务器,转发到公众号指定的服务器上,也就是图中的“聊天机器人服务器”,聊天机器人服务器收到消息之后,经过“思考”,给出一个回复到微信服务器,然后微信服务器就会把这个回复发送到用户A的客户端,一次“对话”就完成了。整个过程中的大部分工作,都已经由微信包办了,我们要做的,只是红框中的聊天机器人服务器,让它按照微信指定的协议,接收消息和发送回复,就是这么简单!
微信服务器和聊天机器人之间通过HTTP通信,所以我们的聊天机器人服务器,实际上就是一个符合微信标准的CGI,基本上你可以用任何语言进行开发。本文将使用Python和Bottle框架进行开发。
申请微信公众号
在微信公众平台注册一个帐号,注意这里使用的邮箱不能跟微信其他服务所绑定的邮箱相同,包括个人微信号、微信开放平台、微信小程序等等。幸好只是换邮箱注册,不是换手机注册。注册之后请按照官方的指引填写相关信息。公众平台提供了编辑和发布图文消息的功能,但是要实现一个聊天机器人,必须使用开发者模式。在"开发" -> "接口权限"下,可以查看当前公众号能够使用的接口,目前个人用户只能申请订阅号,并且不能进行认证,因此权限最低,能使用的接口有限。不过接收消息和自动回复的权限还是有的,并且没有调用次数的限制,这就足够了。
进入"开发" -> "基本配置" -> "服务器配置",修改服务器配置,这里的关键选项是URL和Token。URL就是原理图中,微信服务器和你的聊天机器人服务器之间通信的接口。Token让你的聊天机器人可以验证消息的来源,防止微信服务器之外的恶意调用。微信服务器发送的每条消息,都会带上一个使用Token加密的签名,我们只要使用相同的Token和算法,就能验证收到的消息是否来自微信服务器。关于签名的方法,微信官方的接入指南里有更详细的描述,以及对应的PHP实现。"消息加解密方式"选择"明文模式",EncodingAESKey直接点"随机生成",这样省去了消息加解密的麻烦。
此时直接点击“提交”是不能成功的,因为微信公众平台会立刻发送一个验证消息,检验URL是否可用。我们必须先让自己的服务器跑起来,才能通过验证。
一个最小的公众号服务器
这里我们使用Bottle这个小巧而美丽的Web框架来编写一个能通过验证的最简单的服务器。在你的云主机或者VPS上创建robot.py文件,内容如下:
from bottle import request, route, run
from hashlib import sha1
TOKEN = '这里填写你的Token'
def valid():
l = [TOKEN, request.query.timestamp, request.query.nonce]
l.sort()
h = sha1()
h.update("".join(l))
if request.query.signature == h.hexdigest():
return True
return False
@route('/wx/chatbot')
def check():
if valid():
return request.query.echostr
return "failed"
run(host='0.0.0.0', port=80)
这段端程序的含义是,服务器收到一个访问/wx/chatbot的GET请求时,按照微信指定的算法计算签名,跟请求中的signature参数进行对比,如果二者相符,就把请求中的echostr原样返回。事实上,这个服务器还不是最简单的。因为微信服务器只要收到了echostr,就会认为验证成功,因此我们完全可以跳过签名验证的过程,直接返回echostr。
运行这段程序必须要bottle模块,可以直接从这里下载压缩包,解压出里面的bottle.py放在你程序的目录下。不过更推荐的方法是使用pip,安装方法见这里。
pip install bottle
现在让我们的服务器运行起来,然后回到微信公众平台的配置页面,在URL里填写“http://你的域名或ip/wx/chatbot”, 点击“提交”完成配置,再点击“启用”让我们的配置生效。
python robot.py
A Dumb Robot
到这里,我们已经有了一个可以接收微信消息的服务器了。微信发过来的消息是下面这个样子:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
这是一个XML文件,我们只关注3个字段:ToUserName、FromUserName和Content,分别表示消息的接收者(这里是我们的公众号ID,可以在公众平台的"设置" -> "公众号设置" -> "原始ID"中看到),发送者和内容。要回复消息给用户,也要返回一个指定格式的XML给微信服务器:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
知道了往返消息的格式(详细信息参见微信的开发文档),我们就可以开始“对话”了。在代码中添加下面内容:
from time import time
@route('/wx/chatbot', method='POST')
def chat():
if not valid():
return "invalid msg"
return """
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[Hello]]></Content>
</xml>
""" % int(time())
chat()函数将接收来自微信服务器的POST请求,然后返回一个固定的回复。要让发消息的用户能够收到回复,还需要正确的填写回复中的toUser和fromUser。由于是回复消息,toUser和fromUser跟用户发送给公众号的消息正好是反过来的,我们可以用print语句把收到的消息打印出来,找到对应的字段填在回复里,这样就可以成功的回复消息了。因为toUser是写死的,这个公众号只能给固定的用户回复消息,而且只能say hello。不过,它总算是“活过来”了。在后续的内容里,我们将真正打造一个能够跟用户对话,并且具备一定智能的聊天机器人。
网友评论