add audit logs channel
This commit is contained in:
parent
17d9094084
commit
1a17fad4a9
4 changed files with 86 additions and 1 deletions
57
src/core/audit.py
Normal file
57
src/core/audit.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import logging
|
||||||
|
from src.core.base import MigrationContext
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
async def log_audit_event(context: MigrationContext, title: str, description: str) -> 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
|
||||||
|
if not context.state.audit_log_channel:
|
||||||
|
logger.info("Audit log channel not found 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()
|
||||||
|
channel_id = None
|
||||||
|
|
||||||
|
for ch in channels:
|
||||||
|
name = str(ch.get("name", "")).lower()
|
||||||
|
if name in ["fluxer-reaper", "fluxer_reaper"]:
|
||||||
|
channel_id = str(ch.get("id"))
|
||||||
|
logger.info(f"Found existing audit channel: {channel_id}")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not channel_id:
|
||||||
|
logger.info("Audit log channel not found. Creating #fluxer-reaper.")
|
||||||
|
# Create channel
|
||||||
|
channel_id = await context.fluxer_writer.create_channel(
|
||||||
|
name="fluxer-reaper",
|
||||||
|
topic="Fluxer Reaper - Migration audit logs.",
|
||||||
|
type=0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Immediately lock down 'View Channel' (1024) for @everyone (community_id)
|
||||||
|
await context.fluxer_writer.set_channel_permission(
|
||||||
|
channel_id=channel_id,
|
||||||
|
overwrite_id=context.config.fluxer_community_id, # @everyone matches community ID
|
||||||
|
allow=0,
|
||||||
|
deny=1024,
|
||||||
|
is_role=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Save permanently
|
||||||
|
context.state.audit_log_channel = channel_id
|
||||||
|
context.state.save_state()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to setup audit log channel: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to send audit log event: {e}")
|
||||||
|
|
@ -435,6 +435,12 @@ class FluxerWriter:
|
||||||
# Delete non-category channels first, then categories
|
# Delete non-category channels first, then categories
|
||||||
sorted_channels = sorted(channels, key=lambda c: 0 if c.get("type") == 4 else -1)
|
sorted_channels = sorted(channels, key=lambda c: 0 if c.get("type") == 4 else -1)
|
||||||
for ch in sorted_channels:
|
for ch in sorted_channels:
|
||||||
|
name = str(ch.get("name", "")).lower()
|
||||||
|
if name in ["fluxer-reaper", "fluxer_reaper"]:
|
||||||
|
logger.info(f"Danger Zone: Skipping deletion of audit channel {name}")
|
||||||
|
total -= 1
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.client.delete_channel(ch["id"])
|
await self.client.delete_channel(ch["id"])
|
||||||
deleted += 1
|
deleted += 1
|
||||||
|
|
@ -454,6 +460,12 @@ class FluxerWriter:
|
||||||
total = len(channels)
|
total = len(channels)
|
||||||
processed = 0
|
processed = 0
|
||||||
for ch in channels:
|
for ch in channels:
|
||||||
|
name = str(ch.get("name", "")).lower()
|
||||||
|
if name in ["fluxer-reaper", "fluxer_reaper"]:
|
||||||
|
logger.info(f"Danger Zone: Skipping permission reset for audit channel {name}")
|
||||||
|
total -= 1
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Fetch existing overwrites and delete each one
|
# Fetch existing overwrites and delete each one
|
||||||
overwrites = ch.get("permission_overwrites", [])
|
overwrites = ch.get("permission_overwrites", [])
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ class MigrationState:
|
||||||
self.emoji_map: Dict[str, str] = {}
|
self.emoji_map: Dict[str, str] = {}
|
||||||
self.sticker_map: Dict[str, str] = {}
|
self.sticker_map: Dict[str, str] = {}
|
||||||
|
|
||||||
|
# audit log tracking
|
||||||
|
self.audit_log_channel: str | None = None
|
||||||
|
|
||||||
# message tracking
|
# message tracking
|
||||||
self.message_map: Dict[str, str] = {}
|
self.message_map: Dict[str, str] = {}
|
||||||
self.last_message_timestamps: Dict[str, str] = {}
|
self.last_message_timestamps: Dict[str, str] = {}
|
||||||
|
|
@ -35,6 +38,7 @@ class MigrationState:
|
||||||
self.role_map = data.get("roles", {})
|
self.role_map = data.get("roles", {})
|
||||||
self.emoji_map = data.get("emojis", {})
|
self.emoji_map = data.get("emojis", {})
|
||||||
self.sticker_map = data.get("stickers", {})
|
self.sticker_map = data.get("stickers", {})
|
||||||
|
self.audit_log_channel = data.get("audit_log_channel")
|
||||||
|
|
||||||
# Check for legacy messages in state.json
|
# Check for legacy messages in state.json
|
||||||
if "messages" in data or "last_message_timestamps" in data:
|
if "messages" in data or "last_message_timestamps" in data:
|
||||||
|
|
@ -79,7 +83,8 @@ class MigrationState:
|
||||||
"categories": self.category_map,
|
"categories": self.category_map,
|
||||||
"roles": self.role_map,
|
"roles": self.role_map,
|
||||||
"emojis": self.emoji_map,
|
"emojis": self.emoji_map,
|
||||||
"stickers": self.sticker_map
|
"stickers": self.sticker_map,
|
||||||
|
"audit_log_channel": self.audit_log_channel
|
||||||
}
|
}
|
||||||
with open(self.state_file, "w", encoding="utf-8") as f:
|
with open(self.state_file, "w", encoding="utf-8") as f:
|
||||||
json.dump(data, f, indent=4)
|
json.dump(data, f, indent=4)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from src.core.emoji_stickers import sync_assets_state, migrate_emojis
|
||||||
from src.core.server_metadata import sync_server_metadata
|
from src.core.server_metadata import sync_server_metadata
|
||||||
from src.core.migrate_message import analyze_migration, migrate_messages
|
from src.core.migrate_message import analyze_migration, migrate_messages
|
||||||
from src.core.danger_zone import danger_remove_logo_and_banner, danger_delete_all_channels, danger_reset_channel_permissions, danger_delete_all_roles, danger_delete_all_emojis_and_stickers
|
from src.core.danger_zone import danger_remove_logo_and_banner, danger_delete_all_channels, danger_reset_channel_permissions, danger_delete_all_roles, danger_delete_all_emojis_and_stickers
|
||||||
|
from src.core.audit import log_audit_event
|
||||||
|
|
||||||
class RateLimitHandler(logging.Handler):
|
class RateLimitHandler(logging.Handler):
|
||||||
"""Intersects library logs to print clean rate limit messages."""
|
"""Intersects library logs to print clean rate limit messages."""
|
||||||
|
|
@ -417,6 +418,7 @@ class MigrationCLI:
|
||||||
await migrate_channels(self.engine, progress_callback=update_progress, force=force)
|
await migrate_channels(self.engine, progress_callback=update_progress, force=force)
|
||||||
|
|
||||||
console.print("[bold green]Server Template cloned![/bold green]")
|
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.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error during channel clone: {str(e)}[/bold red]")
|
console.print(f"[bold red]Error during channel clone: {str(e)}[/bold red]")
|
||||||
|
|
@ -501,6 +503,7 @@ class MigrationCLI:
|
||||||
await migrate_roles(self.engine, progress_callback=update_progress, force=force)
|
await migrate_roles(self.engine, progress_callback=update_progress, force=force)
|
||||||
|
|
||||||
console.print("[bold green]Role migration complete![/bold green]")
|
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.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error during role migration: {str(e)}[/bold red]")
|
console.print(f"[bold red]Error during role migration: {str(e)}[/bold red]")
|
||||||
|
|
@ -547,6 +550,7 @@ class MigrationCLI:
|
||||||
await sync_permissions(self.engine, progress_callback=update_progress)
|
await sync_permissions(self.engine, progress_callback=update_progress)
|
||||||
|
|
||||||
console.print("[bold green]Permission synchronization complete![/bold green]")
|
console.print("[bold green]Permission synchronization complete![/bold green]")
|
||||||
|
await log_audit_event(self.engine, "Permissions Synced", "Successfully synchronized channel and category permission overrides.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error during permission sync: {str(e)}[/bold red]")
|
console.print(f"[bold red]Error during permission sync: {str(e)}[/bold red]")
|
||||||
|
|
@ -656,6 +660,7 @@ class MigrationCLI:
|
||||||
)
|
)
|
||||||
|
|
||||||
console.print("[bold green]Migration complete![/bold green]")
|
console.print("[bold green]Migration complete![/bold green]")
|
||||||
|
await log_audit_event(self.engine, "Emojis & Stickers Cloned", "Successfully synchronized emojis and stickers from Discord.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error during emoji migration: {str(e)}[/bold red]")
|
console.print(f"[bold red]Error during emoji migration: {str(e)}[/bold red]")
|
||||||
|
|
@ -715,6 +720,7 @@ class MigrationCLI:
|
||||||
|
|
||||||
await sync_server_metadata(self.engine, progress_callback, components=components)
|
await sync_server_metadata(self.engine, progress_callback, components=components)
|
||||||
console.print("[bold green]Server metadata sync finished![/bold green]")
|
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.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error during metadata sync: {str(e)}[/bold red]")
|
console.print(f"[bold red]Error during metadata sync: {str(e)}[/bold red]")
|
||||||
finally:
|
finally:
|
||||||
|
|
@ -962,6 +968,7 @@ class MigrationCLI:
|
||||||
)
|
)
|
||||||
|
|
||||||
console.print(f"\n[bold green]Success! {count} messages migrated to {target_channel.get('name')}.[/bold green]")
|
console.print(f"\n[bold green]Success! {count} messages migrated to {target_channel.get('name')}.[/bold green]")
|
||||||
|
await log_audit_event(self.engine, "Message History Migrated", f"Successfully migrated {count} messages to #{target_channel.get('name')}.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Migration encountered an error: {str(e)}[/bold red]")
|
console.print(f"[bold red]Migration encountered an error: {str(e)}[/bold red]")
|
||||||
|
|
@ -1020,6 +1027,7 @@ class MigrationCLI:
|
||||||
|
|
||||||
count = await danger_delete_all_channels(self.engine, progress_callback=on_channel_deleted)
|
count = await danger_delete_all_channels(self.engine, progress_callback=on_channel_deleted)
|
||||||
console.print(f"[bold green]{count} channels/categories deleted.[/bold green]")
|
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.")
|
||||||
console.print("[bold green]Done.[/bold green]")
|
console.print("[bold green]Done.[/bold green]")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error: {e}[/bold red]")
|
console.print(f"[bold red]Error: {e}[/bold red]")
|
||||||
|
|
@ -1052,6 +1060,7 @@ class MigrationCLI:
|
||||||
|
|
||||||
count = await danger_reset_channel_permissions(self.engine, progress_callback=on_perm_reset)
|
count = await danger_reset_channel_permissions(self.engine, progress_callback=on_perm_reset)
|
||||||
console.print(f"[bold green]Permissions reset on {count} channels/categories.[/bold green]")
|
console.print(f"[bold green]Permissions reset on {count} channels/categories.[/bold green]")
|
||||||
|
await log_audit_event(self.engine, "Danger Zone: Permissions Wiped", f"Administrators reset permissions on {count} channels and categories.")
|
||||||
console.print("[bold green]Done.[/bold green]")
|
console.print("[bold green]Done.[/bold green]")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error: {e}[/bold red]")
|
console.print(f"[bold red]Error: {e}[/bold red]")
|
||||||
|
|
@ -1085,6 +1094,7 @@ class MigrationCLI:
|
||||||
|
|
||||||
count = await danger_delete_all_roles(self.engine, progress_callback=on_role_deleted)
|
count = await danger_delete_all_roles(self.engine, progress_callback=on_role_deleted)
|
||||||
console.print(f"[bold green]{count} roles deleted.[/bold green]")
|
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.")
|
||||||
console.print("[bold green]Done.[/bold green]")
|
console.print("[bold green]Done.[/bold green]")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error: {e}[/bold red]")
|
console.print(f"[bold red]Error: {e}[/bold red]")
|
||||||
|
|
@ -1118,6 +1128,7 @@ class MigrationCLI:
|
||||||
counts = await danger_delete_all_emojis_and_stickers(self.engine, progress_callback=on_asset_deleted)
|
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('emojis', 0)} emojis deleted.[/bold green]")
|
||||||
console.print(f"[bold green]{counts.get('stickers', 0)} stickers 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.")
|
||||||
console.print("[bold green]Done.[/bold green]")
|
console.print("[bold green]Done.[/bold green]")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"[bold red]Error: {e}[/bold red]")
|
console.print(f"[bold red]Error: {e}[/bold red]")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue