aa
This commit is contained in:
parent
27807e89bb
commit
318507ca2c
@ -164,6 +164,19 @@ class ModerationCog(commands.Cog):
|
|||||||
)(remove_infraction_command)
|
)(remove_infraction_command)
|
||||||
self.moderate_group.add_command(remove_infraction_command)
|
self.moderate_group.add_command(remove_infraction_command)
|
||||||
|
|
||||||
|
# --- Clear Infractions Command ---
|
||||||
|
clear_infractions_command = app_commands.Command(
|
||||||
|
name="clearinfractions",
|
||||||
|
description="Clear all moderation infractions for a user",
|
||||||
|
callback=self.moderate_clear_infractions_callback,
|
||||||
|
parent=self.moderate_group
|
||||||
|
)
|
||||||
|
app_commands.describe(
|
||||||
|
member="The member whose infractions to clear",
|
||||||
|
reason="The reason for clearing all infractions"
|
||||||
|
)(clear_infractions_command)
|
||||||
|
self.moderate_group.add_command(clear_infractions_command)
|
||||||
|
|
||||||
# Helper method for parsing duration strings
|
# Helper method for parsing duration strings
|
||||||
def _parse_duration(self, duration_str: str) -> Optional[datetime.timedelta]:
|
def _parse_duration(self, duration_str: str) -> Optional[datetime.timedelta]:
|
||||||
"""Parse a duration string like '1d', '2h', '30m' into a timedelta."""
|
"""Parse a duration string like '1d', '2h', '30m' into a timedelta."""
|
||||||
@ -827,6 +840,68 @@ class ModerationCog(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
await interaction.response.send_message(f"❌ Failed to remove infraction with Case ID {case_id}. It might have already been removed or an error occurred.", ephemeral=True)
|
await interaction.response.send_message(f"❌ Failed to remove infraction with Case ID {case_id}. It might have already been removed or an error occurred.", ephemeral=True)
|
||||||
|
|
||||||
|
async def moderate_clear_infractions_callback(self, interaction: discord.Interaction, member: discord.Member, reason: str = None):
|
||||||
|
"""Clear all moderation infractions for a user."""
|
||||||
|
# This is a destructive action, so require ban_members permission
|
||||||
|
if not interaction.user.guild_permissions.ban_members:
|
||||||
|
await interaction.response.send_message("❌ You don't have permission to clear all infractions for a user.", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.bot.pg_pool:
|
||||||
|
await interaction.response.send_message("❌ Database connection is not available.", ephemeral=True)
|
||||||
|
logger.error("Cannot clear infractions: pg_pool is None.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Confirmation step
|
||||||
|
view = discord.ui.View()
|
||||||
|
confirm_button = discord.ui.Button(label="Confirm Clear All", style=discord.ButtonStyle.danger, custom_id="confirm_clear_all")
|
||||||
|
cancel_button = discord.ui.Button(label="Cancel", style=discord.ButtonStyle.secondary, custom_id="cancel_clear_all")
|
||||||
|
|
||||||
|
async def confirm_callback(interaction_confirm: discord.Interaction):
|
||||||
|
if interaction_confirm.user.id != interaction.user.id:
|
||||||
|
await interaction_confirm.response.send_message("❌ You are not authorized to confirm this action.", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
deleted_count = await mod_log_db.clear_user_mod_logs(self.bot.pg_pool, interaction.guild.id, member.id)
|
||||||
|
|
||||||
|
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:
|
||||||
|
await mod_log_cog.log_action(
|
||||||
|
guild=interaction.guild,
|
||||||
|
moderator=interaction.user,
|
||||||
|
target=member,
|
||||||
|
action_type="CLEAR_INFRACTIONS",
|
||||||
|
reason=f"Cleared {deleted_count} infractions. Reason: {reason or 'Not specified'}",
|
||||||
|
duration=None
|
||||||
|
)
|
||||||
|
await interaction_confirm.response.edit_message(content=f"✅ Successfully cleared {deleted_count} infractions for {member.mention}. Reason: {reason or 'Not specified'}", view=None)
|
||||||
|
elif deleted_count == 0:
|
||||||
|
await interaction_confirm.response.edit_message(content=f"ℹ️ No infractions found for {member.mention} to clear.", view=None)
|
||||||
|
else: # Should not happen if 0 is returned for no logs
|
||||||
|
await interaction_confirm.response.edit_message(content=f"❌ Failed to clear infractions for {member.mention}. An error occurred.", view=None)
|
||||||
|
|
||||||
|
async def cancel_callback(interaction_cancel: discord.Interaction):
|
||||||
|
if interaction_cancel.user.id != interaction.user.id:
|
||||||
|
await interaction_cancel.response.send_message("❌ You are not authorized to cancel this action.", ephemeral=True)
|
||||||
|
return
|
||||||
|
await interaction_cancel.response.edit_message(content="🚫 Infraction clearing cancelled.", view=None)
|
||||||
|
|
||||||
|
confirm_button.callback = confirm_callback
|
||||||
|
cancel_button.callback = cancel_callback
|
||||||
|
view.add_item(confirm_button)
|
||||||
|
view.add_item(cancel_button)
|
||||||
|
|
||||||
|
await interaction.response.send_message(
|
||||||
|
f"⚠️ Are you sure you want to clear **ALL** infractions for {member.mention}?\n"
|
||||||
|
f"This action is irreversible. Reason: {reason or 'Not specified'}",
|
||||||
|
view=view,
|
||||||
|
ephemeral=True
|
||||||
|
)
|
||||||
|
|
||||||
# --- Legacy Command Handlers (for prefix commands) ---
|
# --- Legacy Command Handlers (for prefix commands) ---
|
||||||
|
|
||||||
@commands.command(name="timeout")
|
@commands.command(name="timeout")
|
||||||
|
@ -488,3 +488,40 @@ async def delete_mod_log(pool: asyncpg.Pool, case_id: int, guild_id: int) -> boo
|
|||||||
await pool.release(connection)
|
await pool.release(connection)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"Error releasing connection back to pool after deleting mod log: {e}")
|
log.warning(f"Error releasing connection back to pool after deleting mod log: {e}")
|
||||||
|
|
||||||
|
async def clear_user_mod_logs(pool: asyncpg.Pool, guild_id: int, target_user_id: int) -> int:
|
||||||
|
"""Deletes all moderation log entries for a specific user in a guild. Returns the number of deleted logs."""
|
||||||
|
query = """
|
||||||
|
DELETE FROM moderation_logs
|
||||||
|
WHERE guild_id = $1 AND target_user_id = $2;
|
||||||
|
"""
|
||||||
|
connection, success = await create_connection_with_retry(pool)
|
||||||
|
if not success or not connection:
|
||||||
|
log.error(f"Failed to acquire database connection for clearing mod logs for user {target_user_id} in guild {guild_id}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with connection.transaction():
|
||||||
|
# Execute the delete command and get the status (e.g., "DELETE 5")
|
||||||
|
result_status = await connection.execute(query, guild_id, target_user_id)
|
||||||
|
# Parse the number of deleted rows from the status string
|
||||||
|
deleted_count = 0
|
||||||
|
if result_status and result_status.startswith("DELETE"):
|
||||||
|
try:
|
||||||
|
deleted_count = int(result_status.split(" ")[1])
|
||||||
|
except (IndexError, ValueError) as e:
|
||||||
|
log.warning(f"Could not parse deleted count from status: {result_status} - {e}")
|
||||||
|
|
||||||
|
if deleted_count > 0:
|
||||||
|
log.info(f"Cleared {deleted_count} mod log entries for user {target_user_id} in guild {guild_id}")
|
||||||
|
else:
|
||||||
|
log.info(f"No mod log entries found to clear for user {target_user_id} in guild {guild_id}")
|
||||||
|
return deleted_count
|
||||||
|
except Exception as e:
|
||||||
|
log.exception(f"Error clearing mod log entries for user {target_user_id} in guild {guild_id}: {e}")
|
||||||
|
return 0
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
await pool.release(connection)
|
||||||
|
except Exception as e:
|
||||||
|
log.warning(f"Error releasing connection back to pool after clearing user mod logs: {e}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user