diff options
| author | 2022-11-25 23:43:03 +0200 | |
|---|---|---|
| committer | 2022-11-25 23:43:03 +0200 | |
| commit | 7591c5e58d97d264505302c984acca32da143fc7 (patch) | |
| tree | 8bb5f4fc7672bd49d334a95e97afe6047a3de8e9 | |
| parent | Add nickname filter (diff) | |
Add offending message deletion scheduling
This is mostly a copy-paste from the old system.
If there are any actions taken against the message, but deleting is not one of them - schedule the message for deletion after 7 days.
| -rw-r--r-- | bot/exts/filtering/filtering.py | 77 |
1 files changed, 72 insertions, 5 deletions
diff --git a/bot/exts/filtering/filtering.py b/bot/exts/filtering/filtering.py index 9c9b1eff4..65089a6d5 100644 --- a/bot/exts/filtering/filtering.py +++ b/bot/exts/filtering/filtering.py @@ -4,7 +4,7 @@ import operator import re import unicodedata from collections import defaultdict -from collections.abc import Iterable +from collections.abc import Iterable, Mapping from functools import partial, reduce from io import BytesIO from operator import attrgetter @@ -14,6 +14,7 @@ import arrow import discord from async_rediscache import RedisCache from botcore.site_api import ResponseCodeError +from botcore.utils import scheduling from discord import Colour, Embed, HTTPException, Message, MessageType from discord.ext import commands, tasks from discord.ext.commands import BadArgument, Cog, Context, command, has_any_role @@ -48,6 +49,7 @@ log = get_logger(__name__) CACHE_SIZE = 100 HOURS_BETWEEN_NICKNAME_ALERTS = 1 +OFFENSIVE_MSG_DELETE_TIME = datetime.timedelta(days=7) WEEKLY_REPORT_ISO_DAY = 3 # 1=Monday, 7=Sunday @@ -66,6 +68,7 @@ class Filtering(Cog): self.bot = bot self.filter_lists: dict[str, FilterList] = {} self._subscriptions: defaultdict[Event, list[FilterList]] = defaultdict(list) + self.delete_scheduler = scheduling.Scheduler(self.__class__.__name__) self.webhook = None self.loaded_settings = {} @@ -95,6 +98,7 @@ class Filtering(Cog): log.error(f"Failed to fetch filters webhook with ID `{Webhooks.filters}`.") self.collect_loaded_types(example_list) + await self.schedule_offending_messages_deletion() self.weekly_auto_infraction_report_task.start() def subscribe(self, filter_list: FilterList, *events: Event) -> None: @@ -175,6 +179,18 @@ class Filtering(Cog): for field_name in extra_fields_type.__fields__ } + async def schedule_offending_messages_deletion(self) -> None: + """Load the messages that need to be scheduled for deletion from the database.""" + response = await self.bot.api_client.get('bot/offensive-messages') + + now = arrow.utcnow() + for msg in response: + delete_at = arrow.get(msg['delete_date']) + if delete_at < now: + await self._delete_offensive_msg(msg) + else: + self._schedule_msg_delete(msg) + async def cog_check(self, ctx: Context) -> bool: """Only allow moderators to invoke the commands in this cog.""" return await has_any_role(*MODERATION_ROLES).predicate(ctx) @@ -197,9 +213,11 @@ class Filtering(Cog): if ctx.send_alert: await self._send_alert(ctx, list_messages) - ctx = FilterContext.from_message(Event.NICKNAME, msg) - ctx.content = msg.author.display_name - await self._check_bad_name(ctx) + nick_ctx = FilterContext.from_message(Event.NICKNAME, msg) + nick_ctx.content = msg.author.display_name + await self._check_bad_name(nick_ctx) + + await self._maybe_schedule_msg_delete(ctx, result_actions) @Cog.listener() async def on_message_edit(self, before: discord.Message, after: discord.Message) -> None: @@ -221,6 +239,7 @@ class Filtering(Cog): await result_actions.action(ctx) if ctx.send_alert: await self._send_alert(ctx, list_messages) + await self._maybe_schedule_msg_delete(ctx, result_actions) @Cog.listener() async def on_voice_state_update(self, member: discord.Member, *_) -> None: @@ -1187,6 +1206,53 @@ class Filtering(Cog): ctx = await bot.instance.get_context(message) await LinePaginator.paginate(lines, ctx, embed, max_lines=15, empty=False, reply=True) + async def _delete_offensive_msg(self, msg: Mapping[str, int]) -> 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 discord.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 _schedule_msg_delete(self, msg: dict) -> None: + """Delete an offensive message once its deletion date is reached.""" + delete_at = arrow.get(msg['delete_date']).datetime + self.delete_scheduler.schedule_at(delete_at, msg['id'], self._delete_offensive_msg(msg)) + + async def _maybe_schedule_msg_delete(self, ctx: FilterContext, actions: ActionSettings | None) -> None: + """Post the message to the database and schedule it for deletion if it's not set to be deleted already.""" + msg = ctx.message + if not msg or not actions or actions.get_setting("remove_context", True): + return + + delete_date = (msg.created_at + OFFENSIVE_MSG_DELETE_TIME).isoformat() + data = { + 'id': msg.id, + 'channel_id': msg.channel.id, + 'delete_date': delete_date + } + + try: + await self.bot.api_client.post('bot/offensive-messages', json=data) + except ResponseCodeError as e: + if e.status == 400 and "already exists" in e.response_json.get("id", [""])[0]: + log.debug(f"Offensive message {msg.id} already exists.") + else: + log.error(f"Offensive message {msg.id} failed to post: {e}") + else: + self._schedule_msg_delete(data) + log.trace(f"Offensive message {msg.id} will be deleted on {delete_date}") + # endregion # region: tasks @@ -1242,8 +1308,9 @@ class Filtering(Cog): # endregion async def cog_unload(self) -> None: - """Cancel the weekly auto-infraction filter report on cog unload.""" + """Cancel the weekly auto-infraction filter report and deletion scheduling on cog unload.""" self.weekly_auto_infraction_report_task.cancel() + self.delete_scheduler.cancel_all() async def setup(bot: Bot) -> None: |