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, 数据上传, 无后门, 蓝牙