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 或者是 EventMessage 表示用户发来的消息,如文本消息、图片消息; Event 则表示用户触发的事件, 如关注事件、扫描二维码事件。 在消息解析、转换完成后, WeRoBot 会讲消息转交给 Handler 进行处理,并将 Handler 的返回值返回给微信服务器。

在刚才的 Hello World 中, 我们编写的

@robot.handler
def hello(message):
    return 'Hello World!'

就是一个简单的 Handler@robot.handler 意味着 robot 会将所有接收到的消息( 包括 MessageEvent ) 都转交给这个 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。

修饰符 类型
robot.text 文本 (Message)
robot.image 图像 (Message)
robot.location 位置 (Message)
robot.link 链接 (Message)
robot.voice 语音 (Message)
robot.unknown 未知类型 (Message)
robot.subscribe 被关注 (Event)
robot.unsubscribe 被取消关注 (Event)
robot.click 自定义菜单事件 (Event)
robot.view 链接 (Event)
robot.scancode_push 扫描推送 (Event)
robot.scancode_waitmsg 扫描弹消息 (Event)
robot.pic_sysphoto 弹出系统拍照发图(Event)
robot.pic_photo_or_album 弹出拍照或者相册发图(Event)
robot.pic_weixin 弹出微信相册发图器(Event)
robot.location_select 弹出地理位置选择器(Event)
robot.scan 已关注扫描二维码(Event)
robot.user_scan_product 打开商品主页事件推送(Event)
robot.user_scan_product_enter_session 进入公众号事件推送(Event)
robot.user_scan_product_async 地理位置信息异步推送(Event)
robot.user_scan_product_verify_action 商品审核结果推送(Event)
robot.card_pass_check 卡券通过审核 (Event)
robot.card_not_pass_check 卡券未通过审核 (Event)
robot.user_get_card 用户领取卡券 (Event)
robot.user_gifting_card 用户转赠卡券 (Event)
robot.user_del_card 用户删除卡券 (Event)
robot.user_consume_card 卡券被核销 (Event)
robot.user_pay_from_pay_cell 微信买单完成 (Event)
robot.user_view_card 用户进入会员卡 (Event)
robot.user_enter_session_from_card 用户卡券里点击查看公众号进入会话 (Event)
robot.update_member_card 会员卡积分余额发生变动 (Event)
robot.card_sku_remind 库存警告 (Event)
robot.card_pay_order 券点发生变动 (Event)
robot.submit_membercard_user_info 激活卡券 (Event)
robot.location_event 上报位置 (Event)
robot.unknown_event 未知类型 (Event)

额,这个 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 —— 回应有指定文本的消息

filter() 是对 text() 修饰符的改进。

现在你可以写这样的代码

@robot.filter("a")
def a():
    return "正文为 a "

import re


@robot.filter(re.compile(".*?bb.*?"))
def b():
    return "正文中含有 b "

@robot.filter(re.compile(".*?c.*?"), "d")
def c():
    return "正文中含有 c 或正文为 d"

这段代码等价于

@robot.text
def a(message):
    if message.content == "a":
        return "正文为 a "
import re


@robot.text
def b():
    if re.compile(".*?bb.*?").match(message.content):
        return "正文中含有 b "

@robot.text
def c():
    if re.compile(".*?c.*?").match(message.content) or message.content == "d":
        return "正文中含有 c 或正文为 d"

如果你想通过修饰符以外的方法添加 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(menu_data)

创建自定义菜单:

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

Client.get_menu()

查询自定义菜单。

返回:返回的 JSON 数据包

自定义菜单删除接口

详细请参考 http://mp.weixin.qq.com/wiki/3/de21624f2d0d3dafde085dafaa226743.html

Client.delete_menu()

删除自定义菜单。

返回:返回的 JSON 数据包

个性化菜单接口

详细请参考 http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html

Client.create_custom_menu(menu_data, matchrule)

创建个性化菜单:

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 数据包

Client.delete_custom_menu(menu_id)

删除个性化菜单。

参数:menu_id – 菜单的 ID
返回:返回的 JSON 数据包
Client.match_custom_menu(user_id)

测试个性化菜单匹配结果。

参数:user_id – 要测试匹配的用户 ID
返回:返回的 JSON 数据包

获取自定义菜单配置接口

详细请参考 http://mp.weixin.qq.com/wiki/14/293d0cb8de95e916d1216a33fcb81fd6.html

Client.get_custom_menu_config()

获取自定义菜单配置接口。

返回:返回的 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 数据包

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 格式

TextMessage

TextMessage 的属性:

name value
type ‘text’
content 信息的内容

ImageMessage

ImageMessage 的属性:

name value
type ‘image’
img 图片网址。你可以从这个网址下到图片

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 语音识别结果

VideoMessage

VideoMessage 的属性:

name value
type ‘video’
media_id 消息媒体 ID
thumb_media_id 视频缩略图媒体 ID

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。 当且仅当未关注公众号扫描二维码时存在

UnSubscribeEvent

UnSubscribeEvent 的属性:

name value
type ‘unsubscribe_event’

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

ClickEvent

ClickEvent 的属性:

name value
type ‘click_event’
key 事件 key 值。

ViewEvent

ViewEvent 的属性:

name value
type ‘view_event’
key 事件 key 值。

LocationEvent

LocationEvent 的属性:

name value
type ‘location_event’
latitude 地理位置纬度
longitude 地理位置经度
precision 地理位置精度

TemplateSendJobFinishEvent

模版消息发送任务完成后的 Event 通知。 属性:

name value
status 发送是否成功。为 ‘success’ 或失败原因

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 对象来自动生成 sourcetarget

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 后, 你需要通过 ArticlesReplyadd_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

小工具

Token 生成器

WeRoBot帮你准备了一个Token生成器:

import werobot.utils

print(werobot.utils.generate_token())

贡献指南

有许多种为 WeRoBot 做贡献的方式, 包括但并不仅限于

贡献代码

如果你希望为 WeRoBot 贡献代码, 请现在 GitHub 上 Fork WeRoBot 仓库, 然后在 develop 分支上开一个新的分支。

注解

master 分支存放着 WeRoBot 最新 release 版本的代码。 所有的开发工作都应该在 develop 分支上展开

如果你的贡献的代码是修复 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) 消息添加一个 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 方法的装饰器。

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 框架, 提供接收微信服务器发来的请求的功能。

run(server=None, host=None, port=None, enable_pretty_logging=True)

运行 WeRoBot。

参数:
  • server – 传递给 Bottle 框架 run 方法的参数,详情见 bottle 文档
  • host – 运行时绑定的主机地址
  • port – 运行时绑定的主机端口
  • enable_pretty_logging – 是否开启 log 的输出格式优化

配置对象

class werobot.config.Config
from_object(obj)

在给定的 Python 对象中读取配置。

参数:obj – 一个 Python 对象
from_pyfile(filename)

在一个 Python 文件中读取配置。

参数:filename – 配置文件的文件名
返回:如果读取成功,返回 True,如果失败,会抛出错误异常

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,比如 pymysqlmysqlclient

理论上符合 PEP-249 的库都可以使用, 测试时使用的是 pymysql

参数:conn

PEP-249 定义的 Connection 对象

log

werobot.logger.enable_pretty_logging(logger, level='info')

按照配置开启 log 的格式化优化。

参数:
  • logger – 配置的 logger 对象
  • level – 要为 logger 设置的等级

Changelog

Version 1.4.1

  • 修复 werobot.client.Client.post() 中文文件名的 bug (#292)

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

Version 1.1.1

Version 1.1.0

  • werobot.robot.BaseRoBot 增加 client property
  • 允许在初始化 werobot.robot.BaseRoBot 时传入 Config 。注意如果传入了 config , BaseRoBot 会忽略除 configlogger 外的其他所有的参数。
  • deprecate werobot.robot.BaseRoBotenable_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 to UnknownMessage.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 to werobot.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 to WeRoBot.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.2

  • Convert all arguments to unicode in Python 2 ( See issue #1 )

Version 0.3.1

  • Add server param in werobot.run

Version 0.3.0

  • Add new messages and replies support for WeChat 4.5