Add game channel redirect feature and sexual_vulgar detection
Detect when users discuss a game in the wrong channel (e.g. GTA talk in #warzone) and send a friendly redirect to the correct channel. Also add sexual_vulgar category and scoring rules so crude sexual remarks directed at someone aren't softened by "lmao". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -358,8 +358,25 @@ class CommandsCog(commands.Cog):
|
||||
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
|
||||
# Build channel context for game detection
|
||||
game_channels = self.bot.config.get("game_channels", {})
|
||||
channel_context = ""
|
||||
if game_channels and hasattr(interaction.channel, "name"):
|
||||
ch_name = interaction.channel.name
|
||||
current_game = game_channels.get(ch_name)
|
||||
lines = []
|
||||
if current_game:
|
||||
lines.append(f"Current channel: #{ch_name} ({current_game})")
|
||||
else:
|
||||
lines.append(f"Current channel: #{ch_name}")
|
||||
channel_list = ", ".join(f"#{ch} ({g})" for ch, g in game_channels.items())
|
||||
lines.append(f"Game channels: {channel_list}")
|
||||
channel_context = "\n".join(lines)
|
||||
|
||||
user_notes = self.bot.drama_tracker.get_user_notes(interaction.user.id)
|
||||
raw, parsed = await self.bot.llm.raw_analyze(message, user_notes=user_notes)
|
||||
raw, parsed = await self.bot.llm.raw_analyze(
|
||||
message, user_notes=user_notes, channel_context=channel_context,
|
||||
)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="BCS Test Analysis", color=discord.Color.blue()
|
||||
@@ -389,6 +406,14 @@ class CommandsCog(commands.Cog):
|
||||
value=parsed["reasoning"][:1024] or "n/a",
|
||||
inline=False,
|
||||
)
|
||||
detected_game = parsed.get("detected_game")
|
||||
if detected_game:
|
||||
game_label = game_channels.get(detected_game, detected_game)
|
||||
embed.add_field(
|
||||
name="Detected Game",
|
||||
value=f"#{detected_game} ({game_label})",
|
||||
inline=True,
|
||||
)
|
||||
else:
|
||||
embed.add_field(
|
||||
name="Parsing", value="Failed to parse response", inline=False
|
||||
|
||||
@@ -19,6 +19,8 @@ class SentimentCog(commands.Cog):
|
||||
self._channel_history: dict[int, deque] = {}
|
||||
# Track which user IDs have unsaved in-memory changes
|
||||
self._dirty_users: set[int] = set()
|
||||
# Per-user redirect cooldown: {user_id: last_redirect_datetime}
|
||||
self._redirect_cooldowns: dict[int, datetime] = {}
|
||||
|
||||
async def cog_load(self):
|
||||
self._flush_states.start()
|
||||
@@ -79,11 +81,16 @@ class SentimentCog(commands.Cog):
|
||||
if not self.bot.drama_tracker.can_analyze(message.author.id, cooldown):
|
||||
return
|
||||
|
||||
# Build channel context for game detection
|
||||
game_channels = config.get("game_channels", {})
|
||||
channel_context = self._build_channel_context(message, game_channels)
|
||||
|
||||
# Analyze the message
|
||||
context = self._get_context(message)
|
||||
user_notes = self.bot.drama_tracker.get_user_notes(message.author.id)
|
||||
result = await self.bot.llm.analyze_message(
|
||||
message.content, context, user_notes=user_notes
|
||||
message.content, context, user_notes=user_notes,
|
||||
channel_context=channel_context,
|
||||
)
|
||||
|
||||
if result is None:
|
||||
@@ -137,6 +144,11 @@ class SentimentCog(commands.Cog):
|
||||
if off_topic:
|
||||
await self._handle_topic_drift(message, topic_category, topic_reasoning, db_message_id)
|
||||
|
||||
# Game channel redirect detection
|
||||
detected_game = result.get("detected_game")
|
||||
if detected_game and game_channels and not monitoring.get("dry_run", False):
|
||||
await self._handle_channel_redirect(message, detected_game, game_channels, db_message_id)
|
||||
|
||||
# Coherence / intoxication detection
|
||||
coherence_score = result.get("coherence_score", 0.85)
|
||||
coherence_flag = result.get("coherence_flag", "normal")
|
||||
@@ -481,6 +493,90 @@ class SentimentCog(commands.Cog):
|
||||
)
|
||||
logger.info("Flushed %d dirty user states to DB.", len(dirty))
|
||||
|
||||
def _build_channel_context(self, message: discord.Message, game_channels: dict) -> str:
|
||||
"""Build a channel context string for LLM game detection."""
|
||||
if not game_channels:
|
||||
return ""
|
||||
channel_name = getattr(message.channel, "name", "")
|
||||
current_game = game_channels.get(channel_name)
|
||||
lines = []
|
||||
if current_game:
|
||||
lines.append(f"Current channel: #{channel_name} ({current_game})")
|
||||
else:
|
||||
lines.append(f"Current channel: #{channel_name}")
|
||||
channel_list = ", ".join(f"#{ch} ({game})" for ch, game in game_channels.items())
|
||||
lines.append(f"Game channels: {channel_list}")
|
||||
return "\n".join(lines)
|
||||
|
||||
async def _handle_channel_redirect(
|
||||
self, message: discord.Message, detected_game: str,
|
||||
game_channels: dict, db_message_id: int | None = None,
|
||||
):
|
||||
"""Send a redirect message if the user is talking about a different game."""
|
||||
channel_name = getattr(message.channel, "name", "")
|
||||
|
||||
# Only redirect if message is in a game channel
|
||||
if channel_name not in game_channels:
|
||||
return
|
||||
|
||||
# No redirect needed if detected game matches current channel
|
||||
if detected_game == channel_name:
|
||||
return
|
||||
|
||||
# Detected game must be a valid game channel
|
||||
if detected_game not in game_channels:
|
||||
return
|
||||
|
||||
# Find the target channel in the guild
|
||||
target_channel = discord.utils.get(
|
||||
message.guild.text_channels, name=detected_game
|
||||
)
|
||||
if not target_channel:
|
||||
return
|
||||
|
||||
# Check per-user cooldown (reuse topic_drift remind_cooldown_minutes)
|
||||
user_id = message.author.id
|
||||
cooldown_minutes = self.bot.config.get("topic_drift", {}).get("remind_cooldown_minutes", 10)
|
||||
now = datetime.now(timezone.utc)
|
||||
last_redirect = self._redirect_cooldowns.get(user_id)
|
||||
if last_redirect and (now - last_redirect) < timedelta(minutes=cooldown_minutes):
|
||||
return
|
||||
|
||||
self._redirect_cooldowns[user_id] = now
|
||||
|
||||
# Send redirect message
|
||||
messages_config = self.bot.config.get("messages", {})
|
||||
game_name = game_channels[detected_game]
|
||||
redirect_text = messages_config.get(
|
||||
"channel_redirect",
|
||||
"Hey {username}, that sounds like {game} talk — head over to {channel} for that!",
|
||||
).format(
|
||||
username=message.author.display_name,
|
||||
game=game_name,
|
||||
channel=target_channel.mention,
|
||||
)
|
||||
|
||||
await message.channel.send(redirect_text)
|
||||
|
||||
await self._log_action(
|
||||
message.guild,
|
||||
f"**CHANNEL REDIRECT** | {message.author.mention} | "
|
||||
f"#{channel_name} → #{detected_game} ({game_name})",
|
||||
)
|
||||
logger.info(
|
||||
"Redirected %s from #%s to #%s (%s)",
|
||||
message.author, channel_name, detected_game, game_name,
|
||||
)
|
||||
|
||||
asyncio.create_task(self.bot.db.save_action(
|
||||
guild_id=message.guild.id,
|
||||
user_id=user_id,
|
||||
username=message.author.display_name,
|
||||
action_type="channel_redirect",
|
||||
message_id=db_message_id,
|
||||
details=f"from=#{channel_name} to=#{detected_game} game={game_name}",
|
||||
))
|
||||
|
||||
def _store_context(self, message: discord.Message):
|
||||
ch_id = message.channel.id
|
||||
if ch_id not in self._channel_history:
|
||||
|
||||
Reference in New Issue
Block a user