update helps and commands (#1288)

This commit is contained in:
NotSoToothless
2026-05-30 09:09:29 -07:00
committed by GitHub
parent 7edc0202f4
commit c69478166b
3 changed files with 19 additions and 52 deletions
+10 -21
View File
@@ -1,8 +1,8 @@
"""TSSBOT autolog matcher. """TSSBOT autolog matcher.
For each game received from the Spectra TSS feed, match it against every For each game received from the Spectra TSS feed, match it against every
subscribing guild's preferences (``tss-team`` / ``tss-player`` entries) and subscribing guild's ``tss-team`` preference entries and work out which channels
work out which channels should be notified, deduping per session. should be notified, deduping per session.
NOTE: building and sending the scoreboard embed is intentionally a TODO for now NOTE: building and sending the scoreboard embed is intentionally a TODO for now
(it needs significant rework). The matching + dedup pipeline below is complete (it needs significant rework). The matching + dedup pipeline below is complete
@@ -33,10 +33,9 @@ def set_bot(bot: discord.Client) -> None:
_bot = bot _bot = bot
def _present_entities(game: dict[str, Any]) -> tuple[set[str], set[str]]: def _present_team_tags(game: dict[str, Any]) -> set[str]:
"""Return (player_uids, team_tags) present in a game dict.""" """Return the set of team tags present in a game dict."""
players = game.get("players") or {} players = game.get("players") or {}
uids = {str(k) for k in players}
tags: set[str] = set() tags: set[str] = set()
for p in players.values(): for p in players.values():
if not isinstance(p, dict): if not isinstance(p, dict):
@@ -45,7 +44,7 @@ def _present_entities(game: dict[str, Any]) -> tuple[set[str], set[str]]:
tag = tag_raw[1:-1] if len(tag_raw) > 2 else tag_raw tag = tag_raw[1:-1] if len(tag_raw) > 2 else tag_raw
if tag: if tag:
tags.add(tag) tags.add(tag)
return uids, tags return tags
async def process_game(game: dict[str, Any]) -> None: async def process_game(game: dict[str, Any]) -> None:
@@ -60,7 +59,7 @@ async def process_game(game: dict[str, Any]) -> None:
if not session_id: if not session_id:
return return
present_uids, present_tags = _present_entities(game) present_tags = _present_team_tags(game)
tags_lower = {t.lower() for t in present_tags} tags_lower = {t.lower() for t in present_tags}
# Resolve present tags → team_ids so team subscriptions (keyed by team_id) match. # Resolve present tags → team_ids so team subscriptions (keyed by team_id) match.
@@ -78,24 +77,14 @@ async def process_game(game: dict[str, Any]) -> None:
for guild_id, prefs in preferences.iter_guild_preferences(): for guild_id, prefs in preferences.iter_guild_preferences():
for entity_id, entry in prefs.items(): for entity_id, entry in prefs.items():
if not isinstance(entry, dict): if not isinstance(entry, dict) or entry.get("Type") != "tss-team":
continue continue
channel_id, enabled = preferences.parse_channel(entry.get("Logs")) channel_id, enabled = preferences.parse_channel(entry.get("Logs"))
if not channel_id or not enabled: if not channel_id or not enabled:
continue continue
etype = entry.get("Type")
matched = False
if etype == "tss-team":
if str(entity_id) in present_team_ids:
matched = True
else:
name = (entry.get("Name") or "").lower() name = (entry.get("Name") or "").lower()
if name and name in tags_lower: matched = str(entity_id) in present_team_ids or (name and name in tags_lower)
matched = True
elif etype == "tss-player":
if str(entity_id) in present_uids:
matched = True
if not matched or channel_id in sent: if not matched or channel_id in sent:
continue continue
@@ -105,6 +94,6 @@ async def process_game(game: dict[str, Any]) -> None:
# The matched target is fully resolved here; the only thing missing # The matched target is fully resolved here; the only thing missing
# is the rendered scoreboard (out of scope for now). # is the rendered scoreboard (out of scope for now).
log.info( log.info(
"autolog match (send TODO): session=%s guild=%s channel=%s entity=%s type=%s", "autolog match (send TODO): session=%s guild=%s channel=%s team=%s",
session_id, guild_id, channel_id, entity_id, etype, session_id, guild_id, channel_id, entity_id,
) )
+1 -23
View File
@@ -150,27 +150,6 @@ class TssCommands(commands.Cog):
f"✅ Logging **{name}**'s matches to <#{interaction.channel_id}>.", ephemeral=True f"✅ Logging **{name}**'s matches to <#{interaction.channel_id}>.", ephemeral=True
) )
# ── /log-player ────────────────────────────────────────────────────────
@app_commands.command(name="log-player", description="Send a player's matches to this channel")
@app_commands.describe(player="Player username")
@app_commands.autocomplete(player=player_autocomplete)
@app_commands.checks.has_permissions(manage_guild=True)
@not_blacklisted()
async def log_player(self, interaction: discord.Interaction, player: str):
await interaction.response.defer(ephemeral=True)
if interaction.guild_id is None or interaction.channel_id is None:
return await interaction.followup.send("This command must be used in a server channel.", ephemeral=True)
candidates = await storage.resolve_players(player)
if not candidates:
return await interaction.followup.send(f"No players found matching **{player}**.", ephemeral=True)
if len(candidates) > 1:
return await interaction.followup.send(_too_many_msg(candidates), ephemeral=True)
uid, nick = str(candidates[0]["uid"]), candidates[0]["nick"]
preferences.upsert_log_entry(interaction.guild_id, uid, "tss-player", nick, interaction.channel_id)
await interaction.followup.send(
f"✅ Logging **{nick}**'s matches to <#{interaction.channel_id}>.", ephemeral=True
)
# ── /set-player ──────────────────────────────────────────────────────── # ── /set-player ────────────────────────────────────────────────────────
@app_commands.command(name="set-player", description="Link your Discord account to a War Thunder player") @app_commands.command(name="set-player", description="Link your Discord account to a War Thunder player")
@app_commands.describe(player="Player username") @app_commands.describe(player="Player username")
@@ -311,8 +290,7 @@ class TssCommands(commands.Cog):
name="Autologging — Manage Server", name="Autologging — Manage Server",
value=( value=(
"`/set-team <team>` — set this server's team\n" "`/set-team <team>` — set this server's team\n"
"`/log-team <team>` — post a team's matches to this channel\n" "`/log-team <team>` — post a team's matches to this channel"
"`/log-player <player>` — post a player's matches to this channel"
), ),
inline=False, inline=False,
) )
+7 -7
View File
@@ -4,20 +4,20 @@ Mirrors SREBOT's PREFERENCES model, adapted for TSS.
Per-guild file: ``STORAGE_VOL_PATH/PREFERENCES/<guild_id>-preferences.json`` — Per-guild file: ``STORAGE_VOL_PATH/PREFERENCES/<guild_id>-preferences.json`` —
**shared with SREBOT** (both bots write to the same file; entries are told apart **shared with SREBOT** (both bots write to the same file; entries are told apart
by ``Type``). A flat dict keyed by the watched entity id (a ``team_id`` for team by ``Type``). A flat dict keyed by the watched ``team_id``::
subs, a player ``uid`` for player subs)::
{ {
"<entity_id>": { "<team_id>": {
"Type": "tss-team" | "tss-player", "Type": "tss-team",
"Name": "STaYA", "Name": "STaYA",
"Logs": "<#channelid>" "Logs": "<#channelid>"
} }
} }
The ``Type`` value's ``tss``/``sre`` prefix marks the data source and the The ``Type`` value's ``tss``/``sre`` prefix marks the data source. (The store is
``team``/``player`` suffix marks the entry kind. Channel values use the SREBOT generic — a future ``tss-player``/``tss-tournament`` kind could be added — but
encoding ``"<#ID>"`` (enabled) / ``"<#DISABLED-ID>"`` (disabled). only ``tss-team`` is written today.) Channel values use the SREBOT encoding
``"<#ID>"`` (enabled) / ``"<#DISABLED-ID>"`` (disabled).
TEAMS.json (set by ``/set-team``) records each guild's own team, mirroring TEAMS.json (set by ``/set-team``) records each guild's own team, mirroring
SREBOT's SQUADRONS.json:: SREBOT's SQUADRONS.json::