- radar_fetcher.py:呼叫 Function Server 抓 twpkinfo.com 資料後寫入 Redis (TTL 300s) - pokemon_radar_api.py:FastAPI /scan,從 Redis 讀取並依 request 即時二次篩選;Redis 不可用時回 200 空結果,不再 500 - pokemon_location_fetcher.py:舊版 CLI,仍由 fetch_and_upload.sh 使用 - 文件:README (參考手冊)、QUICKSTART (操作指南)、SUMMARY (故事線)、CLAUDE.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
4.5 KiB
Markdown
71 lines
4.5 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
|
||
## 常用指令
|
||
|
||
套件管理使用 `uv`(見 `pyproject.toml`、`uv.lock`、`.python-version` = 3.12)。所有 Python 進入點都透過 `uv run` 執行。
|
||
|
||
- **啟動 API**:`./run_pokemon_radar_api.sh`(內含 `uv run uvicorn pokemon_radar_api:app --host 0.0.0.0 --port 8008 --log-config logging.ini`)
|
||
- **執行新版 fetcher(寫入 Redis)**:`uv run python radar_fetcher.py`
|
||
- **執行舊版 fetcher(產生本地 JSON/CSV/截圖)**:`uv run python pokemon_location_fetcher.py --script twpk_radar.mjs --lat 25.0478 --lng 121.5170 --zoom 16 --state state.json --ua-from-state --min-remaining 300 --min-iv 90`
|
||
- **檢視 Redis 快取內容**:`uv run python reader.py`(rich 表格)或 `uv run python check_redis.py`(raw JSON dump)
|
||
- **生產環境排程腳本**:`./fetch_and_upload.sh`(使用 `flock` 鎖定 `/var/lock/poke_radar.lock`;呼叫**舊版** `pokemon_location_fetcher.py`,然後 `scp` 到 `192.168.42.104:/opt/caddy/www/pokemon/spots.json`)
|
||
|
||
本專案沒有測試、linter、build 流程。
|
||
|
||
## 架構
|
||
|
||
這是一個兩階段的 pipeline,被一個 FastAPI 即時查詢層所包裝。有**兩條共存的 fetch pipeline**,修改時務必了解各自的用途:
|
||
|
||
### 資料流
|
||
|
||
```
|
||
twpkinfo.com (ipoke.aspx)
|
||
▲
|
||
│ browser automation (Puppeteer)
|
||
│
|
||
Function Server (192.168.42.124:13000/function)
|
||
▲
|
||
│ POST {code: <injected twpk_radar.mjs>}, 150s timeout
|
||
│
|
||
┌─────┴─────────────────────────────┐
|
||
│ │
|
||
│ (Pipeline A:寫入 Redis) │ (Pipeline B:寫入本地檔)
|
||
│ radar_fetcher.py │ pokemon_location_fetcher.py (CLI)
|
||
│ └─→ Redis SETEX │ └─→ pokemon_data.{json,csv}
|
||
│ pokemon:radar:latest │ + screenshot.png
|
||
│ TTL 300s │
|
||
└─────┬─────────────────────────────┘
|
||
│
|
||
▼
|
||
pokemon_radar_api.py (FastAPI, :8008)
|
||
POST /scan → 即時篩選 Redis 內容回傳
|
||
```
|
||
|
||
### 關鍵設計
|
||
|
||
- **`twpk_radar.mjs` 是 Function Server 上跑的 worker**,不是本地執行的模組。Python 端透過 regex 在字串層級注入變數,這個契約不能壞:
|
||
- `const TARGET = {...};` → 由 `_inject_target_and_shot` 覆寫
|
||
- `const DO_SCREENSHOT = true|false;` → 同上
|
||
- `globalThis.__POKE_STATE__` → 由 `_inject_state` 前置插入(含 cookies/localStorage/UA/langs)
|
||
- 修 MJS 時,上述 3 個錨點的形狀必須維持,否則 regex 會失效
|
||
|
||
- **`pokemon_radar_api.py` 重用舊 client 的 `postprocess` 方法**,但**不**透過它去抓資料:`_filter_helper = PokemonRadarClient(url="", token="")`。Redis 存的是已篩選過一次(由 fetcher)但仍保留較寬原始集的資料,API 再依 request 參數做即時二次篩選(`min_remaining` / `min_iv` / `perfect_only` / `species`)。`ScanRequest` 裡的 `lat/lng/zoom/shot/...` 欄位在 Redis 模式下是**忽略的**,純粹保留給前端/n8n 相容。
|
||
|
||
- **兩個 fetcher 的篩選邏輯重複實作了兩次**(`radar_fetcher._postprocess` ≈ `PokemonRadarClient.postprocess`),修改篩選規則時要同步兩邊。
|
||
|
||
- **`fetch_and_upload.sh` 目前走的是 Pipeline B(本地檔 + scp),不是 Pipeline A(Redis)**。這是生產環境仍在使用的路徑;若改 fetcher 行為要確認影響哪一邊。
|
||
|
||
### 設定
|
||
|
||
- `.env`(本地)驅動 `radar_fetcher.py`、`pokemon_radar_api.py`、`reader.py`、`check_redis.py`。注意 `radar_fetcher.py` 預設 `REDIS_HOST=localhost`,但 API / reader / `.env` 是 `192.168.42.211` — fetcher 跑在 Redis 本機、API 跑遠端。
|
||
- `state.json` 是使用者登入後的 cookies/localStorage/UA 快照;用於繞過 twpkinfo.com 的人機驗證。過期時整條 pipeline 會靜默產 0 筆。`radar_fetcher._postprocess` 有一個診斷:當 `raw_items` 非空但篩完為 0 時會 log remaining/perfect 的分佈,優先看這個。
|
||
- `FUNCTION_TOKEN` 必填(radar_fetcher 會直接 raise),舊 CLI 則有 hard-coded default `6R0W53R135510`。
|
||
|
||
## 專案慣例
|
||
|
||
- 全專案以**繁體中文(台灣用語)**溝通與註解。
|
||
- log 訊息不要使用 emoji;現存檔案中的 emoji 是歷史遺留,新寫或修改時改成純文字。
|
||
- repo 根目錄的 `*_BACKUP_YYYYMMDDHHMMSS` 檔案是舊備份,可以清掉。
|