Auto merge dev → main (#1339)
* feat(tally): /tally-claim, /tally-transfer, /tally-wipe commands Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tally): idle sweep, startup load, and empty-VC expiry Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style(tally): parenthesize voice-state guard for clarity Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tally): update live tallies when sessions finish Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(tally): robust winner matching + cleanup of deleted-VC tallies Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tally): /dev-tally to manually attribute a win/loss in your VC Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+52
-2
@@ -56,13 +56,23 @@ def team_short(team: dict) -> str:
|
||||
return strip_tag(team.get("squadron_short") or team.get("squadron"))
|
||||
|
||||
|
||||
def team_identities(team: dict) -> set[str]:
|
||||
"""All lowercased, tag-stripped squadron identifiers for a replay team."""
|
||||
out = set()
|
||||
for key in ("squadron_short", "squadron", "squadron_tagged"):
|
||||
v = strip_tag(team.get(key))
|
||||
if v:
|
||||
out.add(v.lower())
|
||||
return out
|
||||
|
||||
|
||||
def team_index_for(tally: Tally, teams: list[dict]) -> Optional[int]:
|
||||
"""Return the index of the team the tracked entity is on, else None."""
|
||||
for idx, team in enumerate(teams):
|
||||
if not team:
|
||||
continue
|
||||
if tally.mode == "squadron":
|
||||
if team_short(team).lower() == strip_tag(tally.target).lower():
|
||||
if strip_tag(tally.target).lower() in team_identities(team):
|
||||
return idx
|
||||
else: # player mode
|
||||
target = tally.target.strip().lower()
|
||||
@@ -96,7 +106,7 @@ def evaluate(
|
||||
if is_draw:
|
||||
kind = "draw"
|
||||
tally.losses += 1
|
||||
elif winner_short and strip_tag(winner_short).lower() == team_short(teams[idx]).lower():
|
||||
elif winner_short and strip_tag(winner_short).lower() in team_identities(teams[idx]):
|
||||
kind = "win"
|
||||
tally.wins += 1
|
||||
else:
|
||||
@@ -207,6 +217,28 @@ def wipe(guild_id: int, channel_id: int) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def apply_manual_result(guild_id: int, channel_id: int, kind: str,
|
||||
opponent: str = "DEV") -> Optional["Tally"]:
|
||||
"""Manually bump a VC's active tally by a win or loss (dev/testing).
|
||||
|
||||
``kind`` is "win" or "loss". Mirrors what a finished game would do but with
|
||||
no real opponent, so ``last_opponent`` is set to a fixed label. Returns the
|
||||
updated tally, or None if the VC has no active tally.
|
||||
"""
|
||||
tly = _REGISTRY.get((guild_id, channel_id))
|
||||
if tly is None:
|
||||
return None
|
||||
if kind == "win":
|
||||
tly.wins += 1
|
||||
else:
|
||||
tly.losses += 1
|
||||
tly.last_result_kind = kind
|
||||
tly.last_opponent = opponent
|
||||
tly.last_update_ts = time.time()
|
||||
_save()
|
||||
return tly
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Voice-status HTTP helper
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -254,3 +286,21 @@ async def on_session_processed(guild_id: int, teams: list[dict],
|
||||
await push_status(tly)
|
||||
except Exception as e:
|
||||
logging.error(f"[TALLY] on_session_processed error (guild={guild_id}): {e}")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Idle sweep
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def sweep_idle() -> None:
|
||||
"""Wipe tallies that are idle >= IDLE_TIMEOUT or whose channel is gone."""
|
||||
now = time.time()
|
||||
bot = get_bot()
|
||||
for (g, c), tly in list(_REGISTRY.items()):
|
||||
channel_gone = bot is None or bot.get_channel(c) is None
|
||||
if channel_gone:
|
||||
wipe(g, c) # channel gone: drop without an HTTP clear
|
||||
continue
|
||||
if now - tly.last_update_ts >= IDLE_TIMEOUT:
|
||||
wipe(g, c)
|
||||
await clear_status(c)
|
||||
|
||||
Reference in New Issue
Block a user