Stories

Detail Return Return

微信電腦版4.1.X最新版獲取未讀消息並在右下角彈出提醒

摘要

微信4.1版本的UI採用新的框架開發,能夠獲取到的信息有限,目前只能獲取到消息列表的控件內容。

代碼

import time
import threading
import uiautomation as auto
from win10toast import ToastNotifier
import tkinter as tk
from tkinter import ttk
import re

shown_msgs = set()
FILE_NAME = "messages.txt"

import re

def parse_chat_name_flexible(raw_name: str):
    """
    靈活解析微信未讀消息,並去掉【消息免打擾】字樣和方括號中的未讀數
    """
    if not raw_name:
        return None

    # 去掉消息免打擾
    raw_name = re.sub(r'[\[\(【(]*消息免打擾[\]\)】)]*', '', raw_name)

    parts = raw_name.split()
    if len(parts) < 2:
        return None

    user = parts[0]
    unread = "0條未讀"
    msg = ""
    sender = ""
    time_str = ""

    start_idx = 1

    # 已置頂
    if "已置頂" in parts:
        start_idx += 1

    # 查找未讀條數
    for i in range(start_idx, min(start_idx + 2, len(parts))):
        if "條未讀" in parts[i]:
            unread = parts[i]
            start_idx = i + 1
            break

    # 查找時間
    for i in range(len(parts) - 1, start_idx - 1, -1):
        if ':' in parts[i] or '/' in parts[i]:
            time_str = parts[i]
            end_idx = i
            break
    else:
        end_idx = len(parts)

    msg_parts = parts[start_idx:end_idx]
    msg = " ".join(msg_parts)

    # 拆分發送人和消息內容
    if ":" in msg:
        sender, msg_content = msg.split(":", 1)
    elif ":" in msg:  # 中文冒號
        sender, msg_content = msg.split(":", 1)
    else:
        sender, msg_content = "", msg

    # 清理髮送人裏的 [n條]
    sender = re.sub(r'\[\d+條\]', '', sender).strip()

    return {
        "user": user.strip(),
        "unread": unread.strip(),
        "sender": sender.strip(),
        "msg": msg_content.strip(),
        "time": time_str.strip()
    }


def find_unread_sessions():
    desktop = auto.GetRootControl()
    chat_cells = []

    for control, depth in auto.WalkControl(desktop):
        if control.ClassName == "mmui::ChatSessionCell":
            if "未讀" in (control.Name or ""):
                parsed = parse_chat_name_flexible(control.Name)
                if parsed:
                    chat_cells.append(parsed)

    return chat_cells


def monitor_messages(tree):
    # 確保子線程正確初始化 COM
    with auto.UIAutomationInitializerInThread():
        toaster = ToastNotifier()
        while True:
            try:
                sessions = find_unread_sessions()

                for s in sessions:
                    key = f"{s['user']}_{s['msg']}_{s['time']}"
                    if key not in shown_msgs:
                        title = f"{s['user']} ({s['unread']})"
                        msg = f"{s['msg']}  [{s['time']}]"
                        print(f"[通知] {title} -> {msg}")

                        # 系統通知
                        toaster.show_toast(title, msg, duration=3, threaded=True)

                        # 寫入txt
                        with open(FILE_NAME, "a", encoding="utf-8") as f:
                            f.write(f"{s['time']}\t{s['user']}\t{s['unread']}\t{s['sender']}\t{s['msg']}\n")

                        # 更新表格
                        tree.after(0, lambda s=s: tree.insert(
                            "", "end",
                            values=(s['time'], s['user'], s['unread'], s['sender'], s['msg'])
                        ))

                        shown_msgs.add(key)

            except Exception as e:
                print(f"[錯誤] {e}")

            time.sleep(2)


def start_gui():
    root = tk.Tk()
    root.title("微信未讀消息監控")
    root.geometry("1200x550")

    style = ttk.Style(root)
    style.theme_use("default")
    style.configure("Treeview", rowheight=30, font=("Microsoft YaHei", 11))
    style.configure("Treeview.Heading", font=("Microsoft YaHei", 12, "bold"), anchor="center")

    # 新增 sender 列
    columns = ("time", "user", "unread", "sender", "msg")
    tree = ttk.Treeview(root, columns=columns, show="headings")
    tree.heading("time", text="時間")
    tree.heading("user", text="用户")
    tree.heading("unread", text="未讀")
    tree.heading("sender", text="發送人")
    tree.heading("msg", text="消息內容")

    # 設置列寬和對齊
    tree.column("time", width=100, anchor="center")
    tree.column("user", width=120, anchor="center")
    tree.column("unread", width=80, anchor="center")
    tree.column("sender", width=120, anchor="center")
    tree.column("msg", width=500, anchor="center")

    # 滾動條
    scrollbar = ttk.Scrollbar(root, orient="vertical", command=tree.yview)
    tree.configure(yscroll=scrollbar.set)
    scrollbar.pack(side="right", fill="y")

    tree.pack(fill="both", expand=True)

    # 啓動後台線程
    t = threading.Thread(target=monitor_messages, args=(tree,), daemon=True)
    t.start()

    root.mainloop()

if __name__ == "__main__":
    start_gui()

使用

需要將微信打開,不能關閉,可最小化,但絕對不能叉掉。

image.png

然後允許這個Python腳本即可。

Python wxmsg.py

目前支持的功能:

  1. 讀取私信未讀的最新的那條消息
  2. 讀取羣最新的未讀的那條消息
  3. 讀取最新的收款記錄
  4. 會在右下角彈出
  5. 會有ui界面,記錄消息
  6. 讀取公眾號最新發布的消息

界面

image.png

2025-10-25

部分用户反饋獲取不到消息,我這次基於4.1.1.19版本寫了一個腳本,大家可以試試。

image.png

import uiautomation as auto

def find_target(control, class_name, name):
    """遞歸查找指定 ClassName + Name 的控件"""
    if control.ClassName == class_name and control.Name == name:
        return control
    try:
        for c in control.GetChildren():
            result = find_target(c, class_name, name)
            if result:
                return result
    except Exception:
        pass
    return None

def find_all_names_by_class(control, class_name, results=None):
    """遞歸查找所有指定 ClassName 的控件並提取 Name"""
    if results is None:
        results = []
    try:
        for c in control.GetChildren():
            if c.ClassName == class_name:
                results.append(c.Name)
            find_all_names_by_class(c, class_name, results)
    except Exception:
        pass
    return results

# 查找微信主 XView
root = auto.Control(searchDepth=10, ClassName='mmui::XView')
if not root:
    raise Exception('未找到 XView')

# 查找會話列表 XTableView
x_tableview = find_target(root, 'mmui::XTableView', '會話')
if not x_tableview:
    raise Exception('未找到 mmui::XTableView | 會話')

# 提取所有 ChatSessionCell 的 Name
names = find_all_names_by_class(x_tableview, 'mmui::ChatSessionCell')

# 分割暱稱和消息輸出
for i, full_name in enumerate(names, 1):
    if not full_name:
        continue
    parts = full_name.split(' ', 1)  # 第一個空格分割
    nickname = parts[0]
    message = parts[1] if len(parts) > 1 else ''
    print(f"[{i}] 暱稱: {nickname} | 消息: {message}")

image.png

作者

TANKING

user avatar
0 users favorite the story!

Post Comments

Some HTML is okay.