diff --git a/cogs/commands/misc/canister.py b/cogs/commands/misc/canister.py index da78962..e8b7388 100644 --- a/cogs/commands/misc/canister.py +++ b/cogs/commands/misc/canister.py @@ -7,6 +7,7 @@ from utils import GIRContext, canister_search_package, cfg, transform_context from utils.fetchers import canister_fetch_repos from utils.framework import gatekeeper, whisper_in_general, find_triggered_filters, find_triggered_raid_phrases +from utils.framework.filter import has_only_silent_filtered_words from utils.views import TweakDropdown, default_repos, repo_autocomplete @@ -22,7 +23,6 @@ async def on_message(self, message): author = message.guild.get_member(message.author.id) if author is None: return - if not gatekeeper.has(message.guild, author, 5) and message.channel.id == cfg.channels.general: return @@ -31,8 +31,10 @@ async def on_message(self, message): if not pattern.match(message.content): return - if await find_triggered_filters(message.content, message.author) or await find_triggered_raid_phrases(message.content, message.author): - return + if filter_words := await find_triggered_filters(message.content, message.author) or await find_triggered_raid_phrases(message.content, message.author): + # if any of the triggered filtered words are not silently filtered, don't show results + if not has_only_silent_filtered_words(filter_words): + return matches = pattern.findall(message.content) if not matches: diff --git a/cogs/commands/misc/memes.py b/cogs/commands/misc/memes.py index f3d00d1..7726721 100644 --- a/cogs/commands/misc/memes.py +++ b/cogs/commands/misc/memes.py @@ -15,6 +15,7 @@ find_triggered_filters, find_triggered_raid_phrases, gatekeeper, memed_and_up, mempro_and_up, mod_and_up, whisper) +from utils.framework.filter import has_only_silent_filtered_words from utils.views import GenericDescriptionModal, Menu, memes_autocomplete @@ -463,8 +464,9 @@ async def aitext(self, ctx: GIRContext, prompt: str): data = await resp.json() text = data.get("choices")[0].get("text") text = discord.utils.escape_markdown(text) - if await find_triggered_filters(text, ctx.author) or await find_triggered_raid_phrases(text, ctx.author): - text = "A filter was triggered by this response. Please try a different prompt." + if filter_words := await find_triggered_filters(text, ctx.author) or await find_triggered_raid_phrases(text, ctx.author): + if not has_only_silent_filtered_words(filter_words): + text = "A filter was triggered by this response. Please try a different prompt." embed = discord.Embed(color=discord.Color.random()) prompt_formatted = discord.utils.escape_markdown(prompt) diff --git a/cogs/commands/mod/filter.py b/cogs/commands/mod/filter.py index 7e60b7c..1dea3af 100644 --- a/cogs/commands/mod/filter.py +++ b/cogs/commands/mod/filter.py @@ -1,3 +1,4 @@ +from typing import List import discord from discord import app_commands from data.model import FilterWord @@ -30,6 +31,12 @@ def format_filter_page(_, entries, current_page, all_pages): embed = discord.Embed( title=f'Filtered words', color=discord.Color.blurple()) for word in entries: + if word.silent_filter: + embed.add_field( + name=word.word, value=f"馃か silent filtered" + ) + continue + notify_flag = "" piracy_flag = "" flags_check = "" @@ -107,6 +114,26 @@ async def _list(self, ctx: GIRContext): menu = Menu(ctx, filters, per_page=12, page_formatter=format_filter_page, whisper=False) await menu.start() + + @mod_and_up() + @app_commands.guilds(cfg.guild_id) + @app_commands.command(description="Silently filter a word (ping mods without removing message)") + @app_commands.describe(word="The word to mark as silently filtered") + @app_commands.autocomplete(word=filterwords_autocomplete) + @transform_context + async def silent_filter(self, ctx: GIRContext, word: str): + word = word.lower() + + words: List[FilterWord] = await guild_service.get_filtered_words() + words = list(filter(lambda w: w.word.lower() == word.lower(), words)) + + if len(words) > 0: + words[0].silent_filter = not words[0].silent_filter + await guild_service.update_filtered_word(words[0]) + + await ctx.send_success("Marked as a silently filtered word!" if words[0].silent_filter else "Removed as a silently filtered word!") + else: + await ctx.send_warning("You must filter that word before it can be marked as silently filtered.", delete_after=5) @mod_and_up() @app_commands.guilds(cfg.guild_id) diff --git a/cogs/monitors/misc/songs.py b/cogs/monitors/misc/songs.py index d6187b1..328cd33 100644 --- a/cogs/monitors/misc/songs.py +++ b/cogs/monitors/misc/songs.py @@ -10,6 +10,7 @@ from utils import cfg from utils.framework import find_triggered_filters, gatekeeper +from utils.framework.filter import has_only_silent_filtered_words from utils.logging import logger from datetime import timezone @@ -111,7 +112,7 @@ async def generate_view(self, message: discord.Message, link: str): triggered_words = await find_triggered_filters( title, message.author) - if triggered_words: + if triggered_words and not has_only_silent_filtered_words(triggered_words): title = "<:fr:959135064657109012>" view = discord.ui.View() diff --git a/cogs/monitors/mod/filter.py b/cogs/monitors/mod/filter.py index db78da7..6dd3d67 100644 --- a/cogs/monitors/mod/filter.py +++ b/cogs/monitors/mod/filter.py @@ -10,6 +10,7 @@ from discord.ext import commands from utils import cfg, logger, scam_cache from utils.framework import gatekeeper, find_triggered_filters +from utils.framework.filter import has_only_silent_filtered_words from utils.mod import mute from utils.views import manual_report, report @@ -102,6 +103,9 @@ async def nick_filter(self, member): if not triggered_words: return + if has_only_silent_filtered_words(triggered_words): + return + await member.edit(nick="change name pls") embed = discord.Embed(title="Nickname changed", color=discord.Color.orange()) @@ -117,8 +121,6 @@ async def bad_word_filter(self, message) -> bool: if not triggered_words: return - dev_role = message.guild.get_role(cfg.roles.developer) - triggered = False for word in triggered_words: if word.piracy: @@ -126,12 +128,19 @@ async def bad_word_filter(self, message) -> bool: if message.channel.id == cfg.roles.developer in message.author.roles: continue - if word.notify: + if word.silent_filter: + # don't delete the word if it's silently filtered, + # just notify the mods + await report(self.bot, message, word.word) + return + elif word.notify: await self.delete(message) await self.ratelimit(message) await self.do_filter_notify(message, word) + if not word.piracy: await report(self.bot, message, word.word) + return triggered = True diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..38e6bdb --- /dev/null +++ b/config.example.json @@ -0,0 +1,33 @@ +{ + "channels": { + "applenews": 123, + "booster_emoji": 123, + "emoji_logs": 123, + "private_logs": 123, + "public_logs": 123, + "bot_commands": 123, + "jailbreak": 123, + "general": 123, + "development": 123, + "genius_bar": 123, + "reports": 123, + "rules": 123, + "common_issues": 123, + "sub_news": 123 + }, + "roles": { + "administrator": 123, + "moderator": 123, + "sub_mod": 123, + "genius": 123, + "developer": 123, + "birthday": 123, + "member_ultra": 123, + "member_one": 123, + "member_edition": 123, + "member_pro": 123, + "member_plus": 123, + "sub_news": 123, + "aaron_role": 123 + } +} diff --git a/data/model/filterword.py b/data/model/filterword.py index abb8488..e45af07 100644 --- a/data/model/filterword.py +++ b/data/model/filterword.py @@ -1,8 +1,8 @@ import mongoengine class FilterWord(mongoengine.EmbeddedDocument): - # _id = mongoengine.ObjectIdField(required=True, default=mongoengine.ObjectId., unique=True, primary_key=True) notify = mongoengine.BooleanField(required=True) + silent_filter = mongoengine.BooleanField(default=False) bypass = mongoengine.IntField(required=True) word = mongoengine.StringField(required=True) false_positive = mongoengine.BooleanField(default=False) diff --git a/utils/framework/filter.py b/utils/framework/filter.py index d646fc5..7ed34b9 100644 --- a/utils/framework/filter.py +++ b/utils/framework/filter.py @@ -49,6 +49,13 @@ async def find_triggered_filters(input, member: discord.Member) -> List[FilterWo words_found.append(word) return words_found +def has_only_silent_filtered_words(triggered_filter_words: List[FilterWord]): + """ + We don't want to trigger the filter if the words are silently filtered + return True if all triggered filtered words are silently filtered + """ + return all(filter_word.silent_filter for filter_word in triggered_filter_words) + async def find_triggered_raid_phrases(input, member): symbols = (u"邪斜胁谐写械褢卸蟹懈泄泻谢屑薪芯锌褉褋褌褍褎褏褑褔褕褖褗褘褜褝褞褟袗袘袙袚袛袝衼袞袟袠袡袣袥袦袧袨袩袪小孝校肖啸笑效楔些歇蝎鞋协挟携",