feat: Add send_dm option to ban command and update modal for user notification preferences
This commit is contained in:
parent
777e206d07
commit
cdad3244f8
@ -43,8 +43,19 @@ class ModerationCog(commands.Cog):
|
||||
app_commands.describe(
|
||||
member="The member to ban",
|
||||
reason="The reason for the ban",
|
||||
delete_days="Number of days of messages to delete (0-7)"
|
||||
delete_days="Number of days of messages to delete (0-7)",
|
||||
send_dm="Whether to send a DM notification to the user (default: True)"
|
||||
)(ban_command)
|
||||
|
||||
# Add the send_dm parameter with a default value of True
|
||||
send_dm_param = app_commands.Parameter(
|
||||
name="send_dm",
|
||||
description="Whether to send a DM notification to the user",
|
||||
type=bool,
|
||||
default=True
|
||||
)
|
||||
ban_command.parameters.append(send_dm_param)
|
||||
|
||||
self.moderate_group.add_command(ban_command)
|
||||
|
||||
# --- Unban Command ---
|
||||
@ -203,7 +214,7 @@ class ModerationCog(commands.Cog):
|
||||
|
||||
# --- Command Callbacks ---
|
||||
|
||||
async def moderate_ban_callback(self, interaction: discord.Interaction, member: discord.Member, reason: str = None, delete_days: int = 0):
|
||||
async def moderate_ban_callback(self, interaction: discord.Interaction, member: discord.Member, reason: str = None, delete_days: int = 0, send_dm: bool = True):
|
||||
"""Ban a member from the server."""
|
||||
# Check if the user has permission to ban members
|
||||
if not interaction.user.guild_permissions.ban_members:
|
||||
@ -238,25 +249,26 @@ class ModerationCog(commands.Cog):
|
||||
# Ensure delete_days is within valid range (0-7)
|
||||
delete_days = max(0, min(7, delete_days))
|
||||
|
||||
# Try to send a DM to the user before banning them
|
||||
# Try to send a DM to the user before banning them (if send_dm is True)
|
||||
dm_sent = False
|
||||
try:
|
||||
embed = discord.Embed(
|
||||
title="Ban Notice",
|
||||
description=f"You have been banned from **{interaction.guild.name}**",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
embed.add_field(name="Reason", value=reason or "No reason provided", inline=False)
|
||||
embed.add_field(name="Moderator", value=interaction.user.name, inline=False)
|
||||
embed.set_footer(text=f"Server ID: {interaction.guild.id} • {discord.utils.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
||||
if send_dm:
|
||||
try:
|
||||
embed = discord.Embed(
|
||||
title="Ban Notice",
|
||||
description=f"You have been banned from **{interaction.guild.name}**",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
embed.add_field(name="Reason", value=reason or "No reason provided", inline=False)
|
||||
embed.add_field(name="Moderator", value=interaction.user.name, inline=False)
|
||||
embed.set_footer(text=f"Server ID: {interaction.guild.id} • {discord.utils.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
||||
|
||||
await member.send(embed=embed)
|
||||
dm_sent = True
|
||||
except discord.Forbidden:
|
||||
# User has DMs closed, ignore
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending ban DM to {member} (ID: {member.id}): {e}")
|
||||
await member.send(embed=embed)
|
||||
dm_sent = True
|
||||
except discord.Forbidden:
|
||||
# User has DMs closed, ignore
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending ban DM to {member} (ID: {member.id}): {e}")
|
||||
|
||||
# Perform the ban
|
||||
try:
|
||||
@ -280,8 +292,11 @@ class ModerationCog(commands.Cog):
|
||||
# -------------------------
|
||||
|
||||
# Send confirmation message with DM status
|
||||
dm_status = "✅ DM notification sent" if dm_sent else "❌ Could not send DM notification (user may have DMs disabled)"
|
||||
await interaction.response.send_message(f"🔨 **Banned {member.mention}**! Reason: {reason or 'No reason provided'}\n{dm_status}")
|
||||
if send_dm:
|
||||
dm_status = "✅ DM notification sent" if dm_sent else "❌ Could not send DM notification (user may have DMs disabled)"
|
||||
await interaction.response.send_message(f"🔨 **Banned {member.mention}**! Reason: {reason or 'No reason provided'}\n{dm_status}")
|
||||
else:
|
||||
await interaction.response.send_message(f"🔨 **Banned {member.mention}**! Reason: {reason or 'No reason provided'}\n⚠️ DM notification was disabled")
|
||||
except discord.Forbidden:
|
||||
await interaction.response.send_message("❌ I don't have permission to ban this member.", ephemeral=True)
|
||||
except discord.HTTPException as e:
|
||||
@ -798,7 +813,7 @@ class ModerationCog(commands.Cog):
|
||||
duration_seconds = infraction['duration_seconds']
|
||||
|
||||
moderator = interaction.guild.get_member(moderator_id) or f"ID: {moderator_id}"
|
||||
|
||||
|
||||
value = f"**Case ID:** {case_id}\n"
|
||||
value += f"**Action:** {action_type}\n"
|
||||
value += f"**Moderator:** {moderator}\n"
|
||||
@ -807,12 +822,12 @@ class ModerationCog(commands.Cog):
|
||||
value += f"**Duration:** {duration_str}\n"
|
||||
value += f"**Reason:** {reason}\n"
|
||||
value += f"**Date:** {discord.utils.format_dt(timestamp, style='f')}"
|
||||
|
||||
|
||||
embed.add_field(name=f"Infraction #{case_id}", value=value, inline=False)
|
||||
|
||||
if len(infractions) > 25:
|
||||
embed.set_footer(text=f"Showing 25 of {len(infractions)} infractions.")
|
||||
|
||||
|
||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
|
||||
async def moderate_remove_infraction_callback(self, interaction: discord.Interaction, case_id: int, reason: str = None):
|
||||
@ -836,20 +851,20 @@ class ModerationCog(commands.Cog):
|
||||
|
||||
if deleted:
|
||||
logger.info(f"Infraction (Case ID: {case_id}) removed by {interaction.user} (ID: {interaction.user.id}) in guild {interaction.guild.id}. Reason: {reason}")
|
||||
|
||||
|
||||
# Log the removal action itself
|
||||
mod_log_cog: ModLogCog = self.bot.get_cog('ModLogCog')
|
||||
if mod_log_cog:
|
||||
target_user_id = infraction_to_remove['target_user_id']
|
||||
target_user = await self.bot.fetch_user(target_user_id) # Fetch user for logging
|
||||
|
||||
|
||||
await mod_log_cog.log_action(
|
||||
guild=interaction.guild,
|
||||
moderator=interaction.user,
|
||||
target=target_user if target_user else Object(id=target_user_id),
|
||||
action_type="REMOVE_INFRACTION",
|
||||
reason=f"Removed Case ID {case_id}. Original reason: {infraction_to_remove['reason']}. Removal reason: {reason or 'Not specified'}",
|
||||
duration=None
|
||||
duration=None
|
||||
)
|
||||
await interaction.response.send_message(f"✅ Infraction with Case ID {case_id} has been removed. Reason: {reason or 'Not specified'}", ephemeral=True)
|
||||
else:
|
||||
@ -881,7 +896,7 @@ class ModerationCog(commands.Cog):
|
||||
|
||||
if deleted_count > 0:
|
||||
logger.info(f"{deleted_count} infractions for user {member} (ID: {member.id}) cleared by {interaction.user} (ID: {interaction.user.id}) in guild {interaction.guild.id}. Reason: {reason}")
|
||||
|
||||
|
||||
# Log the clear all action
|
||||
mod_log_cog: ModLogCog = self.bot.get_cog('ModLogCog')
|
||||
if mod_log_cog:
|
||||
@ -1132,6 +1147,7 @@ class BanModal(discord.ui.Modal, title="Ban User"):
|
||||
def __init__(self, member: discord.Member):
|
||||
super().__init__()
|
||||
self.member = member
|
||||
self.send_dm = True # Default to True
|
||||
|
||||
reason = discord.ui.TextInput(
|
||||
label="Reason",
|
||||
@ -1156,7 +1172,7 @@ class BanModal(discord.ui.Modal, title="Ban User"):
|
||||
reason = self.reason.value or "No reason provided"
|
||||
delete_days = int(self.delete_days.value) if self.delete_days.value and self.delete_days.value.isdigit() else 0
|
||||
# Call the existing ban callback
|
||||
await cog.moderate_ban_callback(interaction, self.member, reason=reason, delete_days=delete_days)
|
||||
await cog.moderate_ban_callback(interaction, self.member, reason=reason, delete_days=delete_days, send_dm=self.send_dm)
|
||||
else:
|
||||
await interaction.followup.send("Error: Moderation cog not found.", ephemeral=True)
|
||||
|
||||
@ -1243,6 +1259,33 @@ class RemoveTimeoutModal(discord.ui.Modal, title="Remove Timeout"):
|
||||
|
||||
# Context menu commands must be defined at module level
|
||||
|
||||
class BanOptionsView(discord.ui.View):
|
||||
def __init__(self, member: discord.Member):
|
||||
super().__init__(timeout=60) # 60 second timeout
|
||||
self.member = member
|
||||
self.send_dm = True # Default to True
|
||||
self.update_button_label()
|
||||
|
||||
def update_button_label(self):
|
||||
self.toggle_dm_button.label = f"Send DM: {'Yes' if self.send_dm else 'No'}"
|
||||
self.toggle_dm_button.style = discord.ButtonStyle.green if self.send_dm else discord.ButtonStyle.red
|
||||
|
||||
@discord.ui.button(label="Send DM: Yes", style=discord.ButtonStyle.green, custom_id="toggle_dm")
|
||||
async def toggle_dm_button(self, interaction: discord.Interaction, _: discord.ui.Button):
|
||||
# Toggle the send_dm value
|
||||
self.send_dm = not self.send_dm
|
||||
self.update_button_label()
|
||||
await interaction.response.edit_message(view=self)
|
||||
|
||||
@discord.ui.button(label="Continue to Ban", style=discord.ButtonStyle.danger, custom_id="continue_ban")
|
||||
async def continue_button(self, interaction: discord.Interaction, _: discord.ui.Button):
|
||||
# Create and show the modal
|
||||
modal = BanModal(self.member)
|
||||
modal.send_dm = self.send_dm # Pass the send_dm setting to the modal
|
||||
await interaction.response.send_modal(modal)
|
||||
# Stop listening for interactions on this view
|
||||
self.stop()
|
||||
|
||||
@app_commands.context_menu(name="Ban User")
|
||||
async def ban_user_context_menu(interaction: discord.Interaction, member: discord.Member):
|
||||
"""Bans the selected user via a modal."""
|
||||
@ -1266,8 +1309,13 @@ async def ban_user_context_menu(interaction: discord.Interaction, member: discor
|
||||
await interaction.response.send_message("❌ I cannot ban myself.", ephemeral=True)
|
||||
return
|
||||
|
||||
modal = BanModal(member)
|
||||
await interaction.response.send_modal(modal)
|
||||
# Show options view first
|
||||
view = BanOptionsView(member)
|
||||
await interaction.response.send_message(
|
||||
f"⚠️ You are about to ban **{member.display_name}** ({member.id}).\nPlease select your options:",
|
||||
view=view,
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
@app_commands.context_menu(name="Kick User")
|
||||
async def kick_user_context_menu(interaction: discord.Interaction, member: discord.Member):
|
||||
|
Loading…
x
Reference in New Issue
Block a user