首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >#WorkBuddy# 把自动签到搬到群晖NAS:电脑关机也能每天领积分的完整实录

#WorkBuddy# 把自动签到搬到群晖NAS:电脑关机也能每天领积分的完整实录

原创
作者头像
用户12830706
发布于 2026-10-20 20:42:47
发布于 2026-10-20 20:42:47
1540
举报

把 WorkBuddy 自动签到搬到群晖 NAS:从 WinError 10061 到每天 +100 积分的完整实录

一句话背景

在折腾「让 WorkBuddy 每天自动签到」这件事上,我前后花了两个多小时,中间踩了六个坑——其中一个是系统代理导致的网络假死,一个是把网关 IP 当成 NAS 地址,还有一个是脚本目录假设错误。

这篇文章记录完整的排查过程和最终方案:让 NAS 在你的电脑关机状态下依然每天自动领取 Buddy 加油站积分。

最终成果:电脑可以随便关机,NAS 常开,每天 09:00 自动签到 + 自动派猫猫旅行。

一、先说清楚目标边界:什么叫「电脑不开机也能签到」

刚开始我以为这是个技术问题,后来发现要分两层看。

物理层面的硬限制:电脑彻底关机/拔电时,本地方案无解。机器没有任何计算能力,定时任务连启动载体都没有。这不是配置能绕过的。

实际可达的目标:电脑开机联网,但 WorkBuddy 客户端不必运行。这个是可以做到的——而且进一步,把执行载体搬到常开的 NAS 上,就能做到电脑完全不用开机。

我最终达成了后者。

关键思路是:签到接口的本质是带鉴权的 HTTP 请求,不需要模拟点击客户端 UI。所以只要找到「能自动换取访问凭据的方式」,就能脱离客户端。

二、坑一:WinError 10061 —— 系统代理指向一个死端口

配置好脚本第一次运行,直接报错:

网络错误: [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 地址

脚本在 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 的警告提示 —— home 目录不安全

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 终端

六、坑五和坑六:新手最容易踩的两个终端陷阱

这两个看起来简单,但当时确实卡了不少时间。

陷阱1:以为 SSH 之后还需要「进 NAS 终端」

很多人以为要先在 DSM 里打开某个终端窗口。其实不需要。

SSH 登录成功后,你眼前这个终端窗口就已经是 NAS 终端了——同一个窗口、同一个光标,只是背后的机器换了。

判断方法很简单,看提示符:

提示符

位置

能用 && 吗

PS C:\Users\你的用户名>

Windows 本地

❌ PowerShell 5.1 不支持

yourname@NAS:~$

已连入 NAS

✅ sh 支持

想随时确认,执行:

hostname

# DS720+ → 在 NAS

# MY-PC-... → 还在 Windows

陷阱2:在 PowerShell 里敲 Linux 命令

我第一次远程指导时给了这样的命令:

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 删除。

目录
  • 把 WorkBuddy 自动签到搬到群晖 NAS:从 WinError 10061 到每天 +100 积分的完整实录
    • 一句话背景
    • 一、先说清楚目标边界:什么叫「电脑不开机也能签到」
    • 二、坑一:WinError 10061 —— 系统代理指向一个死端口
    • 三、坑二:把网关地址当成了 NAS 地址
    • 四、坑三:脚本假设了错误的源目录
    • 五、坑四:DSM 的警告提示 —— home 目录不安全
    • 六、坑五和坑六:新手最容易踩的两个终端陷阱
      • 陷阱1:以为 SSH 之后还需要「进 NAS 终端」
      • 陷阱2:在 PowerShell 里敲 Linux 命令
      • 附:中文目录名的定位技巧
    • 七、最终方案
      • 架构
      • 关键设计
      • 部署与验证
      • 实际运行结果
    • 八、几个可能对你有用的判断
    • 最后
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档