update helps and commands (#1288)
This commit is contained in:
+10
-21
@@ -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
@@ -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
@@ -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::
|
||||||
|
|||||||
Reference in New Issue
Block a user