Overlord sees all

This commit is contained in:
erik 2025-04-29 22:23:34 +00:00
commit a2089efa02
7 changed files with 724 additions and 0 deletions

108
main.py Normal file
View file

@ -0,0 +1,108 @@
from datetime import datetime, timedelta, timezone
import json
import sqlite3
from typing import Dict
from fastapi import FastAPI, Header, HTTPException
from fastapi.responses import JSONResponse
from fastapi.routing import APIRoute
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from typing import Optional
from db import init_db, save_snapshot, DB_FILE
# ------------------------------------------------------------------
app = FastAPI()
# In-memory store of the last packet per character
live_snapshots: Dict[str, dict] = {}
SHARED_SECRET = "your_shared_secret"
#LOG_FILE = "telemetry_log.jsonl"
# ------------------------------------------------------------------
ACTIVE_WINDOW = timedelta(seconds=30) # player is “online” if seen in last 30 s
class TelemetrySnapshot(BaseModel):
character_name: str
char_tag: str
session_id: str
timestamp: datetime
ew: float # +E / W
ns: float # +N / S
z: float
kills: int
kills_per_hour: Optional[str] = None # now optional
onlinetime: Optional[str] = None # now optional
deaths: int
rares_found: int
prismatic_taper_count: int
vt_state: str
@app.on_event("startup")
def on_startup():
init_db()
# ------------------------ POST ----------------------------------
@app.post("/position")
@app.post("/position/")
async def receive_snapshot(
snapshot: TelemetrySnapshot,
x_plugin_secret: str = Header(None)
):
if x_plugin_secret != SHARED_SECRET:
raise HTTPException(status_code=401, detail="Unauthorized")
# cache for /live
live_snapshots[snapshot.character_name] = snapshot.dict()
# save in sqlite
save_snapshot(snapshot.dict())
# optional log-file append
#with open(LOG_FILE, "a") as f:
# f.write(json.dumps(snapshot.dict(), default=str) + "\n")
print(f"[{datetime.now()}] {snapshot.character_name} @ NS={snapshot.ns:+.2f}, EW={snapshot.ew:+.2f}")
return {"status": "ok"}
# ------------------------ GET -----------------------------------
@app.get("/debug")
def debug():
return {"status": "OK"}
@app.get("/live")
@app.get("/live/")
def get_live_players():
conn = sqlite3.connect(DB_FILE)
conn.row_factory = sqlite3.Row
rows = conn.execute("SELECT * FROM live_state").fetchall()
conn.close()
# aware cutoff (UTC)
cutoff = datetime.utcnow().replace(tzinfo=timezone.utc) - ACTIVE_WINDOW
players = [
dict(r) for r in rows
if datetime.fromisoformat(
r["timestamp"].replace('Z', '+00:00')
) > cutoff
]
return JSONResponse(content={"players": players})
# -------------------- static frontend ---------------------------
app.mount("/", StaticFiles(directory="static", html=True), name="static")
# list routes for convenience
print("🔍 Registered routes:")
for route in app.routes:
if isinstance(route, APIRoute):
print(f"{route.path} -> {route.methods}")