diff options
| -rw-r--r-- | bot/cogs/antispam.py | 43 | ||||
| -rw-r--r-- | bot/cogs/moderation/modlog.py | 15 | ||||
| -rw-r--r-- | bot/constants.py | 1 | ||||
| -rw-r--r-- | config-default.yml | 1 |
4 files changed, 52 insertions, 8 deletions
diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py index 1340eb608..6444b1f14 100644 --- a/bot/cogs/antispam.py +++ b/bot/cogs/antispam.py @@ -3,10 +3,11 @@ import logging from collections.abc import Mapping from dataclasses import dataclass, field from datetime import datetime, timedelta +from io import BytesIO from operator import itemgetter from typing import Dict, Iterable, List, Set -from discord import Colour, Member, Message, NotFound, Object, TextChannel +from discord import Colour, File, HTTPException, Member, Message, NotFound, Object, TextChannel from discord.ext.commands import Bot, Cog from bot import rules @@ -41,11 +42,13 @@ class DeletionContext: """Represents a Deletion Context for a single spam event.""" channel: TextChannel + bot: Bot members: Dict[int, Member] = field(default_factory=dict) rules: Set[str] = field(default_factory=set) messages: Dict[int, Message] = field(default_factory=dict) + attachments: List[List[str]] = field(default_factory=list) - def add(self, rule_name: str, members: Iterable[Member], messages: Iterable[Message]) -> None: + async def add(self, rule_name: str, members: Iterable[Member], messages: Iterable[Message]) -> None: """Adds new rule violation events to the deletion context.""" self.rules.add(rule_name) @@ -57,6 +60,9 @@ class DeletionContext: if message.id not in self.messages: self.messages[message.id] = message + # Re-upload attachments : + self.attachments.append(await reupload_attachments(self.bot, message)) + async def upload_messages(self, actor_id: int, modlog: ModLog) -> None: """Method that takes care of uploading the queue and posting modlog alert.""" triggered_by_users = ", ".join(f"{m} (`{m.id}`)" for m in self.members.values()) @@ -69,7 +75,7 @@ class DeletionContext: # For multiple messages or those with excessive newlines, use the logs API if len(self.messages) > 1 or 'newlines' in self.rules: - url = await modlog.upload_log(self.messages.values(), actor_id) + url = await modlog.upload_log(self.messages.values(), actor_id, attachments=self.attachments) mod_alert_message += f"A complete log of the offending messages can be found [here]({url})" else: mod_alert_message += "Message:\n" @@ -181,13 +187,16 @@ class AntiSpam(Cog): # If there's no spam event going on for this channel, start a new Message Deletion Context if message.channel.id not in self.message_deletion_queue: log.trace(f"Creating queue for channel `{message.channel.id}`") - self.message_deletion_queue[message.channel.id] = DeletionContext(channel=message.channel) + self.message_deletion_queue[message.channel.id] = DeletionContext( + channel=message.channel, + bot=self.bot + ) self.queue_consumption_tasks = self.bot.loop.create_task( self._process_deletion_context(message.channel.id) ) # Add the relevant of this trigger to the Deletion Context - self.message_deletion_queue[message.channel.id].add( + await self.message_deletion_queue[message.channel.id].add( rule_name=rule_name, members=members, messages=relevant_messages @@ -254,6 +263,30 @@ class AntiSpam(Cog): await deletion_context.upload_messages(self.bot.user.id, self.mod_log) +async def reupload_attachments( + bot: Bot, + message: Message, + channel_id: int = GuildConfig.attachment_repost +) -> List[str]: + """Re-upload message's attachments to the the channel_id and return the list of re-posted attachments URLs.""" + if not message.attachments: + return [] + channel = bot.get_channel(channel_id) + out = [] + for attachment in message.attachments: + try: + with BytesIO() as buffer: + await attachment.save(buffer, use_cached=True) + reupload: Message = await channel.send(file=File(buffer, filename=attachment.filename)) + out.append(reupload.attachments[0].url) + except HTTPException: + log.warning( + f"Tried to re-upload attachment {attachment.filename} " + f"with ID {attachment.id} from message {message.id}, but it has failed." + ) + return out + + def validate_config(rules: Mapping = AntiSpamConfig.rules) -> Dict[str, str]: """Validates the antispam configs.""" validation_errors = {} diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 88f2b6c67..9251b79fb 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -34,7 +34,12 @@ class ModLog(Cog, name="ModLog"): self._cached_deletes = [] self._cached_edits = [] - async def upload_log(self, messages: t.List[discord.Message], actor_id: int) -> str: + async def upload_log( + self, + messages: t.List[discord.Message], + actor_id: int, + attachments: t.List[t.List[str]] = None + ) -> str: """ Uploads the log data to the database via an API endpoint for uploading logs. @@ -42,6 +47,9 @@ class ModLog(Cog, name="ModLog"): Returns a URL that can be used to view the log. """ + if attachments is None: + attachments = [] + response = await self.bot.api_client.post( 'bot/deleted-messages', json={ @@ -53,9 +61,10 @@ class ModLog(Cog, name="ModLog"): 'author': message.author.id, 'channel_id': message.channel.id, 'content': message.content, - 'embeds': [embed.to_dict() for embed in message.embeds] + 'embeds': [embed.to_dict() for embed in message.embeds], + 'attachments': attachment, } - for message in messages + for message, attachment in zip(messages, attachments) ] } ) diff --git a/bot/constants.py b/bot/constants.py index 45f42cf81..0fee51df9 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -405,6 +405,7 @@ class Guild(metaclass=YAMLGetter): id: int ignored: List[int] staff_channels: List[int] + attachment_repost: int class Keys(metaclass=YAMLGetter): diff --git a/config-default.yml b/config-default.yml index ee9f8a06b..608e5660c 100644 --- a/config-default.yml +++ b/config-default.yml @@ -134,6 +134,7 @@ guild: staff_channels: [*ADMINS, *ADMIN_SPAM, *MOD_SPAM, *MODS, *HELPERS, *ORGANISATION, *DEFCON] ignored: [*ADMINS, *MESSAGE_LOG, *MODLOG] + attachment_repost: 649243850006855680 roles: admin: &ADMIN_ROLE 267628507062992896 |