写在开头:这个功能是小白在学Python中做的小玩意儿。慎用,万一你朋友不小心说了句你不爱听的话,撤回了你没看见,大家还是好朋友;万一你看到了,那友情就打折扣了,这可不好。您也别做啥不合法的事,小白还得准备考研二战~
上篇文章[ 初学python--微信防撤回功能改进(一个用处不大的功能)]中,使用Python的itchat库实现了好友聊天消息防撤回:实现原理,备份两分钟之内的消息,通过撤回通知的信息找到被撤回消息的ID,然后提取发送到文件助手,并保存相关的附件,只能保存在PC,手机没法直接查看;对储存的超时消息进行清理。只有简单的exe,没有界面,也没有Mac和Linux版(穷屌用不上Mac(⋟﹏⋞),怎么开发)。
各位跟我一样的小白,看完这两篇文章,我觉得你应该学会以下内容(不管是看书,上网查资料):字符串及其相关常用函数;元组、列表、字典等数据结构及其常用相关函数的使用;正则表达式及其re库的相关函数;time库的常用函数;文件/文件夹的增删改查(os 和 shutil);itchat库的使用。一定要把基础打牢,不要像我一样求快。最后,欢迎学完这些知识后来重构我的代码或者自己写出来,大家一起学习。
这次的版本中添加了:
1.群聊消息的撤回备份
2.保存的附件并不是零散的堆放在程序文件附近,统一存放在cache中,撤回的消息的附件放在revocation中。
3.实现了发送指令到文件助手可以进行查看删除文件。暂不支持中文名称的文件,还在找原因。
4.支持gif动图,但是不支持微信表情包中表情的撤回及备份
效果图:
撤回消息以及查看:
PC端Cache和Revocation文件夹
各种形式的撤回消息,主要是针对可下载的附件:
(注:看图最后,不支持查看中文文件名的文件,但是中文文件已经保存)
已打包生成exe:http://pan.baidu.com/s/1ckZi14 密码:ksu0
最新版本:修复群聊非好友bug,多谢@嘻笑怒骂提出bug并帮忙改进
链接:http://pan.baidu.com/s/1eRAtqTk 密码:4iy1
最新版本3.0 :群聊添加群聊昵称 链接:http://pan.baidu.com/s/1jHVjOhC 密码:6xc3
双击运行,扫码登陆,即可挂在电脑后台,退出的话从手机点击退出网页版微信
有知友说要弹窗,这样好点儿。有程序界面的版本还得等几天,小白正在努力学习GUI。
微信公众号自动签到功能和查询哪位好友删除自己功能还在实现中,如果可以的话我会第一时间在专栏发布。
刚学Python一个多月,代码和思路远远不够pythonic,请各路大神批评指正,不胜感激(´▽`ʃƪ)
下面是主要代码,大部分跟上篇文章中的代码相似。这个文件功能主要执行发送给文件助手的命令:
查看文件[文件名] e.g. 查看文件[170304-220138.gif]
删除文件[文件名] e.g. 删除文件[170304-220138.gif]
撤回附件列表
有问题请及时通过评论,私信反馈给我,帮我进步,谢谢~
喜欢的话点个赞再走呗~
用着还可以的话赞赏一个更好(●'◡'●)ノ❤
最后是代码~
#WechatForRevocation.py
# -*-encoding:utf-8-*-
import os
import re
import shutil
import time
import itchat
from itchat.content import *
import Execution
{msg_id:(msg_from,msg_to,msg_time,msg_time_touser,msg_type,msg_content,msg_url)}
msg_store = {}
ClearTimeOutMsg用于清理消息字典,把超时消息清理掉
为减少资源占用,此函数只在有新消息动态时调用
def ClearTimeOutMsg():
if msg_store.len() > 0:
for msgid in list(msg_store): # 由于字典在遍历过程中不能删除元素,故使用此方法
#print("TimeOut:", time.time() - msg_store.get(msgid, None)["msg_time"])
if time.time() - msg_store.get(msgid, None)["msg_time"] > 130.0: # 超时两分钟
item = msg_store.pop(msgid)
` # 可下载类消息,并删除相关文件
if item['msg_type'] == "Picture" \
or item['msg_type'] == "Recording" \
or item['msg_type'] == "Video" \
or item['msg_type'] == "Attachment":
print("要删除的文件:", item['msg_content'])
os.remove(".\\Cache\\" + item['msg_content'])
`
将接收到的消息存放在字典中,当接收到新消息时对字典中超时的消息进行清理
没有注册note(通知类)消息,通知类消息一般为:红包 转账 消息撤回提醒等,不具有撤回功能
@itchat.msg_register([TEXT, PICTURE, MAP, CARD, SHARING, RECORDING, ATTACHMENT, VIDEO, FRIENDS], isFriendChat=True,
isGroupChat=True)
def Revocation(msg):
# 处理指令
itchat.get_friends(update=True)
print("TEst",msg)
if msg['ToUserName'] == "filehelper" and msg['Type'] == "Text":
result = Execution.Execution(msg)
` if result[0] == 0:
itchat.send(result[1] + r"文件:" + result[2] + r" 失败", toUserName="filehelper")
elif result[0] == 1:
itchat.send(r"删除文件:" + result[2] + r" 成功", toUserName="filehelper")
else:
pass
return
mytime = time.localtime() # 这儿获取的是本地时间
获取用于展示给用户看的时间 2017/03/03 13:23:53
msg_time_touser = mytime.tm_year.str()
+ "/" + mytime.tm_mon.str()
+ "/" + mytime.tm_mday.str()
+ " " + mytime.tm_hour.str()
+ ":" + mytime.tm_min.str()
+ ":" + mytime.tm_sec.str()
msg_id = msg['MsgId'] # 消息ID
msg_time = msg['CreateTime'] # 消息时间
if itchat.search_friends(userName=msg['FromUserName']):
if itchat.search_friends(userName=msg['FromUserName'])['RemarkName']:
msg_from = itchat.search_friends(userName=msg['FromUserName'])['RemarkName'] # 消息发送人备注
elif itchat.search_friends(userName=msg['FromUserName'])['NickName']: # 消息发送人昵称
msg_from = itchat.search_friends(userName=msg['FromUserName'])['NickName'] # 消息发送人昵称
else:
msg_from = r"读取发送消息好友失败"
else:
msg_from = msg['ActualNickName']
msg_type = msg['Type'] # 消息类型
msg_content = None # 根据消息类型不同,消息内容不同
msg_url = None # 分享类消息有url
图片 语音 附件 视频,可下载消息将内容下载暂存到当前目录
if msg['Type'] == 'Text':
msg_content = msg['Text']
elif msg['Type'] == 'Picture':
msg_content = msg['FileName']
msg'Text'
shutil.move(msg_content, r".\Cache\")
elif msg['Type'] == 'Card':
msg_content = msg['RecommendInfo']['NickName'] + r" 的名片"
elif msg['Type'] == 'Map':
x, y, location = re.search("<location x="(.?)" y="(.?)".label="(.?)".*", msg['OriContent']).group(1,
2,
3)
if location is None:
msg_content = r"纬度->" + x.str() + " 经度->" + y.str()
else:
msg_content = r"" + location
elif msg['Type'] == 'Sharing':
msg_content = msg['Text']
msg_url = msg['Url']
elif msg['Type'] == 'Recording':
msg_content = msg['FileName']
msg'Text'
shutil.move(msg_content, r".\Cache\")
elif msg['Type'] == 'Attachment':
msg_content = msg['FileName']
msg'Text'
shutil.move(msg_content, r".\Cache\")
elif msg['Type'] == 'Video':
msg_content = msg['FileName']
msg'Text'
shutil.move(msg_content, r".\Cache\")
elif msg['Type'] == 'Friends':
msg_content = msg['Text']
# print(r"消息提取",
# {"msg_from": msg_from, "msg_time": msg_time, "msg_time_touser": msg_time_touser, "msg_type": msg_type,
# "msg_content": msg_content, "msg_url": msg_url})
print("消息提取", msg)
更新字典
{msg_id:(msg_from,msg_time,msg_time_touser,msg_type,msg_content,msg_url)}
msg_store.update(
{msg_id: {"msg_from": msg_from, "msg_time": msg_time, "msg_time_touser": msg_time_touser, "msg_type": msg_type,
"msg_content": msg_content, "msg_url": msg_url}})
清理字典
ClearTimeOutMsg()
`
@itchat.msg_register([NOTE], isFriendChat=True, isGroupChat=True)
def SaveMsg(msg):
print(msg)
创建可下载消息内容的存放文件夹,并将暂存在当前目录的文件移动到该文件中
if not os.path.exists(".\Revocation"):
os.mkdir(".\Revocation")
`itchat.search_chatrooms()
if re.search(r"![CDATA[.撤回了一条消息]]", msg['Content']) != None:
print("撤回Msg", msg)
if re.search("&lt;msgid&gt;(.?)&lt;/msgid&gt;", msg['Content']) != None:
old_msg_id = re.search("&lt;msgid&gt;(.?)&lt;/msgid&gt;", msg['Content']).group(1)
elif re.search(";msgid&amp;gt;(.?)&amp;lt", msg['Content']) != None:
old_msg_id = re.search(";msgid&amp;gt;(.*?)&amp;lt", msg['Content']).group(1)
old_msg = msg_store.get(old_msg_id, {})
print(r"撤回的消息", old_msg_id, old_msg)
if old_msg:
msg_send = r"您的好友:" \
+ old_msg.get('msg_from', None) \
+ r" 在 [" + old_msg.get('msg_time_touser', None) \
+ r"], 撤回了一条 [" + old_msg.get('msg_type', None) + "] 消息, 内容如下:" \
+ old_msg.get('msg_content', None)
if old_msg['msg_type'] == "Sharing":
msg_send += r", 链接: " \
+ old_msg.get('msg_url', None)
elif old_msg['msg_type'] == 'Picture' \
or old_msg['msg_type'] == 'Recording' \
or old_msg['msg_type'] == 'Video' \
or old_msg['msg_type'] == 'Attachment':
msg_send += r", 存储在当前目录下Revocation文件夹中"
shutil.move(r".\\Cache\\" + old_msg['msg_content'], r".\\Revocation\\")
else:
msg_send = r"您的好友可能撤回了一个微信表情包,暂时不支持微信表情包,请谅解。"
itchat.send(msg_send, toUserName='filehelper') # 将撤回消息的通知以及细节发送到文件助手
msg_store.pop(old_msg_id)
`
if name == 'main':
ClearTimeOutMsg()
if not os.path.exists(".\Cache"):
os.mkdir(".\Cache")
itchat.auto_login(hotReload=True)
itchat.run()
#Execution.py
import os
import re
import time
import itchat
def Execution(message):
command = message['Text']
print("command:", command)
if re.search(r"(.?)文件\[(.?)]", command):
action, filename = re.search(r"(.?)文件\[(.?)]", command).group(1, 2)
return ViewDeleteFile(action, filename)
elif re.search(r"^公众号签到$", command):
return Signin()
elif re.search(r"^查询好友状态$", command):
return (3,"","")
#return FriendStutas()
elif re.match(r"^撤回附件列表$", command):
return ReturnAttachmentList()
else:
itchat.send(r"亲,暂时支持以下指令:"
r"【查看/删除文件[文件名] e.g.123345234.mp3】 "
r"【撤回附件列表(查看都有哪些保存在电脑中的已撤回附件)】 "
r"其他指令暂不支持,请期待最新版本。", toUserName="filehelper")
return (3, "指令", "失败")
def ViewDeleteFile(action, filename):
if action == None or filename == None:
itchat.send(r"亲,目前支持两种指令:查看/删除文件[文件名] e.g.查看文件[12345678.jpg]", toUserName="filehelper")
return (3, "指令", "文件")
`if action == r"查看":
if re.search(r"png|jpg|bmp|jpeg|gif", filename):
msg_type = "Picture"
elif re.search(r"avi|rm|map4|wmv", filename):
msg_type = "Video"
else:
msg_type = "fil"
itchat.send("@%s@%s" % (
{"Picture": "img", "Video": "vid"}.get(msg_type, 'fil'), r".\\Revocation\\" + filename),
toUserName="filehelper")
return (2, action, filename)
elif action == r"删除":
if os.path.exists(r".\Revocation\" + filename):
os.remove(r".\Revocation\" + filename)
return (1, action, filename)
return (0, action, filename)
查询把自己删除的好友 除了一个个添加好友,还有一个实现方式:全部添加进去,然后获取群聊好友列表然后逐个比对。 def FriendStutas(): friendlist = itchat.get_friends()[0:1] delete_friend_list = [] succeed_friend_list = []
chat_topic = r"球队专属聊天群"
itchat.create_chatroom(memberList=friendlist, topic=chat_topic)
if itchat.search_chatrooms(name=chat_topic):
for new_friend in itchat.get_friends()[1:]:
result_friend = itchat.add_member_into_chatroom(itchat.search_chatrooms(name=chat_topic)[0]['UserName'],
[new_friend])
if result_friend.get('BaseResponse', None).get('ErrMsg', None):
print(r"添加结果 ", result_friend)
succeed_friend_list.append(new_friend)
else:
if new_friend['RemarkName']:
delete_friend_list.append(new_friend['RemarkName'])
else:
delete_friend_list.append(new_friend['NickName'])
itchat.delete_member_from_chatroom(itchat.search_chatrooms(name=chat_topic)[0]['UserName'], succeed_friend_list)
msg_send = r"以下好友把你删除了,请核实:"
for item in delete_friend_list: msg_send.join(item + ", ")
itchat.send(msg_send, toUserName="filehelper")
else:
itchat.send(r"查询失败(包括这次一共有三次机会)", toUserName="filehelper")
print('#' * 30)
print(r"以下好友删除你了...")
print("deleted:", delete_friend_list)
print(r"以下好友没有删除你:")
print("succeed:", succeed_friend_list)
print(itchat.search_chatrooms(name=chat_topic))
return (3, "查询", "状态")
`
返回撤回附件所有文件名
def ReturnAttachmentList():
filepath = ".\Revocation"
filelist = os.listdir(filepath)
if filelist:
msg_send = r"所有储存的附件如下:"
for item in filelist: msg_send = msg_send + item + ", "
itchat.send(msg_send, toUserName="filehelper")
else:
itchat.send(r"亲,暂时没有撤回的附件", toUserName="filehelper")
return (3, "附件列表", "成功")
微信公众号签到
def Signin():
itchat.send("亲,暂时不支持公众号签到功能,请谅解。", toUserName="filehelper")
return (3, "签到", "状态")