WeRoBot

WeRoBot 是一个微信公众号开发框架。

如果你在使用 WeRoBot 的过程中有什么建议或者疑惑,欢迎去 https://github.com/whtsky/WeRoBot/issues 提 Issue 或者给我发邮件: whtsky [at] gmail.com

入门

Hello World

最简单的Hello World, 会给收到的每一条信息回复 Hello World

import werobot

robot = werobot.WeRoBot(token='tokenhere')

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

robot.run()

消息加密

WeRoBot 支持对消息的加密,即微信公众号的安全模式。 为 WeRoBot 开启消息加密功能,首先需要安装 cryptography

pip install cryptography

之后需要在微信公众平台的基本配置中将消息加解密方式选择为安全模式,随机生成 EncodingAESKey,并且把它传给 WeRoBot 或者 WeRoBot 实例的 config 或者创建相对应的 Config 类

from werobot import WeRoBot
robot = WeRoBot(token='2333',
                encoding_aes_key='your_encoding_aes_key',
                app_id='your_app_id')

Handlers

WeRoBot会将合法的请求发送给 handlers 依次执行。

如果某一个 Handler 返回了非空值, WeRoBot 就会根据这个值创建回复,后面的 handlers 将不会被执行。

你可以通过两种方式添加 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.scan 扫码 (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 —— 回应自定义菜单

@robot.key_click 是对 @robot.click 修饰符的改进。

如果你在自定义菜单中定义了一个 Key 为 abort 的菜单,响应这个菜单的 handler 可以写成这样

@robot.key_click("abort")
def abort():
    return "I'm a robot"

当然,如果你不喜欢用 @robot.key_click ,也可以写成这样

@robot.click
def abort(message):
    if message.key == "abort":
        return "I'm a robot"

两者是等价的。

robot.filter —— 回应有指定文本的消息

@robot.filter 是对 @robot.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"

消息

公共属性

除了 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 停止开发。

事件

公共属性

除了 UnknownEvent, 每一种 Event 都包括以下属性:

name value
message_id 消息id
target 开发者账号( OpenID )
source 发送方账号( OpenID )
time 信息发送的时间,一个UNIX时间戳。
raw 信息的原始 XML 格式

SubscribeEvent

SubscribeEvent 的属性:

name value
type ‘subscribe’
key 事件 key 值。 当且仅当未关注公众号扫描二维码时存在。
ticket 二维码的 ticket。 当且仅当未关注公众号扫描二维码时存在。

UnSubscribeEvent

UnSubscribeEvent 的属性:

name value
type ‘unsubscribe’

ScanEvent

ScanEvent 的属性:

name value
type ‘scan’

ClickEvent

ClickEvent 的属性:

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

ViewEvent

ViewEvent 的属性:

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

LocationEvent

LocationEvent 的属性:

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

TemplateSendJobFinishEvent

模版消息发送任务完成后的 Event 通知。 属性: =========== =================================== name value =========== =================================== status 发送是否成功。为 ‘success’ 或失败原因 =========== ===================================

UnknownEvent

UnknownEvent 的属性:

name value
type ‘unknown’
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.reply 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

将消息转发到多客服

SuccessReply

给微信服务器回复 “success”。 假如服务器无法保证在五秒内处理并回复,需要回复 SuccessReply ,这样微信服务器才不会对此作任何处理,并且不会发起重试。

Session

WeRoBot 0.4.0 中增加了功能强大的 Session 系统,你可以通过 Session 轻松实现用户状态的记录,享受如同 Web 开发般的便捷。

一个简单的使用 Session 的 Demo

robot = werobot.WeRoBot(token=werobot.utils.generate_token(),
                        enable_session=True)

@robot.text
def first(message, session):
    if 'last' in session:
        return
    session['last'] = message.content
    return message.content

robot.run()

开启 Session

想要开启 Session ,在实例化 WeRoBot 的时候需要传入 enable_sessionsession_storage 两个参数:

修改 Handler 以使用 Session

没有打开 Session 的时候,一个标准的 WeRoBot Handler 应该是这样的

@robot.text
def hello(message):
    return "Hello!"

而在打开 Session 之后, 这个 Handler 需要修改为接受第二个参数: session

@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 字典。

在修改完 Handler 之后, 你的微信机器人就拥有 Session 系统了 :)

可用的 Session Storage

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.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

WeRoBot.Config

WeRoBot 使用 WeRoBot.Config 类来存储配置信息。 WeRoBot 类实例的 config 属性是一个 WeRobot.config.Config 实例。

WeRobot.config.Config 继承自 dict 。因此, 你可以像使用普通 dict 一样使用它

from werobot import WeRoBot
robot = WeRoBot(token='2333')

robot.config.update(
    HOST='0.0.0.0',
    PORT=80
)

与普通 dict 不同的是, 你可以先把配置文件保存在一个对象或是文件中, 然后在 WeRoBot.config.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
)

API

class werobot.config.Config
from_object(obj)

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

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

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

参数:filename – 配置文件的文件名。

与其他 Web 框架集成

WeRoBot 可以作为独立服务运行,也可以集成在其他 Web 框架中一同运行。

Django

WeRoBot 支持 Django 1.6+。

首先,在一个文件中写好你的微信机器人

# Filename: robot.py

from werobot import WeRoBot

robot = WeRoBot(token='token')


@robot.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 robot

urlpatterns = patterns('',
    url(r'^robot/', make_view(robot)),
)

Flask

首先, 同样在文件中写好你的微信机器人

# Filename: robot.py

from werobot import WeRoBot

robot = WeRoBot(token='token')


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

然后, 在 Flask 项目中为 Flask 实例集成 WeRoBot

from flask import Flask
from robot import robot
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'])

Bottle

在你的 Bottle App 中集成 WeRoBot

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))

Tornado

最简单的 Hello World

import tornado.ioloop
import tornado.web
from werobot import WeRoBot
from werobot.contrib.tornado import make_handler

robot = WeRoBot(token='token')


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

application = tornado.web.Application([
    (r"/robot/", make_handler(robot)),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

API

werobot.contrib.django.make_view(robot)

为一个 BaseRoBot 生成 Django view。

参数:robot – 一个 BaseRoBot 实例。
返回:一个标准的 Django view
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
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
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.Client —— 微信 API 操作类

class werobot.client.Client(config)

微信 API 操作类 通过这个类可以方便的通过微信 API 进行一系列操作,比如主动发送消息、创建自定义菜单等

create_group(name)

创建分组 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口

参数:name – 分组名字(30个字符以内)
返回:返回的 JSON 数据包
create_menu(menu_data)

创建自定义菜单

client = Client("id", "secret")
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"
                }
            ]
        }
    ]})

详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口

参数:menu_data – Python 字典
返回:返回的 JSON 数据包
create_qrcode(**data)

创建二维码 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码

参数:data – 你要发送的参数 dict
返回:返回的 JSON 数据包
delete_menu()

删除自定义菜单。 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口

返回:返回的 JSON 数据包
download_media(media_id)

下载多媒体文件。 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件

参数:media_id – 媒体文件 ID
返回:requests 的 Response 实例
get_access_token()

判断现有的token是否过期。 用户需要多进程或者多机部署可以手动重写这个函数 来自定义token的存储,刷新策略。

:return:返回token

get_followers(first_user_id=None)

获取关注者列表 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=获取关注者列表

参数:first_user_id – 可选。第一个拉取的OPENID,不填默认从头开始拉取
返回:返回的 JSON 数据包
get_group_by_id(openid)

查询用户所在分组 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口

参数:openid – 用户的OpenID
返回:返回的 JSON 数据包
get_groups()

查询所有分组 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口

返回:返回的 JSON 数据包
get_menu()

查询自定义菜单。 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口

返回:返回的 JSON 数据包
get_user_info(user_id, lang='zh_CN')

获取用户基本信息 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=获取用户基本信息

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • lang – 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
返回:

返回的 JSON 数据包

grant_token()

获取 Access Token 。 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=通用接口文档

返回:返回的 JSON 数据包
move_user(user_id, group_id)

移动用户分组 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • group_id – 分组 ID
返回:

返回的 JSON 数据包

send_article_message(user_id, articles)

发送图文消息 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • articles – 一个包含至多10个 Article 实例的数组
返回:

返回的 JSON 数据包

send_image_message(user_id, media_id)

发送图片消息 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • media_id – 图片的媒体ID。 可以通过 upload_media() 上传。
返回:

返回的 JSON 数据包

send_music_message(user_id, url, hq_url, thumb_media_id, title=None, description=None)

发送音乐消息 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • url – 音乐链接
  • hq_url – 高品质音乐链接,wifi环境优先使用该链接播放音乐
  • thumb_media_id – 缩略图的媒体ID。 可以通过 upload_media() 上传。
  • title – 音乐标题
  • description – 音乐描述
返回:

返回的 JSON 数据包

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

send_text_message(user_id, content)

发送文本消息 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • content – 消息正文
返回:

返回的 JSON 数据包

send_video_message(user_id, media_id, title=None, description=None)

发送视频消息 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • media_id – 发送的视频的媒体ID。 可以通过 upload_media() 上传。
  • title – 视频消息的标题
  • description – 视频消息的描述
返回:

返回的 JSON 数据包

send_voice_message(user_id, media_id)

发送语音消息 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息

参数:
  • user_id – 用户 ID 。 就是你收到的 Message 的 source
  • media_id – 发送的语音的媒体ID。 可以通过 upload_media() 上传。
返回:

返回的 JSON 数据包

show_qrcode(ticket)

通过ticket换取二维码 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码

参数:ticket – 二维码 ticket 。可以通过 create_qrcode() 获取到
返回:返回的 Request 对象
update_group(group_id, name)

修改分组名 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口

参数:
  • group_id – 分组id,由微信分配
  • name – 分组名字(30个字符以内)
返回:

返回的 JSON 数据包

upload_media(media_type, media_file)

上传多媒体文件。 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件

参数:
  • media_type – 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
  • media_file – 要上传的文件,一个 File-object
返回:

返回的 JSON 数据包

部署

在独立服务器上部署

当你运行 werobot.run 的时候,你可以通过传递 server 参数来手动指定使用的服务器

import werobot

robot = werobot.WeRoBot(token='tokenhere')

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

robot.run(server='gevent', port=12233)

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

使用 Supervisor 管理守护进程

请注意, werobot.run 是跑在 非守护进程模式下 的——也就是说,一旦你关闭终端,进程就会自动退出。

我们建议您使用 Supervisor 来管理 WeRoBot 的进程。

配置文件样例:

[program:wechat_robot]
command = python /home/whtsky/robot.py
user = whtsky
redirect_stderr = true
stdout_logfile = /home/whtsky/logs/robot.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上部署

参考: 示例仓库

如果你需要使用 Session ,最好使用 werobot.session.saekvstorage

小工具

Token 生成器

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

import werobot.utils

print(werobot.utils.generate_token())

贡献指南

欢迎所有人为 WeRoBot 贡献。如果你打算为 WeRoBot 项目贡献代码, 请仔细阅读这份贡献指南。

贡献代码

若要贡献代码, 请注意使用 GitHub 的 workflow 。

标准的做法应该先 Fork 这个项目到自己的 Repo, 然后从 develop 分支创建一个新的分支。 当一切开发完成之后, 可以发 Pull Request 到 develop 分支, 我们会为你的代码做 Review。同时 CI 也会为合并之后的分支运行测试。

如果一切没有问题, 我们将合并你的代码到 develop 分支, 并最终发布在 master 分支的稳定版本。

注解

以下的内容是为 Linux / macOS 操作系统而写的。 如果你使用 Windows 进行开发,可能会遇到一些问题。

环境搭建

建议使用 virtualenv 创建虚拟环境进行开发, 然后安装开发环境需要的 packages。 关于 Python 版本, 推荐使用 Python 3.5 进行开发。

如果使用的是 3.5 版本

# 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

代码风格

请遵循 PEP8 标准进行代码书写。

https://www.python.org/dev/peps/pep-0008/

为了统一代码风格我们推荐使用 flake8 进行代码风格检查, 并为代码提交添加钩子。

# Install git hook for flake8.
flake8 --install-hook
# flake8 will automatically run before commit.

添加钩子之后会在每次代码提交时运行 flake8 进行检查。

若要单独运行 flake8

# Run flake8 immediately.
flake8 werobot

测试

在代码提交之前, 请先运行本地的测试。每次提交之后会有在线的 CI 运行更多版本兼容性的测试, 请密切关注测试结果。

# Run tests locally.
python setup.py test

当然也可以使用 tox 在本地运行多版本的兼容性测试。

# Run multi-version tests locally.
tox

另外请为自己新添加的模块或者功能编写测试代码, 所有的测试文件都在 tests 文件夹下。

Changelog

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