diff --git a/BOT/match_logs.py b/BOT/match_logs.py index 8269364..9e59720 100644 --- a/BOT/match_logs.py +++ b/BOT/match_logs.py @@ -27,11 +27,11 @@ CREATE TABLE IF NOT EXISTS match_logs ( session_id TEXT PRIMARY KEY, chat_log_json TEXT, battle_log_json TEXT, + event_log_json TEXT, built_unix INTEGER ) """ - def _fmt_time(ms: int) -> str: total_s = int(ms) // 1000 return f"{total_s // 60:02d}:{total_s % 60:02d}" @@ -49,6 +49,17 @@ def _decompress_events(raw_events: Any) -> dict: return raw_events or {} +def build_event_log(game: dict[str, Any]) -> dict[str, Any]: + """Return the raw event slices the website needs for per-unit state.""" + events = _decompress_events(game.get("events", {})) + if not isinstance(events, dict): + return {"kills": [], "damage": []} + return { + "kills": list(events.get("kills") or []), + "damage": list(events.get("damage") or []), + } + + def _strip_tag(tag: str) -> str: s = (tag or "").strip() if len(s) >= 3 and not s[0].isalnum() and not s[-1].isalnum(): @@ -166,19 +177,30 @@ def build_match_logs(game: dict[str, Any]) -> tuple[list[str], list[str]]: async def upsert_match_logs( - db_path, session_id: str, chat_log: list[str], battle_log: list[str] + db_path, + session_id: str, + chat_log: list[str], + battle_log: list[str], + event_log: dict[str, Any] | None = None, ) -> None: import time async with aiosqlite.connect(db_path) as conn: await conn.execute(MATCH_LOGS_SQL) + try: + await conn.execute("ALTER TABLE match_logs ADD COLUMN event_log_json TEXT") + except Exception: + pass await conn.execute( - """INSERT INTO match_logs (session_id, chat_log_json, battle_log_json, built_unix) - VALUES (?, ?, ?, ?) + """INSERT INTO match_logs (session_id, chat_log_json, battle_log_json, event_log_json, built_unix) + VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET chat_log_json=excluded.chat_log_json, battle_log_json=excluded.battle_log_json, + event_log_json=excluded.event_log_json, built_unix=excluded.built_unix""", (str(session_id), json.dumps(chat_log, ensure_ascii=False), - json.dumps(battle_log, ensure_ascii=False), int(time.time())), + json.dumps(battle_log, ensure_ascii=False), + json.dumps(event_log or {"kills": [], "damage": []}, ensure_ascii=False), + int(time.time())), ) await conn.commit() diff --git a/scripts/backfill_match_logs.py b/scripts/backfill_match_logs.py index 29c7d00..b3f2783 100644 --- a/scripts/backfill_match_logs.py +++ b/scripts/backfill_match_logs.py @@ -13,7 +13,7 @@ ROOT = pathlib.Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT.parent / "SHARED")) -from BOT.match_logs import build_match_logs, upsert_match_logs, MATCH_LOGS_SQL +from BOT.match_logs import build_event_log, build_match_logs, upsert_match_logs, MATCH_LOGS_SQL from BOT.storage import TSS_BATTLES_DB_PATH REPLAYS = pathlib.Path(os.environ["STORAGE_VOL_PATH"]) / "REPLAYS" / "TSS" @@ -36,10 +36,11 @@ async def main(dry_run: bool) -> None: print(f" skip {sid}: {exc}") continue chat, battle = build_match_logs(game) + event_log = build_event_log(game) if dry_run: - print(f" {sid}: chat={len(chat)} battle={len(battle)}") + print(f" {sid}: chat={len(chat)} battle={len(battle)} kills={len(event_log.get('kills') or [])}") else: - await upsert_match_logs(TSS_BATTLES_DB_PATH, sid, chat, battle) + await upsert_match_logs(TSS_BATTLES_DB_PATH, sid, chat, battle, event_log) done += 1 print(f"{'Would backfill' if dry_run else 'Backfilled'} " f"{len(files) if dry_run else done} sessions") diff --git a/tests/test_match_logs.py b/tests/test_match_logs.py index 028ec46..95ce7c8 100644 --- a/tests/test_match_logs.py +++ b/tests/test_match_logs.py @@ -4,7 +4,7 @@ import pathlib sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1])) sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2] / "SHARED")) -from BOT.match_logs import build_match_logs +from BOT.match_logs import build_event_log, build_match_logs def _game(): @@ -53,8 +53,15 @@ def test_events_base85_zstd_compressed(): assert battle and "destroyed" in battle[0] +def test_event_log_preserves_kill_victim_unit(): + event_log = build_event_log(_game()) + assert event_log["kills"][0]["offended_uid"] == 2 + assert event_log["kills"][0]["offended_unit"] == "germ_pz_iv" + + if __name__ == "__main__": test_chat_log_format() test_battle_log_kill_prefix_and_text() test_events_base85_zstd_compressed() + test_event_log_preserves_kill_victim_unit() print("ALL TESTS PASSED") diff --git a/tss_ws.py b/tss_ws.py index 137643c..3dc2020 100644 --- a/tss_ws.py +++ b/tss_ws.py @@ -164,10 +164,10 @@ async def _handle_game(game: Dict[str, Any]) -> None: await insert_match(game) await insert_player_games(game) await upsert_tss_teams(game) - from BOT.match_logs import build_match_logs, upsert_match_logs + from BOT.match_logs import build_event_log, build_match_logs, upsert_match_logs from BOT.storage import TSS_BATTLES_DB_PATH chat_log, battle_log = build_match_logs(game) - await upsert_match_logs(TSS_BATTLES_DB_PATH, sid, chat_log, battle_log) + await upsert_match_logs(TSS_BATTLES_DB_PATH, sid, chat_log, battle_log, build_event_log(game)) log.info("Stored game %s in DB", sid) except Exception as exc: log.error("DB insert failed for %s: %s", sid, exc)