preserve discord links to messages & channels in migraions
This commit is contained in:
parent
8cdbcab5f6
commit
478c025754
4 changed files with 105 additions and 6 deletions
|
|
@ -278,6 +278,24 @@ class MigrationState:
|
|||
if str(target_channel_id) in self.channel_messages:
|
||||
return self.channel_messages[str(target_channel_id)]["message_map"].get(str(discord_id))
|
||||
return None
|
||||
|
||||
def find_message_mapping(self, discord_id: str) -> tuple[str, str] | tuple[None, None]:
|
||||
"""
|
||||
Searches for a message mapping across all tracked channels.
|
||||
Returns (target_channel_id, target_message_id) or (None, None).
|
||||
"""
|
||||
d_id = str(discord_id)
|
||||
for t_cid, data in self.channel_messages.items():
|
||||
# Check main message map
|
||||
if d_id in data.get("message_map", {}):
|
||||
return str(t_cid), str(data["message_map"][d_id])
|
||||
# Check threads
|
||||
for t_tid, t_data in data.get("threads", {}).items():
|
||||
if d_id in t_data.get("thread_map", {}):
|
||||
# For thread links, the target_channel_id is technically the thread ID in some contexts,
|
||||
# but usually for the URL it's the thread ID itself.
|
||||
return str(t_tid), str(t_data["thread_map"][d_id])
|
||||
return None, None
|
||||
|
||||
def update_last_message_timestamp(self, target_channel_id: str, timestamp: str):
|
||||
self._ensure_channel_tracking(target_channel_id)
|
||||
|
|
|
|||
61
src/core/utils.py
Normal file
61
src/core/utils.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import re
|
||||
import logging
|
||||
from src.core.state import MigrationState
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def resolve_discord_links(content: str, state: MigrationState, platform: str, target_server_id: str) -> str:
|
||||
"""
|
||||
Finds Discord message/channel links and resolves them to the target platform
|
||||
if they have been migrated.
|
||||
"""
|
||||
if not content:
|
||||
return content
|
||||
|
||||
# Regex for Discord links: https://discord.com/channels/{guild}/{channel}/{message}
|
||||
# Matches: https://discord.com/channels/123/456 or https://discord.com/channels/123/456/789
|
||||
discord_link_re = re.compile(r'https?://(?:ptb\.|canary\.)?discord\.com/channels/(\d+)/(\d+)(?:/(\d+))?')
|
||||
|
||||
def replace_link(match):
|
||||
full_url = match.group(0)
|
||||
|
||||
# Check if already part of a markdown link: [text](link) or [text](<link>)
|
||||
# We look backwards for ]( or ](<
|
||||
start_idx = match.start()
|
||||
if start_idx > 2:
|
||||
prev_chars = content[max(0, start_idx-3):start_idx]
|
||||
if prev_chars.endswith("](") or prev_chars.endswith("]<"):
|
||||
return full_url
|
||||
|
||||
guild_id = match.group(1)
|
||||
channel_id = match.group(2)
|
||||
message_id = match.group(3)
|
||||
|
||||
target_cid = state.get_target_channel_id(channel_id) or state.get_target_category_id(channel_id)
|
||||
|
||||
if message_id:
|
||||
# Message link resolution
|
||||
t_cid, t_mid = state.find_message_mapping(message_id)
|
||||
if t_mid:
|
||||
# Use found channel ID if available, otherwise fallback to channel_id mapping
|
||||
final_cid = t_cid or target_cid
|
||||
if final_cid:
|
||||
if platform == "stoat":
|
||||
return f"https://stoat.chat/server/{target_server_id}/channel/{final_cid}/{t_mid}"
|
||||
else: # Fluxer
|
||||
return f"https://fluxer.app/channels/{target_server_id}/{final_cid}/{t_mid}"
|
||||
|
||||
# Fallback for unmigrated message
|
||||
return f"[`discord-message`](<{full_url}>)"
|
||||
else:
|
||||
# Channel link resolution
|
||||
if target_cid:
|
||||
if platform == "stoat":
|
||||
return f"https://stoat.chat/server/{target_server_id}/channel/{target_cid}"
|
||||
else: # Fluxer
|
||||
return f"https://fluxer.app/channels/{target_server_id}/{target_cid}"
|
||||
|
||||
# Fallback for unmigrated channel
|
||||
return f"[`discord-channel`](<{full_url}>)"
|
||||
|
||||
return discord_link_re.sub(replace_link, content)
|
||||
|
|
@ -4,10 +4,11 @@ import re
|
|||
from typing import Callable, Awaitable, Dict, Any
|
||||
|
||||
from src.core.base import MigrationContext
|
||||
from src.core.utils import resolve_discord_links
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def clean_mentions(content: str, guild, user_mentions=None, role_mentions=None, role_map=None, emoji_map=None, channel_map=None, discord_channel_map=None) -> str:
|
||||
def clean_mentions(content: str, guild, user_mentions=None, role_mentions=None, role_map=None, emoji_map=None, channel_map=None, discord_channel_map=None, state=None, target_server_id=None) -> str:
|
||||
if not content or not guild:
|
||||
return content
|
||||
|
||||
|
|
@ -85,6 +86,11 @@ def clean_mentions(content: str, guild, user_mentions=None, role_mentions=None,
|
|||
content = re.sub(r'<#([0-9]+)>', replace_channel, content)
|
||||
content = re.sub(r'<(a?):([^:]+):([0-9]+)>', replace_emoji, content)
|
||||
content = content.replace("@everyone", "`@everyone`").replace("@here", "`@here`")
|
||||
|
||||
# Resolve Discord Links
|
||||
if state and target_server_id:
|
||||
content = resolve_discord_links(content, state, "fluxer", target_server_id)
|
||||
|
||||
return content
|
||||
|
||||
|
||||
|
|
@ -193,7 +199,9 @@ async def migrate_messages(
|
|||
context.discord_reader.role_map,
|
||||
context.state.emoji_map,
|
||||
context.state.channel_map,
|
||||
context.discord_reader.channel_name_map
|
||||
context.discord_reader.channel_name_map,
|
||||
state=context.state,
|
||||
target_server_id=context.fluxer_writer.community_id
|
||||
)
|
||||
|
||||
# Process attachments
|
||||
|
|
@ -224,7 +232,9 @@ async def migrate_messages(
|
|||
context.discord_reader.role_map,
|
||||
context.state.emoji_map,
|
||||
context.state.channel_map,
|
||||
context.discord_reader.channel_name_map
|
||||
context.discord_reader.channel_name_map,
|
||||
state=context.state,
|
||||
target_server_id=context.fluxer_writer.community_id
|
||||
)
|
||||
# Add snapshot attachments to the list to process
|
||||
attachments_to_process.extend(snapshot.attachments)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import re
|
|||
from typing import Callable, Awaitable, Dict, Any
|
||||
|
||||
from src.core.base import MigrationContext
|
||||
from src.core.utils import resolve_discord_links
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def clean_mentions(content: str, guild, user_mentions=None, role_mentions=None, role_map=None, emoji_map=None, channel_map=None, discord_channel_map=None) -> str:
|
||||
def clean_mentions(content: str, guild, user_mentions=None, role_mentions=None, role_map=None, emoji_map=None, channel_map=None, discord_channel_map=None, state=None, target_server_id=None) -> str:
|
||||
if not content or not guild:
|
||||
return content
|
||||
|
||||
|
|
@ -85,6 +86,11 @@ def clean_mentions(content: str, guild, user_mentions=None, role_mentions=None,
|
|||
content = re.sub(r'<#([0-9]+)>', replace_channel, content)
|
||||
content = re.sub(r'<(a?):([^:]+):([0-9]+)>', replace_emoji, content)
|
||||
content = content.replace("@everyone", "`@everyone`").replace("@here", "`@here`")
|
||||
|
||||
# Resolve Discord Links
|
||||
if state and target_server_id:
|
||||
content = resolve_discord_links(content, state, "stoat", target_server_id)
|
||||
|
||||
return content
|
||||
|
||||
|
||||
|
|
@ -201,7 +207,9 @@ async def migrate_messages(
|
|||
context.discord_reader.role_map,
|
||||
context.state.emoji_map,
|
||||
context.state.channel_map,
|
||||
context.discord_reader.channel_name_map
|
||||
context.discord_reader.channel_name_map,
|
||||
state=context.state,
|
||||
target_server_id=context.stoat_writer.community_id
|
||||
)
|
||||
|
||||
# Process attachments
|
||||
|
|
@ -228,7 +236,9 @@ async def migrate_messages(
|
|||
context.discord_reader.role_map,
|
||||
context.state.emoji_map,
|
||||
context.state.channel_map,
|
||||
context.discord_reader.channel_name_map
|
||||
context.discord_reader.channel_name_map,
|
||||
state=context.state,
|
||||
target_server_id=context.stoat_writer.community_id
|
||||
)
|
||||
attachments_to_process.extend(snapshot.attachments)
|
||||
logger.debug(f"Found forwarded snapshot content: {content[:50]}... and {len(snapshot.attachments)} attachments")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue