Yggdrasil-AI-labs/wigle-to-wdgwars

GitHub: Yggdrasil-AI-labs/wigle-to-wdgwars

一个将 WiGLE 格式的 Wi-Fi/BLE wardrive 数据自动推送到 WDGoWars 社区排行榜的 Python CLI 工具。

Stars: 8 | Forks: 0

# wigle-to-wdgwars 将 WiGLE 格式的 Wi-Fi/BLE wardrive CSV(以及可选的 aircraft JSON)推送到 **[WDGoWars](https://wdgwars.pl/)** 社区 wardrive 排行榜。 这是一个小型的 Python 3 CLI。仅有一个依赖项:[gungnir](https://github.com/HiroAlleyCat/gungnir),这是该系列中每个 wdgwars.pl feeder 都会使用的共享 HMAC transport client。使用 `pip install -r requirements.txt` 安装它(不需要在 PATH 中包含 git —— pip 会通过普通的 HTTPS 以 tarball 形式获取它)。 ## 家族 WDGoWars feeder 家族中的兄弟仓库: - [Muninn](https://github.com/HiroAlleyCat/adsb-to-wdgwars) — ADS-B feeder - [Heimdall](https://github.com/HiroAlleyCat/meshcore-to-wdgwars) — MeshCore LoRa feeder - [gungnir](https://github.com/HiroAlleyCat/gungnir) — 共享 HMAC transport 库 - [wdgwars-api-tester](https://github.com/HiroAlleyCat/wdgwars-api-tester) — API 表面探测 ## 目录 - [这是什么](#what-this-is) - [最简单的安装方式 — 引导式设置](#easiest-install--guided-setup) — `./setup.sh` 会保存两个密钥并安装一个每日定时器 - [快速开始 — 无需保存密钥的一次性推送](#quick-start--one-off-push-without-saving-keys) - [安装说明](#installing) — 手动 venv + pip 流程 - [首先获取 WiGLE CSV 文件](#getting-a-wigle-csv-in-the-first-place) - [按计划运行(定时器)](#running-on-a-schedule-timer) — `--schedule` 会安装什么,以及手动编写的方案 - [WDGoWars API 参考](#wdgowars-api-reference) — 逆向工程所得,因为门户网站没有公开文档 - [Aircraft JSON 格式(签名 endpoint)](#aircraft-json-format-signed-endpoint) - [故障排除](#troubleshooting) - [相关工具](#related-tools) - [许可证](#license) ## 这是什么 [WDGoWars](https://wdgwars.pl/)(“Watch Dogs Go Wars”)是一个社区 wardrive 排行榜/游戏。玩家捕捉 Wi-Fi 网络、蓝牙 设备和飞行器,上传他们的观察结果,获取积分,赢得徽章, 并加入帮派。它很小巧、友好,由波兰人运营。 该门户网站接受在三个 endpoint 上进行上传,但**不发布 API 文档**。每个构建了上传器的人都是通过网络抓包或开源固件对 契约进行了逆向工程。这个工具: 1. 将 WiGLE-1.6 CSV 推送到 `/api/upload-csv` 以处理 Wi-Fi + BLE。 2. 将 aircraft 记录的 JSON 列表推送到已签名的 `/api/upload/` endpoint。 3. 可选地**直接从 WiGLE 拉取您的上传内容**(`--from-wigle`)并 推送它们,因此您无需触碰任何文件。 4. 记录传输格式,这样下一个人就不必从头开始了 (参见 [WDGoWars API 参考](#wdgwars-api-reference))。 它的设计初衷是易于阅读、可放入 cron job 中运行,并且对 以前从未发布过 wardrive 的新玩家很友好。 ### 适用人群 - 您使用 **[WiGLE Android 应用](https://play.google.com/store/apps/details?id=net.wigle.wigleandroid)** 或另一个导出 WiGLE 格式 CSV 的工具进行 wardrive,并且您想找一个第二个 地方来发送您的捕获数据。 - 您运行着 **Kismet** 或 **hcxdumptool** 设备,并且已经将其输出 转换为 WiGLE CSV。 - 您希望从 Raspberry Pi/服务器进行**定时推送**,该设备/服务器维护着一个本地的观测 数据库并生成 CSV。 - 您是一名工具作者,需要 WDGoWars 摄取契约的有效参考。 ## 最简单的安装方式 — 引导式设置 如果您只是想每天运行一次推送,而不想阅读本 README 的其余部分,这就是您的路径。一个脚本即可完成整个安装:venv、依赖项、验证两个 API key,以及一个每日定时器。 ``` git clone https://github.com/HiroAlleyCat/wigle-to-wdgwars.git cd wigle-to-wdgwars ./setup.sh # Linux / Mac / Pi ``` ``` REM Windows: double-click setup.bat, or from a terminal: setup.bat ``` `setup.sh` 按顺序执行以下操作: 1. 创建一个项目本地的 `.venv/` 并将 `requirements.txt` 安装到其中 (适用于 PEP 668 发行版,无需 `--break-system-packages`)。 2. 提示输入您的 **WDGoWars API key**,通过 `/api/me` 进行验证, 保存到 `~/.config/wigle-to-wdgwars/wdgwars.key`(权限模式 600)。 3. 提示输入您的 **WiGLE token**(来自 [wigle.net/account](https://wigle.net/account) 的 "Encoded for use" 字符串),通过列出 一条 transaction 进行验证,保存到 `~/.config/wigle-to-wdgwars/wigle.key`(权限模式 600)。 如果您只想推送本地 CSV,则可跳过此步。 4. 提供安装**每日定时器**(systemd user unit / cron 条目 / Windows 计划任务,具体取决于您的操作系统支持什么)的选项,该定时器在 本地时间 03:00 运行 `--from-wigle` 并上传您最新的 WiGLE 驱动。 5. 将定时器默认设置为**试运行**,以便第一次计划触发只解码 并记录日志,但不执行 POST。重新运行 `./run.sh --schedule` 并对 试运行提示回答 "no",即可将其切换为正式运行。 此后,`./run.sh`(无参数)将执行一次性推送,定时器会负责 其余的工作。若要稍后移除时间表:`./run.sh --unschedule`。 您可以随时再次运行 `--setup` 以轮换密钥或重新配置 定时器 —— 它是幂等的,并在替换任何内容之前会先询问。 如果想在无需引导脚本的情况下执行这些步骤(例如,您已经 有一个 venv),请直接调用相同的标志: ``` .venv/bin/python wigle_to_wdgwars.py --setup # full interactive flow .venv/bin/python wigle_to_wdgwars.py --schedule # just the timer step .venv/bin/python wigle_to_wdgwars.py --unschedule # remove the timer # 非交互式等效命令(用于配置): .venv/bin/python wigle_to_wdgwars.py --save-key YOUR_WDGWARS_KEY .venv/bin/python wigle_to_wdgwars.py --save-wigle-key YOUR_WIGLE_TOKEN .venv/bin/python wigle_to_wdgwars.py --schedule --schedule-time 03:00 \ --schedule-chunk-size 10000 --schedule-dry-run ``` ### `./setup.sh` 之后会发生什么 第一次使用时,有几件事可能会被误读为“这是不是坏了?”: - **第一次计划触发不会显示在您的排行榜上。** `--setup` 将定时器默认为试运行 —— 触发时会解码并写入日志,但 从不执行 POST。这是故意的,以便您在 正式上线前验证安装。要上线,请重新运行 `./run.sh --schedule` 并对 试运行提示回答 "no"。 - **计划的运行无法从您的 shell 环境中读取密钥。** systemd / cron / schtasks 都在一个精简的环境中运行,没有您的 `$WDGWARS_API_KEY` / `$WIGLE_API_KEY` 环境变量。计划运行的命令 改为读取已保存的密钥文件(`~/.config/wigle-to-wdgwars/wdgwars.key` + `wigle.key`)。`--setup` 已经为您保存了这两个文件。如果您跳过了 `--setup` 并且只导出了环境变量,定时器在运行时会失败。 - **WiGLE 会对您自己账户的拉取进行速率限制。** 自动安装的定时器 每天运行一次 `--from-wigle --wigle-latest 1`,这可以轻松保持在 WiGLE 免费层级的查询预算之内。如果您提高了 `--wigle-latest` 或运行得更频繁,您可能会达到每个账户的配额,并开始在 日志中看到 `HTTP 429`。 ### 检查是否正在运行 您不必等待每天的触发 —— 可以在 `./setup.sh` 之后立即进行端到端验证: ``` # Linux(systemd 用户管理器) systemctl --user list-timers wigle-to-wdgwars.timer systemctl --user start wigle-to-wdgwars.service # fire one tick now journalctl --user -u wigle-to-wdgwars.service -n 30 # Linux/Mac(cron — 在 systemd 不可用时安装) crontab -l | grep wigle-to-wdgwars tail -f ~/.wigle-to-wdgwars-cron.log # Windows(schtasks) schtasks /Query /TN WigleToWDGoWars /V /FO LIST :: shows Last Run Result schtasks /Run /TN WigleToWDGoWars :: fire one tick now # Task Scheduler 不会捕获 stdout。要查看运行产生的结果, # 请自行从 PowerShell 执行相同的命令: .venv\Scripts\python wigle_to_wdgwars.py --from-wigle --wigle-latest 1 \ --chunk-size 10000 --dry-run ``` 成功的 `--dry-run` 触发看起来像这样(在日志 / journal 中): ``` [wigle] pulling 1 most-recent upload(s): [wigle] : KB -> WDGoWars [wdgwars] POST https://wdgwars.pl/api/upload-csv field=file file=.csv chunks=1 total= KB [wdgwars] dry-run: not sending ``` `dry-run: not sending` 是安全防线 —— 您的数据还没有发送到 排行榜,但在此之前的所有操作都成功了。要正式启用: ``` ./run.sh --schedule # interactive, answer "n" to the dry-run prompt # 或者,headless 模式: .venv/bin/python wigle_to_wdgwars.py --schedule --schedule-time 03:00 \ --schedule-chunk-size 10000 # no --schedule-dry-run = live ``` ### 常见意外 - **`bash: ./setup.sh: Permission denied`** — 您下载了 ZIP 而不是 `git clone`,并且可执行权限位没有保留下来。请改用 `bash setup.sh` 运行,或者先执行 `chmod +x *.sh scripts/*.sh`。 - **`pip install` 报 `error: externally-managed-environment`** — Bookworm / Debian 12+ / Ubuntu 23.04+ / Homebrew Python 强制执行 PEP 668 并拒绝 安装到系统 Python 中。`./setup.sh` 流程使用项目本地的 `.venv/` 并绕过了这个问题。如果您一直在从旧的 README 中复制粘贴 `python3 -m pip install -r requirements.txt`,请切换到 `./setup.sh`(或使用下面[安装说明](#installing)中的 venv 方法)。 - **`./setup.sh` 报 `Failed to create venv`** — 在 Debian/Ubuntu/Pi 上默认情况下未安装 `python3-venv` 模块。请运行 `sudo apt install -y python3-venv python3-full` 然后重试。 - **`./run.sh` 报 `no API key` 错误** — 您跳过了 `--setup`(或者它 没有执行到保存步骤)。运行 `./run.sh --setup` 来执行向导。 - **定时器已安装,但第二天排行榜上什么也没有** — 请参阅 上文的试运行说明。您看到的是安全防线,而不是损坏的安装。 - **日志中出现 `HTTP 429`** — 要么是 WDGoWars 要求您等待 (服务器端的队列正在处理您之前的上传 —— 工具会休眠 并在下一次触发时重试),要么是 WiGLE 因为您的拉取 过于频繁而对您进行了速率限制。位于 `~/.config/wigle-to-wdgwars/cooldown.json` 的冷却文件会在多次运行之间得到遵守。 ## 快速开始 — 无需保存密钥的一次性推送 如果您现在只想推送单个文件而不想向 磁盘保存任何内容,请在命令行中粘贴密钥。请使用 [安装说明](#installing) 中的 venv — 直接针对系统 Python 粘贴 `python3 wigle_to_wdgwars.py` 会报错 `error: externally-managed-environment`(在 Bookworm / Debian 12+ / Homebrew 上)。venv 方法只需多写一行,并且 适用于所有发行版。 ``` # 在 Installing 部分提到的 venv 中 .venv/bin/python wigle_to_wdgwars.py --whoami --key YOUR_WDGWARS_API_KEY # → [wigle-to-wdgwars] key OK — user=… wifi=… ble=… aircraft=… .venv/bin/python wigle_to_wdgwars.py my-wardrive.wiglecsv.gz \ --key YOUR_WDGWARS_API_KEY --chunk-size 10000 ``` `--chunk-size 10000` 是对于任何超过约 5 000 行的数据的安全默认值。具体原因请参阅 [Cloudflare 524 陷阱](#the-cloudflare-524-footgun)。 在 Windows 上:`.venv\Scripts\python wigle_to_wdgwars.py ...`。或者直接使用 上文的[引导式设置](#easiest-install--guided-setup) 中的 `run.bat`。 ### 完全不需要文件 — 直接从 WiGLE 拉取 如果您使用 WiGLE 应用程序进行 wardrive,您的运行数据已经上传到了 WiGLE。 使用 `--from-wigle`,该工具会直接从 WiGLE 获取您的最新上传并 将其推送到 WDGoWars — 您无需导出、解压或移动任何文件。 您需要两个密钥:您的 **WDGoWars** 密钥(`--key`)和您的 **WiGLE** token (`--wigle-key`,即来自 [wigle.net/account](https://wigle.net/account) 的 "Encoded for use" 字符串)。 ``` .venv/bin/python wigle_to_wdgwars.py --from-wigle \ --wigle-key YOUR_WIGLE_ENCODED_TOKEN \ --key YOUR_WDGWARS_API_KEY \ --chunk-size 10000 ``` 默认情况下,它会拉取您最近的一次上传。使用 `--wigle-latest N` 来 推送最近 N 次上传。这是自动安装的 [timer](#running-on-a-schedule-timer) 用于完全自动化 pipeline 所使用的模式。 ### 仅限过去一周(默认值) 每个上传路径在 分块之前都会对 CSV 应用一个**尾部时间窗口过滤**:`FirstSeen` 超出该窗口的行将被丢弃。 默认值为 `7d`,使用 `--since DURATION` 设置: ``` .venv/bin/python wigle_to_wdgwars.py my-wardrive.wiglecsv.gz \ --key YOUR_WDGWARS_API_KEY --chunk-size 10000 # → [wigle] my-wardrive.wiglecsv.gz: --since 7d kept 1842/204311 rows # (丢弃了 202469 条旧记录,0 条无法解析) ``` 这可以防止 cron job 在每次触发时都重复推送多年的 WiGLE 历史记录。 WDGoWars 已经在服务器端对这些行进行了去重;这种传输只会白白浪费 LOCOSP 的每日配额和 Cloudflare 的每个 IP 预算。该窗口也会应用于 `--from-wigle`,因此过时但庞大的 WiGLE transaction 也不会耗尽配额。 | 标志 | 行为 | | ---- | -------- | | `--since 7d` *(默认)* | 保留 `FirstSeen` 在过去 7 天内的行。 | | `--since 24h` | 过去 24 小时。后缀包括:`s`、`m`、`h`、`d`、`w`。纯整数视为天数。 | | `--since 0` | 禁用该过滤。 | | `--all-time` | 禁用该过滤(具名标志,效果相同)。 | 如果 CSV 的窗口为空(保留的行为零),该工具会**完全跳过上传** 并返回 0(不会发送空 POST)。无论哪种情况,2 行 WiGLE header 都会保留在过滤后的字节。 如果 CSV 没有 `FirstSeen` 列(非典型情况,较旧或自定义的导出), 该过滤会记录此情况并原样传递这些字节。 ## 安装说明 您需要 **Python 3.10 或更高版本**和 `pip`。**不需要** Git — pip 会使用标准库 `urllib` 通过普通 HTTPS 获取 gungnir(唯一的依赖项)。 ### 选项 A — ZIP 下载(不需要 git) 1. 从 [GitHub 仓库](https://github.com/HiroAlleyCat/wigle-to-wdgwars) 获取 ZIP(Code → Download ZIP)并解压。 2. 在解压后的文件夹内: ``` python3 -m venv .venv # required on Bookworm / Homebrew (PEP 668) .venv/bin/pip install -r requirements.txt .venv/bin/python wigle_to_wdgwars.py --help ``` ### 选项 B — 使用 git clone ``` git clone https://github.com/HiroAlleyCat/wigle-to-wdgwars.git cd wigle-to-wdgwars python3 -m venv .venv # required on Bookworm / Homebrew (PEP 668) .venv/bin/pip install -r requirements.txt .venv/bin/python wigle_to_wdgwars.py --help ``` ### Windows 它在 Windows 上的运行方式完全相同 — 它是纯 Python,没有任何 Linux 独有的 内容。 1. 从 [python.org](https://www.python.org/downloads/) 安装 Python 3.10+,并在安装程序中 **勾选 "Add python.exe to PATH"**。(或者从 Microsoft Store 获取。) 2. 将仓库下载并解压到一个文件夹中,例如 `C:\Tools\wigle-to-wdgwars\` (如果您有 git,也可以 `git clone`)。 3. 在该文件夹中打开 PowerShell 或命令提示符,并使用 `python`(不是 `python3`): ``` python -m pip install -r requirements.txt python wigle_to_wdgwars.py --whoami --key YOUR_API_KEY_HERE python wigle_to_wdgwars.py my-wardrive.wiglecsv.gz --key YOUR_API_KEY_HERE --chunk-size 10000 ``` 若要在 Windows 上实现全自动的计划推送,请参阅 [按计划运行 → Windows](#windows--task-scheduler)。 ### 更新 最简单的方法是使用 `--update`,它会为您执行这两步操作: ``` ./run.sh --update ``` 如果这是一个 git 检出,它会运行 `git pull --ff-only`,否则会 从 raw GitHub 原子地获取 `wigle_to_wdgwars.py` 和 `requirements.txt`。无论哪种方式,它随后都会刷新 venv 依赖,因此如果某个版本 更新了 gungnir 版本锁定,它可以自我修复,您无需记住 第二步操作。 如果您更愿意手动进行(旧版本曾指示您 这样做): ``` git pull # or: re-download the ZIP and overwrite the folder .venv/bin/pip install --upgrade -r requirements.txt ``` 如果您在依赖项升级的版本中跳过了第二步,您最终会 在新代码中导入旧的 gungnir 字节,这很容易导致微妙的 一致性 bug。 ### API key 的读取位置(按顺序) **WDGoWars**(`--key` / `$WDGWARS_API_KEY` / `wdgwars.key`): 1. 命令行上的 `--key YOUR_KEY`。 2. `$WDGWARS_API_KEY` 环境变量。 3. `~/.config/wigle-to-wdgwars/wdgwars.key`(权限模式 600)。 **WiGLE**(`--wigle-key` / `$WIGLE_API_KEY` / `wigle.key`,由 `--from-wigle` 使用): 1. 命令行上的 `--wigle-key YOUR_TOKEN`。 2. `$WIGLE_API_KEY` 环境变量。 3. `~/.config/wigle-to-wdgwars/wigle.key`(权限模式 600)。 `--setup` 会将两者作为文件为您保存。若要以非交互方式保存它们(以便于 从脚本进行配置): ``` .venv/bin/python wigle_to_wdgwars.py --save-key YOUR_WDGWARS_KEY .venv/bin/python wigle_to_wdgwars.py --save-wigle-key YOUR_WIGLE_TOKEN ``` 该脚本还会在 `~/.config/wigle-to-wdgwars/` 中写入两个状态文件: | 文件 | 用途 | |---|---| | `cooldown.json` | 持久化的服务器冷却截止时间。由 429 响应设置,因此一小时后的计划运行仍会遵守它。 | | `hwm.json` | 高水位线 — 上次成功上传的时间戳和导入计数,用于监控。纯只读输出。 | ## 首先获取 WiGLE CSV 文件 如果您已经在使用 WiGLE Android 应用程序进行 wardrive,请跳至 [选项 A](#option-a--wigle-android-app)。否则,以下是几种最 常见的途径。 ### 选项 A — WiGLE Android 应用 最简单的入门方式。从 Play Store 安装 [WiGLE WiFi Wardriving](https://play.google.com/store/apps/details?id=net.wigle.wigleandroid) ,授予其位置 + 蓝牙权限,并进行一次运行。 之后,您可以通过以下任一方式将导出文件提供给该工具: - **Database → Export to CSV** 会为您提供一个普通的 `WigleWifi_yyyyMMddHHmmss.csv`。 - **share / upload** 流程会为您提供一个经过 gzip 压缩的 `*.wiglecsv.gz`(单一 压缩文件,有时没有内部文件扩展名)。 您**不需要**手动解压 `.gz`。该工具会检测 gzip 并 为您解压,因此只需将其指向您拥有的任何文件即可: ``` # 纯 CSV ./run.sh WigleWifi_20260523120000.csv --chunk-size 10000 # gzip 压缩的导出文件也可以直接使用 — 无需解压 ./run.sh my-run.wiglecsv.gz --chunk-size 10000 ``` 如果您想包含 BLE,请确保在驱动前已在设置中启用了 WiGLE 的蓝牙扫描。 ### 选项 B — Kismet + `kismetdb_to_wiglecsv` 如果您已经在使用 [Kismet](https://www.kismetwireless.net/) 进行捕获,其官方转换工具已经内置在其中: ``` kismetdb_to_wiglecsv \ --in /var/log/kismet/Kismet-20260523.kismet \ --out wardrive.csv ./run.sh wardrive.csv --chunk-size 10000 ``` ### 选项 C — hcxdumptool + `hcxpcapngtool` 如果您运行 [hcxdumptool](https://github.com/ZerBea/hcxdumptool),请将 pcapng 通过 `hcxpcapngtool --csv=...` 管道传递: ``` hcxpcapngtool --csv=wardrive.csv capture.pcapng ./run.sh wardrive.csv --chunk-size 10000 ``` ### 选项 D — 自行构建 WiGLE-1.6 CSV 格式由两行 header 及其后的数据行组成。 各列分别是: ``` MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type ``` `Type` 是 `WIFI`、`BLE` 或 `GSM`(WDGoWars 仅支持 WIFI/BLE)。 第一行 header 是 WiGLE 写入的元注释;该工具 在分块时会保留这两行 header。 最小示例: ``` WigleWifi-1.6,appRelease=v0.0.0 MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type aa:bb:cc:dd:ee:ff,ExampleSSID,[WPA2-PSK-CCMP][ESS],2026-05-23 12:00:00,6,-55,41.0,-81.0,200,10,WIFI ``` ## 按计划运行(定时器) 排行榜的意义在于坚持出现。与其每次都手动推送, 不如设置一个定时器然后忘记它。 **最快的途径 — 让工具为您安装定时器。** `--schedule` 会为您的操作系统写入 正确的配置(在带有 systemd 的 Linux 上为 systemd user unit,在 Mac / 不带 systemd 的 Linux 上为 cron 条目,在 Windows 上为计划任务)。默认值为 每天 03:00 携带 `--chunk-size 10000` 运行 `--from-wigle`,第一次会以试运行模式运行,以便第一次触发时仅解码和记录日志,而不执行 POST。 ``` .venv/bin/python wigle_to_wdgwars.py --schedule # interactive .venv/bin/python wigle_to_wdgwars.py --schedule \ --schedule-time 03:00 --schedule-chunk-size 10000 \ --schedule-dry-run # headless .venv/bin/python wigle_to_wdgwars.py --unschedule # remove later ``` 交互模式会在安装前预览确切的 unit/cron-line/schtasks 命令, 并在最后询问一次“是否立即安装?”以进行确认。重新运行 `--schedule` 并对试运行提示回答 "no",即可从试运行切换为正式上传。 如果您更愿意自己编写 unit / cron 条目 / 计划任务,下面 手动编写的方案仍然有效,并且它们都会一直得到支持。它们比 `--schedule` 自动安装程序为您提供 更精细的控制(文件监视模式、自定义间隔、多重驱动)。 **真正解放双手的版本:** 使用 `--from-wigle`(请参阅 [完全不需要文件](#no-file-at-all--pull-straight-from-wigle))。定时器会拉取 您最新的 WiGLE 上传并将其推送到 WDGoWars,完全不需要涉及 任何文件。将下面任何方案中的命令替换为: ``` ./run.sh --from-wigle --wigle-key WIGLE_TOKEN --key WDGWARS_KEY --chunk-size 10000 ``` **基于文件的版本:** 始终将您的 WiGLE 文件导出(或保存)到 *相同的路径* — 例如 `wardrive.wiglecsv.gz` — 然后将定时器指向该路径。 每次运行都会重新推送该文件;WDGoWars 会在服务器端进行去重,因此重新发送 相同的数据是无害的,并且仍然会拾取任何新行或合并的位置 样本。请在下面选择适合您操作系统的方案。 ### Windows — Task Scheduler 如果您使用手机进行 wardrive 并将导出文件复制到 PC,这是最简单的。保存一个 微型批处理文件,然后将 Task Scheduler 指向它。 `push-wardrive.bat`(编辑路径并在 `--key` 后粘贴您的密钥): ``` @echo off python "C:\Tools\wigle-to-wdgwars\wigle_to_wdgwars.py" "C:\Wardrives\wardrive.wiglecsv.gz" --key YOUR_API_KEY_HERE --chunk-size 10000 >> "C:\Wardrives\push.log" 2>&1 ``` 创建定时器(在**管理员**权限的 PowerShell 或命令提示符中运行一次 — 这会 使其每天凌晨 3 点触发): ``` schtasks /Create /F /TN "WDGoWars Push" /TR "C:\Wardrives\push-wardrive.bat" /SC DAILY /ST 03:00 ``` (`/F` 允许您以后再次运行同一行以更改时间,而不会出现 覆盖提示。) 要更改时间,请使用新的 `/ST` 再次运行相同的 `schtasks /Create`,或者 在 Task Scheduler GUI 中编辑它(在开始菜单中搜索 “Task Scheduler” → 找到 “WDGoWars Push”)。 ### cron (Linux / Mac) — 每 6 小时推送一次 ``` # m h dom mon dow command 0 */6 * * * /usr/bin/env python3 /home/me/bin/wigle_to_wdgwars.py /home/me/wardrives/latest.csv --chunk-size 10000 >> /home/me/wardrives/push.log 2>&1 ``` 将其指向您保持更新的任何文件(`.csv` 或 `.gz` 均可)。该 工具会将冷却状态持久化到 `~/.config/wigle-to-wdgwars/cooldown.json`,因此 连续触发却遇到 429 错误的 job 不会 hammer 服务器。 ### systemd 定时器 — 每天 03:00 `~/.config/systemd/user/wdgwars-push.service`: ``` [Unit] Description=Push wardrive CSV to WDGoWars [Service] Type=oneshot ExecStart=/usr/bin/env python3 %h/bin/wigle_to_wdgwars.py %h/wardrives/latest.csv --chunk-size 10000 ``` `~/.config/systemd/user/wdgwars-push.timer`: ``` [Unit] Description=Daily WDGoWars push [Timer] OnCalendar=*-*-* 03:00:00 Persistent=true [Install] WantedBy=timers.target ``` 启用: ``` systemctl --user daemon-reload systemctl --user enable --now wdgwars-push.timer ``` ### 预检 将推送包裹在 `--whoami` 检查中,这样在您尝试进行长时间上传之前,如果遇到错误/过期的密钥,就会 醒目地报错: ``` #!/bin/sh set -e ./run.sh --whoami > /dev/null exec ./run.sh /home/me/wardrives/latest.csv --chunk-size 10000 ``` ### 解析器预览 在将 CSV 路径绑定到计划任务之前(或者推送您 刚从 Kismet / hcxdumptool 拿到的大型文件之前),值得确认一下解析器 看到的是否是您期望的结果。`--preview` 可以在没有任何网络调用的情况下 完成这项工作: ``` ./run.sh --preview /path/to/your.wiglecsv ``` 将前 6 行数据作为 JSON 打印到 stdout,不需要上传,也不需要密钥。 与 Heimdall 和 Muninn 的 `--preview` 形状相同,因此 思维模型可以在各个 feeder 之间通用。 ### 指向 Staging 主机 `--api-url` 会覆盖 CSV 上传 endpoint。当您需要 针对本地 mock 或 staging 服务器进行测试,而无需更改 `/etc/hosts` 时非常有用: ``` ./run.sh --api-url http://localhost:9999/api/upload-csv \ --dry-run /path/to/your.wiglecsv ``` Aircraft JSON 上传仍然使用未更改的已签名 `/endpoint/upload/` endpoint — 如果您需要重定向这些上传,请使用 Muninn 的 `--api-url`。 ## WDGoWars API 参考 ### Endpoints | 方法 | 路径 | 用途 | 认证 | Body | |---|---|---|---|---| | `GET` | `/api/me` | 验证密钥,读取统计信息/徽章/帮派 | `X-API-Key: ` | — | | `POST` | `/api/upload-csv` | 批量 Wi-Fi/BLE 摄取 | `X-API-Key: ` | `multipart/form-data`,字段 `file=` (WiGLE-1.6 CSV) | | `POST` | `/api/upload/` | 已签名的 JSON 摄取 (aircraft, mesh, …) | `X-API-Key: ` | `application/json` 信封,见下文 | **认证 header 是 `X-API-Key`。** `Authorization: Bearer …` 会被拒绝。 ### `GET /api/me` 响应 ``` { "ok": true, "username": "your_handle", "gang": "Your Gang", "gang_id": 1, "country": "US", "joined": "2026-01-01", "wifi": 1234, "ble": 5678, "aircraft": 0, "mesh": 0, "cracked": 0, "total": 6912, "recent_today": 100, "recent_7d": 900, "badges": ["first_blood", "gang_member", "wifi_100", "wifi_1k", "ble_100", "ble_1k"], "credits": {"balance": 0, "lifetime_earned": 0} } ``` ### `POST /api/upload-csv` 响应 ``` { "ok": true, "imported": 701, "captured": 1, "updated": 0, "duplicates": 56673, "no_gps": 0, "bad_rows": 3, "cooldown": 0, "merged_samples": 156, "total": 48421278 } ``` - `imported` — 接受到用户账户中的新指纹。 - `captured` — 新标记为“首次捕获”的胜利(罕见)。 - `duplicates` — 服务器已经从该用户处看到过的行。 - `no_gps` — 因为缺少经纬度而被跳过的行。 - `bad_rows` — 解析器拒绝的格式错误行。 - `merged_samples` — 作为 附加信号样本折叠到现有指纹中的观测数据。 - `total` — **服务器范围**内所有用户的行计数(不是调用者的)。 - `cooldown` — 当非零时,表示服务器要求客户端 在上传前等待的秒数。 ### 速率限制 服务器强制执行**按账户的上传队列**。当一个上传 仍在处理中时,第二个请求会返回 HTTP 429: ``` {"error":"Another upload is already being processed for this account. Please wait for it to finish before starting a new one.","retry_after":20} ``` 该工具将 `retry_after` 持久化到 `~/.config/wigle-to-wdgwars/cooldown.json` 并在下次运行时休眠到截止时间(上限为 15 分钟,以防止 过期的截止时间造成死锁)。 ### Cloudflare 524 陷阱 门户网站背后的源站在**单个请求中同步** 处理每个 CSV。前面的 Cloudflare 有一个 **120 秒的响应超时**。 任何耗时更长的请求都会返回: ``` HTTP 524 — origin_response_timeout ``` 给您的客户端,但**源站仍在继续摄取** — 即使您的客户端报错,您也会看到这些行 计入您的 `/api/me` 统计中。 **缓解措施:** 将 CSV 切分为 ≤10 000 行的块。每个块在 15–35 秒内即可轻松处理完毕,远低于上限。该工具会自动通过 `--chunk-size 100` 执行此操作。每个块都会重新发送 WiGLE 的 2 行 header,因此 服务器会将其视为有效文件。 ### 常见错误响应 | HTTP | Body | 含义 | |---|---|---| | 400 | `{"error":"Invalid data format"}` | 很可能您将 CSV POST 到了 `/api/upload`(没有 `-csv` 后缀)。错误的 endpoint,而不是格式错误的文件。 | | 401 | `{"error":"Invalid API key"}` | 密钥错误/过期,或者您使用了 `Authorization: Bearer …` 而不是 `X-API-Key:`。 | | 429 | `{"error":"Another upload is already being processed …","retry_after":N}` | 按账户排队。等待 `retry_after` 秒。 | | 413 | `{"error":"payload-too-large","max_bytes":15728640,"received":N,...}` | Body 超过了 LOCOSP 于 2026-06-05 添加的 15 MB 托管上限。客户端会自动二分有问题的块并重试两半 — 无需额外标志。 | | 524 | (来自 Cloudflare 的 HTML) | 源站超时。切分为更小的块。源站上的行仍在继续摄取中。 | ### WiGLE API(`--from-wigle` 拉取端) `--from-wigle` 从 WiGLE 中读取您自己的上传,然后将其馈送到 上方的 WDGoWars 推送中。WiGLE 端使用 HTTP Basic 认证,并使用来自 [wigle.net/account](https://wigle.net/account) 的**预编码 token**(即 "Encoded for use" 字符串),作为 `Authorization: Basic ` 发送。 | 方法 | 路径 | 用途 | |---|---|---| | `GET` | `/api/v2/file/transactions?pagestart=N&pageend=M` | 列出您的上传,最新优先,每页 100 条。每个结果都有一个 `transid`。 | | `GET` | `/api/v2/file/csv/{transid}` | 将该上传下载为 WiGLE CSV。 | 该工具列出最新的 `--wigle-latest N` 条 transaction,并将每一条下载为 CSV。这镜像了社区工具 [joelkoen/wigledl](https://github.com/joelkoen/wigledl) 所使用的契约。WiGLE 强制执行其自己的 按账户的查询限制,因此在一次运行中拉取您的全部历史记录可能会达到 速率限制 —— 拉取最新的上传(默认行为)可以轻松保持在限制之内。 ## Aircraft JSON 格式(签名 endpoint) 已签名的 `/api/upload/` endpoint 接受一种不同的 payload 形状,用于 aircraft、mesh 以及(未来可能有的)其他观测类型。当您有 ADS-B 数据需要推送时,请 使用 `--aircraft-json FILE`。 ### 信封 传输格式将 payload 包装在 HMAC-SHA256 信封中: ``` { "data": "", "nonce": "", "sig": "" } ``` 作为 `Content-Type: application/json` 发送,并使用与 CSV 路径相同的 `X-API-Key` header。 ### Payload 内部的 payload(base64 编码前)为: ``` { "networks": [], "aircraft": [ {}, {}, ... ], "meshcore_nodes": [] } ``` `networks` 和 `meshcore_nodes` 目前会被此工具以空值传递 — Wi-Fi/BLE 会通过 CSV 路径传输,因为服务器端的去重和合并 行为更好。 ### Aircraft 记录 schema ``` { "icao": "A12345", "callsign": "UAL123", "lat": 41.4712, "lon": -81.7887, "alt_ft": 35000, "speed_kt": 450, "heading": 270, "first_seen": "2026-05-23 12:00:00", "type": "ADSB" } ``` `icao` 和 (`lat`, `lon`) 中的至少一个是必需的。`first_seen` 应 为 UTC 格式的 `YYYY-MM-DD HH:MM:SS`。允许缺失字段;格式错误的字段 会被静默置零。 ### 输入文件 传递一个包含这些记录 dict 的顶层**列表**的 JSON 文件: ``` ./run.sh --aircraft-json aircraft.json ``` 如果您希望有一个功能齐全的 ADS-B 上传器,能够自动检测 12 种捕获 格式(dump1090 JSON、SBS-1、Mode-S Beast、GDL-90 等)并为您 生成此 JSON,请改用 **[Muninn (adsb-to-wdgwars)](https://github.com/HiroAlleyCat/adsb-to-wdgwars)**。 该工具的 aircraft 模式适用于您已经 拥有此形状的记录(例如从您自己的 pipeline 导出)的情况。 ### 响应 ``` { "ok": true, "aircraft_imported": 47, "aircraft_already_seen": 1203, "new_badges": ["plane_hunter"] } ``` ## 故障排除 **`{"error":"Invalid data format"}`** — 您使用 CSV 访问了 `/api/upload`(已签名)。CSV endpoint 是 `/api/upload-csv`。该工具默认情况下会使用正确的 endpoint;只有当某些东西重写了 URL 时才会触发此错误。 **`HTTP 401`** — 密钥错误,或者您在某个地方设置了 `Authorization: Bearer …`。 运行 `--whoami` 进行确认。确保您的密钥是来自 WDGoWars 账户页面的完整字符串,没有多余的空格。 **`HTTP 429` 无限重复** — 您之前的上传仍在服务器端排队。 等待 `retry_after` 秒(该工具会在 下次运行时为您执行此操作)。如果是过期的 `cooldown.json` 导致了超过 15 分钟的休眠,请将其 删除:`rm ~/.config/wigle-to-wdgwars/cooldown.json`。 **`HTTP 524`** — Cloudflare 放弃了等待源站。添加或降低 `--chunk-size`(如果在慢速链路上 10000 仍然触发错误,请尝试 5000)。您的 数据无论如何都可能正在摄取中 —— 稍后检查 `--whoami` 的计数。 **`imported: 0, duplicates: `** — 在第二次推送 同一个 CSV 时属于预期行为。WDGoWars 会按指纹进行去重。只有新的 BSSID/SSID(或现有 BSSID/SSID 的新位置)才会被计入。 **`bad_rows: `** — 某些行解析失败。最常见的原因是缺失或 格式错误的 `FirstSeen`,或者是非数字的 `Lat`/`Lon`。请验证: ``` awk -F, 'NR>2 && (length($1)!=17 || $7+0==0) {print NR": "$0}' wardrive.csv ``` **脚本在某个块上卡住几分钟** — 源站正在处理一个 大块数据。该工具的 urlopen 超时时间为 600 秒。如果您想中止 并让源站在后台完成处理,请按 Ctrl-C 并在 30–60 秒后检查 `--whoami`。 ## 相关工具 wardrive + WDGoWars 生态系统中的上传器: | 工具 | 平台 | 路径 | 仓库 | |---|---|---|---| | **wigle-to-wdgwars** (本工具) | Linux/Mac/Win (Python) | Wi-Fi + BLE CSV, aircraft JSON | (本仓库) | | **Muninn (adsb-to-wdgwars)** | Linux/Mac/Win (Python) + 浏览器 | ADS-B aircraft, 12 种捕获格式 | https://github.com/HiroAlleyCat/adsb-to-wdgwars | | **Piglet** | Arduino / RP2040 | 来自设备端捕获的 Wi-Fi | https://github.com/Hamspiced/piglet | | **Raspyjack `wdgwars_upload`** | Bash Bunny / Pi payload | 来自 Raspyjack payload 的 CSV | https://github.com/7h30th3r0n3/Raspyjack | | **pineapple_pager_wdgwars** | Wi-Fi Pineapple | Pineapple 捕获 | https://github.com/LOCOSP/pineapple_pager_wdgwars | | **M5MonsterC5 / CardputerADV** | M5Stack ESP32 | 设备端捕获 | https://github.com/C5Lab/M5MonsterC5-CardputerADV | 交叉链接: - [WiGLE](https://wigle.net/) — 最初的 wardrive 网络。 - [WiGLE WiFi Wardriving (Android)](https://play.google.com/store/apps/details?id=net.wigle.wigleandroid) — 最简单的捕获套件。 - [Kismet](https://www.kismetwireless.net/) — 开源无线检测器 / 嗅探器 / IDS。 - [hcxdumptool](https://github.com/ZerBea/hcxdumptool) — 用于捕捉握手包的快速 802.11 捕获工具;与 `hcxpcapngtool --csv` 配合使用。 ## 许可证 MIT。使用它,fork 它,提交 PR。 ## 致谢 这里通过逆向工程获得的 API 文档已经与 [相关工具](#related-tools) 表格中的开源上传器进行了交叉核对 — 特别是 `Hamspiced/piglet` 和 `7h30th3r0n3/Raspyjack`。这个 绕过 Cloudflare 524 的分块变通方法已在 社区中有据可查;该工具只是默认将其固化了下来。 WDGoWars 由其社区运营。如果您上传了大量数据,考虑加入一个 帮派,帮助排行榜保持奇妙色彩。
标签:Python, wardriving, Wi-Fi, 数据上传, 无后门, 蓝牙