用CLI解析lockfile:构建Valorant皮肤商店查询工具

发布时间:2026/9/19 22:43:10
用CLI解析lockfile:构建Valorant皮肤商店查询工具 1. 从“看商店”这个日常痛点说起如果你刷到过 valorant-skin-cli 这个项目大概率和我一样已经受够了每次想确认今日商店皮肤都要先把游戏客户端打开的那种别扭流程。这个工具不复杂却精准解决了一个高频需求不启动游戏不输入账号密码直接在终端里查看瓦洛兰特当日皮肤商店、收藏库和点券余额还能把数据接给脚本做自动化监控。我最开始也不觉得命令行看皮肤有什么必要直到我想在某个指定皮肤出现的瞬间收到提醒才发现网页版社区皮肤展示虽然有但没法给程序用。CLI 的价值正在这里它把“看商店”从一次性的手动操作变成了一个稳定的数据入口脚本可以每隔几分钟请求一次拿到结构化数据后自己决定怎么处理。输出到终端只是最简单的一种消费方式。什么人会需要这个工具我总结是三类一是平时不常登录游戏但会关注商店更新的玩家二是在做 Valorant 周边工具或数据统计的开发者三是喜欢把一切事情都塞进终端、不愿意被 GUI 绑架的人。如果你只是偶尔打开游戏顺手看一眼那网页社区完全够用但如果你想“掌控”皮肤信息而不是被动等待刷新CLI 几乎是现阶段最合适的形态。1.1 这个工具真正解决的三个问题第一个问题是客户端启动成本太高。很多人都有过这种经历晚上突然想起来商店刷新了打开客户端结果赶上版本更新下载几百兆等进度条走完已经没有购买的冲动了。CLI 可以把整个过程压缩到几秒钟而且不占用显卡和内存。第二个问题是官方没有提供直接的“商店订阅”能力。拳头游戏虽然有一个比较完整的数据后台但普通玩家没有公开接口可以查询自己的商店只能登录游戏或者依赖第三方社区网站。第三方网站要手动打开、手动搜索还没法做持续监控用户体验始终隔了一层。第三个问题是自动化入口缺失。如果你想做心愿单监控想在某个皮肤重新上架时立刻收到通知或者想统计过去一年商店到底出现多少次某个皮肤靠人工刷新根本做不到。CLI 把这个缺口补上了一条命令就能返回机器可读的数据剩下的事情全部可以交给脚本。最近 AI 编程工具持续火起来之后CLI 这种交互形式重新回到了大众视野。市面上的 codex cli、trae cli 本质上都在做同一件事把复杂的操作收敛成一条带参数的指令让机器和人都能理解。valorant-skin-cli 也是这个思路的产物它把查商店、查余额、看收藏这些动作全部收进终端里非常适合继续往上层搭各种自动化。1.2 为什么不做 GUI非要坚持命令行做图形界面需要处理窗口布局、跨平台打包、版本更新提醒维护成本很高。做一个网页服务也行但意味着要部署在线服务还要接触更多玩家的账号数据安全责任一下子变大。CLI 的投入产出比是最高的它只需要处理三件事参数解析、HTTP 请求、终端输出就能覆盖绝大多数真实使用场景。命令行工具还有一个天然优势支持管道和组合。我可以直接写valorant-skin-cli store --json | jq .entries[].name提取出所有皮肤名称再交给下一个脚本做过滤或通知。这种能力传统客户端给不了因为 GUI 的交互是用鼠标点的没法轻松形成可编程链条。在 Linux 服务器或者无图形界面的场景下CLI 更是唯一选择。我见过有人把自己账号的本地数据接口接到群晖 NAS 的定时任务里每天凌晨自动抓取商店并保存到数据库。这种玩法离了 CLI 基本没法实现。2. 拆解核心原理数据从哪来怎样保证安全很多人第一次看到这个工具时都会疑惑游戏皮肤数据不是只能在客户端里看吗Riot 客户端为什么会在本地开放接口其实官方客户端为了实现登录态、商品展示、余额查询这些功能内部本来就要跟服务器通信而承载这些通信的本地服务一直存在只是普通玩家没有注意到。2.1 本地 lockfile 决定了整个工具的运行方式Riot 客户端启动后会在本机的 Riot Client 配置目录下生成一个名为 lockfile 的文件。这个文件有一行文本用冒号分隔了客户端名称、进程 PID、端口号、密码和协议。CLI 工具读取这个文件就能拿到本地服务地址以及一套临时认证信息。lockfile 的路径在不同系统上不完全一样。Windows 通常在%LOCALAPPDATA%\Riot Games\Riot Client\Config\lockfilemacOS 通常在~/Library/Application Support/Riot Games/Riot Client/Config/lockfileLinux 下位置不固定更需要程序做多路径探测或手动配置。使用 lockfile 的最大好处是省掉了账号密码管理。工具不需要保存玩家的账号密码也不需要处理两步验证只要当前系统里已经有一个处于登录状态的官方客户端CLI 就可以借用这个身份来请求用户自己的数据。对使用者来说这比存一份明文密码在本地安全得多。2.2 一次完整的数据请求流程整个流程可以拆成五个步骤读取 lockfile解析出端口、密码和协议。构造基础认证请求头用户名固定为 riot密码就是 lockfile 里那串随机字符串。请求本地接口的商店皮肤端点拿到当天的皮肤 ID、价格和剩余时间。对 JSON 返回做字段清洗和规范化。根据用户参数渲染成表格、纯文本或 JSON 输出。这里有一个容易误解的点本地接口监听在 127.0.0.1只有本机进程能访问不会暴露到局域网。数据最终虽然是远端服务返回的但请求链路经过的是官方客户端建立的安全通道。我在代码里不写任何上传逻辑也不允许工具把请求转发到第三方服务器安全边界非常清晰。2.3 为什么不直接调云端的玩家数据服务社区里有人会走完整 OAuth 流程获取 Access Token再直接请求 Riot 官方的区域数据服务这样能拿到更完整的收藏库信息。但这套流程有一个很现实的问题需要处理登录跳转、回调地址、令牌过期和刷新令牌复杂度一下子提升不少。更重要的是频繁触发令牌申请容易引来官方风控轻则请求失败重则可能影响账号安全。我在实际测试中比较过本地接口的路径短、响应稳定更适合作第一版功能的数据源。如果你之后想做非本机部署比如跑在一台没有登录客户端的服务器上再考虑完整的令牌流程不迟。2.4 功能模块怎么拆分我起初想把所有功能一次性做完后来发现还是要克制。第一版我建议只做四个模块商店查询、收藏库查询、余额查询、轮询提醒。商店查询负责展示今日商店的皮肤与价格收藏库查询读取账号已经拥有的皮肤列表余额查询展示当前 VP 点和 Radianite 点数量轮询提醒则周期性地请求商店接口当发现新皮肤出现时在终端输出提醒。这四个模块互相独立底层共用一套 lockfile 读取和 HTTP 请求代码上层各自负责不同的业务逻辑。这样拆的好处是每块功能都能单独测试不会因为一个模块出错把整个工具拖垮。3. 手把手把 CLI 搭起来下面这一段是我实际搭建项目时走过的完整路径。我选用 Node.js 来实现原因是跨平台支持好、异步编程顺手、终端生态成熟。你用 Python 或者 Go 也能达到差不多的效果但 Node 在命令行工具这个细分领域里确实很舒服。3.1 初始化项目与依赖选择先建一个新目录并初始化 npm 项目mkdir valorant-skin-cli cd valorant-skin-cli npm init -y npm install commander node-fetch chalk cli-table3commander 用来解析命令行参数node-fetch 发送 HTTP 请求chalk 给终端输出配色cli-table3 把数据渲染成表格。如果你本机 Node 版本在 18 以上内置了 fetchnode-fetch 可以不装但保留它能让老版本 Node 也能运行兼容性更好。在 package.json 里加上 bin 配置把命令注册到全局bin: { valorant-skin-cli: ./bin/index.js }然后创建 bin/index.js 作为入口文件文件顶部必须有#!/usr/bin/env node这一行否则系统不知道怎么解释这个文件。做完这些之后执行npm link终端里就能直接使用valorant-skin-cli命令了。3.2 读取 lockfile 的完整代码lockfile 读取是整个工具的地基代码写得好不好直接影响后续功能稳定性。下面是我的实现const fs require(fs); const path require(path); function getLockfile() { const candidates [ path.join(process.env.LOCALAPPDATA || , Riot Games, Riot Client, Config, lockfile), path.join(process.env.HOME || , Library, Application Support, Riot Games, Riot Client, Config, lockfile), path.join(process.env.HOME || , .local, share, riotclient, Config, lockfile) ]; for (const p of candidates) { if (fs.existsSync(p)) { const parts fs.readFileSync(p, utf8).trim().split(:); return { name: parts[0], pid: Number(parts[1]), port: Number(parts[2]), password: parts[3], protocol: parts[4], file: p }; } } throw new Error(找不到 lockfile请确认 Riot 客户端已启动并已登录); } module.exports { getLockfile };有几个细节很容易踩坑。第一文件读取后必须做 trim因为末尾可能有换行符不处理的话 split 出来的最后一个字段会带\n。第二pid 和 port 建议转成数字类型后续比较和拼接 URL 时不容易出类型问题。第三候选路径数组是顺序尝试的如果你用了绿色版或者自定义安装目录可能需要手动添加路径。3.3 请求商店接口并处理证书问题拿到 lockfile 之后下一步就是请求本地接口。商店数据的请求端点和参数会随客户端版本变化下面的路径来自我长期使用的稳定版本const https require(https); const { getLockfile } require(./lockfile); async function fetchStore() { const lockfile getLockfile(); const baseUrl ${lockfile.protocol}://127.0.0.1:${lockfile.port}; const auth Basic Buffer.from(riot: lockfile.password).toString(base64); const res await fetch(${baseUrl}/valorant-store/v1/skin-offers, { headers: { Authorization: auth }, agent: new https.Agent({ rejectUnauthorized: false }) }); if (!res.ok) { throw new Error(商店接口请求失败: ${res.status} ${res.statusText}); } return res.json(); }这里有一个所有做同类工具的人都会遇到的问题本地接口的 HTTPS 证书是自签名的Node 默认会拒绝。解决办法是给当前请求单独设置一个不校验证书的 HTTPS agent。注意不要直接修改全局的NODE_TLS_REJECT_UNAUTHORIZED环境变量否则会影响程序中其他所有 HTTPS 请求。只对 127.0.0.1 这个地址关闭校验风险是可控的。接口返回的 JSON 通常包含商店皮肤 ID、价格、剩余时间等字段但不同版本结构可能不同。我的做法是先做一层适配把原始 JSON 统一清洗成entries数组后续渲染层只认这个结构接口字段变了也只改适配层一个地方。3.4 命令入口与表格渲染CLI 的入口用 commander 注册子命令。第一版我只需要一个 store 命令#!/usr/bin/env node const { Command } require(commander); const { fetchStore } require(../src/api); const { renderStore } require(../src/render); const program new Command(); program .name(valorant-skin-cli) .description(在终端里查看 Valorant 皮肤商店) .version(0.1.0); program .command(store) .description(显示今日商店) .option(--json, 输出 JSON 格式) .action(async (options) { try { const data await fetchStore(); if (options.json) { console.log(JSON.stringify(data, null, 2)); } else { renderStore(data); } } catch (err) { console.error(获取商店失败, err.message); process.exit(1); } }); program.parse(process.argv);渲染函数用 cli-table3 输出表格用 chalk 给价格上色const Table require(cli-table3); const chalk require(chalk); function renderStore(data) { const table new Table({ head: [皮肤名称, 价格(VP), 剩余时间], colWidths: [45, 12, 20] }); for (const item of data.entries) { table.push([ item.name, chalk.green(String(item.price)), item.remaining ]); } console.log(table.toString()); }这里的设计逻辑是默认输出给人看--json输出给脚本用。很多命令行工具只关注终端展示忽略了机器可读输出结果用户想配合 jq 处理都没有入口。我建议从第一版就把 JSON 输出当作一等公民对待。3.5 轮询提醒功能怎么实现商店每天刷新时间是固定的但每个人的作息不一样。我加了 watch 子命令每隔几分钟自动请求商店接口如果发现皮肤 ID 列表里出现新的项就在终端输出提醒async function watch(intervalMinutes) { let previous new Set(); const intervalMs intervalMinutes * 60 * 1000; console.log(开始监控商店每 ${intervalMinutes} 分钟检查一次); setInterval(async () { try { const data await fetchStore(); const current new Set(data.entryIds || []); const added [...current].filter((id) !previous.has(id)); if (added.length 0) { console.log(发现新皮肤时间${new Date().toLocaleTimeString()}); for (const id of added) { console.log(- ${id}); } } previous current; } catch (err) { console.error(检查失败, err.message); } }, intervalMs); }这个版本的 previous 只存在内存里进程一旦重启就会丢失对比基准。我后来改成把历史 ID 缓存到本地文件重启后还能继续对比避免错过一次完整刷新周期。如果你要长期挂机建议早点做持久化不然后续统计会少掉前面几天的数据。4. 常见问题与排查技巧实录工具写出来不难真正用起来之后踩到的坑比想象中多。我把这些经验整理成一份速查表方便你遇到问题时快速定位。现象可能原因解决方案找不到 lockfileRiot 客户端未启动或未登录先手动打开客户端完成登录找不到 lockfile安装路径不是默认路径用 find 命令搜索 lockfile 并手动配置路径请求失败self signed certificate本地接口自签名证书被拒绝只对当前请求设置 rejectUnauthorized false接口返回 401认证头构造错误检查密码字段是否被错误分割接口返回 404本地 API 路径随版本变化抓包确认新路径或提供 --endpoint 参数覆盖请求频繁被限制轮询间隔太短把默认间隔调到 5 分钟以上4.1 本地接口连不上先别急着找网络原因运行时报找不到 lockfile最常见的三个原因客户端没有启动、启动了但还没完成登录、当前终端用户和客户端用户不是同一个系统账户。我踩过最隐蔽的坑是终端用管理员权限启动游戏客户端却是在普通用户桌面下启动的两个进程读到的 LOCALAPPDATA 环境变量完全不同自然找不到同一份配置文件。排查顺序建议这样先打开配置目录看 lockfile 文件是否存在存在的话 cat 一下确认不是空文件再检查是不是跨用户访问问题。很多情况下把终端权限降到普通用户就能解决。4.2 证书校验失败是这类本地接口工具的常见问题报错文本通常是self signed certificate或者unable to verify the first certificate。原因就是本地服务的 HTTPS 证书没有经过系统信任链认证。解决办法是在请求时构造一个关闭校验的 https.Agent代码示例已经在上面给出。务必注意作用域。不要为了省事在环境变量里全局关闭校验否则后续请求外部服务时会失去基本的安全保障。我见过有人在同一个脚本里同时请求本地接口和外部 API因为全局关闭证书校验结果外部请求也被中间人工具截获这是完全可以避免的风险。4.3 接口返回 401 或 404 的时候怎么定位401 说明认证信息不对。先检查 lockfile 内容里的密码字段是否被正确解析尤其是用 split(:) 分割时如果密码段出现冒号就会错位。稳妥做法是先按前四个字段切开再把后面的内容全部拼接为密码。404 则基本是接口路径更新了。客户端升级后部分本地 API 端点会调整社区工具最怕这种情况。我给命令行加了一个--endpoint参数允许用户手动覆盖默认路径这样即使官方改了路径也不用改代码、不用重新发布版本直接用参数传新的目标地址即可。4.4 如何避免请求过于频繁触发限流本地接口监听在 127.0.0.1但数据最终来自官方服务器。如果轮询间隔设置成 1 秒短时间内一定会触发对端限制。正常使用场景下商店一天只刷新一次完全没有高频请求的必要。我的默认间隔是 5 分钟一次这个频率既能保证及时看到刷新结果也不会给服务端造成压力。如果你需要监控特定皮肤出现可以在商店刷新时间点前后半小时提高频率其他时间切回低频率甚至暂停轮询。商店的刷新时间是统一日历日切换北京时间大概在早上 8 点前后设置定时任务时注意时区换算。4.5 跨平台路径不统一怎么兼容Windows 的路径依赖 LOCALAPPDATAmacOS 用的是 Home 目录下的 Application SupportLinux 则完全没有固定标准。代码里我预置了三组候选路径顺序尝试但仍然覆盖不了所有发行版。对于这种场景最稳妥的做法是提供一个配置项让用户手动指定 lockfile 路径。我建议在valorant-skin-cli中增加配置系统把 lockfile 路径、接口地址、输出格式都存到本地配置文件中。用户装好工具后如果默认路径找不到可以执行一条命令手动指定比每次改源码好得多。5. 还可以继续往哪个方向扩展如果你已经把基础的商店查询跑通了后续扩展空间其实非常大。我个人接下来想做的事有三个方向。第一个是历史价格记录。每天把商店数据存入本地数据库慢慢积累之后就知道了每个皮肤的返场频率、价格变化趋势甚至能根据历史数据猜测下一次刷新出现某个皮肤的概率。这比单纯盯着当天商店要有意思得多。第二个是心愿单监控。与其每次刷出一整片列表再人工找不如在配置文件中写几个想等的皮肤 IDCLI 检测到目标出现时直接打出一条醒目的提醒甚至可以触发系统通知。有了这个能力就不用天天手动打开客户端看刷新结果了。第三个是跟其他工具联动。CLI 输出 JSON 结构化数据后理论上可以接进任何自动化系统比如存入数据库、发送到通知服务、生成每日商店海报。工具本身只是入口真正有价值的是在你自己的自动化体系里它扮演了一个稳定的数据供给角色。在动手之前有一个建议第一版只做读取 lockfile 和显示商店两件事不要一开始就想着把收藏库、价格统计、心愿单全部做完。命令行工具最好的成长路径是先用起来再在真实使用中慢慢发现缺什么然后一个一个补。等到你发现自己已经连续用了一周这个工具基本就是成功的了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询