import asyncio import logging from datetime import timedelta 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 mute_user( bot, message: discord.Message, score: float, categories: list[str], db_message_id: int | None, dirty_users: set[int], ): member = message.author if not isinstance(member, discord.Member): return if not message.guild.me.guild_permissions.moderate_members: logger.warning("Missing moderate_members permission, cannot mute.") return offense_num = bot.drama_tracker.record_offense(member.id) timeout_config = bot.config.get("timeouts", {}) escalation = timeout_config.get("escalation_minutes", [5, 15, 30, 60]) idx = min(offense_num - 1, len(escalation) - 1) duration_minutes = escalation[idx] try: await member.timeout( timedelta(minutes=duration_minutes), reason=f"BCS auto-mute: drama score {score:.2f}", ) except discord.Forbidden: logger.warning("Cannot timeout %s — role hierarchy issue.", member) return except discord.HTTPException as e: logger.error("Failed to timeout %s: %s", member, e) return messages_config = bot.config.get("messages", {}) cat_str = ", ".join(c for c in categories if c != "none") or "general negativity" embed = discord.Embed( title=messages_config.get("mute_title", "BREEHAVIOR ALERT"), description=messages_config.get("mute_description", "").format( username=member.display_name, duration=f"{duration_minutes} minutes", score=f"{score:.2f}", categories=cat_str, ), color=discord.Color.red(), ) embed.set_footer( text=f"Offense #{offense_num} | Timeout: {duration_minutes}m" ) await message.channel.send(embed=embed) await log_action( message.guild, f"**MUTE** | {member.mention} | Score: {score:.2f} | " f"Duration: {duration_minutes}m | Offense #{offense_num} | " f"Categories: {cat_str}", ) logger.info( "Muted %s for %d minutes (offense #%d, score %.2f)", member, duration_minutes, offense_num, score, ) asyncio.create_task(bot.db.save_action( guild_id=message.guild.id, user_id=member.id, username=member.display_name, action_type="mute", message_id=db_message_id, details=f"duration={duration_minutes}m offense={offense_num} score={score:.2f} categories={cat_str}", )) save_user_state(bot, dirty_users, member.id) async def warn_user( bot, message: discord.Message, score: float, db_message_id: int | None, dirty_users: set[int], ): timeout_config = bot.config.get("timeouts", {}) cooldown = timeout_config.get("warning_cooldown_minutes", 5) if not bot.drama_tracker.can_warn(message.author.id, cooldown): return bot.drama_tracker.record_warning(message.author.id) try: await message.add_reaction("\u26a0\ufe0f") except discord.HTTPException: pass messages_config = bot.config.get("messages", {}) warning_text = messages_config.get( "warning", "Easy there, {username}. The Breehavior Monitor is watching.", ).format(username=message.author.display_name) await message.channel.send(warning_text) await log_action( message.guild, f"**WARNING** | {message.author.mention} | Score: {score:.2f}", ) logger.info("Warned %s (score %.2f)", message.author, score) asyncio.create_task(bot.db.save_action( guild_id=message.guild.id, user_id=message.author.id, username=message.author.display_name, action_type="warning", message_id=db_message_id, details=f"score={score:.2f}", )) save_user_state(bot, dirty_users, message.author.id)