send more info in audit logs
This commit is contained in:
parent
4f557dc20c
commit
f5b9133947
8 changed files with 203 additions and 47 deletions
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
132
src/ui/app.py
132
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]")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue