欢迎光临
我们一直在努力

微信电脑版4.1.7.30版本聊天记录监听脚本(带ui界面)

摘要

微信4.1.x.x版本的UI采用新的框架开发,能够获取到的信息有限,目前只能获取到消息列表的控件内容。

image.png

代码

运行后需要安装2个库,根据提示安装。

如何运行?

  • 登录微信电脑版4.1.7.30,不能完全点击❌关闭界面,可以最小化;
  • 运行脚本;
  • UI界面启动即可自动获取聊天列表的最新的那条消息;

如果无法获取到,请退出微信重新登录再试试。

# -*- coding: utf-8 -*-
# pip install pyqt5 uiautomation
import sys
import re
import datetime
import uiautomation as auto
from PyQt5 import QtCore, QtWidgets

TARGET_DEPTH = 14


def is_time_line(text: str):
    text = text.strip()
    return bool(re.match(r'^/d{1,2}:/d{2}$', text) or re.match(r'^/d{2}//d{2}$', text))


def parse_session(name_block: str):
    lines = [l.strip() for l in name_block.splitlines()]
    lines = [l for l in lines if l]
    if not lines:
        return None

    session_name = lines[0]
    time_text = None
    for l in reversed(lines):
        if is_time_line(l):
            time_text = l
            break

    ignore_keywords = ['已置顶', '消息免打扰', '撤销']
    message_line = None
    for l in lines[1:]:
        if l == time_text:
            continue
        if any(k in l for k in ignore_keywords):
            continue
        message_line = l

    if not message_line:
        return None

    msg_type = "文本"
    if ':' in message_line:
        sender, content = message_line.split(':', 1)
        return {
            "group_name": session_name,
            "sender": sender.strip().strip('"'),
            "content": content.strip(),
            "time": time_text,
            "msg_type": msg_type
        }

    return {
        "group_name": session_name,
        "sender": "我",
        "content": message_line,
        "time": time_text,
        "msg_type": msg_type
    }


def time_to_sort_key(time_str):
    """把 HH:MM 或 MM/DD 转成 datetime,用于排序"""
    if not time_str:
        return datetime.datetime.min
    try:
        if re.match(r'^/d{1,2}:/d{2}$', time_str):
            h, m = map(int, time_str.split(":"))
            now = datetime.datetime.now()
            return datetime.datetime(now.year, now.month, now.day, h, m)
        elif re.match(r'^/d{2}//d{2}$', time_str):
            month, day = map(int, time_str.split("/"))
            now = datetime.datetime.now()
            return datetime.datetime(now.year, month, day)
    except:
        return datetime.datetime.min
    return datetime.datetime.min


class FetchThread(QtCore.QThread):
    data_signal = QtCore.pyqtSignal(list)

    def __init__(self, interval=1.5):
        super().__init__()
        self.interval = interval
        self._running = False
        auto.SetGlobalSearchTimeout(10)

    def run(self):
        self._running = True
        while self._running:
            try:
                results = self.fetch_data()
                if results:
                    # 按时间倒序排序
                    results.sort(key=lambda x: time_to_sort_key(x.get("time")), reverse=True)
                    self.data_signal.emit(results)
            except:
                pass
            self.msleep(int(self.interval * 1000))

    def stop(self):
        self._running = False

    def set_interval(self, interval):
        self.interval = interval

    def fetch_data(self):
        result_list = []
        root = auto.GetRootControl()
        target = root.Control(searchDepth=5, ClassName='mmui::MainWindow')
        if not target.Exists(2):
            return result_list

        def dump(control, depth=0):
            if depth == TARGET_DEPTH:
                try:
                    if control.ClassName == 'mmui::ChatSessionCell' and control.Name:
                        result = parse_session(control.Name)
                        if result:
                            result_list.append(result)
                except:
                    pass
                return
            for child in control.GetChildren():
                dump(child, depth + 1)

        dump(target)
        return result_list


class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.resize(1300, 690)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        self.main_layout = QtWidgets.QVBoxLayout(self)
        self.main_layout.setContentsMargins(8, 8, 8, 8)

        self.container = QtWidgets.QFrame()
        self.container.setObjectName("container")
        self.container_layout = QtWidgets.QVBoxLayout(self.container)
        self.container_layout.setContentsMargins(20, 20, 20, 20)
        self.main_layout.addWidget(self.container)

        # 顶部栏
        title_bar = QtWidgets.QHBoxLayout()
        self.container_layout.addLayout(title_bar)
        self.title = QtWidgets.QLabel("微信会话实时监听")
        self.title.setStyleSheet("font-size:18px;font-weight:bold;")
        self.close_btn = QtWidgets.QPushButton("✕")
        self.close_btn.setFixedSize(36, 32)
        self.close_btn.clicked.connect(self.close)
        title_bar.addWidget(self.title)
        title_bar.addStretch()
        title_bar.addWidget(self.close_btn)

        # 切换按钮
        self.toggle_btn = QtWidgets.QPushButton()
        self.toggle_btn.setFixedSize(120, 60)
        self.toggle_btn.setStyleSheet("font-size:18px;font-weight:bold;")
        self.container_layout.addWidget(self.toggle_btn, alignment=QtCore.Qt.AlignLeft)

        # 表格
        self.table = QtWidgets.QTableWidget()
        self.table.setColumnCount(5)
        self.table.setHorizontalHeaderLabels(
            ["昵称", "发送者", "消息类型", "内容", "时间"]
        )
        self.table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        self.table.verticalHeader().setVisible(False)
        self.table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.table.setWordWrap(True)
        self.container_layout.addWidget(self.table)

        # 线程
        self.thread = FetchThread(interval=1.5)
        self.thread.data_signal.connect(self.update_table)

        self.toggle_btn.clicked.connect(self.toggle_listen)

        self.setStyleSheet("""
        QWidget {
            font-family: "Microsoft YaHei";
            color: #e6e6e6;
        }
        #container {
            background-color: #1e1f26;
            border-radius: 14px;
        }
        QPushButton {
            border-radius: 10px;
            font-size:18px;
            font-weight:bold;
        }
        QTableWidget {
            background-color: #232530;
            gridline-color: #2f3240;
        }
        QHeaderView::section {
            background-color: #2d2f3a;
            border: none;
            padding: 6px;
        }
        """)

        self._drag_pos = None
        self.listening = False
        self.start_listen()
        self.shown_msgs = set()

    # 拖动
    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self._drag_pos = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if self._drag_pos and event.buttons() == QtCore.Qt.LeftButton:
            self.move(event.globalPos() - self._drag_pos)
            event.accept()

    def mouseReleaseEvent(self, event):
        self._drag_pos = None

    # 监听控制
    def toggle_listen(self):
        if self.listening:
            self.stop_listen()
        else:
            self.start_listen()

    def start_listen(self):
        if not self.thread.isRunning():
            self.thread.start()
        self.listening = True
        self.toggle_btn.setText("停止监听")
        self.toggle_btn.setStyleSheet("""
            QPushButton {
                background-color: #c0392b;
                border-radius: 10px;
                font-size:18px;
                font-weight:bold;
            }
            QPushButton:hover {
                background-color: #e74c3c;
            }
        """)

    def stop_listen(self):
        self.thread.stop()
        self.listening = False
        self.toggle_btn.setText("开始监听")
        self.toggle_btn.setStyleSheet("""
            QPushButton {
                background-color: #27ae60;
                border-radius: 10px;
                font-size:18px;
                font-weight:bold;
            }
            QPushButton:hover {
                background-color: #2ecc71;
            }
        """)

    # 表格追加,最新在上
    def add_center_item(self, row, col, text):
        item = QtWidgets.QTableWidgetItem(text)
        item.setTextAlignment(QtCore.Qt.AlignCenter)
        self.table.setItem(row, col, item)

    # 修改 update_table 方法
    def update_table(self, data):
        for item in data:
            msg_key = (item["group_name"], item["sender"], item["content"], item["time"])
            if msg_key in self.shown_msgs:
                continue  # 已渲染过就跳过
            self.shown_msgs.add(msg_key)

            self.table.insertRow(0)  # 最新消息插在最上面
            self.add_center_item(0, 0, item["group_name"])
            self.add_center_item(0, 1, item["sender"])
            self.add_center_item(0, 2, item["msg_type"])
            content_item = QtWidgets.QTableWidgetItem(item["content"])
            content_item.setTextAlignment(QtCore.Qt.AlignCenter)
            content_item.setToolTip(item["content"])
            self.table.setItem(0, 3, content_item)
            self.add_center_item(0, 4, item["time"] or "")
            self.table.setRowHeight(0, 60)
            self.table.setColumnWidth(3, 300)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

界面

image.png

本文作者

TANKING

https://segmentfault.com/a/1190000047621009

未经允许不得转载:IT极限技术分享汇 » 微信电脑版4.1.7.30版本聊天记录监听脚本(带ui界面)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址