preserve server strucutre order in fluxer

This commit is contained in:
rambros 2026-03-06 18:11:23 +05:30
parent 19750d2862
commit 92b73178ec
3 changed files with 29 additions and 16 deletions

View file

@ -59,8 +59,9 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
progress_callback: Optional callback receiving (item_name, status, current, total) progress_callback: Optional callback receiving (item_name, status, current, total)
force: If True, re-create channels even if they exist in state. force: If True, re-create channels even if they exist in state.
""" """
categories = await context.discord_reader.get_categories() # Sort by position to respect Discord arrangement
channels = await context.discord_reader.get_channels() 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 = { cloned_info = {
"categories_created": [], "categories_created": [],
@ -105,7 +106,8 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
if not context.is_running: break if not context.is_running: break
state_key = str(cat.id) 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) context.state.set_category_mapping(state_key, fluxer_id)
cloned_info["categories_created"].append(cat.name) cloned_info["categories_created"].append(cat.name)
if cat.name not in cloned_info["structure"]: 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}") 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 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( fluxer_id = await context.fluxer_writer.create_channel(
name=channel.name, name=channel.name,
@ -134,7 +137,8 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
type=0, type=0,
parent_id=parent_id, parent_id=parent_id,
nsfw=nsfw, nsfw=nsfw,
slowmode_delay=slowmode slowmode_delay=slowmode,
position=pos
) )
context.state.set_channel_mapping(state_key, fluxer_id) context.state.set_channel_mapping(state_key, fluxer_id)
cloned_info["channels_created"].append(channel.name) cloned_info["channels_created"].append(channel.name)
@ -151,7 +155,8 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
name=channel.name, name=channel.name,
topic=topic, topic=topic,
nsfw=nsfw, nsfw=nsfw,
slowmode_delay=slowmode slowmode_delay=slowmode,
position=pos
) )
current_idx += 1 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}") 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( await context.fluxer_writer.modify_channel(
channel_id=fluxer_id, channel_id=fluxer_id,
parent_id=parent_id, parent_id=parent_id,
name=channel.name, name=channel.name,
topic=topic, topic=topic,
nsfw=nsfw, nsfw=nsfw,
slowmode_delay=slowmode slowmode_delay=slowmode,
position=pos
) )
cloned_info["channels_synced"].append(channel.name) cloned_info["channels_synced"].append(channel.name)

View file

@ -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]: 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.""" """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: if not force:
roles = [r for r in roles if not context.state.get_fluxer_role_id(str(r.id))] 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, name=role.name,
color=role.color.value, color=role.color.value,
hoist=role.hoist, hoist=role.hoist,
mentionable=role.mentionable mentionable=role.mentionable,
position=role.position
) )
if fluxer_id: if fluxer_id:
context.state.set_role_mapping(str(role.id), fluxer_id) context.state.set_role_mapping(str(role.id), fluxer_id)

View file

@ -167,14 +167,14 @@ class FluxerWriter:
"permissions": permissions "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. Creates a new channel in the target Fluxer community.
Returns the new Fluxer channel ID. Returns the new Fluxer channel ID.
""" """
assert self.client is not None 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_channel = await self.client.create_guild_channel(
guild_id=self.community_id, guild_id=self.community_id,
@ -183,17 +183,18 @@ class FluxerWriter:
topic=topic or None, topic=topic or None,
parent_id=parent_id, parent_id=parent_id,
nsfw=nsfw, nsfw=nsfw,
rate_limit_per_user=slowmode_delay rate_limit_per_user=slowmode_delay,
position=position
) )
return str(guild_channel["id"]) 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. Updates channel properties.
""" """
assert self.client is not None 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: try:
await self.client.modify_channel( await self.client.modify_channel(
@ -202,7 +203,8 @@ class FluxerWriter:
topic=topic, topic=topic,
parent_id=parent_id, parent_id=parent_id,
nsfw=nsfw, nsfw=nsfw,
rate_limit_per_user=slowmode_delay rate_limit_per_user=slowmode_delay,
position=position
) )
except Forbidden as e: except Forbidden as e:
if getattr(e, 'code', None) == "NSFW_CONTENT_AGE_RESTRICTED": if getattr(e, 'code', None) == "NSFW_CONTENT_AGE_RESTRICTED":
@ -318,7 +320,7 @@ class FluxerWriter:
print(f"Failed to send marker: {e}") print(f"Failed to send marker: {e}")
return None 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. Creates a new role in the Fluxer community.
Returns the new Fluxer role ID. Returns the new Fluxer role ID.
@ -331,7 +333,8 @@ class FluxerWriter:
name=name, name=name,
color=color, color=color,
hoist=hoist, hoist=hoist,
mentionable=mentionable mentionable=mentionable,
position=position
) )
return str(role["id"]) return str(role["id"])
except Exception as e: except Exception as e: