diff --git a/cogs/aimod.py b/cogs/aimod.py index c3d4142..0813184 100644 --- a/cogs/aimod.py +++ b/cogs/aimod.py @@ -277,6 +277,144 @@ class AIModerationCog(commands.Cog): super().__init__(timeout=3600) self.parent = parent self.target = target + self.message: discord.Message | None = None + + # --- Helper Modals --- + class BanModal(discord.ui.Modal, title="Ban User"): + reason = discord.ui.TextInput( + label="Reason", + placeholder="Reason for ban", + style=discord.TextStyle.paragraph, + required=False, + max_length=512, + ) + + def __init__(self, view: "AIModerationCog.QuickActionView"): + super().__init__() + self.view = view + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.ban_members: + await interaction.response.send_message( + "You lack permission to ban members.", ephemeral=True + ) + return + try: + await self.view.target.ban( + reason=self.reason.value or "Escalated via mod panel" + ) + await interaction.response.send_message( + f"Banned {self.view.target.mention}.", ephemeral=True + ) + except Exception as e: # noqa: BLE001 + await interaction.response.send_message( + f"Failed to ban: {e}", ephemeral=True + ) + self.view.disable_all_items() + if self.view.message: + await self.view.message.edit(view=self.view) + + class KickModal(discord.ui.Modal, title="Kick User"): + reason = discord.ui.TextInput( + label="Reason", + placeholder="Reason for kick", + style=discord.TextStyle.paragraph, + required=False, + max_length=512, + ) + + def __init__(self, view: "AIModerationCog.QuickActionView"): + super().__init__() + self.view = view + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.kick_members: + await interaction.response.send_message( + "You lack permission to kick members.", ephemeral=True + ) + return + try: + await self.view.target.kick( + reason=self.reason.value or "Escalated via mod panel" + ) + await interaction.response.send_message( + f"Kicked {self.view.target.mention}.", ephemeral=True + ) + except Exception as e: # noqa: BLE001 + await interaction.response.send_message( + f"Failed to kick: {e}", ephemeral=True + ) + self.view.disable_all_items() + if self.view.message: + await self.view.message.edit(view=self.view) + + class TimeoutModal(discord.ui.Modal, title="Timeout User"): + duration = discord.ui.TextInput( + label="Duration", + placeholder="e.g. 10m, 1h, 1d", + required=True, + max_length=10, + ) + reason = discord.ui.TextInput( + label="Reason", + placeholder="Reason for timeout", + style=discord.TextStyle.paragraph, + required=False, + max_length=512, + ) + + def __init__(self, view: "AIModerationCog.QuickActionView"): + super().__init__() + self.view = view + + @staticmethod + def parse_duration(duration_str: str) -> datetime.timedelta | None: + if not duration_str: + return None + try: + amount = int("".join(filter(str.isdigit, duration_str))) + unit = "".join(filter(str.isalpha, duration_str)).lower() + if unit in {"d", "day", "days"}: + return datetime.timedelta(days=amount) + if unit in {"h", "hour", "hours"}: + return datetime.timedelta(hours=amount) + if unit in {"m", "min", "minute", "minutes"}: + return datetime.timedelta(minutes=amount) + if unit in {"s", "sec", "second", "seconds"}: + return datetime.timedelta(seconds=amount) + except (ValueError, TypeError): + return None + return None + + async def on_submit(self, interaction: discord.Interaction): + if not interaction.user.guild_permissions.moderate_members: + await interaction.response.send_message( + "You lack permission to timeout members.", ephemeral=True + ) + return + delta = self.parse_duration(self.duration.value) + if not delta or delta > datetime.timedelta(days=28): + await interaction.response.send_message( + "Invalid duration. Use formats like '10m', '1h', '1d'", + ephemeral=True, + ) + return + try: + until = discord.utils.utcnow() + delta + await self.view.target.timeout( + until, reason=self.reason.value or "Escalated via mod panel" + ) + await interaction.response.send_message( + f"Timed out {self.view.target.mention} for {self.duration.value}.", + ephemeral=True, + ) + except Exception as e: # noqa: BLE001 + await interaction.response.send_message( + f"Failed to timeout: {e}", ephemeral=True + ) + self.view.disable_all_items() + if self.view.message: + await self.view.message.edit(view=self.view) @discord.ui.button(label="Escalate Ban", style=discord.ButtonStyle.danger) async def escalate( @@ -287,17 +425,32 @@ class AIModerationCog(commands.Cog): "You lack permission to ban members.", ephemeral=True ) return - try: - await self.target.ban(reason="Escalated via mod panel") + self.message = interaction.message + await interaction.response.send_modal(self.BanModal(self)) + + @discord.ui.button(label="Kick", style=discord.ButtonStyle.primary) + async def kick( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if not interaction.user.guild_permissions.kick_members: await interaction.response.send_message( - f"Banned {self.target.mention}.", ephemeral=True + "You lack permission to kick members.", ephemeral=True ) - except Exception as e: # noqa: BLE001 + return + self.message = interaction.message + await interaction.response.send_modal(self.KickModal(self)) + + @discord.ui.button(label="Timeout", style=discord.ButtonStyle.secondary) + async def timeout( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if not interaction.user.guild_permissions.moderate_members: await interaction.response.send_message( - f"Failed to ban: {e}", ephemeral=True + "You lack permission to timeout members.", ephemeral=True ) - self.disable_all_items() - await interaction.message.edit(view=self) + return + self.message = interaction.message + await interaction.response.send_modal(self.TimeoutModal(self)) @discord.ui.button(label="Ignore", style=discord.ButtonStyle.secondary) async def ignore(