WPS + WorkBuddy 每日签到 (wps-daily-checkin)
Overview
每天自动完成两件事:
- WPS 签到:任务中心·打卡领会员、福利中心·天天领福利,并顺手做可识别的赚积分任务。
登录态通过一次性导出的 Cookie 复用,日常运行完全无头(headless),不需要密码或扫码。 - WorkBuddy 签到:桌面客户端「Buddy 加油站 → 签到领积分」每日礼包领取。
通过 Windows UI Automation(pywinauto)复用当前已登录的桌面会话,不需要额外凭证。
签到结果各自写入报告文件(state/result_wps_<DATE>.json 和 state/result_workbuddy_<DATE>.json),
可由已连接的 Agent Mail 推送摘要邮件。
> WPS 社区签到(bbs.wps.cn)因活动页校验 referer、直链打开报"加载失败"且 selector 易变,
> 默认禁用(enabled=False)。如要启用见 references/wps-endpoints.md。
合规定位
- 本技能仅自动化用户本人账号下已存在的日常签到操作,不涉及破解、逆向、或绕过登录 / 验证码 / 安全验证。
- 所有登录态由用户本人在本地提供(WPS 手动导出 Cookie;WorkBuddy 复用已登录的桌面客户端会话),脚本不获取、不上传任何凭证。
- 仅限个人本地使用,非积分外挂 / 作弊工具;使用者应遵守 WPS / WorkBuddy 相关用户协议。
- 平台改版导致入口失效时,脚本安全跳过并提示,绝不强行操作或试探接口。
⚠️ 已验证的关键坑(维护必读)
WPS 签到
- 任务中心签到页 URL 有两个长得极像的,千万别用错:
- ✅ 正确(标题「任务中心」,含签到日历 + 「点击签到」按钮):
https://personal-act.wps.cn/rubik2/portal/HD2025031821201822/YM2025031821202008
- ❌ 错误(标题「福利中心」,只有会员试用/优惠券,无签到按钮,且含「已领取」文字会误判):
https://personal-act.wps.cn/rubik2/portal/HD2025031721339450/YM2025031721331326 is_present()必须用严格模式:页面有「已连续签到X天」描述文字,若用子串匹配会误判为
已签到而跳过。脚本已改为只匹配 <20 字的按钮类元素(sign_wps.py中is_present)。- 签到按钮文字是「点击签到」(粉色),不是「签到」「今日可领」;点击后变为「已签到」。
WorkBuddy 签到
- 「Buddy 加油站」和「去邀约」在头像下拉菜单中是两个独立菜单项,UIA 树里把这两个
视觉相邻的项的 name 拼接成了一个长串"Buddy 加油站 签到领积分 去邀约 最高得650积分/人",
对应 rect 高约 88px(包含两行)。点中心(行高一半)会命中下方「去邀约」→ 点开了邀请页
而不是签到面板。
- 修复:点击左上 1/4 区域(避开下半部分的去邀约行)。 - 菜单里还有 "关闭 Buddy 加油站" 之类的关闭按钮会被
name_contains("加油站")误匹配
(rect 16x16),点中后会关掉菜单/二级面板,不进入签到流程。
- 修复:搜索时排除 name 中含 "关闭" 的元素。 - UIA 树会包含聊天内容作为 Text 元素(不是 Button)。如果用户对话里出现"今日已领"
"立即领取" 等词,会被脚本误判为签到状态。
- 修复:预检测/领取检测只匹配control_types=("Button",)且max_len<=8的短文本。 - 「今日已领」按钮实际是签到气泡面板**里的按钮(在主窗口左下角),不是
独立弹窗。脚本检测到[Button] '今日已领' at (~78,~893)即为正确命中。 - 脚本对运行前已打开的二级面板是容错的(进入预检测分支直接报告状态),但
会在结束时发 Esc 关闭头像菜单,避免下次运行时菜单已经开着找不到头像。 - 锁屏/无前台桌面时 UIA 完全失效:Electron/Chromium 在系统锁屏(LogonUI.exe 运行、
GetForegroundWindow()==0)时不暴露网页无障碍子树,UIA 树里只剩外层无名 Pane,
头像/菜单读不到、SetForegroundWindow失败、截图也失败。
> 2026-10-09 起触发改由本地看门狗负责(见「签到看门狗」章节);本节描述的是
>sign_workbuddy.py被直接调用时仍保留的守卫/轮询行为,作为兜底。
- 早期守卫 + 弹窗确认 + 内部轮询(2026-08-23 改守卫,2026-08-24 加轮询,2026-08-25 加弹窗,2026-09-21 改锁屏等待 + 单实例闸门):
sign_workbuddy.py在main()开头分级处理——
①今日已签过→直接跳过,且后续唤醒不再启动轮询(不覆盖记录);
②单实例闸门:拿不到命名互斥体 → 说明已有实例在等待/执行,本次静默退出且不写结果文件;
③锁屏 → 不再当场放弃,转静默轮询持续等到解锁(详见坑9),窗口内始终锁屏才记skipped;
④⑤未锁屏(无论空闲还是正在操作) → 统一弹出 Yes/No 确认框问"现在执行签到吗"
(2026-09-29 改:旧逻辑空闲≥60s 静默签、仅操作中弹窗;新逻辑只要本机未锁屏就先弹窗确认,
且解锁瞬间立即弹——不再要求解锁后再空闲 60s):
「是」立即签到;「否」/ 超时(PROMPT_TIMEOUT_SEC=60s)→ 不打扰,当前空闲(多半已离开座位)
即静默补签,否则转静默轮询,每 60s 查一次、最长 40 分钟(POLL_WINDOW_SEC),
解锁即补签;窗口内始终锁屏或始终操作则skipped。
每日弹窗上限MAX_PROMPTS_PER_DAY=50(原 3 太少;实际签到成功后当天自停,上限仅兜底)。
因调度周期有限(每 20 分钟),靠脚本内每 60s 轮询 + 解锁即弹,实现"刚解锁就确认 / 离开约 1 分钟即补签"。
- 用户手动领取也要能识别(2026-08-24 加):sign_workbuddy.py在文件守卫之后、进入轮询之前,增加一次只读 UIA 扫描:
若 WorkBuddy 已打开的签到面板里出现短文本 "今日已领" 按钮,直接保存already并返回,不再空等 30 分钟。
这解决了"用户在客户端手动点掉签到后,脚本仍因结果文件是旧skipped而空轮询"的问题。
若面板已关闭则无法 stealth 识别,仍回到轮询/空闲后再执行的老路径,避免在用户操作时强开菜单抢焦点。
- 历史背景:原 6:00 定时与锁屏冲突(用户为避开白天点选设在 6:00,但机器多半锁屏),
WorkBuddy 签到必然失败。现改为「白天每 20 分钟唤醒一次 + 脚本挑空闲解锁时刻执行」,彻底规避。 - 弹窗必须带超时,否则脚本会被永久挂住(2026-08-25):用
user32.MessageBoxTimeoutW
(未公开但 Win10/11 均导出)实现,超时返回IDTIMEOUT=32000;若该导出不可用则退化为
普通MessageBoxW。标志位必须带MB_SETFOREGROUND|MB_TOPMOST,否则后台进程弹的框
会藏在其它窗口后面看不见。已实测:点「是」返回yes、超时返回timeout、进程退出码 0。
- 防打扰:同一天最多弹MAX_PROMPTS_PER_DAY=3 次(计数持久化在
state/prompt_count_workbuddy_<DATE>.json,跨整点触发共享),超限后只静默轮询。
- 调试:--test-prompt只弹框打印结果、不签到;--no-prompt完全禁用弹窗。
- 注意--dry-run会把当天结果文件覆盖为skipped(调试时留意,不影响正确性,
下次触发会重新检测并记为already)。 - 点击「立即领取」后的成功判定必须包含「今日已领」(2026-09-19 修复):
签到成功后按钮文字由「立即领取」变为「今日已领」。早期sign_workbuddy.py
在两处验证(主流程 +check_any_open_panel)里各自硬编码了
["已领取","领取成功","已签到","连续"],漏掉了「今日已领」,导致实际
已领取成功却只报signed?,用户会误以为签到失败。
- 实测证据(2026-09-19):脚本日志显示已点击「立即领取」,状态为signed?;
但截图04_after_claim.png显示按钮已变「今日已领」、积分 200→300,确认签到成功。
- 修复:抽出常量SIGNED_VERIFY_TEXTS = tuple(ALREADY_CLAIMED_TEXTS) + ("领取成功","连续"),
两处统一复用;并把「点击后 sleep(2.5) + 单次检测」改为
VERIFY_RETRIES=4×VERIFY_INTERVAL_SEC=1.5s轮询重试,应对面板异步渲染。
- 教训:判定文本常量必须集中复用(统一从ALREADY_CLAIMED_TEXTS派生),
不要在验证处二次硬编码,否则改一处漏一处。
- 状态含义:signed=已确认成功;signed?=已点击但未抓到标识(不等于失败,
需看截图或面板实际状态确认);already=今日已领;claimable=可领(dry-run);
skipped=窗口内始终锁屏、或用户始终在操作、或今日已签过。 - 「唤醒那一刻恰好锁屏」曾让本次触发完全作废,且要干等一小时(2026-09-21 修复):
旧逻辑里锁屏是当场放弃——主流程if is_session_locked(): 存 skipped; return,
_poll_for_idle()内也是「检到锁屏 → 立即 return skipped」。两个坏后果叠加:
- 唤醒(当时为每小时一次)时若恰好锁屏,整条链路当场结束、没有任何重试,
必须干等下一次唤醒(最长 60 分钟);
- 用户随后解锁、屏幕亮着、空闲超过 1 分钟,依然毫无动作——因为脚本早就退出了。
- 实测(2026-09-21):12:00 唤醒 → 12:06 判锁屏skipped→ 12:11 用户亮屏 →
12:15 用户追问"亮屏空闲超 1 分钟为何没签"。系统日志中 11:50:51 与 12:11:18 两条
Kernel-Power566 事件正好把 12:06 夹在中间,佐证那段时间机器处于低功耗/息屏。
- 修复(用户拍板 A+B 组合):- A 锁屏不再放弃:主流程锁屏分支改为转
_poll_for_idle();轮询中检到锁屏只打印
提示并continue,解锁后再按空闲条件判断;窗口内一直锁屏才记skipped
(detail 会写明"始终锁屏(未等到解锁)")。--dry-run下仍只报跳过、不真等。 - B 缩短唤醒间隔:automation 由
BYMINUTE=0(每小时)改为BYMINUTE=0,20,40
(每 20 分钟),把"漏掉后的空窗"从最长 60 分钟压到 20 分钟。 - 配套:单实例闸门(B 的必要设施,不可省)。唤醒间隔 20 分钟 < 轮询窗口 30 分钟,
会出现「上一实例还在等待、下一次唤醒又启动」的重叠 → 两个实例同时 attach UIA、
抢焦点、重复点击。_acquire_single_instance_lock()用命名互斥体
WorkBuddyDailyCheckin_Mutex做闸门:拿不到锁的实例静默退出;且必须不写结果文件,
否则后写的skipped会覆盖正在运行的实例稍后写出的真实结果。
互斥体随进程退出由内核自动释放,不会残留僵尸锁。 - 另注:
is_session_locked()判定为「GetForegroundWindow()==0且LogonUI.exe在跑」,
无法区分「息屏」与「锁屏」。若只是显示器关闭而会话未锁,可能被误判 ——
新行为下不再有害(最多白等一个窗口),无需为它单独加判定。 - 教训:「优雅放弃」类分支要问一句"放弃了谁来接手"。锁屏即 return 看着安全,
实则把重试责任全推给了下一个调度周期,而周期又很长 → 变成用户可感知的失败。
- A 锁屏不再放弃:主流程锁屏分支改为转
双签到联合闸门(checkin_gate.py,2026-09-28 新增)
两个签到原本各自独立、互不通信,导致 WorkBuddy 自动化一整天每 20 分钟唤醒、即使用户已手动完成也会反复触发流程/弹窗。新增 scripts/checkin_gate.py 做当日联合闸门:
- 两个自动化(WPS 6:00、WorkBuddy 每 20 分钟)都改为先调用闸门,再决定是否跑签到脚本。
- 闸门读 state/wps-daily-checkin/result_wps_<DATE>.json 与 result_workbuddy_<DATE>.json:
- WPS 完成 = 每个 results[].status ∈ {signed, already};
- WorkBuddy 完成 = status ∈ {signed, already}。
- 两者都完成 → 不运行任何签到脚本;当日首次时打印一次「✅ 今日 WPS + WorkBuddy 双签到均已完成…」确认行并写 daily_checkin_both_done_<DATE>.json;之后静默(0 输出、0 弹窗)。
- 未都完成 → 用 venv python 运行对应签到脚本(sign_wps.py / sign_workbuddy.py),透传 stdout/stderr 与退出码,行为与原脚本完全一致(含锁屏轮询、操作时弹窗确认等)。
- 判定保守:signed? / skipped / error / no_button / unknown 均视为「未完成」,强制继续尝试;脚本幂等,重试安全。
- 效果:一旦当天两签都达成,后续任何触发都只确认一次、不再弹任何签到对话框;次日日期变更,标志自动失效重新判定。
- 推荐配合「自动化自暂停」避免空转会话堆积:WorkBuddy 签到自动化在闸门输出「双签到均已完成」时,调用 automation_update 将自身置为 PAUSED;WPS 6:00 自动化在每次运行开头调用 automation_update 将 WorkBuddy 自动化重新置回 ACTIVE(每日复位开关)。这样双完成后当天不再每 20 分钟空转、侧栏不再新增会话;次日 6:00 由 WPS 任务唤醒复位。风险:若某天 WPS 6:00 任务未运行,WorkBuddy 自动化当天维持 PAUSED(需手动恢复)。
> ⚠️ 2026-10-09 起上面这套「每日复位」已废止:WorkBuddy 自动化整体 PAUSED,WPS 任务的 prompt
> 里也删掉了复位步骤(否则会把停用任务又激活)。闸门新增 --assume-yes(透传给
> sign_workbuddy.py,看门狗已确认时跳过其弹窗与空闲等待),并在 main() 开头做
> 看门狗心跳自愈:心跳文件超过 300s 未刷新就重新拉起 activity_watchdog.py。
工作原理(关键前提)
WPS 部分
WPS 网页登录依赖扫码 / 短信,无法在无头环境自动化,因此采用「一次导出 Cookie + 每天
重放 Cookie」的模式:
1. 用户手动在真实浏览器登录一次(或利用桌面客户端登录态自动确认),export_cookies.py
把 Cookie 存到 ~/.workbuddy/state/wps-daily-checkin/cookies.json。
2. 之后每天 sign_wps.py 用这些 Cookie 无头打开各签到页、关弹窗、点签到、截图、出报告。
3. Cookie 失效时自动恢复:脚本检测到登录态失效后自动打开可见浏览器 → 处理 Chrome 权限弹窗
(「允许」)→ 自动点击「确认登录」(利用桌面客户端已登录态,无需扫码)→ 刷新 Cookie → 继续签到。
用户只需在桌面客户端弹窗中点一次确认即可。
4. 若自动恢复也失败(如桌面客户端未运行),脚本会提示手动操作。
WorkBuddy 部分
WorkBuddy 桌面客户端是 Electron 应用,没有官方 API 暴露给自动化。绕过路径:
1. 不要杀进程、不要重启:脚本用 psutil 找到主浏览器进程 PID,通过
pywinauto (UIA backend) attach 到已运行的窗口。
2. 不需要 Cookie / 凭据:脚本复用的是用户当前已登录的桌面会话,只是模拟点击。
3. 导航流程(与人手操作一致):
- 恢复窗口(如果最小化)
- 点击左下角头像(Image 'Wuu')→ 弹出账户下拉菜单
- 点击菜单中「Buddy 加油站」(左上 1/4) → 弹出签到气泡面板
- 在面板中检测「立即领取」按钮(可领取)→「今日已领」按钮(已领)
4. 避坑细节:见上方 "WorkBuddy 签到" 关键坑 1-5。
资源
scripts/activity_watchdog.py— WorkBuddy 签到主入口:本地常驻看门狗,检测到你在用电脑
就弹窗确认是否签到(2026-10-09 起,取代自动化调度)。scripts/install_watchdog_task.py— 注册/查询/卸载 Windows 计划任务(解锁 + 登录时启动看门狗)。scripts/install_watchdog.py— 写 HKCU Run 键自启 + 立即拉起 + 等心跳自检(登录自启的备份路径)。scripts/_watchdog_launch.py— 拉起看门狗的统一入口(ShellExecute → WMI → breakaway 三级回退)。scripts/checkin_gate.py— 双签到联合闸门:两个签到都完成则当天静默;含看门狗心跳自愈。scripts/export_cookies.py— 一次性导出 WPS 登录 Cookie(弹出真实浏览器,等待用户登录)。scripts/sign_wps.py— 每日 WPS 无头签到主程序,输出state/result_wps_<DATE>.json与.md。scripts/sign_workbuddy.py— WorkBuddy 客户端签到(pywinauto UIA),输出
state/result_workbuddy_<DATE>.json;--assume-yes表示调用方已获用户确认。references/setup.md— 安装、建 venv、装 Playwright、下载 Chromium、首次运行的完整步骤。references/wps-endpoints.md— WPS 各签到页 URL、按钮/已签到文本、Cookie 名称、如何更新。
首次设置(仅需一次)
严格按 references/setup.md 执行:
1. 在 ~/.workbuddy/skills/_venvs/wps-daily-checkin 建隔离 venv,安装 playwright、
psutil、pywinauto、Pillow(pywinauto 的截图需要 PIL),运行
playwright install chromium。
2. 运行 export_cookies.py(会弹浏览器),登录 WPS,生成
~/.workbuddy/state/wps-daily-checkin/cookies.json。
3. 运行 sign_wps.py --no-headless 试跑,核对 state/screenshots/<今天>/ 截图与各目标状态。
4. 若某目标报 no_button,按 references/wps-endpoints.md 更新其 url 与文本候选。
5. WorkBuddy 签到不需要额外设置——只要 WorkBuddy 客户端在跑且当前已登录,
随时可以 sign_workbuddy.py --dry-run 试跑,截图保存在 --screenshot DIR 指定的目录。
> venv 路径约定(Windows,已移到 skill 目录之外以免被打进包):
> %USERPROFILE%\.workbuddy\skills\_venvs\wps-daily-checkin\Scripts\python
每日运行
WPS 侧(自动化,每天 6:00)
WPS 每日签到 (6:00)(automation-1787643199273,ACTIVE):跑
checkin_gate.py --service wps,无头 Playwright,不受锁屏影响。
⚠️ 该任务 prompt 里不得出现"把 WorkBuddy 自动化复位为 ACTIVE"之类步骤(2026-10-09 已移除)——
否则第二天会把已停用的旧任务又激活。
WorkBuddy 侧(本地看门狗,全天)
- 不需要自动化调度。旧任务
automation-1787643185142(每 20 分钟唤醒)已于 2026-10-09
置为 PAUSED 并改名标注"已停用:改由本地看门狗触发"(保留仅为留痕)。 - 触发与执行全部由
activity_watchdog.py负责(常驻由计划任务/登录自启保证,见上文「签到看门狗」)。 - 点「是」后的执行链:
activity_watchdog.py→checkin_gate.py --service workbuddy --assume-yes
→sign_workbuddy.py --assume-yes(跳过弹窗与空闲等待,直接 UIA 打卡)。
报告与后续处理
- WPS 任一目标
error/no_button→ Cookie 失效或活动页改版(见references/wps-endpoints.md)。 - WorkBuddy 状态含义:
signed成功;already今日已领(含你在客户端手动领的);
signed?已点击但未抓到标识(≠失败);skipped正常跳过(锁屏/时段外/已签);
error多为客户端未运行或 UIA 树改版(改sign_workbuddy.py的 selector)。 - Agent Mail 已连接时,把报告摘要发一份;
skipped不发送、不告警。
手动重跑(排查用)
用技能 venv 的 python(venv 在 skill 目录之外):
- WPS:%USERPROFILE%\.workbuddy\skills\_venvs\wps-daily-checkin\Scripts\python scripts/sign_wps.py
- WorkBuddy:... scripts/sign_workbuddy.py(会自动弹窗确认;看门狗已确认时用 --assume-yes)
- 如 venv 不存在,先按 references/setup.md 第一步创建(不要每次重建)。
单目标 / 调试
- 只跑 WPS 任务中心:
... sign_wps.py --target task_center - 显示浏览器调试 WPS:
... sign_wps.py --no-headless - 自定义 WPS state 目录:
... sign_wps.py --state-dir "D:/x/state" - WorkBuddy 检测不领取:
... sign_workbuddy.py --dry-run - WorkBuddy 截图调试:
... sign_workbuddy.py --dry-run --screenshot "D:/x/shots" - WorkBuddy 强制重跑(忽略「今日已完成」早退守卫,调试用):
... sign_workbuddy.py --force - WorkBuddy 只测弹窗(不签到):
... sign_workbuddy.py --test-prompt --prompt-timeout 20
签到看门狗(activity_watchdog.py,2026-10-09 起为 WorkBuddy 签到的主入口)
为什么不再依赖自动化调度
WorkBuddy 签到原先挂在「自动化 → 云端 LLM 会话」上。2026-10-09 实测:当天 5 次触发全部因
429 queue.full(云端额度队列满)失败,本地脚本连启动机会都没有;中间一次侥幸跑起来,又因判
"锁屏"空轮询 40 分钟记 skipped。全天弹窗数 0。结论:这条链不能要。
看门狗是什么
scripts/activity_watchdog.py —— 本地常驻小程序,不联网、不占额度、不依赖 agent 会话:
每 ACTIVE_POLL_SEC(10s) 读一次 GetLastInputInfo,发现"你在用电脑"且今日未签到就弹确认框。
| 项 | 取值 | 说明 |
|---|---|---|
| 采样周期 | 10s | 解锁/开始用电脑后 ≤10s 触发(用户要求"1 分钟内") |
| "在用电脑" | 空闲 < 300s 且未锁屏 | 5 分钟内有键鼠活动即算;看图纸/读记录不动鼠标也算 |
| 弹窗 | 置顶 MessageBox(Yes/No) | 强制提到最前(AttachThreadInput + SetWindowPos TOPMOST,见坑 7);超时 PROMPT_TIMEOUT_SEC(180s) |
| 「是」 | 本地立即执行 | checkin_gate.py --service workbuddy --assume-yes |
| 「否」/超时 | 静默 | REMIND_INTERVAL_SEC(3600s) 后,仍用电脑再问,直到签上 |
| 时段 | 07:00–23:00 | 之外完全不弹 |
| 今日已签 | 当天静默 | 含你在客户端手动领取的情况 |
| 每日上限 | 20 次 | 兜底,正常靠"签上即静默"收敛 |
常驻怎么保证(三层)
- Windows 计划任务(主):
install_watchdog_task.py注册WorkBuddyCheckinWatchdog,
触发器 = 工作站解锁 + 用户登录,IgnoreNew单实例 → 这就是"每次解锁后 1 分钟内弹窗"的系统级保证。 - HKCU Run 键(备):
install_watchdog.py写WorkBuddyCheckinWatchdog(登录自启)。 - 心跳自愈:
checkin_gate.py每次运行都查activity_watchdog.heartbeat,超过 300s 未刷新即重新拉起
(每天 6:00 的 WPS 本地任务必经闸门 = 每天自动巡检一次)。
常用命令
- 看判定(不改状态):
venv\Scripts\python scripts\activity_watchdog.py --status - 立刻弹一次(测试):
... activity_watchdog.py --prompt-now --no-exec - 让已在常驻的实例立刻弹窗:
type nul > %USERPROFILE%\.workbuddy\state\wps-daily-checkin\activity_watchdog.trigger
(看门狗 10s 内消费该文件并弹窗,用于不便直接操作进程时) - 注册/查询/卸载计划任务:
... install_watchdog_task.py [--query|--run|--uninstall|--restart]
(--restart= Stop + 清残留 + Run,让常驻看门狗加载改动后的代码) - 日志:
state/activity_watchdog.log;心跳:state/activity_watchdog.heartbeat
⚠️ 这一轮踩到的坑(务必别再踩)
win32gui.SetTimer不存在(pywin32 未导出,实测 AttributeError)→ 用线程/主循环 sleep。win32gui.PumpMessages()会把同进程的 Python 线程饿死:进程活着、日志正常、心跳只写过一次、
定时线程一次都没跑起来。→ 看门狗改为纯轮询主循环,不用消息循环、不用 WTS 事件。OpenInputDesktop判锁屏会误判:本机返回 NULL,把正常桌面判成"锁屏" → 看门狗一整天一次都不弹。
原则:宁可漏判锁屏,绝不误判锁屏(只认 LogonUI.exe 在跑的强证据)。- 调用方 Job 会清掉子进程:从自动化/终端/沙箱工具里 Popen 或 ShellExecute 拉起的常驻进程,命令结束即消失
(注册表写得好好的,心跳却停住)。只有任务计划服务启动的才真正常驻。 - pywin32 类型库会限制任务计划接口:
Triggers.Create(11)返回基接口ITrigger,没有StateChange
属性(AttributeError)。→ 用 XML 定义 +RegisterTask;XML 里Settings子元素必须按 schema 顺序
(AllowStartOnDemand在MultipleInstancesPolicy之前、ExecutionTimeLimit在Enabled/Hidden之后),
否则报(30,7):AllowDemandStart:之类的解析错。 - 手动签到不写我们的结果文件:看门狗会以为"还没签"而反复问 → 弹窗前先做一次只读 UIA 探测
(_quick_check_already_claimed,节流 30 分钟),面板显示「今日已领」即记already并当天静默。 - 后台进程弹窗不会出现在最前面,只会"闪任务栏"(2026-10-09 用户实测):Windows 前台锁定策略
下,长期后台进程即使给 MessageBox 带MB_SETFOREGROUND|MB_TOPMOST也抢不到前台,用户极易漏看。
正解两步(已实现):① 弹窗前AttachThreadInput到当前前台窗口的线程(自己随即被视为前台活动的一部分,
MessageBox 内部的 SetForegroundWindow 就生效了),弹完立刻 Detach;② 兜底线程在窗口出现后
SetWindowPos(HWND_TOPMOST)+SetForegroundWindow+BringWindowToTop——置顶不依赖激活许可,
即使抢不到焦点也一定盖在其它窗口上面。日志会打印「(已把弹窗强制提到最前)」。
解锁看门狗(unlock_watchdog.py,2026-09-30 起;2026-10-09 已退役)
> ⚠️ 已被 activity_watchdog.py 取代并移出脚本目录,旧文件备份在
> ~/.workbuddy/skills_backup/wps-daily-checkin.bak-20261009/。
> 它只监听 WTS 解锁事件,且执行仍走"20 分钟调度 + 云端会话"那条链(2026-10-09 被 429 全灭)。
> 新看门狗改为键鼠活动采样(同时覆盖解锁事件漏报)+ 每小时提醒 + 纯本地执行。
保留下来的通用经验:
- 单实例必须用 msvcrt 文件锁(state/activity_watchdog.lock、state/checkin_gate_<service>.lock)。
⚠️ 禁止用 psutil 进程扫描判重——venv 的 python.exe 是启动器垫片(shim),spawn 真解释器子进程后
原地等待,扫描会扫到自己父进程的垫片而误判"已有实例"(2026-09-30 根因实锤)。
- 闸门按 service 加锁:同一 service 上一轮还在等用户点弹窗时,新触发直接跳过,避免重复弹窗
(2026-09-30 双弹窗根因)。
- 活着的看门狗在进程列表里出现 2 个是正常的(垫片 + 真身一对)。
- WorkBuddy 禁用弹窗(回到纯静默轮询):... sign_workbuddy.py --no-prompt
- WorkBuddy 改弹窗超时(默认 60s):... sign_workbuddy.py --prompt-timeout 30
- 只跑 WorkBuddy(自动化任务可以并行/串行触发):
... sign_workbuddy.py
容错与边界
- WPS 文本匹配而非 CSS 选择器点按钮,对小幅改版更鲁棒;活动页整体下线需更新
url。 - WPS 脚本对「已签到」状态幂等:重复运行不会重复签到,仅记录
already。 - WPS Cookie 失效自动恢复:检测到失效后自动打开可见浏览器,利用桌面客户端登录态完成
「允许 → 确认登录」三步链路(无需扫码),恢复成功后自动刷新 Cookie 并继续签到。 - WorkBuddy 脚本对「今日已领」幂等;找到「立即领取」则点击并验证。
- WorkBuddy 脚本对已打开的菜单/二级面板容错(直接走预检测分支,不重新打开)。
- WorkBuddy 签到永不在用户未同意时抢焦点:正在操作时先弹窗征询,拒绝或超时即退回后台等待。
state/含登录凭证与截图,不要纳入版本库或分享。- 依赖本机每天开机且 WorkBuddy 自动任务按时触发;长期关机请改用系统任务计划程序跑独立脚本。
- WorkBuddy 签到要求当前 WorkBuddy 客户端在运行且当前用户已登录。
自动化触发前请确保 WorkBuddy 处于登录态(未退出登录)。