AI签到「一劳永逸」

接上篇 《把 WorkBuddy 每日签到搬上云端》 (2026-08-28)

把多个工具汇总到一起

仓库地址

源码(建议直接 Fork):cnb.cool/holzwege.co...

Fork 之后就得到了你自己的「签到仓库」,下面所有操作都在你自己的仓库里进行。


一、为什么需要这个

1. 为什么不能只在本机跑

签到要挂在「每天某个时刻」上,本机方案有个绕不过去的坑:机器得开着。 笔记本合盖、下班关机、客户端没启动,签到就断了。断了还得手动补, 补的时候又常常已经错过活动重置时间。

搬到云端定时跑,这些都不再是问题。代价是凭据得从本机挪到云端。

2. 为什么必须有「密钥仓库」这个独立仓库

因为凭据不能写在代码仓库里。

.cnb.yml 里没有任何 env 硬编码的 token ------ 凭据只能经由 CNB 的 imports 从另一个私有仓库注入。这样带来三个好处:

  • 签到仓库可以放心开源、随手分享,里面一个密钥都没有
  • 换 token 只动密钥仓库一个文件,不用改代码、不用重跑流水线
  • 泄漏面收敛:即使代码仓库被看到,也拿不到任何可用凭据

3. 为什么凭据只能在本机取一次

三个平台的登录态都没法在云端解密:

平台 本机登录态形态 为什么云端解不了
WorkBuddy AES-256-GCM 信封 解密密钥只驻留客户端进程内存,不落盘
TRAE Work AES-256-CBC 加密块 密钥由客户端代码派生,且设备ID只出现在客户端日志里
Qoder CN DPAPI 保护的 AES 密钥 DPAPI 与本机 Windows 账户强绑定,不可导出

所以架构只能是三段式:

objectivec 复制代码
本机(Windows,一次性)        CNB 密钥仓库(私有)        CNB 云端(每天定时)
  auth/<平台>_auth.py    →     <平台>_secrets.yml    →    runner.py + providers/
  解出明文凭据                  只读、只被 imports 引用      不碰加密,直接用明文

4. 为什么只要一个仓库

最初每个工具各占一个仓库,结果是同一套「取凭据 → 存密钥 → 定时签到」 被抄了三遍,三份代码慢慢长得不一样,命名也各不相同。合并成一个仓库之后:

  • 一套调度:几点跑、跑哪些、跑挂了怎么办,只有一个地方要改
  • 一套命名:见下面「命名约定」,脚本、产物、变量名全部同构
  • 加工具只加文件 :不碰 .cnb.yml
  • 工具自己的「附加动作」也收在 provider 里 :比如 WorkBuddy 签完要派遣 Buddy(喵喵)旅行,就放在 providers/travel.py,由 workbuddy provider 调用, 不往 runner 里塞平台判断(见「五、WorkBuddy 的附加动作」)

二、命名约定

这套约定是强制 的 ------ tests/selftest.py 会检查,写歪了自测直接红。

项 约定 示例
取凭据脚本 auth/<平台>_auth.py auth/qoder_auth.py
一键导出(汇总器) export_auth.py tools/ai-checkin/export_auth.py
取凭据产物 <平台>_secrets.yml qoder_secrets.yml
密钥仓库内文件 .cnb/<平台>-secrets.yml .cnb/qoder-secrets.yml
云端实现 providers/<平台>.py providers/qoder.py
环境变量前缀 <平台大写>_ QODER_TOKEN、QODER_MACHINE_OS
工具名 全小写平台名 workbuddy / trae / qoder

一句话:平台名是唯一的命名基准 ,脚本、文件、变量都从它派生。 取凭据脚本一律叫 *_auth.py,作用一律是「拿本机登录态,生成密钥文件」; 产物一律以平台名打头。

汇总器只有一个:export_auth.py 调各平台的 collect(),把它们合成一份成品。 它是「汇总」不是「取凭据」,所以放在 tools/ai-checkin/ 而不是 auth/ ------ selftest.py 会盯着这条边界,别把平台无关的脚本混进 auth/。


三、操作步骤

① 准备密钥仓库

有一个私有 仓库专门放凭据(本仓库默认指向 holzwege.coz/check-in/secrets)。 没有就新建一个,把 .cnb.yml 里 imports 的地址换成你的。

② 在本机一键生成密钥文件(推荐:一条命令)

在仓库根目录执行:

bash 复制代码
python tools/ai-checkin/export_auth.py

脚本在 tools/ai-checkin/ 下,必须带上这个相对路径 ; 在仓库根目录直接敲 python export_auth.py 会报 can't open file '...\export_auth.py': [Errno 2] No such file or directory。 也可以先 cd tools/ai-checkin 再 python export_auth.py,脚本自己会处理路径。

它会逐个平台取本机登录态,然后合成一份 ai-checkin-secrets.yml ------ 文件与密钥仓库里那份同名同构,全选复制、整份覆盖粘贴即可,不用再手工拼。

yaml 复制代码
# ── AI 工具签到 · CNB 密钥仓库文件(一键生成,整份复制即可)────────
# 已包含工具:workbuddy、trae
# 未包含工具(补上凭据后重新跑一次 export_auth.py 即可):
#   - qoder:未找到 Qoder CN 登录态文件,请先在客户端登录。

allow_slugs:
  - "<你的组织>/<签到仓库>"
allow_events:
  - "crontab: 2 0 * * *"
  - "crontab: 2 10 * * *"
  - web_trigger

# ── workbuddy 凭据 ─────────────────────────────────────
WORKBUDDY_TOKEN: "<明文凭据>"
WORKBUDDY_DOMAIN: "www.codebuddy.cn"

# ── trae 凭据 ──────────────────────────────────────────
TRAE_CLOUDIDE_TOKEN: "<明文凭据>"
TRAE_DEVICE_ID: "<纯数字设备ID>"

授权范围(allow_slugs / allow_events)只出现一次 ,从 .cnb.yml 逐字推导 ------ 这正是手工合并最容易写坏的地方(三份产物各带一份,同名 key 互相覆盖)。

前提:对应客户端已启动并登录 。各平台的数据来源不同,所以一台机器上通常 只能导出装了客户端的那几个;取不到的平台自动跳过(文件头会注明原因), 不当失败 ------ runner 对缺凭据的工具本来就是 skip。

常用选项:

bash 复制代码
python tools/ai-checkin/export_auth.py --stdout          # 打到终端,不落盘
python tools/ai-checkin/export_auth.py --copy            # 生成后直接进剪贴板
python tools/ai-checkin/export_auth.py --only qoder      # 只导出指定平台
python tools/ai-checkin/export_auth.py --slug <组织>/<仓库>  # 覆盖 allow_slugs

三个安全约定:授权范围占位符会在粘贴前提醒;覆盖已有产物时若平台变少会 拒绝 (加 --force 才放行),免得一次误跑把凭据越写越少;一个都没取到 就不落盘。

只想单独取某一个平台也可以,生成的是单平台文件:

bash 复制代码
python tools/ai-checkin/auth/workbuddy_auth.py      # → workbuddy_secrets.yml
python tools/ai-checkin/auth/trae_auth.py           # → trae_secrets.yml
python tools/ai-checkin/auth/qoder_auth.py --full   # → qoder_secrets.yml

不想落盘就加 --verify(只校验能不能解出来)或 --stdout。 这条路要自己把几份产物拼成一份,注意 allow_slugs / allow_events 只能留一份。

③ 粘贴进密钥仓库

把 ai-checkin-secrets.yml 整份复制 ,覆盖密钥仓库里的 .cnb/ai-checkin-secrets.yml,提交。

.cnb.yml 里只有一行 imports,指向的正是这个路径:

yaml 复制代码
imports:
  - https://cnb.cool/<密钥仓库>/-/blob/main/.cnb/ai-checkin-secrets.yml

所以文件只有一份,变量按工具分几组 ------ 加工具只是多一组变量,不用动 imports。

手写也行:tools/ai-checkin/secrets.example.yml 是完整的变量清单模板。 但要小心 allow_events 必须与 .cnb.yml 的事件键逐字一致 (含 cron 表达式与空格),只写 crontab 会在 Prepare 阶段报 does not conform to allow_events。用脚本生成就没这个问题。

④ 跑一次验证

在「代码构建」页点「执行」(或推一次代码触发 push),看日志:

ini 复制代码
>>> [workbuddy] WorkBuddy(签到 + 派遣 Buddy 旅行)
    结果: OK · 签到成功(12.3s)
    (detail 里多一段 travel:派猫猫旅行:空闲(可派遣)......)
>>> [trae] TRAE Work(每日签到领积分)
    结果: SKIP · 今日已签到(3.1s)
>>> [qoder] Qoder CN(每日签到领 Credits)
    结果: SKIP · 当前无可领活动(2.4s)
汇总:成功 1 / 跳过 2 / 失败 0

日志末尾会打整段 JSON 摘要(含每个工具的 detail),要看全就往下翻。 WorkBuddy 的 detail.travel 就是派遣结果:available: false 说明旅行接口 没通(签到本身没坏),auto_log 里是「已派出 Buddy 前往【XX】」之类的原话。

没配的工具显示 SKIP 是正常的,不算失败 ------ 想启用就把它的凭据补进密钥仓库。

用「代码构建」页手动跑时,密钥仓库的 allow_events 要加上 web_trigger, 否则 Prepare 阶段失败。脚本生成的产物会自动带上(如果 .cnb.yml 里有这个入口)。

⑤ 之后就自动跑了

时间(北京) 跑什么 为什么是这个点
每天 00:02 workbuddy + trae 按自然日重置,零点后 2 分钟领最稳
每天 10:02 qoder Qoder 活动每日 10:00 重置,早跑必是空的

四、WorkBuddy 的附加动作:派遣 Buddy(喵喵)

「签到」只是 WorkBuddy 这条线的一半。旧仓库 holzwege.coz/workbuddy-checkin 里还有两件本仓库合并时漏掉的事,这次一并接回来了:

动作 在哪 说明
派遣 Buddy 旅行 providers/travel.py 签到之后「先领后派」闭环
多渠道消息通知 notifier.py runner 跑完每个工具发一条,配置从密钥仓库注入

旅行为什么单独一个文件

因为旅行接口和签到接口不是同一个域名,路径规则也不一样:

bash 复制代码
签到   https://www.codebuddy.cn/v2/billing/meter/...         ← 带 /v2
旅行   https://www.workbuddy.cn/activity/growth/buddy/...    ← 不带 /v2

写错任一项直接 404。把两者塞进一个 provider 里,改签到路由时很容易连累旅行, 所以独立成 travel.py,默认域名写死,只有服务端路由变了才用 WORKBUDDY_TRAVEL_DOMAIN 覆盖。

闭环逻辑(与旧仓库一致):已到达就先领积分,空闲且未达每日上限才派出。 三条安全约束:

  • 达每日上限绝不发派遣请求 ------ 上限外的请求服务端会白记一次
  • 只读接口失败(500 / 返回结构不对)一律降级,一个写请求都不发
  • 派遣任何异常都就地吞掉,不改变签到结论、不影响退出码

想只签到不派遣:密钥仓库里加 WORKBUDDY_TRAVEL: "off"。

通知怎么配

通知配置和凭据一样只能从密钥仓库来(容器里没有本机配置文件):

yaml 复制代码
# 值本身是一份 JSON(与旧仓库本机的 notify_config.json 同构),不是文件路径
AI_CHECKIN_PUSH_CONFIG: '{"channels": {"wecom": {"webhook": "https://..."}}}'
AI_CHECKIN_PUSH_CHANNELS: "wecom"   # 选填,缺省发配置里全部已就绪渠道
AI_CHECKIN_PUSH_ON: "always"        # 选填:always(默认)/ ok / error

支持钉钉、飞书、企业微信、PushPlus、邮件、Bark、Slack、Telegram、通用 webhook... (渠道实现从旧仓库 push_message.py 逐字平移,字段见 notifier.py 的 _SAMPLE_CONFIG)。

不注入就完全不发通知,这属预期,不算失败。通知本身坏了也只吞掉自己 ------ 它和签到是同一条流水线里的两件事,通知挂掉不该让签到结果变红。

本机那份 ~/.workbuddy/scripts/notify_config.json 仍然会被读(方便本机调试), 但云端容器里没有它,所以线上只有注入这一条路。


五、加一个新工具

四步,不用改 .cnb.yml:

  1. providers/ 下加文件,导出 PROVIDER = YourProvider()
  2. providers/__init__.py 的 _MODULES 加一行
  3. auth/ 下加 your_tool_auth.py,从 _common 拿现成骨架, 并导出 collect(full=False) -> (values, summary) ------ 一键导出就是靠它自动带上新工具
  4. 密钥仓库加一组 YOUR_TOOL_* 变量

第 4 步的变量名要登记到 tests/selftest.py 的 test_real_required_env_names ------ 写错变量名线上会永远 skip 且没人发现,这条断言就是防这个的。

工具自己的附加动作(如 WorkBuddy 的派遣)放在它自己的文件或同级模块里, 不要 往 runner.py 里加平台判断 ------ runner 的职责只有「逐个跑、逐个兜错、统一收口」。


六、本地自测

不联网,验证装配是否正确(含命名约定、变量对齐、AES 后端对拍):

bash 复制代码
cd tools/ai-checkin && python3 tests/selftest.py

手动跑一次真实签到(会消耗当天签到机会,注意幂等):

bash 复制代码
cd tools/ai-checkin
python3 runner.py --list            # 看哪些工具已配置
python3 runner.py workbuddy         # 只跑一个
python3 runner.py                   # 跑全部

七、目录结构

bash 复制代码
.
├── .cnb.yml                       # 流水线:00:02 主跑 / 10:02 Qoder / 手动 / 知识库
├── README.md                      # 本文档
└── tools/ai-checkin/
    ├── runner.py                  # 汇总调度器:逐个跑、逐个兜错、统一摘要、发通知
    ├── notifier.py                # 多渠道通知(标准库,配置由环境变量注入)
    ├── export_auth.py             # 一键导出:各平台凭据合成一份密钥文件
    ├── secrets.example.yml        # 密钥仓库文件模板(手写时的变量清单)
    ├── auth/                      # 取凭据:本机跑,生成密钥文件
    │   ├── _common.py             # 公共骨架:授权推导 + YAML 生成/合并 + 脱敏输出
    │   ├── _workbuddy_atrest.py   # WorkBuddy 登录态解密器
    │   ├── workbuddy_auth.py
    │   ├── trae_auth.py
    │   └── qoder_auth.py
    ├── providers/                 # 云端签到:零第三方依赖
    │   ├── __init__.py            # 工具注册表
    │   ├── base.py                # Provider 基类与公共契约
    │   ├── workbuddy.py           # 签到,签完调用 travel
    │   ├── travel.py              # 派遣 Buddy 旅行(域名与签到不同,故独立)
    │   ├── trae.py
    │   └── qoder.py
    └── tests/selftest.py          # 离线回归自检

八、常见问题

现象 原因与处理
skip · 缺少凭据 X 该工具未配置,正常。补密钥仓库变量即可
error + 401 / 1001 Token 失效,重跑 export_auth.py 重新导出并更新密钥仓库
error + 9074(TRAE) 设备ID 不对,必须是客户端 AHA SDK 的纯数字 did,不是 telemetry.devDeviceId
Prepare 阶段 allow_events 报错 密钥里的 allow_events 与 .cnb.yml 事件键不一致,用脚本重新生成
Qoder 永远「无可领活动」 活动每日 10:00 重置;确认跑在 10:02 那条 cron
构建整体失败但只有 Qoder 挂了 runner 口径是「有 error 就非 0」,看摘要定位具体工具
取凭据报「未找到登录态」 对应客户端没登录,或没和脚本在同一用户下运行
export_auth.py 只导出了一部分 正常:没装客户端的平台取不到。文件头会写明缺席的是谁
覆盖产物时被拒(平台变少) 防「越跑越秃」:确认要删就加 --force
WorkBuddy 有签到但没派遣 看 detail.travel.available:false 是旅行接口没通(签到没坏);WORKBUDDY_TRAVEL=off 则是自己关掉的
一次通知都没收到 密钥仓库没注入 AI_CHECKIN_PUSH_CONFIG,属预期;注入了就看 detail.notified
派遣多打了一个请求 / 404 旅行路径不带 /v2、域名默认 www.workbuddy.cn,两者都别跟签到混

凭据多久换一次

平台 有效期 到期信号
WorkBuddy 约 55 天 脚本与流水线都会打印剩余天数
TRAE 随客户端会话 返回 code=1001
Qoder 随客户端登录态 活动列表返回 401

客户端大版本升级后建议重新取一次 ------ 设备指纹和登录态格式都可能变。

相关推荐
HySpark1 小时前
qwen-omini情感识别效果不佳,重新梳理语音情感方案
人工智能·机器学习·语音情感识别
GoldenSpider.AI1 小时前
用 AI 改流程之前先做「四桶」体检:9 次返工里 8 次靠规则就能消掉(附 Python 发布前校验器)
人工智能·python·自动化·ai智能体
一切皆是因缘际会1 小时前
同源世界系统:兼备量子系统与信息几何功能星际通信零延迟
人工智能·ai·系统架构·信息与通信·星际通信·量子系统·信息几何
原子延迟1 小时前
视频还没出画面怎么判断是不是坏了
图像处理·人工智能·计算机视觉·音视频
Rain的Java大神实战圈1 小时前
被问麻了!Function Calling、MCP、Skill 到底啥关系?一张图讲透三层架构,附 Java 生产落地全流程+30个坑
ai编程
代码方舟1 小时前
零信任架构实战:基于天远贷前风险报告构建自动化消费信贷资质预审网关
人工智能·架构·自动化
文谦南宁AI普工1 小时前
写了个 PAP/PACT 授权面审计器:把 `poppy.json` 和 PACT 委托链的检查项跑成一条命令
aigc·ai编程
林伽一1 小时前
免人工审查即上生产的企业占比降至 47%,治理支出转向人工审查|2026年10月11日
人工智能·机器学习