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:
+3
-79
@@ -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):
|
def _invoker_voice_channel(interaction: discord.Interaction):
|
||||||
@@ -4339,13 +4339,13 @@ async def tally_transfer(interaction: discord.Interaction, username: str):
|
|||||||
|
|
||||||
|
|
||||||
@bot.tree.command(
|
@bot.tree.command(
|
||||||
name="tally-wipe",
|
name="tally-clear",
|
||||||
description=command_locale(
|
description=command_locale(
|
||||||
"Clear the active tally on your current voice channel",
|
"Clear the active tally on your current voice channel",
|
||||||
"commands.tally.description_wipe",
|
"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"
|
lang = await guild_lang(interaction.guild.id) if interaction.guild else "en"
|
||||||
vc = _invoker_voice_channel(interaction)
|
vc = _invoker_voice_channel(interaction)
|
||||||
if vc is None:
|
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
|
# /view-player-games — Last 20 games for a player
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
+1
-1
@@ -861,7 +861,7 @@
|
|||||||
"not_in_vc": "You must be connected to a voice channel to use this.",
|
"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.",
|
"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`.",
|
"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`.",
|
"claimed": "Now tracking **{target}** in **{channel}**. Status set to `0W-0L`.",
|
||||||
"no_active": "There is no active tally in **{channel}**.",
|
"no_active": "There is no active tally in **{channel}**.",
|
||||||
"transferred": "Tally in **{channel}** now tracking **{target}** (count carried over: `{base}`).",
|
"transferred": "Tally in **{channel}** now tracking **{target}** (count carried over: `{base}`).",
|
||||||
|
|||||||
@@ -217,28 +217,6 @@ def wipe(guild_id: int, channel_id: int) -> bool:
|
|||||||
return True
|
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
|
# Voice-status HTTP helper
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -133,25 +133,6 @@ def test_squadron_mode_matches_bare_name_despite_resolved_short():
|
|||||||
assert team_index_for(t, _teams_resolved_mismatch()) == 0
|
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():
|
def _run():
|
||||||
fns = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
|
fns = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
|
||||||
for fn in fns:
|
for fn in fns:
|
||||||
|
|||||||
Reference in New Issue
Block a user