Skip to content

Add auto report of deleted messages#740

Open
MattyTheHacker wants to merge 9 commits intomainfrom
add-deleted-message-log
Open

Add auto report of deleted messages#740
MattyTheHacker wants to merge 9 commits intomainfrom
add-deleted-message-log

Conversation

@MattyTheHacker
Copy link
Member

@MattyTheHacker MattyTheHacker commented Mar 24, 2026

Send a quoted message to committee channels when a message is deleted.

Under the following conditions:

  • Not when a user deletes their own message
  • Not on messages sent before the bot's current session (i.e. messages not in the current cache)

This feature is designed to make sure that messages deleted as part of moderation action are retained; in the event that the committee member forgets to do it manually.

@MattyTheHacker MattyTheHacker self-assigned this Mar 24, 2026
@MattyTheHacker MattyTheHacker added enhancement New feature or request sync Request bots to automatically keep this PR up to date with it's base branch labels Mar 24, 2026
Copilot AI review requested due to automatic review settings March 24, 2026 18:46
@codecov
Copy link

codecov bot commented Mar 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds automatic reporting of moderator-deleted messages to committee channels by correlating message deletion events with audit log entries, and enables the required Discord intent to access message content.

Changes:

  • Enable Intents.message_content on bot startup to allow access to message content for deletion reporting.
  • Add a new ModerationCog that listens for message deletions and attempts to attribute them to a deleter via audit logs, then forwards details to the committee channel.
  • Register the new cog in cogs/__init__.py so it’s loaded on startup.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
main.py Enables message_content intent needed for deletion content reporting.
cogs/moderation.py New cog implementing deletion tracking + committee reporting logic.
cogs/init.py Adds ModerationCog import, export, and startup registration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +93 to +98
@TeXBotBaseCog.listener()
@capture_guild_does_not_exist_error
async def on_message_delete(self, message: discord.Message) -> None:
"""Listen for message deletions."""
self.most_recently_deleted_message = message

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

most_recently_deleted_message only tracks a single deletion and is never cleared/expired. If a user deletes their own message (no audit-log entry), this cached message can later be incorrectly paired with a moderator message_delete audit-log entry for the same author/channel, causing the bot to report the wrong content (and miss the real deleted message). Consider storing a short-lived queue keyed by (channel_id, author_id) with deletion timestamps (and clearing on successful match), rather than a single global reference.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +92
class ModerationCog(TeXBotBaseCog):
"""Cog to track moderation actions and report them to the committee."""

most_recently_deleted_message: discord.Message | None = None

async def _send_message_to_committee(
self, message: discord.Message, deleter: discord.Member
) -> None:
discord_channel: discord.TextChannel | None = discord.utils.get(
self.bot.main_guild.text_channels,
name="discord", # TODO: Make this user-configurable # noqa: FIX002
)

if not discord_channel:
logger.error("Could not find the channel to send the message deletion report to!")
return

embed_content: str = ""

if message.content:
embed_content += message.content[:600]
if len(message.content) > 600:
embed_content += " _... (truncated to 600 characters)_"
else:
embed_content += "_Deleted message had no content_"
if len(message.attachments) > 0 or len(message.embeds) > 0:
embed_content += " _but did have one or more attachments!_"

embed_content += f"\n[View Original]({message.jump_url})"

if message.reference:
embed_content += f"\n[View Message this replied to]({message.reference.jump_url})"

message_author_avatar_url: str | None = message.author.display_avatar.url

embed_author: discord.EmbedAuthor = discord.EmbedAuthor(
name=message.author.display_name, icon_url=message_author_avatar_url
)

embed_image: str | None = None
if len(message.attachments) == 1:
attachment_type: str | None = message.attachments[0].content_type
if attachment_type and "image" in attachment_type:
embed_image = message.attachments[0].url

await discord_channel.send(
content=(
f"{deleter.mention} deleted a message from {message.author.mention} "
f"in {
message.channel.mention
if isinstance(
message.channel,
(
discord.TextChannel,
discord.VoiceChannel,
discord.StageChannel,
discord.Thread,
),
)
else message.channel
}:"
),
embed=discord.Embed(
author=embed_author,
description=embed_content,
colour=message.author.colour,
image=embed_image,
),
)

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

_send_message_to_committee is effectively duplicated from StrikeContextCommandsCog._send_message_to_committee (same embed construction, truncation rules, attachment handling, etc.). Having two copies makes future changes easy to miss and risks inconsistent committee reporting behavior; consider extracting this into a shared helper (e.g. a util function or a method on TeXBotBaseCog) and reusing it from both cogs.

Suggested change
class ModerationCog(TeXBotBaseCog):
"""Cog to track moderation actions and report them to the committee."""
most_recently_deleted_message: discord.Message | None = None
async def _send_message_to_committee(
self, message: discord.Message, deleter: discord.Member
) -> None:
discord_channel: discord.TextChannel | None = discord.utils.get(
self.bot.main_guild.text_channels,
name="discord", # TODO: Make this user-configurable # noqa: FIX002
)
if not discord_channel:
logger.error("Could not find the channel to send the message deletion report to!")
return
embed_content: str = ""
if message.content:
embed_content += message.content[:600]
if len(message.content) > 600:
embed_content += " _... (truncated to 600 characters)_"
else:
embed_content += "_Deleted message had no content_"
if len(message.attachments) > 0 or len(message.embeds) > 0:
embed_content += " _but did have one or more attachments!_"
embed_content += f"\n[View Original]({message.jump_url})"
if message.reference:
embed_content += f"\n[View Message this replied to]({message.reference.jump_url})"
message_author_avatar_url: str | None = message.author.display_avatar.url
embed_author: discord.EmbedAuthor = discord.EmbedAuthor(
name=message.author.display_name, icon_url=message_author_avatar_url
)
embed_image: str | None = None
if len(message.attachments) == 1:
attachment_type: str | None = message.attachments[0].content_type
if attachment_type and "image" in attachment_type:
embed_image = message.attachments[0].url
await discord_channel.send(
content=(
f"{deleter.mention} deleted a message from {message.author.mention} "
f"in {
message.channel.mention
if isinstance(
message.channel,
(
discord.TextChannel,
discord.VoiceChannel,
discord.StageChannel,
discord.Thread,
),
)
else message.channel
}:"
),
embed=discord.Embed(
author=embed_author,
description=embed_content,
colour=message.author.colour,
image=embed_image,
),
)
async def _send_message_deletion_to_committee(
bot: discord.Client, message: discord.Message, deleter: discord.Member
) -> None:
discord_channel: discord.TextChannel | None = discord.utils.get(
bot.main_guild.text_channels,
name="discord", # TODO: Make this user-configurable # noqa: FIX002
)
if not discord_channel:
logger.error("Could not find the channel to send the message deletion report to!")
return
embed_content: str = ""
if message.content:
embed_content += message.content[:600]
if len(message.content) > 600:
embed_content += " _... (truncated to 600 characters)_"
else:
embed_content += "_Deleted message had no content_"
if len(message.attachments) > 0 or len(message.embeds) > 0:
embed_content += " _but did have one or more attachments!_"
embed_content += f"\n[View Original]({message.jump_url})"
if message.reference:
embed_content += f"\n[View Message this replied to]({message.reference.jump_url})"
message_author_avatar_url: str | None = message.author.display_avatar.url
embed_author: discord.EmbedAuthor = discord.EmbedAuthor(
name=message.author.display_name, icon_url=message_author_avatar_url
)
embed_image: str | None = None
if len(message.attachments) == 1:
attachment_type: str | None = message.attachments[0].content_type
if attachment_type and "image" in attachment_type:
embed_image = message.attachments[0].url
await discord_channel.send(
content=(
f"{deleter.mention} deleted a message from {message.author.mention} "
f"in {
message.channel.mention
if isinstance(
message.channel,
(
discord.TextChannel,
discord.VoiceChannel,
discord.StageChannel,
discord.Thread,
),
)
else message.channel
}:"
),
embed=discord.Embed(
author=embed_author,
description=embed_content,
colour=message.author.colour,
image=embed_image,
),
)
class ModerationCog(TeXBotBaseCog):
"""Cog to track moderation actions and report them to the committee."""
most_recently_deleted_message: discord.Message | None = None
async def _send_message_to_committee(
self, message: discord.Message, deleter: discord.Member
) -> None:
await _send_message_deletion_to_committee(self.bot, message, deleter)

Copilot uses AI. Check for mistakes.
intents=discord.Intents.default() | discord.Intents.members
intents=discord.Intents.default()
| discord.Intents.members
| discord.Intents.message_content
Copy link
Member

Choose a reason for hiding this comment

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

This is the biggest issue here. We have purposefully limited the privileges of TeX-Bot to not have access to read members messages and this is a departure from this. Making this change requires a vote of approval from CSS committee.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request sync Request bots to automatically keep this PR up to date with it's base branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants