- scripts/agent.py: GLM-vision WeChat guard agent (8s watch loop, red-dot detection, whitelist, title-verify gate, rate/quiet-hours caps, self-heal) - scripts/setup-server.sh: idempotent xfce+TigerVNC+noVNC+WeChat-Linux setup - scripts/check-skill.sh: skill guard (structure/secrets/privacy/claims/ encoding/syntax/CHANGELOG/bundle parity), self-exclusion aware - references/linux-server.md: deploy & ops guide for the 7x24 route - references/pitfalls-linux.md: 17 field-tested pitfalls - references/ops-workflow.md: baseline-first authoring + sync discipline (methodology credit: octopus-workflow) - SKILL.md/README: dual-platform entry; CHANGELOG added
12 KiB
name, description
| name | description |
|---|---|
| wechat-ops | 用「截图 → 视觉识别 → 动作 → 截图验证」的闭环操作电脑,代替人手点按钮、输文字、切窗口、走界面流程。只要用户想让你「帮我点一下」「操作我的电脑」「自动发个朋友圈/消息」「把这段文字输入到某个软件」「帮我走一遍这个界面流程」「模拟点击」「识图点击」「截图找按钮」「GUI 自动化」「RPA」,都必须使用本技能——即使用户没说要"用技能"。核心特征:坐标绝不写死落盘,每次实时看图现算、事后验证。若目标是在 Linux 云服务器上 7x24 挂机微信、微信自动回复、微信值守机器人,走 references/linux-server.md 的常驻 Agent 路线。若目标是在 Figma 画布内做设计,请改用 Figma 系列技能。 |
WeChat Ops
用视觉驱动的方式操作电脑:看屏幕 → 判断点哪里 → 动手 → 再看一眼确认。
零、两条路线,先选对
| 路线 A:macOS 本机(本文件主体) | 路线 B:Linux 云服务器(references/linux-server.md) | |
|---|---|---|
| 场景 | 操作你眼前电脑的一次性界面任务 | 微信 7x24 挂机、自动回复值守 |
| 工具 | scripts/vision.py(pyautogui) |
scripts/agent.py(GLM 视觉 Agent,systemd 常驻) |
| 坐标哲学 | 易耗品:每次实时定位,绝不落盘 | 固定资产:虚拟屏分辨率+最大化窗口固定,一次校准长期有效 |
| 决策 | 你实时判断 | 白名单+GLM 拟回复+代码闸门 |
两条路线共享同一套纪律(见 references/ops-workflow.md):失败基线优先、每步截图验证、 安全闸门固化进代码、证据分级表述、守卫检查、三方同步。
一、核心原则
1. 坐标是易耗品,绝不落盘
分辨率、窗口位置、应用版本、系统语言——任何一样变了,写死的坐标就失效。所以:
- 技能里存的是「怎么找到它」,不是「它在哪」
- ❌
朋友圈入口在 (30, 297) - ✅
朋友圈是左侧边栏第 5 个图标,光圈形状
- ❌
- 坐标只存在于本次任务的上下文里,任务结束即作废。
- 如果你正想「把这次成功的坐标记下来下次直接用」——停。那是在造一个明天就会坏的缓存。该记的是语义特征和判断方法。
这条是整个技能的地基。写死坐标的方案(无论存成 JSON 还是 Markdown)都只是把脆弱的快照换了个格式。
2. 每步都要验证
视觉方案真正的价值不是"能看图",而是"点完能确认"。
每次动作后截一张图,确认结果符合预期,再做下一步。看起来慢,实际上:
- 点错了能立刻发现并纠正,而不是一路错到底
- 界面和预想不一致时(弹窗、加载中、位置变了)能及时改道
判断标准:如果这一步失败了你不会察觉,那这一步就该加验证。
3. 先想清楚交互方式,再动手
同一个控件,单击和长按可能完全是两回事。动手前先问:这个操作是单击、双击、长按、右键还是拖拽?
真实案例:微信朋友圈的相机图标,单击打开"选图片"文件框,长按才出纯文字发布框。没搞清楚就会点出一个文件选择器。
拿不准就先用 --dry 或在无关紧要的地方试。
二、坐标系(先读这段,否则必错)
macOS Retina 屏上,截图分辨率和点击分辨率不是一回事。
screencapture输出的是物理像素(Retina 通常是 2 倍)pyautogui点击用的是逻辑像素
vision.py 已经处理了这件事:截图时自动缩放到逻辑分辨率,所以
图上量到的坐标 = 直接可用的点击坐标
这正是为什么所有操作都要走 vision.py 而不是直接 screencapture——手工换算 2 倍关系是最高频的错误来源。
三、工具:scripts/vision.py
| 命令 | 作用 |
|---|---|
shot [名字] |
截当前屏,缩放到逻辑分辨率。始终刷新 cur.png |
grid [名字] [间距] |
截图并叠加带刻度的坐标网格,读位置不用估算 |
crop x1 y1 x2 y2 [名字] [倍数] |
精确裁剪放大,看小图标/小字。左上角标注原点 |
click X Y |
单击 |
dclick X Y |
双击 |
longpress X Y [秒] |
长按(默认 1.2 秒) |
move X Y |
只移动鼠标 |
activate 名称 |
按窗口/进程名激活并置前 |
type "文本" |
输入文本,走剪贴板,支持中文长文本 |
key cmd+v |
按键,支持 cmd+shift+f 这类组合 |
scroll N [X Y] |
滚动。可指定鼠标落点——滚轮事件发给指针所在的视图 |
pos |
打印屏幕尺寸和当前鼠标位置 |
截图统一存在 /tmp/wechat_ops/。
环境依赖:pip install pyautogui pyperclip pillow,并在「系统设置 → 隐私与安全性 → 辅助功能」里授权终端。
四、标准流程
1. 激活目标窗口 activate <名称>
2. 看清全貌 shot / grid
3. 定位目标元素 grid 读数 → crop 放大确认(两种方法对上再动手)
4. 执行动作 click / longpress / type / key
5. 截图验证 shot,确认结果符合预期
└─ 不符合 → 回到 2 重新定位
第 3 步的「两种方法对上」很重要:网格给你绝对位置,裁剪放大给你细节确认。两者一致时基本不会点错。
五、定位技巧
网格读数(找大致位置)
python3 vision.py grid wxgrid 50
在截图上画出每 50px 的红色竖线、蓝色横线并标注刻度。直接读刻度,不靠目测。
裁剪放大(确认细节 + 精确定位)
python3 vision.py crop 0 275 70 335 icon_check 6
放大 6 倍后能看清图标形状、文字内容。输出图左上角标注了裁剪原点(如 origin=(0,275) x6),在放大图里量到的偏移除以倍数、加上原点,就是绝对坐标。
这条标注是刻意设计的:没有它,在放大图里读数极易把原点搞错。
判断元素身份
图标类元素靠形状认(光圈=朋友圈、相机=发表、铃铛=通知),不靠位置猜。文字类直接放大读。
六、已知陷阱(都是实测踩过的)
1. 中文输入法会吃掉组合键
pyautogui.hotkey('command', 'v') 在拼音输入法下会退化成裸字母 v,弹出一个拼音候选框,文字根本没粘进去。
解法:用 AppleScript 发组合键。vision.py 的 type 命令在 macOS 上已默认走这条路:
osascript -e 'tell application "System Events" to keystroke "v" using command down'
自己写脚本时也要注意这一点。
2. 读图命令会读到旧截图
这是最隐蔽的一类错误:图本身没错,错的是它描述的那个瞬间已经过去了。你会对着一个不存在的界面做判断,然后点空,而且毫无察觉。
实测踩过两次:click → type → crop 这样连写时,中间没有 shot,crop 读到的是动作之前的 cur.png——于是明明输入成功了,看到的却是「占位文字还在」,差点重输一遍。
vision.py 的处理:crop / grid 每次自动重新截图。宁可多截一张(几十毫秒),也不要看错。自己写脚本时照做。
3. 弹窗会挡住目标
点了一个按钮后可能弹出对话框、文件选择器、权限提示。动作后一定截图,否则下一步会点在弹窗上。
按 esc 通常能关掉误弹的框。
4. 输入前先确认焦点
点击输入框后,最好截图确认光标进去了(占位文字消失、出现光标),再输入。否则文字可能跑到搜索框之类的别处。
5. 滚动前先把鼠标移进目标区域
滚轮事件发给指针当前所在的那个视图。指针停在别处(比如左侧聊天列表),你想滚朋友圈流,滚的是聊天列表——而且界面上看起来"没反应",很容易误判成"已经到底了"。
vision.py scroll N X Y 会先移动鼠标再滚,就是为这个。
另外滚动方向要实测,别照搬直觉:本机实测 pyautogui.scroll(正数) 是让内容往上走(即翻到更旧的位置),和"正数向上"的字面意思相反。滚错方向时,你会以为已经到顶了,其实是在往下翻。
判断是否到顶:滚一大段后再反着滚回来,如果位置没变,那就是真到边界了。
6. 别在用户的真实账号里乱试
操作微信、邮箱、支付这类真实应用时,先想清楚这一步有没有副作用。
七、安全边界
以下动作先跟用户确认再执行:
- 对外发布:发朋友圈、发消息、发邮件、提交表单
- 破坏性:删除、覆盖、清空、退出未保存的编辑
- 花钱:下单、支付、订阅
- 不可逆:任何做了就回不去的操作
确认时把「将要发生什么」说清楚,而不是只问「可以吗」。
一个实用的做法:把流程走到最后一步停住,让用户看到完整状态(文案已填、设置已选),再决定要不要按下那个不可逆的按钮。
八、完整案例:微信朋友圈发布纯文字
这个案例贯穿了上面所有原则,可作模板参考。
⚠️ 下面括号里的坐标是某一次实测值,仅供理解流程,不要照抄——每次都要重新定位。
| 步骤 | 动作 | 定位方式 |
|---|---|---|
| 1 | activate 微信 |
按进程名激活 |
| 2 | grid 看全貌 |
网格读数找到侧边栏第 5 个图标(光圈形状)= 朋友圈 |
| 3 | crop 放大确认形状 |
交叉验证坐标 (30, 297) |
| 4 | click 进入朋友圈 |
截图确认已进入 |
| 5 | 找发布按钮 | 裁剪右上角,认出三个图标中最右的相机 |
| 6 | longpress 相机图标 |
⚠️ 单击会弹文件框,长按才出纯文字框 |
| 7 | click 输入区 → type "文案" |
截图确认文字进去了 |
| 8 | 点「谁可以看」→ 选「私密」→ 确定 | 截图确认已变成「私密」 |
| 9 | ⏸️ 停住,问用户 | 发布是对外动作,确认后再点「发表」 |
关键教训都在这条流程里:长按 vs 单击、输入后验证、可见范围要显式设置、最后一步停下来问。
附:发图文朋友圈(和纯文字的关键差别)
同一个相机图标,要图就单击,纯文字就长按:
| 想要 | 对相机图标 | 后果 |
|---|---|---|
| 图文 | 单击 | 弹出系统文件选择器,选完图进发布框 |
| 纯文字 | 长按 | 直接进发布框(单击只会弹文件框) |
图文流程比纯文字多两步:
- 文件选择器里进目标目录(左侧「下载」「桌面」等),选中文件 → 点「打开」
- 选完先裁剪确认高亮再点打开,否则可能选中的是相邻文件
- 发布框里多了图片缩略图,其余(输入文案、谁可以看、发表)与纯文字一致
发表之后怎么验证
微信 Mac 版的朋友圈信息流不显示自己发的私密动态——发完回到信息流看不到自己的帖子,这是正常的,别据此判断失败、更别重发。
真正可靠的验证入口:点左下角自己的头像 → 资料卡里的「朋友圈」一行 → 我的朋友圈相册,按「今天 / 昨天 / 更早」分组列出自己的全部动态。刚发的那条会出现在最上面。
(顺带一提:这个「信息流看不到」的现象反过来也能当作旁证——如果一条动态在信息流里也出现了,那它多半不是私密。)
九、和「录制回放」方案的区别
| 录制回放(写死坐标) | 本技能(视觉驱动) | |
|---|---|---|
| 定位时机 | 录制时定一次 | 每次执行时实时定位 |
| 界面变了 | 静默点错 | 看图发现异常,改道 |
| 出错后 | 不知道,继续错 | 截图验证,立即发现 |
| 存什么 | 像素坐标 | 语义特征 + 判断方法 |
| 适合 | 固定不动的界面 | 会变、会弹窗、跨机器的真实场景 |
代价是慢一些、每步要截图。但对真实桌面应用来说,可靠性远比速度重要。