# NetWalk **Repository Path**: kiragz/NetWalk ## Basic Information - **Project Name**: NetWalk - **Description**: 用网速和打字速度去散步 —— 网速/打字速度驱动的地图漫步游戏(CC BY-NC-SA 4.0) - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-12 - **Last Updated**: 2026-09-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # NetWalk · 用网速和打字速度去散步 > ## ⚠ 版权与使用声明 > - **转载**本项目或其内容,必须**注明出处**(https://github.com/kiragz/NetWalk + 原作者名) > - **修改 / 二次创作 / 基于本项目开发**,同样必须**注明出处**,并以相同协议(CC BY-NC-SA 4.0)开源发布 > - **禁止任何商业用途**(包括但不限于售卖、付费下载、商业演示) > > 完整条款见 [LICENSE](./LICENSE)。下载 / 克隆本项目即视为已阅读并同意以上声明。 **🪞 国内镜像**(克隆 / 浏览无需代理):https://gitee.com/kiragz/NetWalk **📺 视频介绍**:【NETWALK「开源摸鱼神器:用网速和打字速度在真实地图上散步」】https://www.bilibili.com/video/BV1rJYd6kEJh/?share_source=copy_web&vd_source=55c11616b1aeb14d5166f02b07e17eb7 把你的**下载/上传速率**和**打字速度**,换算成一个人在地图上的走路或奔跑速度, 让一个分身在城市里替你走完这一天。走到路口就掷一次 **ROLL100**,由点数决定直行、左转、右转还是掉头。 下班时点「结束」,生成当天的轨迹地图与数据日报。 走**重复的路**不会再次参与路口抉择;走过的街区会在地图上被逐步**点亮**, 解锁**成就勋章**(36 枚),可**按日/月/年**查看数据、生成**分享图**,并用**存档码**把进度搬到另一台电脑。 支持**账号系统**(邮箱验证码注册 / 登录,一台机器多个档案)、 **邮箱存档管道**(结束漫游自动把存档码发到邮箱,换设备一键同步数据 + 配置 + 出发点)、 以及**打包成免安装的单文件 exe**(内置 Node 运行时,双击即用,带托盘与老板键)。 --- ## 快速开始 **📦 免安装版(推荐)**:直接下载单文件 exe(86MB,双击即用): https://github.com/kiragz/NetWalk/releases/download/v0.9.17/NetWalk.exe **📱 百度网盘**(国内直连):链接 https://pan.baidu.com/s/1ib4l4Fb01FLuoOthzFtgPQ?pwd=tfe2 提取码: tfe2 源码方式运行: ```bash cd netwalk npm install # 首次运行 npm start # 或双击 start.bat ``` 打开 `http://127.0.0.1:8787` → 点「出发」。 想把它变成真正的桌面小组件:点 HUD 右上角的 **⧉**,会弹出一个独立 DOCK 小窗(`/dock.html`), 拖到屏幕任意角落即可常驻显示。 点 DOCK 底部按钮 **📍 查看今日走了多远 / 走到哪里**,弹出今日总览弹窗: 实时显示完整轨迹 SVG(按速度着色)、起终点标记、当前位置脉冲圈,以及里程/时长/路口/当前路摘要。 HUD 底部的功能栏还有四个入口: | 按钮 | 作用 | |------|------| | 🏆 成就 | 成就墙,按分组浏览 36 枚勋章(已解锁彩色 / 未解锁置灰) | | 📊 数据 | 按 **本日 / 本月 / 本年 / 全部** 查看里程、时长、击键、下载上传等 12 项指标与逐日明细 | | 💾 存档 | 生成 / 复制存档码,或粘贴他人存档码**导入并合并** | | 📤 分享 | 一键生成 750×1000 分享卡片(PNG 可保存)与配套文案 | 自检采集是否正常: ```bash npm run selftest # 运行后敲几下键盘,看 KPM 是否上升 ``` 跑测试(一条命令跑完三层,共 **277 项断言,全部通过**): ```bash npm test # 后端冒烟 + 前端 DOM 级 UI + 端到端 npm run smoke # 后端逻辑:速度换算 / ROLL / 路网 / 日报 / 成就 / 存档码 / 聚合口径 npm run uitest # 前端:jsdom 加载真实页面,逐一交互各弹窗并检查运行时错误 npm run e2e # 端到端:自动起一个隔离实例(独立端口 + 临时数据目录 + 关采集) ``` > `uitest` 依赖 `jsdom`(devDependency)。若未安装,先执行 `npm i -D jsdom`。 > 若报「Cannot find module `<某个子依赖>`」而该包确实在 `node_modules` 里, > 通常是解包不完整,重跑一次 `npm install` 即可。 > > `e2e` **不会污染 `data/`**:它用 `NETWALK_DATA`(临时目录)、`NETWALK_PORT=8788`、 > `NETWALK_NO_COLLECT=1` 起一个独立实例,跑完回收整棵进程树并删除临时目录。 > 也可以打一个已在运行的实例:`NETWALK_BASE=http://127.0.0.1:8787 node test/e2e.js` --- ## 玩法与规则 ### 1. 速度换算 综合强度 = 网络强度 × 0.6 + 打字强度 × 0.4 - **网络强度**用对数曲线归一化(`netFullMbps = 20` 视为满负荷),低速也敏感、高速不失控 - **打字强度** = 当前 KPM / 300 | 强度 | 状态 | 速度 | |------|------|------| | < 10% | 🧍 发呆 | 0 – 1.2 km/h | | 10–35% | 🚶 散步 | 1.2 – 3.6 km/h | | 35–58% | 🚶 走路 | 3.6 – 6.5 km/h | | 58–76% | 🚶 快走 | 6.5 – 9.5 km/h | | 76–92% | 🏃 奔跑 | 9.5 – 16 km/h | | > 92% | 🔥 狂奔 | 16 – 21.6 km/h | 速度经过 2.5 秒指数平滑,避免网速抖动导致人物抽搐。 ### 2. 路口 ROLL100 每走完一段路就掷一次 1–100: | 点数 | 决策 | |------|------| | 01–45 | 直行 | | 46–68 | 左转 | | 69–89 | 右转 | | 90–96 | 掉头 | | 97–100 | 灵感爆发 · 远行(走 900–1500 米)| 转向角度带 ±12° 随机扰动,行进距离随点数递增。 ### 3. 地图尺度 | 尺度 | 时空压缩 | 说明 | |------|---------|------| | 城市 | 1× | 真实步速,进度感最强 | | 全中国 | 50× | 一步跨几公里 | | 全世界 | 500× | 全球漫游 | 里程、步数、卡路里均按**真实步行里程**统计,不受压缩比影响。 ### 4. 不走重复路 + 点亮街区 - 引擎维护一个**已走道路集合**(`visitedRoads`),启动时会把当天已经走过的路先载入。 - 路口掷点后先算这条候选路线的**新路占比**;低于 20% 才重掷换方向 (演练路网最多试 3 次,高德模式 2 次)。 阈值刻意放宽了:原来要求"过半是新路",走到老城区这种路网密集的地方会反复重掷, 每次重掷都是一次网络请求,分身就**卡在路口不动**了。宁可偶尔走一小段旧路, 也不要站着等网络。 - **道路不通就原路返回**(新增):走到断头路、水域边、路网边界规划不出下一段时, 不再硬穿一条直线过去,而是**退回到上一个路口重新 ROLL100**,并换个明显不同的朝向, 避免又撞同一堵墙。连续受阻 3 次以上才强制大转向,保证任何情况下都在往前走。 - **规划有总时间预算**(新增):一次路口抉择最多 7 秒,单次路径规划最多 6 秒, 路网服务挂起时按预算放弃并改用直线推进 —— 引擎不会永久停在 `planning` 状态。 - 每次决策都会把 `fresh`(新路占比)一并记录下来,可在轨迹数据的 `rolls` 里回溯。 - 人走到哪里,就把**约 150 m 的街区块**点亮,地图上以半透明蓝色方块叠加; HUD 的「探索」「点亮」两行实时显示已走道路数与点亮块数。 ### 5. 成就系统(36 枚勋章) 勋章共 5 档:铜 / 银 / 金 / 钻 / 史诗,纯 SVG 绘制,未解锁时置灰。 | 分组 | 数量 | 示例 | |------|------|------| | 里程 | 7 | 首次迈步、5 km、半马 21 km、全马 42 km、累计 100 / 1000 km | | 探索 | 6 | 走过 10 / 50 / 200 / 500 条路,点亮 50 / 300 / 1000 块街区 | | 速度 | 4 | 跑起来、15 / 20 / 25 km/h | | 数据 | 6 | 下载 1 / 10 / 100 GB,击键 1 万 / 10 万 / 100 万 | | 路口 | 3 | 掷点 10 / 100 / 1000 次 | | 坚持 | 4 | 连续活跃 3 / 7 / 30 / 100 天 | | 城市 | 3 | 漫游 2 / 5 / 10 座城市 | | 国家 | 2 | 走遍中国、走向世界 | 成就在**每次「结束」漫游时结算**,页面上有未领取的新成就时,功能栏的 🏆 会亮起小圆点。 ### 6. 存档码(跨设备继承) 「结束」后可生成 **存档码**:`NW1.` 前缀 + `base64url(zlib deflateRaw(JSON))`, 包含全部每日轨迹、统计与成就解锁时间。 - 实质是**压缩后的纯文本**,实测约 16% 体积(44 KB 原始数据 → 7 KB 存档码) - 在另一台电脑粘贴导入:**本地为空的日期直接写入,已存在的日期按时间戳去重合并** - 导入后会立刻重算成就,历史数据也能补发勋章;**出发记录也会合并,并按时间全局重排为 1..N** - ⚠ **0.9.0 起存档码可携带本机配置**(高德 Key / 安全密钥 / SMTP / IMAP / 邮箱授权码 / 城市与出发点), 换设备导入后点「恢复本机配置」即可完整复原。因此**存档码属于隐私内容,请勿公开发布**; 导入**别人的**存档码不会自动应用其中的配置(必须手动点按钮),不会覆盖你自己的邮箱配置 ### 6.1 多设备同步(推荐流程) | 步骤 | 操作 | |------|------| | 旧设备 | 正常玩,「结束漫游」会自动把最新存档发到邮箱 | | 新设备 | ⚙ 设置 → 📮 邮件服务:填一次 SMTP/IMAP 与授权码 | | 新设备 | 👤 账号 → 填邮箱 → 获取验证码 → **用验证码登录**(本机没有该档案会自动建档) | | 新设备 | 登录成功后自动**一键同步**:数据 + 高德 Key + 出发点全部取回,弹窗显示「存档 X 天 / 配置 Y 项 / 出发记录 N 次」 | | 出发 | 从**时间上最新的**结束位置继续(多台设备交替走时,按轨迹时间戳判定,与导入顺序无关) | 不想用邮箱也可以:旧设备「存档 → 生成存档码 → 复制」,新设备「存档 → 粘贴 → 导入并合并 → 恢复本机配置」,全程离线自助。 > 重置数据后会**暂停自动同步**(否则出发时的自动同步会把邮箱里的旧存档又拉回来), > 需要恢复旧数据时点「一键同步」或手动导入存档码即可解除。 ### 6.2 邮箱服务(存档自动备份 / 一键同步 / 清理旧邮件) ⚙ 设置 → 📮 配置邮件服务:填 SMTP(如 `smtp.qq.com:465`)、邮箱账号、**SMTP 授权码**(不是登录密码)、 IMAP(如 `imap.qq.com:993`)。QQ / 163 邮箱需先在邮箱后台开启 SMTP/IMAP 服务并生成授权码。 配好之后: | 功能 | 说明 | |------|------| | 自动备份 | 结束漫游自动把最新存档发到邮箱(可关:取消勾选「结束漫游后自动把存档发到邮箱」) | | 手动备份 | 「⬆ 上传存档」或结束面板的「📧 发送到邮箱」随时手动发 | | 一键同步 | 「🔄 同步」按钮:恢复配置 + 拉回最新存档,出发前也会自动做一次 | | 🧹 清理旧邮件 | 只保留最新一封 NetWalk 邮件,其余删除(同步只需要最新那封);两步确认,不影响其他邮件 | ⚠ 两个注意:**① 同步只读「收件箱」**,把 NetWalk 邮件挪到其他文件夹会导致同步失败——要整理就删旧的,别挪走; **② 邮箱里存着你的存档码(含高德 Key / 授权码)**,不要把邮箱借给别人。 ### 7. 分享 「📤 分享」用纯 Canvas 渲染一张 750×1000 的卡片:标题、按速度着色的轨迹图、8 项数据格、 成就勋章行,可保存为 PNG;「复制文案」则生成带 `#NetWalk` 话题的分享文字。 --- ## 配置高德 Key(可选) 未配置 Key 时自动进入**演练模式**:程序生成的虚构城市路网(1156 个路口 / 2027 条道路), 玩法完整,但路名是虚构的。配置 Key 后切换为真实城市路网与真实路名。 申请步骤: 1. 打开 并登录 2. 应用管理 → 创建新应用 → 添加 Key 3. 服务平台必须选 **Web 端 (JS API)** ← 选错会直接不可用,见下方排错 4. 拿到 Key(以及可能的安全密钥 `securityJsCode`)后: - 在网页右上角点击地图状态胶囊 → 填入 Key → 保存(页面会自动重载) - 或直接编辑 `data/config.json` 的 `amapKey` 字段 服务只监听 `127.0.0.1`,Key 通过本机专用接口 `/api/mapkey` 下发,不对外暴露; `/api/config` 里始终是 `***configured***` 脱敏值。 若要在公网部署,请改为后端代理签名,并到高德控制台配置 Referer 白名单。 ### 排错 | 症状 | 原因 | 处理 | |------|------|------| | 顶部胶囊显示 **高德地图未就绪**,地图一片空白 | ① Key 的服务平台选错(不是 Web 端 JS API)② 控制台要求安全密钥但没填 | 补填「高德安全密钥」;或在控制台确认 Key 类型 | | 控制台 / 网络返回 `USERKEY_PLAT_NOMATCH (10009)` | 这是用 **Web 服务** 接口去调一个 **JS API** Key 的典型报错——如果你的 Key 只在 REST 里报这个错,说明 Key 类型是对的 | 无需处理 | | 控制台返回 `INVALID_USER_KEY` | Key 不存在或已被删除 | 重新申请 | | 自动降级成「演练路网(虚构)」 | SDK 脚本加载失败(网络不通) | 检查网络 / 代理是否放行 `webapi.amap.com` | **自检方法**:填好 Key 后刷新页面,顶部胶囊应在几秒内变成绿色的 **高德真实路网**, 地图上出现深圳的街道;否则日志区(右下「漫游日志」)会给出具体原因。 > 实现说明:`AMapProvider` 用 `AMap.plugin()` 显式加载 `AMap.Walking` / `AMap.Geocoder` > (2.0 里只靠 URL 的 `plugin` 参数可能还没挂载就 `new`,会误判成 Key 不可用而降级), > 并监听地图 `complete` 事件等待首帧;8 秒未出图就明确提示,而不是留一张空白地图。 --- ## 出发点(可选) 默认从所选城市的市中心出发。在设置弹窗的「出发点」区块里可以改成任意位置,四种方式: | 方式 | 说明 | |------|------| | 地址搜索 | 输入「深圳湾公园」这类地点名,用高德地理编码解析成坐标(仅高德模式可用) | | 在地图上点选 | 收起弹窗进入点选模式,鼠标点地图任意位置即可(两种模式都可用) | | 用我的当前定位 | 调用浏览器定位 API,需要允许定位权限 | | 手动输入经纬度 | 直接填经度和纬度 | 改完点「保存并重启漫游」生效:分身在「出发」时会从这一点开始走。 > 演练模式下路网是以出发点为中心程序生成的,所以换出发点相当于换一片街区; > 高德模式下则直接落在真实位置。 --- ## 分享给别人时,怎么不泄露我的 Key 先说结论:**Key 只在本机的一个配置文件里,不会跟着分享图、存档码或代码走。** 但有一个坑要注意 —— 别把 `data/` 目录一起发出去。 | 途径 | 会不会带出 Key | |------|----------------| | 📤 分享图(PNG)/ 分享文案 | ❌ 不带。只含轨迹与统计数据 | | 💾 存档码(0.9.0 之前生成的) | ❌ 不带。只含轨迹、统计、成就解锁时间 | | 💾 存档码(0.9.0 起,**默认带配置**) | ⚠ **带!** 含高德 Key / 邮箱授权码 / 出发点——方便换设备一键复原。**别公开发布**;导入别人的码也不会自动应用其中的配置 | | 📧 邮箱里的存档邮件 | ⚠ 同上,正文带配置区,属隐私内容 | | 日报 HTML | ❌ 不带 | | 网页接口 `/api/config` | ❌ 不带。始终返回 `***configured***` | | 网页接口 `/api/selfcheck` | ❌ 不带。只有布尔与路径(版本 / 端口 / 数据目录 / 是否已配置) | | 网页接口 `/api/mapkey` | ✅ 返回真实 Key,但**仅允许 127.0.0.1 本机访问**,外部请求返回 403 | | **`data/config.json` 文件**(源码模式)
**`NetWalk-data/config.json`**(打包版) | ✅✅ **带了!这是唯一会泄露的途径** | 所以分享时: 1. **只发代码目录,不要发 `data/`。** 项目已内置 `.gitignore` 忽略 `data/`(只保留 `data/config.example.json` 模板),`git status` 里不会出现你的 Key。 2. 如果是打包 zip 发人,手动把 `data/config.json` 删掉,或把 `data/` 整个排除。 3. 对方拿到后填**自己手机号申请的 Key**(设置弹窗里写了完整步骤), 或者干脆不填 —— 演练模式不需要 Key,玩法一样完整。 另外,**不要把这个服务暴露到公网**。它只监听 `127.0.0.1` 就是为了这个: JS API 的 Key 按设计是给浏览器用的,页面上必然能看到。真要做公网服务, 得改成后端代理 + 高德 Web 服务 Key + 签名(`sig`),并配 Referer 白名单。 --- ## 会超过高德免费额度吗? **不会。** 个人认证开发者的日配额是 **5000 次**([官方说明](https://lbs.amap.com/faq/account/certification/39670)), 本游戏的实际用量比这个数低一个数量级,而且代码里加了硬保险。 ### 用量测算 只有两类操作会消耗配额,其余(瓦片渲染、`Marker`、`Polyline`、`Circle` 点亮方块)都是纯前端绘制,不计次。 **1. 步行路径规划** —— 每个路口决策调用一次 - 单段路程平均约 **542 m**(`180 + roll×5.5 + 随机120`,远行 900–1500 m) - 按较激进的 **8 km/h** 连续走 8 小时算:`8000 × 8 ÷ 542 ≈ 118` 个路口 - 正常情况下每次决策 **1 次**调用(路线多为新路,不算重复);极端情况(一直撞死路反复重掷) 最多 3 次尝试 → 约 `118 × 3 ≈ 354` 次 **2. 逆地理编码(取路名)** —— 仅在路径结果里没有路名时才用,带 5 秒节流 高德返回的步行步骤通常自带路名,所以实际很少触发。 ### 结论 | 场景 | 估算日调用 | 占 5000 配额 | |------|-----------|-------------| | 典型一天(8 小时办公挂机) | **约 120–200 次** | ~3% | | 重度使用(10 小时 + 频繁重掷) | 约 500 次 | ~10% | | 理论最坏(24 小时满速 + 每次决策都重掷) | 约 4200 次 | ~84% | 也就是说正常使用**连 5% 都用不到**。 ### 内置保险 `AmapProvider` 会按天累计调用次数(存在浏览器 `localStorage` 里,跨刷新不丢): - 达到 **4000 次**(可配置 `amapMaxCallsPerDay`)后**停止请求高德**, 引擎自动退化为直线推进 —— 路线没那么真实了,但走、里程、成就、轨迹图全部照常 - 用掉 90% 时在日志里提醒一次;DOCK 的「高德调用」一行实时显示 `已用 / 上限` - 逐条调用都有超时保护(路线 8 秒、逆地理 6 秒),失败即降级,不会卡住游戏 想收紧或放宽,改 `data/config.json` 的 `amapMaxCallsPerDay` 即可。 --- ## 目录结构 ``` netwalk/ ├── server/ │ ├── index.js HTTP + WebSocket 服务、配置、API │ ├── entry.js 统一入口:主服务 / 采集子进程 二选一分流(打包也用这个) │ ├── paths.js 源码 / 打包两种形态的路径解析 │ ├── static.js 极简静态中间件(readFileSync,适配打包快照) │ ├── webassets.js 打包时把内嵌前端资源释放到 exe 旁边 │ ├── native.js 打包时把内嵌 uiohook 原生模块释放到 exe 旁边 │ ├── childproc.js 统一的采集子进程启动器(spawn + ipc) │ ├── netmon.js 网速采集(PowerShell 常驻子进程) │ ├── networker.js 网速兜底采集子进程(systeminformation) │ ├── keymon.js 击键采集(主进程侧,IPC 取数) │ ├── keyworker.js 全局击键钩子子进程(同时负责识别老板键) │ ├── bosskey.js 老板键判定(纯函数:键码 + 修饰键) │ ├── tray.js 托盘管理(Node 侧,命令文件 / stdout 双向通信) │ ├── tray.ps1 托盘常驻脚本(NotifyIcon + 隐藏/恢复窗口) │ ├── store.js 轨迹落盘 + 聚合统计(日/月/年)+ 出发记录合并与全局重排 │ ├── achievements.js 成就判定与解锁状态持久化 │ ├── archive.js 存档码导出 / 导入(zlib + base64url,可携带本机配置) │ ├── report.js 日报生成(自包含 HTML + SVG 轨迹) │ ├── account.js 账号:注册 / 验证码登录 / 多档案(accounts.json) │ ├── profile.js 旅行者档案:起名 / 出发点 / 重置保护(profile.json) │ ├── mailer.js 零依赖 SMTP 客户端(465 直连 TLS / 587 STARTTLS / AUTH LOGIN) │ ├── mailbox.js 零依赖 IMAP 客户端(一键同步:取回存档码 + 配置区 / 清理旧邮件) │ ├── cities.js 城市中心坐标表 │ ├── version.js 版本号 + 更新概要(GET /api/version) │ ├── smoke.js 后端冒烟测试(88 项) │ ├── selftest.js 采集自检 │ ├── native-payload.js 构建生成:内嵌 uiohook 原生模块(base64) │ └── public-payload.js 构建生成:内嵌前端资源(base64) ├── build.js 打包脚本:生成载荷 + 调 pkg ├── public/ │ ├── index.html 主界面(地图 + HUD + 5 个功能弹窗) │ ├── dock.html 独立 DOCK 小窗 │ ├── css/style.css │ └── js/ │ ├── speed.js 速度换算与 ROLL100 │ ├── achievements.js 成就定义 + 勋章 SVG + 成就墙渲染 │ ├── provider-drill.js 演练路网提供者(含点亮区图层) │ ├── provider-amap.js 高德路网提供者(含点亮区图层) │ ├── engine.js 漫游引擎(不走重复路 / 点亮街区) │ ├── share.js 分享卡片 Canvas 渲染 + 分享文案 │ └── app.js 主应用 ├── test/ │ ├── ui.js 前端 DOM 级 UI 测试(jsdom,136 项:含 mock 高德 SDK、 │ │ 出发点搜索/点选/越界校验/保存载荷/结束面板) │ └── e2e.js 端到端测试(自动起隔离实例,53 项:含账号登录、 │ │ 换设备建档、存档码携带配置、出发次数全局重排) ├── dist/ 打包产物 NetWalk.exe(已 gitignore) └── data/ ├── config.json 配置(含你的 Key)—— ⚠ 分享时不要带上这个文件 ├── config.example.json 配置模板(不含 Key,可安全提交) ├── tracks/ 每日轨迹 ├── achievements.json 成就解锁状态 └── reports/ 每日日报 HTML ``` 根目录另有 `.gitignore`,已忽略 `data/`(保留示例模板)、`node_modules/`、 `dist/`、临时文件与日志,以及构建生成的 `server/native-payload.js` 与 `server/public-payload.js`。 > **实际运行的数据目录不在项目里**:0.8.3 起固定在 `%LOCALAPPDATA%\NetWalk-data` > (源码与打包版一致,`NETWALK_DATA` 可覆盖)。这样 exe 挪目录、多存一份都不会丢配置。 > 项目里的 `data/` 只是源码模式的默认兜底与示例配置所在。 --- ## 两个重要的工程决策 **为什么采集全部放在子进程?** `systeminformation` 在 Windows 上单次 `networkStats()` 实测耗时可达 10–20 秒, 在主进程周期调用会彻底阻塞事件循环(HTTP/WebSocket 全部无响应)。 因此网速改由常驻 PowerShell 子进程每秒输出网卡累计字节数、由主进程差分算速率; 全局键盘钩子(uiohook)同样独占消息循环,也放入独立子进程。 **隐私** 只统计击键的**次数与时间戳**,不记录任何键码、字符或窗口标题。 所有轨迹数据仅保存在本机 `data/` 目录。 **为什么存档码用 zlib + base64url?** 轨迹是密集的经纬度点,JSON 冗余度极高。`deflateRaw` 后再转 URL 安全字符, 既能压到 ~16% 体积、方便复制粘贴,又不会出现 `+ / =` 被聊天软件转义的问题。 --- ## HTTP 接口 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/config` | 读取配置(Key 已脱敏) | | POST | `/api/config` | 保存配置 | | GET | `/api/mapkey` | 下发高德真实 Key(仅本机可访问) | | GET | `/api/status` | 采集状态与当日累计量 | | POST | `/api/session/start` | 记录本次漫游的城市与尺度 | | POST | `/api/session/end` | 结束漫游、落盘、**结算成就** | | GET | `/api/day/:date` | 某日轨迹 + 当日已走道路(`visitedRoads`) | | GET | `/api/stats?range=day\|month\|year\|all` | 区间聚合统计 | | GET | `/api/achievements` | 成就状态 + 全量聚合数据 | | POST | `/api/archive/export` | 生成存档码 | | POST | `/api/archive/import` | 导入并合并存档码 | | POST | `/api/report/:date` | 生成当日日报 HTML | | POST | `/api/track/path` \| `/sample` \| `/roll` | 轨迹点 / 环境采样 / 路口决策上报 | | GET | `/api/sessions` \| `/api/lastpos` | 全部出发点(含序号) / 最近一次走过的位置(按时间戳) | | GET | `/api/version` \| `/api/selfcheck` | 版本与更新概要 / 自检(版本·端口·数据目录·邮箱·Key,不含明文密钥) | | POST | `/api/account/code` \| `/register` \| `/login` | 邮箱验证码 / 注册 / 登录(新设备自动建档) | | POST | `/api/account/logout` \| `/rename` \| `/reset` | 登出 / 改名 / 重置当前账号数据 | | POST | `/api/mailbox/push` \| `/pull` \| `/status` \| `/cleanup` \| `/restore-config` | 上传存档 / 一键同步 / 存档状态 / 清理旧邮件 / 从邮箱恢复配置 | | POST | `/api/archive/apply-config` | 应用存档码里携带的本机配置(换设备免手填) | | POST | `/api/restart` | 重启服务(切换账号后让新数据目录生效;带端口自愈) | | WS | `/ws` | 每秒推送网速与击键(`tick`) | ### 环境变量 | 变量 | 默认 | 说明 | |------|------|------| | `NETWALK_PORT` | `8787` | 监听端口(被占用自动顺延,启动日志会写明实际端口) | | `NETWALK_DATA` | **`%LOCALAPPDATA%\NetWalk-data`**(源码与打包版一致) | 数据目录。固定在用户目录是为了避免"exe 挪个地方就丢配置";测试时指到临时目录做隔离 | | `NETWALK_TRAY` | 打包版 `1` | 设为 `0` 不启动托盘图标 | | `NETWALK_RESTART` | — | 由 `/api/restart` 自动设置:标记"我是被重启拉起的",会等旧进程释放端口而不是误判"已在运行"退出 | | `NETWALK_NO_COLLECT` | — | 设为 `1` 时不启动网速/击键采集(测试用,避免拉起子进程) | | `NETWALK_NO_OPEN` | — | 打包运行时设为 `1` 则启动后不自动打开浏览器 | | `NETWALK_NO_HOLD` | — | 打包运行时设为 `1` 则出错后不等待回车(直接退出) | | `NETWALK_NATIVE_DIR` | `/NetWalk-native` | 原生模块释放目录 | | `NETWALK_PUBLIC_DIR` | `/NetWalk-public` | 前端资源释放目录 | | `NETWALK_UIOHOOK_DIR` | — | 由主进程传给采集子进程,告诉它去哪儿加载 uiohook | --- ## 打包成单文件 exe ```bash npm run build # 默认走 Node SEA(用本机 node.exe 做底座,无需联网下载) npm run build:node24 # 备选:@yao-pkg/pkg(需要联网拉 ~55MB 预编译 Node) npm run payload # 只重新生成内嵌载荷,不打包 ``` 产物在 `dist/NetWalk.exe`(约 90 MB,内置 Node 运行时 + 前端资源 + 原生模块,双击即用)。 > SEA 是默认模式:`node --experimental-sea-config` 出 blob,再用 `postject` 注入底座 `node.exe`。 > 打包前需要 `npm i -D esbuild postject`;构建流程见 `build.js`(生成载荷 → esbuild 打包 → SEA 注入)。 > ⚠ **部署前先退出正在运行的 NetWalk**,否则 exe 被占用会导致复制失败、部署了旧版本而不自知。 ### 打包后 exe 旁边的三个目录 | 目录 | 内容 | 说明 | |------|------|------| | `NetWalk-data/` | `config.json`、`tracks/`、成就 | 你的 Key 和战绩,**分享时不要带上这个目录** | | `NetWalk-public/` | 前端 html/css/js | 首次运行自动释放,可以直接改 UI,改完刷新页面即可 | | `NetWalk-native/` | uiohook 原生模块 | 首次运行自动释放,删掉也能跑(只是失去全局击键统计) | ### 摸鱼外观:纯文字 Excel 风格报表 主面板的「📑 报表」按钮会打开 `/report-excel.html` —— 一个**长得和 Excel 一模一样**的页面, 把今天的 NetWalk 漫游统计伪装成「外勤巡检日报表」。字段全部套上「工号 / 工时 / 巡检里程 / 工单数 / 流量 / 操作次数」这样的工作化名字,按 30~60 分钟一段拆成 3~9 条「任务」,底部 「今日合计」+ 存档码分多行展示。 | 元素 | 仿造 | |------|------| | 标题栏 | 绿色 #217346,文件名为「工作日报表 - 2026-09-11.xlsx - Excel」 | | Ribbon | 文件 / 开始 / 插入 / 页面布局 / 公式 / 数据 / 审阅 / 视图 / 帮助 | | 公式栏 | 名称框 + fx + 输入框 | | 单元格 | 列号 A~I + 行号 1~N + 细灰网格 + 选中态绿框 | | 合计行 | 黄底加粗(#fff2cc) | | 状态栏 | 绿底白字,「就绪 / 共 N 条记录 / 工时合计 X h / 第 1 页 / 100%」 | #### 按日期查看 Ribbon 右侧有**日期下拉**(数据来自 `GET /api/days`),切换后标题栏文件名、表格内容、状态栏 全部跟着变。也支持直接用 `?date=2026-09-10` 打开指定日期。 #### 导出 Excel(做统计图用) | 按钮 | 产出 | 用途 | |------|------|------| | **💾 下载本日 .xls** | `NetWalk_2026-09-11.xls` | 单日明细(3~9 条任务 + 合计 + 存档码) | | **📈 导出全部(按日汇总)** | `NetWalk_按日汇总_20260911.xls` | 一行一天:日期 / 工时 / 里程 / 路口 / 流量 / 击键 / 平均速度 / 最高速度 + 合计行 | 汇总表是**给统计图准备的**:直接用 Excel 选中数据区 → 插入图表,就能出 「里程随日期变化」「每日击键量」这类趋势图。 `.xls` 是 Excel 兼容的纯 HTML(`application/vnd.ms-excel` + BOM),Excel / WPS / LibreOffice 都能直接打开,不需要任何第三方库。数字列带 `mso-number-format:"General"`, 保证 Excel 识别成数值而不是文本(否则做不了图表)。 主面板还有一个独立的 **「📋 复制码」** 按钮(不和 Excel 页面联动),点一下就生成并存剪贴板, 适合临时复制存档码换设备导入。 ### 版本号与更新概要 主面板标题栏旁边有一个 **版本号胶囊**(如 `v0.6.0`),点它弹出完整的版本更新概要: 每个版本的日期、标题、变更清单。数据来自 `GET /api/version`(`server/version.js`, 新增版本只要在 `CHANGELOG` 数组最前面插一项即可)。 ### 托盘图标与老板键 打包后的 exe 会在**任务栏右下角**放一个小图标: | 操作 | 效果 | |------|------| | 双击图标 | 打开 NetWalk 面板 | | 右键 → 打开面板 | 同上 | | 右键 → 隐藏窗口(老板键) | 一键把所有浏览器窗口和 NetWalk 自己的窗口藏起来 | | 右键 → 退出 NetWalk | 保存数据并彻底退出 | **老板键默认 F9**(设置里可改成 F10 / F11 / F12 / Ctrl+Shift+H / Ctrl+Alt+N)。 按下后所有浏览器窗口(Edge、Chrome、Firefox、360、QQ 浏览器……)和 NetWalk 的控制台 窗口会立刻消失,任务栏里也不留痕迹;**再按一次**(或双击托盘图标)原样恢复, 游戏在后台照常走,里程、击键、网速都不中断。 隐私:老板键只比对「键码 + 修饰键」,不记录你按了什么字符,也不关心焦点在哪个窗口。 ### 双击 exe 没反应怎么办 按这个顺序排查: 1. **先看有没有 NetWalk 的托盘图标** —— 有的话说明程序在跑,只是浏览器没弹出来, 双击图标即可;没有图标再看第 2 条。 2. **看日志文件** `NetWalk-data/netwalk.log`。任何启动失败都会写在这里, 打包版失败时还会弹一个系统提示框。 3. **Windows Defender SmartScreen / 杀毒软件拦截** —— 未签名的 exe 第一次运行常被拦。 解决办法:右键 exe → 属性 → 勾选「解除锁定」;或在杀毒软件里把 exe 加白名单。 首次运行慢(十几秒才出画面)通常也是杀毒在扫描未签名 exe,属正常现象。 4. **端口被占** —— 已经在跑一个 NetWalk 时,新开的程序会提示「已经在运行」并帮你打开 已有的窗口;如果是别的程序占了 8787,会自动顺延到 8796,启动日志里会写明。 5. 想看完整的启动过程,可以在 exe 所在目录开命令行跑 `NetWalk.exe`, 输出会留在窗口里(正常运行时也能看到采集器的就绪状态)。 ### 打包为什么要绕这么多弯 三个坑,都需要专门处理: 1. **原生模块进不了快照** `uiohook-napi` 是 `.node` 原生模块,打包器的只读快照里无法 `dlopen`。 做法:构建时把「包描述 + JS 包装层 + `win32-x64` 预编译产物 + `node-gyp-build`」 压成 base64 内嵌(`server/native-payload.js`),首次运行释放到 `NetWalk-native/`, 再由子进程按真实路径 `require`。 2. **静态资源走不了 `express.static`** 它底层是 `fs.createReadStream` + Range 处理,在快照里行为不稳定。 做法:改用 `server/static.js` 里的 `readFileSync` 中间件(本项目就十来个静态文件, 一次性读出来更快也更确定),并把 `public/` 也做成内嵌载荷释放到 `NetWalk-public/`。 3. **子进程不能 `fork` 快照里的文件** 采集子进程(全局键盘钩子、网速兜底)原本用 `fork(脚本路径)`, 打包后脚本在只读快照里,没有可执行的独立文件。 做法:`server/childproc.js` 改成「用同一个 exe + `--netwalk-child=` 标记」 启动子进程,由 `server/entry.js` 分流;用 `spawn` 的 `'ipc'` stdio 保留 `process.send` 通道。 源码模式下走 `node server/entry.js --netwalk-child=`,两条路径行为一致。 4. **pkg 自 spawn 的第一个参数会被当作脚本路径** pkg 会给打包内应用发起的所有 `spawn` 注入 `PKG_EXECPATH`,子进程的 bootstrap 据此走「`argv[1]` = 要执行的脚本」分支——直接传 `--netwalk-child=xxx` 会被当成 `netwalk\--netwalk-child=xxx` 这个文件去找,子进程立刻崩溃退出。 做法:`childproc.js` 里把快照内的入口脚本 `process.argv[1]`(打包父进程里 恰好就是它)作为第一个参数,角色标记跟在第二个。 实测时可以看启动日志:击键采集若长期「连接中→降级」,多半是这一条又踩了。 5. **打包子进程首启很慢,就绪判定窗要放宽** 打包后的子进程每次启动都要解压 GZip 快照,还可能被杀毒软件首扫, 实测首次 `ready` 要 6~10 秒。keymon 的就绪判定窗若只有 3 秒, 全局钩子会永远降级成「仅浏览器窗口内」。现已放宽到 20 秒, 且就绪结果异步补打一行日志,不再阻塞 HTTP 服务启动。 > **目标版本注意**:`@yao-pkg/pkg` 的远程缓存(pkg-fetch v3.6)只提供 > **node22 / node24 / node26** 的 win-x64 预编译产物,没有 node18 和 node20。 > 指定不存在的版本会让它回退到「从源码编译 Node」,本机缺 NASM 会直接失败。 --- ## 已知限制 - 全局键盘钩子在某些以管理员权限运行的程序上无法捕获击键;若自检 KPM 始终为 0, 可尝试用管理员权限运行,此时会降级为「仅统计浏览器窗口内的击键」 - 高德模式下每次路口决策会调用步行路径规划,频繁决策会产生较多 API 调用,注意配额 - 日报使用内置 SVG 绘制轨迹(自包含、可离线打开),不依赖地图 SDK - **「不走重复路」按路名判定**:演练路网的路名是规则生成的,同名路可能出现在路网不同位置, 因此演练模式下会略偏保守(宁可绕开同名路)。高德真实路网存在同名道路(如各区都有的「建设路」) 时同理,属于预期行为 - 「点亮街区」按约 150 m 网格统计,网格是等经纬度划分,在南北方向上实际边长会略有差异 - 存档码是明文压缩,**不是加密**;请勿把包含隐私位置习惯的存档码公开发布 - 前端 UI 测试基于 jsdom(无真实渲染引擎),覆盖的是 DOM 结构与逻辑正确性, 不含像素级视觉回归;Canvas/DOM 在极老版本浏览器上可能表现不同 --- ## 📄 版权与转载声明 本项目采用 **[CC BY-NC-SA 4.0(署名-非商业性使用-相同方式共享 4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.zh-Hans)** 许可协议发布。 简单说,你可以: - ✅ **自由转载** —— 复制、分发本项目或其内容,但必须**标明出处**(本仓库链接)与原作者 - ✅ **自由改写** —— 修改、二次创作、基于本项目开发新功能,同样需注明出处 - ❌ **禁止商用** —— 不得将本项目或改写版本用于商业目的 - 🔄 **相同方式共享** —— 改写后的作品需以相同协议发布 完整协议文本见 [LICENSE](./LICENSE) 文件。