具体代码实现如下:
import itchat import requests as rq @itchat.msg_register(itchat.content.TEXT) def reply_msg(msg): info = msg['Content'].encode('utf8') # 图灵API接口 api_url = 'http://openapi.tuling123.com/openapi/api/v2' # 接口请求数据 data = { "reqType": 0, "perception": { "inputText": { "text": str(info) } }, "userInfo": { "apiKey": "7e9377d76fc7ee9499f6dec8eed37bbb", "userId": "123" } } headers = { 'Content-Type': 'application/json', 'Host': 'openapi.tuling123.com', 'User-Agent': 'Mozilla/5.0 (Wi`ndows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3486.0 ' 'Safari/537.36 ' } # 请求接口 result = rq.post(api_url, headers=headers, json=data).json() # 提取text,发送给发信息的人 itchat.send_msg(result['results'][0]['values']['text'], msg['FromUserName']) print() if __name__ == '__main__': itchat.auto_login() itchat.run()
代码执行结果如下图所示:
19.5 实用示例:实现微信信息防撤回
上面的图灵机器人有时回答得牛头不对马嘴,特别是在多轮问答的时候,闲聊玩玩还是不错的。 接着的我们要利用itchat来编写一个微信信息防撤回的脚本。当监控到用户或者群聊发送信息 撤回的话,把撤回的内容通过文件传输助手发送给我们,说下大体的思路流程。
(1)监听所有聊天记录,包括群聊,把信息都存入到一个字典里,资源类文件下载到本地。 (2)监听到撤回信息后,根据撤回的信息id,查找字典里对应的信息,发送给文件助手。 (3)每隔五分钟清理缓存数据。
流程看上去是挺简单,接着我们一点点来摸索实现。
19.5.1 监控接收到的数据
先是监控信息,信息又分为好友聊天和群聊,我们编写代码来监控下收到的数据是怎么样的?
import itchat from itchat.content import * # 好友信息监听 @itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isFriendChat=True) def handle_friend_msg(msg): print("好友信息: ", msg) # 群聊信息监听 @itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isGroupChat=True) def information(msg): print("群聊信息: ", msg) if __name__ == '__main__': itchat.auto_login() itchat.run()
给机器人发送一条信息,还有往群聊里发送一条信息,查看打印的信息内容,先是好友信息
好友信息: {'MsgId': '5500935424291030814', 'FromUserName': '@8fd8b5b2bd0862ed5d0d573bc6c08362', 'ToUserName': '@913c3124d973db6ae25924bd0598b48a0028e0c2d01e18d8095cc6fd58db116b', 'MsgType': 1, 'Content': '123', 'Status': 3, 'ImgStatus': 1, 'CreateTime': 1533010285, 'VoiceLength': 0, 'PlayLength': 0, 'FileName': '', 'FileSize': '', 'MediaId': '', 'Url': '', 'AppMsgType': 0, 'StatusNotifyCode': 0, 'StatusNotifyUserName': '', 'RecommendInfo': {'UserName': '', 'NickName': '', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '', 'Signature': '', 'Alias': '', 'Scene': 0, 'VerifyFlag': 0, 'AttrStatus': 0, 'Sex': 0, 'Ticket': '', 'OpCode': 0}, 'ForwardFlag': 0, 'AppInfo': {'AppID': '', 'Type': 0}, 'HasProductId': 0, 'Ticket': '', 'ImgHeight': 0, 'ImgWidth': 0, 'SubMsgType': 0, 'NewMsgId': 5500935424291030814, 'OriContent': '', 'EncryFileName': '', 'User': <User: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@8fd8b5b2bd0862ed5d0d573bc6c08362', 'NickName': 'CoderPig', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=625711027&username=@8fd8b5b2bd0862ed5d0d573bc6c08362&skey=@crypt_2d4a1972_26dc3be99a177455b82518b3ca6e6cc5', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 1, 'Signature': '不服气,就用行动去证明,少说多做...2018.7.29', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'CODERPIG', 'PYQuanPin': 'CoderPig', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 33783847, 'Province': '广东', 'City': '江门', 'Alias': '', 'SnsFlag': 17, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'zpj', 'EncryChatRoomId': '', 'IsOwner': 0}>, 'Type': 'Text', 'Text': '123'}
分析下要采集的数据,MsgId(微信信息的标识,用来作为键),NickName(发送者的用户名),Content(信息内容),CreateTime(创建时间), Type(信息类型)。接着是群聊信息:
群聊信息: {'MsgId': '3177606925001563512', 'FromUserName': '@@16521484d35b2fe9c953282d98ec4f11456607924b3a7cc6d7fb671fe7e3081c', 'ToUserName': '@913c3124d973db6ae25924bd0598b48a0028e0c2d01e18d8095cc6fd58db116b', 'MsgType': 1, 'Content': '嘿嘿', 'Status': 3, 'ImgStatus': 1, 'CreateTime': 1533010298, 'VoiceLength': 0, 'PlayLength': 0, 'FileName': '', 'FileSize': '', 'MediaId': '', 'Url': '', 'AppMsgType': 0, 'StatusNotifyCode': 0, 'StatusNotifyUserName': '', 'RecommendInfo': {'UserName': '', 'NickName': '', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '', 'Signature': '', 'Alias': '', 'Scene': 0, 'VerifyFlag': 0, 'AttrStatus': 0, 'Sex': 0, 'Ticket': '', 'OpCode': 0}, 'ForwardFlag': 0, 'AppInfo': {'AppID': '', 'Type': 0}, 'HasProductId': 0, 'Ticket': '', 'ImgHeight': 0, 'ImgWidth': 0, 'SubMsgType': 0, 'NewMsgId': 3177606925001563512, 'OriContent': '', 'EncryFileName': '', 'ActualNickName': '易♂建♂联', 'IsAt': False, 'ActualUserName': '@8fd8b5b2bd0862ed5d0d573bc6c08362', 'User':
同样分析下要采集的数据,MsgId(微信信息的标识),ActualNickName(发送者群名称),Content(信息内容),CreateTime(创建时间), Type(信息类型)。改下我们的程序,把这些都提取打印出来。
import itchat from itchat.content import * # 好友信息监听 @itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isFriendChat=True) def handle_friend_msg(msg): msg_id = msg['MsgId'] msg_from_user = msg['User']['NickName'] msg_content = msg['Content'] msg_create_time = msg['CreateTime'] msg_type = msg['Type'] print("收到信息: ", msg_id, msg_from_user, msg_content, msg_create_time,msg_type) # 群聊信息监听 @itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isGroupChat=True) def information(msg): msg_id = msg['MsgId'] msg_from_user = msg['ActualNickName'] msg_content = msg['Content'] msg_create_time = msg['CreateTime'] msg_type = msg['Type'] print("群聊信息: ",msg_id, msg_from_user, msg_content, msg_create_time,msg_type) if __name__ == '__main__': itchat.auto_login() itchat.run()
代码执行结果如下:
群聊信息: 2254622820807367335 胡小韬 对手公司 1533023277 Text 群聊信息: 1765614482944449471 xia_ang 还有自干五 1533023285 Text 好友信息: 615083621872361432 CoderPig 哈哈 1533023293 Text 好友信息: 7292909308782687092 CoderPig 你好哦 1533023302 Text
19.5.2 验证不同信息类型和对应处理方式
嗯,信息提取成功,接下来要验证的是不同的信息类型和对应的处理方式,文字,图片(表情),音频,视频,文件五种,后面四种都需要下载到本地,itchat中提供了一个下载文件的方法**msg['Text'](文件存储路径)
**,调用这个方法即可完成文件下载,修改后的代码如下:
import itchat from itchat.content import * import os import time # 文件临时存储页 rec_tmp_dir = os.path.join(os.getcwd(), 'tmp/') # 存储数据的字典 rec_msg_dict = {} # 好友信息监听 @itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isFriendChat=True) def handle_friend_msg(msg): msg_id = msg['MsgId'] msg_from_user = msg['User']['NickName'] msg_content = '' # 收到信息的时间 msg_time_rec = time.strftime("%Y-%m-%d %H:%M%S", time.localtime()) msg_create_time = msg['CreateTime'] msg_type = msg['Type'] if msg['Type'] == 'Text': msg_content = msg['Content'] elif msg['Type'] == 'Picture' \ or msg['Type'] == 'Recording' \ or msg['Type'] == 'Video' \ or msg['Type'] == 'Attachment': msg_content = r"" + msg['FileName'] msg['Text'](rec_tmp_dir + msg['FileName']) rec_msg_dict.update({ msg_id: { 'msg_from_user': msg_from_user, 'msg_time_rec': msg_time_rec, 'msg_create_time': msg_create_time, 'msg_type': msg_type, 'msg_content': msg_content } }) print(msg) # 群聊信息监听 @itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isGroupChat=True) def information(msg): msg_id = msg['MsgId'] msg_from_user = msg['ActualNickName'] msg_content = '' # 收到信息的时间 msg_time_rec = time.strftime("%Y-%m-%d %H:%M%S", time.localtime()) msg_create_time = msg['CreateTime'] msg_type = msg['Type'] if msg['Type'] == 'Text': msg_content = msg['Content'] elif msg['Type'] == 'Picture' \ or msg['Type'] == 'Recording' \ or msg['Type'] == 'Video' \ or msg['Type'] == 'Attachment': msg_content = r"" + msg['FileName'] msg['Text'](rec_tmp_dir + msg['FileName']) rec_msg_dict.update({ msg_id: { 'msg_from_user': msg_from_user, 'msg_time_rec': msg_time_rec, 'msg_create_time': msg_create_time, 'msg_type': msg_type, 'msg_content': msg_content } }) print(msg) if __name__ == '__main__': if not os.path.exists(rec_tmp_dir): os.mkdir(rec_tmp_dir) itchat.auto_login() itchat.run()
代码执行后,分别测试下发送各种文件,看是否都缓存下来,如图所示
19.5.3 监控撤回信息和数据提取
接着到撤回信息的监控,撤回的信息类型是NOTE,我们监听下看看,看看撤回的信息具体内容。
{'MsgId': '7399110162640182490', 'FromUserName': '@9c1a8bf4e28771a6b3ab635991dea2a1', 'ToUserName': '@49d1d90b90371099297a08da1009f3cdd042f21194239ef47b60e8f0b52e4553', 'MsgType': 10002, 'Content': '<sysmsg type="revokemsg"><revokemsg><session>zpj779878443</session><oldmsgid>1625723544</oldmsgid><msgid>3154925139554625499</msgid><replacemsg><![CDATA["CoderPig" 撤回了一条消息]]></replacemsg></revokemsg></sysmsg>', 'Status': 4, 'ImgStatus': 1, 'CreateTime': 1533103679, 'VoiceLength': 0, 'PlayLength': 0, 'FileName': '', 'FileSize': '', 'MediaId': '', 'Url': '', 'AppMsgType': 0, 'StatusNotifyCode': 0, 'StatusNotifyUserName': '', 'RecommendInfo': {'UserName': '', 'NickName': '', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '', 'Signature': '', 'Alias': '', 'Scene': 0, 'VerifyFlag': 0, 'AttrStatus': 0, 'Sex': 0, 'Ticket': '', 'OpCode': 0}, 'ForwardFlag': 0, 'AppInfo': {'AppID': '', 'Type': 0}, 'HasProductId': 0, 'Ticket': '', 'ImgHeight': 0, 'ImgWidth': 0, 'SubMsgType': 0, 'NewMsgId': 7399110162640182490, 'OriContent': '', 'EncryFileName': '', 'User': <User: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@9c1a8bf4e28771a6b3ab635991dea2a1', 'NickName': 'CoderPig', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=625711027&username=@9c1a8bf4e28771a6b3ab635991dea2a1&skey=@crypt_2d4a1972_e0963a9b961045c2e06293043f1c98a8', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 1, 'Signature': '不服气,就用行动去证明,少说多做...2018.7.29', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'CODERPIG', 'PYQuanPin': 'CoderPig', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 33783847, 'Province': '广东', 'City': '江门', 'Alias': '', 'SnsFlag': 17, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'zpj', 'EncryChatRoomId': '', 'IsOwner': 0}>, 'Type': 'Note', 'Text': '"CoderPig" 撤回了一条消息'}
首先是判断系统信息是默认撤回了一条信息,内容是: 显示判断是否是撤回的信息,从上面可以看到这样的串字符串:<![CDATA["CoderPig" 撤回了一条消息]]>
,我们只需要写个正则判断是否有这样的内容,有说明是撤回的信息提醒,接着找撤回的信息MsgId,在上面这串信息的前面就是了:<msgid>3154925139554625499</msgid>
,同样用正则提取。接着要做的就是拿着MsgId去查存消息的字典,根据信息类型拼接文字或文字加文件,然后发送给文件传输助手。具体代码如下:
@itchat.msg_register([NOTE], isFriendChat=True, isGroupChat=True) def revoke_msg(msg): if revoke_msg_compile.search(msg['Content']) is not None: old_msg_id = extract_msgid_compile.search(msg['Content']).group(1) old_msg = rec_msg_dict.get(old_msg_id, {}) # 先发送一条文字信息 itchat.send_msg(str(old_msg.get('msg_from_user') + "撤回了一条信息:" + old_msg.get('msg_content')), toUserName="filehelper") # 判断文msg_content是否存在,不存在说明可能是 if os.path.exists(os.path.join(rec_tmp_dir, old_msg.get('msg_content'))): if old_msg.get('msg_type') == 'Picture': itchat.send_image(os.path.join(rec_tmp_dir, old_msg.get('msg_content')), toUserName="filehelper") elif old_msg.get('msg_type') == 'Video': itchat.send_video(os.path.join(rec_tmp_dir, old_msg.get('msg_content')), toUserName="filehelper") elif old_msg.get('msg_type') == 'Attachment' \ or old_msg.get('msg_type') == 'Recording': itchat.send_file(os.path.join(rec_tmp_dir, old_msg.get('msg_content')), toUserName="filehelper")
运行后测试下发送信息后撤回,看下是否生效,测试结果如图所示。
19.5.4 定时清理缓存
另外,我们的信息都是用一个字典存着的,时间长了的话,数据会很多,而且图片文件这些会越攒越多,我们可以添加一个定时任务,比如每隔五分钟清理一下创建时长超过2分钟的信息和对应的文件。相关代码如下:
# 每隔五种分钟执行一次清理任务 def clear_cache(): # 当前时间 cur_time = time.time() # 遍历字典,如果有创建时间超过2分钟(120s)的记录,删除,非文本的话,连文件也删除 for key in list(rec_msg_dict.keys()): if int(cur_time) - int(rec_msg_dict.get(key).get('msg_create_time')) > 120: if not rec_msg_dict.get(key).get('msg_type') == 'Text': file_path = os.path.join(rec_tmp_dir, rec_msg_dict.get(key).get('msg_content')) print(file_path) if os.path.exists(file_path): os.remove(file_path) rec_msg_dict.pop(key) # 开始轮询任务 def start_schedule(): sched.add_job(clear_cache, 'interval', minutes=2) sched.start() # 退出停止所有任务并清空缓存文件夹 def after_logout(): sched.shutdown() shutil.rmtree(rec_tmp_dir) if __name__ == '__main__': sched = BlockingScheduler() if not os.path.exists(rec_tmp_dir): os.mkdir(rec_tmp_dir) itchat.auto_login(exitCallback=after_logout) itchat.run(blockThread=False) start_schedule()
19.6 小结
本章我们对微信网页接口开源库itchat库进行了学习,通过三个实用的代码示例,相信你对这个库已经有个大概的了解了,你也可以根据自己的需求来定制一个自己的机器人,比如添加自动回复,特定信息监控转发等。