From 92b73178ec2e9cfc46a95aa3e3c9e7b87f24e53f Mon Sep 17 00:00:00 2001 From: rambros Date: Fri, 6 Mar 2026 18:11:23 +0530 Subject: [PATCH] preserve server strucutre order in fluxer --- src/fluxer/clone_server.py | 20 ++++++++++++++------ src/fluxer/roles_permissions.py | 6 ++++-- src/fluxer/writer.py | 19 +++++++++++-------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/fluxer/clone_server.py b/src/fluxer/clone_server.py index 0efd6f4..e4df087 100644 --- a/src/fluxer/clone_server.py +++ b/src/fluxer/clone_server.py @@ -59,8 +59,9 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl progress_callback: Optional callback receiving (item_name, status, current, total) force: If True, re-create channels even if they exist in state. """ - categories = await context.discord_reader.get_categories() - channels = await context.discord_reader.get_channels() + # Sort by position to respect Discord arrangement + categories = sorted(await context.discord_reader.get_categories(), key=lambda c: getattr(c, 'position', 0)) + channels = sorted(await context.discord_reader.get_channels(), key=lambda c: getattr(c, 'position', 0)) cloned_info = { "categories_created": [], @@ -105,7 +106,8 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl if not context.is_running: break state_key = str(cat.id) - fluxer_id = await context.fluxer_writer.create_channel(cat.name, type=4) + pos = getattr(cat, 'position', None) + fluxer_id = await context.fluxer_writer.create_channel(cat.name, type=4, position=pos) context.state.set_category_mapping(state_key, fluxer_id) cloned_info["categories_created"].append(cat.name) if cat.name not in cloned_info["structure"]: @@ -127,6 +129,7 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl logger.debug(f"Creating channel {channel.name}: topic={topic}, nsfw={nsfw}, slowmode={slowmode}") parent_id = context.state.get_fluxer_category_id(str(channel.category_id)) if channel.category_id else None + pos = getattr(channel, 'position', None) fluxer_id = await context.fluxer_writer.create_channel( name=channel.name, @@ -134,7 +137,8 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl type=0, parent_id=parent_id, nsfw=nsfw, - slowmode_delay=slowmode + slowmode_delay=slowmode, + position=pos ) context.state.set_channel_mapping(state_key, fluxer_id) cloned_info["channels_created"].append(channel.name) @@ -151,7 +155,8 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl name=channel.name, topic=topic, nsfw=nsfw, - slowmode_delay=slowmode + slowmode_delay=slowmode, + position=pos ) current_idx += 1 @@ -169,13 +174,16 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl logger.debug(f"Syncing existing channel {channel.name} ({fluxer_id}): topic={topic}, nsfw={nsfw}, slowmode={slowmode}") + pos = getattr(channel, 'position', None) + await context.fluxer_writer.modify_channel( channel_id=fluxer_id, parent_id=parent_id, name=channel.name, topic=topic, nsfw=nsfw, - slowmode_delay=slowmode + slowmode_delay=slowmode, + position=pos ) cloned_info["channels_synced"].append(channel.name) diff --git a/src/fluxer/roles_permissions.py b/src/fluxer/roles_permissions.py index 45d7709..6589e4b 100644 --- a/src/fluxer/roles_permissions.py +++ b/src/fluxer/roles_permissions.py @@ -131,7 +131,8 @@ async def sync_permissions(context: MigrationContext, progress_callback: Callabl 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() + # Sort roles by position to respect Discord hierarchy + roles = sorted(await context.discord_reader.get_roles(), key=lambda r: r.position) if not force: roles = [r for r in roles if not context.state.get_fluxer_role_id(str(r.id))] @@ -151,7 +152,8 @@ async def migrate_roles(context: MigrationContext, progress_callback: Callable[[ name=role.name, color=role.color.value, hoist=role.hoist, - mentionable=role.mentionable + mentionable=role.mentionable, + position=role.position ) if fluxer_id: context.state.set_role_mapping(str(role.id), fluxer_id) diff --git a/src/fluxer/writer.py b/src/fluxer/writer.py index dc624f8..097a208 100644 --- a/src/fluxer/writer.py +++ b/src/fluxer/writer.py @@ -167,14 +167,14 @@ class FluxerWriter: "permissions": permissions } - async def create_channel(self, name: str, topic: str = "", type: int = 0, parent_id: Optional[str] = None, nsfw: bool = False, slowmode_delay: int = 0) -> str: + async def create_channel(self, name: str, topic: str = "", type: int = 0, parent_id: Optional[str] = None, nsfw: bool = False, slowmode_delay: int = 0, position: Optional[int] = None) -> str: """ Creates a new channel in the target Fluxer community. Returns the new Fluxer channel ID. """ assert self.client is not None - logger.debug(f"Fluxer: Creating channel {name} (type {type}) with topic='{topic}', nsfw={nsfw}, slowmode={slowmode_delay}") + logger.debug(f"Fluxer: Creating channel {name} (type {type}) with topic='{topic}', nsfw={nsfw}, slowmode={slowmode_delay}, position={position}") guild_channel = await self.client.create_guild_channel( guild_id=self.community_id, @@ -183,17 +183,18 @@ class FluxerWriter: topic=topic or None, parent_id=parent_id, nsfw=nsfw, - rate_limit_per_user=slowmode_delay + rate_limit_per_user=slowmode_delay, + position=position ) return str(guild_channel["id"]) - async def modify_channel(self, channel_id: str, parent_id: Optional[str] = None, name: Optional[str] = None, topic: Optional[str] = None, nsfw: Optional[bool] = None, slowmode_delay: Optional[int] = None) -> bool: + async def modify_channel(self, channel_id: str, parent_id: Optional[str] = None, name: Optional[str] = None, topic: Optional[str] = None, nsfw: Optional[bool] = None, slowmode_delay: Optional[int] = None, position: Optional[int] = None) -> bool: """ Updates channel properties. """ assert self.client is not None - logger.debug(f"Fluxer: Modifying channel {channel_id}: name={name}, topic='{topic}', parent_id={parent_id}, nsfw={nsfw}, slowmode={slowmode_delay}") + logger.debug(f"Fluxer: Modifying channel {channel_id}: name={name}, topic='{topic}', parent_id={parent_id}, nsfw={nsfw}, slowmode={slowmode_delay}, position={position}") try: await self.client.modify_channel( @@ -202,7 +203,8 @@ class FluxerWriter: topic=topic, parent_id=parent_id, nsfw=nsfw, - rate_limit_per_user=slowmode_delay + rate_limit_per_user=slowmode_delay, + position=position ) except Forbidden as e: if getattr(e, 'code', None) == "NSFW_CONTENT_AGE_RESTRICTED": @@ -318,7 +320,7 @@ class FluxerWriter: print(f"Failed to send marker: {e}") return None - async def create_role(self, name: str, color: int, hoist: bool, mentionable: bool) -> str: + async def create_role(self, name: str, color: int, hoist: bool, mentionable: bool, position: Optional[int] = None) -> str: """ Creates a new role in the Fluxer community. Returns the new Fluxer role ID. @@ -331,7 +333,8 @@ class FluxerWriter: name=name, color=color, hoist=hoist, - mentionable=mentionable + mentionable=mentionable, + position=position ) return str(role["id"]) except Exception as e: