微信机器人 / 可能是最优雅的微信个人号 API
wxpy 在 itchat 的基础上,通过大量接口优化提升了模块的易用性,并进行丰富的功能扩展
注意
强烈建议仅使用小号运行机器人!
从近期 (17年6月下旬) 反馈来看,使用机器人存在一定概率被限制登录的可能性。
主要表现为无法登陆 Web 微信 (但不影响手机等其他平台)。
项目主页
https://github.com/youfou/wxpy
用来干啥
一些常见的场景
- 控制路由器、智能家居等具有开放接口的玩意儿
- 运行脚本时自动把日志发送到你的微信
- 加群主为好友,自动拉进群中
- 跨号或跨群转发消息
- 自动陪人聊天
- 逗人玩
- …
总而言之,可用来实现各种微信个人号的自动化操作
轻松安装
wxpy 支持 Python 3.4-3.6,以及 2.7 版本
将下方命令中的 “pip” 替换为 “pip3” 或 “pip2”,可确保安装到对应的 Python 版本中
- 从 PYPI 官方源下载安装 (在国内可能比较慢或不稳定):
pip install -U wxpy
- 从豆瓣 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 的原接口
-
当然,还覆盖了各类常见基本功能:
- 发送文本、图片、视频、文件
- 通过关键词或用户属性搜索 好友、群聊、群成员等
- 获取好友/群成员的昵称、备注、性别、地区等信息
- 加好友,建群,邀请入群,移出群
更新日志
https://github.com/youfou/wxpy/releases
机器人对象
机器人 Bot
对象可被理解为一个 Web 微信客户端。
注解
关于发送消息,请参见聊天对象。
关于消息对象和自动处理,请参见 消息处理。
初始化/登陆
注解
Bot
在初始化时便会执行登陆操作,需要手机扫描登陆。
class wxpy.Bot(cache_path=None, console_qr=False, qr_path=None, qr_callback=None, login_callback=None, logout_callback=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 – 保存二维码的路径
- qr_callback – 获得二维码后的回调,可以用来定义二维码的处理方式,接收参数: uuid, status, qrcode
- login_callback – 登陆成功后的回调,若不指定,将进行清屏操作,并删除二维码文件
- logout_callback – 登出时的回调
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'
小技巧
puid
是 wxpy 特有的聊天对象/用户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(update=False)
获取所有好友
参数: update – 是否更新
返回: 聊天对象合集
返回类型: wxpy.Chats
Bot.groups(update=False, contact_only=False)
获取所有群聊对象
一些不活跃的群可能无法被获取到,可通过在群内发言,或修改群名称的方式来激活
参数:
- update – 是否更新
- contact_only – 是否限于保存为联系人的群聊
返回: 群聊合集
返回类型: wxpy.Groups
Bot.mps(update=False)
获取所有公众号
参数: update – 是否更新
返回: 聊天对象合集
返回类型:wxpy.Chats
Bot.chats(update=False)
获取所有聊天对象
参数: update – 是否更新
返回: 聊天对象合集
返回类型: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 – 用户对象,或 user_name
- verify_content – 验证说明信息
Bot.add_mp(user)
添加/关注 公众号
参数: user – 公众号对象,或 user_name
Bot.accept_friend(user, verify_content=’’)
接受用户为好友
参数:
- user – 用户对象或 user_name
- verify_content – 验证说明信息
返回:
新的好友对象
返回类型: 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.user_details(user_or_users, chunk_size=50)
获取单个或批量获取多个用户的详细信息(地区、性别、签名等),但不可用于群聊成员
参数:
- user_or_users – 单个或多个用户对象或 user_name
- chunk_size – 分配请求时的单批数量,目前为 50
返回: 单个或多个用户用户的详细信息
Bot.upload_file(path)
上传文件,并获取 media_id
可用于重复发送图片、表情、视频,和文件
参数: path – 文件路径
返回: media_id
返回类型: str
Bot.join()
堵塞进程,直到结束消息监听 (例如,机器人被登出时)
Bot.logout()
登出当前账号
控制多个微信 (多开)
仅需初始化多个 Bot
对象,即可同时控制多个微信:
bot1 = Bot()
bot2 = Bot()
聊天对象
通过机器人对象 Bot
的 chats()
, friends()
,groups()
, mps()
方法, 可分别获取到当前机器人的 所有聊天对象、好友、群聊,以及公众号列表。
而获得到的聊天对象合集 Chats
和 Groups
具有一些合集方法,例如: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
,表示个体聊天对象 (而非群聊)。 -
被以下聊天对象所继承
- 好友对象
Friend
- 群成员对象
Member
- 公众号对象
MP
- 好友对象
-
拥有 性别、省份、城市、是否为好友 等属性
-
拥有 加为好友
User.add()
, 接受为好友User.accept()
等方法
实际类
在实际使用过程中,我们会更多的用到以下实际聊天对象类型。
小技巧
请牢记,除了自身私有的属性和方法外,它们还拥有对应基础类的属性和方法 (未重复列出)。
- 好友
Friend
- 群聊
Group
- 群成员
Member
- 公众号
MP
注解
阅读以下内容,你将了解:
- 如何获取他们的各种属性 (ID、昵称、性别、地区、是否为好友关系等)
- 如何对他们进行发送消息、加为好友、加入群聊、下载头像 等操作
基本聊天对象
所有聊天对象都继承于”基本聊天对象”,并拥有相应的属性和方法。
class wxpy.Chat(raw, bot)
单个用户 (User
) 和群聊 (Group
) 的基础类
bot
所属的 机器人对象
raw
原始数据
puid
持续有效,且稳定唯一的聊天对象/用户ID,适用于持久保存
请使用 Bot.enable_puid()
来启用 puid 属性
小技巧
puid
是 wxpy 特有的聊天对象/用户ID
不同于其他 ID 属性,puid 可始终被获取到,且具有稳定的唯一性
注意
puid 映射数据 不可跨机器人使用
nick_name
该聊天对象的昵称 (好友、群员的昵称,或群名称)
name
该聊天对象的友好名称
具体为: 从 备注名称、群聊显示名称、昵称(或群名称),或微信号中
按序选取第一个可用的
send(content=None, media_id=None)
动态发送不同类型的消息,具体类型取决于 msg 的前缀。
参数:
- content –
- 由 前缀 和 内容 两个部分组成,若 省略前缀,将作为纯文本消息发送
- 前缀 部分可为: ‘@fil@’, ‘@img@’, ‘@msg@’, ‘@vid@’ (不含引号)
- 分别表示: 文件,图片,纯文本,视频
- 内容 部分可为: 文件、图片、视频的路径,或纯文本的内容
- media_id – 填写后可省略上传过程
返回类型: wxpy.SentMessage
send_msg
(msg=None)
发送文本消息
参数: msg – 文本内容
返回类型: wxpy.SentMessage
send_image(path, media_id=None)
发送图片
参数:
- path – 文件路径
- media_id – 设置后可省略上传
返回类型: wxpy.SentMessage
send_file(path, media_id=None)
发送文件
参数:
- path – 文件路径
- media_id – 设置后可省略上传
返回类型: wxpy.SentMessage
send_video(path=None, media_id=None)
发送视频
参数:
- path – 文件路径
- media_id – 设置后可省略上传
返回类型: wxpy.SentMessage
send_raw_msg(raw_type, raw_content, uri=None, msg_ext=None)
以原始格式发送其他类型的消息。
参数:
- raw_type (int) – 原始的整数消息类型
- raw_content (str) – 原始的消息内容
- uri (str) – 请求路径,默认为 ‘/webwxsendmsg’
- msg_ext (dict) – 消息的扩展属性 (会被更新到 Msg 键中)
返回类型: wxpy.SentMessage
例如,发送好友或公众号的名片:
my_friend.send_raw_msg(
# 名片的原始消息类型
raw_type=42,
# 注意 `username` 在这里应为微信 ID,且被发送的名片必须为自己的好友
raw_content='<msg username="wxpy_bot" nickname="wxpy 机器人"/>'
)
mark_as_read()
消除当前聊天对象的未读提示小红点
pin()
将聊天对象置顶
unpin()
取消聊天对象的置顶状态
get_avatar(save_path=None)
获取头像
参数: save_path – 保存路径(后缀通常为.jpg),若为 None 则返回字节数据
uin
微信中的聊天对象ID,固定且唯一
因微信的隐私策略,该属性有时无法被获取到
建议使用 puid
作为用户的唯一 ID
alias
若用户进行过一次性的 “设置微信号” 操作,则该值为用户设置的”微信号”,固定且唯一
因微信的隐私策略,该属性有时无法被获取到
建议使用 puid
作为用户的唯一 ID
wxid
聊天对象的微信ID (实际为 .alias 或 .uin)
因微信的隐私策略,该属性有时无法被获取到
建议使用 puid
作为用户的唯一 ID
user_name
该聊天对象的内部 ID,通常不需要用到
注意
同个聊天对象在不同用户中,此 ID 不一致 ,且可能在新会话中 被改变!
单个聊天对象
class wxpy.User(raw, bot)
好友(Friend
)、群聊成员(Member
),和公众号(MP
) 的基础类
remark_name
备注名称
set_remark_name(remark_name)
设置或修改好友的备注名称
参数: remark_name – 新的备注名称
sex
性别,目前有:
# 男性
MALE = 1
# 女性
FEMALE = 2
未设置时为 None
province
省份
city
城市
signature
个性签名
is_friend
判断当前用户是否为好友关系
返回: 若为好友关系,返回对应的好友,否则返回 False
add(verify_content=’’)
把当前用户加为好友
参数: verify_content – 验证信息(文本)
accept(verify_content=’’)
接受当前用户为好友
参数: verify_content – 验证信息(文本)
返回: 新的好友对象
返回类型: wxpy.Friend
好友
class wxpy.Friend(raw, bot)
好友对象
群聊
class wxpy.Group(raw, bot)
群聊对象
members
群聊的成员列表
search(keywords=None, **attributes)
在群聊中搜索成员
注解
搜索结果为一个 Chats (列表)
对象
建议搭配 ensure_one()
使用
参数:
- keywords – 成员名称关键词
- attributes – 属性键值对
返回: 匹配的群聊成员
返回类型: wxpy.Chats
owner
返回群主对象
is_owner
判断所属 bot 是否为群管理员
self
机器人自身 (作为群成员)
update_group(members_details=False)
更新群聊的信息
参数: members_details – 是否包括群聊成员的详细信息 (地区、性别、签名等)
add_members(users, use_invitation=False)
向群聊中加入用户
参数:
- users – 待加入的用户列表或单个用户
- use_invitation – 使用发送邀请的方式
remove_members(members)
从群聊中移除用户
参数: members – 待移除的用户列表或单个用户
rename_group(name)
修改群聊名称
参数: name – 新的名称,超长部分会被截断 (最长32字节)
群成员
class wxpy.Member(raw, group)
群聊成员对象
display_name
在群聊中的显示昵称
remove()
从群聊中移除该成员
name
该群成员的友好名称
具体为: 从 群聊显示名称、昵称(或群名称),或微信号中,按序选取第一个可用的
实用技巧
判断一位用户是否在群中只需用 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(raw, bot)
公众号对象
聊天对象合集
好友、公众号、群聊成员的合集
在 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)
多个聊天对象的合集,可用于搜索或统计
search(keywords=None, **attributes)
在聊天对象合集中进行搜索
注解
搜索结果为一个 Chats (列表)
对象
建议搭配 ensure_one()
使用
参数:
- keywords – 聊天对象的名称关键词
- attributes – 属性键值对,键可以是 sex(性别), province(省份), city(城市) 等。例如可指定 province=’广东’
返回: 匹配的聊天对象合集
返回类型: wxpy.Chats
stats(attribs=(‘sex’, ‘province’, ‘city’))
统计各属性的分布情况
参数: attribs – 需统计的属性列表或元组
返回: 统计结果
stats_text(total=True, sex=True, top_provinces=10, top_cities=10)
简单的统计结果的文本
参数:
- total – 总体数量
- sex – 性别分布
- top_provinces – 省份分布
- top_cities – 城市分布
返回: 统计结果文本
add_all(interval=3, verify_content=’’)
将合集中的所有用户加为好友,请小心应对调用频率限制!
参数:
- interval – 间隔时间(秒)
- verify_content – 验证说明文本
群聊的合集
class wxpy.Groups(group_list=None)
群聊的合集,可用于按条件搜索
search(keywords=None, users=None, **attributes)
在群聊合集中,根据给定的条件进行搜索
参数:
- keywords – 群聊名称关键词
- users – 需包含的用户
- attributes – 属性键值对,键可以是 owner(群主对象), is_owner(自身是否为群主), nick_name(精准名称) 等。
返回: 匹配条件的群聊列表
返回类型:wxpy.Groups
消息处理
每当机器人接收到消息时,会自动执行以下两个步骤
- 将消息保存到
Bot.messages
中 - 查找消息预先注册的函数,并执行(若有匹配的函数)
消息对象
消息对象代表每一条从微信获取到的消息。
基本属性
Message.type
消息的类型,目前可为以下值:
# 文本
TEXT = 'Text'
# 位置
MAP = 'Map'
# 名片
CARD = 'Card'
# 提示
NOTE = 'Note'
# 分享
SHARING = 'Sharing'
# 图片
PICTURE = 'Picture'
# 语音
RECORDING = 'Recording'
# 文件
ATTACHMENT = 'Attachment'
# 视频
VIDEO = 'Video'
# 好友请求
FRIENDS = 'Friends'
# 系统
SYSTEM = 'System'
返回类型: str
Message.bot
接收此消息的 机器人对象
Message.id
消息的唯一 ID (通常为大于 0 的 64 位整型)
内容数据
Message.text
消息的文本内容
Message.get_file(save_path=None)
下载图片、视频、语音、附件消息中的文件内容。
可与 Message.file_name
配合使用。
参数: 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, raise_for_unsupported=False)
将本消息转发给其他聊天对象
支持以下消息类型
-
文本 (TEXT)
-
视频(VIDEO)
-
文件 (ATTACHMENT)
-
图片/自定义表情 (PICTURE)
- 但不支持表情商店中的表情
-
名片 (CARD)
- 仅支持公众号名片,以及自己发出的个人号名片
-
分享 (SHARING)
- 会转化为 标题 + 链接 形式的文本消息
-
语音 (RECORDING)
- 会以文件方式发送
-
地图 (MAP)
- 会转化为 位置名称 + 地图链接 形式的文本消息
参数:
-
chat (Chat) – 接收转发消息的聊天对象
-
prefix (str) – 转发时增加的 前缀 文本,原消息为文本时会自动换行
-
suffix (str) – 转发时增加的 后缀 文本,原消息为文本时会自动换行
-
raise_for_unsupported (bool) –
为 True 时,将为不支持的消息类型抛出 NotImplementedError 异常
例如,将公司群中的老板消息转发出来:
from wxpy import *
bot = Bot()
# 定位公司群
company_group = ensure_one(bot.groups().search('公司微信群'))
# 定位老板
boss = ensure_one(company_group.search('老板大名'))
# 将老板的消息转发到文件传输助手
@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 – 消息的类型:单个或列表形式的多个消息类型,为空时匹配所有消息类型 (SYSTEM 类消息除外)
- except_self – 排除由自己发送的消息
- run_async – 是否异步执行所配置的函数:可提高响应速度
- enabled – 当前配置的默认开启状态,可事后动态开启或关闭
小技巧
- chats 和 msg_types 参数可以接收一个列表或干脆一个单项。按需使用,方便灵活。
- chats 参数既可以是聊天对象实例,也可以是对象类。当为类时,表示匹配该类型的所有聊天对象。
- 在被注册函数中,可以通过直接 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 提供以下两种方式来实现这个需求。
获得专用
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 交互界面
- 可为:ipython,bpython,python,或它们的首字母
- 默认:以上首个可用的 Python 命令行
- 例子:-s bpython
-
-v / –version
- 展示版本信息并退出z
- 例子:-v
例子
初始化一个名为 bot 的机器人:
wxpy bot
在此基础上,使用终端二维码,且单元格宽度为 2:
wxpy bot -q 2
分别初始化名为 bot1 和 bot2 的两个机器人:
wxpy bot1 bot2
在此基础上,使用会话缓存功能:
wxpy bot1 bot2 -c
在此基础上,指定使用 bpython:
wxpy bot1 bot2 -c -s bpython
实用组件
额外内置了一些实用的小组件,可按需使用。
聊天机器人
目前提供了以下两种自动聊天机器人接口。
图灵
class wxpy.Tuling(api_key=None)
与 wxpy 深度整合的图灵机器人
内置的 api key 存在调用限制,建议自行申请。
免费申请: http://www.tuling123.com/
参数: 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
免费申请: http://cloud.xiaoi.com/
参数:
- 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(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
对象,并重新注册消息。
因为重新登录后,聊天对象的 user_name 可能已经变化,所以原先的消息注册也会因此失效。
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 参数说明。
小技巧
遇到以下错误?请使用 Bot
的 console_qr 参数。
FileNotFoundError: [Errno 2] No such file or directory: ‘xdg-open’
支持 红包、转账、朋友圈… 吗?
wxpy 使用了 Web 微信的通讯协议,因此仅能覆盖 Web 微信本身所具备的功能。
所以以下功能目前 均不支持
- 支付相关 - 红包、转账、收款 等都不支持
- 在群聊中@他人 - 是的,Web 微信中被人@后也不会提醒
- 发送名片 - 但可以通过
send_raw_msg()
转发 - 发送分享链接 - 也无法转发
- 发送语音消息
- 朋友圈相关
为什么要开发 wxpy?
wxpy 的初衷是帮助人们利用微信来使生活和工作更轻松。
注解
希望每位使用者在使用机器人时
- 维护良好的交流环境
- 永远不骚扰他人
- 遵守法律和平台规则
wxpy文档:https://wxpy.readthedocs.io/zh/latest/
itchat文档:
https://itchat.readthedocs.io/zh/latest/
https://github.com/littlecodersh/ItChat