# workbuddy-daily **Repository Path**: strange_touchannel/workbuddy-daily ## Basic Information - **Project Name**: workbuddy-daily - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-10 - **Last Updated**: 2026-11-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # WorkBuddy Daily 🐱 每天自动完成两件事:**Buddy 加油站签到** + **派猫猫去旅行**。 > ⏰ **几点跑,你说了算。** 这两个脚本是纯手动触发的,里面**没有任何定时逻辑** —— > 执行时间完全由你在 **WorkBuddy 桌面端的「自动化」里**自己设置 —— 想几点就几点,一天几次也行。 > 仓库不规定、也不附带任何默认时间。 ## 两个任务 | 步骤 | 脚本 | 原理 | 稳定性 | | --- | --- | --- | --- | | ① 加油站签到 | `wb_checkin.js --checkin` | 复用常驻 Edge(专用 profile)里已登录的 www.workbuddy.cn 页面,**同源 fetch** 调官方签到接口;不读、不打印任何 token | 稳定,幂等;登录态由每日运行自己维持 | | ② 派猫猫旅行 | `wb_auto_task.js` | Playwright 驱动 Edge(专用 profile)点击成长中心的「派猫猫旅行」 | v4 起整流程自带最多 3 轮重试,一次运行即吸收 SPA 抖动 | ## 用法 > ### ⭐ 推荐用法:让 WorkBuddy 自己搞定(朋友也适用) > > 不用你记命令、也不用手动配调度。把这段话直接甩给 WorkBuddy: > > > 帮我安装 WorkBuddy Daily:https://gitee.com/strange_touchannel/workbuddy-daily > > 按仓库里的 INSTALL.md 执行。装好后把每日签到(`wb_checkin.js --checkin`) > > 和自动派猫(`wb_auto_task.js`)设为定时自动化任务,每天到点自己跑。 > > 它会替你完成:装依赖 → 拉起登录窗等你扫码 → 建自动化任务。 > **你唯一要自己做的事,是在自动弹出的 Edge 窗口里登录一次**(微信扫码或账号密码,以页面为准)—— > 这是唯一没法代劳的一步,原因见下方「为什么不复用你已有的浏览器登录态」。 > 登录成功后永久有效,日常全自动:到点签到领积分、派猫猫去旅行。 > Edge 重启/关窗导致的会话失效,后台跑一次 `node wb_login_then_run.js`:它会弹出登录窗等你扫码, > 检测到登录成功后**在同一个常驻会话里自动跑完「派猫 → 签到」**,不用再手动补跑 > (只想要登录窗、不跑任务时才用 `node wb_login_once.js --timeout=15`。 > 桌面端令牌自 2026-09-24 起改为加密信封,独立脚本无法解密,「静默续期」路线已废弃)。 > > **时间是建自动化任务时你随口指定的**(脚本本身不带定时),想改直接在桌面端的自动化列表里改, > 或者跟 WorkBuddy 说一句「改成每天早上 9 点」。 > > 详细步骤、故障兜底、给安装者 AI 的纪律都在 [INSTALL.md](INSTALL.md) —— 那是写给帮忙安装的 WorkBuddy 读的。 ### 手动运行 ```bash # ① 签到(--status 只查询不领取,--checkin 才真正领取) node wb_checkin.js --checkin # ② 派猫猫旅行(输出 JSON) node wb_auto_task.js # ③ 看成长任务清单还差什么(只读,无任何写操作) node wb_tasks.js # ④ 网页端做成长任务 + 点真实领奖按钮(默认干跑,加 --apply 才真执行) node wb_start_edge.js && node wb_run_tasks.js --apply # ⑤ 领活跃地图的连登里程碑奖励(默认干跑,加 --apply 才真领) node wb_claim_rewards.js --apply ``` 不想敲命令可以双击(会自动切到脚本目录并调用正确的 node): | 双击 | 作用 | | --- | --- | | `run_login.bat` | 扫码登录兜底 —— 常驻 Edge 被关过(重启 / 手动关窗)导致会话失效时运行;只负责登录并让窗口保持常驻。想让扫码成功后自动把当天两步跑完,改用 `node wb_login_then_run.js` | | `run_cat.bat` | 派猫猫旅行 | | `run_tasks.bat` | 成长任务清单(只读) | | `run_growth.bat` | 成长任务:网页端点真实按钮(会先确保常驻 Edge 在跑) | | `run_rewards.bat` | 活跃地图连登奖励:先干跑,敲 `y` 才真领(会先确保常驻 Edge 在跑) | ### 签到结果判定 - `code=0` 且出现 `credit` / `streak_days` → 领取成功 - `code=10001`(今天已签到)→ 幂等,无需重复,视为成功 - `401/403` 或连续网络失败 → 登录态失效,后台跑 `node wb_login_then_run.js` 扫码,成功后自动补跑两步 ### 派猫结果判定 - `login:true` + `catTravel` 以 `dispatched` 开头 → 派遣成功 - `already-traveling` → 猫还在旅行中,跳过 - `gift-claim-failed` → 礼物没领成功,本轮**不会**继续派遣(看 `giftLog` 定位) - `login:false` → 常驻 Edge 会话失效(被关过 / 重启过 / 会话 cookie 焚毁)→ 后台跑 `node wb_login_then_run.js`,扫码成功后自动在同会话内跑完「派猫 → 签到」 - `attempts` > 1 → 首轮撞上 SPA 瞬时抖动,脚本自己重载重试后成功(`note` 里有逐轮结果),**属正常自愈** - `attempts` = 3 且仍未达成 → 三轮自愈用尽,需人工核对(罕见图) > 脚本 v4 起自带重试,**一天跑一次就够**,不需要再叠一层「稍后补派」的定时任务。 ### 为什么不复用你已有的浏览器登录态? 很多人会问:脚本连我平时用的 Edge(已经登录着 WorkBuddy)不就行了,为什么还要扫码一次? 三条路都实测走不通: - **拷贝 cookie 到独立 profile**:workbuddy.cn 的会话 cookie 受 App-Bound Encryption(ABE)保护, 密钥绑定 Edge 二进制 + 机器 + profile 实例。跨 profile 移植后 ABE 层解密失败被直接丢弃 (实测 0 条可解密)。 - **attach 正在运行的普通 Edge**:CDP 端口必须在 Edge **启动时**通过 `--remote-debugging-port` 开启, 普通 Edge 默认不开;手动重启带端口又慢(1.7GB profile 实测 150s 都绑不上)。 - **attach 桌面端 WorkBuddy**:其 CDP(9222)只暴露 `file://` 渲染页且禁用 `newPage`,无法驱动; 桌面端 accessToken 自 2026-09-24 起改为加密信封,独立进程同样拿不到。 所以架构只能是:脚本拉起自己的专用 Edge profile,在里面**原生登录一次**(扫码或账密), 之后保持常驻、每日运行自己维持会话。扫码是唯一无法代劳的一步,也是一次性的。 ### 成长任务清单(只读) ```bash node wb_tasks.js # 按状态分组 + 算出每个任务"还差多少" node wb_tasks.js --all # 连已领取/未解锁的也列出 node wb_tasks.js --json # 结构化输出,供自动化消费 node wb_tasks.js --raw # 打印原始响应(排查上游字段变化用) ``` 拉一次成长中心的任务清单,告诉你 18 个任务里**哪些能领、哪些还差几次、哪些有截止日期**。 输出分四组:🟢 可领奖 / 🔵 计次进行中(带「还差 N 次」)/ 🟡 无计数待做 / ✅ 已领取(默认折叠)。 **它只有 GET,没有任何写操作。** 这是刻意的边界:进度由服务端按**真实行为**点亮, 所以这个脚本只读取、绝不构造或补报行为事件,也绝不伪造客户端身份标识 —— 即「不编造证据」。 它的合规定位与 `wb_checkin.js --status` 同档:自己的登录态、官方端点、只读不写。 **跑它不会让任何进度前进**;要达标得去客户端里真实操作(派猫那类可交给 `wb_auto_task.js`)。 两个实测口径,写脚本时容易踩: - 上游对「无计数」的一次性任务给的是 `progress: null`,**不是** `target: 0` —— 当成 0 会误判成"还差 0 次"。 - `claimed_button_text` 字段**不可信**:未接受的任务(`accept_status=not_accepted`)也返回 `"已完成"`。 判断是否已领只能看 `accept_status`。 - 只给一部分任务返回 `valid_end`(有效期),脚本会显示「⏳ 有效期至 X(剩 N 天)」。 ### 成长任务:网页端真实点击(wb_run_tasks.js) ```bash node wb_start_edge.js # 先确保常驻 Edge 在跑(冷启动可能 >30s) node wb_run_tasks.js # 干跑:只报告,不点任何东西(默认) node wb_run_tasks.js --apply # 真执行 node wb_run_tasks.js --apply --only=chat_5 # 只做指定任务 node wb_run_tasks.js --claim-only --apply # 只领奖 ``` 用真实浏览器 UI 点击完成「网页端能做」的任务,并点**真实领奖按钮**。它会先把 18 个任务 逐个分类(依据 `jump_url` 协议 + DOM 里的 `dt-remark.taskCode`): | 分类 | 含义 | 本脚本行为 | | --- | --- | --- | | 🟢 可领奖 | 进度已足,只差领 | 点真实领奖按钮并复核 | | 🤖 可网页代做 | 网页端点击即可完成 | 代做 | | 🙋 需客户端/真人 | 跳 `workbuddy://` 指向桌面端专属界面,或必须真人 | **不碰**,列出让你自己点 | 边界(刻意,勿为省事改掉):**只做真实点击**。不发任何行为上报、不伪造设备标识、 不代做需真人的项。 #### 实测踩到的坑(都写进代码了) - **`wb_start_edge.js` 存在的原因**:不是"绕过代理",而是**冷启动慢**。成长中心的常驻 Edge 冷启动可 > 30s,早先内联在派猫脚本里的等待循环只有 30s,于是反复拉起。现在单独成文件、等满 90s。 - **⚠️ 关于 `HTTP_PROXY` 的一处曾用错的归因(2026-09-17 实测修正)**: 本机环境设了 `HTTP_PROXY`(指向一个本地代理端口),曾判定"代理把 `127.0.0.1:9223` 的环回请求 也送进代理 → 拿到 502 → 误判「常驻 Edge 未就绪」"。**这个归因是错的**,实测三种探活方式 全部成功:不清代理 `fetch` → HTTP 200;清掉代理 + `NO_PROXY` → HTTP 200;`net.connect` → 端口在听。 事实是**代理能正确转发环回请求**:9223 在听就回 200,没人听才回 502(直连则是 `ECONNREFUSED`)。 也就是说**502 == 确实没在听**,不是假阴性 —— 真因是 Edge 冷启动还没起来。 仍然保留 TCP 探测,理由是**刻意收敛不确定性**(代理端口每次会话会变、代理可能消失,TCP 少一层); 但**它不是 bug 修复**,其余脚本的旧写法可用,不必为此改动。 **仍然成立的一条**:给 `fetch` 传 `{ proxy: undefined }` **无效** —— undici 读的是环境变量。 - **出网不受清代理影响**:实测保留/清空代理两种情况下请求 `copilot.tencent.com` 都返回 401(= 到达服务器、只是没带 token),所以那个 `__loopbackNoProxy` 块对出网无害。 - **`gs-load-more`(「全部任务」)是分页式**:点一次只多渲染几行,必须循环点到行数不再增长, 否则只能看到 5 行(首屏只渲染"进行中"的那几个)。 - **「领取更多」不是领奖按钮**:点了不弹窗、不发请求、按钮文案不变 —— 它只是个跳转入口 (`Hp_Appearance`)。按 `/领取/` 模糊匹配会把这种跳转钮当领奖点,白点一次还误报成功。 现在用精确文案 `^(收下奖励|领取奖励|领取 N 积分|领取)$` 判定,并把跳转钮显式标注出来。 - **SPA 渲染竞态会伪装成「页面没数据」**:成长中心冷启动渲染出 `.gs-task-row` 需 ~9s。 原先固定 `await sleep(4000)` 会读到 **0 行**,并让 `expandAll` 第一轮就 `break` —— 而且**一条日志都不打**,看起来像页面空了/选择器失效了,极难排查。 现改为轮询等首行出现(`waitRows`),每次 600ms、上限 30s;等不到会明说当前 URL。 **教训**:凡"等 SPA 渲染完再读 DOM",一律用「等元素出现」,不要用固定 sleep。 - **API 与 DOM 会不一致**:`/v2/activity/growth/tasks` 的 `accept_status` 有延迟, 实测 DOM 上已出现可领按钮时 API 仍报 `claimable=0`。**以 DOM 为准**。 - **本机后台进程留不住**:跨 `Bash` 调用启动的 Edge 会被回收,所以 `run_growth.bat` 把「拉起 Edge」和「跑任务」放在同一次调用里。 #### 实测结论(2026-09-17,以 DOM 为准) 18 个成长任务里:**可领奖 0 · 可网页代做 0 · 需客户端/真人 7**(其余 11 已完成)。 也就是说 **没有一个任务能靠网页端代做**。4 个原本归为 `auto` 的 (`chat_5` / `expert_5` / `create_canvas` / `playbook_prompt`)**早已全部领完**, 剩余 7 项不是跳 `workbuddy://` 桌面端专属界面,就是必须真人: | taskCode | 分类 | 为什么本脚本不碰 | | --- | --- | --- | | `Buddy_App` | desktop_only | 客户端「发现应用」里进入任一应用 | | `Buddy_App_QQ` | desktop_only | 客户端进入「企鹅教师助手」应用 | | `Expert_team_use_3` | desktop_only | 客户端召唤 3 次专家团 | | `template_5` | desktop_only | 客户端用 5 个模板(网页端无模板库) | | `Expert_lighthouse` | desktop_only | 客户端体验「腾讯轻量云」专家 | | `Hp_Appearance` | desktop_only | 客户端「设置 → 外观」切主题(网页端「领取更多」只是跳转钮) | | `black_cat` | human_only | 夜间折扣活动,与消费相关 | **所以这个脚本的真正价值是「分类 + 守边界」** —— 明确告诉你哪些不该碰、 哪些该自己点,而不是代做本身。 ### 活跃地图 · 连登奖励(wb_claim_rewards.js) 「9月活跃地图」里有条连登进度条:连登满 7 / 14 / 28 天可各换一档奖励 (入门档 / 进阶档 / 巅峰档)。这个脚本自动点掉还没领的档位。 ```bash node wb_claim_rewards.js # 干跑:报连登状态 + 打开弹窗列出各档位(不点任何东西) node wb_claim_rewards.js --apply # 真领:打开弹窗,点掉所有可领档位 node wb_claim_rewards.js --status # 极简只读:只读连登 DOM,连弹窗都不开 node wb_claim_rewards.js --probe # 点击链路自检:验证能否定位/点到档位按钮(不点) ``` **怎么在「没有可领档位」时确认它真能用?** —— 用 `--probe`。它解析 locator、读 位置 / 尺寸 / 可见性 / `disabled`,**但不点击**,所以即使本月已兑完也能验证点击链路是通的: ``` ── --probe 点击链路自检(不点击)── ✓ 入门档 位置=(720,299) 尺寸=70x32 可见=是 可点=否(locked) 定位正常,但 locked → 点会等到超时(--apply 跳过) ✓ 进阶档 位置=(720,367) 尺寸=70x32 可见=是 可点=否(locked) 定位正常,但 locked → 点会等到超时(--apply 跳过) ✓ 巅峰档 位置=(734,435) 尺寸=56x32 可见=是 可点=否(locked) 定位正常,但 locked → 点会等到超时(--apply 跳过) [--probe] 只验可达性,未点任何按钮。「可点=是」的档位才可能被 --apply 领到。 ``` **怎么读这三行** —— `boundingBox()` 返回 `{x, y, width, height}` 四项:`x`/`y` 是元素左上角 **在视口里的坐标**(位置),`width`/`height` 是元素**自身尺寸**。 ⚠️ **`x`/`y` 会随窗口尺寸与滚动变化,不是稳定常量**;`尺寸`才由文案长度决定、是可比的。 - **`y` 逐行递增 68px**(299 → 367 → 435)→ 三档是**纵向等距的三行**,确实是三个独立元素。 - **`x` 前两档都是 720,巅峰档是 734** → 算 `x + width`:`790 / 790 / 790`,**右边缘完全对齐**。 即这三个按钮是**右对齐**排布,巅峰档因为窄 14px,左边缘便右移 14px。 这条顺带说明**为什么不能用「尺寸相同」判定是同一元素** —— 真正区分元素的是位置。 - **尺寸由文案长度决定**:入门/进阶的文案都是「已兑换」**3 个全角字** → 都是 `70x32`; 巅峰档是「兑换」**2 个字** → `56x32`。差值 14px 正好是一个全角字宽。 所以**尺寸能反推文案**:若两档尺寸不一样,说明读到的按钮文案不同(可用来自检)。 > ⚠️ **「定位成功」不等于「能点」**:这三档当时全部 `locked`(入门/进阶是已兑换 → 按钮 disabled; > 巅峰是未解锁)。Playwright 对 `disabled` 元素的 `click()` **不是立即失败,而是等到超时**, > 所以 `--apply` 在点之前必须再核一次锁态,否则会把整个循环每档卡 8 秒并误报「点击失败」。 实测输出(2026-09-17): ``` ── 活跃地图 · 连登概览 ── 标题 : 9月活跃地图 连登天数 : 当前连续登录 17 天 (进度条 60.7143%) 下一档 : 连登 28 天获得奖励 本月已兑 : 2 次 · 补登卡: ×4 里程碑 : 7天 已达成 · 14天 已达成 · 28天 未达成 ── 选择奖励 · 档位状态 ── 入门档 已兑换 [已兑换] +2 能量 · +1 张补登卡 · +1 次抽奖 进阶档 已兑换 [已兑换] +50 积分 · +3 能量 · +1 张补登卡 · +1 次抽奖 巅峰档 未解锁 [兑换] +150 积分 · +5 能量 · +1 张补登卡 · +1 次抽奖 ``` #### 实测踩到的坑(都写进代码了) - **档位按钮的文案不可信** —— 最关键的一条。巅峰档(28 天)在连登不足时文案**就已经是「兑换」**, 但它 `disabled=true` 且 class 含 `is-locked`。若按文案 `/兑换/` 判定可领,就会去点一个 点不动的按钮并**误报成功**。判定一律看 `disabled` / `is-locked`,文案只用来识别「已兑换」。 (与成长任务的 `claimed_button_text` 是同一类陷阱。) - **连登天数在子元素里**:`.gs-streak-days` 的实际 HTML 是 `当前连续登录 17 天`。 只取直接文本节点会得到「当前连续登录 天」(**数字消失**)→ 必须用 `innerText`。 - **`.gs-section-title` 页面里有多个**:第一个是「任务」区块的标题,直接 `querySelector('.gs-section-title')` 会拿错 → 要用 `.gs-streak-row` 往上 `closest('section')` 锚定。 - **里程碑状态有机器可读标记**:`.gs-streak-dot.is-reached`,不用去解析「7天/14天」文案。 - **只读接口「时通时不通」,不是"坏"**:`GET /activity/growth/streak` 与 `/activity/growth/redeem/summary` 在页面内 `fetch` 曾实测 `TypeError: Failed to fetch` (同页 `heatmap` 却 200 OK),后来**同一台机器、同一登录态又都返回 200**。 所以脚本**以 DOM 为准**,接口只当可选佐证、失败不致命。 两者口径一致时能交叉验证:实测 `redeem/summary` 返回 `starter_count / advanced_count / legendary_count = 1 / 1 / 0`, 与 DOM 的「入门/进阶已兑换、巅峰未解锁」吻合。 #### 边界与「补登」 只做**真实 UI 点击**:打开「兑换奖励」→ 点各档「兑换」。点击用 Playwright 的 `click()` (CDP 真实输入事件,`isTrusted=true`),而不是页面内的 `element.click()` 合成事件 —— 对齐 `wb_auto_task.js`。 不向 `/v2/report` 发合成事件、不伪造设备标识,也不直调 `POST /activity/growth/redeem` (口径已摸清,但刻意走点击:服务端记的才是真实发生的领取)。 关于**补登**:日历上「未打卡(可补登)」的格子能用补登卡回填 (端点 `/activity/growth/makeup-cards/use`),把连登天数顶上去、提前解锁高阶档位。 它属于可用功能,但改变的是**打卡记录本身**而非"领取奖励",语义与领奖不同, 所以本脚本**默认不做**。要顺手补登请明确说。 > ⚠️ 该活动规则:**完成一次对话才能被记录** —— 只有真实对话才算当天活跃。 ### 换新活动页时的探法(方法论,下次直接照走) 活动每月会换(`9月活跃地图` 这个标题就是变量),但探法固定。**按顺序来,别跳步**: 1. **先写只读探针,别先写点击。** 新开一个 `_wd_probe_xxx.js`(`.gitignore` 已覆盖 `_wd_probe_*.js`), 复用常驻 Edge 的登录态,用 `page.evaluate` 打印候选锚点与 `innerText`。看清楚再动手。 2. **锚点认「数据属性」,不认文案。** `.gs-task-action` 的 `dt-remark` 里是 JSON (`taskCode` / `title` / `buttonText`);文案会变、会重复、会撒谎(见上面「领取更多」)。 3. **判定认 DOM 状态,不认按钮文字。** `disabled` / `is-locked` / `is-reached` 这类 class 与属性才是机器可读的真相 —— `claimed_button_text` 已有实测反例, 巅峰档未解锁时文案就已经是「兑换」。 4. **接口只当佐证,以 DOM 为准。** 同页 `fetch` 有的通(`heatmap` 200)有的失败 (`streak` / `redeem/summary` → `Failed to fetch`);`accept_status` 还是**延迟聚合**的。 5. **列表面板可能是「累加式分页」。** `gs-load-more`(「全部任务」)点一次只多渲染几行, 必须循环点到行数不再增长,否则只看到首屏 5 行。 6. **等待一律「等元素出现」,不用固定 `sleep`。** 且失败要**明确报错** —— 静默 `break` 会把"没渲染"伪装成"没数据",极难排查(见上面 SPA 竞态那条)。 7. **默认干跑,`--apply` 才真执行。** 再配一个 `--probe`(只验 locator 可达、**不点击**), 这样即使当下没有可操作项,也能证明链路是通的、而不是"从没跑过"的纸面功能。 8. **⚠️ 点击前必须复核锁态。** Playwright 对 `disabled` 元素的 `click()` **不是立即失败, 而是等到超时** —— 不先查 `disabled` / `is-locked`,会把整个循环每档白卡 8 秒并误报「点击失败」。 9. **写操作只走真实 UI 点击**(Playwright `click()` = CDP 真实输入事件,`isTrusted=true`), 不直调写端点、不发合成上报。这条是刻意的取舍,勿为省事改掉。 ### 排障脚本 ```bash node wb_diag_dom.js # dump 派猫按钮 + 弹窗真实 DOM(加 --click 会点一下主按钮观察弹窗) node wb_diag_gift_flow.js # 完整跑一遍领礼物流程,逐步输出耗时与状态变迁 ``` 两者都附着在常驻 Edge(9223)上,不会把常驻窗口杀掉。 ## 复盘 - [领礼物静默失败复盘(2026-09-13)](docs/postmortem-2026-09-13-gift-claim.md) —— 一次「note 说已领取、实际没领到」的伪装成功故障;顺带记了自动化脚本里为什么不该用固定 `sleep`、 为什么不能写空 `catch`。 - [派猫登录态根因与零扫码修复(2026-09-15)](docs/cat-travel-auth-rootcause.md) —— 用 cookie 数据证实「登录态 = 进程在不在」,进而改用令牌续期方式做到重启零扫码 (含实测记录与遗留边界)。 ## 仓库镜像到 Gitee 🔁 [`.github/workflows/sync-to-gitee.yml`](.github/workflows/sync-to-gitee.yml) 把仓库镜像到 Gitee 账号 [`strange_touchannel`](https://gitee.com/strange_touchannel)。push 到 `main` 即自动触发,纯 git 直推,约 20 秒完成。 - **可见性自动跟随 GitHub**:由 [`ensure-gitee-repo.sh`](.github/scripts/ensure-gitee-repo.sh) 读 GitHub 仓库的 `private` 字段再写回 Gitee —— GitHub 上公开,Gitee 上就公开,不靠人工同步; 已存在且已一致时不发任何写请求,避免无谓覆盖描述等其他设置。 注意顺序:Gitee **不允许把空仓库设为公开**(`422 空仓库不支持设置为公开仓库`), 所以脚本拆成 `ensure`(推送前建仓,一律先私有)和 `align`(推送后改可见性)两个阶段。 - 手动 Run workflow 勾选 `include_all_repos`,会**并行**把 workflow 里 `matrix.repo` 名单中的所有仓库 一并镜像(`fail-fast: false`,单个仓库失败不拖累其他)。 - 只用三个凭据,且都是现成的:`GITEE_TOKEN`(建仓 / 改可见性)、`GITEE_SSH_KEY`(推送)、 `GITHUB_TOKEN`(runner 自带,仅用于读一个 `private` 字段)。 **不需要任何 GitHub PAT**,因此不存在「令牌被轮换 → 静默失效」的风险。 - **唯一维护点**:新建仓库后要把名字追加进 `matrix.repo` 名单,不会自动发现。 - **2026-09-16 重写原因**:旧版用 `Yikun/hub-mirror-action`,它必须先调 GitHub API 列出账号下的仓库清单,这需要 `src_token`。该 secret 从未配置 → 请求走匿名 → 撞上 GitHub 匿名限流(60 次/小时,runner 共享出口 IP 几乎必然耗尽)→ 日志里 `Total: 0, successed: 0`(**一个仓库都没同步**),但退出码仍是 0 → Actions 上显示绿色 ✓。 实测同步是「时灵时不灵」的抽奖,且失败时**没有任何提示**。新版全程不碰 GitHub API 的仓库枚举 (git 协议不消耗 API 配额),任何一步失败都会让 job 真失败,不会再出现「绿色空转」。 ## 首次部署 > 走了上面的「推荐用法」的话,这三步 WorkBuddy 已经替你跑完了,这里只是留档说明每一步在做什么。完整版(含故障兜底)见 [INSTALL.md](INSTALL.md)。 1. 装依赖(Playwright core,走本地 playwright-core,无需下载浏览器): ```bash npm i playwright-core ``` 2. 登录一次: ```bash node wb_login_once.js --timeout=15 ``` 在弹出的 Edge 窗口里登录 www.workbuddy.cn。**登完不要关窗口** —— SSO 会话是浏览器会话级的, 进程一关就焚(2026-09-06 实测踩坑),保持常驻才能让每日任务复用。登录态落在 `wb_auto_profile_edge/`。 想「登录 + 验证」一步到位,直接跑 `node wb_login_then_run.js`:扫码成功后它会自动在同一个常驻会话里 依次跑「派猫 → 签到」并打印结果。 > **不需要**关闭 Edge 的 App-Bound Encryption,也**不需要**改动任何浏览器安全策略。 > 脚本附着在自己拉起的、带独立 `--user-data-dir` 的专用 Edge 上,cookie 由 Edge 进程自己解密与维持。 > > 如果早期版本导入过 `disable_edge_abe.reg`(v1 遗留步骤,已确认不再需要): > 在注册表编辑器里把 `HKLM\SOFTWARE\Policies\Microsoft\Edge` 下的 > `ApplicationBoundEncryptionEnabled` 改为 `1`(或删除该值),重启 Edge 即恢复系统默认的凭据保护。 ## 设计要点 / 踩过的坑 - **派猫必须点二次确认框**。主按钮点完会弹「确定派出」,不点这个确认框服务端不会真正派遣 —— 早期版本漏了这步导致"假成功"。脚本现在会点击后复核按钮是否切到「旅行中/倒计时」才算真成功。 - **礼物态要先领再派**。猫回家后按钮变成「领取礼物」,需先领奖励,按钮才会回到「派猫猫旅行」。 - **⚠️ 领取礼物是异步的,别在领取中刷新页面**(2026-09-13 故障根因)。点「领取 N 积分」后按钮会变成「领取中…」并 disabled;旧版只等 1.5s 就 `page.goto` 刷新,把还没完成的领取请求打断 → 礼物其实没领到,刷新后按钮仍是「领取礼物」,而外层 `catch(e){}` 又把异常静默吞掉,最后报 `unknown-state:领取礼物` 还谎称"已领礼物"。现在改成:**全程轮询**(等弹窗、等领取按钮可点、等按钮真正离开「领取」态最多 20s)+ **结果强校验** + **失败重试一轮** + **异常全记进 `res.giftLog`**。 - **SPA 渲染会延迟**。`getTravelBtn` 轮询最多 25s 等按钮出现,否则会误判 `no-travel-button`(成长中心实测约 5–10s 才渲染出按钮)。 - **别用固定 `sleep` 等异步 UI**。凡是"点了之后再操作"的地方(礼物弹窗、派遣确认框)一律轮询等目标出现/可点,页面负载一波动固定等待必然偶发漏步。 - **加油站签到走浏览器同源会话,不读 token**。签到 API 与页面同源(www.workbuddy.cn),常驻 Edge 对该域有有效会话 cookie,页面内同源 fetch 即完成鉴权。早期版本读桌面端登录态文件直连 API,2026-09-24 桌面端把 accessToken 改为加密信封后该路线废弃。 - **Edge 会话失效需重新扫码(v3 之后的现实)**。SSO 会话 cookie 是会话级的,浏览器进程一关就焚。v3 曾用桌面端长期令牌续期做到重启零扫码,但 2026-09-24 起令牌加密(独立脚本无法解密),该路线已废弃;扫码本身由 `wb_login_once.js` 在自己的专用 profile 里原生完成,登完保持常驻。**掉登录后的自动化兜底是 `wb_login_then_run.js`**(2026-10-07 新增):登录等待与任务执行放在同一个 Node 进程里(本机会回收父进程退出后的 detached Edge),扫码成功后立即在同一常驻会话内依次跑「派猫 → 签到」,彻底免掉「扫完码还要手动补跑」那一步。 - **签到收尾不能关浏览器**。`wb_checkin.js` 释放连接时只 `disconnect()` CDP 句柄,不再 `browser.close()` —— 附着模式下 `close()` 会把整个常驻 Edge 关掉,连带焚毁会话级 cookie,还会让后续派猫只能冷启动去撞 `connectOverCDP` 超时(2026-10-07 修复)。同时末尾显式 `process.exit(0)`,避免 `disconnect()` 之后 playwright 句柄占着事件循环、进程挂住不退(2026-10-11 修复)。 - **瞬时失败靠「整流程重试」吸收(v4)**。SPA 渲染抖动/冷启动竞态会让点击、查找按钮偶发失败(实测 2026-09-15、09-16 每天都要人工复跑 1~2 次才派成功)。现在把「进成长中心 → 判定状态 → 执行动作」抽成单轮函数 `runOnce()`,外层最多跑 **3 轮**,每轮重新加载页面,**一次运行即自愈**。这也是 19:25「补派兜底」自动化被删掉的前提——兜底能力已下沉到脚本内部,比「25 分钟后再跑一次」更准、更省积分。回归测试见 `test_round_retry.js`。 ## 安全说明 🔒 - 脚本**不读取、不打印、不落盘任何 accessToken**。签到通过常驻 Edge 内的同源 fetch 完成,凭据完全留在浏览器进程内;日志只输出积分与连续签到天数。 - 只请求 `www.workbuddy.cn` 官方域名(与页面同源),无第三方。 - `wb_auto_profile_edge/` 是浏览器 profile,**含真实登录 cookie,已在 `.gitignore` 中排除,切勿提交**。 - 只读本机登录态文件,不修改 WorkBuddy 本体。 - **仓库内不存在任何"行为上报"**:`wb_tasks.js` 只有 GET;`wb_auto_task.js` 与 `wb_run_tasks.js` 只做真实 UI 点击(点了按钮服务端才记账)。没有一条请求是替你 去"声明"某件你没做过的事,也不伪造设备标识/客户端指纹。这条是刻意的取舍, 勿为省事发合成事件。 ## 环境 - Windows + Edge(macOS / Linux 也能跑,路径会自动探测) - Node 22+(`fetch` 原生可用) - `playwright-core`(`npm i playwright-core`) ### 路径是自动探测的 机器相关的路径 —— Edge 可执行文件、浏览器 profile、playwright-core、node.exe —— 全部由 [`wb_paths.js`](wb_paths.js) 自动探测,**仓库里不写死任何本机路径**,clone 下来就能跑。 优先级(高 → 低): | 级别 | 方式 | 说明 | | --- | --- | --- | | 1 | 环境变量 `WB_EDGE` / `WB_PROFILE` / `WB_PLAYWRIGHT` / `WB_NODE` | 临时改一次用 | | 2 | `local.config.js` | **不提交**(已 gitignore),从 `local.config.example.js` 复制。想永久钉死自己的路径就写这儿 | | 3 | 自动探测 | 按平台枚举常见安装位置,找不到再退到 PATH | 换机器后想确认探测得对不对: ```bash node wb_paths.js # 打印 edge / profile / playwright / node 的最终解析结果 ```