从零搭建和平营地多账号管理 Skill 的实战记录
前言
和平营地是《和平精英》的官方社区工具,可以通过 API 激活账号保持活跃状态。之前有个项目就是专门处理激活账号的,但是一直是手动管理几个账号,token 过期了就更新,激活失败了就查日志,操作繁琐且容易遗漏。
自从开始用 Hermes Agent(基于 OpenClaw 的聊天机器人)后,一直想实现一个场景:我丢一段抓包数据给 Agent,它自动解析、添加账号、更新配置,我确认一下就行。正好 Agent 已经支持 Skill 扩展,于是决定动手做一个 campus Skill。
这篇文章完整记录了从需求分析到最终可用的全过程。
第一步:摸清需求
做 Skill 之前,先梳理一下现有痛点。
手动操作的痛点:
- 添加账号要手动编辑 config.json,格式容易出错
- 更新 token 要先找到对应账号,再改两个地方(config.json + .env)
- 批量操作没有现成工具,10个账号要点10次
- 服务状态全靠
ps aux | grep campus和crontab -l手动查 - 定时任务暂停/恢复要手动注释 crontab 行
理想中的交互:
用户:[发送抓包数据]
Agent:账号添加成功,备注"我的微信",已开启邮件通知
第二步:设计 Skill 结构
参考 pan Skill 和 article Skill 的设计思路,campus Skill 也是 SKILL.md + scripts 目录:
campus/
├── SKILL.md # Skill 说明文档
└── scripts/
├── parse_capture.py # 抓包数据解析
├── update_account.py # 单账号配置更新
├── batch_manage.py # 批量管理
└── service_manage.py # 服务和定时任务管理
设计原则:每个脚本只做一件事,Agent 按需调用,SKILL.md 负责串联工作流。
第三步:抓包解析脚本
这是最核心的功能——用户发一段 URL 参数,脚本自动解析并保存账号。
解析逻辑
def parse_and_save(raw_data, note="未备注", is_send_email=1, owner_email=""):
# 1. 解析URL参数
params = urllib.parse.parse_qs(raw_data, keep_blank_values=True)
collected = {k: v[0] for k, v in params.items() if v}
# 2. 检查必要字段
required = ["openId", "userId", "token", "accessToken"]
login_type = collected.get("loginType", "")
if login_type == "wx":
required.append("refreshToken")
missing = [f for f in required if f not in collected]
if missing:
print(f"❌ 缺少字段: {', '.join(missing)}")
return False
# 3. 构建账号配置
account = {
"note": note,
"login_type": "wx" if login_type == "wx" else "qqconnect",
"openId": collected["openId"],
"userId": collected["userId"],
"token": collected["token"],
"login_accessToken": collected["accessToken"],
...
}
# 4. 保存配置
config = config_manager.load_config()
# 检查重复
for acc in config["accounts"]:
if acc["userId"] == account["userId"]:
print(f"⚠️ 账号 {account['userId']} 已存在")
return False
config["accounts"].append(account)
config_manager.save_config(config)
关键设计决策
判断登录类型:根据 loginType 字段区分微信和 QQ,两种登录方式需要的字段不同:
- 微信:需要
refreshToken - QQ:需要
appId、pf、gameOpenId
重复检测:用 userId 作为唯一标识,避免重复添加同一个账号。
字段验证:解析后立即检查必要字段是否齐全,缺字段直接报错,不会保存不完整的账号配置。
第四步:账号更新脚本
单账号更新用 argparse 子命令,支持修改备注、开关邮件、更新 token 等操作。
双文件同步
更新 token 时需要同时改两个地方:
def _update_env(key, value):
"""更新.env文件中的环境变量"""
env_path = os.path.join(os.path.dirname(__file__), '..', 'config', '.env')
lines = []
found = False
if os.path.exists(env_path):
with open(env_path, 'r', encoding='utf-8') as f:
for line in f:
if line.strip().startswith(f"{key}="):
lines.append(f"{key}={value}\n")
found = True
else:
lines.append(line)
if not found:
lines.append(f"{key}={value}\n")
with open(env_path, 'w', encoding='utf-8') as f:
f.writelines(lines)
为什么用 .env 存 token:config.json 会被 Git 跟踪,token 是敏感信息不能提交。所以把 token 从 config.json 迁移到 .env,config.json 里只保留 userId 等非敏感字段。
env 变量命名规则:CAMPUS_TOKEN_{userId}、CAMPUS_ACCESS_TOKEN_{userId},这样每个账号的 token 独立存储,批量更新时逐个匹配。
第五步:批量管理脚本
支持三种批量操作:
batch_manage.py email on/off # 批量开关邮件
batch_manage.py delete 1 2 3 # 批量删除
batch_manage.py token token "新token" # 批量更新token
batch_manage.py list --type wx # 按类型列出
实现要点:
- 邮件开关和 token 更新支持
--ids参数指定账号,不指定则操作全部 - 删除操作一次性传入多个 userId,用列表推导过滤
- token 更新需要同步修改 config.json 和 .env 两个文件
批量更新 token 的实现
def batch_update_token(token_type, token_value, user_ids=None):
config = config_manager.load_config()
# 读取.env
env_lines = []
if os.path.exists(env_path):
with open(env_path, 'r', encoding='utf-8') as f:
env_lines = f.readlines()
for acc in config["accounts"]:
if user_ids and str(acc["userId"]) not in user_ids:
continue
# 更新config.json
acc[field] = token_value
# 更新.env
env_key = f"CAMPUS_TOKEN_{user_id}"
updated = False
for i, line in enumerate(env_lines):
if line.strip().startswith(f"{env_key}="):
env_lines[i] = f"{env_key}={token_value}\n"
updated = True
break
if not updated:
env_lines.append(f"{env_key}={token_value}\n")
# 保存
config_manager.save_config(config)
with open(env_path, 'w', encoding='utf-8') as f:
f.writelines(env_lines)
设计考量:先读取整个 .env 文件到内存,逐行匹配替换,最后一次性写回。这样避免多次文件 I/O,批量操作时效率更高。
第六步:服务管理脚本
服务管理主要是查看状态、管理定时任务。
状态查看
def get_status():
# 检查主进程
stdout, _, _ = run_cmd("ps aux | grep 'campus' | grep -v grep")
running = bool(stdout)
# 检查定时任务
stdout, _, _ = run_cmd("crontab -l 2>/dev/null | grep campus")
cron_enabled = bool(stdout)
# 检查gateway
stdout, _, _ = run_cmd("pgrep -f hermes-gateway")
gateway_running = bool(stdout)
print(f"主进程: {'✅ 运行中' if running else '❌ 未运行'}")
print(f"定时任务: {'✅ 已启用' if cron_enabled else '❌ 未启用'}")
print(f"Gateway: {'✅ 运行中' if gateway_running else '❌ 未运行'}")
检查三个组件:主进程(激活脚本)、定时任务(crontab)、Gateway(Hermes Agent 网关)。任何一个挂了都能一眼看出来。
定时任务管理
def pause_cron():
"""临时暂停定时任务(注释掉)"""
run_cmd("crontab -l 2>/dev/null | sed 's|^\(.*campus.*main.py.*\)$|#\1|' | crontab -")
def resume_cron():
"""恢复定时任务(取消注释)"""
run_cmd("crontab -l 2>/dev/null | sed 's|^#\(.*campus.*main.py.*\)$|\1|' | crontab -")
暂停/恢复:用 sed 注释/取消注释 crontab 行,比删除再添加更安全,不会丢失原有配置。
下次执行时间
def next_run():
stdout, _, _ = run_cmd("crontab -l 2>/dev/null | grep campus")
match = re.search(r'(\d+)\s+(\d+)\s+\*\s+\*\s+\*', stdout)
if match:
minute, hour = int(match.group(1)), int(match.group(2))
now = datetime.now()
next_time = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
if next_time <= now:
next_time += timedelta(days=1)
print(f"⏰ 下次执行时间: {next_time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" 距今: {next_time - now}")
解析 cron 表达式:用正则提取 hour 和 minute,计算距今多久。如果今天的时间点已过,就加一天。
第七步:SKILL.md 的迭代
SKILL.md 是 Agent 的使用手册,定义了工作流和规则。
初版问题
一开始写得太详细,把每个脚本的所有参数都列了一遍,结果:
- Agent 加载时 Token 消耗大
- Agent 不需要知道所有参数细节,运行时脚本会自己报错提示
- 决策逻辑不清晰,Agent 不知道什么时候该调哪个脚本
优化后的结构
重构后的 SKILL.md 只保留核心:
- Overview — 一句话说明这是什么
- When to Use — 触发条件
- 基础命令 — Agent 最常用的几个命令
- 抓包解析 — 添加新账号的完整流程
- 更新配置 — 单账号修改
- 批量管理 — 批量操作
- 服务管理 — 状态查看和定时任务
- 故障排查 — 常用排查命令
- Common Pitfalls — 易错点
精简原则:参数表格、示例输出这些运行时可见的东西不写在文档里,决策树和规则才是 Agent 真正需要的东西。
踩坑记录
1. config.json 和 .env 的职责分离
最初 token 也放在 config.json 里,但 config.json 会被 Git 跟踪。虽然 .gitignore 可以忽略,但多账号场景下逐个配置太麻烦。
解决方案:config.json 只存非敏感信息(userId、备注、登录类型等),token 统一放 .env,用 CAMPUS_TOKEN_{userId} 命名。
2. 微信和 QQ 的字段差异
微信登录需要 refreshToken,QQ 登录需要 appId、pf、gameOpenId。一开始只处理了 QQ,后来有用户用微信登录才发现字段缺失。
解决方案:根据 loginType 字段动态构建账号配置,两种登录方式分别处理。
3. 批量更新 .env 的效率问题
最初用逐个读取写入的方式更新 .env,10 个账号要读写 10 次文件,效率低且容易出错。
解决方案:先读取整个 .env 到内存列表,逐行匹配替换,最后一次性写回。
4. 定时任务暂停的安全性
直接删除 crontab 行再恢复,万一恢复时忘记写回原来的配置就麻烦了。
解决方案:用 sed 注释/取消注释,配置始终保留在 crontab 里,只是生效与否的区别。
最终功能
目录结构
campus/
├── SKILL.md
└── scripts/
├── parse_capture.py # 89行
├── update_account.py # 157行
├── batch_manage.py # 136行
└── service_manage.py # 107行
核心命令
# 添加账号(解析抓包数据)
python scripts/parse_capture.py 'openId=xxx&userId=123&token=abc&accessToken=def' 我的微信 1 admin@example.com
# 查看账号
campus -l
# 更新配置
python scripts/update_account.py <userId> --show
python scripts/update_account.py <userId> --token "新token"
# 批量操作
python scripts/batch_manage.py email on
python scripts/batch_manage.py delete 1 2 3
# 服务管理
python scripts/service_manage.py status
python scripts/service_manage.py pause
python scripts/service_manage.py next
总结
做这个 Skill 的几个关键经验:
职责分离,各司其职:config.json 存结构化数据,.env 存敏感信息,每个脚本只做一件事,SKILL.md 负责串联流程。这样任何一部分出问题都能独立排查和修复
解析即验证:抓包数据解析后立即检查必要字段,缺字段直接报错,不会保存不完整的配置。比保存后再校验更安全
批量操作要考虑效率:10 个账号逐个读写文件太慢,先读取到内存统一处理再一次性写回,既快又安全
SKILL.md 要精简:参数细节运行时脚本会自己提示,Agent 真正需要的是"什么时候调什么命令"的决策树,不是 API 文档
暂停比删除更安全:定时任务用 sed 注释/取消注释,配置始终保留,避免恢复时丢失原配置
双文件同步要原子化:同时改 config.json 和 .env 时,先改 config.json 再改 .env,中间出错至少还有一个是对的。批量操作时先读取到内存,改完再一起写回
整个 Skill 从想法到可用,大概花了 30 分钟。Claude Code 在这个过程中的角色不只是写代码,更像是一个搭档 — 我提需求,它分析、实现、测试,遇到问题一起排查。比如微信登录字段缺失的问题,是实际测试中发现的,它立刻帮我补上了处理逻辑。这种协作模式比自己从零写要高效得多。
