From c9e67a11e5d732dacae8cdafae624f79b5914a3c Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 25 Jul 2018 21:56:12 +0200 Subject: Updating config-default.yml with the required configuration parameters for the censor plugin --- bot/constants.py | 1 + config-default.yml | 107 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 30289ff0e..9c6e75c9b 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -261,6 +261,7 @@ class Channels(metaclass=YAMLGetter): off_topic_2: int off_topic_3: int python: int + staff_lounge: int verification: int diff --git a/config-default.yml b/config-default.yml index dacf12cd9..aed07c137 100644 --- a/config-default.yml +++ b/config-default.yml @@ -38,46 +38,89 @@ bot: user_update: "https://cdn.discordapp.com/emojis/469952898684551168.png" + + + + + guild: id: 267624335836053506 channels: - admins: &ADMINS 365960823622991872 - announcements: 354619224620138496 - big_brother_logs: 468507907357409333 - bot: 267659945086812160 - checkpoint_test: 422077681434099723 - devalerts: 460181980097675264 - devlog: 409308876241108992 - devtest: 414574275865870337 - help_0: 303906576991780866 - help_1: 303906556754395136 - help_2: 303906514266226689 - help_3: 439702951246692352 - help_4: 451312046647148554 - help_5: 454941769734422538 - helpers: 385474242440986624 - message_log: &MESSAGE_LOG 467752170159079424 - modlog: &MODLOG 282638479504965634 - off_topic_0: 291284109232308226 - off_topic_1: 463035241142026251 - off_topic_2: 463035268514185226 - python: 267624335836053506 - verification: 352442727016693763 + admins: &ADMINS 365960823622991872 + announcements: 354619224620138496 + big_brother_logs: &BBLOGS 468507907357409333 + bot: 267659945086812160 + checkpoint_test: 422077681434099723 + devalerts: 460181980097675264 + devlog: &DEVLOG 409308876241108992 + devtest: &DEVTEST 414574275865870337 + help_0: 303906576991780866 + help_1: 303906556754395136 + help_2: 303906514266226689 + help_3: 439702951246692352 + help_4: 451312046647148554 + help_5: 454941769734422538 + helpers: 385474242440986624 + message_log: &MESSAGE_LOG 467752170159079424 + modlog: &MODLOG 282638479504965634 + off_topic_0: 291284109232308226 + off_topic_1: 463035241142026251 + off_topic_2: 463035268514185226 + python: 267624335836053506 + staff_lounge: &STAFF_LOUNGE 464905259261755392 + verification: 352442727016693763 ignored: [*ADMINS, *MESSAGE_LOG, *MODLOG] roles: - admin: 267628507062992896 - announcements: 463658397560995840 - champion: 430492892331769857 - contributor: 295488872404484098 - devops: 409416496733880320 - jammer: 423054537079783434 - moderator: 267629731250176001 - owner: 267627879762755584 - verified: 352427296948486144 - helpers: 267630620367257601 + admin: &ADMIN_ROLE 267628507062992896 + announcements: 463658397560995840 + champion: 430492892331769857 + contributor: 295488872404484098 + devops: 409416496733880320 + jammer: 423054537079783434 + moderator: &MOD_ROLE 267629731250176001 + owner: &OWNER_ROLE 267627879762755584 + verified: 352427296948486144 + helpers: 267630620367257601 + + +censor: + + # What do we filter? + filter_zalgo: true + filter_invites: true + filter_domains: true + filter_expression: true + + # Filter configuration + domain_blacklist: + - pornhub.com + + blocked_expressions: + - .*ni[g]*er.* + + guild_invite_whitelist: + - vywQPxd # Code Monkeys + - kWJYurV # Functional Programming + - 010z0Kw1A9ql5c1Qe # Programming: Meme Edition + - XBGetGp # STEM + + # Censor doesn't apply to these + channel_whitelist: + - *ADMINS + - *DEVTEST + - *MODLOG + - *MESSAGE_LOG + - *DEVLOG + - *BBLOGS + - *STAFF_LOUNGE + + role_whitelist: + - *ADMIN_ROLE + - *MOD_ROLE + - *OWNER_ROLE keys: -- cgit v1.2.3 From ec486db76ab8962ad95e4f4b91d882eba0ac748d Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 25 Jul 2018 22:35:27 +0200 Subject: Adding all the constants to the dataclass. --- bot/constants.py | 74 +++++++++++++++++++++++++++++++++--------------------- config-default.yml | 8 +----- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 9c6e75c9b..3cb9849aa 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -191,6 +191,51 @@ class Bot(metaclass=YAMLGetter): token: str +class Censor(metaclass=YAMLGetter): + section = "censor" + + filter_zalgo: bool + filter_invites: bool + filter_domain: bool + filter_expression: bool + + domain_blacklist: List[str] + blocked_expressions: List[str] + guild_invite_whitelist: List[str] + + channel_whitelist: List[int] + role_whitelist: List[int] + + +class Channels(metaclass=YAMLGetter): + section = "guild" + subsection = "channels" + + admins: int + announcements: int + big_brother_logs: int + bot: int + checkpoint_test: int + devalerts: int + devlog: int + devtest: int + help_0: int + help_1: int + help_2: int + help_3: int + help_4: int + help_5: int + helpers: int + message_log: int + modlog: int + off_topic_1: int + off_topic_2: int + off_topic_3: int + python: int + staff_lounge: int + verification: int + + class Cooldowns(metaclass=YAMLGetter): section = "bot" subsection = "cooldowns" @@ -236,35 +281,6 @@ class Icons(metaclass=YAMLGetter): user_update: str -class Channels(metaclass=YAMLGetter): - section = "guild" - subsection = "channels" - - admins: int - announcements: int - big_brother_logs: int - bot: int - checkpoint_test: int - devalerts: int - devlog: int - devtest: int - help_0: int - help_1: int - help_2: int - help_3: int - help_4: int - help_5: int - helpers: int - message_log: int - modlog: int - off_topic_1: int - off_topic_2: int - off_topic_3: int - python: int - staff_lounge: int - verification: int - - class Roles(metaclass=YAMLGetter): section = "guild" subsection = "roles" diff --git a/config-default.yml b/config-default.yml index aed07c137..ce2600c61 100644 --- a/config-default.yml +++ b/config-default.yml @@ -38,11 +38,6 @@ bot: user_update: "https://cdn.discordapp.com/emojis/469952898684551168.png" - - - - - guild: id: 267624335836053506 @@ -87,7 +82,6 @@ guild: censor: - # What do we filter? filter_zalgo: true filter_invites: true @@ -110,12 +104,12 @@ censor: # Censor doesn't apply to these channel_whitelist: - *ADMINS - - *DEVTEST - *MODLOG - *MESSAGE_LOG - *DEVLOG - *BBLOGS - *STAFF_LOUNGE +# - *DEVTEST role_whitelist: - *ADMIN_ROLE -- cgit v1.2.3 From 17113bfd26d7d64f8ac3310e61baac18a7f26454 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 25 Jul 2018 23:54:07 +0200 Subject: Created the Filtering cog and implemented a filter for zalgo. --- bot/cogs/filtering.py | 34 ++++++++++++++++++++++++++++++++++ bot/constants.py | 8 ++++---- config-default.yml | 9 +++++---- 3 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 bot/cogs/filtering.py diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py new file mode 100644 index 000000000..ccd270924 --- /dev/null +++ b/bot/cogs/filtering.py @@ -0,0 +1,34 @@ +import logging +import re + +from discord import Message +from discord.ext.commands import Bot + +log = logging.getLogger(__name__) + + +class Filtering: + """ + Filtering out invites, blacklisting domains, + and preventing certain expressions""" + + def __init__(self, bot: Bot): + self.bot = bot + + async def on_message(self, msg: Message): + self._filter_zalgo(msg.content) + + @staticmethod + async def _has_zalgo(text): + """ + Returns True if the text contains zalgo characters. + + Zalgo range is \u0300 – \u036F and \u0489. + """ + + return bool(re.search(r"[\u0300-\u036F\u0489]", text)) + + +def setup(bot: Bot): + bot.add_cog(Filtering(bot)) + log.info("Cog loaded: Filtering") diff --git a/bot/constants.py b/bot/constants.py index 3cb9849aa..2abdcfca9 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -191,16 +191,16 @@ class Bot(metaclass=YAMLGetter): token: str -class Censor(metaclass=YAMLGetter): - section = "censor" +class Filtering(metaclass=YAMLGetter): + section = "filtering" filter_zalgo: bool filter_invites: bool filter_domain: bool - filter_expression: bool + watch_expressions: bool domain_blacklist: List[str] - blocked_expressions: List[str] + watched_expressions: List[str] guild_invite_whitelist: List[str] channel_whitelist: List[int] diff --git a/config-default.yml b/config-default.yml index ce2600c61..cf42568e5 100644 --- a/config-default.yml +++ b/config-default.yml @@ -81,18 +81,19 @@ guild: helpers: 267630620367257601 -censor: +filtering: + # What do we filter? filter_zalgo: true filter_invites: true filter_domains: true - filter_expression: true + watch_expressions: true # Filter configuration domain_blacklist: - pornhub.com - blocked_expressions: + watched_expressions: - .*ni[g]*er.* guild_invite_whitelist: @@ -109,7 +110,7 @@ censor: - *DEVLOG - *BBLOGS - *STAFF_LOUNGE -# - *DEVTEST + - *DEVTEST role_whitelist: - *ADMIN_ROLE -- cgit v1.2.3 From eaf146e7c09f1127f5a12e0174d77b38cd32ba2f Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Fri, 27 Jul 2018 21:38:01 +0200 Subject: minor testing parameters --- bot/cogs/filtering.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index ccd270924..23b0d9e96 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -4,6 +4,8 @@ import re from discord import Message from discord.ext.commands import Bot +from bot.constants import Channels + log = logging.getLogger(__name__) @@ -16,7 +18,13 @@ class Filtering: self.bot = bot async def on_message(self, msg: Message): - self._filter_zalgo(msg.content) + + has_zalgo = await self._filter_zalgo(msg.content) + + if has_zalgo: + self.bot.get_channel(Channels.modlog).send( + content="ZALGO!" + ) @staticmethod async def _has_zalgo(text): -- cgit v1.2.3 From eadbe81edc1df13be7edfc51f6011631ebece376 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 28 Jul 2018 02:00:45 +0200 Subject: Added smart invite filtering and dumb domain filtering. --- bot/__main__.py | 1 + bot/cogs/filtering.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++----- bot/constants.py | 5 +-- config-default.yml | 6 ++-- 4 files changed, 89 insertions(+), 12 deletions(-) diff --git a/bot/__main__.py b/bot/__main__.py index 4429c2a0d..3ecf8cb18 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -41,6 +41,7 @@ bot.load_extension("bot.cogs.logging") bot.load_extension("bot.cogs.modlog") bot.load_extension("bot.cogs.security") bot.load_extension("bot.cogs.events") +bot.load_extension("bot.cogs.filtering") # Commands, etc bot.load_extension("bot.cogs.bigbrother") diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 23b0d9e96..eee1dbd16 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -4,27 +4,71 @@ import re from discord import Message from discord.ext.commands import Bot -from bot.constants import Channels +from bot.constants import Channels, Filter log = logging.getLogger(__name__) +INVITE_RE = ( + 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 +) + +URL_RE = "(https?://[^\s]+)" +ZALGO_RE = r"[\u0300-\u036F\u0489]" + class Filtering: """ Filtering out invites, blacklisting domains, - and preventing certain expressions""" + and preventing certain expressions + """ def __init__(self, bot: Bot): self.bot = bot async def on_message(self, msg: Message): - has_zalgo = await self._filter_zalgo(msg.content) + if msg.channel.id == Channels.devtest and not msg.author.bot: - if has_zalgo: - self.bot.get_channel(Channels.modlog).send( - content="ZALGO!" - ) + has_zalgo = await self._has_zalgo(msg.content) + has_invites = await self._has_invites(msg.content) + has_urls = await self._has_urls(msg.content) + + if has_zalgo: + await self.bot.get_channel(msg.channel.id).send( + content="ZALGO!" + ) + + if has_invites: + await self.bot.get_channel(msg.channel.id).send( + content="INVITES!" + ) + + if has_urls: + await self.bot.get_channel(msg.channel.id).send( + content="EVIL ILLEGAL HITLER DOMAINS!" + ) + + @staticmethod + async def _has_urls(text): + """ + Returns True if the text contains one of + the blacklisted URLs from the config file. + """ + + if not re.search(URL_RE, text): + return False + + for url in Filter.domain_blacklist: + if url in text: + return True + + return False @staticmethod async def _has_zalgo(text): @@ -34,7 +78,36 @@ class Filtering: Zalgo range is \u0300 – \u036F and \u0489. """ - return bool(re.search(r"[\u0300-\u036F\u0489]", text)) + return bool(re.search(ZALGO_RE, text)) + + @staticmethod + async def _has_invites(text): + """ + Returns True if the text contains an invite which + is not on the guild_invite_whitelist in config.yml. + + Also catches a lot of common ways to try to cheat the system. + """ + + # Remove spaces to prevent cases like + # d i s c o r d . c o m / i n v i t e / p y t h o n + text = text.replace(" ", "") + + # Remove backslashes to prevent escape character aroundfuckery like + # discord\.gg/gdudes-pony-farm + text = text.replace("\\", "") + + invites = re.findall(INVITE_RE, text) + for invite in invites: + + filter_invite = ( + invite not in Filter.guild_invite_whitelist + and invite.lower() not in Filter.vanity_url_whitelist + ) + + if filter_invite: + return True + return False def setup(bot: Bot): diff --git a/bot/constants.py b/bot/constants.py index 834c14fd8..597c7c4a7 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -191,8 +191,8 @@ class Bot(metaclass=YAMLGetter): token: str -class Filtering(metaclass=YAMLGetter): - section = "filtering" +class Filter(metaclass=YAMLGetter): + section = "filter" filter_zalgo: bool filter_invites: bool @@ -202,6 +202,7 @@ class Filtering(metaclass=YAMLGetter): domain_blacklist: List[str] watched_expressions: List[str] guild_invite_whitelist: List[str] + vanity_url_whitelist: List[str] channel_whitelist: List[int] role_whitelist: List[int] diff --git a/config-default.yml b/config-default.yml index eeeb5fb34..06117ef05 100644 --- a/config-default.yml +++ b/config-default.yml @@ -90,7 +90,7 @@ guild: helpers: 267630620367257601 -filtering: +filter: # What do we filter? filter_zalgo: true @@ -111,6 +111,9 @@ filtering: - 010z0Kw1A9ql5c1Qe # Programming: Meme Edition - XBGetGp # STEM + vanity_url_whitelist: + - python # Python Discord + # Censor doesn't apply to these channel_whitelist: - *ADMINS @@ -126,7 +129,6 @@ filtering: - *MOD_ROLE - *OWNER_ROLE - keys: deploy_bot: !ENV "DEPLOY_BOT_KEY" deploy_site: !ENV "DEPLOY_SITE" -- cgit v1.2.3 From 80f197b57c5e2baad0fc233cc9539e306c2e1527 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 28 Jul 2018 13:56:47 +0200 Subject: finished all filters and built a rules engine. now just need to write the automod and the mod-alert features so we can actually respond accordingly. --- bot/cogs/filtering.py | 131 ++++++++++++++++++++++++++++++++++++++++++++------ bot/constants.py | 10 ++-- config-default.yml | 47 +++++++++++++----- 3 files changed, 157 insertions(+), 31 deletions(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index eee1dbd16..1da378c08 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -31,28 +31,127 @@ class Filtering: def __init__(self, bot: Bot): self.bot = bot + self.filters = { + "filter_zalgo": { + "enabled": Filter.filter_zalgo, + "function": self._has_zalgo, + "type": "filter" + }, + "filter_invites": { + "enabled": Filter.filter_invites, + "function": self._has_invites, + "type": "filter" + }, + "filter_domains": { + "enabled": Filter.filter_domains, + "function": self._has_urls, + "type": "filter" + }, + "watch_words": { + "enabled": Filter.watch_words, + "function": self._has_watchlist_words, + "type": "watchlist" + }, + "watch_tokens": { + "enabled": Filter.watch_tokens, + "function": self._has_watchlist_tokens, + "type": "watchlist" + }, + } + async def on_message(self, msg: Message): + """ + Whenever a message is received, + run it through our filters to see if it + violates any of our rules, and then respond + accordingly. + """ - if msg.channel.id == Channels.devtest and not msg.author.bot: + # Check if the sender has a role that is whitelisted + role_whitelisted = False + for role in msg.author.roles: + if role.id in Filter.role_whitelist: + role_whitelisted = True + + # Is the channel whitelisted or is the sender a bot? + filter_message = ( + msg.channel.id not in Filter.channel_whitelist + and not role_whitelisted + and not msg.author.bot + ) + + filter_message = not msg.author.bot and msg.channel.id == Channels.modlog # for testing + + # If none of the above, we can start filtering. + if filter_message: + for filter_name, _filter in self.filters.items(): + + # Is the filter enabled in the config? + if _filter["enabled"]: + triggered = await _filter["function"](msg.content) + + if triggered: + # If a filter is triggered, we should automod it. + if _filter["type"] == "filter": + log.debug( + f"The {filter_name} filter was triggered " + f"by {msg.author.name} in {msg.channel.name} with " + f"the following message:\n{msg.content}." + ) + + # Replace this with actual automod + await self.bot.get_channel(msg.channel.id).send( + content=f"The **{filter_name}** filter triggered!" + ) + + # If a watchlist triggers, we should send a mod alert. + elif _filter["type"] == "watchlist": + log.debug( + f"The {filter_name} watchlist was triggered " + f"by {msg.author.name} in {msg.channel.name} with " + f"the following message:\n{msg.content}." + ) + + # Replace this with actual mod alerts! + await self.bot.get_channel(msg.channel.id).send( + content=f"The **{filter_name}** watchlist was triggered!" + ) + + break # We don't want multiple filters to trigger - has_zalgo = await self._has_zalgo(msg.content) - has_invites = await self._has_invites(msg.content) - has_urls = await self._has_urls(msg.content) + @staticmethod + async def _has_watchlist_words(text): + """ + Returns True if the text contains + one of the regular expressions from the + word_watchlist in our filter config. - if has_zalgo: - await self.bot.get_channel(msg.channel.id).send( - content="ZALGO!" - ) + Only matches words with boundaries before + and after the expression. + """ + + for expression in Filter.word_watchlist: + if re.search(fr"\b{expression}\b", text.lower()): + return True - if has_invites: - await self.bot.get_channel(msg.channel.id).send( - content="INVITES!" - ) + return False - if has_urls: - await self.bot.get_channel(msg.channel.id).send( - content="EVIL ILLEGAL HITLER DOMAINS!" - ) + @staticmethod + async def _has_watchlist_tokens(text): + """ + Returns True if the text contains + one of the regular expressions from the + token_watchlist in our filter config. + + This will match the expression even if it + does not have boundaries before and after + """ + + for expression in Filter.token_watchlist: + if re.search(fr"{expression}", text.lower()): + return True + + return False @staticmethod async def _has_urls(text): diff --git a/bot/constants.py b/bot/constants.py index 597c7c4a7..2dd41b1e5 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -196,13 +196,15 @@ class Filter(metaclass=YAMLGetter): filter_zalgo: bool filter_invites: bool - filter_domain: bool - watch_expressions: bool + filter_domains: bool + watch_words: bool + watch_tokens: bool - domain_blacklist: List[str] - watched_expressions: List[str] guild_invite_whitelist: List[str] vanity_url_whitelist: List[str] + domain_blacklist: List[str] + word_watchlist: List[str] + token_watchlist: List[str] channel_whitelist: List[int] role_whitelist: List[int] diff --git a/config-default.yml b/config-default.yml index 06117ef05..405a50b8a 100644 --- a/config-default.yml +++ b/config-default.yml @@ -82,7 +82,7 @@ guild: announcements: 463658397560995840 champion: 430492892331769857 contributor: 295488872404484098 - devops: 409416496733880320 + devops: &DEVOPS_ROLE 409416496733880320 jammer: 423054537079783434 moderator: &MOD_ROLE 267629731250176001 owner: &OWNER_ROLE 267627879762755584 @@ -93,18 +93,13 @@ guild: filter: # What do we filter? - filter_zalgo: true - filter_invites: true - filter_domains: true - watch_expressions: true + filter_zalgo: true + filter_invites: true + filter_domains: true + watch_words: true + watch_tokens: true # Filter configuration - domain_blacklist: - - pornhub.com - - watched_expressions: - - .*ni[g]*er.* - guild_invite_whitelist: - vywQPxd # Code Monkeys - kWJYurV # Functional Programming @@ -114,6 +109,35 @@ filter: vanity_url_whitelist: - python # Python Discord + domain_blacklist: + - pornhub.com + + word_watchlist: + - goo+k + - ky+s+ + - g[ae]+y+ + - ki+ke+ + - beane?r* + - coo+n + - nig+lets? + - slant-eye + - towe?l-?head+ + - chi*n+k+ + - spick* + - kill* +(?:yo)?urself+ + - jew+s* + - suicide + - rape + - re+tar+d+ + - cunt + + token_watchlist: + - fa+g+s* + - 卐 + - cuck + - nigg+(?:e*r+|a+)s? + - fag+o+t+s* + # Censor doesn't apply to these channel_whitelist: - *ADMINS @@ -128,6 +152,7 @@ filter: - *ADMIN_ROLE - *MOD_ROLE - *OWNER_ROLE + - *DEVOPS_ROLE keys: deploy_bot: !ENV "DEPLOY_BOT_KEY" -- cgit v1.2.3 From a504d864f343229101684e5fda0c70576188efe4 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 28 Jul 2018 20:10:39 +0200 Subject: Still WIP, but almost done. I think I need the clean MRs merged before I continue. --- bot/cogs/filtering.py | 70 +++++++++++++++++++++++++++++++++------------------ config-default.yml | 26 +++++++++---------- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 1da378c08..71b0b9bee 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -4,7 +4,8 @@ import re from discord import Message from discord.ext.commands import Bot -from bot.constants import Channels, Filter +from bot.cogs.modlog import ModLog +from bot.constants import Channels, Filter, Icons log = logging.getLogger(__name__) @@ -14,7 +15,7 @@ INVITE_RE = ( 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")(?:[\/]|slash)" # / or 'slash' r"([a-zA-Z0-9]+)" # the invite code itself ) @@ -25,7 +26,7 @@ ZALGO_RE = r"[\u0300-\u036F\u0489]" class Filtering: """ Filtering out invites, blacklisting domains, - and preventing certain expressions + and warning us of certain regular expressions """ def __init__(self, bot: Bot): @@ -59,6 +60,10 @@ class Filtering: }, } + @property + def modlog(self) -> ModLog: + return self.bot.get_cog("ModLog") + async def on_message(self, msg: Message): """ Whenever a message is received, @@ -67,17 +72,16 @@ class Filtering: accordingly. """ - # Check if the sender has a role that is whitelisted + # Should we filter this message? role_whitelisted = False for role in msg.author.roles: if role.id in Filter.role_whitelist: role_whitelisted = True - # Is the channel whitelisted or is the sender a bot? filter_message = ( - msg.channel.id not in Filter.channel_whitelist - and not role_whitelisted - and not msg.author.bot + msg.channel.id not in Filter.channel_whitelist # Channel not in whitelist + and not role_whitelisted # Role not in whitelist + and not msg.author.bot # Author not a bot ) filter_message = not msg.author.bot and msg.channel.id == Channels.modlog # for testing @@ -86,11 +90,12 @@ class Filtering: if filter_message: for filter_name, _filter in self.filters.items(): - # Is the filter enabled in the config? + # Is this specific filter enabled in the config? if _filter["enabled"]: triggered = await _filter["function"](msg.content) if triggered: + # If a filter is triggered, we should automod it. if _filter["type"] == "filter": log.debug( @@ -106,21 +111,38 @@ class Filtering: # If a watchlist triggers, we should send a mod alert. elif _filter["type"] == "watchlist": - log.debug( - f"The {filter_name} watchlist was triggered " - f"by {msg.author.name} in {msg.channel.name} with " - f"the following message:\n{msg.content}." - ) - - # Replace this with actual mod alerts! - await self.bot.get_channel(msg.channel.id).send( - content=f"The **{filter_name}** watchlist was triggered!" - ) + await self._mod_alert(filter_name, msg) break # We don't want multiple filters to trigger + async def _mod_alert(self, watchlist_name: str, msg: Message): + """ + Send a mod alert into the #mod-alert channel. + + Ping staff so they can take action. + """ + + log.debug( + f"The {filter_name} watchlist was triggered " + f"by {msg.author.name} in {msg.channel.name} with " + f"the following message:\n{msg.content}." + ) + + # Replace this with actual mod alerts! + await self.bot.get_channel(msg.channel.id).send( + content=f"The **{filter_name}** watchlist was triggered!" + ) + + # Send pretty modlog embed to mod-alerts + await self.modlog.send_log_message( + Icons.token_removed, COLOUR_RED, "Entry denied", + message, member.avatar_url_as(static_format="png") + ) + + + @staticmethod - async def _has_watchlist_words(text): + async def _has_watchlist_words(text: str) -> bool: """ Returns True if the text contains one of the regular expressions from the @@ -137,7 +159,7 @@ class Filtering: return False @staticmethod - async def _has_watchlist_tokens(text): + async def _has_watchlist_tokens(text: str) -> bool: """ Returns True if the text contains one of the regular expressions from the @@ -154,7 +176,7 @@ class Filtering: return False @staticmethod - async def _has_urls(text): + async def _has_urls(text: str) -> bool: """ Returns True if the text contains one of the blacklisted URLs from the config file. @@ -170,7 +192,7 @@ class Filtering: return False @staticmethod - async def _has_zalgo(text): + async def _has_zalgo(text: str) -> bool: """ Returns True if the text contains zalgo characters. @@ -180,7 +202,7 @@ class Filtering: return bool(re.search(ZALGO_RE, text)) @staticmethod - async def _has_invites(text): + async def _has_invites(text: str) -> bool: """ Returns True if the text contains an invite which is not on the guild_invite_whitelist in config.yml. diff --git a/config-default.yml b/config-default.yml index 405a50b8a..46b06ef06 100644 --- a/config-default.yml +++ b/config-default.yml @@ -113,29 +113,29 @@ filter: - pornhub.com word_watchlist: - - goo+k + - goo+ks* - ky+s+ - - g[ae]+y+ - - ki+ke+ - - beane?r* - - coo+n - - nig+lets? - - slant-eye - - towe?l-?head+ - - chi*n+k+ - - spick* + - gh?[ae]+y+s* + - ki+ke+s* + - beane?r*s* + - coo+ns* + - nig+lets* + - slant-eyes* + - towe?l-?head+s* + - chi*n+k+s* + - spick*s* - kill* +(?:yo)?urself+ - jew+s* - suicide - rape - - re+tar+d+ - - cunt + - (?:re+)?tar+d+(?:ed)? + - cunts* token_watchlist: - fa+g+s* - 卐 - cuck - - nigg+(?:e*r+|a+)s? + - nigg+(?:e*r+|a+h+?|u+h+)s? - fag+o+t+s* # Censor doesn't apply to these -- cgit v1.2.3 From 457a34f2aa5a309a5a5c44b9c41b9b00230993a5 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 29 Jul 2018 22:59:13 +0200 Subject: WIP commit --- bot/cogs/filtering.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index c3302012d..8460b7ea0 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -115,6 +115,11 @@ class Filtering: break # We don't want multiple filters to trigger + async def _auto_mod(self, filter_name: str, msg: Message): + """ + Removes a message and sends a + """ + async def _mod_alert(self, watchlist_name: str, msg: Message): """ Send a mod alert into the #mod-alert channel. -- cgit v1.2.3 From f3bd9a2b4fcbacea6de7893dd02f1f0eb668f0a0 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 30 Jul 2018 13:28:01 +0200 Subject: WIP --- bot/cogs/filtering.py | 9 +++++---- config-default.yml | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 8460b7ea0..1ef797445 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -61,7 +61,7 @@ class Filtering: } @property - def modlog(self) -> ModLog: + def mod_log(self) -> ModLog: return self.bot.get_cog("ModLog") async def on_message(self, msg: Message): @@ -136,9 +136,10 @@ class Filtering: log.debug(message) # Send pretty modlog embed to mod-alerts - await self.modlog.send_log_message( - Icons.token_removed, Colours.soft_red, "Watchlist triggered!", - message, msg.author.avatar_url_as(static_format="png") + await self.mod_log.send_log_message( + Icons., Colours.soft_red, "Watchlist triggered!", + message, msg.author.avatar_url_as(static_format="png"), + ping_everyone=True ) @staticmethod diff --git a/config-default.yml b/config-default.yml index 5c765a0ab..eaff5d56d 100644 --- a/config-default.yml +++ b/config-default.yml @@ -40,6 +40,8 @@ style: defcon_enabled: "https://cdn.discordapp.com/emojis/470326274213150730.png" defcon_updated: "https://cdn.discordapp.com/emojis/472472638342561793.png" + filtering: "https://cdn.discordapp.com/emojis/472472638594482195.png" + guild_update: "https://cdn.discordapp.com/emojis/469954765141442561.png" hash_blurple: "https://cdn.discordapp.com/emojis/469950142942806017.png" -- cgit v1.2.3 From a0bd77674eabd03ac76260eb440217426b7e28ca Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 30 Jul 2018 14:40:48 +0200 Subject: Case insensitive URL filtering --- bot/cogs/filtering.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index d9cb2bb82..c6da923ae 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -168,11 +168,11 @@ class Filtering: the blacklisted URLs from the config file. """ - if not re.search(URL_RE, text): + if not re.search(URL_RE.lower(), text.lower()): return False for url in Filter.domain_blacklist: - if url in text: + if url.lower() in text.lower(): return True return False -- cgit v1.2.3 From 683a903d5c4124e00cf8febb6df4c89d1ca16595 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 30 Jul 2018 18:33:20 +0200 Subject: Addressing all review comments from gdude --- bot/cogs/filtering.py | 12 +++++++----- config-default.yml | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index c6da923ae..9b9a9999b 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -139,7 +139,7 @@ class Filtering: """ for expression in Filter.word_watchlist: - if re.search(fr"\b{expression}\b", text.lower()): + if re.search(fr"\b{expression}\b", text, re.IGNORECASE): return True return False @@ -156,7 +156,7 @@ class Filtering: """ for expression in Filter.token_watchlist: - if re.search(fr"{expression}", text.lower()): + if re.search(fr"{expression}", text, re.IGNORECASE): return True return False @@ -168,11 +168,13 @@ class Filtering: the blacklisted URLs from the config file. """ - if not re.search(URL_RE.lower(), text.lower()): + if not re.search(URL_RE, text, re.IGNORECASE): return False + text = text.lower() + for url in Filter.domain_blacklist: - if url.lower() in text.lower(): + if url.lower() in text: return True return False @@ -204,7 +206,7 @@ class Filtering: # discord\.gg/gdudes-pony-farm text = text.replace("\\", "") - invites = re.findall(INVITE_RE, text) + invites = re.findall(INVITE_RE, text, re.IGNORECASE) for invite in invites: filter_invite = ( diff --git a/config-default.yml b/config-default.yml index 2beb4edbf..17c438a8f 100644 --- a/config-default.yml +++ b/config-default.yml @@ -132,6 +132,7 @@ filter: domain_blacklist: - pornhub.com + - liveleak.com word_watchlist: - goo+ks* @@ -155,6 +156,7 @@ filter: token_watchlist: - fa+g+s* - 卐 + - 卍 - cuck - nigg+(?:e*r+|a+h+?|u+h+)s? - fag+o+t+s* -- cgit v1.2.3 From 5703372a6715538c564ecd2cc4dc32c534bd7d33 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 30 Jul 2018 22:31:10 +0200 Subject: Gotta filter edits too, to combat sneaky racists --- bot/cogs/filtering.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 9b9a9999b..92fa13f5b 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -68,8 +68,14 @@ class Filtering: return self.bot.get_cog("ModLog") async def on_message(self, msg: Message): + await self._filter_message(msg) + + async def on_message_edit(self, _: Message, after: Message): + await self._filter_message(after) + + async def _filter_message(self, msg: Message): """ - Whenever a message is received, + Whenever a message is sent or edited, run it through our filters to see if it violates any of our rules, and then respond accordingly. -- cgit v1.2.3 From e27ff90075d59f9f1797501551159027ff353ba6 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 30 Jul 2018 22:43:54 +0200 Subject: Sanity check for msg.author. Only Member has roles. --- bot/cogs/filtering.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 92fa13f5b..89735e57c 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -1,7 +1,7 @@ import logging import re -from discord import Colour, Message +from discord import Colour, Member, Message from discord.ext.commands import Bot from bot.cogs.modlog import ModLog @@ -83,9 +83,11 @@ class Filtering: # Should we filter this message? role_whitelisted = False - for role in msg.author.roles: - if role.id in Filter.role_whitelist: - role_whitelisted = True + + if type(msg.author) is Member: # Only Member has roles, not User. + for role in msg.author.roles: + if role.id in Filter.role_whitelist: + role_whitelisted = True filter_message = ( msg.channel.id not in Filter.channel_whitelist # Channel not in whitelist -- cgit v1.2.3