fix voice channel

This commit is contained in:
rambros 2026-03-27 11:24:53 +05:30
parent 939e063d46
commit 93c54c4d0f
4 changed files with 101 additions and 47 deletions

View file

@ -998,7 +998,8 @@ class BackupReader:
Forbidden = BackupForbidden Forbidden = BackupForbidden
CHANNEL_TYPE_TEXT = ChannelType.text CHANNEL_TYPE_TEXT = ChannelType.text
CHANNEL_TYPE_NEWS = ChannelType.voice # simplified CHANNEL_TYPE_VOICE = ChannelType.voice
CHANNEL_TYPE_NEWS = ChannelType.news
CHANNEL_TYPE_FORUM = ChannelType.forum CHANNEL_TYPE_FORUM = ChannelType.forum
@staticmethod @staticmethod

View file

@ -61,7 +61,17 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
""" """
# Sort by position to respect Discord arrangement # Sort by position to respect Discord arrangement
categories = sorted(await context.discord_reader.get_categories(), key=lambda c: getattr(c, 'position', 0)) 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)) all_channels = sorted(await context.discord_reader.get_channels(), key=lambda c: getattr(c, 'position', 0))
# Only migrate text-like and voice channels. Forum channels are not yet supported in Fluxer.
reader = context.discord_reader
channels = [
ch for ch in all_channels
if ch.type != reader.CHANNEL_TYPE_FORUM
]
skipped = [ch.name for ch in all_channels if ch not in channels]
if skipped:
logger.info(f"Skipping {len(skipped)} forum channel(s): {', '.join(skipped)}")
cloned_info = { cloned_info = {
"categories_created": [], "categories_created": [],
@ -130,13 +140,27 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
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) pos = getattr(channel, 'position', None)
# Map Discord-specific types to target-supported types
# 5 (News) -> 0 (Text), and fallback any unknown non-voice types to text
raw_type = channel.type.value if hasattr(channel.type, 'value') else 0
if raw_type == context.discord_reader.CHANNEL_TYPE_VOICE.value:
ch_type = 2
is_voice = True
elif raw_type in [context.discord_reader.CHANNEL_TYPE_TEXT.value, context.discord_reader.CHANNEL_TYPE_NEWS.value]:
ch_type = 0
is_voice = False
else:
# Fallback for Stage channels (13) etc. to Text for safety
ch_type = 0
is_voice = False
fluxer_id = await context.fluxer_writer.create_channel( fluxer_id = await context.fluxer_writer.create_channel(
name=channel.name, name=channel.name,
topic=topic, topic=topic if not is_voice else "",
type=0, type=ch_type,
parent_id=parent_id, parent_id=parent_id,
nsfw=nsfw, nsfw=nsfw if not is_voice else False,
slowmode_delay=slowmode, slowmode_delay=slowmode if not is_voice else 0,
position=pos position=pos
) )
context.state.set_channel_mapping(state_key, fluxer_id) context.state.set_channel_mapping(state_key, fluxer_id)
@ -147,16 +171,17 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
cloned_info["structure"][parent_name] = [] cloned_info["structure"][parent_name] = []
cloned_info["structure"][parent_name].append(channel.name) cloned_info["structure"][parent_name].append(channel.name)
# Sync again immediately because some properties (like slowmode) are ignored on creation # Sync again immediately (only for non-voice channels)
await context.fluxer_writer.modify_channel( if not is_voice:
channel_id=fluxer_id, await context.fluxer_writer.modify_channel(
parent_id=parent_id, channel_id=fluxer_id,
name=channel.name, parent_id=parent_id,
topic=topic, name=channel.name,
nsfw=nsfw, topic=topic,
slowmode_delay=slowmode, nsfw=nsfw,
position=pos slowmode_delay=slowmode,
) position=pos
)
current_idx += 1 current_idx += 1
if progress_callback: await progress_callback(channel.name, "Copying", current_idx, total) if progress_callback: await progress_callback(channel.name, "Copying", current_idx, total)

View file

@ -61,11 +61,21 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
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() categories = await context.discord_reader.get_categories()
channels = await context.discord_reader.get_channels() all_channels = await context.discord_reader.get_channels()
# Sort categories and channels by position to preserve order # Sort categories and channels by position to preserve order
categories = sorted(categories, key=lambda c: getattr(c, 'position', 0)) categories = sorted(categories, key=lambda c: getattr(c, 'position', 0))
channels = sorted(channels, key=lambda c: getattr(c, 'position', 0)) all_channels = sorted(all_channels, key=lambda c: getattr(c, 'position', 0))
# Only migrate text-like and voice channels. Forum channels are not yet supported in Stoat.
reader = context.discord_reader
channels = [
ch for ch in all_channels
if ch.type != reader.CHANNEL_TYPE_FORUM
]
skipped = [ch.name for ch in all_channels if ch not in channels]
if skipped:
logger.info(f"Skipping {len(skipped)} forum channel(s): {', '.join(skipped)}")
cloned_info = { cloned_info = {
"categories_created": [], "categories_created": [],
@ -97,22 +107,7 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
if total == 0: if total == 0:
return cloned_info return cloned_info
# 1. Migrate Categories # 1. Create missing channels (unparented for now)
for cat in missing_categories:
if not context.is_running: break
state_key = str(cat.id)
target_id = await context.writer.create_channel(cat.name, type=4)
if target_id:
context.state.set_target_category_mapping(state_key, target_id)
cloned_info["categories_created"].append(cat.name)
if cat.name not in cloned_info["structure"]:
cloned_info["structure"][cat.name] = []
current_idx += 1
if progress_callback: await progress_callback(f"Cat: {cat.name}", "Copying", current_idx, total)
# 2. Create missing channels (unparented for now)
for channel in channels_to_create: for channel in channels_to_create:
if not context.is_running: break if not context.is_running: break
@ -123,13 +118,27 @@ 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}")
# Map Discord-specific types to target-supported types
# 5 (News) -> 0 (Text), and fallback any unknown non-voice types to text
raw_type = channel.type.value if hasattr(channel.type, 'value') else 0
if raw_type == context.discord_reader.CHANNEL_TYPE_VOICE.value:
ch_type = 2
is_voice = True
elif raw_type in [context.discord_reader.CHANNEL_TYPE_TEXT.value, context.discord_reader.CHANNEL_TYPE_NEWS.value]:
ch_type = 0
is_voice = False
else:
# Fallback for Stage channels (13) etc. to Text for safety
ch_type = 0
is_voice = False
target_id = await context.writer.create_channel( target_id = await context.writer.create_channel(
name=channel.name, name=channel.name,
topic=topic, topic=topic if not is_voice else "",
type=0, type=ch_type,
parent_id=None, parent_id=None,
nsfw=nsfw, nsfw=nsfw if not is_voice else False,
slowmode_delay=slowmode slowmode_delay=slowmode if not is_voice else 0
) )
if target_id: if target_id:
context.state.set_target_channel_mapping(state_key, target_id) context.state.set_target_channel_mapping(state_key, target_id)
@ -140,14 +149,15 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
cloned_info["structure"][parent_name] = [] cloned_info["structure"][parent_name] = []
cloned_info["structure"][parent_name].append(channel.name) cloned_info["structure"][parent_name].append(channel.name)
# Sync properties immediately # Sync properties immediately (only for non-voice channels)
await context.writer.modify_channel( if not is_voice:
channel_id=target_id, await context.writer.modify_channel(
name=channel.name, channel_id=target_id,
topic=topic, name=channel.name,
nsfw=nsfw, topic=topic,
slowmode_delay=slowmode nsfw=nsfw,
) slowmode_delay=slowmode
)
current_idx += 1 current_idx += 1
if progress_callback: await progress_callback(channel.name, "Copying", current_idx, total) if progress_callback: await progress_callback(channel.name, "Copying", current_idx, total)
@ -175,6 +185,21 @@ async def migrate_channels(context: MigrationContext, progress_callback: Callabl
current_idx += 1 current_idx += 1
if progress_callback: await progress_callback(channel.name, "Syncing", current_idx, total) if progress_callback: await progress_callback(channel.name, "Syncing", current_idx, total)
# 3. Create missing categories
for cat in missing_categories:
if not context.is_running: break
state_key = str(cat.id)
target_id = await context.writer.create_channel(cat.name, type=4)
if target_id:
context.state.set_target_category_mapping(state_key, target_id)
cloned_info["categories_created"].append(cat.name)
if cat.name not in cloned_info["structure"]:
cloned_info["structure"][cat.name] = []
current_idx += 1
if progress_callback: await progress_callback(f"Cat: {cat.name}", "Copying", current_idx, total)
# 4. Final step: Parent the channels into categories via mass server.edit() # 4. Final step: Parent the channels into categories via mass server.edit()
logger.info("Parenting all channels into their respective categories...") logger.info("Parenting all channels into their respective categories...")
# Force refresh server to get latest categories created during migration # Force refresh server to get latest categories created during migration

View file

@ -263,9 +263,12 @@ class StoatWriter:
await server.edit(categories=categories) await server.edit(categories=categories)
self._server = None # Clear cache after structural change self._server = None # Clear cache after structural change
return new_id return new_id
elif type == 2: # Voice Channel
ch = await server.create_voice_channel(name=name)
self._server = None # Clear cache
return str(ch.id)
else: # Text Channel else: # Text Channel
ch = await server.create_text_channel(name=name, description=topic) ch = await server.create_text_channel(name=name, description=topic)
# We no longer parent here, clone_server.py will do it in bulk
self._server = None # Clear cache self._server = None # Clear cache
return str(ch.id) return str(ch.id)
except Exception as e: except Exception as e: