add graceful handling of duplicate channel names
This commit is contained in:
parent
fea2f8f573
commit
27f8d98c69
4 changed files with 58 additions and 10 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from src.ui.disco_reaper_app import run_disco_reaper
|
from src.ui.reaper_app import run_disco_reaper
|
||||||
from src.core.configuration import load_config
|
from src.core.configuration import load_config
|
||||||
|
|
||||||
def setup_logging():
|
def setup_logging():
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import sys
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from src.ui.app import run_cli
|
from src.ui.shuttle_app import run_cli
|
||||||
from src.core.configuration import load_config
|
from src.core.configuration import load_config
|
||||||
|
|
||||||
def setup_logging():
|
def setup_logging():
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,10 @@ class DiscoReaperCLI:
|
||||||
console.print("(1) Backup Server Profile")
|
console.print("(1) Backup Server Profile")
|
||||||
console.print("(2) Backup Messages")
|
console.print("(2) Backup Messages")
|
||||||
console.print("(3) Update & Sync Backup")
|
console.print("(3) Update & Sync Backup")
|
||||||
console.print("(C) Configuration")
|
console.print("(4) Configuration")
|
||||||
console.print("(Q) Exit")
|
console.print("(Q) Exit")
|
||||||
|
|
||||||
choice = Prompt.ask("\nSelect an option", choices=["1", "2", "3", "C", "Q"], default="Q", show_choices=False).upper()
|
choice = Prompt.ask("\nSelect an option", choices=["1", "2", "3", "4", "Q"], default="Q", show_choices=False).upper()
|
||||||
|
|
||||||
if choice == "1":
|
if choice == "1":
|
||||||
await self.backup_server_profile()
|
await self.backup_server_profile()
|
||||||
|
|
@ -96,7 +96,7 @@ class DiscoReaperCLI:
|
||||||
await self.backup_messages()
|
await self.backup_messages()
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
await self.sync_backup()
|
await self.sync_backup()
|
||||||
elif choice == "C":
|
elif choice == "4":
|
||||||
await self.edit_configuration()
|
await self.edit_configuration()
|
||||||
elif choice == "Q":
|
elif choice == "Q":
|
||||||
await self.engine.close_connections()
|
await self.engine.close_connections()
|
||||||
|
|
@ -154,7 +154,11 @@ class DiscoReaperCLI:
|
||||||
await self.exporter.export_channels_structure()
|
await self.exporter.export_channels_structure()
|
||||||
|
|
||||||
# 2. Select Channels
|
# 2. Select Channels
|
||||||
|
with console.status("[yellow]Fetching channels & categories...[/yellow]"):
|
||||||
all_channels = await self.engine.discord_reader.get_channels()
|
all_channels = await self.engine.discord_reader.get_channels()
|
||||||
|
all_categories = await self.engine.discord_reader.get_categories()
|
||||||
|
cat_map = {c.id: c.name for c in all_categories}
|
||||||
|
|
||||||
# Filter for exportable channels
|
# Filter for exportable channels
|
||||||
eligible_channels = [
|
eligible_channels = [
|
||||||
c for c in all_channels
|
c for c in all_channels
|
||||||
|
|
@ -166,8 +170,19 @@ class DiscoReaperCLI:
|
||||||
return
|
return
|
||||||
|
|
||||||
console.print(f"\n[bold]Select Channels to Backup ({len(eligible_channels)} total):[/bold]")
|
console.print(f"\n[bold]Select Channels to Backup ({len(eligible_channels)} total):[/bold]")
|
||||||
|
|
||||||
|
# Identify duplicate names to show categories for them
|
||||||
|
name_counts = {}
|
||||||
|
for chan in eligible_channels:
|
||||||
|
name_counts[chan.name] = name_counts.get(chan.name, 0) + 1
|
||||||
|
|
||||||
for i, chan in enumerate(eligible_channels):
|
for i, chan in enumerate(eligible_channels):
|
||||||
console.print(f"({i+1}) {chan.name}")
|
display_name = chan.name
|
||||||
|
if name_counts[chan.name] > 1:
|
||||||
|
cat_name = cat_map.get(chan.category_id)
|
||||||
|
if cat_name:
|
||||||
|
display_name = f"{chan.name} [{cat_name}]"
|
||||||
|
console.print(f"({i+1}) {display_name}")
|
||||||
|
|
||||||
console.print("(A) [bold green]All Channels[/bold green]")
|
console.print("(A) [bold green]All Channels[/bold green]")
|
||||||
console.print("(B) Back")
|
console.print("(B) Back")
|
||||||
|
|
@ -1065,17 +1065,30 @@ class MigrationCLI:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with console.status("[yellow]Fetching Discord channels...[/yellow]"):
|
with console.status("[yellow]Fetching Discord channels & categories...[/yellow]"):
|
||||||
await self.engine.start_connections()
|
await self.engine.start_connections()
|
||||||
d_channels = await self.engine.discord_reader.get_channels()
|
d_channels = await self.engine.discord_reader.get_channels()
|
||||||
|
d_categories = await self.engine.discord_reader.get_categories()
|
||||||
|
d_cat_map = {c.id: c.name for c in d_categories}
|
||||||
|
|
||||||
if not d_channels:
|
if not d_channels:
|
||||||
console.print("[yellow]No text channels found in Discord server.[/yellow]")
|
console.print("[yellow]No text channels found in Discord server.[/yellow]")
|
||||||
return
|
return
|
||||||
|
|
||||||
console.print("\n[bold]Select Source Discord Channel:[/bold]")
|
console.print("\n[bold]Select Source Discord Channel:[/bold]")
|
||||||
|
|
||||||
|
# Identify duplicate names to show categories for them
|
||||||
|
d_name_counts = {}
|
||||||
|
for ch in d_channels:
|
||||||
|
d_name_counts[ch.name] = d_name_counts.get(ch.name, 0) + 1
|
||||||
|
|
||||||
for i, ch in enumerate(d_channels):
|
for i, ch in enumerate(d_channels):
|
||||||
console.print(f"({i+1}) {ch.name}")
|
display_name = ch.name
|
||||||
|
if d_name_counts[ch.name] > 1:
|
||||||
|
cat_name = d_cat_map.get(ch.category_id)
|
||||||
|
if cat_name:
|
||||||
|
display_name = f"{ch.name} [{cat_name}]"
|
||||||
|
console.print(f"({i+1}) {display_name}")
|
||||||
|
|
||||||
console.print("(B) Back")
|
console.print("(B) Back")
|
||||||
d_choices = [str(i+1) for i in range(len(d_channels))] + ["B", "b"]
|
d_choices = [str(i+1) for i in range(len(d_channels))] + ["B", "b"]
|
||||||
|
|
@ -1113,8 +1126,28 @@ class MigrationCLI:
|
||||||
|
|
||||||
console.print(f"\n[bold]Select Target {platform_name} Channel:[/bold]")
|
console.print(f"\n[bold]Select Target {platform_name} Channel:[/bold]")
|
||||||
|
|
||||||
|
# Identify duplicate names to show categories for them
|
||||||
|
f_name_counts = {}
|
||||||
|
for ch in f_channels:
|
||||||
|
name = ch.get('name', 'Unnamed Channel')
|
||||||
|
f_name_counts[name] = f_name_counts.get(name, 0) + 1
|
||||||
|
|
||||||
|
# Get category map for target platform
|
||||||
|
# For Fluxer/Stoat, the channel object in f_channels contains 'parent_id'
|
||||||
|
# We need to resolve these IDs to names.
|
||||||
|
target_cat_names = {}
|
||||||
|
for ch in full_f_channels: # use full list including categories (type 4)
|
||||||
|
if ch.get('type') == 4:
|
||||||
|
target_cat_names[str(ch.get('id'))] = ch.get('name')
|
||||||
|
|
||||||
for i, ch in enumerate(f_channels):
|
for i, ch in enumerate(f_channels):
|
||||||
console.print(f"({i+1}) {ch.get('name', 'Unnamed Channel')}")
|
name = ch.get('name', 'Unnamed Channel')
|
||||||
|
display_name = name
|
||||||
|
if f_name_counts[name] > 1:
|
||||||
|
parent_id = ch.get('parent_id')
|
||||||
|
if parent_id and str(parent_id) in target_cat_names:
|
||||||
|
display_name = f"{name} [{target_cat_names[str(parent_id)]}]"
|
||||||
|
console.print(f"({i+1}) {display_name}")
|
||||||
|
|
||||||
f_choices = [str(i+1) for i in range(len(f_channels))] + ["B", "b", "N", "n"]
|
f_choices = [str(i+1) for i in range(len(f_channels))] + ["B", "b", "N", "n"]
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue