highlight already cloned channels, roles

This commit is contained in:
rambros 2026-03-03 22:56:24 +05:30
parent 8cf97f9739
commit 650aea98eb

View file

@ -314,26 +314,29 @@ class ShuttlePane(Container):
modal = ProgressScreen() modal = ProgressScreen()
self.app.push_screen(modal) self.app.push_screen(modal)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
discord_started = False connections_started = False
try: try:
# Phase 1: Connect early to fetch source server structure for preview # Phase 1: Connect early to fetch both source and target structure for preview
modal.set_status("Connecting to Source Server (Discord) for Preview...") modal.set_status("Connecting to Source and Target Servers for Preview...")
try: try:
await self.engine.discord_reader.start() await self.engine.start_connections()
discord_started = True connections_started = True
except Exception as e: except Exception as e:
logger.warning(f"Could not pre-connect to Discord for Clone preview: {e}") logger.warning(f"Could not pre-connect for Clone preview: {e}")
modal.set_status(f"Awaiting Confirmation for {len(selections)} Operations...") modal.set_status(f"Awaiting Confirmation for {len(selections)} Operations...")
# Fetch and display live preview from Discord # Fetch and display live preview with presence highlighting
preview = await self._fetch_clone_preview(selections) if discord_started else {} preview = await self._fetch_clone_preview(selections) if connections_started else {}
if "roles" in preview: if "roles" in preview:
roles = preview["roles"] roles = preview["roles"]
modal.write(f"[bold cyan]Roles to be Cloned ({len(roles)}):[/bold cyan]") modal.write(f"[bold cyan]Roles to be Cloned ({len(roles)}):[/bold cyan]")
for r in roles[:15]: for name, exists in roles[:15]:
modal.write(f" - {r}") if exists:
modal.write(f" - [green]{name}[/green]")
else:
modal.write(f" - {name}")
if len(roles) > 15: if len(roles) > 15:
modal.write(f" [dim]... and {len(roles)-15} more[/dim]") modal.write(f" [dim]... and {len(roles)-15} more[/dim]")
modal.write("") modal.write("")
@ -341,11 +344,35 @@ class ShuttlePane(Container):
if "structure" in preview: if "structure" in preview:
structure = preview["structure"] structure = preview["structure"]
total_ch = sum(len(chans) for chans in structure.values()) total_ch = sum(len(chans) for chans in structure.values())
modal.write(f"[bold cyan]Server Structure ({len(structure)} Categories, {total_ch} Channels):[/bold cyan]") num_cats = sum(1 for k in structure if k is not None)
for cat_name, channels in structure.items(): modal.write(f"[bold cyan]Server Structure ({num_cats} Categories, {total_ch} Channels):[/bold cyan]")
modal.write(f" [bold yellow]📁 {cat_name}[/bold yellow]")
for ch_name in channels: # Show uncategorized channels first at the top
if None in structure:
_, _, uncat_channels = structure[None]
for ch_name, ch_exists in uncat_channels:
if ch_exists:
modal.write(f" - [green]# {ch_name}[/green]")
else:
modal.write(f" - # {ch_name}") modal.write(f" - # {ch_name}")
for cat_id, (cat_name, cat_exists, channels) in structure.items():
if cat_id is None:
continue # already shown above
cat_color = "green" if cat_exists else "bold yellow"
modal.write(f" [{cat_color}]📁 {cat_name}[/{cat_color}]")
for ch_name, ch_exists in channels:
if ch_exists:
modal.write(f" - [green]# {ch_name}[/green]")
else:
modal.write(f" - # {ch_name}")
modal.write("")
if connections_started:
# Add highlighting note
target_valid = await self.engine.writer.validate()
community_name = target_valid.get("community_name", "the target")
modal.write(f"[dim]Note: entities shown in 'green' are already present in {community_name} community[/dim]")
modal.write("") modal.write("")
choice = await modal.phase_wait_confirm( choice = await modal.phase_wait_confirm(
@ -366,7 +393,8 @@ class ShuttlePane(Container):
modal.cancel_callback = lambda: setattr(self.engine, "is_running", False) modal.cancel_callback = lambda: setattr(self.engine, "is_running", False)
modal.phase_progress() modal.phase_progress()
# Re-confirm connections (reader is already started, writer starts now) # Connections already started above
if not connections_started:
await self.engine.start_connections() await self.engine.start_connections()
self.engine.is_running = True self.engine.is_running = True
@ -972,33 +1000,56 @@ class ShuttlePane(Container):
return preview return preview
async def _fetch_clone_preview(self, selections: list[str]) -> dict[str, Any]: async def _fetch_clone_preview(self, selections: list[str]) -> dict[str, Any]:
"""Fetches preview data from Discord (source server) for cloning confirmation.""" """Fetches preview data from Discord (source server) for cloning confirmation,
comparing with existing entities on the target server for presence highlighting."""
preview = {} preview = {}
reader = self.engine.discord_reader reader = self.engine.discord_reader
writer = self.engine.writer
is_fluxer = self.target_platform == "fluxer"
# Fetch target data for comparison
target_roles = []
target_channels = []
try:
if is_fluxer:
target_roles_raw = await writer.client.get_guild_roles(self.engine.config.target_server_id)
target_roles = [r.get("name", "").lower() for r in target_roles_raw]
else:
server = await writer._get_server()
target_roles = [r.name.lower() for r in server.roles.values()]
target_chans_raw = await writer.get_channels()
target_channels = [c.get("name", "").lower() for c in target_chans_raw]
except Exception as e:
logger.warning(f"Clone Preview: failed to fetch target data for comparison: {e}")
try: try:
if "sub_clone_roles" in selections: if "sub_clone_roles" in selections:
roles = await reader.get_roles() roles = await reader.get_roles()
preview["roles"] = [r.name for r in roles] preview["roles"] = [(r.name, r.name.lower() in target_roles) for r in roles]
except Exception as e: except Exception as e:
logger.warning(f"Clone Preview: failed to fetch roles: {e}") logger.warning(f"Clone Preview: failed to fetch roles: {e}")
try: try:
if "sub_clone_channels" in selections: if "sub_clone_channels" in selections:
# Build hierarchy # Build hierarchy
categories = await reader.get_categories() src_categories = await reader.get_categories()
channels = await reader.get_channels() src_channels = await reader.get_channels()
# group channels by category parent # structure[cat_id] = (cat_name, cat_exists, [(ch_name, ch_exists), ...])
structure = {} structure = {}
# Handle categorization for cat in src_categories:
for cat in categories: cat_exists = cat.name.lower() in target_channels
# In discord.py fetch_channels() returns full objects structure[cat.id] = (cat.name, cat_exists, [])
structure[cat.name] = [ch.name for ch in channels if ch.category_id == cat.id]
# Handle uncategorized for ch in src_channels:
uncategorized = [ch.name for ch in channels if ch.category_id is None] ch_exists = ch.name.lower() in target_channels
if uncategorized: if ch.category_id in structure:
structure["No Category"] = uncategorized structure[ch.category_id][2].append((ch.name, ch_exists))
else:
if None not in structure:
structure[None] = ("No Category", False, [])
structure[None][2].append((ch.name, ch_exists))
preview["structure"] = structure preview["structure"] = structure
except Exception as e: except Exception as e: