aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Akarys42 <[email protected]>2019-10-25 12:10:26 +0200
committerGravatar Akarys42 <[email protected]>2019-10-25 12:10:26 +0200
commit9b8d688657f7044ad27ff81f7eb7d50f7f593ed6 (patch)
tree89a5c2b8b1892920c1505a999de7000a52c67f77
parentMerge pull request #541 from ikuyarihS/master (diff)
Autodelete offensive messages after one week.
If the filter cog filter a message that's considered as offensive (filter["offensive_msg"] is True), the cog create a new offensive message object in the bot db with a delete_date of one week after it was sent. A background task run every day, pull up a list of message to delete, find them back, and delete them.
-rw-r--r--bot/cogs/filtering.py89
1 files changed, 79 insertions, 10 deletions
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 1d1d74e74..d1d28ac10 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -1,12 +1,15 @@
+import asyncio
+import datetime
import logging
import re
from typing import Optional, Union
import discord.errors
from dateutil.relativedelta import relativedelta
-from discord import Colour, DMChannel, Member, Message, TextChannel
+from discord import Colour, DMChannel, Member, Message, NotFound, TextChannel
from discord.ext.commands import Bot, Cog
+from bot.api import ResponseCodeError
from bot.cogs.moderation import ModLog
from bot.constants import (
Channels, Colours, DEBUG_MODE,
@@ -16,13 +19,13 @@ from bot.constants import (
log = logging.getLogger(__name__)
INVITE_RE = re.compile(
- r"(?:discord(?:[\.,]|dot)gg|" # Could be discord.gg/
- r"discord(?:[\.,]|dot)com(?:\/|slash)invite|" # or discord.com/invite/
+ r"(?:discord(?:[\.,]|dot)gg|" # Could be discord.gg/
+ r"discord(?:[\.,]|dot)com(?:\/|slash)invite|" # or discord.com/invite/
r"discordapp(?:[\.,]|dot)com(?:\/|slash)invite|" # or discordapp.com/invite/
- r"discord(?:[\.,]|dot)me|" # or discord.me
- r"discord(?:[\.,]|dot)io" # or discord.io.
- r")(?:[\/]|slash)" # / or 'slash'
- r"([a-zA-Z0-9]+)", # the invite code itself
+ r"discord(?:[\.,]|dot)me|" # or discord.me
+ r"discord(?:[\.,]|dot)io" # or discord.io.
+ r")(?:[\/]|slash)" # / or 'slash'
+ r"([a-zA-Z0-9]+)", # the invite code itself
flags=re.IGNORECASE
)
@@ -36,6 +39,8 @@ TOKEN_WATCHLIST_PATTERNS = [
re.compile(fr'{expression}', flags=re.IGNORECASE) for expression in Filter.token_watchlist
]
+OFFENSIVE_MSG_DELETE_TIME = datetime.timedelta(days=7) # Time before an offensive msg is deleted.
+
class Filtering(Cog):
"""Filtering out invites, blacklisting domains, and warning us of certain regular expressions."""
@@ -54,7 +59,8 @@ class Filtering(Cog):
"notification_msg": (
"Your post has been removed for abusing Unicode character rendering (aka Zalgo text). "
f"{_staff_mistake_str}"
- )
+ ),
+ "offensive_msg": False
},
"filter_invites": {
"enabled": Filter.filter_invites,
@@ -65,7 +71,8 @@ class Filtering(Cog):
"notification_msg": (
f"Per Rule 10, your invite link has been removed. {_staff_mistake_str}\n\n"
r"Our server rules can be found here: <https://pythondiscord.com/pages/rules>"
- )
+ ),
+ "offensive_msg": False
},
"filter_domains": {
"enabled": Filter.filter_domains,
@@ -75,28 +82,47 @@ 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}"
- )
+ ),
+ "offensive_msg": False
},
"watch_rich_embeds": {
"enabled": Filter.watch_rich_embeds,
"function": self._has_rich_embed,
"type": "watchlist",
"content_only": False,
+ "offensive_msg": False
},
"watch_words": {
"enabled": Filter.watch_words,
"function": self._has_watchlist_words,
"type": "watchlist",
"content_only": True,
+ "offensive_msg": True
},
"watch_tokens": {
"enabled": Filter.watch_tokens,
"function": self._has_watchlist_tokens,
"type": "watchlist",
"content_only": True,
+ "offensive_msg": True
},
}
+ self.deletion_task = None
+ self.bot.loop.create_task(self.init_deletion_task())
+
+ def cog_unload(self) -> None:
+ """Cancel any running updater tasks on cog unload."""
+ if self.deletion_task is not None:
+ self.deletion_task.cancel()
+
+ async def init_deletion_task(self) -> None:
+ """Start offensive messages deletion event loop if it hasn't already started."""
+ await self.bot.wait_until_ready()
+ if self.deletion_task is None:
+ coro = delete_offensive_msg(self.bot)
+ self.deletion_task = self.bot.loop.create_task(coro)
+
@property
def mod_log(self) -> ModLog:
"""Get currently loaded ModLog cog instance."""
@@ -159,6 +185,21 @@ class Filtering(Cog):
triggered = await _filter["function"](msg)
if triggered:
+ # 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["offensive_msg"]:
+ delete_date = msg.created_at.date() + OFFENSIVE_MSG_DELETE_TIME
+ await self.bot.api_client.post(
+ 'bot/offensive-message',
+ json={
+ 'id': msg.id,
+ 'channel_id': msg.channel.id,
+ 'delete_date': delete_date.isoformat()
+ }
+ )
+ log.trace(f"Offensive message will be deleted on "
+ f"{delete_date.isoformat()}")
+
# If this is a filter (not a watchlist), we should delete the message.
if _filter["type"] == "filter":
try:
@@ -360,6 +401,34 @@ class Filtering(Cog):
await channel.send(f"{filtered_member.mention} {reason}")
+async def delete_offensive_msg(bot: Bot) -> None:
+ """Background task that pull up a list of offensive messages every day and delete them."""
+ while True:
+ tomorrow = datetime.date.today() + datetime.timedelta(days=1)
+ time_until_next = datetime.datetime(tomorrow.year, tomorrow.month, tomorrow.day) - datetime.datetime.now()
+ try:
+ msg_list = await bot.api_client.get(
+ 'bot/offensive-message',
+ params={'delete_date': datetime.date.today().isoformat()}
+ )
+ except ResponseCodeError as e:
+ log.error(f"Failed to get offending messages to delete (got code {e.response.status}), "
+ f"retrying in 30 minutes.")
+ time_until_next = datetime.timedelta(minutes=30)
+ msg_list = []
+ for msg in msg_list:
+ try:
+ channel = 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).")
+ log.info(f"Deleted {len(msg_list)} offensive message(s).")
+ await asyncio.sleep(time_until_next.seconds)
+
+
def setup(bot: Bot) -> None:
"""Filtering cog load."""
bot.add_cog(Filtering(bot))