From ee391701a763385b38cd959474316509875f8339 Mon Sep 17 00:00:00 2001 From: rambros Date: Fri, 20 Feb 2026 12:14:38 +0530 Subject: [PATCH] add server metadata sync --- src/core/engine.py | 40 +++++++++++++++++++++++++++++++++++++++ src/discord_bot/reader.py | 7 ++++++- src/fluxer_bot/writer.py | 30 +++++++++++++++++++++++++++++ src/ui/app.py | 30 +++++++++++++++++++++++++---- 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/src/core/engine.py b/src/core/engine.py index 8d7c27e..707108f 100644 --- a/src/core/engine.py +++ b/src/core/engine.py @@ -56,6 +56,46 @@ class MigrationEngine: await self.discord_reader.close() await self.fluxer_writer.close() + async def sync_server_metadata(self, progress_callback: Callable[[str, str], Awaitable[None]]): + """Syncs the server name, logo and banner.""" + metadata = await self.discord_reader.get_server_metadata() + name = metadata.get("name") + + # 1. Sync Name + try: + await self.fluxer_writer.update_guild_metadata(name=name) + await progress_callback("Server Name", "DONE") + except Exception: + await progress_callback("Server Name", "ERROR") + + # 2. Sync Icon + try: + icon_bytes = None + if self.discord_reader.guild and self.discord_reader.guild.icon: + icon_bytes = await self.discord_reader.download_asset(self.discord_reader.guild.icon) + + if icon_bytes: + await self.fluxer_writer.update_guild_metadata(icon=icon_bytes) + await progress_callback("Server Icon", "DONE") + else: + await progress_callback("Server Icon", "SKIP") + except Exception: + await progress_callback("Server Icon", "ERROR") + + # 3. Sync Banner + try: + banner_bytes = None + if self.discord_reader.guild and self.discord_reader.guild.banner: + banner_bytes = await self.discord_reader.download_asset(self.discord_reader.guild.banner) + + if banner_bytes: + await self.fluxer_writer.update_guild_metadata(banner=banner_bytes) + await progress_callback("Server Banner", "DONE") + else: + await progress_callback("Server Banner", "SKIP") + except Exception: + await progress_callback("Server Banner", "ERROR") + async def migrate_channels(self, progress_callback: Callable[[str, int, int], Awaitable[None]] | None = None): """Clones categories and text channels.""" categories = await self.discord_reader.get_categories() diff --git a/src/discord_bot/reader.py b/src/discord_bot/reader.py index 788458b..5888350 100644 --- a/src/discord_bot/reader.py +++ b/src/discord_bot/reader.py @@ -49,9 +49,14 @@ class DiscordReader: return { "name": self.guild.name, "id": str(self.guild.id), - "icon_url": self.guild.icon.url if self.guild.icon else None + "icon_url": self.guild.icon.url if self.guild.icon else None, + "banner_url": self.guild.banner.url if self.guild.banner else None } + async def download_asset(self, asset: discord.Asset) -> bytes: + """Downloads an asset (icon, banner) into memory.""" + return await asset.read() + async def get_categories(self): if not self.guild: return [] diff --git a/src/fluxer_bot/writer.py b/src/fluxer_bot/writer.py index 7bca939..dfa3486 100644 --- a/src/fluxer_bot/writer.py +++ b/src/fluxer_bot/writer.py @@ -115,6 +115,36 @@ class FluxerWriter: print(f"Failed to copy emoji {name}: {e}") return "" + async def update_guild_metadata(self, name: Optional[str] = None, icon: Optional[bytes] = None, banner: Optional[bytes] = None) -> None: + """ + Updates the Fluxer community name, icon, and banner. + """ + assert self.client is not None + + kwargs = {} + if banner: + import base64 + image_data = base64.b64encode(banner).decode("ascii") + if banner.startswith(b"\x89PNG"): + mime_type = "image/png" + elif banner.startswith(b"\xff\xd8\xff"): + mime_type = "image/jpeg" + elif banner.startswith(b"GIF89a") or banner.startswith(b"GIF87a"): + mime_type = "image/gif" + else: + mime_type = "image/png" + kwargs["banner"] = f"data:{mime_type};base64,{image_data}" + + try: + await self.client.modify_guild( + guild_id=self.community_id, + name=name, + icon=icon, + **kwargs + ) + except Exception as e: + print(f"Failed to update community metadata: {e}") + async def close(self): """Cleanly close connection.""" if self.client: diff --git a/src/ui/app.py b/src/ui/app.py index f857a93..9289dae 100644 --- a/src/ui/app.py +++ b/src/ui/app.py @@ -37,14 +37,15 @@ class MigrationCLI: console.print("(1) Clone Server Template (Channels & Categories)") console.print("(2) Copy Roles & Permissions") console.print("(3) Copy Emojis & Stickers") - console.print("(4) Migrate message history") + console.print("(4) Sync Server Name, Logo and Banner") + console.print("(5) Migrate message history") val_status = "[bold green][VALID][/bold green]" if self.tokens_valid else "[bold red][INVALID][/bold red]" - console.print(f"(5) Configuration {val_status}") + console.print(f"(6) Configuration {val_status}") console.print("(Q) Exit") - choice = Prompt.ask("Select an option", choices=["1", "2", "3", "4", "5", "Q", "q"], default="1").upper() + choice = Prompt.ask("Select an option", choices=["1", "2", "3", "4", "5", "6", "Q", "q"], default="1").upper() if choice == "1": await self.clone_server_template() @@ -53,8 +54,10 @@ class MigrationCLI: elif choice == "3": await self.copy_emojis() elif choice == "4": - await self.migrate_message_history() + await self.sync_server_metadata() elif choice == "5": + await self.migrate_message_history() + elif choice == "6": await self.edit_configuration() elif choice == "Q": console.print("[yellow]Exiting tool...[/yellow]") @@ -257,6 +260,25 @@ class MigrationCLI: await self.engine.close_connections() self.engine.is_running = False + async def sync_server_metadata(self): + if not Confirm.ask("Are you sure you want to sync server name, logo and banner?"): + return + + console.print("\n[bold green]Syncing Server Metadata...[/bold green]") + + async def progress_callback(item: str, status: str): + color = "green" if status == "DONE" else "red" if status == "ERROR" else "yellow" + console.print(f"{item} [[bold {color}]{status}[/bold {color}]]") + + try: + await self.engine.start_connections() + await self.engine.sync_server_metadata(progress_callback) + console.print("[bold green]Server metadata sync finished![/bold green]") + except Exception as e: + console.print(f"[bold red]Error during metadata sync: {str(e)}[/bold red]") + finally: + await self.engine.close_connections() + async def migrate_message_history(self): if not Confirm.ask("Are you sure you want to migrate message history?"): return