diff options
| -rw-r--r-- | bot/exts/moderation/infraction/_scheduler.py | 72 | 
1 files changed, 70 insertions, 2 deletions
diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py index 9abf3305b..7610ec04c 100644 --- a/bot/exts/moderation/infraction/_scheduler.py +++ b/bot/exts/moderation/infraction/_scheduler.py @@ -2,14 +2,17 @@ import textwrap  import typing as t  from abc import abstractmethod  from collections.abc import Awaitable, Callable +from datetime import UTC, datetime, timedelta  from gettext import ngettext  import arrow  import dateutil.parser  import discord +from async_rediscache import RedisCache  from discord.ext.commands import Context  from pydis_core.site_api import ResponseCodeError  from pydis_core.utils import scheduling +from pydis_core.utils.channel import get_or_fetch_channel  from bot import constants  from bot.bot import Bot @@ -24,18 +27,26 @@ from bot.utils.modlog import send_log_message  log = get_logger(__name__) +AUTOMATED_TIDY_UP_HOURS = 8 +  class InfractionScheduler:      """Handles the application, pardoning, and expiration of infractions.""" +    messages_to_tidy: RedisCache = RedisCache() +      def __init__(self, bot: Bot, supported_infractions: t.Container[str]):          self.bot = bot          self.scheduler = scheduling.Scheduler(self.__class__.__name__) +        self.tidy_up_scheduler = scheduling.Scheduler( +            f"{self.__class__.__name__}TidyUp" +        )          self.supported_infractions = supported_infractions      async def cog_unload(self) -> None:          """Cancel scheduled tasks."""          self.scheduler.cancel_all() +        self.tidy_up_scheduler.cancel_all()      @property      def mod_log(self) -> ModLog: @@ -76,7 +87,46 @@ class InfractionScheduler:              self.scheduler.schedule_at(next_reschedule_point, -1, self.cog_load()) -        log.trace("Done rescheduling") +        log.trace("Done rescheduling expirations, scheduling tidy up tasks.") + +        for key, expire_at in await self.messages_to_tidy.items(): +            channel_id, message_id = map(int, key.split(":")) +            expire_at = dateutil.parser.isoparse(expire_at) + +            log.trace( +                "Scheduling tidy up for message %s in channel %s at %s", +                message_id, channel_id, expire_at +            ) + +            self.tidy_up_scheduler.schedule_at( +                expire_at, +                message_id, +                self._delete_infraction_message(channel_id, message_id) +            ) + +    async def _delete_infraction_message( +        self, +        channel_id: int, +        message_id: int +    ) -> None: +        """ +        Delete a message in the given channel. + +        This is used to delete infraction messages after a certain period of time. +        """ +        channel = await get_or_fetch_channel(self.bot, channel_id) +        if channel is None: +            log.warning(f"Channel {channel_id} not found for infraction message deletion.") +            return + +        try: +            message = await channel.fetch_message(message_id) +            await message.delete() +            log.trace(f"Deleted infraction message {message_id} in channel {channel_id}.") +        except discord.NotFound: +            log.warning(f"Message {message_id} not found in channel {channel_id}.") + +        await self.messages_to_tidy.delete(f"{channel_id}:{message_id}")      async def reapply_infraction(          self, @@ -269,7 +319,25 @@ class InfractionScheduler:          # Send a confirmation message to the invoking context.          log.trace(f"Sending infraction #{id_} confirmation message.")          mentions = discord.AllowedMentions(users=[user], roles=False) -        await ctx.send(f"{dm_result}{confirm_msg}{infr_message}.", allowed_mentions=mentions) +        sent_msg = await ctx.send(f"{dm_result}{confirm_msg}{infr_message}.", allowed_mentions=mentions) + +        if infraction["actor"] == self.bot.user.id: +            expire_message_time = datetime.now(UTC) + timedelta(hours=AUTOMATED_TIDY_UP_HOURS) + +            log.trace(f"Scheduling message tidy for infraction #{id_} in {AUTOMATED_TIDY_UP_HOURS} hours.") + +            # Schedule the message to be deleted after a certain period of time. +            self.tidy_up_scheduler.schedule_at( +                expire_message_time, +                sent_msg.id, +                self._delete_infraction_message(ctx.channel.id, sent_msg.id) +            ) + +            # Persist to Redis to handle for bot restarts. +            await self.messages_to_tidy.set( +                f"{ctx.channel.id}:{sent_msg.id}", +                expire_message_time.isoformat(), +            )          if jump_url is None:              jump_url = "(Infraction issued in a ModMail channel.)"  |