Add PC post-match player pages with opt-in public OSS sync.
Generate /players/{account_id}[/{match_id}] locally after POST_GAME via OpenDota; publish to OSS only when public_share is enabled.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+74
-2
@@ -4,8 +4,9 @@ Usage:
|
||||
python serve_relations.py
|
||||
python serve_relations.py --port 8765
|
||||
|
||||
Hero relations, rankings, streamers, mechanics, items, patches — read-only browser UI.
|
||||
Edit data/*.json directly, then refresh the page.
|
||||
Hero relations, rankings, streamers, mechanics, items, patches, players —
|
||||
read-only browser UI. Edit data/*.json directly, then refresh the page.
|
||||
Player pages: GET /api/players/{account_id}[/{match_id}] from pc/player_pages/.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -38,6 +39,7 @@ from shared.paths import (
|
||||
HERO_PORTRAITS,
|
||||
ITEM_CAT_ICONS,
|
||||
ITEM_ICONS,
|
||||
PC_PLAYER_PAGES,
|
||||
RANK_ICONS,
|
||||
ROLE_ICONS,
|
||||
ROOT,
|
||||
@@ -943,6 +945,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if path == "/api/mobile-demand":
|
||||
self._json(200, {"count": _read_mobile_demand_count()})
|
||||
return
|
||||
if path.startswith("/api/players/"):
|
||||
self._serve_player_api(path)
|
||||
return
|
||||
if path.startswith("/attr/"):
|
||||
key = path[len("/attr/") :]
|
||||
if key not in ("str.png", "agi.png", "int.png", "all.png"):
|
||||
@@ -1172,6 +1177,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"mechanics",
|
||||
"items",
|
||||
"patches",
|
||||
"players",
|
||||
}
|
||||
first = path.strip("/").split("/", 1)[0] if path.strip("/") else ""
|
||||
if first in spa_pages:
|
||||
@@ -1181,11 +1187,77 @@ class Handler(BaseHTTPRequestHandler):
|
||||
return
|
||||
self.send_error(404)
|
||||
|
||||
def _serve_player_api(self, path: str) -> None:
|
||||
"""GET /api/players/{account_id}[/match_id] from pc/player_pages/."""
|
||||
parts = [p for p in path.strip("/").split("/") if p]
|
||||
# ["api", "players", account_id] or + match_id
|
||||
if len(parts) < 3 or parts[0] != "api" or parts[1] != "players":
|
||||
self._json(400, {"error": "bad players path"})
|
||||
return
|
||||
account_id = parts[2]
|
||||
if not account_id.isdigit():
|
||||
self._json(400, {"error": "bad account_id"})
|
||||
return
|
||||
if len(parts) == 3:
|
||||
fpath = PC_PLAYER_PAGES / account_id / "profile.json"
|
||||
if not fpath.is_file():
|
||||
self._json(404, {"error": "profile not found", "account_id": account_id})
|
||||
return
|
||||
try:
|
||||
self._json(200, json.loads(fpath.read_text(encoding="utf-8")))
|
||||
except (OSError, ValueError) as e:
|
||||
self._json(500, {"error": str(e)})
|
||||
return
|
||||
if len(parts) == 4:
|
||||
match_id = parts[3]
|
||||
if not match_id.isdigit():
|
||||
self._json(400, {"error": "bad match_id"})
|
||||
return
|
||||
fpath = PC_PLAYER_PAGES / account_id / "matches" / f"{match_id}.json"
|
||||
if not fpath.is_file():
|
||||
self._json(
|
||||
404,
|
||||
{
|
||||
"error": "match not found",
|
||||
"account_id": account_id,
|
||||
"match_id": match_id,
|
||||
},
|
||||
)
|
||||
return
|
||||
try:
|
||||
self._json(200, json.loads(fpath.read_text(encoding="utf-8")))
|
||||
except (OSError, ValueError) as e:
|
||||
self._json(500, {"error": str(e)})
|
||||
return
|
||||
self._json(400, {"error": "bad players path"})
|
||||
|
||||
def do_POST(self) -> None: # noqa: N802
|
||||
path = urlparse(self.path).path
|
||||
if path == "/api/mobile-demand":
|
||||
self._json(200, {"count": _inc_mobile_demand_count(), "voted": True})
|
||||
return
|
||||
if path == "/api/players/publish":
|
||||
# Local dev: no OSS write; PC already wrote pc/player_pages/.
|
||||
length = int(self.headers.get("Content-Length", 0) or 0)
|
||||
raw = self.rfile.read(length) if length else b"{}"
|
||||
try:
|
||||
body = json.loads(raw.decode("utf-8"))
|
||||
except (ValueError, UnicodeDecodeError):
|
||||
self._json(400, {"error": "invalid json"})
|
||||
return
|
||||
account_id = body.get("account_id")
|
||||
match_id = body.get("match_id")
|
||||
self._json(
|
||||
200,
|
||||
{
|
||||
"ok": True,
|
||||
"local": True,
|
||||
"message": "local publish no-op; files under pc/player_pages/",
|
||||
"account_id": account_id,
|
||||
"match_id": match_id,
|
||||
},
|
||||
)
|
||||
return
|
||||
self.send_error(404)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user