在折腾「让 WorkBuddy 每天自动签到」这件事上,我前后花了两个多小时,中间踩了六个坑——其中一个是系统代理导致的网络假死,一个是把网关 IP 当成 NAS 地址,还有一个是脚本目录假设错误。
这篇文章记录完整的排查过程和最终方案:让 NAS 在你的电脑关机状态下依然每天自动领取 Buddy 加油站积分。
最终成果:电脑可以随便关机,NAS 常开,每天 09:00 自动签到 + 自动派猫猫旅行。
刚开始我以为这是个技术问题,后来发现要分两层看。
物理层面的硬限制:电脑彻底关机/拔电时,本地方案无解。机器没有任何计算能力,定时任务连启动载体都没有。这不是配置能绕过的。
实际可达的目标:电脑开机联网,但 WorkBuddy 客户端不必运行。这个是可以做到的——而且进一步,把执行载体搬到常开的 NAS 上,就能做到电脑完全不用开机。
我最终达成了后者。
关键思路是:签到接口的本质是带鉴权的 HTTP 请求,不需要模拟点击客户端 UI。所以只要找到「能自动换取访问凭据的方式」,就能脱离客户端。
配置好脚本第一次运行,直接报错:
网络错误: [WinError 10061] 由于目标计算机积极拒绝,无法连接
第一反应是网络不通、是 NAS 连不上、是被墙了。但排查后发现完全不是这样。
排查过程:
# DNS 解析正常
nslookup www.workbuddy.cn
# → 114.114.114.114 / 223.5.5.5,解析完全正常
# 端口能连
python -c "import socket; s=socket.socket(); s.settimeout(5); s.connect(('www.workbuddy.cn',443)); print('OK')"
# → OK,TCP 握手成功
既然网络是通的,那问题出在哪?看 Python 的请求配置:
import urllib.request
print(urllib.request.getproxies())
# → {"http": "http://127.0.0.1:7890", "https": "http://127.0.0.1:7890"}

报错原文:状态查询因网络错误失败,detail 里令牌已脱敏、加密状态正常
根因找到了:Windows 系统代理指向 127.0.0.1:7890(典型的 Clash 类代理工具端口),但这个端口当时没有任何进程在监听。而 Python 的 urllib 会自动读取系统代理设置,于是它老老实实地往一个死端口上发请求,必然被拒绝。
验证方式——绕过代理后立刻通了:
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
r = opener.open(request, timeout=20)
# → HTTP 401(未带token,401 恰好证明接口可达)
拿到 401 我反而松了口气——能返回 401 说明网络完全通畅,只是缺凭据。
下面是完整的排查输出:

排查过程:DNS 与 TCP 均正常,问题出在系统代理指向一个没有进程监听的端口
最终修复\\:在脚本启动时先探测代理端口是否存活,不通就自动切直连:
def _patch_proxy_if_dead():
proxies = urllib.request.getproxies()
dead = False
for scheme in ("https", "http"):
p = proxies.get(scheme)
# ...解析出 host:port
sock = socket.socket()
sock.settimeout(3)
try:
sock.connect((host, port)) # 探测端口是否存活
except Exception:
dead = True
break
finally:
sock.close()
if dead:
os.environ["no_proxy"] = "*"
urllib.request.install_opener(
urllib.request.build_opener(urllib.request.ProxyHandler({}))
)
这个设计的好处是:探测可连时不做任何改动,所以对代理正常的机器完全无影响。
这是一个很容易被忽略的坑——如果你也遇到 10061,第一件事应该是 print(urllib.request.getproxies()),而不是去怀疑网络。
脚本在 NAS 上跑通了,我兴冲冲让用户连接,但马上就卡住了:
ssh: connect to host 192.168.1.1 port 22: Connection refused
我一开始给的是 192.168.1.1。查一下本机网络:
ipconfig
# 本机 IPv4 : 192.168.1.20
# 默认网关 : 192.168.1.1← 这是路由器,不是 NAS
192.168.x.1 是典型的网关地址。很多路由器(包括不少家用 NAS 自带的路由功能)都占用 .1。
定位 NAS 的正确方法——两步:
# 第一步:读 ARP 缓存,找出网段内活跃设备
arp -a
# → 192.168.1.50 00-11-32-xx-xx-xx
这一步的关键是看 MAC 地址前缀:00:11:32 是群晖(Synology)的厂商 OUI。这就锁定了目标。
# 第二步:扫端口确认
Test-NetConnection -ComputerName 192.168.1.50 -Port 22 -InformationLevel Quiet
# → True
# 同时 445/80/5000/5001 全部开放—— DSM + SMB + SSH 的标准组合

先用 ipconfig 确认网关不是 NAS,再用 arp -a 配合 MAC 厂商前缀定位真实设备
最终 NAS 地址:192.168.1.50
这里有个额外收获:因为端口 22 确实是开放的,说明用户其实早就开好了 SSH,根本不用再去控制面板改设置。之前的 Connection refused 纯粹是 IP 填错。
通用教训:拿到「Connection refused」先确认 IP 是不是网关。可以用 arp -a + 厂商 OUI 反查,比一个个 IP 试快得多。
NAS 跑通后,我让他们把部署包上传到shared夹。但脚本里写的是:
WORKDIR="$HOME/workbuddy-checkin"
cd "$WORKDIR"
# 然后才去找脚本文件 ← 问题在这
for f in rt_auth.py ...; do
if [ -f "./$f" ]; then # 已经在新目录里找了
逻辑是「先跳到部署目录,再从当前目录找文件」。但文件在shared夹啊,于是必然报「缺失: rt_auth.py」。
修复:用脚本自身所在目录作为源目录,这样在哪个目录调用都能工作。
SRCDIR="$(cd "$(dirname "$0")" && pwd)"
if [ -z "$WORKDIR" ]; then
WORKDIR="$SRCDIR/workbuddy-checkin"
fi
顺带把「令牌需要人工粘贴」也去掉了——改成从部署包自动安装,全程不用手动输入密钥:
if [ -f "$SRCDIR/checkin-rt.json" ]; then
mkdir -p "$WBDIR"
cp "$SRCDIR/checkin-rt.json" "$RTDEST"
chmod 600 "$RTDEST"
fi
在终端粘贴长令牌既容易出错,又会在历史记录里留下明文。能自动化就别让人手动操作。

DSM 对非共享目录存放数据的警告原文:Data should only be stored in shared folders
部署时DSM 弹出了一条容易被忽略的警告:
Warning: Data should only be stored in shared folders.
Data stored elsewhere may be deleted when the system is updated/restarted.
群晖的 home 目录(/var/services/homes/<用户名>/)属于系统管理区域,固件更新时可能被清理。如果把脚本装在这里,今天能跑、某次更新后突然不跑了,排查起来会很崩溃。
解决:把工作目录和令牌都挪到shared夹,并用环境变量让脚本读正确位置。
在 rt_auth.py 里加一个可覆盖的基路径:
WB_BASE = os.environ.get("WORKBUDDY_HOME") or os.path.join(HOME, ".workbuddy")
RT_FILE = os.path.join(WB_BASE, "checkin-rt.json")
这样既保留了本地默认行为(Windows 上照旧用 ~/.workbuddy),又能在 NAS 上显式指向共享目录。实测验证:设了独立 HOME 后,home 目录保持为空,所有文件都落在共享目录。
这种「默认值 + 环境变量覆盖」的设计,兼容性最好。

同一个窗口:提示符为 PS 开头时命令对电脑生效,SSH 登录后同一窗口即 NAS 终端
这两个看起来简单,但当时确实卡了不少时间。
很多人以为要先在 DSM 里打开某个终端窗口。其实不需要。
SSH 登录成功后,你眼前这个终端窗口就已经是 NAS 终端了——同一个窗口、同一个光标,只是背后的机器换了。
判断方法很简单,看提示符:
提示符 | 位置 | 能用 && 吗 |
|---|---|---|
PS C:\Users\你的用户名> | Windows 本地 | ❌ PowerShell 5.1 不支持 |
yourname@NAS:~$ | 已连入 NAS | ✅ sh 支持 |
想随时确认,执行:
hostname
# DS720+ → 在 NAS
# MY-PC-... → 还在 Windows
我第一次远程指导时给了这样的命令:
which python3 && python3 --version
cd /volume1 && cd shared夹 && ls
用户原样粘贴,结果三个全报错。原因是:他压根没登录 NAS,就在 PowerShell 里执行了。于是:
· && 报「不是此版本中的有效语句分隔符」→ PowerShell 5.1 不支持这个语法
· sh 找不到 → Windows 本地没有 sh,而且目录也不对
· Connection refused → 还是那个 IP 问题
教训:远程指导时,第一步必须让对方确认「你现在在哪台机器上」,而不是假设。
在 SSH 里 ls 一个中文目录名,终端常常会显示成 \344\270... 这种八进制转义,看起来像乱码。
更可靠的做法是直接按文件名搜索:
find /volume1 -maxdepth 2 -name "deploy.sh" 2>/dev/null
# → /volume1/shared/deploy.sh
一次就定位到了,比 ls + 肉眼辨认快得多。
群晖 NAS(常开)
└── /volume1/shared/workbuddy-checkin/
├── rt_auth.py 签到主程序
├── workbuddy_checkin.py 网络/旅行/日志
├── push_message.py 推送模块
├── .workbuddy/
│ └── checkin-rt.json长效令牌(权限 600)
└── checkin.log 执行日志
每天 09:00 触发 → 签到 + 派猫猫旅行闭环
1. 长效令牌自续期
不依赖客户端登录态,而是用一次性导出的长效令牌。每次运行自动换取新的访问令牌,并把轮换出的新令牌回写本地,长期不用重新配置。
实测每次运行都返回 rt_rotated: true,说明续期机制正常工作。
2. 幂等设计
当天已签到时接口返回 skip_already_signed,重复执行不会重复发放。这一点很重要——我同时保留了 Windows 计划任务和 NAS 任务,两者并行也安全。
3. 跨平台验证
在真正部署前,我用独立 HOME + 路径隔离的方式模拟了 Linux 环境,确认:
· 只依赖 Python 标准库(json/os/stat/sys/time/urllib),无需装任何第三方包
· 唯一的 os.chmod 有异常保护
· Windows 专属代码(ctypes/PowerShell)都有 sys.platform 守卫,Linux 下自动跳过

一键部署完整输出:四步全通,验证返回 status ok 与积分余额
# 部署(一键,自动完成脚本拷贝 + Python 检测 + 令牌安装 + 验证)
cd /volume1/shared
sh deploy.sh
# 日常查看
cd /volume1/shared/workbuddy-checkin
WORKBUDDY_HOME=$(pwd)/.workbuddy python3 rt_auth.py --check
# 看历史记录
cat checkin.log

定时任务配置:任务名只能用英文/数字/空格,此限制来自官方文档
定时任务(DSM 任务计划 → 用户定义的脚本,或 crontab):
0 9 * * * cd /volume1/shared/workbuddy-checkin && WORKBUDDY_HOME=/volume1/shared/workbuddy-checkin/.workbuddy python3 rt_auth.py --no-notify >> checkin.out 2>&1
{
"status": "ok",
"action": "skip_already_signed",
"balance": 800,
"msg": "状态查询成功,当前积分余额 800 | 派猫猫旅行:已到达,待领取,咖啡馆,可领 7 积分",
"travel": {
"state": "idle",
"auto_log": ["已领取旅行奖励 +7 积分", "今日派遣次数已用完,跳过派遣"]
}
}
首次部署当天就自动补领了旅行奖励 +7 积分。「今日派遣次数已用完」是服务端的每日限额,次日自动恢复,属正常现象。
1. Python 10061 先看代理
这是本文最想传达的一条。看到 10061 就怀疑网络是最直觉的反应,但这次完全是代理配置问题。先 print(urllib.request.getproxies()),再测端口存活,能省很多时间。
2. Connection refused 先怀疑 IP
尤其是地址里有 .1 或 .2 这种。arp -a 配合 MAC 厂商前缀反查,比逐个IP 试快。
3. NAS 上定位文件用 find
中文目录名在 SSH 终端会显示为八进制转义,ls 之后肉眼辨认很痛苦。find /volume1 -name "具体文件名" 一次搞定。
4. 数据尽量放共享文件夹
群晖 home 目录在固件更新时可能被清理。看到 DSM 那个警告就该重视。
5. 能自动化的别让人手动操作
令牌这种长字符串,让用户在终端粘贴既容易错又会留明文。设计成「部署包自带 + 自动安装」,体验好得多。
整个过程最深的体会是:很多「玄学问题」最终都是环境问题。
WinError 10061 不是网络故障,是代理配置;Connection refused 不是防火墙,是记错了 IP;脚本没跑起来不是逻辑错,是假设了一个不存在的目录。
遇到问题时,先把「环境事实」摸清楚——端口通不通、代理指向哪、提示符在哪台机器上——比反复调试逻辑要高效得多。
本文基于真实部署过程整理,涉及版本:DSM 7.x(DS720+)、Python 3.8.15、WorkBuddy 客户端 5.6.2+。具体界面与路径可能因版本略有差异。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。