From f5b9133947676ea20337578f1655689e2b729c61 Mon Sep 17 00:00:00 2001 From: rambros Date: Mon, 23 Feb 2026 17:40:44 +0530 Subject: [PATCH] send more info in audit logs --- src/core/audit.py | 21 ++++-- src/core/clone_server.py | 25 ++++++- src/core/emoji_stickers.py | 12 +++- src/core/fluxer_writer.py | 5 +- src/core/roles_permissions.py | 35 +++++++-- src/core/server_metadata.py | 10 ++- src/core/state.py | 10 +-- src/ui/app.py | 132 +++++++++++++++++++++++++++------- 8 files changed, 203 insertions(+), 47 deletions(-) diff --git a/src/core/audit.py b/src/core/audit.py index bf69584..3be4c48 100644 --- a/src/core/audit.py +++ b/src/core/audit.py @@ -3,14 +3,27 @@ from src.core.base import MigrationContext logger = logging.getLogger(__name__) -async def log_audit_event(context: MigrationContext, title: str, description: str) -> None: +async def log_audit_event(context: MigrationContext, title: str, description: str, files: list[dict] | None = None) -> None: """ Logs an event by sending a summary to the `#fluxer-reaper` audit channel. If the channel does not exist, it will dynamically create it and hide it from @everyone. """ - # 1. Initialize channel if not tracked + # 1. Initialize or Validate channel + channel_id = context.state.audit_log_channel + + if channel_id: + # Verify it still exists in Fluxer to avoid 404 errors + try: + fluxer_channels = await context.fluxer_writer.get_channels() + channel_exists = any(str(ch.get("id")) == str(channel_id) for ch in fluxer_channels) + if not channel_exists: + logger.info(f"Audit channel {channel_id} no longer exists in Fluxer. Resetting.") + context.state.audit_log_channel = None + except Exception as e: + logger.warning(f"Failed to verify audit channel existence: {e}") + if not context.state.audit_log_channel: - logger.info("Audit log channel not found in state. Checking Fluxer community...") + logger.info("Audit log channel not found or invalid in state. Checking Fluxer community...") try: # Check if it already exists in the community but isn't in state channels = await context.fluxer_writer.get_channels() @@ -52,6 +65,6 @@ async def log_audit_event(context: MigrationContext, title: str, description: st # 2. Format and send the message natively through FluxerBot (avoiding impersonation webhook for admin logs) content = f"**[{title}]**\n{description}" try: - await context.fluxer_writer.send_marker(context.state.audit_log_channel, content) + await context.fluxer_writer.send_marker(context.state.audit_log_channel, content, files=files) except Exception as e: logger.error(f"Failed to send audit log event: {e}") diff --git a/src/core/clone_server.py b/src/core/clone_server.py index 273d743..3ddcd21 100644 --- a/src/core/clone_server.py +++ b/src/core/clone_server.py @@ -52,7 +52,7 @@ async def sync_channel_state(context: MigrationContext): logger.info(f"Channel sync: {updates} mapped, {removals} stale mappings removed") -async def migrate_channels(context: MigrationContext, progress_callback: Callable[[str, str, int, int], Awaitable[None]] | None = None, force: bool = False): +async def migrate_channels(context: MigrationContext, progress_callback: Callable[[str, str, int, int], Awaitable[None]] | None = None, force: bool = False) -> dict: """Clones categories and text channels. Args: @@ -62,6 +62,14 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl categories = await context.discord_reader.get_categories() channels = await context.discord_reader.get_channels() + cloned_info = { + "categories_created": [], + "channels_created": [], + "channels_synced": [], + "structure": {} # category_name -> [channel_names] + } + cat_name_map = {str(cat.id): cat.name for cat in categories} + # 1. Identify categories to create missing_categories = [cat for cat in categories if force or not context.state.get_fluxer_category_id(str(cat.id))] missing_category_ids = {str(cat.id) for cat in missing_categories} @@ -90,7 +98,7 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl current_idx = 0 if total == 0: - return + return cloned_info # Migrate Categories first for cat in missing_categories: @@ -99,6 +107,9 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl state_key = str(cat.id) fluxer_id = await context.fluxer_writer.create_channel(cat.name, type=4) context.state.set_category_mapping(state_key, fluxer_id) + cloned_info["categories_created"].append(cat.name) + if cat.name not in cloned_info["structure"]: + cloned_info["structure"][cat.name] = [] current_idx += 1 if progress_callback: await progress_callback(f"Cat: {cat.name}", "Copying", current_idx, total) @@ -126,6 +137,12 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl slowmode_delay=slowmode ) context.state.set_channel_mapping(state_key, fluxer_id) + cloned_info["channels_created"].append(channel.name) + + parent_name = cat_name_map.get(str(channel.category_id), "No Category") if channel.category_id else "No Category" + if parent_name not in cloned_info["structure"]: + cloned_info["structure"][parent_name] = [] + cloned_info["structure"][parent_name].append(channel.name) # Sync again immediately because some properties (like slowmode) are ignored on creation await context.fluxer_writer.modify_channel( @@ -161,6 +178,10 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl slowmode_delay=slowmode ) + cloned_info["channels_synced"].append(channel.name) + current_idx += 1 if progress_callback: await progress_callback(channel.name, "Syncing", current_idx, total) await asyncio.sleep(context.config.migration.rate_limit_delay_seconds) + + return cloned_info diff --git a/src/core/emoji_stickers.py b/src/core/emoji_stickers.py index 0b27a13..c6b0728 100644 --- a/src/core/emoji_stickers.py +++ b/src/core/emoji_stickers.py @@ -55,11 +55,14 @@ async def sync_assets_state(context: MigrationContext): logger.info(f"Asset sync: {updates} mapped, {removals} stale mappings removed") -async def migrate_emojis(context: MigrationContext, progress_callback: Callable[[str, str, int, int], Awaitable[None]] | None = None, types_to_include: List[str] = ["Emoji", "Sticker"], force: bool = False): +async def migrate_emojis(context: MigrationContext, progress_callback: Callable[[str, str, int, int], Awaitable[None]] | None = None, types_to_include: List[str] = ["Emoji", "Sticker"], force: bool = False) -> dict[str, dict[str, str]]: """Copies custom emojis and stickers. Args: force: If True, skip state cache and re-copy even if already migrated. + + Returns: + A dictionary containing dicts of cloned asset names to their new IDs by type: {"Emoji": {"name": "id", ...}, "Sticker": {"name": "id", ...}} """ objs = [] if "Emoji" in types_to_include: @@ -75,9 +78,10 @@ async def migrate_emojis(context: MigrationContext, progress_callback: Callable[ )] total = len(objs) + cloned_assets: dict[str, dict[str, str]] = {"Emoji": {}, "Sticker": {}} if total == 0: - return + return cloned_assets for idx, (obj, obj_type) in enumerate(objs): if not context.is_running: break @@ -91,6 +95,7 @@ async def migrate_emojis(context: MigrationContext, progress_callback: Callable[ ) if fluxer_id: context.state.set_emoji_mapping(str(obj.id), fluxer_id) + cloned_assets["Emoji"][obj.name] = fluxer_id else: img_data = await context.discord_reader.download_sticker(obj) fluxer_id = await context.fluxer_writer.create_sticker( @@ -99,8 +104,11 @@ async def migrate_emojis(context: MigrationContext, progress_callback: Callable[ ) if fluxer_id: context.state.set_sticker_mapping(str(obj.id), fluxer_id) + cloned_assets["Sticker"][obj.name] = fluxer_id except Exception as e: logger.error(f"Error downloading/uploading {obj_type.lower()} {obj.name}: {e}") if progress_callback: await progress_callback(obj.name, obj_type, idx + 1, total) await asyncio.sleep(context.config.migration.rate_limit_delay_seconds) + + return cloned_assets diff --git a/src/core/fluxer_writer.py b/src/core/fluxer_writer.py index 732172d..2863720 100644 --- a/src/core/fluxer_writer.py +++ b/src/core/fluxer_writer.py @@ -279,7 +279,7 @@ class FluxerWriter: print(err_msg) return None - async def send_marker(self, channel_id: str, content: str) -> Optional[str]: + async def send_marker(self, channel_id: str, content: str, files: list[dict] | None = None) -> Optional[str]: """ Sends a simple marker message (e.g., thread start/end) using the bot directly. """ @@ -287,7 +287,8 @@ class FluxerWriter: try: msg_data = await self.client.send_message( channel_id=channel_id, - content=content + content=content, + files=files ) return str(msg_data["id"]) if msg_data else None except Exception as e: diff --git a/src/core/roles_permissions.py b/src/core/roles_permissions.py index 5399e45..fa3eaf4 100644 --- a/src/core/roles_permissions.py +++ b/src/core/roles_permissions.py @@ -37,7 +37,7 @@ async def sync_roles_state(context: MigrationContext): logger.info(f"Role sync: {updates} mapped, {removals} stale mappings removed") -async def sync_permissions(context: MigrationContext, progress_callback: Callable[[str, int, int], Awaitable[None]] | None = None): +async def sync_permissions(context: MigrationContext, progress_callback: Callable[[str, int, int], Awaitable[None]] | None = None) -> dict: """Syncs category and channel role overrides/permissions.""" categories = await context.discord_reader.get_categories() channels = await context.discord_reader.get_channels() @@ -46,11 +46,17 @@ async def sync_permissions(context: MigrationContext, progress_callback: Callabl categories = [c for c in categories if context.state.get_fluxer_category_id(str(c.id))] channels = [c for c in channels if context.state.get_fluxer_channel_id(str(c.id))] + synced_info = { + "categories_synced": [], + "channels_synced": [], + "structure": {} # category_name -> [channel_names] + } + total = len(categories) + len(channels) current_idx = 0 if total == 0: - return + return synced_info async def _sync_overwrites(discord_item, fluxer_id): """Helper to sync role overwrites for a given channel or category.""" @@ -75,6 +81,10 @@ async def sync_permissions(context: MigrationContext, progress_callback: Callabl is_role=True ) + # Dictionary to map category names to their synced channels + # Categorize items as we sync them + cat_name_map = {str(cat.id): cat.name for cat in (await context.discord_reader.get_categories())} + # Sync Category Permissions (Role Overwrites) for cat in categories: if not context.is_running: break @@ -82,6 +92,9 @@ async def sync_permissions(context: MigrationContext, progress_callback: Callabl if fluxer_id: try: await _sync_overwrites(cat, fluxer_id) + synced_info["categories_synced"].append(cat.name) + if cat.name not in synced_info["structure"]: + synced_info["structure"][cat.name] = [] except Exception as e: logger.error(f"Failed syncing permissions for category {cat.name}: {e}") @@ -95,24 +108,33 @@ async def sync_permissions(context: MigrationContext, progress_callback: Callabl if fluxer_id: try: await _sync_overwrites(channel, fluxer_id) + synced_info["channels_synced"].append(channel.name) + + parent_name = cat_name_map.get(str(channel.category_id), "No Category") if channel.category_id else "No Category" + if parent_name not in synced_info["structure"]: + synced_info["structure"][parent_name] = [] + synced_info["structure"][parent_name].append(channel.name) except Exception as e: logger.error(f"Failed syncing permissions for channel {channel.name}: {e}") current_idx += 1 if progress_callback: await progress_callback(channel.name, current_idx, total) + return synced_info -async def migrate_roles(context: MigrationContext, progress_callback: Callable[[str, int, int], Awaitable[None]] | None = None, force: bool = False): - """Copies roles and their baseline permissions.""" + +async def migrate_roles(context: MigrationContext, progress_callback: Callable[[str, int, int], Awaitable[None]] | None = None, force: bool = False) -> list[str]: + """Copies roles and their baseline permissions. Returns a list of cloned role names.""" roles = await context.discord_reader.get_roles() if not force: roles = [r for r in roles if not context.state.get_fluxer_role_id(str(r.id))] total = len(roles) + cloned_role_names = [] if total == 0: - return + return cloned_role_names for idx, role in enumerate(roles): if not context.is_running: break @@ -125,6 +147,9 @@ async def migrate_roles(context: MigrationContext, progress_callback: Callable[[ ) if fluxer_id: context.state.set_role_mapping(str(role.id), fluxer_id) + cloned_role_names.append(role.name) if progress_callback: await progress_callback(role.name, idx + 1, total) await asyncio.sleep(context.config.migration.rate_limit_delay_seconds) + + return cloned_role_names diff --git a/src/core/server_metadata.py b/src/core/server_metadata.py index 103e0e3..8836002 100644 --- a/src/core/server_metadata.py +++ b/src/core/server_metadata.py @@ -5,15 +5,17 @@ from src.core.base import MigrationContext logger = logging.getLogger(__name__) -async def sync_server_metadata(context: MigrationContext, progress_callback: Callable[[str, str], Awaitable[None]], components: List[str] = ["name", "icon", "banner"]): - """Syncs the server name, logo and banner.""" +async def sync_server_metadata(context: MigrationContext, progress_callback: Callable[[str, str], Awaitable[None]], components: List[str] = ["name", "icon", "banner"]) -> dict: + """Syncs the server name, logo and banner. Returns a dict of cloned attributes.""" metadata = await context.discord_reader.get_server_metadata() + cloned_data = {} # 1. Sync Name if "name" in components: try: name = metadata.get("name") await context.fluxer_writer.update_guild_metadata(name=name) + cloned_data["name"] = name await progress_callback("Server Name", "DONE") except Exception: await progress_callback("Server Name", "ERROR") @@ -27,6 +29,7 @@ async def sync_server_metadata(context: MigrationContext, progress_callback: Cal if icon_bytes: await context.fluxer_writer.update_guild_metadata(icon=icon_bytes) + cloned_data["icon"] = icon_bytes await progress_callback("Server Icon", "DONE") else: await progress_callback("Server Icon", "SKIP") @@ -42,8 +45,11 @@ async def sync_server_metadata(context: MigrationContext, progress_callback: Cal if banner_bytes: await context.fluxer_writer.update_guild_metadata(banner=banner_bytes) + cloned_data["banner"] = banner_bytes await progress_callback("Server Banner", "DONE") else: await progress_callback("Server Banner", "SKIP") except Exception: await progress_callback("Server Banner", "ERROR") + + return cloned_data diff --git a/src/core/state.py b/src/core/state.py index d84d32b..36356a2 100644 --- a/src/core/state.py +++ b/src/core/state.py @@ -21,8 +21,8 @@ class MigrationState: # message tracking self.message_map: Dict[str, str] = {} - self.last_message_timestamps: Dict[str, str] = {} self.last_message_ids: Dict[str, str] = {} + self.last_message_timestamps: Dict[str, str] = {} self.load() @@ -44,8 +44,8 @@ class MigrationState: # Check for legacy messages in state.json if "messages" in data or "last_message_timestamps" in data: self.message_map = data.get("messages", {}) - self.last_message_timestamps = data.get("last_message_timestamps", {}) self.last_message_ids = data.get("last_message_ids", {}) + self.last_message_timestamps = data.get("last_message_timestamps", {}) migrated_messages = True # We found legacy data, we should write it out to messages.json later # Legacy Migration: Move role_, emoji_, sticker_ from channel_map to dedicated maps @@ -69,8 +69,8 @@ class MigrationState: with open(self.messages_file, "r", encoding="utf-8") as f: msg_data = json.load(f) self.message_map = msg_data.get("messages", {}) - self.last_message_timestamps = msg_data.get("last_message_timestamps", {}) self.last_message_ids = msg_data.get("last_message_ids", {}) + self.last_message_timestamps = msg_data.get("last_message_timestamps", {}) migrated_messages = False # No need to force a migrating save since it already exists # 3. Save if we migrated any legacy data to separate maps/files @@ -95,8 +95,8 @@ class MigrationState: def save_messages(self): """Saves only the message tracking data.""" data = { - "last_message_timestamps": self.last_message_timestamps, "last_message_ids": self.last_message_ids, + "last_message_timestamps": self.last_message_timestamps, "messages": self.message_map } with open(self.messages_file, "w", encoding="utf-8") as f: @@ -201,6 +201,6 @@ class MigrationState: def clear_message_history(self): """Clears all message mappings and timestamps.""" self.message_map.clear() - self.last_message_timestamps.clear() self.last_message_ids.clear() + self.last_message_timestamps.clear() self.save_messages() diff --git a/src/ui/app.py b/src/ui/app.py index 1245363..e847e98 100644 --- a/src/ui/app.py +++ b/src/ui/app.py @@ -415,10 +415,26 @@ class MigrationCLI: progress.update(channel_task, total=total, completed=current, description=f"[{color}]{status} Channel: {item_name}") self.engine.is_running = True - await migrate_channels(self.engine, progress_callback=update_progress, force=force) + cloned_info = await migrate_channels(self.engine, progress_callback=update_progress, force=force) console.print("[bold green]Server Template cloned![/bold green]") - await log_audit_event(self.engine, "Server Template Cloned", "Successfully cloned channels and categories from Discord.") + + if cloned_info and cloned_info.get("structure"): + lines = ["Successfully cloned channels and categories from Discord:"] + # Sort categories but keep "No Category" at the bottom if it exists + cats = sorted(cloned_info["structure"].keys(), key=lambda x: (x == "No Category", x)) + for cat_name in cats: + channel_names = cloned_info["structure"][cat_name] + # Only print if the category itself was created OR it has channels created under it + if cat_name in cloned_info["categories_created"] or channel_names: + lines.append(f"- **{cat_name}**") + for ch_name in sorted(channel_names): + lines.append(f" - {ch_name}") + + audit_desc = "\n".join(lines) + await log_audit_event(self.engine, "Server Template Cloned", audit_desc) + else: + await log_audit_event(self.engine, "Server Template Cloned", "No new channels or categories were cloned.") except Exception as e: console.print(f"[bold red]Error during channel clone: {str(e)}[/bold red]") @@ -500,10 +516,14 @@ class MigrationCLI: progress.update(role_task, total=total, completed=current, description=f"[cyan]Syncing Role: {item_name}") self.engine.is_running = True - await migrate_roles(self.engine, progress_callback=update_progress, force=force) + cloned_roles = await migrate_roles(self.engine, progress_callback=update_progress, force=force) console.print("[bold green]Role migration complete![/bold green]") - await log_audit_event(self.engine, "Roles Cloned", "Successfully cloned roles and baseline role permissions from Discord.") + if cloned_roles: + audit_desc = "Successfully cloned these roles and their baseline permissions:\n" + "\n".join(f"- {r}" for r in cloned_roles) + await log_audit_event(self.engine, "Roles Cloned", audit_desc) + else: + await log_audit_event(self.engine, "Roles Cloned", "No new roles were cloned.") except Exception as e: console.print(f"[bold red]Error during role migration: {str(e)}[/bold red]") @@ -547,10 +567,26 @@ class MigrationCLI: progress.update(perm_task, total=total, completed=current, description=f"[cyan]Syncing: {item_name}") self.engine.is_running = True - await sync_permissions(self.engine, progress_callback=update_progress) + synced_info = await sync_permissions(self.engine, progress_callback=update_progress) console.print("[bold green]Permission synchronization complete![/bold green]") - await log_audit_event(self.engine, "Permissions Synced", "Successfully synchronized channel and category permission overrides.") + + if synced_info and synced_info.get("structure"): + lines = ["Successfully synchronized channel and category permission overrides:"] + # Sort categories but keep "No Category" at the bottom + cats = sorted(synced_info["structure"].keys(), key=lambda x: (x == "No Category", x)) + for cat_name in cats: + channel_names = synced_info["structure"][cat_name] + # For permissions, we show everything that was successfully synced + if cat_name in synced_info["categories_synced"] or channel_names: + lines.append(f"- **{cat_name}**") + for ch_name in sorted(channel_names): + lines.append(f" - {ch_name}") + + audit_desc = "\n".join(lines) + await log_audit_event(self.engine, "Permissions Synced", audit_desc) + else: + await log_audit_event(self.engine, "Permissions Synced", "No permissions were synchronized.") except Exception as e: console.print(f"[bold red]Error during permission sync: {str(e)}[/bold red]") @@ -652,7 +688,7 @@ class MigrationCLI: progress.update(emoji_task, total=total, completed=current, description=f"[cyan]Copying {item_type}: {item_name}") self.engine.is_running = True - await migrate_emojis( + cloned_assets = await migrate_emojis( self.engine, progress_callback=update_progress, types_to_include=types_to_include, @@ -660,7 +696,22 @@ class MigrationCLI: ) console.print("[bold green]Migration complete![/bold green]") - await log_audit_event(self.engine, "Emojis & Stickers Cloned", "Successfully synchronized emojis and stickers from Discord.") + if cloned_assets and (cloned_assets.get("Emoji") or cloned_assets.get("Sticker")): + lines = [] + if cloned_assets.get("Emoji"): + lines.append("Successfully cloned these emojis:") + for name, e_id in cloned_assets["Emoji"].items(): + lines.append(f"- {name} <:{name}:{e_id}>") + if cloned_assets.get("Sticker"): + if lines: lines.append("") + lines.append("Successfully cloned these stickers:") + for name, s_id in cloned_assets["Sticker"].items(): + lines.append(f"- {name}") + + audit_desc = "\n".join(lines) + await log_audit_event(self.engine, "Emojis & Stickers Cloned", audit_desc) + else: + await log_audit_event(self.engine, "Emojis & Stickers Cloned", "No new emojis or stickers were cloned.") except Exception as e: console.print(f"[bold red]Error during emoji migration: {str(e)}[/bold red]") @@ -718,9 +769,36 @@ class MigrationCLI: color = "green" if status == "DONE" else "red" if status == "ERROR" else "yellow" console.print(f"{item} [[bold {color}]{status}[/bold {color}]]") - await sync_server_metadata(self.engine, progress_callback, components=components) - console.print("[bold green]Server metadata sync finished![/bold green]") - await log_audit_event(self.engine, "Server Metadata Synced", "Successfully synchronized the community icon, banner, and name.") + cloned_data = await sync_server_metadata(self.engine, progress_callback, components=components) + console.print("[bold green]Server profile sync finished![/bold green]") + + audit_lines = ["Successfully synchronized Community profile:"] + if "name" in cloned_data: + audit_lines.append(f"- **Name**: {cloned_data['name']}") + if "icon" in cloned_data: + audit_lines.append(f"- **Icon**") + if "banner" in cloned_data: + audit_lines.append(f"- **Banner**") + + files = [] + if "icon" in cloned_data: + icon_ext = "gif" if cloned_data["icon"].startswith(b"GIF") else "png" + files.append({ + "filename": f"icon.{icon_ext}", + "data": cloned_data["icon"] + }) + if "banner" in cloned_data: + banner_ext = "gif" if cloned_data["banner"].startswith(b"GIF") else "png" + files.append({ + "filename": f"banner.{banner_ext}", + "data": cloned_data["banner"] + }) + + audit_desc = "\n".join(audit_lines) + if cloned_data: + await log_audit_event(self.engine, "Server Profile Synced", audit_desc, files=files) + else: + await log_audit_event(self.engine, "Server Profile Synced", "No profile information was synchronized.") except Exception as e: console.print(f"[bold red]Error during metadata sync: {str(e)}[/bold red]") finally: @@ -1019,17 +1097,21 @@ class MigrationCLI: console.print(f"\n[bold green]Success! {result_stats['messages']} messages migrated to {target_channel.get('name')}.[/bold green]") - audit_text = ( - f"**Message info:**\n" - f"first message: {result_stats['first_message_url'] or 'N/A'}\n" - f"last message: {result_stats['last_message_url'] or 'N/A'}\n" - f"**Stats:**\n" - f"{stats['messages']} messages\n" - f"{stats['attachments']} attachments\n" - f"{stats['threads']} threads\n" - f"Successfully migrated to <#{target_channel.get('id')}>\n" - ) - await log_audit_event(self.engine, "Message History Migrated", audit_text) + lines = [f"Successfully migrated messages from Discord [cyan]#{source_channel.name}[/cyan] to [blue]#{target_channel.get('name')}[/blue]:"] + lines.append(f"\n**Stats:**") + lines.append(f"- {result_stats['messages']} messages") + lines.append(f"- {result_stats['attachments']} attachments") + lines.append(f"- {result_stats['threads']} threads") + + if result_stats.get('first_message_url') or result_stats.get('last_message_url'): + lines.append(f"\n**Message Info:**") + if result_stats.get('first_message_url'): + lines.append(f"- First message: <{result_stats['first_message_url']}>") + if result_stats.get('last_message_url'): + lines.append(f"- Last message: <{result_stats['last_message_url']}>") + + audit_desc = "\n".join(lines) + await log_audit_event(self.engine, "Message History Migrated", audit_desc) except Exception as e: console.print(f"[bold red]Migration encountered an error: {str(e)}[/bold red]") @@ -1088,7 +1170,7 @@ class MigrationCLI: count = await danger_delete_all_channels(self.engine, progress_callback=on_channel_deleted) console.print(f"[bold green]{count} channels/categories deleted.[/bold green]") - await log_audit_event(self.engine, "Danger Zone: Channels Wiped", f"Administrators deleted {count} channels and categories from the community.") + await log_audit_event(self.engine, "Danger Zone: Channels Wiped", f"Deleted {count} channels and categories from the community.") console.print("[bold green]Done.[/bold green]") except Exception as e: console.print(f"[bold red]Error: {e}[/bold red]") @@ -1155,7 +1237,7 @@ class MigrationCLI: count = await danger_delete_all_roles(self.engine, progress_callback=on_role_deleted) console.print(f"[bold green]{count} roles deleted.[/bold green]") - await log_audit_event(self.engine, "Danger Zone: Roles Wiped", f"Administrators deleted {count} roles from the community.") + await log_audit_event(self.engine, "Danger Zone: Roles Wiped", f"Deleted {count} roles from the community.") console.print("[bold green]Done.[/bold green]") except Exception as e: console.print(f"[bold red]Error: {e}[/bold red]") @@ -1189,7 +1271,7 @@ class MigrationCLI: counts = await danger_delete_all_emojis_and_stickers(self.engine, progress_callback=on_asset_deleted) console.print(f"[bold green]{counts.get('emojis', 0)} emojis deleted.[/bold green]") console.print(f"[bold green]{counts.get('stickers', 0)} stickers deleted.[/bold green]") - await log_audit_event(self.engine, "Danger Zone: Assets Wiped", f"Administrators deleted {counts.get('emojis', 0)} emojis and {counts.get('stickers', 0)} stickers.") + await log_audit_event(self.engine, "Danger Zone: Assets Wiped", f"Deleted {counts.get('emojis', 0)} emojis and {counts.get('stickers', 0)} stickers.") console.print("[bold green]Done.[/bold green]") except Exception as e: console.print(f"[bold red]Error: {e}[/bold red]")