diff --git a/src/fluxer/migrate_message.py b/src/fluxer/migrate_message.py index 37d5c06..8f74cce 100644 --- a/src/fluxer/migrate_message.py +++ b/src/fluxer/migrate_message.py @@ -1,4 +1,5 @@ import asyncio +import discord import logging import re from typing import Callable, Awaitable, Dict, Any @@ -74,6 +75,16 @@ async def migrate_messages(context: MigrationContext, source_channel_id: int, ta if not context.is_running: break + # Skip system messages like "pinned a message", etc. + # We treat thread_starter_message (type 21) as our thread marker. + if msg.type == discord.MessageType.thread_starter_message: + content = f"> <<< THREAD: **{msg.channel.name}** >>>" + elif msg.type not in [discord.MessageType.default, discord.MessageType.reply]: + continue + else: + # Get clean content + content = msg.clean_content + # Process attachments files = [] attachments_to_process = list(msg.attachments) @@ -85,13 +96,13 @@ async def migrate_messages(context: MigrationContext, source_channel_id: int, ta is_forwarded = msg.flags.forwarded # If forwarded, the content and attachments might be in message_snapshots (discord.py 2.5+) - content = msg.clean_content + # Note: If content was set by thread_starter_message, we don't overwrite it. if is_forwarded: logger.debug(f"Detected forwarded message: ID={msg.id}, Flags={msg.flags.value}") if hasattr(msg, 'message_snapshots') and msg.message_snapshots: # For now we handle the first snapshot snapshot = msg.message_snapshots[0] - if not content: + if not content: # Only update content if it wasn't already set (e.g., by thread_starter_message) content = snapshot.content if hasattr(msg, 'guild') and msg.guild: content = clean_mentions(content, msg.guild) @@ -132,13 +143,6 @@ async def migrate_messages(context: MigrationContext, source_channel_id: int, ta thread = msg.thread logger.info(f"Detected thread '{thread.name}' on message {msg.id}") - # Send Start Marker - stats["threads"] += 1 - await context.fluxer_writer.send_marker( - channel_id=target_channel_id, - content=f"> <<< THREAD: **{thread.name}** >>>" - ) - # Migrate thread messages # We don't pass a progress callback here to avoid confusing the UI # but we do want to track count if possible. diff --git a/src/fluxer/writer.py b/src/fluxer/writer.py index a4feb0d..64bd9a1 100644 --- a/src/fluxer/writer.py +++ b/src/fluxer/writer.py @@ -1,7 +1,8 @@ import asyncio +import io import logging from typing import Optional, List, Dict, Any -from fluxer import Bot, Webhook, Forbidden +from fluxer import Bot, Webhook, Forbidden, File logger = logging.getLogger(__name__) @@ -245,6 +246,11 @@ class FluxerWriter: final_content = prefix + display_content if display_content else prefix + # Convert files to fluxer.File objects + fluxer_files = None + if files: + fluxer_files = [File(io.BytesIO(f["data"]), filename=f["filename"]) for f in files] + try: # Current limitation: fluxer.py execute_webhook doesn't support 'message_reference' yet. # So if we have a reply, we MUST use the bot's direct send method. @@ -253,7 +259,7 @@ class FluxerWriter: content=final_content, username=f"{author_name} (discord)", avatar_url=author_avatar_url, - files=files, + files=fluxer_files, wait=True ) return str(msg.id) if msg else None @@ -274,7 +280,7 @@ class FluxerWriter: msg_data = await self.client.send_message( channel_id=channel_id, content=final_bot_content, - files=files, + files=fluxer_files, message_reference=message_reference ) return str(msg_data["id"]) if msg_data else None @@ -290,11 +296,16 @@ class FluxerWriter: Sends a simple marker message (e.g., thread start/end) using the bot directly. """ assert self.client is not None + + fluxer_files = None + if files: + fluxer_files = [File(io.BytesIO(f["data"]), filename=f["filename"]) for f in files] + try: msg_data = await self.client.send_message( channel_id=channel_id, content=content, - files=files + files=fluxer_files ) return str(msg_data["id"]) if msg_data else None except Exception as e: diff --git a/src/stoat/migrate_message.py b/src/stoat/migrate_message.py index c2842af..3d97146 100644 --- a/src/stoat/migrate_message.py +++ b/src/stoat/migrate_message.py @@ -1,4 +1,5 @@ import asyncio +import discord import logging import re from typing import Callable, Awaitable, Dict, Any @@ -73,6 +74,17 @@ async def migrate_messages(context: MigrationContext, source_channel_id: int, ta if not context.is_running: break + # Skip system messages like "pinned a message", etc. + # We treat thread_starter_message (type 21) as our thread marker. + content = "" # Initialize content + if msg.type == discord.MessageType.thread_starter_message: + content = f"> <<< THREAD: **{msg.channel.name}** >>>" + elif msg.type not in [discord.MessageType.default, discord.MessageType.reply]: + continue + else: + # Get clean content + content = msg.clean_content + # Process attachments files = [] attachments_to_process = list(msg.attachments) @@ -82,8 +94,6 @@ async def migrate_messages(context: MigrationContext, source_channel_id: int, ta if hasattr(msg.flags, 'forwarded'): is_forwarded = msg.flags.forwarded - # Get clean content - content = msg.clean_content if is_forwarded: logger.debug(f"Detected forwarded message: ID={msg.id}, Flags={msg.flags.value}") if hasattr(msg, 'message_snapshots') and msg.message_snapshots: @@ -128,13 +138,6 @@ async def migrate_messages(context: MigrationContext, source_channel_id: int, ta thread = msg.thread logger.info(f"Detected thread '{thread.name}' on message {msg.id}") - # Send Start Marker - stats["threads"] += 1 - await context.stoat_writer.send_marker( - channel_id=target_channel_id, - content=f"> <<< THREAD: **{thread.name}** >>>" - ) - # Migrate thread messages recursively thread_stats = await migrate_messages( context=context, diff --git a/src/ui/app.py b/src/ui/app.py index 9068fa3..c0eca69 100644 --- a/src/ui/app.py +++ b/src/ui/app.py @@ -1076,7 +1076,8 @@ class MigrationCLI: # 2. Select Target Channel with console.status(f"[yellow]Fetching {platform_name} channels...[/yellow]"): - f_channels = await self.engine.writer.get_channels() + full_f_channels = await self.engine.writer.get_channels() + f_channels = [c for c in full_f_channels if c.get('name') not in ["reaper_logs", "reaper-logs"]] if not f_channels: console.print(f"[yellow]No channels found in {platform_name} community.[/yellow]") return