当前位置 博文首页 > 撤回我也能看到!教你用Python制作微信防撤回脚本

    撤回我也能看到!教你用Python制作微信防撤回脚本

    作者:克金森沐沐 时间:2021-08-09 18:28

    目录
    • 一、之前解决方案
    • 二、分析msg信息
    • 三、确定消息类型
    • 四、锁定撤回的消息
    • 五、结语

    一、之前解决方案

    大概是这样:短时间内同一位好友发送了多条消息,当他随便撤回一条消息时,我们不能确定他到底撤回的到底是哪一条消息。只能猜他可能是撤回了最近的一条消息,然后将其他消息贴出来作为备选。代码如下:

    target_msg_pattern = '"{}" 撤回了一条消息'.format(sender_name)
    if content == target_msg_pattern:
        return_msg = '【{}】撤回了一条消息:\n'.format(sender_name)
        if len(log[sender_name].items()) == 0:
            return_msg = '缓存信息列表为空!'
        else:
            return_msg += log[sender_name].items()[-1][-1] + '\n'
            if len(log[sender_name].items()) > 1:
                msgs = [msg for timestamp, msg in log[sender_name].items()[:-1]]
                return_msg += '也有可能是下列信息中的某一条:\n' + '\n'.join(msgs)
    

    实际效果是这样:


    在这里插入图片描述 

    我这个强迫症简直受不了这么不确定的说法。

    二、分析msg信息

    要想确定撤回了哪一条信息,就必须先熟悉普通msg和撤回的msg里面都有哪些信息,他们的相同点和不同点。下面就来看看这两种情况下msg都是怎么样的,不需要仔细的看每一行,后面会作具体分析。

    先是用机器人“小帮帮”发送过来的信息得到的msg信息:

    {
    	'MsgId': '2018511155698964390',
    	'FromUserName': '@**********f511363f8200853d724137bb31236a7ea81e5183cc06cb4ec978e3',
    	'ToUserName': '@**********c2e61fdb47b5c241553a2f',
    	'MsgType': 1,
    	'Content': 'msg里面到底有什么?',
    	'Status': 3,
    	'ImgStatus': 1,
    	'CreateTime': 1578069291,
    	'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': 2018511155698964390,
    	'OriContent': '',
    	'EncryFileName': '',
    	'User': < User: {
    		'MemberList': < ContactList: [] > ,
    		'Uin': 0,
    		'UserName': '@**********f511363f8200853d724137bb31236a7ea81e5183cc06cb4ec978e3',
    		'NickName': '小帮帮',
    		'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=699837854&username=@**********f511363f8200853d724137bb31236a7ea81e5183cc06cb4ec978e3&skey=@crypt_****c00c_92668c8ba7d285c221a85e**********',
    		'ContactFlag': 2049,
    		'MemberCount': 0,
    		'RemarkName': '小帮帮',
    		'HideInputBarFlag': 0,
    		'Sex': 2,
    		'Signature': '',
    		'VerifyFlag': 0,
    		'OwnerUin': 0,
    		'PYInitial': 'XBB',
    		'PYQuanPin': 'xiaobangbang',
    		'RemarkPYInitial': 'XBB',
    		'RemarkPYQuanPin': 'xiaobangbang',
    		'StarFriend': 0,
    		'AppAccountFlag': 0,
    		'Statues': 0,
    		'AttrStatus': 33658937,
    		'Province': '浙江',
    		'City': '台州',
    		'Alias': '',
    		'SnsFlag': 17,
    		'UniFriend': 0,
    		'DisplayName': '',
    		'ChatRoomId': 0,
    		'KeyWord': '',
    		'EncryChatRoomId': '',
    		'IsOwner': 0
    	} > ,
    	'Type': 'Text',
    	'Text': 'msg里面到底有什么?'
    }
    

    下面是机器人撤回刚才的信息得到的msg信息:

    {
    	'MsgId': '4056955577161654067',
    	'FromUserName': '@**********f511363f8200853d724137bb31236a7ea81e5183cc06cb4ec978e3',
    	'ToUserName': '@**********c2e61fdb47b5c241553a2f',
    	'MsgType': 10002,
    	'Content': '<sysmsg type="revokemsg"><revokemsg><session>wxid_4gngrr04aqjn21</session><oldmsgid>1123721956</oldmsgid><msgid>2018511155698964390</msgid><replacemsg><![CDATA["小帮帮" 撤回了一条消息]]></replacemsg></revokemsg></sysmsg>',
    	'Status': 4,
    	'ImgStatus': 1,
    	'CreateTime': 1578069381,
    	'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': 4056955577161654067,
    	'OriContent': '',
    	'EncryFileName': '',
    	'User': < User: {
    		'MemberList': < ContactList: [] > ,
    		'Uin': 0,
    		'UserName': '@**********f511363f8200853d724137bb31236a7ea81e5183cc06cb4ec978e3',
    		'NickName': '小帮帮',
    		'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=699837854&username=@**********f511363f8200853d724137bb31236a7ea81e5183cc06cb4ec978e3&skey=@crypt_****c00c_92668c8ba7d285c221a85e**********',
    		'ContactFlag': 2049,
    		'MemberCount': 0,
    		'RemarkName': '小帮帮',
    		'HideInputBarFlag': 0,
    		'Sex': 2,
    		'Signature': '',
    		'VerifyFlag': 0,
    		'OwnerUin': 0,
    		'PYInitial': 'XBB',
    		'PYQuanPin': 'xiaobangbang',
    		'RemarkPYInitial': 'XBB',
    		'RemarkPYQuanPin': 'xiaobangbang',
    		'StarFriend': 0,
    		'AppAccountFlag': 0,
    		'Statues': 0,
    		'AttrStatus': 33658937,
    		'Province': '浙江',
    		'City': '台州',
    		'Alias': '',
    		'SnsFlag': 17,
    		'UniFriend': 0,
    		'DisplayName': '',
    		'ChatRoomId': 0,
    		'KeyWord': '',
    		'EncryChatRoomId': '',
    		'IsOwner': 0
    	} > ,
    	'Type': 'Note',
    	'Text': '"小帮帮" 撤回了一条消息'
    }
    

    得到了两种类型的msg,下面是对比(高亮的部分是不同处,省略了部分相同内容。可以点击放大查看大图

    在这里插入图片描述

    现在来分析几条关键信息:

    • MsgId(与下面的NewMsgId同)
    • 消息编号。这个很好理解,每条消息都是通过一个独一无二的编号来与其他消息区分,所以这两条消息的编号不同很正常。如果我们能拿到好友撤回消息的编号,也就能锁定这条消息了。
    • MsgType(与下面的Type同)
    • 消息类型。如下图,左边是普通的对话消息,右边类似于系统提示消息。是不是可以根据这条信息来判断是不是有好友撤回了消息?

    在这里插入图片描述

    Content

    消息内容,注意与下面的Text区分,这两种消息类型在内容上最大的区别可能就在这里了。

    来看一下撤回消息的Content是怎么样的(为了便于查看,已经经过格式化)

    <sysmsg type="revokemsg">
        <revokemsg>
            <session>wxid_4gngrr04aqjn21</session>
            <oldmsgid>1123721956</oldmsgid>
            <msgid>2018511155698964390</msgid>
            <replacemsg><![CDATA["小帮帮" 撤回了一条消息]]></replacemsg>
        </revokemsg>
    </sysmsg>
    

    一眼就能发现关键点:撤回的那条消息属于系统消息(sysmsg),类型是撤回消息(revokemsg),对应的消息编号是2018511155698964390

    细心的读者已经发现,这个消息编号正好就是左边那条消息的编号。

    通过这个推理,猜测Content字段是系统内部传输的内容,而Text字段则是用户看到的内容。

    三、确定消息类型

    根据上述分析,有三个地方帮助确定收到的某条信息是否是撤回的消息:

    1.MsgType

    1就是普通消息,是10002则可能为撤回消息。

    2.Content

    如果Content里有包含type="revokemsg"则可能为撤回消息,否则不是撤回消息。

    3.Type

    是Text就是普通消息,是Note则可能为撤回消息。

    精确起见,消息还要同时满足上面三种情况才可认定为撤回消息。

    四、锁定撤回的消息

    由于要锁定撤回消息必须要MsgId才能确定,所以在存储临时消息时需要加上这一字段。

    log[sender_name][cur_timestamp] = msg['MsgId'] + '|||' + content

    为了简化数据复杂度,我通过分隔符|||直接把MsgId加在前面。

    于是,锁定并发送撤回消息的代码就时这样:

    content = str(msg['Text'])
    revoke_info = msg['Content']
    print('{}, {} 发来消息: {}'.format(formatted_timestamp, sender_name, content))
    target_msg_pattern = '"{}" 撤回了一条消息'.format(sender_name)
    if target_msg_pattern == content and msg['Type'] == 'Note' and str(msg['MsgType']) == '10002' and 'type="revokemsg"' in revoke_info:
        return_msg = ''
        return_msg_head = '{},【{}】撤回了一条消息:\n'.format(formatted_timestamp, sender_name)
        revoke_msg_id = revoke_info.split('<msgid>')[-1].split('</msgid>')[0]
        for _, value in log[sender_name].items():
            if value.split('|||')[0] == revoke_msg_id:
                return_msg = value.split('|||')[1]
        if return_msg == '':
            return_msg = '缓存信息列表为空!'
        return_msg = return_msg_head + return_msg
        print(return_msg)
        itchat.send_msg(return_msg, 'filehelper')
    

    测试一下,为便于查看,将撤回提醒直接发给机器人“小帮帮”

    在这里插入图片描述 

    一个完美的微信防撤回脚本大功告成!

    五、结语

    Python有很多好用好玩的库,可以慢慢发掘。本期我们利用ItChat库编写了一个微信防撤回脚本。其实ItChat功能远远不止这些,它还可以处理微信群消息以及各种其他类型的消息,我们讲到的只是九牛一毛,更多的还要大家自己去探索。

    jsjbwy