From 01f6f2b2014b9dec8c5eeb7de491aa43f25e7d39 Mon Sep 17 00:00:00 2001 From: Eaven Kimura Date: Mon, 22 Sep 2025 16:16:23 +0000 Subject: [PATCH] Add purge embeds feature --- .dockerignore | 3 +- .gitignore | 4 +- pterodisbot.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 3 deletions(-) diff --git a/.dockerignore b/.dockerignore index 0b16dd7..de11c6d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,8 @@ .gitea .gitignore .env -__pycache__ +.venv +__pycache__/ md_images/ *.pyc *.pyo diff --git a/.gitignore b/.gitignore index 3ef7e9e..f184e86 100644 --- a/.gitignore +++ b/.gitignore @@ -175,10 +175,10 @@ cython_debug/ .pypirc # Logs -/logs +logs/ # Embeds -/embed +embed/ # Config file config.ini \ No newline at end of file diff --git a/pterodisbot.py b/pterodisbot.py index 87c09d8..63201da 100644 --- a/pterodisbot.py +++ b/pterodisbot.py @@ -1062,6 +1062,155 @@ async def refresh_embeds(interaction: discord.Interaction): ephemeral=True ) +@bot.tree.command(name="purge_embeds", description="Permanently delete all server status embeds (admin only)") +async def purge_embeds(interaction: discord.Interaction): + """ + Slash command to permanently purge all server status embeds from Discord channels. + + This command performs a complete cleanup of all tracked server status embeds by: + 1. Iterating through all tracked embed locations in embed_locations.json + 2. Attempting to delete each embed message from its respective Discord channel + 3. Clearing the embed tracking file and internal state tracking + 4. Providing real-time progress updates during the operation + + Args: + interaction: Discord interaction object representing the command invocation + + Workflow: + - Validates administrator permissions + - Checks if any embeds are currently tracked + - Sends initial progress embed + - Processes each embed sequentially with error handling + - Updates progress embed in real-time + - Saves cleared tracking data to disk + - Sends final results with comprehensive statistics + + Safety: + - Only affects tracked embeds (won't delete arbitrary messages) + - Maintains logs for audit purposes + - Provides rollback protection through immediate tracking removal + - Includes rate limiting to avoid Discord API limits + + Returns: + None: Sends follow-up messages with operation results + """ + if not await check_allowed_guild(interaction): + return + + logger.info(f"Purge embeds command invoked by {interaction.user.name}") + await interaction.response.defer(ephemeral=True) + + # Require administrator permissions + if not interaction.user.guild_permissions.administrator: + logger.warning(f"Unauthorized purge attempt by {interaction.user.name}") + await interaction.followup.send( + "You need administrator permissions to purge all embeds.", + ephemeral=True + ) + return + + try: + logger.info("Starting embed purge per admin request") + + # Variables to track purge statistics + deleted_count = 0 + not_found_count = 0 + error_count = 0 + total_embeds = len(bot.embed_locations) + + if total_embeds == 0: + await interaction.followup.send( + "No embeds are currently being tracked. Nothing to purge.", + ephemeral=True + ) + return + + # Create progress embed + progress_embed = discord.Embed( + title="🔄 Purging Server Embeds", + description=f"Processing {total_embeds} embeds...", + color=discord.Color.orange() + ) + progress_embed.add_field(name="Deleted", value="0", inline=True) + progress_embed.add_field(name="Not Found", value="0", inline=True) + progress_embed.add_field(name="Errors", value="0", inline=True) + progress_embed.set_footer(text="This may take a while...") + + progress_message = await interaction.followup.send(embed=progress_embed, ephemeral=True) + + # Process each tracked embed + for server_id, location in list(bot.embed_locations.items()): + try: + channel = bot.get_channel(int(location['channel_id'])) + if channel: + try: + message = await channel.fetch_message(int(location['message_id'])) + await message.delete() + deleted_count += 1 + logger.debug(f"Successfully purged embed for server {server_id}") + except discord.NotFound: + not_found_count += 1 + logger.debug(f"Embed for server {server_id} already deleted") + except discord.Forbidden: + error_count += 1 + logger.error(f"Permission denied when deleting embed for server {server_id}") + except Exception as e: + error_count += 1 + logger.error(f"Error deleting embed for server {server_id}: {str(e)}") + else: + not_found_count += 1 + logger.warning(f"Channel not found for server {server_id}") + + # Remove from tracking immediately + bot.embed_locations.pop(server_id, None) + bot.previous_states.pop(server_id, None) # Also clean up state tracking + + # Update progress every 5 embeds or for the last one + if (deleted_count + not_found_count + error_count) % 5 == 0 or \ + (deleted_count + not_found_count + error_count) == total_embeds: + + progress_embed.description = f"Processed {deleted_count + not_found_count + error_count}/{total_embeds} embeds" + progress_embed.set_field_at(0, name="Deleted", value=str(deleted_count), inline=True) + progress_embed.set_field_at(1, name="Not Found", value=str(not_found_count), inline=True) + progress_embed.set_field_at(2, name="Errors", value=str(error_count), inline=True) + + await progress_message.edit(embed=progress_embed) + + # Small delay to avoid rate limits + await asyncio.sleep(0.3) + + except Exception as e: + error_count += 1 + logger.error(f"Unexpected error processing server {server_id}: {str(e)}") + + # Save the cleared embed locations + await bot.save_embed_locations() + + # Create results embed + result_embed = discord.Embed( + title="✅ Embed Purge Complete", + color=discord.Color.green(), + timestamp=datetime.now() + ) + result_embed.add_field(name="Total Tracked", value=str(total_embeds), inline=True) + result_embed.add_field(name="✅ Successfully Deleted", value=str(deleted_count), inline=True) + result_embed.add_field(name="❌ Already Missing", value=str(not_found_count), inline=True) + result_embed.add_field(name="⚠️ Errors", value=str(error_count), inline=True) + result_embed.add_field(name="📊 Success Rate", + value=f"{((deleted_count + not_found_count) / total_embeds * 100):.1f}%", + inline=True) + result_embed.set_footer(text="Embed tracking file has been cleared") + + await progress_message.edit(embed=result_embed) + logger.info(f"Embed purge completed: {deleted_count} deleted, {not_found_count} not found, {error_count} errors") + + except Exception as e: + logger.error(f"Embed purge failed: {str(e)}") + await interaction.followup.send( + f"❌ Failed to purge embeds: {str(e)}", + ephemeral=True + ) + # ============================================== # BOT EVENTS # ==============================================