Original Summary

每天打开 IDE 客户端点一下"签到"领积分,这件事完全可以交给脚本。 最近我同时使用三个 AI 编程工具—— Qoder CN 、 Trae CN 、 WorkBuddy ——它们都提供了签到积分机制,但三种客户端的登录态格式各不相同:有的明文存储,有的自定义加密,有的用了 DPAPI + AES-GCM 双重保护。通过逐一拆解,我找到了一套通用的自动化思路,今天把它整理成文,并在文末附上三个完整可运行的 Python 脚本。 声明:全文基于公开技术分析,接口来自官方客户端同源调用,仅供个人学习研究使用。建议仅在个人电脑上运行,勿将 token 相关代码外传。 一、统一思路:只读登录态,直调官方 API 三个工具的底层逻辑完全一致: 只读本地登录态文件 ↓ 解出 token(或直接使用) ↓ 调官方签到 API ↓ 写日志,不打印敏感信息 关键设计原则: 只读不改 任何登录态文件(避免干扰客户端状态同步) 不打印 token (日志里只留尾 6 字符脱敏) 多账号去重 (同一 token 只签到一次) 幂等处理 (今天已签则跳过,服务端会返回 replayed:true ) 下面分别讲三种格式的差异和坑点。 二、Qoder CN:DPAPI + AES-256-GCM 双重保护 2.1 凭证文件结构 Qoder CN 是 Electron 客户端,登录态拆成两个文件: 文件 路径 内容 保护方式 Local State %APPDATA%\com.qodercn.app.stable\ 32 字节 AES 密钥 DPAPI(用户作用域) auth.v1.dat 同上目录 token / refreshToken / user AES-256-GCM 解密链路: Local State 的 os_crypt.encrypted_key (base64) → base64 decode → 前 5 字节 = "DPAPI" → CryptUnprotectData(需当前 Windows 用户已登录) → 32 字节 AES-256 密钥 → 解 auth.v1.dat(v10 + 12B nonce + CT + 16B tag) → JSON:{token, refreshToken, expiresAt, user} 实测 expiresAt = 2026-10-10T06:49:23Z (约 16 天有效期)。 2.2 抓包定位签到接口 直接抓包这条路走不通:我的 agent 会话 shell 挂在主进程 7544 底下,重启客户端会把自己杀掉;第二实例又会被单实例锁秒退。但客户端自带了一个"抓包器"—— main.log 里每一行都是结构化的请求日志: [INFO] [Campaign] 活动状态请求发出 {"method":"GET","origin":"https://openapi.qoder.com.cn", "path":"/sash/api/v1/me/campaigns","clientType":10,"forceRefresh":true, "requestHeaders":{"Accept":"application/json","Authorization":"[redacted]", "Cosy-ClientType":"10","Cosy-Version":"0.3.4",...}} 用日志 diff 脚本(基线 + 点击窗口对比),毫秒级对齐操作与请求行,三步定位接口。 2.3 签到接口 GET https://openapi.qoder.com.cn/sash/api/v1/me/campaigns POST https://openapi.qoder.com.cn/sash/api/v1/me/campaigns/{campaignId}/claim Header 必需: Authorization: Bearer <token> + Cosy-ClientType: 10 (缺了这个返回 200 但 campaigns 空数组,是个容易踩的坑)。 签到是 幂等 的: {"grantId":"01a0c7f4-79f6-7700-b286-be70d0efb479", "status":"CLAIMED","replayed":true, "benefit":{"kind":"CREDITS","amount":100, "validity":{"mode":"RELATIVE_DAYS","days":30}}} replayed:true 表示服务端不会重复发权益,乱点无害。 2.4 意外发现:签到其实是"自动"的 通过对比 iframe JS(远端 growth-page/activity-iframe ,在 CDN 上)发现: // iframe 自动 claim 所有 CLAIMABLE 的活动 L = useCallback(() => { const e = a.flatMap(c => "viewDetails" !== c.actionType ? [] : (y.current.add(c.id), [P(c.id)]) ); Promise.allSettled(e.map(...)) }, [a, n, P]) 面板一打开就自动把 CLAIMABLE 的条目全部 POST claim 了——所以你看到的"点签到按钮",实际上只是触发了 campaigns 重拉,真签到动作在 Surface 加载时就完成了。 2.5 额度数字接口 面板上展示的 Credits 累计值来自另一组接口(字段名与客户端内部不同,别照 bundle 猜): GET /sash/api/v1/ai-conversations/credits-summary → {totalCredits, peakCredits} GET /sash/api/v1/ai-conversations/seat-activity → {cumulativeActiveDays, currentConsecutiveDays, maxConsecutiveDays, lastActiveDate} 实测字段名是 currentConsecutiveDays ,不是 bundle 里的 currentStreakDays 。 三、Trae CN / TRAE SOLO CN:iCubeAuthInfo 自定义加密 3.1 凭证文件 %APPDATA%\Trae CN\User\globalStorage\storage.json { "iCubeAuthInfo://icube.cloudide": "<base64 密文>" } 3.2 解密算法 客户端自定义格式(非标准 DPAPI): t = base64.b64decode(enc) key = t[6:38] # 32B key sha = hashlib.sha512(key).digest() xor = bytes(a ^ b for a, b in zip(URE, DRE)) # URE/DRE 是硬编码常量 h = hashlib.sha512(sha + xor).digest() aes_key, iv = h[:16], h[16:32] ct = t[38:] plain = unpad(AES.new(aes_key, AES.MODE_CBC, iv).decrypt(ct), 16) return json.loads(plain[64:].decode()) # 去掉 64B HMAC 3.3 签到接口 POST https://api.trae.cn/trae/api/v2/ug/checkin_credits/status POST https://api.trae.cn/trae/api/v2/ug/checkin_credits/claim Headers: Authorization: Cloud-IDE-JWT <token> + x-device-id (从 ahanet/tt_net_config.config 自动读取)+ X-User-Region 。 设备 ID 的读取顺序:配置 → 最近 5 条 main.log 里的 [ICDRS] did: → 兜底常量(一般留空即可)。 四、WorkBuddy 5.6.0+:$wbEncrypted AES-256-GCM 信封 4.1 5.6.0 变更 5.6.0 之前 accessToken 是明文 JSON,5.6.0 起改为: { "auth": { "accessToken": "$wbEncrypted:<base64>", "domain": "...", "expiresAt": 1790783940000 } } 4.2 逆向过程 2026-09-20 实测步骤: 对比新旧 .info 文件,发现 accessToken 字段变成 $wbEncrypted 信封 解包 app.asar,确认字段为 AES-256-GCM 字段级加密 密钥由构建期内嵌的 atRestSecretKey 经 SHA-256 派生 从主进程内存提取密钥,复刻 AAD 字节序列 ( WB-AAD\x00 ) Python 解密后实测领取 100 积分,旧版明文同样兼容 _WB_KEY = hashlib.sha256(_AT_REST_SECRET.encode()).digest() _WB_KEY_ID = hashlib.sha256(_WB_KEY).hexdigest()[:16] AAD = b"WB-AAD\x00" + b"\x01" + _WB_KEY_ID.encode() + nonce cipher = AES.new(_WB_KEY, AES.MODE_GCM, nonce=nonce, mac_len=16) cipher.update(AAD) plain = cipher.decrypt_and_verify(ct, tag) 4.3 签到接口 POST https://copilot.tencent.com/v2/billing/meter/daily-checkin POST https://copilot.tencent.com/v2/billing/meter/checkin-status Headers: Authorization: Bearer <accessToken> + X-User-Id + X-Domain 。 服务端有两个状态码: code: 0 成功, code: 10001 今天已签到请明天再来。 4.4 过期检测 expires_at = session.get("auth", {}).get("expiresAt") or 0 if expires_at / 1000 < time.time(): log(f"[{name}] accessToken 已过期,需启动 WorkBuddy 刷新登录态") 五、部署三步 5.1 安装依赖 pip install requests pycryptodome 5.2 手动验证 在各自客户端登录完成后,先手动跑一次: python checkin_Qoder.py # Qoder CN 用量汇总 python checkin_Qoder.py claim # Qoder CN 签到 python checkin_trae.py # Trae CN 签到 python checkin_workbuddy.py # WorkBuddy 签到 观察同目录 checkin.log ,出现"签到成功"或"今日已签到"即正常。 5.3 注册 Windows 计划任务 schtasks /Create /TN "QoderCN每日签到" /TR "pythonw.exe C:\scripts\checkin_Qoder.py claim" /SC DAILY /ST 10:05 /IT schtasks /Create /TN "TraeCN每日签到" /TR "pythonw.exe C:\scripts\checkin_trae.py" /SC DAILY /ST 08:30 schtasks /Create /TN "WorkBuddy每日签到" /TR "pythonw.exe C:\scripts\checkin_workbuddy.py" /SC DAILY /ST 08:35 三个必记参数 : 参数 作用 为什么必须 /IT 交互式运行 DPAPI 解 Local State 依赖用户登录态,不加则失败 pythonw.exe 无窗口后台 不弹窗打扰工作 /SC DAILY /ST HH:MM 每天指定时间 Qoder CN 建议 10:05(活动窗口 10:00 生效) 六、常见问题排查 Q1. 脚本报"CryptUnprotectData failed" 计划任务里没加 /IT 。DPAPI 主密钥只在用户交互会话可用,选「不管用户是否登录都要运行」会失败。 Q2. campaigns 返回空数组 少了 Cosy-ClientType: 10 header,或者 clientType 不匹配。裸 token 不加 Bearer 会 401 TOKEN_INVALID 。 Q3. 签到后客户端显示未登录 脚本里自行调了 refresh token 接口且没写回文件。refresh token 一次一换 ,脚本改了但不写回 → 客户端下次刷新拿到 400/401 → 代码里直接走 expireSession() 。 正确做法 :脚本只读,不动 refresh。token 快到期(10-10)前打开一次客户端让它自己刷新即可。 Q4. 计划任务跑完没日志 pythonw 不写 stdout,日志必须显式 >> checkin.log 2>&1 重定向,或在脚本里写文件。 七、安全注意事项 三个脚本均 只读 登录态文件,不修改任何文件 token 永远 不打印到日志 (用 …last6chars 脱敏) 接口均来


  • 情报分类:硬件与数码
  • 分类依据:内容涉及硬件、数码产品或通信卡
  • 信息来源:服务器 / LINUX DO - 最新话题
  • 发布时间:2026/9/24 11:43:05