Skip to content
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

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
5 changes: 4 additions & 1 deletion core/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,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
76 changes: 43 additions & 33 deletions core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,39 +658,49 @@ 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_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)"
formatted_part += f"[Attachments: {attachment_info}]\n"

# Add source link to the container message since snapshot doesn't have its own public link
formatted_part += f"\n**Source:** {message.jump_url}"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This always returns a link to the forwarded message instance that is in the DM with the bot, rather than to the original message that was forwarded - which won't be accessible to the moderator reviewing the content in the logviewer.

The "Context" line when a forwarded message is sent in a Modmail ticket channel (which appears when the forwarded message location is one that the bot is able to fetch) would be a better solution here - perhaps something like what is done in thread.py

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be a nice solution yea. If you are able to PR this that would be great, otherwise i will look into this either this week or next week.


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
Loading