feat: move user aliases from config to DB with /bcs-alias command

Aliases now stored in UserState table instead of config.yaml. Adds
Aliases column (NVARCHAR 500), loads on startup, persists via flush.
New /bcs-alias slash command (view/set/clear) for managing nicknames.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 10:35:19 -05:00
parent ad1234ec99
commit 33d56f8737
6 changed files with 124 additions and 28 deletions
+9 -12
View File
@@ -253,18 +253,18 @@ class SentimentCog(commands.Cog):
"""Replace display name keys with anonymous keys in user notes map."""
return {anon_map.get(name, name): notes for name, notes in user_notes_map.items()}
@staticmethod
def _build_alias_context(
self,
messages: list[discord.Message],
anon_map: dict[str, str],
alias_config: dict,
) -> str:
"""Build anonymized alias context string for the LLM.
Maps user IDs from messages to their known nicknames using the
config, then replaces display names with anonymous keys.
Maps user IDs from messages to their known nicknames from
DramaTracker, then replaces display names with anonymous keys.
"""
if not alias_config:
all_aliases = self.bot.drama_tracker.get_all_aliases()
if not all_aliases:
return ""
lines = []
seen_ids: set[int] = set()
@@ -273,14 +273,13 @@ class SentimentCog(commands.Cog):
if uid in seen_ids:
continue
seen_ids.add(uid)
aliases = alias_config.get(uid)
aliases = all_aliases.get(uid)
if aliases:
anon_key = anon_map.get(msg.author.display_name, msg.author.display_name)
lines.append(f" {anon_key} is also known as: {', '.join(aliases)}")
# Also include aliases for members NOT in the conversation (so the LLM
# can recognize name-drops of absent members)
for uid, aliases in alias_config.items():
uid = int(uid) if isinstance(uid, str) else uid
for uid, aliases in all_aliases.items():
if uid not in seen_ids:
lines.append(f" (not in chat) also known as: {', '.join(aliases)}")
return "\n".join(lines) if lines else ""
@@ -498,8 +497,7 @@ class SentimentCog(commands.Cog):
anon_conversation = self._anonymize_conversation(conversation, anon_map)
anon_notes = self._anonymize_notes(user_notes_map, anon_map) if user_notes_map else user_notes_map
alias_config = config.get("user_aliases", {})
alias_context = self._build_alias_context(all_messages, anon_map, alias_config)
alias_context = self._build_alias_context(all_messages, anon_map)
channel_context = build_channel_context(ref_message, game_channels)
@@ -675,8 +673,7 @@ class SentimentCog(commands.Cog):
anon_conversation = self._anonymize_conversation(conversation, anon_map)
anon_notes = self._anonymize_notes(user_notes_map, anon_map) if user_notes_map else user_notes_map
alias_config = config.get("user_aliases", {})
alias_context = self._build_alias_context(raw_messages, anon_map, alias_config)
alias_context = self._build_alias_context(raw_messages, anon_map)
channel_context = build_channel_context(raw_messages[0], game_channels)
mention_context = (
+7
View File
@@ -4,6 +4,11 @@ import logging
logger = logging.getLogger("bcs.sentiment")
def _aliases_csv(user_data) -> str | None:
"""Convert aliases list to comma-separated string for DB storage."""
return ",".join(user_data.aliases) if user_data.aliases else None
def save_user_state(bot, dirty_users: set[int], user_id: int) -> None:
"""Fire-and-forget save of a user's current state to DB."""
user_data = bot.drama_tracker.get_user(user_id)
@@ -16,6 +21,7 @@ def save_user_state(bot, dirty_users: set[int], user_id: int) -> None:
user_notes=user_data.notes or None,
warned=user_data.warned_since_reset,
last_offense_at=user_data.last_offense_time or None,
aliases=_aliases_csv(user_data),
))
dirty_users.discard(user_id)
@@ -37,5 +43,6 @@ async def flush_dirty_states(bot, dirty_users: set[int]) -> None:
user_notes=user_data.notes or None,
warned=user_data.warned_since_reset,
last_offense_at=user_data.last_offense_time or None,
aliases=_aliases_csv(user_data),
)
logger.info("Flushed %d dirty user states to DB.", len(dirty))