Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,8 @@ def __init__(self, original_message, forwarded_content):
self.author = original_message.author
self.content = forwarded_content
self.attachments = []
for snap in getattr(original_message, "message_snapshots", []):
self.attachments.extend(getattr(snap, "attachments", []))
self.stickers = []
self.created_at = original_message.created_at
self.embeds = []
Expand Down
11 changes: 10 additions & 1 deletion core/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,9 +661,18 @@ async def append_log(
channel_id: str = "",
type_: str = "thread_message",
) -> dict:
from core.utils import extract_forwarded_content
Comment thread
lorenzo132 marked this conversation as resolved.
Outdated

channel_id = str(channel_id) or str(message.channel.id)
message_id = str(message_id) or str(message.id)

content = message.content or ""
if forwarded := extract_forwarded_content(message):
if content:
content += "\n" + forwarded
else:
content = forwarded

data = {
"timestamp": str(message.created_at),
"message_id": message_id,
Expand All @@ -674,7 +683,7 @@ async def append_log(
"avatar_url": message.author.display_avatar.url if message.author.display_avatar else None,
"mod": not isinstance(message.channel, DMChannel),
},
"content": message.content,
"content": content,
"type": type_,
"attachments": [
{
Expand Down
11 changes: 10 additions & 1 deletion core/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
ConfirmThreadCreationView,
DummyParam,
extract_forwarded_content,
extract_forwarded_attachments,
)

logger = getLogger(__name__)
Expand Down Expand Up @@ -206,7 +207,10 @@ async def snooze(self, moderator=None, command_used=None, snooze_for=None):
"messages": [
{
"author_id": m.author.id,
"content": m.content,
"content": (
(m.content or "")
+ (("\n" + extract_forwarded_content(m)) if extract_forwarded_content(m) else "")
).strip(),
"attachments": [a.url for a in m.attachments],
"embeds": [e.to_dict() for e in m.embeds],
"created_at": m.created_at.isoformat(),
Expand Down Expand Up @@ -1955,6 +1959,11 @@ async def send(

ext = [(a.url, a.filename, False) for a in message.attachments]

# Add forwarded message attachments
forwarded_attachments = extract_forwarded_attachments(message)
for url, filename in forwarded_attachments:
ext.append((url, filename, False))

images = []
attachments = []
for attachment in ext:
Expand Down
126 changes: 88 additions & 38 deletions core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"ConfirmThreadCreationView",
"DummyParam",
"extract_forwarded_content",
"extract_forwarded_attachments",
]


Expand Down Expand Up @@ -640,6 +641,37 @@ def __init__(self):
self.value = None


def extract_forwarded_attachments(message) -> typing.List[typing.Tuple[str, str]]:
"""
Extract attachment URLs from forwarded messages.

Parameters
----------
message : discord.Message
The message to extract attachments from.

Returns
-------
List[Tuple[str, str]]
List of (url, filename) tuples for attachments.
"""
import discord
Comment thread
lorenzo132 marked this conversation as resolved.
Outdated

attachments = []
try:
if getattr(message, "message_snapshots", None):
Comment thread
lorenzo132 marked this conversation as resolved.
Outdated
for snap in message.message_snapshots:
if getattr(snap, "attachments", None):
for a in snap.attachments:
url = getattr(a, "url", None)
filename = getattr(a, "filename", "Unknown")
if url:
attachments.append((url.split("?")[0], filename))
Comment thread
sebkuip marked this conversation as resolved.
except Exception:
pass
return attachments


def extract_forwarded_content(message) -> typing.Optional[str]:
"""
Extract forwarded message content from Discord forwarded messages.
Expand All @@ -658,39 +690,51 @@ def extract_forwarded_content(message) -> typing.Optional[str]:

try:
# Handle multi-forward (message_snapshots)
if hasattr(message, "flags") and getattr(message.flags, "has_snapshot", False):
if hasattr(message, "message_snapshots") and message.message_snapshots:
forwarded_parts = []
for snap in message.message_snapshots:
author = getattr(snap, "author", None)
author_name = getattr(author, "name", "Unknown") if author else "Unknown"
snap_content = getattr(snap, "content", "")

if snap_content:
# Truncate very long messages to prevent spam
if len(snap_content) > 500:
snap_content = snap_content[:497] + "..."
forwarded_parts.append(f"**{author_name}:** {snap_content}")
elif getattr(snap, "embeds", None):
for embed in snap.embeds:
if hasattr(embed, "description") and embed.description:
embed_desc = embed.description
if len(embed_desc) > 300:
embed_desc = embed_desc[:297] + "..."
forwarded_parts.append(f"**{author_name}:** {embed_desc}")
break
elif getattr(snap, "attachments", None):
attachment_info = ", ".join(
[getattr(a, "filename", "Unknown") for a in snap.attachments[:3]]
)
if len(snap.attachments) > 3:
attachment_info += f" (+{len(snap.attachments) - 3} more)"
forwarded_parts.append(f"**{author_name}:** [Attachments: {attachment_info}]")
else:
forwarded_parts.append(f"**{author_name}:** [No content]")

if forwarded_parts:
return "\n".join(forwarded_parts)
# Check directly for snapshots as flags.has_snapshot can be unreliable in some versions
if getattr(message, "message_snapshots", None):
forwarded_parts = []
for snap in message.message_snapshots:
author = getattr(snap, "author", None)
# If author is missing, we can try to rely on the container message context or just omit.
# Since we can't reliably get the original author from snapshot in this state, we focus on content.

snap_content = getattr(snap, "content", "")

formatted_part = "📨 **Forwarded Message**\n"

if snap_content:
if len(snap_content) > 500:
snap_content = snap_content[:497] + "..."
formatted_part += "\n".join([f"{line}" for line in snap_content.splitlines()]) + "\n"

if getattr(snap, "embeds", None):
for embed in snap.embeds:
if hasattr(embed, "description") and embed.description:
embed_desc = embed.description
if len(embed_desc) > 300:
embed_desc = embed_desc[:297] + "..."
formatted_part += (
"\n".join([f"> {line}" for line in embed_desc.splitlines()]) + "\n"
)
break # One embed preview is usually enough

if getattr(snap, "attachments", None):
attachment_links = []
for a in snap.attachments[:3]:
filename = getattr(a, "filename", "Unknown")
url = getattr(a, "url", None)
if url:
url = url.split("?")[0]
attachment_links.append(f"[{filename}]({url})")
else:
attachment_links.append(filename)
if len(snap.attachments) > 3:
attachment_links.append(f"(+{len(snap.attachments) - 3} more)")
formatted_part += f"📎 {', '.join(attachment_links)}\n"
forwarded_parts.append(formatted_part)

if forwarded_parts:
return "\n".join(forwarded_parts)

# Handle single-message forward
elif getattr(message, "type", None) == getattr(discord.MessageType, "forward", None):
Expand Down Expand Up @@ -719,12 +763,18 @@ def extract_forwarded_content(message) -> typing.Optional[str]:
embed_desc = embed_desc[:297] + "..."
return f"**{ref_author_name}:** {embed_desc}"
elif getattr(ref_msg, "attachments", None):
attachment_info = ", ".join(
[getattr(a, "filename", "Unknown") for a in ref_msg.attachments[:3]]
)
attachment_links = []
for a in ref_msg.attachments[:3]:
filename = getattr(a, "filename", "Unknown")
url = getattr(a, "url", None)
if url:
url = url.split("?")[0]
attachment_links.append(f"[{filename}]({url})")
else:
attachment_links.append(filename)
if len(ref_msg.attachments) > 3:
attachment_info += f" (+{len(ref_msg.attachments) - 3} more)"
return f"**{ref_author_name}:** [Attachments: {attachment_info}]"
attachment_links.append(f"(+{len(ref_msg.attachments) - 3} more)")
return f"**{ref_author_name}:** 📎 {', '.join(attachment_links)}"
except Exception as e:
# Log and continue; failing to extract a reference preview shouldn't break flow
logger.debug("Failed to extract reference preview: %s", e)
Expand Down
Loading