diff options
| -rw-r--r-- | bot/cogs/filtering.py | 85 | ||||
| -rw-r--r-- | bot/constants.py | 1 | ||||
| -rw-r--r-- | config-default.yml | 3 | 
3 files changed, 81 insertions, 8 deletions
| diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 74538542a..83e706a26 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -1,10 +1,12 @@ +import datetime  import logging  import re -from typing import Optional, Union +from typing import Mapping, Optional, Union +import dateutil  import discord.errors  from dateutil.relativedelta import relativedelta -from discord import Colour, DMChannel, Member, Message, TextChannel +from discord import Colour, DMChannel, HTTPException, Member, Message, NotFound, TextChannel  from discord.ext.commands import Cog  from bot.bot import Bot @@ -13,6 +15,8 @@ from bot.constants import (      Channels, Colours,      Filter, Icons, URLs  ) +from bot.utils.scheduling import Scheduler +from bot.utils.time import wait_until  log = logging.getLogger(__name__) @@ -37,12 +41,15 @@ TOKEN_WATCHLIST_PATTERNS = [      re.compile(fr'{expression}', flags=re.IGNORECASE) for expression in Filter.token_watchlist  ] +OFFENSIVE_MSG_DELETE_TIME = datetime.timedelta(days=Filter.offensive_msg_delete_days) -class Filtering(Cog): + +class Filtering(Cog, Scheduler):      """Filtering out invites, blacklisting domains, and warning us of certain regular expressions."""      def __init__(self, bot: Bot):          self.bot = bot +        super().__init__()          staff_mistake_str = "If you believe this was a mistake, please let staff know!"          self.filters = { @@ -55,7 +62,8 @@ class Filtering(Cog):                  "notification_msg": (                      "Your post has been removed for abusing Unicode character rendering (aka Zalgo text). "                      f"{staff_mistake_str}" -                ) +                ), +                "schedule_deletion": False              },              "filter_invites": {                  "enabled": Filter.filter_invites, @@ -66,7 +74,8 @@ class Filtering(Cog):                  "notification_msg": (                      f"Per Rule 6, your invite link has been removed. {staff_mistake_str}\n\n"                      r"Our server rules can be found here: <https://pythondiscord.com/pages/rules>" -                ) +                ), +                "schedule_deletion": False              },              "filter_domains": {                  "enabled": Filter.filter_domains, @@ -76,28 +85,34 @@ class Filtering(Cog):                  "user_notification": Filter.notify_user_domains,                  "notification_msg": (                      f"Your URL has been removed because it matched a blacklisted domain. {staff_mistake_str}" -                ) +                ), +                "schedule_deletion": False              },              "watch_rich_embeds": {                  "enabled": Filter.watch_rich_embeds,                  "function": self._has_rich_embed,                  "type": "watchlist",                  "content_only": False, +                "schedule_deletion": False              },              "watch_words": {                  "enabled": Filter.watch_words,                  "function": self._has_watchlist_words,                  "type": "watchlist",                  "content_only": True, +                "schedule_deletion": True              },              "watch_tokens": {                  "enabled": Filter.watch_tokens,                  "function": self._has_watchlist_tokens,                  "type": "watchlist",                  "content_only": True, +                "schedule_deletion": True              },          } +        self.bot.loop.create_task(self.reschedule_offensive_msg_deletion()) +      @property      def mod_log(self) -> ModLog:          """Get currently loaded ModLog cog instance.""" @@ -176,6 +191,20 @@ class Filtering(Cog):                              if _filter["user_notification"]:                                  await self.notify_member(msg.author, _filter["notification_msg"], msg.channel) +                        # If the message is classed as offensive, we store it in the site db and +                        # it will be deleted it after one week. +                        if _filter["schedule_deletion"]: +                            delete_date = (msg.created_at + OFFENSIVE_MSG_DELETE_TIME).isoformat() +                            data = { +                                'id': msg.id, +                                'channel_id': msg.channel.id, +                                'delete_date': delete_date +                            } + +                            await self.bot.api_client.post('bot/offensive-messages', json=data) +                            self.schedule_task(self.bot.loop, msg.id, data) +                            log.trace(f"Offensive message {msg.id} will be deleted on {delete_date}") +                          if isinstance(msg.channel, DMChannel):                              channel_str = "via DM"                          else: @@ -300,7 +329,7 @@ class Filtering(Cog):          Attempts to catch some of common ways to try to cheat the system.          """ -        # Remove backslashes to prevent escape character aroundfuckery like +        # Remove backslashes to prevent escape character around fuckery like          # discord\.gg/gdudes-pony-farm          text = text.replace("\\", "") @@ -369,6 +398,48 @@ class Filtering(Cog):          except discord.errors.Forbidden:              await channel.send(f"{filtered_member.mention} {reason}") +    async def _scheduled_task(self, msg: dict) -> None: +        """Delete an offensive message once its deletion date is reached.""" +        delete_at = dateutil.parser.isoparse(msg['delete_date']) + +        await wait_until(delete_at) +        await self.delete_offensive_msg(msg) + +        self.cancel_task(msg['id']) + +    async def reschedule_offensive_msg_deletion(self) -> None: +        """Get all the pending message deletion from the API and reschedule them.""" +        await self.bot.wait_until_ready() +        response = await self.bot.api_client.get('bot/offensive-messages',) + +        now = datetime.datetime.utcnow() + +        for msg in response: +            delete_at = dateutil.parser.isoparse(msg['delete_date']).replace(tzinfo=None) + +            if delete_at < now: +                await self.delete_offensive_msg(msg) +            else: +                self.schedule_task(self.bot.loop, msg['id'], msg) + +    async def delete_offensive_msg(self, msg: Mapping[str, str]) -> None: +        """Delete an offensive message, and then delete it from the db.""" +        try: +            channel = self.bot.get_channel(msg['channel_id']) +            if channel: +                msg_obj = await channel.fetch_message(msg['id']) +                await msg_obj.delete() +        except NotFound: +            log.info( +                f"Tried to delete message {msg['id']}, but the message can't be found " +                f"(it has been probably already deleted)." +            ) +        except HTTPException as e: +            log.warning(f"Failed to delete message {msg['id']}: status {e.status}") + +        await self.bot.api_client.delete(f'bot/offensive-messages/{msg["id"]}') +        log.info(f"Deleted the offensive message with id {msg['id']}.") +  def setup(bot: Bot) -> None:      """Load the Filtering cog.""" diff --git a/bot/constants.py b/bot/constants.py index 25c7856ba..a10aaa72c 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -211,6 +211,7 @@ class Filter(metaclass=YAMLGetter):      notify_user_domains: bool      ping_everyone: bool +    offensive_msg_delete_days: int      guild_invite_whitelist: List[int]      domain_blacklist: List[str]      word_watchlist: List[str] diff --git a/config-default.yml b/config-default.yml index 1a8aaedae..20a6ac71f 100644 --- a/config-default.yml +++ b/config-default.yml @@ -194,7 +194,8 @@ filter:      notify_user_domains:     false      # Filter configuration -    ping_everyone: true  # Ping @everyone when we send a mod-alert? +    ping_everyone:             true  # Ping @everyone when we send a mod-alert? +    offensive_msg_delete_days: 7     # How many days before deleting an offensive message?      guild_invite_whitelist:          - 280033776820813825  # Functional Programming | 
