diff --git a/redbot/cogs/filter/filter.py b/redbot/cogs/filter/filter.py index 5fa4b0bbe..a95f7aaaa 100644 --- a/redbot/cogs/filter/filter.py +++ b/redbot/cogs/filter/filter.py @@ -1,11 +1,13 @@ import discord -from typing import Union +import re +from typing import Union, Set from redbot.core import checks, Config, modlog, commands from redbot.core.bot import Red from redbot.core.i18n import Translator, cog_i18n from redbot.core.utils.chat_formatting import pagify +RE_WORD_SPLIT = re.compile(r"[^\w]") _ = Translator("Filter", __file__) @@ -317,17 +319,38 @@ class Filter(commands.Cog): return removed + async def filter_hits( + self, text: str, server_or_channel: Union[discord.Guild, discord.TextChannel] + ) -> Set[str]: + if isinstance(server_or_channel, discord.Guild): + word_list = set(await self.settings.guild(server_or_channel).filter()) + elif isinstance(server_or_channel, discord.TextChannel): + word_list = set( + await self.settings.guild(server_or_channel.guild).filter() + + await self.settings.channel(server_or_channel).filter() + ) + else: + raise TypeError("%r should be Guild or TextChannel" % server_or_channel) + + content = text.lower() + msg_words = set(RE_WORD_SPLIT.split(content)) + + filtered_phrases = {x for x in word_list if len(RE_WORD_SPLIT.split(x)) > 1} + filtered_words = word_list - filtered_phrases + + hits = {p for p in filtered_phrases if p in content} + hits |= filtered_words & msg_words + return hits + async def check_filter(self, message: discord.Message): server = message.guild author = message.author - word_list = set( - await self.settings.guild(server).filter() - + await self.settings.channel(message.channel).filter() - ) + filter_count = await self.settings.guild(server).filterban_count() filter_time = await self.settings.guild(server).filterban_time() user_count = await self.settings.member(author).filter_count() next_reset_time = await self.settings.member(author).next_reset_time() + if filter_count > 0 and filter_time > 0: if message.created_at.timestamp() >= next_reset_time: next_reset_time = message.created_at.timestamp() + filter_time @@ -336,36 +359,36 @@ class Filter(commands.Cog): user_count = 0 await self.settings.member(author).filter_count.set(user_count) - if word_list: - for w in word_list: - if w in message.content.lower(): - try: - await message.delete() - except discord.HTTPException: - pass - else: - if filter_count > 0 and filter_time > 0: - user_count += 1 - await self.settings.member(author).filter_count.set(user_count) - if ( - user_count >= filter_count - and message.created_at.timestamp() < next_reset_time - ): - reason = _("Autoban (too many filtered messages.)") - try: - await server.ban(author, reason=reason) - except discord.HTTPException: - pass - else: - await modlog.create_case( - self.bot, - server, - message.created_at, - "filterban", - author, - server.me, - reason, - ) + hits = await self.filter_hits(message.content, message.channel) + + if hits: + try: + await message.delete() + except discord.HTTPException: + pass + else: + if filter_count > 0 and filter_time > 0: + user_count += 1 + await self.settings.member(author).filter_count.set(user_count) + if ( + user_count >= filter_count + and message.created_at.timestamp() < next_reset_time + ): + reason = _("Autoban (too many filtered messages.)") + try: + await server.ban(author, reason=reason) + except discord.HTTPException: + pass + else: + await modlog.create_case( + self.bot, + server, + message.created_at, + "filterban", + author, + server.me, + reason, + ) async def on_message(self, message: discord.Message): if isinstance(message.channel, discord.abc.PrivateChannel):