WeRoBot¶
WeRoBot 是一个微信公众号开发框架。
入门¶
Hello World¶
最简单的Hello World, 会给收到的每一条信息回复 Hello World
import werobot
robot = werobot.WeRoBot(token='tokenhere')
@robot.handler
def hello(message):
return 'Hello World!'
# 让服务器监听在 0.0.0.0:80
robot.config['HOST'] = '0.0.0.0'
robot.config['PORT'] = 80
robot.run()
消息处理¶
WeRoBot 会解析微信服务器发来的消息, 并将消息转换成成 Message 或者是 Event 。 Message 表示用户发来的消息,如文本消息、图片消息; Event 则表示用户触发的事件, 如关注事件、扫描二维码事件。 在消息解析、转换完成后, WeRoBot 会讲消息转交给 Handler 进行处理,并将 Handler 的返回值返回给微信服务器。
在刚才的 Hello World 中, 我们编写的
@robot.handler
def hello(message):
return 'Hello World!'
就是一个简单的 Handler , @robot.handler 意味着 robot 会将所有接收到的消息( 包括 Message 和 Event ) 都转交给这个 Handler 来处理。 当然, 你也可以编写一些只能处理特定消息的 Handler
# @robot.text 修饰的 Handler 只处理文本消息
@robot.text
def echo(message):
return message.content
# @robot.image 修饰的 Handler 只处理图片消息
@robot.image
def img(message):
return message.img
使用 Session 记录用户状态¶
WeRoBot 提供了 Session 功能, 可以让你方便的记录用户状态。 比如, 这个 Handler 可以判断发消息的用户之前有没有发送过消息
@robot.text
def first(message, session):
if 'first' in session:
return '你之前给我发过消息'
session['first'] = True
return '你之前没给我发过消息'
Session 功能默认开启, 并使用 SQLite 存储 Session 数据。 详情请参考 Session 文档
创建自定义菜单¶
自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。 werobot.client.Client
封装了微信的部分 API 接口,我们可以使用 werobot.client.Client.create_menu()
来创建自定义菜单。
在使用 Client 之前, 我们需要先提供微信公众平台内的 AppID 和 AppSecret
from werobot import WeRoBot
robot = WeRoBot()
robot.config["APP_ID"] = "你的 AppID"
robot.config["APP_SECRET"] = "你的 AppSecret"
client = robot.client
然后, 我们就可以创建自定义菜单了
client.create_menu({
"button":[{
"type": "click",
"name": "今日歌曲",
"key": "music"
}]
})
注意以上代码只需要运行一次就可以了。在创建完自定义菜单之后, 我们还需要写一个 Handler 来响应菜单的点击操作
@robot.key_click("music")
def music(message):
return '你点击了“今日歌曲”按钮'
消息加解密¶
WeRoBot 支持对消息的加解密,即微信公众号的安全模式。
在开启消息加解密功能之前,请先阅读微信官方的 消息加解密说明
为 WeRoBot 开启消息加密功能,首先需要安装 cryptography
pip install cryptography
之后, 你只需要将开发者 ID(AppID) 和微信公众平台后台设置的 EncodingAESKey 加到 WeRoBot 的 Config 里面就可以了
from werobot import WeRoBot
robot = WeRoBot()
robot.config["APP_ID"] = "Your AppID"
robot.config['ENCODING_AES_KEY'] = 'Your Encoding AES Key'
WeRoBot 之后会自动进行消息的加解密工作。
部署¶
注解
本节所讨论的是将 WeRoBot 作为独立服务运行情况下的部署操作。 如果你希望将 WeRoBot 集成到其他 Web 框架内,请阅读 与其他 Web 框架集成
在独立服务器上部署¶
使用 werobot.run
来启动 WSGI 服务器¶
你可以在 werobot.config
中配置好 WeRoBot 需要监听的地址和端口号, 然后使用 werobot.run
来启动服务器
import werobot
robot = werobot.WeRoBot(token='tokenhere')
@robot.handler
def echo(message):
return 'Hello World!'
robot.config['HOST'] = '0.0.0.0'
robot.config['PORT'] = 80
robot.run()
注解
你需要 root 或管理员权限才能监听 1024 以下的端口。
你可以通过传递 server 参数来手动指定使用的服务器
import werobot
robot = werobot.WeRoBot(token='tokenhere')
@robot.handler
def echo(message):
return 'Hello World!'
robot.config['HOST'] = '0.0.0.0'
robot.config['PORT'] = 80
robot.run(server='gevent')
server 支持以下几种:
- cgi
- flup
- wsgiref
- waitress
- cherrypy
- paste
- fapws3
- tornado
- gae
- twisted
- diesel
- meinheld
- gunicorn
- eventlet
- gevent
- rocket
- bjoern
- auto
当 server 为 auto 时, WeRoBot 会自动依次尝试以下几种服务器:
- Waitress
- Paste
- Twisted
- CherryPy
- WSGIRef
所以,只要你安装了相应的服务器软件,就可以使用 werobot.run
直接跑在生产环境下。
注解
server 的默认值为 auto
。
注意
WSGIRef 的性能非常差, 仅能用于开发环境。 如果你要在生产环境下部署 WeRoBot , 请确保你在使用其他 server 。
通过 WSGI HTTP Server 运行 WeRoBot¶
werobot.wsgi
暴露了一个 WSGI Application ,你可以使用任何你喜欢的 WSGI HTTP Server 来部署 WeRoBot。
比如, 如果你想用 Gunicorn 来部署
# FileName: robot.py
from werobot import WeRoBot
robot = WeRoBot()
那么你只需要在 Shell 下运行
gunicorn robot:robot.wsgi
就可以了。
使用 Supervisor 管理守护进程¶
请注意, werobot.run
是跑在 非守护进程模式下 的——也就是说,一旦你关闭终端,进程就会自动退出。
我们建议您使用 Supervisor 来管理 WeRoBot 的进程。
配置文件样例:
[program:wechat_robot]
command = python /home/<username>/robot.py
user = <username>
redirect_stderr = true
stdout_logfile = /home/<username>/logs/robot.log
stdout_errfile = /home/<username>/logs/robot_error.log
使用 Nginx 进行反向代理¶
微信服务器只支持80端口的机器人——显然,你的服务器上不会只跑着一个微信机器人。对于这种情况,我们建议您使用 Nginx 来进行反向代理。
配置文件样例:
server {
server_name example.com;
listen 80;
location / {
proxy_pass_header Server;
proxy_redirect off;
proxy_pass http://127.0.0.1:12233;
}
}
注解
在这个例子中, WeRoBot 的端口号为 12233。你应该在微信管理后台中将服务器地址设为 http://example.com
。
在SAE上部署¶
新浪云上的 Python 应用的入口为 index.wsgi:application ,也就是 index.wsgi 这个文件中名为 application 的 callable object。
所以,假设你在 robot.py 中使用了 WeRoBot
# filename: robot.py
import werobot
robot = werobot.WeRoBot(token='tokenhere')
@robot.handler
def echo(message):
return 'Hello World!'
你需要再创建一个 index.wsgi 文件, 里面写
import sae
from robot import robot
application = sae.create_wsgi_app(robot.wsgi)
然后按照 SAE 的要求编写好 config.yaml 文件就可以了。 可以参考 示例仓库
如果你希望使用 SAE 提供的 KVDB 存储 Session 数据, 可以选择 werobot.session.saekvstorage
作为你的 Session Storage.
Handler¶
WeRoBot会将合法的请求发送给 handlers 依次执行。
如果某一个 Handler 返回了非空值, WeRoBot 就会根据这个值创建回复,后面的 handlers 将不会被执行。
你可以通过修饰符或 add_handler()
添加 handler
import werobot
robot = werobot.WeRoBot(token='tokenhere')
# 通过修饰符添加handler
@robot.handler
def echo(message):
return 'Hello World!'
# 通过`add_handler`添加handler
def echo(message):
return 'Hello World!'
robot.add_handler(echo)
类型过滤¶
在大多数情况下, 一个 Handler 并不能处理所有类型的消息。幸运的是, WeRoBot 可以帮你过滤收到的消息。
只想处理被新用户关注的消息?:
import werobot
robot = werobot.WeRoBot(token='tokenhere')
@robot.subscribe
def subscribe(message):
return 'Hello My Friend!'
robot.run()
或者,你的 handler 只能处理文本?
import werobot
robot = werobot.WeRoBot(token='tokenhere')
@robot.text
def echo(message):
return message.content
robot.run()
在 WeRobot 中我们把请求分成了 Message 和 Event 两种类型,针对两种类型的请求分别有不同的 Handler。
额,这个 handler 想处理文本信息和地理位置信息?
import werobot
robot = werobot.WeRoBot(token='tokenhere')
@robot.text
@robot.location
def handler(message):
# Do what you love to do
pass
robot.run()
当然,你也可以用 add_handler()
函数添加handler,就像这样:
import werobot
robot = werobot.WeRoBot(token='tokenhere')
def handler(message):
# Do what you love to do
pass
robot.add_handler(handler, types=['text', 'location'])
robot.run()
注解
通过 robot.handler
添加的 handler 将收到所有信息;只有在其他 handler 没有给出返回值的情况下, 通过 robot.handler
添加的 handler 才会被调用。
robot.key_click —— 回应自定义菜单¶
key_click()
是对 click()
修饰符的改进。
如果你在自定义菜单中定义了一个 Key 为 abort
的菜单,响应这个菜单的 handler 可以写成这样
@robot.key_click("abort")
def abort():
return "I'm a robot"
当然,如果你不喜欢用 key_click()
,也可以写成这样
@robot.click
def abort(message):
if message.key == "abort":
return "I'm a robot"
两者是等价的。
robot.filter —— 回应有指定文本的消息¶
现在你可以写这样的代码
@robot.filter("a")
def a():
return "正文为 a "
import re
@robot.filter(re.compile(".*?bb.*?"))
def b():
return "正文中含有 bb "
@robot.filter(re.compile(".*?c.*?"), "d")
def c():
return "正文中含有 c 或正文为 d"
@robot.filter(re.compile("(.*)?e(.*)?"), "f")
def d(message, session, match):
if match:
return "正文为 " + match.group(1) + "e" + match.group(2)
return "正文为 f"
这段代码等价于
@robot.text
def a(message):
if message.content == "a":
return "正文为 a "
import re
@robot.text
def b(message):
if re.compile(".*?bb.*?").match(message.content):
return "正文中含有 b "
@robot.text
def c(message):
if re.compile(".*?c.*?").match(message.content) or message.content == "d":
return "正文中含有 c 或正文为 d"
@robot.text
def d(message):
match = re.compile("(.*)?e(.*)?").match(message.content)
if match:
return "正文为 " + match.group(1) + "e" + match.group(2)
if message.content == "f":
return "正文为 f"
如果你想通过修饰符以外的方法添加 filter,可以使用 add_filter()
方法
def say_hello():
return "hello!"
robot.add_filter(func=say_hello, rules=["hello", "hi", re.compile(".*?hello.*?")])
更多内容详见 werobot.robot.BaseRoBot
Session¶
你可以通过 Session 实现用户状态的记录。
一个简单的使用 Session 的 Demo
from werobot import WeRoBot
robot = WeRoBot(token=werobot.utils.generate_token())
@robot.text
def first(message, session):
if 'last' in session:
return
session['last'] = message.content
return message.content
robot.run()
开启/关闭 Session¶
Session 在 WeRoBot 中默认开启, 并使用 werobot.session.sqlitestorage.SQLiteStorage
作为存储后端。 如果想要更换存储后端, 可以修改 Config 中的 SESSION_STORAGE
from werobot import WeRoBot
from werobot.session.filestorage import FileStorage
robot = WeRoBot(token="token")
robot.config['SESSION_STORAGE'] = FileStorage()
如果想要关闭 Session 功能, 只需把 SESSION_STORAGE
设为 False 即可
from werobot import WeRoBot
robot = WeRoBot(token="token")
robot.config['SESSION_STORAGE'] = False
修改 Handler 以使用 Session¶
没有打开 Session 的时候,一个标准的 WeRoBot Handler 应该是这样的
@robot.text
def hello(message):
return "Hello!"
而在打开 Session 之后, 如果你的 handler 不需要使用 Session ,可以保持不变; 如果需要使用 Session ,则这个 Handler 需要修改为接受第二个参数: session
@robot.subscribe_event
def intro(message):
return "Hello!"
@robot.text
def hello(message, session):
count = session.get("count", 0) + 1
session["count"] = count
return "Hello! You have sent %s messages to me" % count
传入的 session
参数是一个标准的 Python 字典。
更多可用的 Session Storage 详见 Session 对象。
WeRoBot.Client
—— 微信 API 操作类¶
有部分接口暂未实现,可自行调用微信接口。
开始开发¶
获取 access token¶
详细请参考 http://mp.weixin.qq.com/wiki/14/9f9c82c1af308e3b14ba9b973f99a8ba.html
-
Client.
grant_token
()¶ 获取 Access Token。
返回: 返回的 JSON 数据包
-
Client.
get_access_token
()¶ 判断现有的token是否过期。 用户需要多进程或者多机部署可以手动重写这个函数 来自定义token的存储,刷新策略。
返回: 返回token
注解
Client 的操作都会自动进行 access token 的获取和过期刷新操作,如果有特殊需求(如多进程部署)可重写 get_access_token
。
获取微信服务器IP地址¶
详细请参考 http://mp.weixin.qq.com/wiki/4/41ef0843d6e108cf6b5649480207561c.html
-
Client.
get_ip_list
()¶ 获取微信服务器IP地址。
返回: 返回的 JSON 数据包
自定义菜单¶
自定义菜单创建接口¶
详细请参考 http://mp.weixin.qq.com/wiki/10/0234e39a2025342c17a7d23595c6b40a.html
创建自定义菜单:
client.create_menu({ "button":[ { "type":"click", "name":"今日歌曲", "key":"V1001_TODAY_MUSIC" }, { "type":"click", "name":"歌手简介", "key":"V1001_TODAY_SINGER" }, { "name":"菜单", "sub_button":[ { "type":"view", "name":"搜索", "url":"http://www.soso.com/" }, { "type":"view", "name":"视频", "url":"http://v.qq.com/" }, { "type":"click", "name":"赞一下我们", "key":"V1001_GOOD" } ] } ]})
参数: menu_data – Python 字典 返回: 返回的 JSON 数据包
自定义菜单查询接口¶
详细请参考 http://mp.weixin.qq.com/wiki/5/f287d1a5b78a35a8884326312ac3e4ed.html
查询自定义菜单。
返回: 返回的 JSON 数据包
自定义菜单删除接口¶
详细请参考 http://mp.weixin.qq.com/wiki/3/de21624f2d0d3dafde085dafaa226743.html
删除自定义菜单。
返回: 返回的 JSON 数据包
个性化菜单接口¶
详细请参考 http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html
创建个性化菜单:
button = [ { "type":"click", "name":"今日歌曲", "key":"V1001_TODAY_MUSIC" }, { "name":"菜单", "sub_button":[ { "type":"view", "name":"搜索", "url":"http://www.soso.com/" }, { "type":"view", "name":"视频", "url":"http://v.qq.com/" }, { "type":"click", "name":"赞一下我们", "key":"V1001_GOOD" }] }] matchrule = { "group_id":"2", "sex":"1", "country":"中国", "province":"广东", "city":"广州", "client_platform_type":"2", "language":"zh_CN" } client.create_custom_menu(button, matchrule)
参数: - menu_data – 如上所示的 Python 字典
- matchrule – 如上所示的匹配规则
返回: 返回的 JSON 数据包
删除个性化菜单。
参数: menu_id – 菜单的 ID 返回: 返回的 JSON 数据包
测试个性化菜单匹配结果。
参数: user_id – 要测试匹配的用户 ID 返回: 返回的 JSON 数据包
获取自定义菜单配置接口¶
详细请参考 http://mp.weixin.qq.com/wiki/14/293d0cb8de95e916d1216a33fcb81fd6.html
获取自定义菜单配置接口。
返回: 返回的 JSON 数据包
消息管理¶
客服接口¶
详细请参考 http://mp.weixin.qq.com/wiki/11/c88c270ae8935291626538f9c64bd123.html 发送卡券接口暂时未支持。可自行实现。
-
Client.
add_custom_service_account
(account, nickname, password)¶ 添加客服帐号。
参数: - account – 客服账号的用户名
- nickname – 客服账号的昵称
- password – 客服账号的密码
返回: 返回的 JSON 数据包
-
Client.
update_custom_service_account
(account, nickname, password)¶ 修改客服帐号。
参数: - account – 客服账号的用户名
- nickname – 客服账号的昵称
- password – 客服账号的密码
返回: 返回的 JSON 数据包
-
Client.
delete_custom_service_account
(account, nickname, password)¶ 删除客服帐号。
参数: - account – 客服账号的用户名
- nickname – 客服账号的昵称
- password – 客服账号的密码
返回: 返回的 JSON 数据包
-
Client.
upload_custom_service_account_avatar
(account, avatar)¶ 设置客服帐号的头像。
参数: - account – 客服账号的用户名
- avatar – 头像文件,必须是 jpg 格式
返回: 返回的 JSON 数据包
-
Client.
get_custom_service_account_list
()¶ 获取所有客服账号。
返回: 返回的 JSON 数据包
-
Client.
send_text_message
(user_id, content)¶ 发送文本消息。
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- content – 消息正文
返回: 返回的 JSON 数据包
-
Client.
send_image_message
(user_id, media_id)¶ 发送图片消息。
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- media_id – 图片的媒体ID。 可以通过
upload_media()
上传。
返回: 返回的 JSON 数据包
-
Client.
send_voice_message
(user_id, media_id)¶ 发送语音消息。
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- media_id – 发送的语音的媒体ID。 可以通过
upload_media()
上传。
返回: 返回的 JSON 数据包
-
Client.
send_video_message
(user_id, media_id, title=None, description=None)¶ 发送视频消息。
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- media_id – 发送的视频的媒体ID。 可以通过
upload_media()
上传。 - title – 视频消息的标题
- description – 视频消息的描述
返回: 返回的 JSON 数据包
-
Client.
send_music_message
(user_id, url, hq_url, thumb_media_id, title=None, description=None)¶ 发送音乐消息。 注意如果你遇到了缩略图不能正常显示的问题, 不要慌张; 目前来看是微信服务器端的问题。 对此我们也无能为力 ( #197 )
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- url – 音乐链接
- hq_url – 高品质音乐链接,wifi环境优先使用该链接播放音乐
- thumb_media_id – 缩略图的媒体ID。 可以通过
upload_media()
上传。 - title – 音乐标题
- description – 音乐描述
返回: 返回的 JSON 数据包
-
Client.
send_article_message
(user_id, articles)¶ 发送图文消息:
articles = [ { "title":"Happy Day", "description":"Is Really A Happy Day", "url":"URL", "picurl":"PIC_URL" }, { "title":"Happy Day", "description":"Is Really A Happy Day", "url":"URL", "picurl":"PIC_URL" } ] client.send_acticle_message("user_id", acticles)
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- articles – 一个包含至多8个 article 字典或 Article 对象的数组
返回: 返回的 JSON 数据包
-
Client.
send_news_message
(user_id, media_id)¶ 发送永久素材中的图文消息。
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- media_id – 媒体文件 ID
返回: 返回的 JSON 数据包
用户管理¶
用户分组管理¶
详细请参考 http://mp.weixin.qq.com/wiki/8/d6d33cf60bce2a2e4fb10a21be9591b8.html
-
Client.
create_group
(name)¶ 创建分组。
参数: name – 分组名字(30个字符以内) 返回: 返回的 JSON 数据包
-
Client.
get_groups
()¶ 查询所有分组。
返回: 返回的 JSON 数据包
-
Client.
get_group_by_id
(openid)¶ 查询用户所在分组。
参数: openid – 用户的OpenID 返回: 返回的 JSON 数据包
-
Client.
update_group
(group_id, name)¶ 修改分组名。
参数: - group_id – 分组 ID,由微信分配
- name – 分组名字(30个字符以内)
返回: 返回的 JSON 数据包
-
Client.
move_user
(user_id, group_id)¶ 移动用户分组。
参数: - user_id – 用户 ID,即收到的 Message 的 source
- group_id – 分组 ID
返回: 返回的 JSON 数据包
-
Client.
move_users
(user_id_list, group_id)¶ 批量移动用户分组。
参数: - user_id_list – 用户 ID 的列表(长度不能超过50)
- group_id – 分组 ID
返回: 返回的 JSON 数据包
-
Client.
delete_group
(group_id)¶ 删除分组。
参数: group_id – 要删除的分组的 ID 返回: 返回的 JSON 数据包
设置备注名¶
详细请参考 http://mp.weixin.qq.com/wiki/16/528098c4a6a87b05120a7665c8db0460.html
-
Client.
remark_user
(user_id, remark)¶ 设置备注名。
参数: - user_id – 设置备注名的用户 ID
- remark – 新的备注名,长度必须小于30字符
返回: 返回的 JSON 数据包
获取用户基本信息¶
详细请参考 http://mp.weixin.qq.com/wiki/1/8a5ce6257f1d3b2afb20f83e72b72ce9.html
-
Client.
get_user_info
(user_id, lang='zh_CN')¶ 获取用户基本信息。
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- lang – 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
返回: 返回的 JSON 数据包
-
Client.
get_users_info
(user_id_list, lang='zh_CN')¶ 批量获取用户基本信息。
参数: - user_id_list – 用户 ID 的列表
- lang – 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
返回: 返回的 JSON 数据包
账户管理¶
长链接转短链接接口和微信认证事件推送暂未添加,可自行实现。
生成带参数的二维码¶
详细请参考 http://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html
-
Client.
create_qrcode
(data)¶ 创建二维码。
参数: data – 你要发送的参数 dict 返回: 返回的 JSON 数据包
-
Client.
show_qrcode
(ticket)¶ 通过ticket换取二维码。
参数: ticket – 二维码 ticket 。可以通过 create_qrcode()
获取到返回: 返回的 Request 对象
获取用户列表¶
详细请参考 http://mp.weixin.qq.com/wiki/12/54773ff6da7b8bdc95b7d2667d84b1d4.html
-
Client.
get_followers
(first_user_id=None)¶ 获取关注者列表 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=获取关注者列表
参数: first_user_id – 可选。第一个拉取的OPENID,不填默认从头开始拉取 返回: 返回的 JSON 数据包
素材管理¶
新增临时素材¶
详细请参考 http://mp.weixin.qq.com/wiki/15/2d353966323806a202cd2deaafe8e557.html
-
Client.
upload_media
(media_type, media_file)¶ 上传临时多媒体文件。
参数: - media_type – 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
- media_file – 要上传的文件,一个 File-object
返回: 返回的 JSON 数据包
获取临时素材¶
详细请参考 http://mp.weixin.qq.com/wiki/9/677a85e3f3849af35de54bb5516c2521.html
-
Client.
download_media
(media_id)¶ 下载临时多媒体文件。
参数: media_id – 媒体文件 ID 返回: requests 的 Response 实例
新增永久素材¶
详细请参考 http://mp.weixin.qq.com/wiki/10/10ea5a44870f53d79449290dfd43d006.html
-
Client.
add_news
(articles)¶ 新增永久图文素材:
articles = [{ "title": TITLE, "thumb_media_id": THUMB_MEDIA_ID, "author": AUTHOR, "digest": DIGEST, "show_cover_pic": SHOW_COVER_PIC(0 / 1), "content": CONTENT, "content_source_url": CONTENT_SOURCE_URL } # 若新增的是多图文素材,则此处应有几段articles结构,最多8段 ] client.add_news(articles)
参数: articles – 如示例中的数组 返回: 返回的 JSON 数据包
-
Client.
upload_news_picture
(file)¶ 上传图文消息内的图片。
参数: file – 要上传的文件,一个 File-object 返回: 返回的 JSON 数据包
-
Client.
upload_permanent_media
(media_type, media_file)¶ 上传其他类型永久素材。
参数: - media_type – 媒体文件类型,分别有图片(image)、语音(voice)和缩略图(thumb)
- media_file – 要上传的文件,一个 File-object
返回: 返回的 JSON 数据包
-
Client.
upload_permanent_video
(title, introduction, video)¶ 上传永久视频。
参数: - title – 视频素材的标题
- introduction – 视频素材的描述
- video – 要上传的视频,一个 File-object
返回: requests 的 Response 实例
获取永久素材¶
详细请参考 http://mp.weixin.qq.com/wiki/12/3c12fac7c14cb4d0e0d4fe2fbc87b638.html
-
Client.
download_permanent_media
(media_id)¶ 获取永久素材。
参数: media_id – 媒体文件 ID 返回: requests 的 Response 实例
删除永久素材¶
详细请参考 http://mp.weixin.qq.com/wiki/7/2212203f4e17253b9aef77dc788f5337.html
-
Client.
delete_permanent_media
(media_id)¶ 删除永久素材。
参数: media_id – 媒体文件 ID 返回: 返回的 JSON 数据包
修改永久图文素材¶
详细请参考 http://mp.weixin.qq.com/wiki/10/c7bad9a463db20ff8ccefeedeef51f9e.html
-
Client.
update_news
(update_data)¶ 修改永久图文素材:
update_data = { "media_id":MEDIA_ID, "index":INDEX, "articles": { "title": TITLE, "thumb_media_id": THUMB_MEDIA_ID, "author": AUTHOR, "digest": DIGEST, "show_cover_pic": SHOW_COVER_PIC(0 / 1), "content": CONTENT, "content_source_url": CONTENT_SOURCE_URL } } client.update_news(update_data)
参数: update_data – 更新的数据,要包含 media_id(图文素材的 ID),index(要更新的文章在图文消息中的位置),articles(新的图文素材数据) 返回: 返回的 JSON 数据包
获取素材总数¶
详细请参考 http://mp.weixin.qq.com/wiki/5/a641fd7b5db7a6a946ebebe2ac166885.html
-
Client.
get_media_count
()¶ 获取素材总数。
返回: 返回的 JSON 数据包
获取素材列表¶
详细请参考 http://mp.weixin.qq.com/wiki/15/8386c11b7bc4cdd1499c572bfe2e95b3.html
-
Client.
get_media_list
(media_type, offset, count)¶ 获取素材列表。
参数: - media_type – 素材的类型,图片(image)、视频(video)、语音 (voice)、图文(news)
- offset – 从全部素材的该偏移位置开始返回,0表示从第一个素材返回
- count – 返回素材的数量,取值在1到20之间
返回: 返回的 JSON 数据包
模板消息¶
-
Client.
send_template_message
(user_id, template_id, data, url='')¶ 发送模板消息 详情请参考 http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html
参数: - user_id – 用户 ID 。 就是你收到的 Message 的 source
- template_id – 模板 ID。
- data – 用于渲染模板的数据。
- url – 模板消息的可选链接。
返回: 返回的 JSON 数据包
48001 – API Unauthorized¶
如果你遇到了这个错误,请检查你的微信公众号是否有调用该接口的权限。 参考: https://mp.weixin.qq.com/wiki/13/8d4957b72037e3308a0ca1b21f25ae8d.html
Message¶
Message 公共属性¶
除了 UnknownMessage, 每一种 Message 都包括以下属性:
name | value |
---|---|
message_id | 消息id,64位整型 |
target | 开发者账号( OpenID ) |
source | 发送方账号( OpenID ) |
time | 信息发送的时间,一个UNIX时间戳。 |
raw | 信息的原始 XML 格式 |
LinkMessage¶
name | value |
---|---|
type | ‘link’ |
title | 消息标题 |
description | 消息描述 |
url | 消息链接 |
LocationMessage¶
LocationMessage 的属性:
name | value |
---|---|
type | ‘location’ |
location | 一个元组。(纬度, 经度) |
scale | 地图缩放大小 |
label | 地理位置信息 |
VoiceMessage¶
VoiceMessage 的属性:
name | value |
---|---|
type | ‘voice’ |
media_id | 消息媒体 ID |
format | 声音格式 |
recognition | 语音识别结果 |
UnknownMessage¶
UnknownMessage 的属性:
name | value |
---|---|
type | ‘unknown’ |
raw | 请求的正文部分。标准的XML格式。 |
注解
如果你不为 WeRoBot 贡献代码,你完全可以无视掉 UnknownMessage 。在正常的使用中,WeRoBot应该不会收到 UnknownMessage ——除非 WeRoBot 停止开发。
Event¶
Event 公共属性¶
除了 UnknownEvent, 每一种 Event 都包括以下属性:
name | value |
---|---|
message_id | 消息id |
target | 开发者账号( OpenID ) |
source | 发送方账号( OpenID ) |
time | 信息发送的时间,一个UNIX时间戳 |
raw | 信息的原始 XML 格式 |
SubscribeEvent¶
SubscribeEvent 的属性:
name | value |
---|---|
type | ‘subscribe_event’ |
key | 事件 key 值。 当且仅当未关注公众号扫描二维码时存在 |
ticket | 二维码的 ticket。 当且仅当未关注公众号扫描二维码时存在 |
ScanEvent¶
ScanEvent 的属性:
name | value |
---|---|
type | ‘scan_event’ |
key | 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码 scene_id |
ticket | 二维码的 ticket,可用来换取二维码图片 |
ScanCodePushEvent¶
ScanCodePushEvent 的属性:
name | value |
---|---|
type | ‘scancode_push_event’ |
scan_type | 扫描类型,一般是qrcode |
scan_result | 扫描结果,即二维码对应的字符串信息 |
ScanCodeWaitMsgEvent¶
ScanCodeWaitMsgEvent 的属性:
name | value |
---|---|
type | ‘scancode_waitmsg_event’ |
scan_type | 扫描类型,一般是qrcode |
scan_result | 扫描结果,即二维码对应的字符串信息 |
PicSysphotoEvent¶
弹出系统拍照发图的事件推送的 Event。属性:
name | value |
---|---|
type | ‘pic_sysphoto_event’ |
key | 事件KEY值,由开发者在创建菜单时设定 |
count | 发送的图片数量 |
pic_list | 图片列表,例如 [{‘pic_md5_sum’: ‘123’}] |
PicPhotoOrAlbumEvent¶
弹出拍照或者相册发图的事件推送的 Event。属性:
name | value |
---|---|
type | ‘pic_photo_or_album_event’ |
key | 事件KEY值,由开发者在创建菜单时设定 |
count | 发送的图片数量 |
pic_list | 图片列表,例如 [{‘pic_md5_sum’: ‘123’}] |
PicWeixinEvent¶
弹出微信相册发图器的事件推送的 Event。属性:
name | value |
---|---|
type | ‘pic_weixin_event’ |
key | 事件KEY值,由开发者在创建菜单时设定 |
count | 发送的图片数量 |
pic_list | 图片列表,例如 [{‘pic_md5_sum’: ‘123’}] |
LocationSelectEvent¶
弹出地理位置选择器的事件推送的 Event。属性:
name | value |
---|---|
type | ‘location_select_event’ |
key | 事件KEY值,由开发者在创建菜单时设定 |
location_x | X坐标信息 |
location_y | Y坐标信息 |
scale | 精度,可理解为精度或者比例尺、越精细的话 scale 越高 |
label | 地理位置的字符串信息 |
poi_name | 朋友圈POI的名字,可能为 None |
LocationEvent¶
LocationEvent 的属性:
name | value |
---|---|
type | ‘location_event’ |
latitude | 地理位置纬度 |
longitude | 地理位置经度 |
precision | 地理位置精度 |
UserScanProductEvent¶
打开商品主页事件推送的 Event。属性:
name | value |
---|---|
type | ‘user_scan_product_event’ |
key_standard | 商品编码标准。 |
key_str | 商品编码内容。 |
country | 用户在微信内设置的国家。 |
province | 用户在微信内设置的省份。 |
city | 用户在微信内设置的城市。 |
sex | 用户的性别,1为男性,2为女性,0代表未知。 |
scene | 打开商品主页的场景,1为扫码,2为其他打开场景(如会话、收藏或朋友圈)。 |
ext_info | 调用“获取商品二维码接口”时传入的 extinfo,为标识参数。 |
UserScanProductEnterSessionEvent¶
当用户从商品主页进入公众号会话时推送的 Event。属性:
name | value |
---|---|
type | ‘user_scan_product_enter_session_event’ |
key_standard | 商品编码标准。 |
key_str | 商品编码内容。 |
ext_info | 调用“获取商品二维码接口”时传入的 extinfo,为标识参数。 |
UserScanProductAsyncEvent¶
当用户打开商品主页,微信会将该用户实时的地理位置信息以异步事件的形式推送的 Event。属性:
name | value |
---|---|
type | ‘user_scan_product_async_event’ |
key_standard | 商品编码标准。 |
key_str | 商品编码内容。 |
ext_info | 调用“获取商品二维码接口”时传入的 extinfo,为标识参数。 |
region_code | 用户的实时地理位置信息(目前只精确到省一级),可在国家统计局网站查到对应明细: http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201504/t20150415_712722.html |
UserScanProductVerifyActionEvent¶
提交审核的商品,完成审核后,微信会将审核结果以事件的形式推送的 Event。属性:
name | value |
---|---|
type | ‘user_scan_product_verify_action_event’ |
key_standard | 商品编码标准。 |
key_str | 商品编码内容。 |
result | 审核结果。verify_ok表示审核通过,verify_not_pass表示审核未通过。 |
reason_msg | 审核未通过的原因。 |
CardPassCheckEvent¶
生成的卡券通过审核时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘card_pass_check_event’ |
card_id | 卡券 ID。 |
refuse_reason | 审核不通过原因。 |
CardNotPassCheckEvent¶
生成的卡券未通过审核时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘card_not_pass_check_event’ |
card_id | 卡券 ID。 |
refuse_reason | 审核不通过原因。 |
UserGetCardEvent¶
用户在领取卡券时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘user_get_card_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
is_give_by_friend | 是否为转赠领取,1 代表是,0 代表否。 |
friend_user_name | 当 is_give_by_friend 为 1 时填入的字段,表示发起转赠用户的 openid。 |
outer_id | 未知。 |
old_user_card_code | 为保证安全,微信会在转赠发生后变更该卡券的 code 号,该字段表示转赠前的 code。 |
outer_str | 领取场景值,用于领取渠道数据统计。可在生成二维码接口及添加 Addcard 接口中自定义该字段的字符串值。 |
is_restore_member_card | 用户删除会员卡后可重新找回,当用户本次操作为找回时,该值为 1,否则为 0。 |
is_recommend_by_friend | 未知。 |
UserGiftingCardEvent¶
用户在转赠卡券时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘user_gifting_card_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
friend_user_name | 接收卡券用户的openid。 |
is_return_back | 是否转赠退回,0 代表不是,1 代表是。 |
is_chat_room | 是否是群转赠。 |
UserDelCardEvent¶
用户在删除卡券时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘user_del_card_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。自定义 code 及非自定义 code 的卡券被领取后都支持事件推送。 |
UserConsumeCardEvent¶
卡券被核销时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘user_consume_card_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
consume_source | 核销来源。 |
location_name | 门店名称,当前卡券核销的门店名称(只有通过自助核销和买单核销时才会出现该字段)。 |
staff_open_id | 核销该卡券核销员的 openid(只有通过卡券商户助手核销时才会出现)。 |
verify_code | 自助核销时,用户输入的验证码。 |
remark_amount | 自助核销时 ,用户输入的备注金额。 |
outer_str | 开发者发起核销时传入的自定义参数,用于进行核销渠道统计。 |
UserPayFromPayCellEvent¶
用户微信买单完成时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘user_pay_from_pay_cell_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
trans_id | 微信支付交易订单号(只有使用买单功能核销的卡券才会出现)。 |
location_id | 门店 ID,当前卡券核销的门店 ID(只有通过卡券商户助手和买单核销时才会出现)。 |
fee | 实付金额,单位为分。 |
original_fee | 应付金额,单位为分。 |
UserViewCardEvent¶
用户在进入会员卡时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘user_view_card_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
outer_str | 商户自定义二维码渠道参数,用于标识本次扫码打开会员卡来源来自于某个渠道值的二维码。 |
UserEnterSessionFromCardEvent¶
用户在卡券里点击查看公众号进入会话时(需要用户已经关注公众号),微信推送的 Event。属性:
name | value |
---|---|
type | ‘user_enter_session_from_card_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
UpdateMemberCardEvent¶
用户的会员卡积分余额发生变动时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘update_member_card_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
modify_bonus | 变动的积分值。 |
modify_balance | 变动的余额值。 |
CardSkuRemindEvent¶
当某个card_id的初始库存数大于200且当前库存小于等于100时,用户尝试领券,微信推送的 Event。属性:
name | value |
---|---|
type | ‘card_sku_remind_event’ |
card_id | 卡券 ID。 |
detail | 报警详细信息。 |
CardPayOrderEvent¶
用户的会员卡积分余额发生变动时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘card_pay_order_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
order_id | 本次推送对应的订单号。 |
status | 本次订单号的状态。 |
create_order_time | 购买券点时,支付二维码的生成时间。 |
pay_finish_time | 购买券点时,实际支付成功的时间。 |
desc | 支付方式,一般为微信支付充值。 |
free_coin_count | 剩余免费券点数量。 |
pay_coin_count | 剩余付费券点数量。 |
refund_free_coin_count | 本次变动的免费券点数量。 |
refund_pay_coin_count | 本次变动的付费券点数量 |
order_type | 所要拉取的订单类型。 |
memo | 系统备注,说明此次变动的缘由,如开通账户奖励、门店奖励、核销奖励以及充值、扣减。 |
receipt_info | 所开发票的详情。 |
SubmitMembercardUserInfoEvent¶
用户通过一键激活的方式提交信息并点击激活或者用户修改会员卡信息时,微信推送的 Event。属性:
name | value |
---|---|
type | ‘submit_membercard_user_info_event’ |
card_id | 卡券 ID。 |
user_card_code | code 序列号。 |
UnknownEvent¶
UnknownEvent 的属性:
name | value |
---|---|
type | ‘unknown_event’ |
raw | 请求的正文部分。标准的XML格式。 |
注解
如果你不为 WeRoBot 贡献代码,你完全可以无视掉 UnknownEvent 。在正常的使用中,WeRoBot应该不会收到 UnknownEvent ——除非 WeRoBot 停止开发。
回复¶
你可以在构建Reply时传入一个合法的 Message 对象来自动生成 source 和 target
reply = TextReply(message=message, content='Hello!')
TextReply¶
TextReply 是简单的文本消息,构造函数的参数如下:
name | value |
---|---|
content | 信息正文。 |
target | 信息的目标用户。通常是机器人用户。 |
source | 信息的来源用户。通常是发送信息的用户。 |
time | 信息发送的时间,一个UNIX时间戳。默认情况下会使用当前时间。 |
注解
如果你的handler返回了一个字符串, WeRoBot会自动将其转化为一个文本消息。
ImageReply¶
ImageReply 为回复图片消息,构造函数的参数如下:
name | value |
---|---|
media_id | 通过素材管理接口上传多媒体文件,得到的id。 |
target | 信息的目标用户。通常是机器人用户。 |
source | 信息的来源用户。通常是发送信息的用户。 |
time | 信息发送的时间,一个UNIX时间戳。默认情况下会使用当前时间。 |
VoiceReply¶
VoiceReply 为回复语音消息,构造函数的参数如下:
name | value |
---|---|
media_id | 通过素材管理接口上传多媒体文件,得到的id。 |
target | 信息的目标用户。通常是机器人用户。 |
source | 信息的来源用户。通常是发送信息的用户。 |
time | 信息发送的时间,一个UNIX时间戳。默认情况下会使用当前时间。 |
VideoReply¶
VideoReply 为回复视频消息,构造函数的参数如下:
name | value |
---|---|
media_id | 通过素材管理接口上传多媒体文件,得到的id。 |
title | 视频消息的标题。可为空。 |
description | 视频消息的描述。可为空。 |
target | 信息的目标用户。通常是机器人用户。 |
source | 信息的来源用户。通常是发送信息的用户。 |
time | 信息发送的时间,一个UNIX时间戳。默认情况下会使用当前时间。 |
ArticlesReply¶
ArticlesReply 是图文消息,构造函数的参数如下:
name | value |
---|---|
content | 信息正文。可为空。 |
target | 信息的目标用户。通常是机器人用户。 |
source | 信息的来源用户。通常是发送信息的用户。 |
time | 信息发送的时间,一个UNIX时间戳。默认情况下会使用当前时间。 |
你需要给 ArticlesReply 添加 Article 来增加图文。 Article 类位于 werobot.reply.Article 。
Article 的构造函数的参数如下:
name | value |
---|---|
title | 标题 |
description | 描述 |
img | 图片链接 |
url | 点击图片后跳转链接 |
注意,微信公众平台对图片链接有特殊的要求,详情可以在 消息接口使用指南 里看到。
在构造完一个 Article 后, 你需要通过 ArticlesReply 的 add_article 参数把它添加进去。就像这样:
from werobot.replies import ArticlesReply, Article
reply = ArticlesReply(message=message)
article = Article(
title="WeRoBot",
description="WeRoBot是一个微信机器人框架",
img="https://github.com/apple-touch-icon-144.png",
url="https://github.com/whtsky/WeRoBot"
)
reply.add_article(article)
注解
每个ArticlesReply中 最多添加10个Article 。
- 你也可以让你的 handler 返回一个列表, 里面每一个元素都是一个长度为四的列表,
WeRoBot 会将其自动转为 ArticlesReply 。就像这样:
import werobot robot = werobot.WeRoBot(token='tokenhere') @robot.text def articles(message): return [ [ "title", "description", "img", "url" ], [ "whtsky", "I wrote WeRoBot", "https://secure.gravatar.com/avatar/0024710771815ef9b74881ab21ba4173?s=420", "http://whouz.com/" ] ] robot.run()
MusicReply¶
MusicReply 是音乐消息,构造函数的参数如下:
name | value |
---|---|
target | 信息的目标用户。通常是机器人用户。 |
source | 信息的来源用户。通常是发送信息的用户。 |
time | 信息发送的时间,一个UNIX时间戳。默认情况下会使用当前时间。 |
title | 标题 |
description | 描述 |
url | 音乐链接 |
hq_url | 高质量音乐链接,WIFI环境优先使用该链接播放音乐。可为空 [3] |
- 你也可以让你的 handler 返回一个长度为三或四的列表, [3]
WeRoBot 会将其自动转为 MusicReply 。就像这样:
import werobot robot = werobot.WeRoBot(token='tokenhere') @robot.text def music(message): return [ "title", "description", "music_url", "hq_music_url" ] @robot.text def music2(message): return [ "微信你不懂爱", "龚琳娜最新力作", "http://weixin.com/budongai.mp3", ] robot.run()
[3] | (1, 2) 如果你省略了高质量音乐链接的地址, WeRoBot 会自动将音乐链接的地址用于高质量音乐链接。 |
TransferCustomerServiceReply¶
将消息转发到多客服,构造函数的参数如下:
name | value |
---|---|
target | 信息的目标用户。通常是机器人用户。 |
source | 信息的来源用户。通常是发送信息的用户。 |
time | 信息发送的时间,一个UNIX时间戳。默认情况下会使用当前时间。 |
account | 指定会话接入的客服账号 |
SuccessReply¶
给微信服务器回复 “success”。 假如服务器无法保证在五秒内处理并回复,需要回复 SuccessReply ,这样微信服务器才不会对此作任何处理,并且不会发起重试。
Config¶
WeRoBot 使用 WeRoBot.Config
类来存储配置信息。 WeRoBot
类实例的 config
属性是一个 werobot.config.Config
实例。
Config
继承自 dict 。因此, 你可以像使用普通 dict 一样使用它
from werobot import WeRoBot
robot = WeRoBot(token='2333')
robot.config.update(
HOST='0.0.0.0',
PORT=80
)
当然, 你也可以先创建一个 Config ,然后在初始化 WeRobot
的时候传入自己的 Config
from werobot.config import Config
config = Config(
TOKEN="token from config!"
)
robot = WeRoBot(config=config, token="token from init")
assert robot.token == "token from config!"
注解
如果你在初始化 WeRoBot
时传入了 config
参数, WeRoBot
会忽略除 logger
外其他所有的初始化参数。 如果你需要对 WeRoBot
进行一些配置操作, 请修改 Config 。
与普通 dict 不同的是, 你可以先把配置文件保存在一个对象或是文件中, 然后在 Config
中导入配置
from werobot import WeRoBot
robot = WeRoBot(token='2333')
class MyConfig(object):
HOST = '0.0.0.0'
PORT = 80
robot.config.from_object(MyConfig)
robot.config.from_pyfile("config.py")
默认配置¶
dict(
TOKEN=None,
SERVER="auto",
HOST="127.0.0.1",
PORT="8888",
SESSION_STORAGE=None,
APP_ID=None,
APP_SECRET=None,
ENCODING_AES_KEY=None
)
与其他 Web 框架集成¶
WeRoBot 可以作为独立服务运行,也可以集成在其他 Web 框架中一同运行。
Django¶
WeRoBot 支持 Django 1.8+。
首先,在一个文件中写好你的微信机器人
# Filename: robot.py
from werobot import WeRoBot
myrobot = WeRoBot(token='token')
@myrobot.handler
def hello(message):
return 'Hello World!'
然后,在你 Django 项目中的 urls.py
中调用 werobot.contrib.django.make_view()
,将 WeRoBot 集成进 Django
from django.conf.urls import patterns, include, url
from werobot.contrib.django import make_view
from robot import myrobot
urlpatterns = patterns('',
url(r'^robot/', make_view(myrobot)),
)
-
werobot.contrib.django.
make_view
(robot)¶ 为一个 BaseRoBot 生成 Django view。
参数: robot – 一个 BaseRoBot 实例。 返回: 一个标准的 Django view
Flask¶
首先, 同样在文件中写好你的微信机器人
# Filename: robot.py
from werobot import WeRoBot
myrobot = WeRoBot(token='token')
@myrobot.handler
def hello(message):
return 'Hello World!'
然后, 在 Flask 项目中为 Flask 实例集成 WeRoBot
from flask import Flask
from robot import myrobot
from werobot.contrib.flask import make_view
app = Flask(__name__)
app.add_url_rule(rule='/robot/', # WeRoBot 挂载地址
endpoint='werobot', # Flask 的 endpoint
view_func=make_view(myrobot),
methods=['GET', 'POST'])
-
werobot.contrib.flask.
make_view
(robot)¶ 为一个 BaseRoBot 生成 Flask view。
Usage
from werobot import WeRoBot robot = WeRoBot(token='token') @robot.handler def hello(message): return 'Hello World!' from flask import Flask from werobot.contrib.flask import make_view app = Flask(__name__) app.add_url_rule(rule='/robot/', # WeRoBot 的绑定地址 endpoint='werobot', # Flask 的 endpoint view_func=make_view(robot), methods=['GET', 'POST'])
参数: robot – 一个 BaseRoBot 实例 返回: 一个标准的 Flask view
Bottle¶
在你的 Bottle App 中集成 WeRoBot
from werobot import WeRoBot
myrobot = WeRoBot(token='token')
@myrobot.handler
def hello(message):
return 'Hello World!'
from bottle import Bottle
from werobot.contrib.bottle import make_view
app = Bottle()
app.route('/robot', # WeRoBot 挂载地址
['GET', 'POST'],
make_view(myrobot))
-
werobot.contrib.bottle.
make_view
(robot)¶ 为一个 BaseRoBot 生成 Bottle view。
Usage
from werobot import WeRoBot robot = WeRoBot(token='token') @robot.handler def hello(message): return 'Hello World!' from bottle import Bottle from werobot.contrib.bottle import make_view app = Bottle() app.route( '/robot', # WeRoBot 挂载地址 ['GET', 'POST'], make_view(robot) )
参数: robot – 一个 BaseRoBot 实例 返回: 一个标准的 Bottle view
Tornado¶
最简单的 Hello World
import tornado.ioloop
import tornado.web
from werobot import WeRoBot
from werobot.contrib.tornado import make_handler
myrobot = WeRoBot(token='token')
@myrobot.handler
def hello(message):
return 'Hello World!'
application = tornado.web.Application([
(r"/robot/", make_handler(myrobot)),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
-
werobot.contrib.tornado.
make_handler
(robot)¶ 为一个 BaseRoBot 生成 Tornado Handler。
Usage
import tornado.ioloop import tornado.web from werobot import WeRoBot from tornado_werobot import make_handler robot = WeRoBot(token='token') @robot.handler def hello(message): return 'Hello World!' application = tornado.web.Application([ (r"/", make_handler(robot)), ])
参数: robot – 一个 BaseRoBot 实例。 返回: 一个标准的 Tornado Handler
错误页面¶
WeRoBot
自带了一个错误页面,它将会在 Signature 验证不通过的时候返回错误页面。
定制错误页面¶
如果你想为 WeRoBot
指定 Signature 验证不通过时显示的错误页面,可以这么做:
@robot.error_page
def make_error_page(url):
return "<h1>喵喵喵 %s 不是给麻瓜访问的快走开</h1>" % url
贡献指南¶
有许多种为 WeRoBot 做贡献的方式, 包括但并不仅限于
- 上报 bug
- 提交 Feature Request
- 贡献代码
- 加入 WeRoBot QQ 群(283206829) 帮助解答问题
- 把 WeRoBot 安利给你周围的人 :)
贡献代码¶
如果你希望为 WeRoBot 贡献代码, 请现在 GitHub 上 Fork WeRoBot 仓库, 然后在 master
分支上开一个新的分支。
如果你的贡献的代码是修复 Bug , 请确认这个 Bug 已经有了对应的 Issue (如果没有, 请先创建一个); 然后在 Pull Request 的描述里面引用这个 Bug 的 Issue ID , 就像这样(假设 Issue ID 为 153)
Fix #153
环境搭建¶
建议使用 virtualenv
创建虚拟环境进行开发, 然后安装开发环境需要的 packages。
关于 Python 版本, 推荐使用 Python 3.6 进行开发。
如果使用的是 3.x 版本
# Python 3.5
python -m venv venv
如果是其他版本
# virtualenv is highly recommended.
virtualenv venv
# Activate virtualenv.
source venv/bin/activate
# Install dev packages.
pip install -r dev-requirements.txt
代码风格¶
我们使用 yapf 进行代码格式化。 在提交代码之前,请格式化一下你的代码
# Install yapf
pip install yapf
# format code
yapf -p --recursive -i werobot/
你也可以 安装 yapf Pre-Commit Hook 来自动进行代码格式化工作。
测试¶
在代码提交之前, 请先运行本地的测试。每次提交之后会有在线的 CI 运行更多版本兼容性的测试, 请密切关注测试结果。
# Run tests locally.
python setup.py test
当然也可以使用 tox 在本地运行多版本的兼容性测试。
# Run multi-version tests locally.
tox
如果你的 Pull Request 添加了新的模块或者功能,请为这些代码添加必要的测试。 所有的测试文件都在 tests 文件夹下。
当一切开发完成之后, 可以发 Pull Request 到 develop
分支, 我们会为你的代码做 Review。同时 CI 也会自动运行测试。
注解
我们只会 Merge 通过了测试的代码。
如果一切没有问题, 我们将合并你的代码到 develop
分支, 并最终发布在 master
分支的稳定版本。
API¶
应用对象¶
-
class
werobot.robot.
BaseRoBot
(token=None, logger=None, enable_session=None, session_storage=None, app_id=None, app_secret=None, encoding_aes_key=None, config=None, **kwargs)¶ BaseRoBot 是整个应用的核心对象,负责提供 handler 的维护,消息和事件的处理等核心功能。
参数: - logger – 用来输出 log 的 logger,如果是
None
,将使用 werobot.logger - config – 用来设置的
werobot.config.Config
对象
注解
对于下面的参数推荐使用
Config
进行设置, 并且以下参数均已 deprecated。参数: - token – 微信公众号设置的 token (deprecated)
- enable_session – 是否开启 session (deprecated)
- session_storage – 用来储存 session 的对象,如果为
None
, 将使用 werobot.session.sqlitestorage.SQLiteStorage (deprecated) - app_id – 微信公众号设置的 app id (deprecated)
- app_secret – 微信公众号设置的 app secret (deprecated)
- encoding_aes_key – 用来加解密消息的 aes key (deprecated)
-
add_filter
(func, rules)¶ 为 BaseRoBot 添加一个
filter handler
。参数: - func – 如果 rules 通过,则处理该消息的 handler。
- rules – 一个 list,包含要匹配的字符串或者正则表达式。
返回: None
-
add_handler
(func, type='all')¶ 为 BaseRoBot 实例添加一个 handler。
参数: - func – 要作为 handler 的方法。
- type – handler 的种类。
返回: None
-
card_not_pass_check
(f)¶ 为生成的卡券未通过审核
(card_not_pass_check_event)
事件添加一个 handler 方法的装饰器。
-
card_pass_check
(f)¶ 为生成的卡券通过审核
(card_pass_check_event)
事件添加一个 handler 方法的装饰器。
-
card_pay_order
(f)¶ 为券点发生变动
(card_pay_order_event)
事件添加一个 handler 方法的装饰器。
-
card_sku_remind
(f)¶ 为某个card_id的初始库存数大于200且当前库存小于等于100
(card_sku_remind_event)
事件添加一个 handler 方法的装饰器。
-
check_signature
(timestamp, nonce, signature)¶ 根据时间戳和生成签名的字符串 (nonce) 检查签名。
参数: - timestamp – 时间戳
- nonce – 生成签名的随机字符串
- signature – 要检查的签名
返回: 如果签名合法将返回
True
,不合法将返回False
-
click
(f)¶ 为自定义菜单事件
(click)
事件添加一个 handler 方法的装饰器。
-
error_page
(f)¶ 为 robot 指定 Signature 验证不通过时显示的错误页面。
Usage:
@robot.error_page def make_error_page(url): return "<h1>喵喵喵 %s 不是给麻瓜访问的快走开</h1>" % url
-
filter
(*args)¶ 为文本
(text)
消息添加 handler 的简便方法。使用
@filter("xxx")
,@filter(re.compile("xxx"))
或@filter("xxx", "xxx2")
的形式为特定内容添加 handler。
-
get_encrypted_reply
(message)¶ 对一个指定的 WeRoBot Message ,获取 handlers 处理后得到的 Reply。 如果可能,对该 Reply 进行加密。 返回 Reply Render 后的文本。
参数: message – 一个 WeRoBot Message 实例。 返回: reply (纯文本)
-
get_reply
(message)¶ 根据 message 的内容获取 Reply 对象。
参数: message – 要处理的 message 返回: 获取的 Reply 对象
-
handler
(f)¶ 为每一条消息或事件添加一个 handler 方法的装饰器。
-
image
(f)¶ 为图像
(image)
消息添加一个 handler 方法的装饰器。
-
key_click
(key)¶ 为自定义菜单
(click)
事件添加 handler 的简便方法。@key_click(‘KEYNAME’) 用来为特定 key 的点击事件添加 handler 方法。
-
link
(f)¶ 为链接
(link)
消息添加一个 handler 方法的装饰器。
-
location
(f)¶ 为位置
(location)
消息添加一个 handler 方法的装饰器。
-
location_event
(f)¶ 为上报位置
(location_event)
事件添加一个 handler 方法的装饰器。
-
location_select
(f)¶ 为弹出地理位置选择器的事件推送
(location_select_event)
事件添加一个 handler 方法的装饰器。
-
parse_message
(body, timestamp=None, nonce=None, msg_signature=None)¶ 解析获取到的 Raw XML ,如果需要的话进行解密,返回 WeRoBot Message。 :param body: 微信服务器发来的请求中的 Body。 :return: WeRoBot Message
-
pic_photo_or_album
(f)¶ 为弹出拍照或者相册发图的事件推送
(pic_photo_or_album_event)
事件添加一个 handler 方法的装饰器。
-
pic_sysphoto
(f)¶ 为弹出系统拍照发图的事件推送
(pic_sysphoto_event)
事件添加一个 handler 方法的装饰器。
-
pic_weixin
(f)¶ 为弹出微信相册发图器的事件推送
(pic_weixin_event)
事件添加一个 handler 方法的装饰器。
-
scan
(f)¶ 为扫描推送
(scan)
事件添加一个 handler 方法的装饰器。
-
scancode_push
(f)¶ 为扫描推送
(scancode_push)
事件添加一个 handler 方法的装饰器。
-
scancode_waitmsg
(f)¶ 为扫描弹消息
(scancode_waitmsg)
事件添加一个 handler 方法的装饰器。
-
shortvideo
(f)¶ 为小视频
(shortvideo)
消息添加一个 handler 方法的装饰器。
-
submit_membercard_user_info
(f)¶ 为用户通过一键激活的方式提交信息并点击激活或者用户修改会员卡信息
(submit_membercard_user_info_event)
事件添加一个 handler 方法的装饰器。
-
subscribe
(f)¶ 为被关注
(subscribe)
事件添加一个 handler 方法的装饰器。
-
text
(f)¶ 为文本
(text)
消息添加一个 handler 方法的装饰器。
-
unknown
(f)¶ 为未知类型
(unknown)
消息添加一个 handler 方法的装饰器。
-
unknown_event
(f)¶ 为未知类型
(unknown_event)
事件添加一个 handler 方法的装饰器。
-
unsubscribe
(f)¶ 为被取消关注
(unsubscribe)
事件添加一个 handler 方法的装饰器。
-
update_member_card
(f)¶ 为用户的会员卡积分余额发生变动
(update_member_card_event)
事件添加一个 handler 方法的装饰器。
-
user_consume_card
(f)¶ 为卡券被核销
(user_consume_card_event)
事件添加一个 handler 方法的装饰器。
-
user_del_card
(f)¶ 为用户删除卡券
(user_del_card_event)
事件添加一个 handler 方法的装饰器。
-
user_enter_session_from_card
(f)¶ 为用户卡券里点击查看公众号进入会话
(user_enter_session_from_card_event)
事件添加一个 handler 方法的装饰器。
-
user_get_card
(f)¶ 为用户领取卡券
(user_get_card_event)
事件添加一个 handler 方法的装饰器。
-
user_gifting_card
(f)¶ 为用户转赠卡券
(user_gifting_card_event)
事件添加一个 handler 方法的装饰器。
-
user_pay_from_pay_cell
(f)¶ 为微信买单完成
(user_pay_from_pay_cell_event)
事件添加一个 handler 方法的装饰器。
-
user_scan_product
(f)¶ 为打开商品主页事件推送
(user_scan_product_event)
事件添加一个 handler 方法的装饰器。
-
user_scan_product_async
(f)¶ 为地理位置信息异步推送
(user_scan_product_async_event)
事件添加一个 handler 方法的装饰器。
-
user_scan_product_enter_session
(f)¶ 为进入公众号事件推送
(user_scan_product_enter_session_event)
事件添加一个 handler 方法的装饰器。
-
user_scan_product_verify_action
(f)¶ 为商品审核结果推送
(user_scan_product_verify_action_event)
事件添加一个 handler 方法的装饰器。
-
user_view_card
(f)¶ 为用户进入会员卡
(user_view_card_event)
事件添加一个 handler 方法的装饰器。
-
video
(f)¶ 为视频
(video)
消息添加一个 handler 方法的装饰器。
-
view
(f)¶ 为链接
(view)
事件添加一个 handler 方法的装饰器。
-
voice
(f)¶ 为语音
(voice)
消息添加一个 handler 方法的装饰器。
- logger – 用来输出 log 的 logger,如果是
-
class
werobot.robot.
WeRoBot
(token=None, logger=None, enable_session=None, session_storage=None, app_id=None, app_secret=None, encoding_aes_key=None, config=None, **kwargs)¶ WeRoBot 是一个继承自 BaseRoBot 的对象,在 BaseRoBot 的基础上使用了 bottle 框架, 提供接收微信服务器发来的请求的功能。
配置对象¶
Session 对象¶
-
class
werobot.session.sqlitestorage.
SQLiteStorage
(filename='werobot_session.sqlite3')¶ SQLiteStorge 会把 Session 数据储存在一个 SQLite 数据库文件中
import werobot from werobot.session.sqlitestorage import SQLiteStorage session_storage = SQLiteStorage robot = werobot.WeRoBot(token="token", enable_session=True, session_storage=session_storage)
参数: filename – SQLite数据库的文件名, 默认是 werobot_session.sqlite3
-
class
werobot.session.filestorage.
FileStorage
(filename='werobot_session')¶ FileStorage 会把你的 Session 数据以 dbm 形式储存在文件中。
参数: filename – 文件名, 默认为 werobot_session
-
class
werobot.session.mongodbstorage.
MongoDBStorage
(collection)¶ MongoDBStorage 会把你的 Session 数据储存在一个 MongoDB Collection 中
import pymongo import werobot from werobot.session.mongodbstorage import MongoDBStorage collection = pymongo.MongoClient()["wechat"]["session"] session_storage = MongoDBStorage(collection) robot = werobot.WeRoBot(token="token", enable_session=True, session_storage=session_storage)
你需要安装
pymongo
才能使用 MongoDBStorage 。参数: collection – 一个 MongoDB Collection。
-
class
werobot.session.redisstorage.
RedisStorage
(redis, prefix='ws_')¶ RedisStorage 会把你的 Session 数据储存在 Redis 中
import redis import werobot from werobot.session.redisstorage import RedisStorage db = redis.Redis() session_storage = RedisStorage(db, prefix="my_prefix_") robot = werobot.WeRoBot(token="token", enable_session=True, session_storage=session_storage)
你需要安装
redis
才能使用 RedisStorage 。参数: - redis – 一个 Redis Client。
- prefix – Reids 中 Session 数据 key 的 prefix 。默认为
ws_
-
class
werobot.session.saekvstorage.
SaeKVDBStorage
(prefix='ws_')¶ SaeKVDBStorage 使用SAE 的 KVDB 来保存你的session
import werobot from werobot.session.saekvstorage import SaeKVDBStorage session_storage = SaeKVDBStorage() robot = werobot.WeRoBot(token="token", enable_session=True, session_storage=session_storage)
需要先在后台开启 KVDB 支持
参数: prefix – KVDB 中 Session 数据 key 的 prefix 。默认为 ws_
-
class
werobot.session.mysqlstorage.
MySQLStorage
(conn)¶ MySQLStorage 会把你的 Session 数据储存在 MySQL 中
import MySQLdb # 使用 mysqlclient import werobot from werobot.session.mysqlstorage import MySQLStorage conn = MySQLdb.connect(user='', db='', passwd='', host='') session_storage = MySQLStorage(conn) robot = werobot.WeRoBot(token="token", enable_session=True, session_storage=session_storage)
或者
import pymysql # 使用 pymysql import werobot from werobot.session.mysqlstorage import MySQLStorage session_storage = MySQLStorage( conn=pymysql.connect( user='喵', password='喵喵', db='werobot', host='127.0.0.1', charset='utf8' )) robot = werobot.WeRoBot(token="token", enable_session=True, session_storage=session_storage)
你需要安装一个 MySQL Client 才能使用 MySQLStorage,比如
pymysql
,mysqlclient
。理论上符合 PEP-249 的库都可以使用, 测试时使用的是
pymysql
。参数: conn – PEP-249 定义的 Connection 对象
Changelog¶
Version 1.5.0¶
- 为正则匹配的 handler 加入匹配后的
Match Object
作为参数(#305)(Author: cxgreat2014)
Version 1.4.0¶
- 增加
werobot.messages.events.CardPassCheckEvent
- 增加
werobot.messages.events.CardNotPassCheckEvent
- 增加
werobot.messages.events.UserGetCardEvent
- 增加
werobot.messages.events.UserGiftingCardEvent
- 增加
werobot.messages.events.UserDelCardEvent
- 增加
werobot.messages.events.UserConsumeCardEvent
- 增加
werobot.messages.events.UserPayFromPayCellEvent
- 增加
werobot.messages.events.UserViewCardEvent
- 增加
werobot.messages.events.UserEnterSessionFromCardEvent
- 增加
werobot.messages.events.UpdateMemberCardEvent
- 增加
werobot.messages.events.CardSkuRemindEvent
- 增加
werobot.messages.events.CardPayOrderEvent
- 增加
werobot.messages.events.SubmitMembercardUserInfoEvent
Version 1.3.0¶
- 增加
werobot.messages.events.UserScanProductEvent
- 增加
werobot.messages.events.UserScanProductEnterSessionEvent
- 增加
werobot.messages.events.UserScanProductAsyncEvent
- 增加
werobot.messages.events.UserScanProductVerifyActionEvent
- 增加
werobot.messages.events.PicSysphotoEvent
- 增加
werobot.messages.events.PicPhotoOrAlbumEvent
- 增加
werobot.messages.events.PicWeixinEvent
- 增加
werobot.messages.events.LocationSelectEvent
Version 1.2.0¶
- 增加
werobot.session.mysqlstorage.MySQLStorage
- 增加
werobot.messages.events.ScanCodePushEvent
- 增加
werobot.messages.events.ScanCodeWaitMsgEvent
- 增加
werobot.robot.BaseRoBot.add_filter()
werobot.utils.generate_token()
在 Python 3.6+ 下优先使用secrets.choice
来随机生成 token- 修复
werobot.client.Client.get_media_list()
的调用参数错误 (#208) - 修复了某些情况下 Client 中文编码不正确的问题 (#250)
- Handler 中的 Exception 现在会以 Error level 记录到 logger 中
- 在文档中增加了独立的 API 部分
- 添加了
video
和shortvideo
的修饰器 - 增加了
werobot.session.saekvstorage.SaeKVDBStorage
的测试 - 增加了对 Django 2.0 的测试
- 抛弃对 Django < 1.8 、 Django 1.9 、 Django 1.10 的支持
Version 1.1.1¶
- 修复
werobot.client.Client.create_menu()
文档中的错误 - 在
werobot.client.Client.send_music_message()
的文档中提示了可能的缩略图不显示的问题
Version 1.1.0¶
- 为
werobot.robot.BaseRoBot
增加client
property - 允许在初始化
werobot.robot.BaseRoBot
时传入 Config 。注意如果传入了 config , BaseRoBot 会忽略除config
与logger
外的其他所有的参数。 - deprecate
werobot.robot.BaseRoBot
的enable_session
参数 - Session Storage 现在是惰性加载的了; 如果希望关闭 Session , 请将 Config 中的
SESSION_STORAGE
设为False
(#189) - 修复了打包时 error.html 被忽略导致的默认错误页面错误的问题 (#194)
- 允许使用
reply.time
的方式快速读取 Reply 属性 - 完善 WeRoBot.Client —— 微信 API 操作类 中自定义菜单、消息管理、素材管理、用户管理、账户管理、素材管理部分的 API
- 修复了直接 GET 访问 Robot 主页返回 500 的问题
Version 1.0.0¶
- 增加对消息加解密的支持
- 重写 werobot.messages, 完善对 Event 的支持
- 将微信消息的 id 属性重命名为 message_id
- 增加
werobot.reply.SuccessReply
- 增加
werobot.reply.ImageReply
- 增加
werobot.reply.VoiceReply
- 增加
werobot.reply.VideoReply
- 删除
werobot.reply.create_reply()
- 为
werobot.reply.WeChatReply
增加process_args
方法 - 为
werobot.robot.BaseRoBot
增加parse_message
方法 - 为
werobot.robot.BaseRoBot
增加get_encrypted_reply
方法 - 删去了 Reply 中过时的 flag
- 修复
werobot.session.filestorage.FileStorage
在 PyPy 下的兼容性问题 - 增加
werobot.session.sqlitestorage.SQLiteStorage
- 将默认的 SessionBackend 切换为
werobot.session.sqlitestorage.SQLiteStorage
- 将图文消息单个消息的渲染函数放到
werobot.replies.Article
内 - 取消对 Python2.6, Python3.3 的支持
- 增加与 Django 1.6+, Flask, Bottle, Tornado 集成的支持
- 替换 inspect.getargspec()
Version 0.6.1¶
- Fix wrong URL in
upload_media
- Add VideoMessage
Version 0.6.0¶
- Add
@werobot.filter
- Add
werobot.session.saekvstorage
- Add support for Weixin Pay (
werobot.pay.WeixinPayClient
) - Add
werobot.reply.TransferCustomerServiceReply
- Fix FileStorage’s bug
Version 0.5.3¶
- Fix: can’t handle request for root path
Version 0.5.2¶
- Fix Python 3 support
Version 0.5.1¶
- Fix typo
Version 0.5.0¶
- Add
werobot.client
- Add
werobot.config
- Add
werobot.logger
- Add
@werobot.key_click
(Thanks @tg123) - Support Location Event
- Use smart args
- Friendly 403 page
- Improved server support
- Enable session by default.
- Drop
werobot.testing.make_text_message
- Drop
werobot.testing.make_image_message
- Drop
werobot.testing.make_location_message
- Drop
werobot.testing.make_voice_message
- Drop
werobot.testing.WeTest.send
- Rewrite
werobot.message
- Rewrite testing case
Version 0.4.1¶
- Add VoiceMessage
- Add
message.raw
: Raw XML of message - Rename
UnknownMessage.content
toUnknownMessage.raw
- Fix a bug when signature is invalid.
- Ignore session when receive UnknownMessage
Version 0.4.0¶
- Add session support
- Add logging support
- Rename
werobot.test
towerobot.testing
- Handlers added by
@robot.handler
will have the lowest priority.
Version 0.3.5¶
- Bug fix: Make
BaseRoBot
importable
Version 0.3.4¶
- Rename
WeRoBot.app
toWeRoBot.wsgi
- Add
BaseRoBot
class. It’s useful for creating extensions. - Reorganized documents.
Version 0.3.3¶
- Add
host
param in werobot.run - Update EventMessage
- Add LinkMessage
Version 0.3.1¶
- Add
server
param in werobot.run
Version 0.3.0¶
- Add new messages and replies support for WeChat 4.5