BuddyCron:给 WorkBuddy 写一本自动签到簿
WorkBuddy 的签到规则很简单:每天点一下,100 积分。积分能换额度,额度能换成实际能用的东西,所以这 100 分不算白给。
问题是我名下不止一个账号。每天开客户端、切账号、点签到,再顺手把成长中心那堆任务领一遍,三五分钟就没了,还经常忘。忘一天连签就断了,断了要拿补登卡补,补登卡也是有限的。
于是就写了 BuddyCron:多账号签到与积分看板,Python 标准库零依赖,带一个本地网页,Docker 一拉就能跑。
先说风险
这篇不是安利。写之前得把话说在前面,因为这东西确实踩在几条线上。
接口是逆向出来的。 没有开放平台、没有 SDK、没有官方文档。所有路由和参数都是从官方网页前端的 JS bundle 里读出来的:assets.workbuddy.cn 的 growthSpace-*.js 和 index-CIb8jkEw.js。官方改一版前端,这些路径就可能全废。
官方规则明确反对这类玩法。 活动规则里写着禁止「恶意注册多个账号」和「使用脚本篡改任务完成数据」。BuddyCron 不做任务数据伪造——有个任务必须在客户端里真的聊一次,服务端根本没有上报进度的接口,脚本帮不上忙——但「多账号 + 脚本自动签到」本身就已经贴着红线走了。规则怎么解释,主动权在官方手里。
参考了别人的实现。 成长中心那七段的执行顺序,最早是 88lin 那个项目摸出来的;签到接口和 wb-checkin 交叉验证过。代码是我重写的,接口契约最后是按官方 bundle 重新核的(那两个参考实现在四个端点上都有对不上的地方),但「先领旅行礼物、再兑换连登、最后派出」这种编排结论,最早不是我想的。如果上游作者认为这构成了不当使用,我接受这个判断,也愿意按对方要求调整或撤下。
仓库还没有 LICENSE。 没有协议文件,默认就是保留所有权利。想二次分发、打包进自己的东西,先来问我。
商标。 WorkBuddy、CodeBuddy 是腾讯的商标,文里只作说明性使用,不表示任何关联、赞助或背书。
代价也很直接:轻则接口改版失效,重则账号被风控、积分清零。只用于管理你自己名下的账号,后果自担。
我把它写出来,是因为实现里有几个坑值得记下来——多账号下的 token 轮换、HTTP 200 不等于成功、定时任务的时间预算。不是推荐你去用。真要用,先自己读一遍官方活动规则。
它能做什么
| 能力 | 说明 |
|---|---|
| 多账号签到 | 每个账号独立 OAuth 授权,各自一条 refresh 链,互不影响 |
| 成长中心 | 旅行领奖与派出、任务接取与领奖、断登补登、连登兑换、抽奖、盲盒 |
| 积分看板 | 余额、按用途分组、包级到期时间、订阅类型 |
| 定时 | 每天 09:00 起跑(随机延迟 0~60 分),09/13/17/21 点补签兜底 |
| 日志 | 按日分文件,每行一个 JSON,可直接 jq,写前脱敏 |
| 部署 | 本地 docker compose,或者 stack.yml + Caddy 自动证书 |
不用 Docker 也行,Python 3.11+ 直接跑:
python3 cli.py web # 起本地网页
python3 cli.py run # 手动跑一轮
python3 cli.py status # 看账号与最近结果
零依赖这件事是有意为之的:整个项目只用标准库,所以没有虚拟环境、没有 pip install、没有依赖冲突。代价是 HTTP、Web 服务、模板都得自己写,但这一层的量并不大——core/ 加 web/server.py 一共四千多行,配 125 个纯逻辑测试,不联网也能跑。
多账号是真正的难点
单账号签到是几十行的事。多账号就不是了,几乎每个地方都要重新想一遍。
token 必须自己管。 单账号工具可以不刷新 token,因为客户端会替你刷。多账号不行——大部分账号根本不在客户端里登录着。而且这个 refresh 是单次轮换:服务端每刷一次就作废旧的那条,两个进程同时刷新,后到的那个必然失败。所以刷新必须走文件锁,进锁之后先回读账号库,发现库里已经变了就直接采用,不再刷第二次。
HTTP 200 不等于成功。 这个接口层的习惯很坑:{"code": 10001} 配着 200 返回是常态。所以每个响应都要过一遍 api_success(status, body) 的双码校验,只看 status 会把一堆失败当成成功记进日志。
写操作绝不重试。 抽奖、领奖、兑换、补登,这些请求如果在服务端已经处理完之后才超时,重试就是重复提交。http.post() 默认 retry=False,只有两个明确幂等的签到接口例外。只读接口才允许重试,而且退避要分级:网络类失败用 5/15/30/60/90 秒(定时任务最容易撞上「刚开机 WiFi 还没连上」),服务端 5xx 只等 3 秒和 10 秒,4xx 一次都不重试。
时间预算得自己切。 systemd 和 cron 会把跑超时的进程直接杀掉,而结果往往最后才落盘,进程一死当天记录就整条没了。所以程序自设一个比任务时限更小的预算:全局 900 秒,每个账号切 120 秒,正好 7 个账号。第 8 个会被跳过并标注「时间预算耗尽」——这是设计,不是 bug。加账号就得同时改 DAILY_BUDGET 和 systemd 的 TimeoutStartSec,后者必须更大。
脱敏要按账号做。 一开始脱敏是全局一个集合,结果 A 的 token 会把 B 汇报里恰好相同的字符串也替换掉。现在 _mask() 只抹本账号自己的 token。
日志里还有个 quiet 判定。 5 个账号 4 个是空跑(已经签过、旅行还在途中),只有 1 个真领到东西。全写进去,日志就被淹没,真出问题时翻不出来。所以空跑按账号判定,不写。
成长中心的七段顺序
这是整个项目里最绕的部分:签到只是第一步,真正花时间的是成长中心那一堆活动。七段顺序是固定的,每一段的位置都有理由:
| # | 动作 | 为什么在这里 |
|---|---|---|
| 1 | 旅行:领已有礼物 | 先领,后面的操作会覆盖旅行记录 |
| 2 | 任务:接取 + 领已完成奖励 | 任务给的能量和抽奖机会,后面马上要用 |
| 3 | 补登:断登补一张卡 | 会改变连签天数,必须早于兑换 |
| 4 | 连登兑换:7 / 14 / 28 天三档 | 要读最新的连签状态 |
| 5 | 抽奖:用兑换来的次数 | 次数来自上一步 |
| 6 | 盲盒:用能量开桌宠 | 会覆盖旅行记录,必须在旅行明确 idle 时 |
| 7 | 最后派出 | 不覆盖在途礼物,也给每天只跑一次的人留了开盲盒的机会 |
顺序一乱就会静默丢奖励,所以它写进了 AGENTS.md 的「不能做」清单。
补登还有个克制的地方:写操作不可逆,一轮最多只补一张,剩下的留给下一轮。选错日期就是白耗一张卡。
能力边界
有几个限制是服务端定的,不是实现偷懒,值得单独写出来,免得看着表格以为功能残了:
- 任务类积分(一次性 1800)拿不全。 18 项任务都要求先完成
first_buddy,而它的条件是「在客户端里真的产生一次对话」。服务端没有上报进度的接口,工具能做的是「接取」和「领奖」两端,中间那段得自己聊。 - 旅行每天只能派一次,早领礼物不增加收益。
- 能量只能换虚拟宠物,对积分零收益,纯装饰。
所以签到一项就占了稳态收益的 86%。这也是为什么这个工具的重点其实是「别忘了签到」,其他都是顺带的。
界面:签到簿
界面我刻意没做成通用仪表盘。底色是纸,字是墨,已签是朱红,连签是墨绿——像一本签到簿,每天盖一个章。
技术上就是 Tailwind 编译成静态 CSS,产物提交进仓库。运行时不需要 Node,也不从 CDN 拉任何东西——这一点是硬要求,因为这个服务手里握着所有账号的 token,页面能执行远端代码就等于把 token 交出去。CI 会对比 web/dist/ 和源码,忘了重编直接失败。
折叠用的是原生 <details> / <summary>,没引框架。账号行用 flex 而不是绝对定位——各算各的 top 永远对不齐,这种坑踩一次就够了。
安全
一个持有多个账号 token 的本地服务,安全上不能含糊:
- 默认只绑
127.0.0.1。绑非回环地址必须设BUDDYCRON_REQUIRE_HTTPS=1,否则拒绝启动——这是硬拦截,不是警告,防止口令和 token 明文跑在公网上。 - 校验
Host头,防 DNS rebinding;拒绝带Origin的跨站请求,POST 强制application/json。两条合起来挡住「恶意网页偷偷 POST 到 127.0.0.1:8787」。 - 登录口令 PBKDF2-HMAC-SHA256 20 万轮,会话是 HMAC 签名的 HttpOnly cookie,连续 5 次失败锁 5 分钟。改密码让所有旧会话失效。
- 数据目录 0700、文件 0600,写入走「临时文件 + rename」。
- 日志写前脱敏,宁可误抹。
漏一条,本地工具就成了别人进你账号的门。
部署
本地用 docker-compose.yml,绑定挂载 ./data,只绑回环地址:
mkdir -p data
docker compose up -d
docker compose logs -f buddycron # 首次会打印登录密码
服务器上用 stack.yml,命名卷 + Caddy 自动申请证书。三个必填项已经在文件里配好,只要改域名和密码:
docker stack deploy -c stack.yml buddycron
不想配证书也行,SSH 隧道就够了:
ssh -L 8787:127.0.0.1:8787 you@server
一个坑必须提:容器调度和 systemd 定时只能二选一。两边都开会抢锁,结果是两边都跑不干净。
最后
写完这个工具,我对「签到」这件事的看法反而更明确了:它设计的初衷就是让你每天打开一次客户端。用脚本绕过去,省的是时间,赌的是账号。
所以再说一次:接口是逆向的,官方规则反对脚本化和多账号,仓库没有 LICENSE,出问题自己承担。 我把它公开,是因为里面的实现细节(单次轮换的 refresh、双码校验、时间预算、每账号隔离)确实是真坑,值得留个记录。