1. 项目概述与核心价值
上周折腾了一个挺有意思的小工具,起因是我发现自己在电脑前工作时,手机经常放在一边充电或者静音,结果错过了不少微信消息。虽然PC版微信能解决一部分问题,但它必须保持前台运行,而且通知形式比较单一,容易淹没在其他窗口里。于是我就想,能不能把微信消息直接推到Windows的原生通知中心里去?这样既醒目,又能利用通知中心的特性,比如历史记录、勿扰模式,甚至更进一步——支持快捷回复,不用切回微信窗口就能回个“好的”、“马上到”。
这个想法听起来简单,但真动手做,发现里面门道不少。核心思路就是让Python扮演一个“桥梁”的角色:一边监听微信的消息,另一边调用Windows的接口把消息发出去,并接收用户的回复动作。整个过程涉及几个关键环节:如何稳定地获取微信消息、如何与Windows通知系统交互、如何设计一个支持回调的快捷回复按钮。我最终实现的效果是,当手机微信收到消息时,电脑右下角会弹出一个Windows原生风格的通知,上面显示发送人、消息内容,并附带几个预设的快捷回复按钮(比如“收到”、“稍等”)。点击按钮,回复就会自动发送回微信。
这个项目特别适合那些需要长时间专注在电脑前,但又不想错过重要即时消息的朋友,比如程序员、设计师、文案工作者。它不依赖任何商业软件,完全本地运行,隐私有保障。下面,我就把这一周从零搭建这个工具的全过程、踩过的坑以及核心代码的实现细节,毫无保留地分享出来。
2. 技术方案选型与整体架构
要实现这个“微信消息转发到Windows通知中心并支持快捷回复”的功能,我们需要拆解成三个核心模块:消息获取模块、通知发送与交互模块和消息回复模块。每个模块都有几种技术路线可选,我的选型是基于稳定性、易实现性和对普通用户友好度来考虑的。
2.1 消息获取模块:为何选择Hook方式而非官方API
首先是最关键的一步:如何让Python知道微信收到了新消息。最理想的情况当然是调用微信官方API,但众所周知,个人微信并没有开放这样的消息推送接口。所以我们必须另辟蹊径。常见的有两种思路:一是模拟一个微信客户端(Web版或协议版)来登录并接收消息;二是直接监听已经登录的PC版微信客户端。
第一种方式,比如使用itchat或wxpy这类库,它们本质上是调用微信Web版的协议。但近年来微信对Web版登录的限制越来越严格,经常需要扫码且不稳定,容易被封,不适合作为长期后台服务运行。因此我放弃了这条路。
我选择的是第二种:直接监听PC微信客户端的进程和窗口。PC微信在收到消息时,无论是任务栏图标闪烁,还是聊天窗口标题变化,其进程内存或UI状态一定会发生改变。我们可以通过Windows API去捕获这些变化。具体来说,我使用了pywin32这个库,它提供了对Windows API的Python绑定。通过FindWindow找到微信窗口,然后周期性地检查其窗口标题(当有新消息时,标题会包含消息数提示),或者更底层地,通过内存扫描寻找消息存储的结构。为了简化并提高稳定性,我最终采用了UI Automation(通过uiautomation库)来监听微信聊天列表的变化。这个库可以获取窗口内控件的属性变化,当新消息出现时,对应联系人的列表项会更新,我们可以捕获到这个事件。
注意:任何对非自有软件进行自动化的操作都应遵守其用户协议,并仅限于个人学习和提高效率使用。本项目代码不应用于任何干扰他人或商业牟利的场景。
2.2 通知发送与交互模块:WinRT API的优势
获取到消息内容后,下一步就是把它展示到Windows通知中心。这里也有多个选择:最简单的可以用win10toast库,它封装了旧版的ToastNotification,但功能较弱,不支持按钮等交互。为了实现“快捷回复”,我们必须使用更现代、功能更完整的接口。
Windows 10/11 的通知系统基于WinRT (Windows Runtime) API。我们可以通过Python的winrt包(即python-windows-runtime)来直接调用这些原生接口。它允许我们创建包含标题、正文、图片、按钮等丰富元素的Toast通知,并且可以为按钮绑定回调函数。当用户点击通知按钮时,我们的Python程序能够接收到这个事件,从而触发后续的回复逻辑。这是实现“快捷回复”功能的技术基础。
2.3 消息回复模块:模拟输入与控件操作
最后,当用户点击了通知中的快捷回复按钮,我们需要将对应的回复文本发送回微信。这里我们同样无法调用发送消息的API,所以只能采用模拟用户操作的方式。
我的方案是:当点击回复按钮后,Python脚本会自动将PC微信窗口激活到前台,然后模拟键盘输入回复内容,最后模拟按下回车键发送。这个过程需要用到pyautogui或pynput库来模拟键盘操作,同时用pywin32来确保目标输入框获得焦点。这里有一个关键细节:需要确保模拟输入的速度适中,并且要在输入前清空可能的原有内容,避免消息发送错乱。
2.4 整体架构流程图
整个工具的运行时数据流是这样的:
- 监听线程:使用
uiautomation持续监控微信聊天列表控件。 - 事件触发:当检测到新消息项(通过控件属性如
Name变化判断)时,提取发送人和消息摘要。 - 消息处理:将提取的信息(发送人、内容预览)传递给通知管理模块。
- 通知发送:使用
winrt创建包含快捷回复按钮的Toast通知,并显示。 - 用户交互:用户点击通知按钮,触发注册的回调函数。
- 回复执行:回调函数根据按钮ID,将对应的回复文本通过
pyautogui模拟输入到激活的微信输入框中,并发送。
这个架构清晰地将监听、展示、交互三个环节解耦,方便后续维护和扩展(比如增加更多消息源或回复模板)。
3. 环境准备与核心库详解
工欲善其事,必先利其器。在开始写代码之前,我们需要搭建好Python环境并安装几个关键的库。我会详细说明每个库的作用、安装时可能遇到的问题以及版本选择的原因。
3.1 Python环境与IDE选择
我使用的是Python 3.8+的版本。选择这个版本区间是因为它既有较好的新特性支持,又与后面要用到的winrt等库的兼容性最好。太老的版本(如3.6)可能缺少一些依赖,太新的版本(如3.11+)有时会遇到第三方库尚未适配的问题。
开发工具我推荐VSCode或PyCharm。VSCode轻量且插件丰富,对于这种小型脚本项目非常合适。记得安装Python扩展,它会帮你管理虚拟环境、提供代码提示和调试功能。
强烈建议使用虚拟环境来隔离项目依赖。在项目根目录下,打开终端执行:
python -m venv venv然后激活它:
- Windows (CMD):
venv\Scripts\activate.bat - Windows (PowerShell):
venv\Scripts\Activate.ps1(可能需要先执行Set-ExecutionPolicy RemoteSigned)
3.2 核心依赖库安装与避坑指南
接下来是安装核心库。请按顺序执行以下命令:
pip install pywin32 pip install uiautomation pip install winrt pip install pyautogui下面我逐个解释这些库,并分享安装和使用中的坑:
pywin32
- 作用:提供访问Windows API的能力,比如查找窗口、操作窗口、模拟键盘消息(
SendMessage)等。它是我们与Windows系统交互的基础。 - 避坑:直接
pip install通常没问题。但如果安装失败,可以去 pywin32在GitHub的发布页 下载对应Python版本和系统位数的.whl文件进行离线安装。
- 作用:提供访问Windows API的能力,比如查找窗口、操作窗口、模拟键盘消息(
uiautomation
- 作用:用于自动化控制和监听Windows GUI元素。我们将用它来监听微信主窗口聊天列表的变化,这是获取新消息通知的关键。
- 避坑:这个库有时在获取某些控件时速度较慢或不太稳定。在实际代码中,我们需要合理设置查找控件的超时时间,并做好异常处理。另外,确保以管理员身份运行你的Python脚本,否则可能无法正确捕获其他进程的UI控件。
winrt
- 作用:调用Windows Runtime API,用于创建和发送带有交互按钮的现代Toast通知。
- 避坑:这是最容易出问题的一环。
winrt库的安装和导入方式有些特殊。- 安装:务必使用
pip install winrt,而不是过时的pip install python-windows-runtime。 - 导入:不能直接
import winrt。正确的导入方式是import winrt.windows.ui.notifications as notifications和import winrt.windows.data.xml.dom as xmlDom。如果导入时报错“找不到模块”,很可能是因为你的Python环境不是从微软商店安装的,或者系统版本太旧。WinRT API要求Windows 10版本1809(Build 17763)或更高。
- 安装:务必使用
pyautogui
- 作用:模拟鼠标移动、点击和键盘输入。我们将用它来把回复文本“敲”进微信的输入框。
- 避坑:模拟操作的速度要控制好,
pyautogui.PAUSE参数可以设置每个函数调用后的暂停时间,避免操作过快导致程序跟不上。另外,在模拟输入前,最好用pyautogui.click()先点击一下目标输入框区域,确保焦点正确。
3.3 辅助工具:Inspect.exe 和 Notification Visualizer
在开发过程中,有两个Windows自带的小工具能帮上大忙:
- Inspect.exe:它是Windows SDK的一部分(如果你安装了Visual Studio,通常可以在
C:\Program Files (x86)\Windows Kits\10\bin\下找到)。用它可以直接查看任何窗口控件的自动化属性(如Name,AutomationId,ClassName),是我们编写uiautomation选择器时的“眼睛”。 - Notification Visualizer:这是一个Windows商店应用。它可以让你预览Toast通知的XML模板,确保布局和按钮显示正确,对于调试通知格式非常有用。
环境准备好后,我们就可以进入激动人心的编码环节了。
4. 核心代码实现与分步解析
接下来,我将把整个项目拆解成四个核心函数,并附上详细的代码和注释。你可以跟着一步步实现。
4.1 第一步:监听微信新消息
我们的目标是监听微信主界面左侧的聊天列表。当有新消息时,列表里对应联系人的条目会发生变化(比如未读计数、消息预览更新)。
首先,我们需要用uiautomation定位到微信的主窗口和列表控件。
import uiautomation as auto import time def get_wechat_window(): """ 查找并返回微信主窗口的控件对象。 微信窗口的ClassName通常是'WeChatMainWndForPC'。 """ wechat_window = auto.WindowControl(searchDepth=1, ClassName='WeChatMainWndForPC') if not wechat_window.Exists(): print("未找到微信窗口,请确保微信PC版已登录并打开。") return None # 将窗口提到前台,便于后续监听(可选,监听其实不需要前台) wechat_window.SetTopmost(True) time.sleep(0.5) wechat_window.SetTopmost(False) return wechat_window def get_chat_list_control(window): """ 在微信窗口内查找聊天列表控件。 通过Inspect.exe工具,我确认聊天列表是一个ListView控件。 """ # 查找深度可以调整,确保能定位到 list_control = window.ListControl(searchDepth=4, ClassName='ListView') if list_control.Exists(): return list_control else: print("未找到聊天列表控件。") return None有了列表控件,我们就可以监听其子项的变化了。思路是定期获取当前所有列表项的关键信息(如显示的名称),与上一次的结果对比,找出新增或变化的项。
def monitor_new_messages(interval=2): """ 监控微信聊天列表,检测新消息。 :param interval: 检查间隔(秒) """ prev_items = {} # 用于缓存上一次扫描到的项,格式:{item_name: item_object} while True: window = get_wechat_window() if not window: time.sleep(interval) continue chat_list = get_chat_list_control(window) if not chat_list: time.sleep(interval) continue current_items = {} # 获取当前列表的所有子项 for item in chat_list.GetChildren(): # 尝试获取显示的名称,这里可能需要根据实际情况调整属性 # 有时是 Name 属性,有时需要获取子TextControl的文本 name = item.Name if not name: # 如果Name为空,尝试其他方式 text_controls = item.TextControl() if text_controls: # 可能第一个TextControl是联系人,第二个是消息预览 name = text_controls[0].Name if text_controls else "Unknown" if name and name not in ['', '微信']: # 过滤掉无效项和标题栏 current_items[name] = item # 判断是否为新增或更新的消息:不在上一次的缓存中,或者该项有“未读”状态标识 # 一个简单的启发式方法:检查控件名称是否包含数字(未读计数)或特定标识 if name not in prev_items: print(f"[新消息] 来自: {name}") # 这里可以进一步提取消息预览,例如查找item下的第二个TextControl preview = "收到一条新消息" # 默认预览 text_all = item.GetChildren() for child in text_all: if isinstance(child, auto.TextControl): # 通常联系人名和消息预览是不同的TextControl # 这里逻辑需要根据实际UI结构调整,可能需要取第二个非空的Text if child.Name != name and child.Name: preview = child.Name break # 触发通知发送函数 send_windows_notification(sender=name, message=preview) prev_items = current_items time.sleep(interval)实操心得:
uiautomation获取控件属性有时不稳定,尤其是在UI刷新时。所以我的对比逻辑做得比较“宽松”,只要发现新的Name就认为是新消息。这可能会导致重复通知(比如同一个联系人连续发多条),但稳定性优先。你可以通过增加更精确的状态判断(如检查是否有表示未读的红点控件)来优化。
4.2 第二步:构造并发送Windows Toast通知
这是项目的亮点。我们将使用winrt创建包含按钮的Toast通知。
import winrt.windows.ui.notifications as notifications import winrt.windows.data.xml.dom as xmlDom from winrt.windows.ui.notifications import ToastNotificationManager, ToastNotification # 定义我们的快捷回复选项 QUICK_REPLIES = { "reply_1": "收到,稍后处理。", "reply_2": "好的,马上来。", "reply_3": "在忙,晚点联系。" } def send_windows_notification(sender, message): """ 创建并发送一个带有快捷回复按钮的Windows Toast通知。 """ # 1. 获取Toast通知管理器和应用标识 # app_id可以任意,但最好是一个固定的字符串,这样通知会归组 app_id = "WeChat.Notifier.Python" notifier = ToastNotificationManager.create_toast_notifier(app_id) # 2. 定义Toast的XML模板 # 这里使用ToastGeneric模板,它支持Win10/11的现代样式 toast_xml = f""" <toast scenario="reminder" launch="action=view"> <visual> <binding template="ToastGeneric"> <text>微信新消息</text> <text>{sender}</text> <text>{message}</text> <image placement="appLogoOverride" src="C:\\Path\\To\\WeChat.ico"/> </binding> </visual> <actions> <action content="{QUICK_REPLIES['reply_1']}" arguments="action=quick_reply&type=reply_1" activationType="foreground"/> <action content="{QUICK_REPLIES['reply_2']}" arguments="action=quick_reply&type=reply_2" activationType="foreground"/> <action content="{QUICK_REPLIES['reply_3']}" arguments="action=quick_reply&type=reply_3" activationType="foreground"/> <action content="打开微信" arguments="action=open_wechat" activationType="foreground"/> </actions> </toast> """ # 注意:image的src需要替换为一个实际存在的图标路径,或者删除这一行。 # 3. 解析XML并创建通知对象 xml_doc = xmlDom.XmlDocument() xml_doc.load_xml(toast_xml) toast = ToastNotification(xml_doc) # 4. 为通知添加激活(点击)事件处理程序 # 当用户点击通知或按钮时,会触发这个事件 def on_activated(sender, args): arguments = args.arguments print(f"通知被激活,参数: {arguments}") # 解析参数,执行相应操作 if "action=quick_reply" in arguments: # 提取回复类型 for key in QUICK_REPLIES: if f"type={key}" in arguments: reply_text = QUICK_REPLIES[key] perform_quick_reply(reply_text, target_sender=sender) break elif "action=open_wechat" in arguments: # 打开微信(这里简单地将微信窗口前置) wechat_window = get_wechat_window() if wechat_window: wechat_window.SetFocus() toast.add_activated(on_activated) # 5. 发送通知 notifier.show(toast) print(f"已发送通知: {sender} - {message}")代码关键点解析:
- XML模板:
ToastGeneric是功能最丰富的模板。<actions>部分定义了按钮。每个<action>的arguments属性是自定义字符串,用于在回调中识别是哪个按钮被点击了。activationType="foreground"表示点击后激活前台应用(我们的Python脚本)。 - 事件处理:
toast.add_activated(on_activated)为整个通知(包括按钮和通知主体)注册了回调函数。所有交互都会触发这个函数,我们需要在函数内部根据args.arguments来区分不同的动作。 - 应用标识(App ID):
app_id很重要,相同app_id的通知会在通知中心里被分组。你可以起一个独特的名字。
4.3 第三步:实现快捷回复的模拟输入
当用户点击了通知按钮,on_activated函数被调用,并确定了要回复的文本。接下来就需要模拟键盘操作,将这段文本发送到正确的微信聊天窗口。
import pyautogui import pygetwindow as gw import time def perform_quick_reply(reply_text, target_sender): """ 执行快捷回复:激活对应联系人的微信窗口,输入文本并发送。 :param reply_text: 要回复的文本 :param target_sender: 消息发送者(联系人名称) """ print(f"准备回复 [{target_sender}]:{reply_text}") # 1. 激活微信主窗口 wechat_window = get_wechat_window() if not wechat_window: print("无法找到微信窗口,回复失败。") return # 2. 在聊天列表中查找目标联系人并点击激活对话 # 这里简化处理:我们假设目标联系人的聊天项就在当前可视列表内 # 更稳健的做法是遍历列表项进行匹配 chat_list = get_chat_list_control(wechat_window) if chat_list: found = False for item in chat_list.GetChildren(): if target_sender in item.Name: # 模糊匹配 item.Click() found = True time.sleep(0.3) # 等待聊天窗口加载 break if not found: print(f"未在聊天列表中找到联系人: {target_sender},尝试直接输入。") else: print("未找到聊天列表,直接尝试输入。") # 3. 确保输入框获得焦点 # 先模拟点击一下聊天输入区域中间偏下的位置(经验坐标,可能需要调整) # 获取窗口坐标 window_rect = wechat_window.BoundingRectangle # 假设输入框在窗口底部区域 input_x = window_rect.left + 200 input_y = window_rect.bottom - 100 pyautogui.click(input_x, input_y) time.sleep(0.2) # 4. 模拟键盘输入 # 先清空可能的原有内容(Ctrl+A, Delete) pyautogui.hotkey('ctrl', 'a') time.sleep(0.1) pyautogui.press('delete') time.sleep(0.1) # 输入回复内容 pyautogui.write(reply_text, interval=0.05) # interval控制输入速度 time.sleep(0.1) # 5. 模拟按下回车键发送 pyautogui.press('enter') print(f"回复已发送。")重要提示:模拟点击的坐标
(input_x, input_y)高度依赖于你的微信窗口大小和屏幕分辨率。这段代码很可能在你的电脑上不准确。正确的方法是:先不要写死坐标,在代码里加入调试,打印出窗口的BoundingRectangle,然后手动计算输入框的大致位置。或者,更高级的做法是使用uiautomation定位到输入框控件(EditControl)并直接调用其SetFocus()方法。
4.4 第四步:整合与主循环
最后,我们把所有部分组装起来,形成一个可以持续运行的服务。
import threading def main(): print("微信消息转发服务启动...") print("确保微信PC版已登录并窗口打开。") print("快捷回复按钮:", list(QUICK_REPLIES.values())) # 在一个单独的线程中运行消息监控,防止阻塞 monitor_thread = threading.Thread(target=monitor_new_messages, daemon=True) monitor_thread.start() # 主线程可以做一些其他事情,或者简单地等待 try: while True: time.sleep(1) except KeyboardInterrupt: print("\n服务被用户中断。") finally: print("服务停止。") if __name__ == "__main__": main()这样,一个完整的、支持快捷回复的微信消息转发工具就初具雏形了。运行main()函数,它就会在后台默默工作,将手机微信的消息实时转发到电脑通知中心。
5. 调试技巧与常见问题排查实录
在实际开发和运行过程中,你几乎一定会遇到各种问题。下面是我踩过坑后总结出来的排查清单和解决方案。
5.1 通知不显示或按钮点击无反应
这是最常见的问题,根本原因通常是WinRT通知的配置或事件处理有问题。
检查1:应用ID与快捷方式Windows Toast通知机制要求有一个“应用标识”,并且这个标识最好关联到一个开始菜单快捷方式。对于开发测试,我们可以手动创建一个。
- 在项目目录下创建一个
.bat脚本文件(如start_notifier.bat),内容为python your_script.py。 - 右键这个
.bat文件,“创建快捷方式”。 - 右键这个快捷方式,选择“属性” -> “高级” -> 勾选“用管理员身份运行”(可选,但有时需要)。
- 将这个快捷方式复制或移动到
C:\Users\[你的用户名]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs。 - 在代码中,
app_id必须设置为这个快捷方式的文件名(不含.lnk后缀),例如快捷方式叫WeChatNotifier.lnk,那么app_id = "WeChatNotifier"。
这个步骤很关键,很多通知显示问题都是因为App ID没有正确关联到开始菜单项。
- 在项目目录下创建一个
检查2:事件处理函数被垃圾回收在
send_windows_notification函数中,我们为toast对象添加了事件处理函数on_activated。如果这个toast对象被销毁,或者函数是局部函数且没有被持久引用,回调可能会失效。确保你的toast对象在需要响应的生命周期内(通常是整个程序运行期间)被持续引用。我的做法是将notifier和重要的回调绑定到全局或类实例变量中。检查3:系统通知设置去Windows设置 -> 系统 -> 通知和操作,确保“获取来自应用和其他发送者的通知”是打开的,并且检查下方应用列表里,你的应用ID(如
WeChatNotifier)是否被允许发送通知。
5.2 uiautomation 无法找到微信控件或监听不到变化
- 以管理员身份运行:这是首要条件。右键你的Python脚本或IDE,选择“以管理员身份运行”。没有管理员权限,
uiautomation无法跨进程访问其他程序的UI树。 - 使用Inspect.exe验证:打开Inspect.exe,将鼠标移动到微信的聊天列表上,查看工具显示的
ClassName、Name、AutomationId等属性。确保你的代码里使用的搜索条件(如ClassName='ListView')与Inspect显示的一致。微信的UI结构可能会随版本更新而变化。 - 调整搜索深度和条件:
searchDepth参数很重要。太浅可能找不到,太深可能找到多个同类控件或效率低下。可以尝试从1开始递增测试。也可以结合使用ControlType、Name等条件进行更精确的查找。 - 处理UI延迟:微信UI更新可能有延迟。在
monitor_new_messages函数中,time.sleep(interval)的间隔不宜太短(如小于1秒),否则可能错过更新周期或造成高CPU占用。2-3秒是个比较平衡的间隔。
5.3 模拟输入定位不准或发送到错误窗口
- 不要依赖绝对坐标:
pyautogui.click(input_x, input_y)这行代码是最不可靠的部分。一旦微信窗口移动、缩放,或者换了显示器,坐标就全错了。 - 改用控件定位:优先使用
uiautomation定位输入框控件。# 尝试定位微信输入框,它的ClassName可能是‘Edit’ edit_control = wechat_window.EditControl(searchDepth=6, ClassName='Edit') if edit_control.Exists(): edit_control.Click() # 或者直接设置焦点 edit_control.SetFocus() else: print("未找到输入框,尝试备用坐标方案。") # 备用方案:使用相对坐标(基于窗口位置计算) window_rect = wechat_window.BoundingRectangle input_x = window_rect.left + int(window_rect.width() * 0.3) # 横向30%处 input_y = window_rect.bottom - 50 # 距离底部50像素 pyautogui.click(input_x, input_y) - 增加延迟和确认:在关键操作(点击、输入)前后增加
time.sleep,确保UI有足够时间响应。可以在模拟输入前先发送一个测试字符(如#),看看是否真的输入到了微信里。 - 锁定目标窗口:在模拟输入前,确保微信窗口是激活状态。除了用
SetFocus,还可以用pyautogui的getWindowsWithTitle函数来获取并激活窗口。
5.4 程序稳定性与异常处理
一个需要长期运行的后台服务,健壮性至关重要。
- 全局异常捕获:在
monitor_new_messages和perform_quick_reply等主要函数内部,用try...except包裹核心逻辑,捕获AttributeError、TimeoutError等常见异常,并记录日志,避免因为一次偶然的UI异常导致整个线程崩溃。 - 心跳或看门狗机制:可以设置一个简单的计时器,如果超过一定时间没有正常执行循环,就尝试重启监听线程。
- 资源清理:虽然Python有GC,但对于
uiautomation和winrt的对象,在程序退出时最好能显式地进行一些清理操作(如果有相关方法的话)。
6. 功能扩展与优化方向
基础功能跑通后,你可以根据自己的需求,对这个工具进行多方面的增强。
6.1 消息内容过滤与优先级
不是所有消息都值得弹通知。可以在monitor_new_messages函数里,在触发send_windows_notification之前加入过滤逻辑。
- 关键词过滤:只通知包含特定关键词(如“急”、“开会”、“@你”)的消息。
keywords = ["急", "开会", "@"] if not any(keyword in message for keyword in keywords): return # 忽略此消息 - 联系人白名单/黑名单:只接收特定联系人或群组的消息,或者屏蔽某些人的消息。
- 消息类型判断:尝试区分文本、图片、语音、转账等。纯表情或系统消息可能不需要通知。
6.2 通知样式与行为的深度定制
WinRT Toast通知的XML模板非常强大。
- 添加头像:在
<image>标签中,可以引用网络图片或本地文件作为头像显示。 - 进度条:对于“对方正在输入...”这种场景,可以使用
<progress>标签(但需要持续更新通知,实现较复杂)。 - 输入框:甚至可以创建一个带输入框的Toast,让用户直接在里面输入文字回复,但这需要处理更复杂的前台激活和文本提交逻辑。
- 通知分组和排序:通过设置
tag和group属性,可以让来自同一联系人的通知合并显示,或者按时间排序。
6.3 部署为后台服务与开机自启
要让这个工具真正无缝地在后台工作,需要把它部署成服务。
- 使用NSSM:这是一个将普通exe封装成Windows服务的小工具。你可以先用
pyinstaller将Python脚本打包成notifier.exe,然后用NSSM将其安装为系统服务,并设置为开机自动启动(延迟启动,等微信启动后再运行)。 - 任务计划程序:更轻量的方法是创建一个任务计划,在用户登录时触发,运行你的Python脚本或打包后的exe。
- 日志记录:将
print语句替换为写入日志文件的函数(如使用logging模块),方便在后台运行时排查问题。
6.4 隐私与安全考量
最后必须强调一点:这个工具会读取你微信界面的内容。虽然代码本地运行,但务必注意:
- 代码审计:只使用你信任的代码。我的示例代码仅作学习参考。
- 网络隔离:确保脚本不会将任何消息内容发送到外部服务器。
- 权限最小化:以完成功能所需的最小权限运行脚本。
这个项目从构思到实现,花了我差不多一周的业余时间。最大的成就感不在于功能多么炫酷,而在于用代码实实在在地解决了一个日常痛点。整个过程就像在解一个多层的谜题:Windows API的调用、UI自动化的不确定性、通知交互的细节,每一个环节都有小坑。但当你看到手机一震,电脑右下角立刻弹出熟悉的通知,并能一键回复时,那种流畅感会让你觉得所有的折腾都是值得的。