Skip to content

ZiyaoZh/btd6_api_processor

Repository files navigation

BTD6 API Processor

基于 Ninja Kiwi Open Data API 的 BTD6 数据抓取与整理工具。

本项目适合两类使用场景:

  • 作为命令行工具,定时生成群公告用的 Markdown 或排行榜图片。
  • 作为 Python 模块,被其他项目直接调用,拿到结构化结果后继续加工。

当前覆盖活动:Race、Boss、Odyssey、Daily。 说明:CT 相关链路已在本项目中移除。


1. 功能总览

  • 获取 BTD6 官方 Open Data 原始数据。
  • 输出活动简报(summary)。
  • 输出最新一期详细信息(detail):race / boss / odyssey / daily。
  • 输出排行榜(leaderboard):
    • markdown 文本榜单
    • image 图片榜单(适合社群转发)
  • 支持翻译表(translate.md)做中文化映射。
  • 支持 10 分钟 TTL 缓存,普通查询优先命中缓存,未命中或过期时按需回源。
  • 支持一键全量更新(update)。
  • 支持 collection event 轮换表与图片导出。
  • 支持 collection event 缓存读取与按需回源。

2. 目录结构

说明:所有输出路径都相对于本项目目录解析,不受当前命令执行目录影响。

.
├── btd6_cli.py                # CLI 入口
├── api_raw_fetcher.py         # 原始 API 请求层
├── btd6_core/
│   ├── common.py              # 通用工具:翻译、时间、格式化
│   ├── cache_store.py         # 缓存索引与文件读写
│   ├── summary_service.py     # 简报生成
│   ├── detail_service.py      # 详情生成
│   ├── leaderboard_service.py # 排行榜生成
│   ├── image_renderer.py      # PNG 排行榜渲染
│   └── update_service.py      # 全量更新流程
├── translate.md               # 翻译映射表
├── output/                    # 所有输出根目录(运行时自动创建)
│   ├── cache_index.json       # 缓存索引
│   ├── summary/               # 简报输出
│   ├── race/ boss/ odyssey/ daily/ # 详情与排行榜输出
│   └── collection_event/      # 收集活动缓存输出
├── assets/fonts/              # 图片渲染字体缓存
└── assets/InstaMonkeyIcon/    # collection event 图标资源

3. 环境与安装

3.1 Python 版本

推荐 Python 3.10+。

3.2 安装依赖

本项目核心网络请求使用标准库 urllib,图片输出依赖 Pillow。

pip install pillow

3.3 API Key(可选)

可通过环境变量设置:

export NK_API_KEY="your_api_key"

也可以每次通过 --api-key 传入。


4. 命令行接口(CLI)

入口脚本:btd6_cli.py

python btd6_cli.py --help

4.1 公共参数

  • --api-key:Ninja Kiwi API Key,可选。
  • --translate:翻译表路径,默认 translate.md
  • --output:主输出文件路径,默认 output/btd6_digest.md
  • --mode:模式,可选 summary / detail / leaderboard / collection-event / update
  • --collection-event-output:collection-event 模式的 JSON 输出路径,默认 output/collection_event_schedule.json
  • --collection-event-image-output:collection-event 模式的图片输出路径,默认 output/collection_event_schedule.png
  • --only-upcoming:collection-event 模式仅输出当前和未来轮换。

4.2 summary 模式(按需缓存)

生成当前活动简报(Race/Boss/Odyssey/Daily)。

python btd6_cli.py \
  --mode summary \
  --output output/summary.md

4.3 detail 模式(按需缓存)

生成指定活动类型的“最新一期详细信息”。

参数:

  • --detail-types:逗号分隔,支持 race,boss,odyssey,daily

示例:

python btd6_cli.py \
  --mode detail \
  --detail-types race,boss \
  --output output/detail.md

说明:

  • 若只传一个类型,输出该类型详情。
  • 若传多个类型,主输出会包含每个类型的文件路径和合并内容。

4.4 leaderboard 模式(按需缓存)

生成排行榜(当前实现固定从第一页起拉取,内部按类型取固定人数)。

参数:

  • --leaderboard-typerace / boss-standard / boss-elite
  • --leaderboard-formatmarkdown / image

示例(文本排行榜):

python btd6_cli.py \
  --mode leaderboard \
  --leaderboard-type race \
  --leaderboard-format markdown \
  --output output/race_lb.md

示例(图片排行榜):

python btd6_cli.py \
  --mode leaderboard \
  --leaderboard-type boss-elite \
  --leaderboard-format image \
  --output output/boss_elite_image_result.md

说明:

  • --output 保存的是执行摘要(来源、生成文件路径),真正榜单文件写入活动目录。
  • 图片榜单仅显示:排行、玩家、得分。
  • 图片底部左侧显示数据来源,右侧显示数据获取时间(YYYY-MM-DD HH:MM:SS)。

4.5 update 模式(主动刷新)

一键更新所有核心数据:

  • 简报:summary
  • 详情:race / boss / odyssey / daily
  • 排行榜:race / boss-standard / boss-elite(markdown + image)
python btd6_cli.py \
  --mode update \
  --output output/update.md

4.6 collection-event 模式(收集活动轮换)

生成 collection event 的轮换 JSON 与图片。会优先读取缓存中的 JSON 和图片;如果缓存不存在或已过期,才会现场生成并写入缓存。图片图标默认读取 assets/InstaMonkeyIcon/,字体优先使用 assets/fonts/Gardenia-Bold.woff2,其次会按 assets/fonts/ 下的其他本地字体回退。

python btd6_cli.py \
  --mode collection-event \
  --collection-event-output output/collection_event_schedule.json \
  --collection-event-image-output output/collection_event_schedule.png

说明:

  • --only-upcoming 仅保留当前和未来轮换。
  • 缓存 TTL 为 10 分钟,超过后会自动重新获取并覆盖缓存。

5. 输出文件协议

5.1 详情输出

  • output/race/{event_id}_detail.md
  • output/boss/{event_id}_detail.md
  • output/odyssey/{event_id}_detail.md
  • output/daily/{event_id}_detail.md

5.2 简报输出

  • output/summary/latest_summary.md

5.3 排行榜输出

Markdown:

  • output/race/{event_id}_top100.md
  • output/boss/{event_id}_standard_top150.md
  • output/boss/{event_id}_elite_top150.md

Image:

  • output/race/{event_id}_top100.png
  • output/boss/{event_id}_standard_top150.png
  • output/boss/{event_id}_elite_top150.png

5.4 固定人数策略(image)

  • Race:固定前 100 人(取前 2 页并截断)。
  • Boss:固定前 150 人(尝试前 6 页并截断)。

5.5 collection-event 缓存输出

  • output/collection_event/latest_collection_event.json
  • output/collection_event/latest_collection_event.png
  • output/collection_event/latest_collection_event.upcoming.json
  • output/collection_event/latest_collection_event.upcoming.png

说明:

  • collection-event 模式优先读取对应缓存,缓存缺失或过期时会重新生成并写入。

6. 缓存机制

缓存索引文件:output/cache_index.json

索引结构示例:

{
  "items": {
    "detail:race": {
      "id": "race_event_id",
      "path": "output/race/race_event_id_detail.md",
      "updatedAt": "2026-03-24T12:34:56+08:00",
      "updatedAtEpoch": 1774326896,
      "ttlSeconds": 600
    },
    "leaderboard:boss-elite:image-fixed": {
      "id": "boss_event_id",
      "path": "output/boss/boss_event_id_elite_top150.png",
      "updatedAt": "2026-03-24T12:35:10+08:00",
      "updatedAtEpoch": 1774326910,
      "ttlSeconds": 600
    }
  }
}

读取逻辑:

  • summary / detail / leaderboard / collection-event 模式优先读取缓存文件。
  • 缓存不存在或超过 10 分钟 TTL 时,会自动请求远程并覆盖缓存。
  • 如果远程请求失败且本地仍有旧缓存,会自动回退到旧缓存继续服务。
  • update 会强制回源并刷新全部缓存。

7. 对外可复用的 Python 接口

下面接口可直接在其他 Python 项目中导入调用。

7.1 原始请求层

文件:api_raw_fetcher.py

  • ApiClient(api_key: str | None = None, timeout: int = 45, retries: int = 2)
  • ApiClient.get(path_or_url: str) -> dict
  • fetch_raw_data(client: ApiClient) -> dict

说明:

  • get 支持传完整 URL 或相对路径。
  • 内置重试与指数退避。
  • 超时和 HTTP 错误会被格式化成更清晰的错误文本,例如 读取超时(45s)HTTP 503 Service Unavailable
  • 统一期望 Open Data 返回格式:{ success, error, body }

示例:

from api_raw_fetcher import ApiClient, fetch_raw_data

client = ApiClient(api_key=None)
raw = fetch_raw_data(client)
print(raw.keys())  # dict_keys(['races', 'bosses', 'odyssey', 'daily'])

7.2 简报服务

文件:btd6_core/summary_service.py

  • build_report(client, trans) -> str
  • resolve_summary(client, trans, refresh=False) -> tuple[path, content, cached]

说明:

  • refresh=False 时优先读缓存;缓存不存在或过期时会主动请求远程。
  • 如果远程请求失败且本地仍有旧缓存,会自动回退到旧缓存。

示例:

from pathlib import Path
from api_raw_fetcher import ApiClient
from btd6_core.common import parse_translation_tables
from btd6_core.summary_service import build_report

client = ApiClient()
trans = parse_translation_tables(Path("translate.md"))
report = build_report(client, trans)

7.3 详情服务

文件:btd6_core/detail_service.py

  • resolve_detail(client, trans, detail_type, refresh=False) -> tuple[path, content, cached]

参数:

  • detail_typerace / boss / odyssey / daily

返回:

  • path:详情文件路径
  • content:详情文本
  • cached:是否来自缓存

说明:

  • refresh=False 时优先读缓存;缓存不存在或过期时会主动请求远程。
  • 如果远程请求失败且本地仍有旧缓存,会自动回退到旧缓存。

7.4 排行榜服务

文件:btd6_core/leaderboard_service.py

  • resolve_leaderboard(client, leaderboard_type, page, output_format="markdown", refresh=False) -> tuple[path, content, cached]

参数:

  • leaderboard_typerace / boss-standard / boss-elite
  • page:保留参数,当前内部统一按固定策略处理
  • output_formatmarkdown / image

返回:

  • path:榜单文件路径(md 或 png)
  • content:markdown 内容(image 时为空字符串)
  • cached:是否来自缓存

说明:

  • refresh=False 时优先读缓存;缓存不存在或过期时会主动请求远程。
  • 如果远程请求失败且本地仍有旧缓存,会自动回退到旧缓存。

7.5 更新服务

文件:btd6_core/update_service.py

  • update_all_data(client, trans) -> str

用于批量刷新 summary、所有详情、排行榜和 collection event 缓存。 如果官方 API 刷新失败但本地仍有旧缓存,结果文本会明确标记为“已回退旧缓存”。


8. 翻译表规范(translate.md)

解析逻辑在 btd6_core/common.pyparse_translation_tables

支持分类标题:

  • 难度等级
  • 英雄
  • 猴塔
  • 地图
  • 地图类型
  • 游戏模式
  • BOSS气球

每个分类使用 Markdown 表格,至少两列(英文、中文)。

示例:

## 4. 地图
| 英文 | 中文 |
| --- | --- |
| MonkeyMeadow | 猴子草地 |
| TreeStump | 树桩 |

9. 图片排行榜渲染说明

渲染模块:btd6_core/image_renderer.py

  • collection event 绘图优先使用本地 assets/fonts/*.woff2 字体。
  • 竞速图布局:4 列 × 25 行。
  • Boss 图布局:3 列 × 50 行。
  • 每行字段:排行、玩家、得分。
  • 使用斑马纹提高可读性。
  • 底部页脚:左侧 数据来源: btd6 open data,右侧 数据获取时间: YYYY-MM-DD HH:MM:SS

10. 典型接入方式

10.1 作为定时任务

业务侧可以直接调用查询命令,缓存有效期为 10 分钟;如果希望提前预热,也可以单独跑一次 update

python btd6_cli.py --mode update --output output/update.md
python btd6_cli.py --mode summary --output output/summary.md
python btd6_cli.py --mode leaderboard --leaderboard-type race --leaderboard-format image --output output/race_image.md

你的系统只需读取 output/*.mdoutput/ 下活动目录生成的 *.png 即可。

10.2 在已有 Python 服务中调用

from pathlib import Path
from api_raw_fetcher import ApiClient
from btd6_core.common import parse_translation_tables
from btd6_core.detail_service import resolve_detail

client = ApiClient()
trans = parse_translation_tables(Path("translate.md"))
path, content, cached = resolve_detail(client, trans, "boss", refresh=False)

payload = {
    "path": str(path),
    "cached": cached,
    "preview": content[:200],
}

11. 错误处理建议

  • 网络波动:已内置重试与退避;若业务侧严格 SLA,建议外层再包一层任务重试。
  • 缓存文件缺失或过期:查询命令会自动回源并刷新缓存。
  • API 失败但存在旧缓存:查询命令会自动回退旧缓存,不会直接中断。
  • API 失败:ApiClient.get 会抛出 RuntimeError,上层应记录日志并告警。

12. 数据来源与免责声明

  • 数据来源:Ninja Kiwi Open Data API
  • 本项目为数据整理与展示工具,不隶属于 Ninja Kiwi 官方。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages