add SREBOT, SHARED, TSSBOT contents (fixup for #1223)

PR #1223 only staged the deletions of the old paths because the new
top-level directories were still untracked when the commit was authored.
This commit adds the actual restructured tree: SREBOT/ (existing bot),
SHARED/ (vromfs, data_parser, ICONS/MAPS/FONTS, DAGOR_FILES,
update_game_files), and TSSBOT/ (skeleton).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
FURRO404
2026-05-13 23:17:02 -07:00
commit 2b399fdb81
186 changed files with 96596 additions and 0 deletions
+450
View File
@@ -0,0 +1,450 @@
"""
weekly_br_elo.py
Window-scoped player & squadron ELO scoring. Python port of the
computePerformanceScore + ratingCtes pipeline from server.js, used by the
Weekly BR Report executor.
The percentile distribution (benchmark) is built from the same in-window
dataset, so scores represent ranking against peers who also played that week.
"""
# Standard Library Imports
import logging
import math
from typing import Any, Dict, List, Optional, Tuple
# Third-Party Library Imports
import aiosqlite
# Local Module Imports
from .utils import SQ_BATTLES_DB_PATH
# Score weights (mirror server.js:631 computePerformanceScore)
_W_KDR = 0.32
_W_HEAVY_RATE = 0.23
_W_KILLS_PER_GAME = 0.14
_W_GAMES_LOG = 0.10
_W_WIN_RATE = 0.06
_W_ASSISTS_PER_GAME = 0.06
_W_CAPTURES_PER_GAME = 0.04
_W_DEATHS_PER_GAME = 0.05 # lower-is-better
_METRIC_KEYS: Tuple[str, ...] = (
"kdr",
"kills_per_game",
"heavy_rate",
"win_rate",
"assists_per_game",
"captures_per_game",
"deaths_per_game",
"games_log",
)
def _rating_ctes(entity_expr: str) -> str:
"""Return the WITH-clause CTEs that produce per-session heavy scores.
Direct port of server.js:647 ratingCtes(). The window predicate uses
`endtime_unix BETWEEN ? AND ?` -- params order: (start_ts, end_ts).
"""
return f"""
WITH player_sessions AS (
SELECT
{entity_expr} AS entity_key,
p.UID,
p.session_id,
SUM(p.ground_kills + p.air_kills) AS kills,
SUM(p.assists) AS assists,
SUM(p.captures) AS captures,
SUM(p.deaths) AS deaths,
MAX(CASE WHEN UPPER(p.victor_bool) = 'WIN' THEN 1 ELSE 0 END) AS win
FROM player_games_hist p
WHERE p.UID IS NOT NULL
AND p.nick NOT LIKE 'coop/%'
AND p.endtime_unix >= ?
AND p.endtime_unix <= ?
GROUP BY entity_key, p.UID, p.session_id
),
leader_stats AS (
SELECT ps.session_id, MAX(ps.kills) AS max_kills
FROM player_sessions ps
GROUP BY ps.session_id
),
leader_counts AS (
SELECT ps.session_id, COUNT(*) AS top_count
FROM player_sessions ps
JOIN leader_stats ls ON ls.session_id = ps.session_id
AND ls.max_kills = ps.kills
GROUP BY ps.session_id
),
second_stats AS (
SELECT ps.session_id, MAX(ps.kills) AS second_kills
FROM player_sessions ps
JOIN leader_stats ls ON ls.session_id = ps.session_id
WHERE ps.kills < ls.max_kills
GROUP BY ps.session_id
),
scored_sessions AS (
SELECT
ps.*,
CASE
WHEN ps.kills >= 3
AND ps.kills = ls.max_kills
AND lc.top_count = 1
AND (ps.kills - COALESCE(ss.second_kills, 0)) >= 2
THEN MIN(1.0, (ps.kills - 2) / 2.0)
WHEN ps.kills >= 3
AND ps.kills = ls.max_kills
AND lc.top_count = 1
THEN 0.6 * MIN(1.0, (ps.kills - 2) / 2.0)
ELSE 0
END AS heavy_score
FROM player_sessions ps
JOIN leader_stats ls ON ls.session_id = ps.session_id
JOIN leader_counts lc ON lc.session_id = ps.session_id
LEFT JOIN second_stats ss ON ss.session_id = ps.session_id
)
"""
async def _fetch_rows(sql: str, params: Tuple[Any, ...]) -> List[Dict[str, Any]]:
"""Run a read-only query against sq_battles.db and return list-of-dicts."""
async with aiosqlite.connect(
f"file:{SQ_BATTLES_DB_PATH}?mode=ro", uri=True, timeout=30.0
) as db:
await db.execute("PRAGMA busy_timeout=30000;")
async with db.execute(sql, params) as cursor:
cols = [c[0] for c in (cursor.description or [])]
rows = await cursor.fetchall()
return [dict(zip(cols, r)) for r in rows]
async def _query_player_aggregates(
start_ts: int, end_ts: int
) -> List[Dict[str, Any]]:
sql = _rating_ctes("p.UID") + """
SELECT
entity_key AS uid,
COUNT(*) AS games,
SUM(kills) AS total_kills,
SUM(assists) AS total_assists,
SUM(captures) AS total_captures,
SUM(deaths) AS total_deaths,
SUM(win) AS wins,
SUM(heavy_score) AS heavy_score
FROM scored_sessions
GROUP BY entity_key
"""
return await _fetch_rows(sql, (start_ts, end_ts))
async def _query_squadron_aggregates(
start_ts: int, end_ts: int
) -> List[Dict[str, Any]]:
sql = _rating_ctes("p.squadron_name") + """,
squadron_sessions AS (
SELECT
entity_key,
session_id,
SUM(kills) AS kills,
SUM(assists) AS assists,
SUM(captures) AS captures,
SUM(deaths) AS deaths,
MAX(win) AS win,
SUM(heavy_score) AS heavy_score
FROM scored_sessions
WHERE entity_key IS NOT NULL
AND entity_key != 'UNKNOWN'
AND entity_key != ''
GROUP BY entity_key, session_id
)
SELECT
entity_key AS squadron_name,
COUNT(*) AS games,
SUM(kills) AS total_kills,
SUM(assists) AS total_assists,
SUM(captures) AS total_captures,
SUM(deaths) AS total_deaths,
SUM(win) AS wins,
SUM(heavy_score) AS heavy_score
FROM squadron_sessions
GROUP BY entity_key
"""
return await _fetch_rows(sql, (start_ts, end_ts))
async def _query_uid_to_meta(
start_ts: int, end_ts: int
) -> Dict[str, Tuple[str, str]]:
"""Map UID -> (most-recent nick, most-recent squadron_name) within window."""
sql = """
SELECT
p.UID AS uid,
p.nick AS nick,
p.squadron_name AS squadron_name,
MAX(p.endtime_unix) AS last_seen
FROM player_games_hist p
WHERE p.UID IS NOT NULL
AND p.nick NOT LIKE 'coop/%'
AND p.endtime_unix >= ?
AND p.endtime_unix <= ?
GROUP BY p.UID
"""
rows = await _fetch_rows(sql, (start_ts, end_ts))
out: Dict[str, Tuple[str, str]] = {}
for r in rows:
uid = str(r.get("uid") or "")
if not uid:
continue
out[uid] = (str(r.get("nick") or ""), str(r.get("squadron_name") or ""))
return out
def _normalize_rating_stats(row: Dict[str, Any]) -> Dict[str, float]:
games = max(0, int(row.get("games") or 0))
kills = max(0, int(row.get("total_kills") or 0))
deaths = max(0, int(row.get("total_deaths") or 0))
assists = max(0, int(row.get("total_assists") or 0))
captures = max(0, int(row.get("total_captures") or 0))
wins = max(0, int(row.get("wins") or 0))
heavy = max(0.0, float(row.get("heavy_score") or 0))
return {
"games": float(games),
"kdr": (kills / deaths) if deaths > 0 else float(kills),
"kills_per_game": (kills / games) if games > 0 else 0.0,
"heavy_rate": (heavy / games) if games > 0 else 0.0,
"win_rate": (wins / games * 100.0) if games > 0 else 0.0,
"assists_per_game": (assists / games) if games > 0 else 0.0,
"captures_per_game": (captures / games) if games > 0 else 0.0,
"deaths_per_game": (deaths / games) if games > 0 else 0.0,
"games_log": math.log1p(games),
}
def _build_benchmark(rows: List[Dict[str, Any]]) -> Dict[str, List[float]]:
"""Build sorted percentile distributions for each scoring metric."""
normalized = [_normalize_rating_stats(r) for r in rows]
return {m: sorted(float(n[m]) for n in normalized) for m in _METRIC_KEYS}
def _upper_bound(values: List[float], v: float) -> int:
lo, hi = 0, len(values)
while lo < hi:
mid = (lo + hi) // 2
if values[mid] <= v:
lo = mid + 1
else:
hi = mid
return lo
def _lower_bound(values: List[float], v: float) -> int:
lo, hi = 0, len(values)
while lo < hi:
mid = (lo + hi) // 2
if values[mid] < v:
lo = mid + 1
else:
hi = mid
return lo
def _metric_percentile(
value: float, values: List[float], lower_is_better: bool = False
) -> float:
if not values:
return 0.0
if lower_is_better:
return (len(values) - _lower_bound(values, value)) / len(values)
return _upper_bound(values, value) / len(values)
def _compute_performance_score(
row: Dict[str, Any], benchmark: Dict[str, List[float]]
) -> float:
s = _normalize_rating_stats(row)
weighted = (
_W_KDR * _metric_percentile(s["kdr"], benchmark["kdr"])
+ _W_HEAVY_RATE * _metric_percentile(s["heavy_rate"], benchmark["heavy_rate"])
+ _W_KILLS_PER_GAME
* _metric_percentile(s["kills_per_game"], benchmark["kills_per_game"])
+ _W_GAMES_LOG * _metric_percentile(s["games_log"], benchmark["games_log"])
+ _W_WIN_RATE * _metric_percentile(s["win_rate"], benchmark["win_rate"])
+ _W_ASSISTS_PER_GAME
* _metric_percentile(s["assists_per_game"], benchmark["assists_per_game"])
+ _W_CAPTURES_PER_GAME
* _metric_percentile(s["captures_per_game"], benchmark["captures_per_game"])
+ _W_DEATHS_PER_GAME
* _metric_percentile(
s["deaths_per_game"], benchmark["deaths_per_game"], lower_is_better=True
)
)
sample = max(0.0, min(1.0, math.sqrt(s["games"] / 10.0)))
return round(max(0.0, min(5.0, 5.0 * weighted * sample)), 2)
async def player_scores(start_ts: int, end_ts: int) -> Dict[str, Dict[str, Any]]:
"""Return {uid: {nick, squadron_name, score, games, kdr, win_rate, kills_per_game}}."""
rows = await _query_player_aggregates(start_ts, end_ts)
if not rows:
return {}
benchmark = _build_benchmark(rows)
meta = await _query_uid_to_meta(start_ts, end_ts)
out: Dict[str, Dict[str, Any]] = {}
for r in rows:
uid = str(r.get("uid") or "")
if not uid:
continue
norm = _normalize_rating_stats(r)
score = _compute_performance_score(r, benchmark)
nick, squadron = meta.get(uid, ("", ""))
out[uid] = {
"uid": uid,
"nick": nick,
"squadron_name": squadron,
"score": score,
"games": int(norm["games"]),
"kdr": norm["kdr"],
"win_rate": norm["win_rate"],
"kills_per_game": norm["kills_per_game"],
}
return out
async def squadron_scores(start_ts: int, end_ts: int) -> Dict[str, Dict[str, Any]]:
"""Return {squadron_name: {score, games, kdr, win_rate, kills_per_game}}."""
rows = await _query_squadron_aggregates(start_ts, end_ts)
if not rows:
return {}
benchmark = _build_benchmark(rows)
out: Dict[str, Dict[str, Any]] = {}
for r in rows:
sq = str(r.get("squadron_name") or "")
if not sq:
continue
norm = _normalize_rating_stats(r)
score = _compute_performance_score(r, benchmark)
out[sq] = {
"squadron_name": sq,
"score": score,
"games": int(norm["games"]),
"kdr": norm["kdr"],
"win_rate": norm["win_rate"],
"kills_per_game": norm["kills_per_game"],
}
return out
def top_n_squadrons_with_top_k_players_from_maps(
sq_map: Dict[str, Dict[str, Any]],
pl_map: Dict[str, Dict[str, Any]],
n: int = 20,
k: int = 5,
) -> List[Dict[str, Any]]:
"""Sync helper: top N squadrons + top K players from already-computed maps.
Lets a caller pay the cost of `squadron_scores`/`player_scores` once and
then synthesize multiple report payloads (wildcard + per-squadron) from
the same data without re-hitting the DB.
"""
ranked = sorted(
sq_map.values(),
key=lambda r: (-float(r["score"]), -int(r["games"]), str(r["squadron_name"])),
)[:n]
by_squadron: Dict[str, List[Dict[str, Any]]] = {}
for p in pl_map.values():
s = str(p.get("squadron_name") or "")
if not s:
continue
by_squadron.setdefault(s, []).append(p)
payload: List[Dict[str, Any]] = []
for r in ranked:
s = str(r["squadron_name"])
roster = sorted(
by_squadron.get(s, []),
key=lambda p: (-float(p["score"]), -int(p["games"]), str(p.get("nick") or "")),
)[:k]
payload.append({**r, "top_players": roster})
return payload
def squadron_report_for_variants_from_maps(
sq_map: Dict[str, Dict[str, Any]],
pl_map: Dict[str, Dict[str, Any]],
name_variants: List[str],
k: int = 15,
) -> Tuple[Optional[Dict[str, Any]], List[Dict[str, Any]]]:
"""Sync helper: per-squadron payload from already-computed maps."""
sq_row: Optional[Dict[str, Any]] = None
for v in name_variants:
if not v:
continue
if v in sq_map:
sq_row = sq_map[v]
break
variant_set = {v for v in name_variants if v}
roster = [p for p in pl_map.values() if str(p.get("squadron_name") or "") in variant_set]
roster.sort(
key=lambda p: (-float(p["score"]), -int(p["games"]), str(p.get("nick") or ""))
)
return sq_row, roster[:k]
async def top_n_squadrons_with_top_k_players(
start_ts: int, end_ts: int, n: int = 20, k: int = 5
) -> List[Dict[str, Any]]:
"""Wildcard-mode payload: top N squadrons by score, each with their top K players.
Convenience wrapper that fetches the maps then calls the `_from_maps` helper.
Prefer the explicit two-step (compute maps once, call from-maps multiple
times) when building reports for many subscribers.
"""
sq_map = await squadron_scores(start_ts, end_ts)
pl_map = await player_scores(start_ts, end_ts)
return top_n_squadrons_with_top_k_players_from_maps(sq_map, pl_map, n=n, k=k)
async def squadron_report_for_variants(
start_ts: int,
end_ts: int,
name_variants: List[str],
k: int = 15,
) -> Tuple[Optional[Dict[str, Any]], List[Dict[str, Any]]]:
"""Per-squadron mode payload: best-matching squadron row + its top K players.
Tries name_variants in order against the squadron name appearing in
player_games_hist (long_name, tag_name, short_name). Returns (None, [])
if no variant matched and no players in window carry any of the variants.
"""
sq_map = await squadron_scores(start_ts, end_ts)
pl_map = await player_scores(start_ts, end_ts)
return squadron_report_for_variants_from_maps(sq_map, pl_map, name_variants, k=k)
def br_color_int(max_br: float) -> int:
"""Map a BR value to an embed sidebar color (matches /schedule color bands)."""
if max_br >= 13.0:
return 0xE74C3C
if max_br >= 10.0:
return 0xE67E22
if max_br >= 7.0:
return 0xF1C40F
if max_br >= 4.0:
return 0x2ECC71
return 0x3498DB
__all__ = [
"player_scores",
"squadron_scores",
"top_n_squadrons_with_top_k_players",
"top_n_squadrons_with_top_k_players_from_maps",
"squadron_report_for_variants",
"squadron_report_for_variants_from_maps",
"br_color_int",
]