| name | wechat-personal-message |
| description | 微信自动化助手 — 激活微信 → 置顶窗口 → 搜索联系人 → Vision OCR识别 → 进入聊天 → 自动发送。全程本地运行,支持 OCR 视觉反馈校验,可被外部工具调用。 |
| triggers | ["微信自动发消息","微信OCR识别","微信自动化","wechat automation","微信关键词回复","微信发送消息","微信Mac自动化"] |
微信自动化助手 (WeChat Automator) v4.2
功能概述
一套完整的微信自动化工具,基于 macOS 原生能力实现,全程本地运行:
激活微信 → 前置置顶窗口 → 搜索联系人 → 截图聊天窗口 → Vision OCR识别 → 关键词匹配 → 自动回复 → 发送消息
依赖与环境
| 依赖 | 要求 | 安装命令 |
|---|
| macOS | 10.15+ | — |
| pyobjc | 必须 | pip3 install pyobjc |
| cliclick | 必须 | brew install cliclick |
| Vision Framework | 内置 | macOS 10.15+ 自带 |
| screencapture | 内置 | macOS 系统自带 |
权限要求
请按以下步骤开启权限:
- 辅助功能权限:
系统设置 → 隐私与安全性 → 辅助功能 → 终端 → 开启
- 屏幕录制权限:
系统设置 → 隐私与安全性 → 屏幕录制 → 终端 → 开启
快速开始
检查系统环境
python3 ~/.hermes/skills/openclaw-imports/wechat-personal-message/scripts/wechat_automator.py check
完整自动化流程(OCR + 自动回复)
python3 ~/.hermes/skills/openclaw-imports/wechat-personal-message/scripts/wechat_automator.py run -c 张三
仅发送消息(不 OCR)
python3 ~/.hermes/skills/openclaw-imports/wechat-personal-message/scripts/wechat_automator.py send -c 张三 -m "晚上8点开会"
核心函数(供外部调用)
execute_wechat_automation()
主执行函数,支持完整自动化流程。
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|
contact_name | str | ✅ | 联系人名称(备注名或微信号) |
keyword_dict | dict | ❌ | 关键词字典,格式 {"类别": ["关键词1", "关键词2"]} |
default_reply | str | ❌ | 默认回复文案 |
custom_replies | dict | ❌ | 自定义回复覆盖,格式 {"类别": "回复内容"} |
chat_region | tuple | ❌ | 截图区域 (x, y, w, h) |
auto_reply | bool | ❌ | 是否自动回复,默认 True |
verbose | bool | ❌ | 是否输出详细日志,默认 True |
返回值: 执行结果字典
示例:
from wechat_automator import execute_wechat_automation
result = execute_wechat_automation(
contact_name="张三",
keyword_dict={
"价格": ["多少钱", "价格", "报价"],
"合作": ["合作", "代理", "加盟"],
"问候": ["你好", "您好", "在吗"]
},
default_reply="感谢您的消息,我会尽快回复您。"
)
print(result["success"])
print(result["matched_category"])
print(result["recognized_texts"])
print(result["reply_content"])
send_text_message()
快捷函数:仅发送文本消息(不 OCR)。
from wechat_automator import send_text_message
success = send_text_message("张三", "晚上8点开会")
check_system_ready()
检查系统环境是否就绪。
from wechat_automator import check_system_ready
status = check_system_ready()
print(status["ready"])
技术实现细节
AppleScript 键盘操作(关键实现)
⚠️ 重要:macOS AppleScript 的特殊键必须用 key code 方式,不能用 keystroke "return":
key_code_map = {
"return": 36,
"enter": 36,
"escape": 53,
"tab": 48,
"space": 49,
"backspace": 51,
"up": 126,
"down": 125,
"left": 123,
"right": 124,
}
script = '''
tell application "System Events"
key code 36 -- return
end tell
'''
动态窗口坐标计算(关键!)
⚠️ 绝对禁止硬编码坐标。微信窗口位置不固定,必须先获取窗口实际 rect,再按比例计算输入框坐标:
script = '''
tell application "System Events"
tell process "WeChat"
get {position, size} of window 1
end tell
end tell
'''
input_x = win_x + int(win_w * 0.75)
input_y = win_y + int(win_h * 0.92)
script = f'''
tell application "System Events"
tell process "WeChat"
click at {{{input_x}, {input_y}}}
end tell
end tell
'''
OCR 识别(Vision Framework)⚠️ pyobjc API 注意事项
新版 pyobjc (3.x+) API 与旧版不同,以下是已验证可用的 API:
from Vision import VNRecognizeTextRequest, VNImageRequestHandler
from AppKit import NSImage
img = NSImage.alloc().initWithContentsOfFile_(image_path)
recognized_texts = []
def completionHandler(request, error):
if error:
return
for observation in request.results() or []:
text = observation.text()
if text and text.strip():
recognized_texts.append(text)
request = VNRecognizeTextRequest.alloc().initWithCompletionHandler_(completionHandler)
request.setRecognitionLanguages_(["zh-Hans", "en-US"])
request.setUsesLanguageCorrection_(True)
img_data = img.TIFFRepresentation()
handler = VNImageRequestHandler.alloc().initWithData_options_(img_data, {})
handler.performRequests_error_([request], None)
常见错误:
initWithData_ → 应为 initWithData_options_
initWithContentsOfFile_() → 应先用 alloc() 再调用
setCompletionHandler_() → 应在 initWithCompletionHandler_ 时传入回调
initWithImage_options_ → 不存在,必须用 initWithData_options_
截图(screencapture)
screencapture -x -R "x,y,width,height" output.png
执行流程详解
1. send_text_message(contact, message)
├─ 保存原始剪贴板(最开始保存,避免被 search_contact 污染)
├─ activate_wechat()
├─ bring_to_front()
├─ search_contact(name) ← 会写入联系人名到剪贴板
├─ send_message(message, original_clipboard) ← 用保存的原始值
└─ 恢复原始剪贴板
2. search_contact(name)
├─ write_to_clipboard(name)
├─ Cmd+F 打开搜索框
├─ Cmd+V 粘贴联系人名
├─ 按 Enter 进入聊天(✅ 用 Enter,不依赖坐标点击!)
└─ ESC 关闭悬浮资料卡(如果出现)
3. send_message(text)
├─ 写入消息到剪贴板
├─ 多点位尝试点击输入框(75%, 50%, 居中)
├─ OCR 确认输入框就绪
├─ Cmd+V 粘贴消息
├─ OCR 确认文本已粘贴
├─ Enter 发送(微信 Mac 版用 Enter 发送)
└─ OCR 验证消息出现在聊天中
常见问题
Q: 消息发到了搜索聊天记录面板而不是聊天窗口?
A: 这是 v4.0 修复的关键问题。当 OCR 同时检测到"发送人"+"日期"+"搜索聊天记录"+"暂无"时,说明进入了「搜索聊天记录」面板而非真正的聊天窗口。ocr_check_chat_entered() 已内置此判断,send_message() 也会检测并按 ESC 关闭。但根本原因是:该联系人在微信里没有直接的私聊历史记录,微信只能搜索到群聊里的消息。这种情况无法自动化解决,需要先在微信里手动给该联系人发一条消息建立私聊,之后才可以用自动化发送。
Q: AI实赣派刘拾柒/AI星辰社朱老师发消息失败?
A: v4.1 修复了此问题。新逻辑:搜索后点击第一个结果 → 立即尝试发消息(不验证是否真正进入聊天窗口)。即使该联系人没有私聊记录,只要搜索结果列表第一项是该联系人,就会尝试发送。OCR 验证改为发送后验证,如果消息内容出现在屏幕上即认为成功。
Q: 搜索后 Enter 进入的是公众号文章页而不是聊天窗口?
A: 这种情况发生在搜索结果第一个是「AI搜索/网络结果」(公众号文章)时。v3.8 修复:当 Enter 未进入聊天时,代码会重新搜索、OCR 识别目标联系人在列表中的实际索引、点击正确坐标后再 Enter。OCR 定位联系人位置(支持部分名称匹配),搜索列表每项约 28 像素高。
Q: 消息发到了搜索框/悬浮资料卡而不是聊天窗口?
A: 这是 v3.5 修复的核心问题。搜索后按 Enter 会打开悬浮资料卡。正确流程是:① Cmd+F 搜索 ② Cmd+V 粘贴 ③ ↓ 方向键选中列表项 ④ Enter 进入聊天 ⑤ OCR 验证是否真的是聊天窗口(有输入框UI)。已内置自动修正逻辑。
Q: 返回 "returnescapereturn" 文字?
A: 说明 press_key("return") 用了 keystroke 方式而非 key code。已修复,特殊键必须用 key code。
Q: get_wechat_window_rect() 返回 None?
A: 微信窗口信息可能无法通过 System Events 获取,此时使用默认屏幕尺寸 (1920×1080) 作为 fallback,虽然可能不精准但通常仍可工作。如持续失败,可手动指定 chat_region 参数。
Q: 消息粘贴到输入框了但发送后对方没收到?
A: 检查是否用了错误的发送键 — 微信 Mac 版必须用 Enter(Return) 发送消息,单独按 Return 是换行。v3.6 已修复此问题。
Q: OCR 报错 VNImageRequestHandler has no attribute initWithData_?
A: 这是 pyobjc 版本问题。新版 API 必须是 initWithData_options_(带 options 参数),且必须先 alloc() 再 init...。
Q: OCR 验证说"仍在搜索预览"但实际右侧已显示聊天内容?
A: 这是 v3.8 修复的关键问题。微信 Mac 搜索后,即使左侧搜索UI还在,右侧也可能已显示聊天记录(联系人名+时间戳+消息内容)。ocr_check_chat_entered() 新增 has_right_panel_preview 判断:当检测到搜索UI存在但同时有联系人名、时间戳、多段文字(>10段)时,认为已进入「聊天预览」状态,可以直接发送消息。
Q: 搜索后点击联系人,但没进入聊天窗口而是弹出了悬浮资料卡?
A: 这是 v3.3 修复的问题:搜索后按 Return 会直接打开悬浮的资料卡。正确的做法是:① 先按一次 ↓ 方向键选中列表项 ② 再单击/双击联系人区域进入聊天,而不是按 Return。已加入 OCR 验证自动修正逻辑。
Q: 发送消息后,剪贴板里残留了联系人名称?
A: 这是 v4.2 修复的关键 bug。search_contact() 会把联系人名称写入剪贴板(用于粘贴搜索),如果保存原始剪贴板的时机不对,恢复时就变成了联系人名称。v4.2 修复:send_text_message() 在整个流程的最开始就保存原始剪贴板,确保恢复的是真正原始的内容。
版本历史
-
v4.2 (2026-05-22) — 剪贴板污染修复 + Enter 进入聊天
- 🐛 修复:剪贴板污染 bug —
send_text_message() 在 search_contact() 之后保存原始剪贴板,但 search_contact() 会写入联系人名称到剪贴板,导致恢复时残留联系人名而非原始内容。修复:最开始就保存原始剪贴板。
- 🐛 修复:搜索后点击「窗口右侧 75% 位置」会点到聊天列表里的其他联系人。修复:改用 Enter 键 直接进入聊天,不再依赖坐标点击。
- ✅ 实测:5位好友批量发送全部成功,OCR 验证确认消息送达,剪贴板恢复正常。
-
v4.1 (2026-05-21) — 无私聊记录联系人发送逻辑
- 🐛 修复:搜索后直接点第一个结果 → 发消息,不再验证是否真正进入聊天窗口
- 🐛 修复:
search_contact() 移除 OCR 聊天验证,点击第一个结果后直接返回成功
- 🐛 修复:
cliclick 坐标浮点数报错 — 所有坐标计算后取 int()
- ⚡ 逻辑变更:搜索后尝试点击窗口右侧聊天区域(65%, 75%) + ESC 关闭悬浮资料卡 → 直接发消息
- ✅ 实测:
AI实赣派刘拾柒(无私聊记录)成功发送消息,OCR 确认「这是来自拾柒老师的爱马仕Al...」出现在聊天中
-
v4.0 (2026-05-21) — 搜索聊天记录面板识别 + 联系人无私聊限制
- 🐛 修复:
ocr_check_chat_entered() 新增"搜索聊天记录面板"判断 — 当 OCR 检测到"发送人"+"日期"+"搜索聊天记录"+"暂无"时,认为是搜索聊天记录面板而非真正聊天窗口
- 🐛 修复:
send_message() 也会检测并关闭搜索聊天记录面板
- ⚠️ 重要限制:如联系人没有私聊历史记录,微信搜索后只能进入「搜索聊天记录」面板,无法主动发起私信,必须先手动建立私聊
- 🐛 修复:
search_contact() 优先匹配直接联系人(排除"包含"条目),避免点击到群聊里的成员名
- 🐛 修复:点击位置改为偏左下(55%, 88%),更接近真实输入框位置
-
v3.8 (2026-05-21) — 搜索预览状态精确判断 + 点击定位修复
- 🐛 修复:Enter后OCR验证认为"仍在搜索预览"但实际右侧已显示聊天内容的情况
- ✨ 新增:
has_right_panel_preview 判断 — 当搜索UI可见但右侧显示「联系人名+时间戳+消息内容」时,认为已进入聊天预览,可直接发消息
- 🐛 修复:fallback点击逻辑不再硬编码位置,而是用OCR在搜索列表中找到目标联系人的实际索引,再计算点击坐标
- 🔑 判断条件:
contact_name in text or text in contact_name 做部分匹配定位
- ✨ 搜索列表每项高度约28像素,从窗口顶部(8%)开始计算
-
v3.7 (2026-05-21) — 搜索结果分类识别 + 智能选择
- 🐛 修复:搜索结果第一个可能是「AI搜索/网络结果」(公众号文章)而非实际群聊/好友
- ✨ 新增
parse_search_results() — 解析OCR结果,识别「群聊/AI搜索/公众号/好友」类型
- ✨ 新增
find_target_result() — 优先选择「群聊」类型,排除AI搜索和公众号
- ✨ 新增
navigate_to_search_result() — 用方向键移动到目标条目
- 🔑 搜索结果详情页特征:
"AI搜索", "全部 文章", "账号 文章", "划线 视频", "搜一搜"
- 🔑 悬浮资料卡特征:
"微信号", "地区", "朋友圈", "更多", "个性签名" + 无输入框
-
v3.6 (2026-05-21) — 关键流程修正
- 🐛 修复:搜索后不要按 ↓ 方向键,否则会跳过好友(第一项)选中群聊(第二项)
- 🐛 修复:微信 Mac 版发送键是
Enter(Return),不是 Cmd+Return
- ✅ 实测:成功发送消息给朱子函,OCR 确认进入正确好友聊天
- ✨ 改用
cliclick 替代 osascript click,执行更稳定
- ✨ 新增
double_click_at坐标() 和 cliclick_wait() 支持双击和等待
- ✨ 重写
ocr_check_chat_entered() — 区分悬浮资料卡和真正聊天窗口
- 🔑 悬浮资料卡特征:有"微信号/地区/朋友圈"但无输入框
- 🔑 真正聊天窗口特征:有"输入/发送/图片/表情"等UI元素,或有联系人名+时间戳
- ✨
search_contact() 现在用 Enter 进入聊天(比双击更可靠),失败后自动修正
- ✨
send_message() 多点位扫描输入框 + 每步 OCR 验证
- ✅ 实测:刘时佶聊天窗口进入成功,OCR 确认显示「这是测试消息」
-
v3.4 (2026-05-21) — OCR 修复 + Cmd+Return 发送键修复
- 🐛 修复:pyobjc 新版 OCR API 全部更新(
initWithData_options_、initWithCompletionHandler_ 等)
- 🐛 修复:微信 Mac 版发送键必须是
Cmd+Return,单独 Return 只换行不发送
- ✨ 新增:
ocr_guided_send_message() — 每步 OCR 验证 + 自动修正的发送流程
- ✨ 新增:
click_input_box_smart() — 智能扫描窗口底部定位输入框
- ✨ 新增:
ensure_in_chat_window() — OCR 验证是否已进入聊天,未进入则自动修正
- ✨ 新增:
ocr_verify_message_sent() — 发送后 OCR 验证消息是否出现在聊天中
- ✨ 新增:OCR 验证流程嵌入 search_contact 和 send_message 每一步
- ✅ 实测:刘时佶成功收到消息,OCR 确认屏幕显示「这是测试消息」
-
v3.3 (2026-05-21) — 搜索进入聊天关键修复
- 🐛 修复:搜索后直接按 Return 打开悬浮资料卡而非进入聊天,增加 ESC 关闭悬浮卡
- 🔍 调试方法:截图 + vision 分析定位问题;单步日志确定失效步骤
- 📌 已知问题:搜索后 ↓ 方向键选择逻辑仍不稳定,取决于列表初始焦点
-
v3.2 (2026-05-21) — 发送流程关键修复
- 🐛 修复:双击输入框后可能选中了文字,导致粘贴覆盖而非插入。增加第三次点击(偏左50px)重置光标位置
- 🐛 修复:粘贴后等待时间从 0.3s 增至 0.5s,确保剪贴板内容完全写入
- ✨ 新增:发送后额外一次点击确认,解除输入框状态
- ✅ 实测:刘时佶成功收到自动发送的消息
-
v3.1 (2026-05-21) — 关键 bug 修复
- 🐛 修复:特殊键(return/escape)必须用
key code 而非 keystroke,否则会输入文字
- 🐛 修复:
click_at坐标 必须在 tell process "WeChat" 内执行才有权限
- 🐛 修复:进入聊天窗口后等待时间从 0.5s 增至 2.0s
- ✨ 新增:
click_at坐标() 函数替代无效的旧实现
- ✅ 实测:发送消息功能完全正常
-
v3.0 (2026-05-21) — 完全重写
- ✨ 基于 Vision Framework 重写 OCR 模块
- ✨ 纯 AppleScript 实现,移除 cliclick 依赖
- ✨ 新增
execute_wechat_automation() 主函数
维护者
贰拾(二十四)/ 辰景网络技术团队