From 460b4bb3f2042d330b34601d82b9e84f82392103 Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Tue, 23 Apr 2019 16:01:20 +0200 Subject: [PATCH] [Mod] Allow admins to choose amount of repeats for "deleterepeats" (#2437) * feat(mod): configurable amount of repeats for "deleterepeats" resolves #2267 * fix(mod): check user input instead of changing it if it's invalid * fix(mod): only purge cache when argument is valid * perf(mod): fetch repeats from config only when guild not in cache --- redbot/cogs/mod/__init__.py | 6 +++-- redbot/cogs/mod/events.py | 28 +++++++++++++------- redbot/cogs/mod/mod.py | 28 +++++++++++++++++--- redbot/cogs/mod/settings.py | 53 ++++++++++++++++++++++++++++++------- 4 files changed, 90 insertions(+), 25 deletions(-) diff --git a/redbot/cogs/mod/__init__.py b/redbot/cogs/mod/__init__.py index 3cf600db7..425b2fdf2 100644 --- a/redbot/cogs/mod/__init__.py +++ b/redbot/cogs/mod/__init__.py @@ -2,5 +2,7 @@ from redbot.core.bot import Red from .mod import Mod -def setup(bot: Red): - bot.add_cog(Mod(bot)) +async def setup(bot: Red): + cog = Mod(bot) + await cog.initialize() + bot.add_cog(cog) diff --git a/redbot/cogs/mod/events.py b/redbot/cogs/mod/events.py index ca42a8019..9c7cf88fa 100644 --- a/redbot/cogs/mod/events.py +++ b/redbot/cogs/mod/events.py @@ -1,4 +1,5 @@ from datetime import datetime +from collections import defaultdict, deque import discord from redbot.core import i18n, modlog @@ -19,17 +20,24 @@ class Events(MixinMeta): guild = message.guild author = message.author - if await self.settings.guild(guild).delete_repeats(): - if not message.content: + guild_cache = self.cache.get(guild.id, None) + if guild_cache is None: + repeats = await self.settings.guild(guild).delete_repeats() + if repeats == -1: return False - self.cache[author].append(message) - msgs = self.cache[author] - if len(msgs) == 3 and msgs[0].content == msgs[1].content == msgs[2].content: - try: - await message.delete() - return True - except discord.HTTPException: - pass + guild_cache = self.cache[guild.id] = defaultdict(lambda: deque(maxlen=repeats)) + + if not message.content: + return False + + guild_cache[author].append(message.content) + msgs = guild_cache[author] + if len(msgs) == msgs.maxlen and len(set(msgs)) == 1: + try: + await message.delete() + return True + except discord.HTTPException: + pass return False async def check_mention_spam(self, message): diff --git a/redbot/cogs/mod/mod.py b/redbot/cogs/mod/mod.py index 7c4c639b7..96cb94952 100644 --- a/redbot/cogs/mod/mod.py +++ b/redbot/cogs/mod/mod.py @@ -1,4 +1,4 @@ -from collections import deque, defaultdict +from collections import defaultdict from typing import List, Tuple import discord @@ -15,14 +15,18 @@ from .settings import ModSettings _ = T_ = Translator("Mod", __file__) +__version__ = "1.0.0" + @cog_i18n(_) class Mod(ModSettings, Events, KickBanMixin, MoveToCore, MuteMixin, ModInfo, commands.Cog): """Moderation tools.""" + default_global_settings = {"version": ""} + default_guild_settings = { "ban_mention_spam": False, - "delete_repeats": False, + "delete_repeats": -1, "ignored": False, "respect_hierarchy": True, "delete_delay": -1, @@ -41,21 +45,39 @@ class Mod(ModSettings, Events, KickBanMixin, MoveToCore, MuteMixin, ModInfo, com self.bot = bot self.settings = Config.get_conf(self, 4961522000, force_registration=True) + self.settings.register_global(**self.default_global_settings) self.settings.register_guild(**self.default_guild_settings) self.settings.register_channel(**self.default_channel_settings) self.settings.register_member(**self.default_member_settings) self.settings.register_user(**self.default_user_settings) self.ban_queue: List[Tuple[int, int]] = [] self.unban_queue: List[Tuple[int, int]] = [] - self.cache: dict = defaultdict(lambda: deque(maxlen=3)) + self.cache: dict = {} self.registration_task = self.bot.loop.create_task(self._casetype_registration()) self.tban_expiry_task = self.bot.loop.create_task(self.check_tempban_expirations()) self.last_case: dict = defaultdict(dict) + async def initialize(self): + await self._maybe_update_config() + def __unload(self): self.registration_task.cancel() self.tban_expiry_task.cancel() + async def _maybe_update_config(self): + """Maybe update `delete_delay` value set by Config prior to Mod 1.0.0.""" + if await self.settings.version(): + return + guild_dict = await self.settings.all_guilds() + for guild_id, info in guild_dict.items(): + delete_repeats = info.get("delete_repeats", False) + if delete_repeats: + val = 3 + else: + val = -1 + await self.settings.guild(discord.Object(id=guild_id)).delete_repeats.set(val) + await self.settings.version.set(__version__) + @staticmethod async def _casetype_registration(): try: diff --git a/redbot/cogs/mod/settings.py b/redbot/cogs/mod/settings.py index 62d3293ae..e7923c57e 100644 --- a/redbot/cogs/mod/settings.py +++ b/redbot/cogs/mod/settings.py @@ -1,3 +1,5 @@ +from collections import defaultdict, deque + from redbot.core import commands, i18n, checks from redbot.core.utils.chat_formatting import box @@ -25,8 +27,10 @@ class ModSettings(MixinMeta): delete_delay = await self.settings.guild(guild).delete_delay() reinvite_on_unban = await self.settings.guild(guild).reinvite_on_unban() msg = "" - msg += _("Delete repeats: {yes_or_no}\n").format( - yes_or_no=_("Yes") if delete_repeats else _("No") + msg += _("Delete repeats: {num_repeats}\n").format( + num_repeats=_("after {num} repeats").format(num=delete_repeats) + if delete_repeats != -1 + else _("No") ) msg += _("Ban mention spam: {num_mentions}\n").format( num_mentions=_("{num} mentions").format(num=ban_mention_spam) @@ -101,16 +105,45 @@ class ModSettings(MixinMeta): @modset.command() @commands.guild_only() - async def deleterepeats(self, ctx: commands.Context): - """Enable auto-deletion of repeated messages.""" + async def deleterepeats(self, ctx: commands.Context, repeats: int = None): + """Enable auto-deletion of repeated messages. + + Must be between 2 and 20. + + Set to -1 to disable this feature. + """ guild = ctx.guild - cur_setting = await self.settings.guild(guild).delete_repeats() - if not cur_setting: - await self.settings.guild(guild).delete_repeats.set(True) - await ctx.send(_("Messages repeated up to 3 times will be deleted.")) + if repeats is not None: + if repeats == -1: + await self.settings.guild(guild).delete_repeats.set(repeats) + self.cache.pop(guild.id, None) # remove cache with old repeat limits + await ctx.send(_("Repeated messages will be ignored.")) + elif 2 <= repeats <= 20: + await self.settings.guild(guild).delete_repeats.set(repeats) + # purge and update cache to new repeat limits + self.cache[guild.id] = defaultdict(lambda: deque(maxlen=repeats)) + await ctx.send( + _("Messages repeated up to {num} times will be deleted.").format(num=repeats) + ) + else: + await ctx.send( + _( + "Number of repeats must be between 2 and 20" + " or equal to -1 if you want to disable this feature!" + ) + ) else: - await self.settings.guild(guild).delete_repeats.set(False) - await ctx.send(_("Repeated messages will be ignored.")) + repeats = await self.settings.guild(guild).delete_repeats() + if repeats != -1: + await ctx.send( + _( + "Bot will delete repeated messages after" + " {num} repeats. Set this value to -1 to" + " ignore repeated messages" + ).format(num=repeats) + ) + else: + await ctx.send(_("Repeated messages will be ignored.")) @modset.command() @commands.guild_only()