import asyncio import logging import discord from cogs.sentiment.log_utils import log_action from cogs.sentiment.state import save_user_state logger = logging.getLogger("bcs.sentiment") async def handle_topic_drift( bot, message: discord.Message, topic_category: str, topic_reasoning: str, db_message_id: int | None, dirty_users: set[int], ): config = bot.config.get("topic_drift", {}) if not config.get("enabled", True): return ignored = config.get("ignored_channels", []) if message.channel.id in ignored or getattr(message.channel, "name", "") in ignored: return dry_run = bot.config.get("monitoring", {}).get("dry_run", False) if dry_run: return tracker = bot.drama_tracker user_id = message.author.id cooldown = config.get("remind_cooldown_minutes", 10) if not tracker.can_topic_remind(user_id, cooldown): return count = tracker.record_off_topic(user_id) escalation_threshold = config.get("escalation_count", 3) messages_config = bot.config.get("messages", {}) if count >= escalation_threshold and not tracker.was_owner_notified(user_id): tracker.mark_owner_notified(user_id) owner = message.guild.owner if owner: dm_text = messages_config.get( "topic_owner_dm", "Heads up: {username} keeps going off-topic in #{channel}. Reminded {count} times.", ).format( username=message.author.display_name, channel=message.channel.name, count=count, ) try: await owner.send(dm_text) except discord.HTTPException: logger.warning("Could not DM server owner about topic drift.") await log_action( message.guild, f"**TOPIC DRIFT \u2014 OWNER NOTIFIED** | {message.author.mention} | " f"Off-topic count: {count} | Category: {topic_category}", ) logger.info("Notified owner about %s topic drift (count %d)", message.author, count) asyncio.create_task(bot.db.save_action( guild_id=message.guild.id, user_id=user_id, username=message.author.display_name, action_type="topic_escalation", message_id=db_message_id, details=f"off_topic_count={count} category={topic_category}", )) save_user_state(bot, dirty_users, user_id) elif count >= 2: nudge_text = messages_config.get( "topic_nudge", "{username}, let's keep it to gaming talk in here.", ).format(username=message.author.display_name) await message.channel.send(nudge_text) await log_action( message.guild, f"**TOPIC NUDGE** | {message.author.mention} | " f"Off-topic count: {count} | Category: {topic_category}", ) logger.info("Topic nudge for %s (count %d)", message.author, count) asyncio.create_task(bot.db.save_action( guild_id=message.guild.id, user_id=user_id, username=message.author.display_name, action_type="topic_nudge", message_id=db_message_id, details=f"off_topic_count={count} category={topic_category}", )) save_user_state(bot, dirty_users, user_id) else: remind_text = messages_config.get( "topic_remind", "Hey {username}, this is a gaming server \u2014 maybe take the personal stuff to DMs?", ).format(username=message.author.display_name) await message.channel.send(remind_text) await log_action( message.guild, f"**TOPIC REMIND** | {message.author.mention} | " f"Category: {topic_category} | {topic_reasoning}", ) logger.info("Topic remind for %s (count %d)", message.author, count) asyncio.create_task(bot.db.save_action( guild_id=message.guild.id, user_id=user_id, username=message.author.display_name, action_type="topic_remind", message_id=db_message_id, details=f"off_topic_count={count} category={topic_category} reasoning={topic_reasoning}", )) save_user_state(bot, dirty_users, user_id)