我平时有三件事一直在重复做,说大不大,但都很烦:
第三点最难受——分析完了但看不见,等于白做。
所以我想把它们串成一条流水线:定时触发 → 自动执行 → 结果推送到手机。
整体结构很简单:
定时触发器
↓
① 签到:直连官方接口(不点界面)
↓
② 查询:12306 数据抓取 + 清洗
↓
③ 推送:结果发到微信
↓
失败才告警(成功不打扰)我的初始思路很自然:让 WorkBuddy 每天帮我点一下客户端的签到按钮。
这条路一开始就是死的。 WorkBuddy 的自动化是文字型 Agent,看不见桌面窗口,也没有点击 GUI 的能力。我不该在"怎么点"上耗时间,而该换个角度想问题。
转折点是想明白一件事:签到这个动作,本质就是一次带登录态的 HTTP 请求。 点按钮只是它外面包的一层皮。
想通之后,方案就变成直连接口,好处是压倒性的:
关键一步是拿到正确的域名和登录态。
登录态文件在这里:
%LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\workbuddy-desktop.info里面有三个字段有用:
auth.accessToken —— 身份凭证account.uid —— 用户 IDauth.domain —— 接口域名这里有个大坑。 网上大部分教程写的域名是 copilot.tencent.com,我照着试直接 404。
正确做法是:别信教程,去读客户端自己写的值。 客户端连哪个域名,就跟着用哪个。版本会变,教程会过期,但客户端自己的配置永远是对的。实测是 www.codebuddy.cn。
用到的接口:
用途 | 方法 | 路径 |
|---|---|---|
查询签到状态 | POST |
|
执行签到 | POST |
|
查询积分余额 | POST |
|
获取邀请码 | GET |
|
核心代码(纯标准库,零依赖):
import json, os, urllib.request
path = os.path.expandvars(
r"%LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\workbuddy-desktop.info")
d = json.load(open(path, encoding="utf-8"))
token = d["auth"]["accessToken"]
uid = d["account"]["uid"]
base = "https://" + d["auth"]["domain"]
def post(p, method="POST"):
req = urllib.request.Request(base + p, data=b"{}", method=method)
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + token)
req.add_header("X-User-Id", uid)
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read().decode("utf-8"))
st = post("/v2/billing/meter/checkin-activity-status")
print(st["data"]["streak_days"], st["data"]["total_credits"])返回的字段比想象中丰富:streak_days 是连续天数,total_credits 是本期累计,还有 start_time / end_time 能看出活动周期,action_button 会推送当期活动入口。
同样的思路。12306 需要先建立会话拿 Cookie,再调查询接口:
# 1. 先访问初始化页面拿会话
curl -c ck.txt "https://kyfw.12306.cn/otn/leftTicket/init" -H "User-Agent: Mozilla/5.0"
# 2. 注意:查询接口是 queryG,不是 query
curl -b ck.txt "https://kyfw.12306.cn/otn/leftTicket/queryG?leftTicketDTO.train_date=2026-09-11&leftTicketDTO.from_station=FZS&leftTicketDTO.to_station=XMS&purpose_codes=ADULT"两个坑:
queryG,用 query 会被提示跳转,白折腾半天result 数组是管道符分隔的字符串,不是 JSON 对象,要按 | 切分取字段还有一个业务层面的坑:查"福州"会同时返回福州站和福州南站的车次,必须按始发站字段二次过滤,否则给用户的时间表是错的。我第一版就漏了这个,差点给出错误的出行建议。
个人微信没有开放的消息接口,但可以用 Server酱 这类服务补上:微信扫码关注,拿到 SendKey,之后任何脚本发一个 HTTP 请求,消息就进微信了。
import urllib.request, urllib.parse
def push(title, content):
url = "https://sctapi.ftqq.com/%s.send" % SENDKEY
data = urllib.parse.urlencode({"title": title, "desp": content}).encode()
urllib.request.urlopen(urllib.request.Request(url, data=data), timeout=30)返回 {"code":0} 就是成功。Markdown 格式的表格在微信里也能正常渲染。
坑 | 现象 | 解法 |
|---|---|---|
让 AI 点按钮 | 根本做不到 | 改走 HTTP 接口 |
域名照抄教程 | 404 | 读登录态里的 |
12306 接口名 | 提示跳转 | 用 |
返回非 JSON | 解析报错 | 按 |
同名站点混淆 | 结果混入福州南 | 按始发站二次过滤 |
每天推送成功通知 | 通知变噪音被忽略 | 只在失败时推送 |
最后一条值得展开说。成功不推送,失败才推送。 每天一条"签到成功"很快就会变成背景噪音,人会自动忽略它——而一旦习惯了忽略,真正重要的失败通知也就跟着一起被漏掉了。
事项 | 之前 | 之后 |
|---|---|---|
每日签到 | 靠记忆,平均每周漏 2 天 | 定时自动,连续 10 天未断 |
查车次 | 手动翻页约 5 分钟 | 一条命令,10 秒出结果 |
看分析结果 | 必须回到电脑前 | 手机直接收 |
异常感知 | 发现时已断签好几天 | 失败当天推微信告警 |
accessToken 等同于登录密码。
很多教程喜欢教人"打印前 15 位 + 后 8 位"来调试,说这样不算泄露。这是错的——尾部几位也是真实字符,配合已知的 token 结构,依然构成有效泄露。
我的处理是:脚本从头到尾一个真实字符都不打印,调试输出统一显示"已加载(内容已隐藏)"。写这篇文章时也检查过,token 没有出现在任何要公开的内容里。
另外脚本严格只读登录态文件,不修改、不删除、不切换账号。
全部代码为 Python 标准库实现,不需要安装任何第三方包,Windows / macOS 通用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。