init: Redis-backed Pokémon 雷達 API

- 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>
This commit is contained in:
2026-04-20 09:13:59 +08:00
commit f7ef105436
18 changed files with 2919 additions and 0 deletions

129
QUICKSTART.md Normal file
View File

@@ -0,0 +1,129 @@
# QUICKSTART
從零把整條 pipeline 跑起來。大約 5 分鐘。
完整規格看 `README.md`,設計背景看 `SUMMARY.md`
## 需求
- [`uv`](https://github.com/astral-sh/uv)Python 套件管理)
- Docker用來跑本機 Redis
- 能連到 Function Server `http://192.168.42.124:13000/function`(內網)
- 有效的 `state.json`twpkinfo.com 登入快照,放 repo 根目錄)
## 1. 起本機 Redis
```bash
docker run -d --name pokemon-radar-redis \
--restart unless-stopped \
-p 6379:6379 \
redis:7-alpine
docker exec pokemon-radar-redis redis-cli PING # 應回 PONG
```
管理指令:`docker stop/start/rm pokemon-radar-redis``docker logs -f pokemon-radar-redis`
## 2. 設定 `.env`
複製或編輯 `.env`,本機跑的最小設定:
```ini
FUNCTION_URL=http://192.168.42.124:13000/function
FUNCTION_TOKEN=6R0W53R135510
SCRIPT_PATH=twpk_radar.mjs
STATE_PATH=state.json
UA_FROM_STATE=true
RADAR_LAT=25.0478
RADAR_LNG=121.5170
RADAR_ZOOM=11
MIN_REMAINING=300
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_DB=0
REDIS_PASSWORD=
REDIS_TTL=300
REDIS_KEY=pokemon:radar:latest
```
連正式 Redis`192.168.42.211`)時把 `REDIS_HOST``REDIS_PASSWORD` 改回去即可。
## 3. 拉一次資料進 Redis
```bash
uv run python radar_fetcher.py
```
成功的 log
```
INFO ENV: RADAR_ZOOM=11, MIN_REMAINING=300, ...
INFO Loaded state: cookies=7, ls=2, ua=N
INFO Calling Function Server: http://192.168.42.124:13000/function
INFO Got 120 results (before filters)
SUCCESS Redis write OK host=127.0.0.1 key=pokemon:radar:latest ttl=300s
```
跑一次約 30-60 秒。
## 4. 檢查 Redis 內容
```bash
uv run python reader.py
```
會列出目前 TTL、總筆數以及 `rich` 表格(名稱 / IV / CP-Lv / 技能 / 剩餘時間 / 座標)。
想看 raw JSON 用 `uv run python check_redis.py`
## 5. 起 API
```bash
./run_pokemon_radar_api.sh
# 或uv run uvicorn pokemon_radar_api:app --host 0.0.0.0 --port 8008 --log-config logging.ini
```
另開一個 shell 測:
```bash
curl -s http://127.0.0.1:8008/ping
# {"status":"ok","backend":"redis"}
curl -s -X POST http://127.0.0.1:8008/scan \
-H "Content-Type: application/json" \
-d '{"lat":25.0478,"lng":121.5170,"min_iv":90}'
```
看到 `"source":"redis"``count_filtered` > 0 就成功。
## 6.(選用)定期自動更新
Redis TTL 預設 300s過期後 API 會回空陣列。要讓資料持續可用,把 fetcher 排成 cron
```cron
*/3 * * * * cd /path/to/pokemon-radar-api && /root/.local/bin/uv run python radar_fetcher.py >> /var/log/radar_fetcher.log 2>&1
```
生產環境另有 `fetch_and_upload.sh`,走的是**舊 pipeline**(本地檔 + scp不經 Redis`SUMMARY.md`
---
## 常見問題
**`FUNCTION_TOKEN 未設定`**
`.env``FUNCTION_TOKEN`
**`Redis 連線失敗: Timeout connecting to server`**
Redis 連不到。確認容器在跑(`docker ps | grep redis`),或 `.env``REDIS_HOST` 是否正確。API 端此時會回 `source: "redis (unreachable)"` 不會 500。
**`Got 0 results``Filtered to 0`**
通常是 `state.json` 過期,網站把你擋下來。重新登入 twpkinfo.com 匯出新的 state。`radar_fetcher` 會印 `remaining_min / remaining_max / perfect` 做診斷;若 `raw_items` 本身就 0就是 state 問題。
**API 回 `source: "redis (empty)"`**
Redis 裡沒資料或 key 已過期。跑 `radar_fetcher.py` 重灌。
**`MJS regex 沒套到`(例如座標沒改)**
表示 `twpk_radar.mjs` 裡的 `const TARGET = {...};``const DO_SCREENSHOT = ...;` 格式被改動。Python 端用 regex 字串替換,形狀要維持。