wxpy: 用 Python 玩微信

https://badge.fury.io/py/wxpy.svg https://img.shields.io/pypi/pyversions/wxpy.svg https://readthedocs.org/projects/wxpy/badge/?version=latest
微信机器人 / 可能是最优雅的微信个人号 API
wxpy 在 itchat 的基础上,通过大量接口优化提升了模块的易用性,并进行丰富的功能扩展

注意

强烈建议仅使用小号运行机器人!
从近期 (17年6月下旬) 反馈来看,使用机器人存在一定概率被限制登录的可能性。
主要表现为无法登陆 Web 微信 (但不影响手机等其他平台)。

用来干啥

一些常见的场景

  • 控制路由器、智能家居等具有开放接口的玩意儿
  • 运行脚本时自动把日志发送到你的微信
  • 加群主为好友,自动拉进群中
  • 跨号或跨群转发消息
  • 自动陪人聊天
  • 逗人玩
  • ...

总而言之,可用来实现各种微信个人号的自动化操作

体验一下

这有一个现成的微信机器人,想不想调戏一下?

记得填写入群口令 👉 [ wxpy ],与群里的大神们谈笑风生 😏

_images/wechat-group.png

轻松安装

wxpy 支持 Python 3.4-3.6,以及 2.7 版本

将下方命令中的 "pip" 替换为 "pip3" 或 "pip2",可确保安装到对应的 Python 版本中

  1. 从 PYPI 官方源下载安装 (在国内可能比较慢或不稳定):
pip install -U wxpy
  1. 从豆瓣 PYPI 镜像源下载安装 (推荐国内用户选用):
pip install -U wxpy -i "https://pypi.doubanio.com/simple/"

简单上手

登陆微信:

# 导入模块
from wxpy import *
# 初始化机器人,扫码登陆
bot = Bot()

找到好友:

# 搜索名称含有 "游否" 的男性深圳好友
my_friend = bot.friends().search('游否', sex=MALE, city="深圳")[0]

发送消息:

# 发送文本给好友
my_friend.send('Hello WeChat!')
# 发送图片
my_friend.send_image('my_picture.jpg')

自动响应各类消息:

# 打印来自其他好友、群聊和公众号的消息
@bot.register()
def print_others(msg):
    print(msg)

# 回复 my_friend 的消息 (优先匹配后注册的函数!)
@bot.register(my_friend)
def reply_my_friend(msg):
    return 'received: {} ({})'.format(msg.text, msg.type)

# 自动接受新的好友请求
@bot.register(msg_types=FRIENDS)
def auto_accept_friends(msg):
    # 接受好友请求
    new_friend = msg.card.accept()
    # 向新的好友发送消息
    new_friend.send('哈哈,我自动接受了你的好友请求')

保持登陆/运行:

# 进入 Python 命令行、让程序保持运行
embed()

# 或者仅仅阻塞线程
# bot.join()

模块特色

  • 全面对象化接口,调用更优雅

  • 默认多线程响应消息,回复更快

  • 包含 聊天机器人、共同好友 等 实用组件

  • 只需两行代码,在其他项目中 用微信接收警告

  • 愉快的探索和调试,无需涂涂改改

  • 可混合使用 itchat 的原接口

  • 当然,还覆盖了各类常见基本功能:

    • 发送文本、图片、视频、文件
    • 通过关键词或用户属性搜索 好友、群聊、群成员等
    • 获取好友/群成员的昵称、备注、性别、地区等信息
    • 加好友,建群,邀请入群,移出群

文档目录

机器人对象

机器人 Bot 对象可被理解为一个 Web 微信客户端。

注解

关于发送消息,请参见 聊天对象
关于消息对象和自动处理,请参见 消息处理

初始化/登陆

注解

Bot 在初始化时便会执行登陆操作,需要手机扫描登陆。

class wxpy.Bot(cache_path=None, console_qr=None, qr_path=None, proxies=None, hooks=None)[源代码]

机器人对象,用于登陆和操作微信账号,涵盖大部分 Web 微信的功能:

from wxpy import *
bot = Bot()

# 机器人账号自身
myself = bot.self

# 向文件传输助手发送消息
bot.file_helper.send('Hello from wxpy!')
参数:
  • cache_path --
    • 设置当前会话的缓存路径,并开启缓存功能;为 None (默认) 则不开启缓存功能。
    • 开启缓存后可在短时间内避免重复扫码,缓存失效时会重新要求登陆。
    • 设为 True 时,使用默认的缓存路径 'wxpy.pkl'。
  • console_qr --
    • 在终端中显示登陆二维码,需要安装 pillow 模块 (pip3 install pillow)。
    • 可为整数(int),表示二维码单元格的宽度,通常为 2 (当被设为 True 时,也将在内部当作 2)。
    • 也可为负数,表示以反色显示二维码,适用于浅底深字的命令行界面。
    • 例如: 在大部分 Linux 系统中可设为 True 或 2,而在 macOS Terminal 的默认白底配色中,应设为 -2。
  • qr_path -- 保存二维码的路径
  • proxies -- requests 代理,形式为
  • hooks -- 用于快速重载 Core 中的各种方法,形式为 {'原方法名': 新函数, ...}
Bot.enable_puid(path='wxpy_puid.pkl')[源代码]

可选操作: 启用聊天对象的 puid 属性:

# 启用 puid 属性,并指定 puid 所需的映射数据保存/载入路径
bot.enable_puid('wxpy_puid.pkl')

# 指定一个好友
my_friend = bot.friends().search('游否')[0]

# 查看他的 puid
print(my_friend.puid)
# 'edfe8468'

小技巧

puidwxpy 特有的聊天对象/用户ID
不同于其他 ID 属性,puid 可始终被获取到,且具有稳定的唯一性
参数:path -- puid 所需的映射数据保存/载入路径
Bot.auto_mark_as_read

为 True 时,将自动消除手机端的新消息小红点提醒 (默认为 False)

获取聊天对象

Bot.self

机器人自身 (作为一个聊天对象)

若需要给自己发送消息,请先进行以下一次性操作:

# 在 Web 微信中把自己加为好友
bot.self.add()
bot.self.accept()

# 发送消息给自己
bot.self.send('能收到吗?')
Bot.file_helper

文件传输助手

Bot.friends()

好友列表

返回类型:wxpy.Chats
Bot.groups()

群聊列表

一些不活跃的群可能无法被获取到
建议重要的群加入到通讯录中,以确保始终可见
返回类型:wxpy.Chats
Bot.mps()

公众号列表

返回类型:wxpy.Chats
Bot.chats()

所有聊天对象

返回类型:wxpy.Chats

搜索聊天对象

注解

  • 通过 .search() 获得的搜索结果 均为列表
  • 若希望找到唯一结果,可使用 ensure_one()

搜索好友:

# 搜索名称包含 '游否' 的深圳男性好友
found = bot.friends().search('游否', sex=MALE, city='深圳')
# [<Friend: 游否>]
# 确保搜索结果是唯一的,并取出唯一结果
youfou = ensure_one(found)
# <Friend: 游否>

搜索群聊:

# 搜索名称包含 'wxpy',且成员中包含 `游否` 的群聊对象
wxpy_groups = bot.groups().search('wxpy', [youfou])
# [<Group: wxpy 交流群 1>, <Group: wxpy 交流群 2>]

在群聊中搜素:

# 在刚刚找到的第一个群中搜索
group = wxpy_groups[0]
# 搜索该群中所有浙江的群友
found = group.search(province='浙江')
# [<Member: 浙江群友 1>, <Group: 浙江群友 2>, <Group: 浙江群友 3> ...]

搜索任何类型的聊天对象 (但不包含群内成员)

# 搜索名称含有 'wxpy' 的任何聊天对象
found = bot.search('wxpy')
# [<Friend: wxpy 机器人>, <Group: wxpy 交流群 1>, <Group: wxpy 交流群 2>]

加好友和建群

Bot.add_friend(user, verify_content='')[源代码]

添加用户为好友 (注意有严格的调用频率限制!)

参数:
  • user -- 用户对象,或 username
  • verify_content -- 验证说明信息
Bot.add_mp(user)[源代码]

添加/关注 公众号

参数:user -- 公众号对象,或 username
Bot.accept_friend(user)[源代码]

接受用户为好友

参数:user -- 用户对象 (msg.card) 或 username
返回:新的好友对象
返回类型:wxpy.Friend

自动接受好友请求:

# 注册好友请求类消息
@bot.register(msg_types=FRIENDS)
# 自动接受验证信息中包含 'wxpy' 的好友请求
def auto_accept_friends(msg):
    # 判断好友请求中的验证文本
    if 'wxpy' in msg.text.lower():
        # 接受好友 (msg.card 为该请求的用户对象)
        new_friend = bot.accept_friend(msg.card)
        # 或 new_friend = msg.card.accept()
        # 向新的好友发送消息
        new_friend.send('哈哈,我自动接受了你的好友请求')
Bot.create_group(users, topic=None)[源代码]

创建一个新的群聊 (注意有严格的调用频率限制!)

参数:
  • users -- 用户列表 (不含自己,至少 2 位)
  • topic -- 群名称
返回:

若建群成功,返回一个新的群聊对象

返回类型:

wxpy.Group

其他

Bot.upload_file(path, msg_type)[源代码]
上传文件,并获取 media_id
可用于重复发送图片、表情、视频,和文件
参数:
  • path -- 文件路径
  • msg_type -- 发送时的消息类型,支持 IMAGE, STICKER, VIDEO, FILE
返回:

media_id

返回类型:

str

Bot.join()[源代码]

阻塞进程,直到结束消息监听 (例如,机器人被登出时)

Bot.logout()[源代码]

登出当前账号

控制多个微信 (多开)

仅需初始化多个 Bot 对象,即可同时控制多个微信:

bot1 = Bot()
bot2 = Bot()

聊天对象

通过机器人对象 Botchats(), friends()groups(), mps() 方法, 可分别获取到当前机器人的 所有聊天对象、好友、群聊,以及公众号列表。

而获得到的聊天对象合集 ChatsGroups 具有一些合集方法,例如:Chats.search() 可用于按条件搜索聊天对象:

from wxpy import *
bot = Bot()
my_friend = bot.friends().search('游否', sex=MALE, city='深圳')[0]
# <Friend: 游否>

在找到好友(或其他聊天对象)后,还可使用该聊天对象的 send 系列方法,对其发送消息:

# 发送文本
my_friend.send('Hello, WeChat!')
# 发送图片
my_friend.send_image('my_picture.png')
# 发送视频
my_friend.send_video('my_video.mov')
# 发送文件
my_friend.send_file('my_file.zip')
# 以动态的方式发送图片
my_friend.send('@img@my_picture.png')

各类型的继承关系

在继续了解各个聊天对象之前,我们需要首先 理解各种不同类型聊天对象的继承关系

基础类

所有聊天对象,均继承于以下两种基础类,并拥有相应的属性和方法。

基本聊天对象 Chat
  • 所有的聊天对象均继承于此类型
  • 拥有 微信ID、昵称 等属性
  • 拥有 发送消息 Chat.send(), 获取头像 Chat.get_avatar() 等方法
单个聊天对象 User
  • 继承于 Chat,表示个体聊天对象 (而非群聊)。
  • 被以下聊天对象所继承
  • 拥有 性别、省份、城市、是否为好友 等属性
  • 拥有 加为好友 User.add(), 接受为好友 User.accept() 等方法
实际类

在实际使用过程中,我们会更多的用到以下实际聊天对象类型。

小技巧

请牢记,除了自身私有的属性和方法外,它们还拥有对应基础类的属性和方法 (未重复列出)。

注解

阅读以下内容,你将了解:

  • 如何获取他们的各种属性 (ID、昵称、性别、地区、是否为好友关系等)
  • 如何对他们进行发送消息、加为好友、加入群聊、下载头像 等操作

基本聊天对象

所有聊天对象都继承于"基本聊天对象",并拥有相应的属性和方法。

class wxpy.Chat(core, _chat)[源代码]
基本聊天对象
单个用户 (User) 和群聊 (Group) 的基础类
参数:
  • core -- 关联的内核对象
  • _chat -- username 或 原始数据 dict
bot

所属的 机器人对象

raw

原始数据

puid

持续有效,且稳定唯一的聊天对象/用户ID,适用于持久保存

请使用 Bot.enable_puid() 来启用 puid 属性

小技巧

puidwxpy 特有的聊天对象/用户ID
不同于其他 ID 属性,puid 可始终被获取到,且具有稳定的唯一性

注意

puid 映射数据 不可跨机器人使用

nickname

该聊天对象的昵称 (好友、群员的昵称,或群名称)

name
该聊天对象的友好名称
即: 从 备注名称、群聊显示名称、昵称(或群名称),username 中按序选取第一个可用的
send(content, msg_type=None, media_id=None)[源代码]

发送消息。默认为发送文本消息,也可指定其他消息类型

参数:
  • content -- 消息类型为 TEXT 时为消息的文本内容,其他类型时为文件路径
  • msg_type -- 消息类型,支持 TEXT, IMAGE, STICKER, VIDEO, FILE (默认为 TEXT)
  • media_id -- 文件在服务器中的唯一 ID,填写后可省略上传步骤
返回:

已发送的消息

返回类型:

SentMessage

send_image(path, media_id=None)[源代码]

发送图片

参数:
  • path -- 图片的文件路径
  • media_id -- 文件在服务器中的唯一 ID,填写后可省略上传步骤
返回:

已发送的消息

返回类型:

SentMessage

send_sticker(path, media_id=None)[源代码]

发送表情 (类似于手机端中发送收藏的表情)

参数:
  • path -- 表情图片的文件路径
  • media_id -- 文件在服务器中的唯一 ID,填写后可省略上传步骤
返回:

已发送的消息

返回类型:

SentMessage

send_video(path, media_id=None)[源代码]

发送视频

参数:
  • path -- 视频的文件路径 (通常为 mp4 格式)
  • media_id -- 文件在服务器中的唯一 ID,填写后可省略上传步骤
返回:

已发送的消息

返回类型:

SentMessage

send_file(path, media_id=None)[源代码]

发送文件

参数:
  • path -- 文件路径
  • media_id -- 文件在服务器中的唯一 ID,填写后可省略上传步骤
返回:

已发送的消息

返回类型:

SentMessage

send_card(wxid, friend_or_mp)[源代码]

发送好友名片或公众号名片 (被发送的名片必须为好友关系或已关注的公众号)

参数:
  • wxid -- 好友或公众号的微信ID (无法直接获取,需要手动填写)
  • friend_or_mp -- 好友对象或公众号对象
mark_as_read()[源代码]

消除当前聊天对象的未读提示小红点

pin()[源代码]

将聊天对象置顶

unpin()[源代码]

取消聊天对象的置顶状态

get_avatar(save_path=None)[源代码]

获取头像

参数:save_path -- 保存路径(后缀通常为.jpg),若空则直接返回字节数据
raw

原始数据

username

该聊天对象的内部 ID,会随着登陆会话而改变,通常不需要用到

注意

此 ID 在机器人重新登录后 会被改变 !

update()[源代码]

更新聊天对象的详细信息

小技巧

对于群聊对象,group.update() 仅更新群本身,而不会更新群成员的详细信息
若要更新群员信息,可使用 group.members.update()

单个聊天对象

class wxpy.User(core, _chat)[源代码]

好友(Friend)、群聊成员(Member),和公众号(MP) 的基础类

基本聊天对象
单个用户 (User) 和群聊 (Group) 的基础类
参数:
  • core -- 关联的内核对象
  • _chat -- username 或 原始数据 dict
remark_name

备注名称

set_remark_name(remark_name)[源代码]

设置或修改好友的备注名称

参数:remark_name -- 新的备注名称
sex

性别,目前有:

# 男性
MALE = 1
# 女性
FEMALE = 2

未设置时为 None

province

省份

city

城市

signature

个性签名

is_friend

判断当前用户是否为好友关系

返回:若为好友关系,返回对应的好友,否则返回 False
add(verify_content=None)[源代码]

添加当前用户为好友

参数:verify_content -- 验证信息(文本)
accept()[源代码]

接受当前用户为好友

返回:新的好友对象
返回类型:wxpy.Friend

好友

class wxpy.Friend(core, _chat)[源代码]

好友对象

基本聊天对象
单个用户 (User) 和群聊 (Group) 的基础类
参数:
  • core -- 关联的内核对象
  • _chat -- username 或 原始数据 dict

群聊

class wxpy.Group(core, _chat)[源代码]

群聊对象

members

群聊的成员列表

返回类型:class:Chats:
nickname

该群聊对象的名称

get(keywords=None, **attributes)[源代码]

在群聊中查找群成员,等同于 Group.members.get(...)

find(keywords=None, **attributes)[源代码]

在群聊中查找群成员,等同于 Group.members.find(...)

search(keywords=None, **attributes)[源代码]

在群聊中查找群成员,等同于 Group.members.search(...)

owner

返回群主对象

is_owner

判断所属 bot 是否为群管理员

self

机器人自身 (作为群成员)

add(members, use_invitation=False)[源代码]

向群聊中加入用户

参数:
  • members -- 待加入的用户列表或单个用户
  • use_invitation -- 使用发送邀请的方式
remove(members)[源代码]

从群聊中移除用户

参数:members -- 待移除的用户列表或单个用户
rename(topic)[源代码]

修改群聊名称

参数:topic -- 新的名称,超长部分会被截断 (最长32字节)
群成员
class wxpy.Member(core, _raw, group_username)[源代码]

群聊成员对象

display_name

在群聊中的显示昵称

remove()[源代码]

从群聊中移除该成员

实用技巧

判断一位用户是否在群中只需用 in 语句:

friend = bot.friends().search('游否')[0]
group = bot.groups().search('wxpy 交流群')[0]

if friend in group:
    print('是的,{}{} 中!'.format(friend.name, group.name))
    # 是的,游否 在 wxpy 交流群 中!

若要遍历群成员,可直接对群对象使用 for 语句:

# 打印所有群成员
for member in group:
    print(member)

若需查看群成员数量,直接使用 len() 即可:

len(group) # 这个群的成员数量

若需判断一位群成员是否就是某个好友,使用 == 即可:

member = group.search('游否')[0]
if member == friend:
    print('{} is {}'.format(member, friend))
    # <Member: 游否> is <Friend: 游否>

公众号

class wxpy.MP(core, _chat)[源代码]

公众号对象

基本聊天对象
单个用户 (User) 和群聊 (Group) 的基础类
参数:
  • core -- 关联的内核对象
  • _chat -- username 或 原始数据 dict

聊天对象合集

好友、公众号、群聊成员的合集

Chats 对象中,除了最常用到的 search() 外,还有两个特别的方法,stats()stats_text(),可用来统计好友或群成员的性别和地区分布:

bot.friends().stats_text()
# 游否 共有 100 位微信好友\n\n男性: 67 (67.0%)\n女性: 23 (23.0%) ...
class wxpy.Chats(chat_list=None, source=None)[源代码]

多个聊天对象的列表,可用于搜索、更新、统计等功能

get(keywords=None, **attributes)[源代码]
找到匹配所设条件的唯一聊天对象
若结果不唯一,或没有找到,会抛出 ValueError 异常
参数:
  • keywords --
    聊天对象的名称关键词 (空格分隔, 不区分大小写)
    用于在 remark_name, display_name, nickname, wxid 中进行搜索
  • attributes --

    属性键值对,键可以是 sex(性别), province(省份), city(城市) 等,例如:

    bot.chats.get(nickname='游否', city='深圳', sex=MALE)
    
返回类型:

Chat

find(keywords=None, **attributes)[源代码]
类似于 any:Chats.get,不同之处在于仅返回 首个匹配的 聊天对象
也就是说: 即便列表内有多个匹配,也不会抛出异常; 若没有匹配的,则返回 None
返回类型:Chat
search(keywords=None, **attributes)[源代码]

类似于 any:Chats.get,不同之处在于会返回 所有匹配的 聊天对象

返回类型:Chats
update()[源代码]

更新列表中的所有聊天对象

stats(attribs=('sex', 'province', 'city'))[源代码]

统计各属性的分布情况

参数:attribs -- 需统计的属性列表或元组
返回:统计结果
stats_text(total=True, sex=True, top_provinces=10, top_cities=10)[源代码]

简单的统计结果的文本

参数:
  • total -- 总体数量
  • sex -- 性别分布
  • top_provinces -- 省份分布
  • top_cities -- 城市分布
返回:

统计结果文本

群聊的合集

消息处理

每当机器人接收到消息时,会自动执行以下两个步骤

  1. 将消息保存到 Bot.messages
  2. 查找消息预先注册的函数,并执行(若有匹配的函数)

消息对象

消息对象代表每一条从微信获取到的消息。

基本属性
Message.type

消息的类型,目前可为以下值:

# 文本
TEXT = 'TEXT'
# 位置
LOCATION = 'LOCATION'
# 图片
IMAGE = 'IMAGE'
# 语音
VOICE = 'VOICE'
# 好友验证
NEW_FRIEND = 'NEW_FRIEND'
# 名片
CARD = 'CARD'
# 视频
VIDEO = 'VIDEO'
# 表情 (不支持商店表情,下载前请先检查 file_size 属性)
EMOTICON = 'EMOTICON'
# URL
URL = 'SHARE_URL'
# 文件
FILE = 'FILE'
# 转账
CASH = 'CASH'
# 系统提示
NOTICE = 'NOTICE'
# 撤回提示
RECALLED = 'RECALLED'
# 未知
UNKNOWN = 'UNKNOWN'
返回类型:MessageType
Message.bot

接收此消息的 机器人对象

Message.id

消息的唯一 ID (通常为大于 0 的 64 位整型)

内容数据
Message.text

消息的文本内容

Message.get_file(save_path=None)[源代码]

下载图片、视频、语音、附件消息中的文件内容。

可与 Message.file_name, Message.file_ext 配合使用。

参数:save_path -- 文件的保存路径。若为 None,将直接返回字节数据
Message.file_name

消息中文件的文件名 (含后缀名)

Message.file_size

消息中文件的体积大小

Message.media_id

文件类消息中的文件资源 ID (但图片视频语音等其他消息中为空)

Message.raw

原始数据 (dict 数据)

用户相关
Message.chat

消息所在的聊天会话,即:

  • 对于自己发送的消息,为消息的接收者
  • 对于别人发送的消息,为消息的发送者
返回类型:wxpy.User, wxpy.Group
Message.sender

消息的发送者

返回类型:wxpy.User, wxpy.Group
Message.receiver

消息的接收者

返回类型:wxpy.User, wxpy.Group
Message.member
  • 若消息来自群聊,则此属性为消息的实际发送人(具体的群成员)
  • 若消息来自其他聊天对象(非群聊),则此属性为 None
返回类型:NoneType, wxpy.Member
Message.card
  • 好友请求中的请求用户
  • 名片消息中的推荐用户
群聊相关
Message.member
  • 若消息来自群聊,则此属性为消息的实际发送人(具体的群成员)
  • 若消息来自其他聊天对象(非群聊),则此属性为 None
返回类型:NoneType, wxpy.Member
Message.is_at

当消息来自群聊,且被 @ 时,为 True

时间相关
Message.create_time

服务端发送时间

Message.receive_time

本地接收时间

Message.latency

消息的延迟秒数 (发送时间和接收时间的差值)

其他属性
Message.url

分享类消息中的网页 URL

Message.articles

公众号推送中的文章列表 (首篇的 标题/地址 与消息中的 text/url 相同)

其中,每篇文章均有以下属性:

  • title: 标题
  • summary: 摘要
  • url: 文章 URL
  • cover: 封面或缩略图 URL
Message.location

位置消息中的地理位置信息

Message.img_height

图片高度

Message.img_width

图片宽度

Message.play_length

视频长度

Message.voice_length

语音长度

回复方法
Message.reply(...)

等同于 Message.chat.send(...)

Message.reply_image(...)

等同于 Message.chat.send_image(...)

Message.reply_file(...)

等同于 Message.chat.send_file(...)

Message.reply_video(...)

等同于 Message.chat.send_video(...)

Message.reply_msg(...)

等同于 Message.chat.send_msg(...)

Message.reply_raw_msg(...)

等同于 Message.chat.send_raw_msg(...)

转发消息
Message.forward(chat, prefix=None, suffix=None)[源代码]

将本消息转发给其他聊天对象

支持以下消息类型
  • 文本 (TEXT)
  • 图片 (IMAGE)
  • 自定义表情 (STICKER)
    • 注: 不支持表情商店中的表情
  • 视频(VIDEO)
  • 文件 (FILE)
  • 名片 (CARD)
  • 语音 (VOICE)
    • 注: 会以文件方式发送
  • 分享链接 (URL)
    • 注: 会转化为 标题 + 链接 形式的文本消息
  • 地图 (LOCATION)
    • 注: 会转化为 位置名称 + 地图链接 形式的文本消息
参数:
  • chat (Chat) -- 接收转发消息的聊天对象
  • prefix (str) -- 转发时增加的 前缀 文本,原消息为文本时会自动换行
  • suffix (str) -- 转发时增加的 后缀 文本,原消息为文本时会自动换行
返回:

若该消息支持转发,返回转发后的 SentMessage 对象;反之返回 NotImplemented

例如,将公司群中的老板消息转发出来:

from wxpy import *

bot = Bot()

# 定位公司群
company_group = bot.groups.get('公司微信群')

# 定位老板
boss = company_group.get('老板大名')

# 将老板的消息转发到文件传输助手
@bot.register(company_group)
def forward_boss_message(msg):
    if msg.member == boss:
        msg.forward(bot.file_helper, prefix='老板发言')

# 阻塞线程
embed()

自动处理消息

可通过 预先注册 的方式,实现消息的自动处理。

"预先注册" 是指
预先将特定聊天对象的特定类型消息,注册到对应的处理函数,以实现自动回复等功能。
注册消息

提示

每当收到新消息时,将根据注册规则找到匹配条件的执行函数。
并将 消息对象 作为唯一参数传入该函数。

Bot.register() 作为函数的装饰器,即可完成注册。

# 打印所有*群聊*对象中的*文本*消息
@bot.register(Group, TEXT)
def print_group_msg(msg):
    print(msg)

注意

优先匹配 后注册 的函数,且仅匹配 一个 注册函数。

Bot.register(chats=None, msg_types=None, except_self=True, run_async=True, enabled=True)[源代码]

装饰器:用于注册消息配置

参数:
  • chats -- 消息所在的聊天对象:单个或列表形式的多个聊天对象或聊天类型,为空时匹配所有聊天对象
  • msg_types -- 消息的类型:单个或列表形式的多个消息类型,为空时匹配所有已知消息类型
  • except_self -- 排除由自己发送的消息
  • run_async -- 是否异步执行所配置的函数:可提高响应速度
  • enabled -- 当前配置的默认开启状态,可事后动态开启或关闭

小技巧

  1. chatsmsg_types 参数可以接收一个列表或干脆一个单项。按需使用,方便灵活。
  2. chats 参数既可以是聊天对象实例,也可以是对象类。当为类时,表示匹配该类型的所有聊天对象。
  3. 在被注册函数中,可以通过直接 return <回复内容> 的方式来回复消息,等同于调用 msg.reply(<回复内容>)
开始运行

注解

在完成注册操作后,若没有其他操作,程序会因主线程执行完成而退出。
因此务必阻塞线程以保持监听状态!
wxpy 的 embed() 可在阻塞线程的同时,进入 Python 命令行,方便调试,一举两得。
from wxpy import *

bot = Bot()

@bot.register()
def print_messages(msg):
    print(msg)

# 阻塞线程,并进入 Python 命令行
embed()
wxpy.embed(local=None, banner='', shell=None)[源代码]
进入交互式的 Python 命令行界面,并阻塞当前线程
支持使用 ipython, bpython 以及原生 python
参数:
  • shell (str) --
    指定命令行类型,可设为 'ipython','bpython','python',或它们的首字母;
    若为 None,则按上述优先级进入首个可用的 Python 命令行。
  • local (dict) -- 设定本地变量环境,若为 None,则获取进入之前的变量环境。
  • banner (str) -- 设定欢迎内容,将在进入命令行后展示。
示例代码

在以下例子中,机器人将

  • 忽略 "一个无聊的群" 的所有消息
  • 回复好友 "游否" 和其他群聊中被 @ 的 TEXT 类消息
  • 打印所有其他消息

初始化机器人,并找到好友和群聊:

from wxpy import *
bot = Bot()
my_friend = bot.friends().search('游否')[0]
boring_group = bot.groups().search('一个无聊的群')[0]

打印所有其他消息:

@bot.register()
def just_print(msg):
    # 打印消息
    print(msg)

回复好友"游否"和其他群聊中被 @ 的 TEXT 类消息:

@bot.register([my_friend, Group], TEXT)
def auto_reply(msg):
    # 如果是群聊,但没有被 @,则不回复
    if isinstance(msg.chat, Group) and not msg.is_at:
        return
    else:
        # 回复消息内容和类型
        return '收到消息: {} ({})'.format(msg.text, msg.type)

忽略"一个无聊的群"的所有消息:

@bot.register(boring_group)
def ignore(msg):
    # 啥也不做
    return

阻塞线程,并进入 Python 命令行:

embed()
动态开关注册配置

注解

该操作需要在额外的线程中进行!

查看当前的注册配置情况:

bot.registered
# [<MessageConfig: just_print (Async, Enabled)>,
#  <MessageConfig: auto_reply (Async, Enabled)>,
#  <MessageConfig: ignore (Async, Enabled)>]

关闭所有注册配置:

bot.registered.disable()

重新开启 just_print 函数:

bot.registered.enable(just_print)

查看当前开启的注册配置:

bot.registered.enabled
# [<MessageConfig: just_print (Async, Enabled)>]
class wxpy.api.messages.Registered(bot)[源代码]

保存当前机器人所有已注册的消息配置

参数:bot -- 所属的机器人
get_config(msg)[源代码]

获取给定消息的注册配置。每条消息仅匹配一个注册配置,后注册的配置具有更高的匹配优先级。

参数:msg -- 给定的消息
返回:匹配的回复配置
get_config_by_func(func)[源代码]

通过给定的函数找到对应的注册配置

参数:func -- 给定的函数
返回:对应的注册配置
enable(func=None)[源代码]

开启指定函数的对应配置。若不指定函数,则开启所有已注册配置。

参数:func -- 指定的函数
disable(func=None)[源代码]

关闭指定函数的对应配置。若不指定函数,则关闭所有已注册配置。

参数:func -- 指定的函数
enabled

检查处于开启状态的配置

返回:处于开启状态的配置
disabled

检查处于关闭状态的配置

返回:处于关闭状态的配置

已发送消息

class wxpy.SentMessage(**attributes)[源代码]

程序中通过 .send/reply() 系列方法发出的消息

使用程序发送的消息也将被记录到历史消息 bot.messages 中

提示

大部分属性与 Message 相同

recall()[源代码]

撤回本条消息 (应为 2 分钟内发出的消息)

历史消息

可通过访问 bot.messages 来查看历史消息列表。

消息列表为 Messages 对象,具有搜索功能。

例如,搜索所有自己在手机上发出的消息:

sent_msgs = bot.messages.search(sender=bot.self)
print(sent_msgs)
class wxpy.Messages(msg_list=None, max_history=200)[源代码]

多条消息的合集,可用于记录或搜索

max_history

设置最大保存条数,即:仅保存最后的 n 条消息。

bot = Bot()
# 设置历史消息的最大保存数量为 10000 条
bot.messages.max_history = 10000
search(keywords=None, **attributes)[源代码]

搜索消息记录

参数:
  • keywords -- 文本关键词
  • attributes -- 属性键值对
返回:

所有匹配的消息

返回类型:

wxpy.Messages

# 搜索所有自己发送的,文本中包含 'wxpy' 的消息
bot.messages.search('wxpy', sender=bot.self)

用微信监控你的程序

通过利用微信强大的通知能力,我们可以把程序中的警告/日志发到自己的微信上。

wxpy 提供以下两种方式来实现这个需求。

获得专用 Logger

wxpy.get_wechat_logger(receiver=None, name=None, level=30)[源代码]

获得一个可向指定微信聊天对象发送日志的 Logger

参数:
  • receiver --
    • 当为 None, True 或字符串时,将以该值作为 cache_path 参数启动一个新的机器人,并发送到该机器人的"文件传输助手"
    • 当为 机器人 时,将发送到该机器人的"文件传输助手"
    • 当为 聊天对象 时,将发送到该聊天对象
  • name -- Logger 名称
  • level -- Logger 等级,默认为 logging.WARNING
返回:

Logger

from wxpy import get_wechat_logger

# 获得一个专用 Logger
# 当不设置 `receiver` 时,会将日志发送到随后扫码登陆的微信的"文件传输助手"
logger = get_wechat_logger()

# 发送警告
logger.warning('这是一条 WARNING 等级的日志,你收到了吗?')

# 接收捕获的异常
try:
    1 / 0
except:
    logger.exception('现在你又收到了什么?')

加入到现有的 Logger

class wxpy.WeChatLoggingHandler(receiver=None)[源代码]

可向指定微信聊天对象发送日志的 Logging Handler

参数:receiver --
  • 当为 None, True 或字符串时,将以该值作为 cache_path 参数启动一个新的机器人,并发送到该机器人的"文件传输助手"
  • 当为 机器人 时,将发送到该机器人的"文件传输助手"
  • 当为 聊天对象 时,将发送到该聊天对象
import logging
from wxpy import WeChatLoggingHandler

# 这是你现有的 Logger
logger = logging.getLogger(__name__)

# 初始化一个微信 Handler
wechat_handler = WeChatLoggingHandler()
# 加到入现有的 Logger
logger.addHandler(wechat_handler)

logger.warning('你有一条新的告警,请查收。')

指定接收者

当然,我们也可以使用其他聊天对象来接收日志。

比如,先在微信中建立一个群聊,并在里面加入需要关注这些日志的人员。然后把这个群作为接收者。

from wxpy import *

# 初始化机器人
bot = Bot()
# 找到需要接收日志的群 -- `ensure_one()` 用于确保找到的结果是唯一的,避免发错地方
group_receiver = ensure_one(bot.groups().search('XX业务-告警通知'))

# 指定这个群为接收者
logger = get_wechat_logger(group_receiver)

logger.error('打扰大家了,但这是一条重要的错误日志...')

愉快的探索和调试

想要做点小试验,调试代码,或是探索 wxpy 的功能特性?反复修改和运行太麻烦。

试试下面两种玩法,告别涂涂改改的摸索方式。

使用 embed()

注解

适用于在现有的代码中进行探索和调试

只需将 embed() 放在代码中的任何位置。运行后,就可以从那儿开始探索和调试。

例如,初始化一个机器人,然后看看它能做些什么:

from wxpy import *
bot = Bot()
embed() # 进入 Python 命令行

# 输入对象名称并回车
>>> bot
# Out[1]: <Bot: 游否>
>>> bot.friends()
# Out[2]: [<Friend: 路人甲>, <Friend: 路人乙>, <Friend: 路人丙>]
wxpy.embed(local=None, banner='', shell=None)[源代码]
进入交互式的 Python 命令行界面,并阻塞当前线程
支持使用 ipython, bpython 以及原生 python
参数:
  • shell (str) --
    指定命令行类型,可设为 'ipython','bpython','python',或它们的首字母;
    若为 None,则按上述优先级进入首个可用的 Python 命令行。
  • local (dict) -- 设定本地变量环境,若为 None,则获取进入之前的变量环境。
  • banner (str) -- 设定欢迎内容,将在进入命令行后展示。

使用 wxpy 命令

注解

适用于在命令行中边写边探索

第二种情况:想要简单写几行,而不想创建脚本,那么使用 wxpy 命令行边写边探索,更方便。

在命令行中输入 wxpy -h 可快速查看使用说明。

选项
  • bot1 bot2 bot3...
    • 一个或多个需要初始化的机器人对象的名称,以空格分割
    • 默认:不初始化机器人
    • 例子: bot1 bot2
  • -c / --cache
    • 使用会话缓存功能,将创建 wxpy_*.pkl 缓存文件
    • 默认:不缓存会话
    • 例子:-c
  • -q 宽度 / --console_qr 宽度
    • 终端二维码的单元格宽度
    • 默认:不使用终端二维码
    • 例子:-q 2
  • -l 等级 / --logging_level 等级 (注意是小写 L,不是 I)
    • 日志等级
    • 默认:INFO
    • 例子:-l DEBUG
  • -s 交互界面 / --shell 交互界面
    • 选择所需使用的 Python 交互界面
    • 可为:ipythonbpythonpython,或它们的首字母
    • 默认:以上首个可用的 Python 命令行
    • 例子:-s bpython
  • -v / --version
    • 展示版本信息并退出z
    • 例子:-v
例子

初始化一个名为 bot 的机器人:

wxpy bot

在此基础上,使用终端二维码,且单元格宽度为 2:

wxpy bot -q 2

分别初始化名为 bot1bot2 的两个机器人:

wxpy bot1 bot2

在此基础上,使用会话缓存功能:

wxpy bot1 bot2 -c

在此基础上,指定使用 bpython:

wxpy bot1 bot2 -c -s bpython

实用组件

额外内置了一些实用的小组件,可按需使用。

聊天机器人

目前提供了以下两种自动聊天机器人接口。

图灵
class wxpy.Tuling(api_key=None)[源代码]

与 wxpy 深度整合的图灵机器人

内置的 api key 存在调用限制,建议自行申请。
参数:api_key -- 你申请的 api key
bot = Bot()
my_friend = ensure_one(bot.search('游否'))
tuling = Tuling(api_key='你申请的 API KEY')

# 使用图灵机器人自动与指定好友聊天
@bot.register(my_friend)
def reply_my_friend(msg):
    tuling.do_reply(msg)
do_reply(msg, at_member=True)[源代码]

回复消息,并返回答复文本

参数:
  • msg -- Message 对象
  • at_member -- 若消息来自群聊,回复时 @发消息的群成员
返回:

答复文本

返回类型:

str

reply_text(msg, at_member=True)[源代码]

仅返回消息的答复文本

参数:
  • msg -- Message 对象
  • at_member -- 若消息来自群聊,回复时 @发消息的群成员
返回:

答复文本

返回类型:

str

小 i
class wxpy.XiaoI(key, secret)[源代码]

与 wxpy 深度整合的小 i 机器人

需要通过注册获得 key 和 secret
参数:
  • key -- 你申请的 key
  • secret -- 你申请的 secret
bot = Bot()
my_friend = ensure_one(bot.search('寒风'))
xiaoi = XiaoI('你申请的 Key', '你申请的 Secret')

# 使用小 i 机器人自动与指定好友聊天
@bot.register(my_friend)
def reply_my_friend(msg):
    xiaoi.do_reply(msg)
do_reply(msg)[源代码]

回复消息,并返回答复文本

参数:msg -- Message 对象
返回:答复文本
reply_text(msg)[源代码]

仅返回答复文本

参数:msg -- Message 对象,或消息文本
返回:答复文本

查找共同好友

wxpy.mutual_friends(*args)[源代码]

找到多个微信用户的共同好友

参数:args -- 每个参数为一个微信用户的机器人(Bot),或是聊天对象合集(Chats)
返回:共同好友列表
返回类型:wxpy.Chats
bot1 = Bot()
bot2 = Bot()

# 打印共同好友
for mf in mutual_friends(bot, bot2):
    print(mf)

确保查找结果的唯一性

wxpy.ensure_one(found)[源代码]

确保列表中仅有一个项,并返回这个项,否则抛出 ValueError 异常

通常可用在查找聊天对象时,确保查找结果的唯一性,并直接获取唯一项

参数:found -- 列表
返回:唯一项
bot = Bot()
# 确保只找到了一个叫"游否"的好友,并返回这个好友
my_friend = ensure_one(bot.search('游否'))
# <Friend: 游否>

在多个群中同步消息

wxpy.sync_message_in_groups(msg, groups, prefix=None, suffix=None, raise_for_unsupported=False, run_async=True)[源代码]

将消息同步到多个微信群中

支持以下消息类型
  • 文本 (TEXT)

  • 视频(VIDEO)

  • 文件 (ATTACHMENT)

  • 图片/自定义表情 (PICTURE)

    • 但不支持表情商店中的表情
  • 名片 (CARD)

    • 仅支持公众号名片,以及自己发出的个人号名片
  • 分享 (SHARING)

    • 会被转化为 标题 + 链接 形式的纯文本
  • 语音 (RECORDING)

    • 会以文件方式发送
  • 地图 (MAP)

    • 会转化为 位置名称 + 地图链接 形式的文本消息
参数:
  • msg (Message) -- 需同步的消息对象
  • groups (Group) -- 需同步的群列表
  • prefix (str) --
    • 转发时的 前缀 文本,原消息为文本时会自动换行
    • 若不设定,则使用默认前缀作为提示
  • suffix (str) --
    • 转发时的 后缀 文本,原消息为文本时会自动换行
    • 默认为空
  • raise_for_unsupported (bool) --
    为 True 时,将为不支持的消息类型抛出 NotImplementedError 异常
  • run_async (bool) -- 是否异步执行,为 True 时不阻塞线程
my_groups = [group1, group2, group3 ...]

@bot.register(my_groups, except_self=False)
def sync_my_groups(msg):
    sync_message_in_groups(msg, my_groups)

检测频率限制

wxpy.detect_freq_limit(func, *args, **kwargs)[源代码]

检测各类 Web 微信操作的频率限制,获得限制次数和周期

参数:
  • func -- 需要执行的操作函数
  • args -- 操作函数的位置参数
  • kwargs -- 操作函数的命名参数
返回:

限制次数, 限制周期(秒数)

例如,测试发送文本消息的频率限制:

bot = Bot('test.pkl')

# 定义需要检测的操作
def action():
    bot.file_helper.send()

# 执行检测
result = detect_freq_limit(action)
# 查看结果
print(result)
# (120, 120.111222333)

忽略 ResponseError 异常

wxpy.dont_raise_response_error(func)[源代码]

装饰器:用于避免被装饰的函数在运行过程中抛出 ResponseError 错误

异常处理

异常的抛出和捕捉

每当使用 wxpy 向微信发出请求 (例如发送消息、加好友、建群等操作),wxpy 都会在收到服务端响应后进行检查。

若响应中的错误码不为 0,程序将抛出 ResponseError 异常。

class wxpy.ResponseError(core, err_code, err_msg)[源代码]

当 BaseResponse 的返回值不为 0 时抛出的异常

err_code

错误码 (int)

err_msg

错误消息 (文本),但可能为空

捕捉异常:

try:
    # 尝试向某个群员发送消息
    group.members[3].send('Hello')
except ResponseError as e:
    # 若群员还不是好友,将抛出 ResponseError 错误
    print(e.err_code, e.err_msg) # 查看错误号和错误消息

已知错误码

通常来说,每个错误码表示一种类型的错误。

但因微信未公开 (也没有义务公开) 这套错误码体系的具体说明,我们只能根据经验猜测部分错误码的定义。

以下为一些常见的已知错误码。欢迎提交 PR 进行完善

1205

通常因为操作频率过高。需要控制频率,避免再次引起该错误。

注意

Web 微信对 加好友、建群 这两种操作的频率限制尤其严格!

对于微信而言,为了机器人避免打扰其他用户,以及控制服务器的负载压力,需要对各种不同的操作进行频率限制。

通常每种操作可有多层频率限制,而每层频率限制分为两个参数:

周期、次数,分布表示: 在 x 周期内,只能发送 y 个请求。

举个例子:

对于 发送消息 操作,可能会是这样 (数值为虚构):

限制周期 限制次数
1 2 分钟 120
2 10 分钟 300
3 1 小时 1000
4 24 小时 2000
可能会有用户在 1 分钟内狂发 100 条消息。
但这样的频率不可能维持一整天,所以一天内 3000 条是足够的。

通过以上方式,微信可实现较为合理的限制。

1204

通常因为操作对象不为好友关系。例如尝试向一位不为好友的群员发送消息时,会引起这个错误。

1100, 1101, 1102

通常表示机器人已经掉线,需要重新登录。

请重新初始化 Bot 对象,并重新注册消息。

因为重新登录后,聊天对象的 username 可能已经变化,所以原先的消息注册也会因此失效。

itchat 与原始数据

正是得益于 itchat 的坚实基础,wxpy 才能够在短时间内快速实现这些新的接口和功能。

感谢 itchat 维护者们的辛勤付出。

以下为如何在 wxpy 中混合使用 itchat 的原接口和原始数据。

使用 itchat 的原接口

只需在 wxpy 的 Bot 对象后紧跟 .core.* 即可调用 itchat 的原接口。

例如,使用 itchat 的 search_friends 接口:

from wxpy import *
bot = Bot()
found = bot.core.search_friends('游否')

注意

通过 itchat 原接口所获取到的结果为原始数据,可能无法直接传递到 wxpy 的对应方法中。

使用原始数据

wxpy 的所有 聊天对象消息对象 均基于从 itchat 获取到的数据进行封装。若需使用原始数据,只需在对象后紧跟 .raw

例如,查看一个 好友 对象的原始数据:

from wxpy import *
bot = Bot()
a_friend = bot.friends()[0]
print(a_friend.raw)

必看: 常见问题 FAQ

提示

这里罗列了一些常见的问题,在提出新的问题前,请先看完本文。

启动后马上退出了?

因为主线程执行完成了,程序自然会退出。

只需在代码结尾加一句 embed() 即可阻塞线程,还能进入 Python 命令行:

from wxpy import *

# 你的其他代码...

# 阻塞线程,并进入 Python 命令行
embed()

或者,也可以使用 Bot.join() 仅仅阻塞线程:

bot = Bot()

# 你的其他代码...

# 仅仅阻塞线程
bot.join()

# 机器人登出后会继续往下执行

每次登陆都要扫码?

可启用登陆状态缓存功能,在短时间内重新运行程序,可自动登录。

具体请见 Bot 中的 cache_path 参数说明。

可以在 Linux 中使用吗?

wxpy 不依赖于图形界面,因此完全兼容各种纯终端的服务器。

但有一点需要注意,在纯终端环境中,登陆时必须使用"终端二维码"参数。

具体请见 Bot 中的 console_qr 参数说明。

小技巧

遇到以下错误?请使用 Botconsole_qr 参数。

FileNotFoundError: [Errno 2] No such file or directory: 'xdg-open'

支持 红包、转账、朋友圈… 吗?

wxpy 使用了 Web 微信的通讯协议,因此仅能覆盖 Web 微信本身所具备的功能。

所以以下功能目前 均不支持

  • 支付相关 - 红包、转账、收款 等都不支持
  • 在群聊中@他人 - 是的,Web 微信中被人@后也不会提醒
  • 发送名片 - 但可以通过 send_raw_msg() 转发
  • 发送分享链接 - 也无法转发
  • 发送语音消息
  • 朋友圈相关

为什么要开发 wxpy?

wxpy 的初衷是帮助人们利用微信来使生活和工作更轻松。

注解

希望每位使用者在使用机器人时

  • 维护良好的交流环境
  • 永远不骚扰他人
  • 遵守法律和平台规则