Auto merge dev → main (#1341)

* chore(tally): remove /dev-tally testing command

Feature is verified working; drop the dev-only manual win/loss command and its now-unused apply_manual_result helper and test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(tally): rename /tally-wipe to /tally-clear

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:
NotSoToothless
2026-06-19 01:33:29 -07:00
committed by GitHub
parent db7c546a24
commit 3590b1f42f
4 changed files with 4 additions and 121 deletions
+3 -79
View File
@@ -4242,7 +4242,7 @@ async def set_player_perm_error(interaction, error):
# ═══════════════════════════════════════════════════════════════════════════
# /tally-claim · /tally-transfer · /tally-wipe — live VC scoreline tracking
# /tally-claim · /tally-transfer · /tally-clear — live VC scoreline tracking
# ═══════════════════════════════════════════════════════════════════════════
def _invoker_voice_channel(interaction: discord.Interaction):
@@ -4339,13 +4339,13 @@ async def tally_transfer(interaction: discord.Interaction, username: str):
@bot.tree.command(
name="tally-wipe",
name="tally-clear",
description=command_locale(
"Clear the active tally on your current voice channel",
"commands.tally.description_wipe",
),
)
async def tally_wipe(interaction: discord.Interaction):
async def tally_clear(interaction: discord.Interaction):
lang = await guild_lang(interaction.guild.id) if interaction.guild else "en"
vc = _invoker_voice_channel(interaction)
if vc is None:
@@ -4362,82 +4362,6 @@ async def tally_wipe(interaction: discord.Interaction):
)
@bot.tree.command(
name="dev-tally",
description="[DEV] Manually attribute a win or loss to the tally in your voice channel",
)
@app_commands.describe(
result="Whether to record a win or a loss",
username="The player to attribute it to (use alone, not with squadron)",
squadron="The squadron to attribute it to (use alone, not with username)",
)
@app_commands.choices(result=[
app_commands.Choice(name="Win", value="win"),
app_commands.Choice(name="Loss", value="loss"),
])
@discord.app_commands.autocomplete(username=player_autocomplete, squadron=squadron_autocomplete)
async def dev_tally(interaction: discord.Interaction, result: app_commands.Choice[str],
username: str = "", squadron: str = ""):
await collect_command_stats(interaction)
if not await is_dev_team(interaction):
await interaction.response.send_message(t("en", "dev.restricted_dev_team"), ephemeral=True)
return
lang = await guild_lang(interaction.guild.id) if interaction.guild else "en"
vc = _invoker_voice_channel(interaction)
if vc is None:
await interaction.response.send_message(t(lang, "commands.tally.not_in_vc"), ephemeral=True)
return
username = username.strip()
squadron = squadron.strip()
if username and squadron:
await interaction.response.send_message(
"Use `username` or `squadron` separately — don't pass both.", ephemeral=True
)
return
if not username and not squadron:
await interaction.response.send_message(
"Provide a `username` or a `squadron` to attribute the result to.", ephemeral=True
)
return
if not interaction.guild_id:
await interaction.response.send_message(t(lang, "commands.tally.not_in_vc"), ephemeral=True)
return
active = tally.get(interaction.guild_id, vc.id)
if active is None:
await interaction.response.send_message(
t(lang, "commands.tally.no_active", channel=vc.name), ephemeral=True
)
return
# Only attribute the result if the argument matches what this VC tracks —
# a player arg must match a player-mode tally's target; a squadron arg must
# match a squadron-mode tally's target (tag-insensitive). Mirrors how a real
# finished game only counts when the tracked entity actually played.
if username:
matches = active.mode == "player" and active.target.strip().lower() == username.lower()
else:
matches = active.mode == "squadron" and \
tally.strip_tag(active.target).lower() == tally.strip_tag(squadron).lower()
if not matches:
await interaction.response.send_message(
f"This VC is tracking the {active.mode} **{active.display_target}** — "
f"**{username or squadron}** doesn't match it, so nothing was recorded.",
ephemeral=True,
)
return
tly = tally.apply_manual_result(interaction.guild_id, vc.id, result.value)
if tly is None:
await interaction.response.send_message(
t(lang, "commands.tally.no_active", channel=vc.name), ephemeral=True
)
return
await tally.push_status(tly)
await interaction.response.send_message(
f"Recorded a **{result.name}** for **{username or squadron}** in **{vc.name}** "
f"(`{tly.wins}W-{tly.losses}L`).",
ephemeral=True,
)
# ═══════════════════════════════════════════════════════════════════════════
# /view-player-games — Last 20 games for a player
# ═══════════════════════════════════════════════════════════════════════════
+1 -1
View File
@@ -861,7 +861,7 @@
"not_in_vc": "You must be connected to a voice channel to use this.",
"premium_required": "This is a premium feature. Use /unlock to enable it for this server.",
"need_one_input": "Provide exactly one of `ign` or `squadron_short`.",
"already_active": "A tally is already active in **{channel}** tracking **{target}**. Use /tally-transfer or /tally-wipe first.",
"already_active": "A tally is already active in **{channel}** tracking **{target}**. Use /tally-transfer or /tally-clear first.",
"claimed": "Now tracking **{target}** in **{channel}**. Status set to `0W-0L`.",
"no_active": "There is no active tally in **{channel}**.",
"transferred": "Tally in **{channel}** now tracking **{target}** (count carried over: `{base}`).",
-22
View File
@@ -217,28 +217,6 @@ 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
# ---------------------------------------------------------------------------
-19
View File
@@ -133,25 +133,6 @@ def test_squadron_mode_matches_bare_name_despite_resolved_short():
assert team_index_for(t, _teams_resolved_mismatch()) == 0
def test_apply_manual_result_win_and_loss():
import tempfile
import BOT.tally as tal
with tempfile.TemporaryDirectory() as d:
tal.TALLY_PATH = Path(d) / "TALLY.json"
tal._REGISTRY.clear()
tal.claim(1, 2, "player", "bravo", "Bravo", user_id=7)
win = tal.apply_manual_result(1, 2, "win")
assert win is not None
assert (win.wins, win.losses) == (1, 0)
assert win.last_result_kind == "win" and win.last_opponent == "DEV"
loss = tal.apply_manual_result(1, 2, "loss")
assert loss is not None
assert (loss.wins, loss.losses) == (1, 1)
assert loss.last_result_kind == "loss" and loss.last_opponent == "DEV"
# No active tally in a different VC → None
assert tal.apply_manual_result(1, 999, "win") is None
def _run():
fns = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
for fn in fns: