diff --git a/changelog.d/2897.breaking.1.rst b/changelog.d/2897.breaking.1.rst new file mode 100644 index 000000000..249871d7b --- /dev/null +++ b/changelog.d/2897.breaking.1.rst @@ -0,0 +1 @@ +Modlog casetypes no longer have an attribute for auditlog action type. \ No newline at end of file diff --git a/changelog.d/2897.bugfix.1.rst b/changelog.d/2897.bugfix.1.rst new file mode 100644 index 000000000..8bc2bbeb7 --- /dev/null +++ b/changelog.d/2897.bugfix.1.rst @@ -0,0 +1 @@ +Modlog entries now show up properly without the mod cog loaded \ No newline at end of file diff --git a/changelog.d/2897.enhance.2.rst b/changelog.d/2897.enhance.2.rst new file mode 100644 index 000000000..3a48af0f0 --- /dev/null +++ b/changelog.d/2897.enhance.2.rst @@ -0,0 +1 @@ +Modlog no longer generates cases without being told to for actions the bot did. \ No newline at end of file diff --git a/changelog.d/2897.enhance.3.rst b/changelog.d/2897.enhance.3.rst new file mode 100644 index 000000000..8e69e1cfb --- /dev/null +++ b/changelog.d/2897.enhance.3.rst @@ -0,0 +1 @@ +Some generic modlog casetypes are now pre-registered for cog creator use \ No newline at end of file diff --git a/changelog.d/mod/2897.misc.rst b/changelog.d/mod/2897.misc.rst new file mode 100644 index 000000000..47cbdff5e --- /dev/null +++ b/changelog.d/mod/2897.misc.rst @@ -0,0 +1 @@ +Modlog case registration and modlog event handling was moved to the core bot \ No newline at end of file diff --git a/docs/framework_modlog.rst b/docs/framework_modlog.rst index a9f66d817..18a9cf453 100644 --- a/docs/framework_modlog.rst +++ b/docs/framework_modlog.rst @@ -57,9 +57,6 @@ it from your setup function: "default_setting": True, "image": "\N{HAMMER}", "case_str": "Ban", - # audit_type should be omitted if the action doesn't show - # up in the audit log. - "audit_type": "ban", } try: await modlog.register_casetype(**ban_case) @@ -73,14 +70,12 @@ it from your setup function: "default_setting": True, "image": "\N{BUST IN SILHOUETTE}\N{HAMMER}", "case_str": "Hackban", - "audit_type": "ban", }, { "name": "kick", "default_setting": True, "image": "\N{WOMANS BOOTS}", "case_str": "Kick", - "audit_type": "kick" } ] await modlog.register_casetypes(new_types) diff --git a/redbot/__main__.py b/redbot/__main__.py index 6f7a6935d..38338b0ca 100644 --- a/redbot/__main__.py +++ b/redbot/__main__.py @@ -121,7 +121,7 @@ def main(): if cli_flags.dev: red.add_cog(Dev()) # noinspection PyProtectedMember - loop.run_until_complete(modlog._init()) + loop.run_until_complete(modlog._init(red)) # noinspection PyProtectedMember bank._init() diff --git a/redbot/cogs/filter/filter.py b/redbot/cogs/filter/filter.py index e81494234..40be8cd1e 100644 --- a/redbot/cogs/filter/filter.py +++ b/redbot/cogs/filter/filter.py @@ -40,7 +40,7 @@ class Filter(commands.Cog): async def register_filterban(): try: await modlog.register_casetype( - "filterban", False, ":filing_cabinet: :hammer:", "Filter ban", "ban" + "filterban", False, ":filing_cabinet: :hammer:", "Filter ban" ) except RuntimeError: pass diff --git a/redbot/cogs/mod/abc.py b/redbot/cogs/mod/abc.py index 9f00dffea..c81b95693 100644 --- a/redbot/cogs/mod/abc.py +++ b/redbot/cogs/mod/abc.py @@ -17,8 +17,6 @@ class MixinMeta(ABC): self.settings: Config self.bot: Red self.cache: dict - self.ban_queue: List[Tuple[int, int]] - self.unban_queue: List[Tuple[int, int]] @staticmethod @abstractmethod @@ -26,15 +24,3 @@ class MixinMeta(ABC): ctx: commands.Context, user_voice_state: Optional[discord.VoiceState], **perms: bool ) -> bool: raise NotImplementedError() - - @classmethod - @abstractmethod - async def get_audit_entry_info( - cls, guild: discord.Guild, action: discord.AuditLogAction, target - ): - raise NotImplementedError() - - @staticmethod - @abstractmethod - async def get_audit_log_entry(guild: discord.Guild, action: discord.AuditLogAction, target): - raise NotImplementedError() diff --git a/redbot/cogs/mod/events.py b/redbot/cogs/mod/events.py index f39362388..b83dddebb 100644 --- a/redbot/cogs/mod/events.py +++ b/redbot/cogs/mod/events.py @@ -94,77 +94,6 @@ class Events(MixinMeta): if not deleted: await self.check_mention_spam(message) - @commands.Cog.listener() - async def on_member_ban(self, guild: discord.Guild, member: discord.Member): - if (guild.id, member.id) in self.ban_queue: - self.ban_queue.remove((guild.id, member.id)) - return - try: - await modlog.get_modlog_channel(guild) - except RuntimeError: - return # No modlog channel so no point in continuing - mod, reason, date = await self.get_audit_entry_info( - guild, discord.AuditLogAction.ban, member - ) - if date is None: - date = datetime.now() - try: - await modlog.create_case( - self.bot, guild, date, "ban", member, mod, reason if reason else None - ) - except RuntimeError as e: - print(e) - - @commands.Cog.listener() - async def on_member_unban(self, guild: discord.Guild, user: discord.User): - if (guild.id, user.id) in self.unban_queue: - self.unban_queue.remove((guild.id, user.id)) - return - try: - await modlog.get_modlog_channel(guild) - except RuntimeError: - return # No modlog channel so no point in continuing - mod, reason, date = await self.get_audit_entry_info( - guild, discord.AuditLogAction.unban, user - ) - if date is None: - date = datetime.now() - try: - await modlog.create_case(self.bot, guild, date, "unban", user, mod, reason) - except RuntimeError as e: - print(e) - - @commands.Cog.listener() - async def on_modlog_case_create(self, case: modlog.Case): - """ - An event for modlog case creation - """ - try: - mod_channel = await modlog.get_modlog_channel(case.guild) - except RuntimeError: - return - use_embeds = await case.bot.embed_requested(mod_channel, case.guild.me) - case_content = await case.message_content(use_embeds) - if use_embeds: - msg = await mod_channel.send(embed=case_content) - else: - msg = await mod_channel.send(case_content) - await case.edit({"message": msg}) - - @commands.Cog.listener() - async def on_modlog_case_edit(self, case: modlog.Case): - """ - Event for modlog case edits - """ - if not case.message: - return - use_embed = await case.bot.embed_requested(case.message.channel, case.guild.me) - case_content = await case.message_content(use_embed) - if use_embed: - await case.message.edit(embed=case_content) - else: - await case.message.edit(content=case_content) - @commands.Cog.listener() async def on_member_update(self, before: discord.Member, after: discord.Member): if before.name != after.name: diff --git a/redbot/cogs/mod/kickban.py b/redbot/cogs/mod/kickban.py index f408f5c47..31e64e09e 100644 --- a/redbot/cogs/mod/kickban.py +++ b/redbot/cogs/mod/kickban.py @@ -85,7 +85,6 @@ class KickBanMixin(MixinMeta): audit_reason = get_audit_reason(author, reason) queue_entry = (guild.id, user.id) - self.ban_queue.append(queue_entry) try: await guild.ban(user, reason=audit_reason, delete_message_days=days) log.info( @@ -94,10 +93,8 @@ class KickBanMixin(MixinMeta): ) ) except discord.Forbidden: - self.ban_queue.remove(queue_entry) return _("I'm not allowed to do that.") except Exception as e: - self.ban_queue.remove(queue_entry) return e # TODO: impproper return type? Is this intended to be re-raised? if create_modlog_case: @@ -134,15 +131,13 @@ class KickBanMixin(MixinMeta): if now > unban_time: # Time to unban the user user = await self.bot.fetch_user(uid) queue_entry = (guild.id, user.id) - self.unban_queue.append(queue_entry) try: await guild.unban(user, reason=_("Tempban finished")) guild_tempbans.remove(uid) except discord.Forbidden: - self.unban_queue.remove(queue_entry) log.info("Failed to unban member due to permissions") - except discord.HTTPException: - self.unban_queue.remove(queue_entry) + except discord.HTTPException as e: + log.info(f"Failed to unban member: error code: {e.code}") await asyncio.sleep(60) @commands.command() @@ -319,16 +314,13 @@ class KickBanMixin(MixinMeta): user = discord.Object(id=user_id) audit_reason = get_audit_reason(author, reason) queue_entry = (guild.id, user_id) - self.ban_queue.append(queue_entry) try: await guild.ban(user, reason=audit_reason, delete_message_days=days) log.info("{}({}) hackbanned {}".format(author.name, author.id, user_id)) except discord.NotFound: - self.ban_queue.remove(queue_entry) errors[user_id] = _("User {user_id} does not exist.").format(user_id=user_id) continue except discord.Forbidden: - self.ban_queue.remove(queue_entry) errors[user_id] = _("Could not ban {user_id}: missing permissions.").format( user_id=user_id ) @@ -389,7 +381,6 @@ class KickBanMixin(MixinMeta): invite_link=invite, ) ) - self.ban_queue.append(queue_entry) try: await guild.ban(user) except discord.Forbidden: @@ -455,24 +446,19 @@ class KickBanMixin(MixinMeta): ) except discord.HTTPException: msg = None - self.ban_queue.append(queue_entry) try: await guild.ban(user, reason=audit_reason, delete_message_days=1) except discord.errors.Forbidden: - self.ban_queue.remove(queue_entry) await ctx.send(_("My role is not high enough to softban that user.")) if msg is not None: await msg.delete() return except discord.HTTPException as e: - self.ban_queue.remove(queue_entry) print(e) return - self.unban_queue.append(queue_entry) try: await guild.unban(user) except discord.HTTPException as e: - self.unban_queue.remove(queue_entry) print(e) return else: @@ -571,11 +557,9 @@ class KickBanMixin(MixinMeta): await ctx.send(_("It seems that user isn't banned!")) return queue_entry = (guild.id, user.id) - self.unban_queue.append(queue_entry) try: await guild.unban(user, reason=audit_reason) except discord.HTTPException: - self.unban_queue.remove(queue_entry) await ctx.send(_("Something went wrong while attempting to unban that user")) return else: diff --git a/redbot/cogs/mod/mod.py b/redbot/cogs/mod/mod.py index a303aab50..a004251f3 100644 --- a/redbot/cogs/mod/mod.py +++ b/redbot/cogs/mod/mod.py @@ -71,10 +71,7 @@ class Mod( 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 = {} - 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) @@ -99,13 +96,6 @@ class Mod( 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: - await modlog.register_casetypes(CASETYPES) - except RuntimeError: - pass - # TODO: Move this to core. # This would be in .movetocore , but the double-under name here makes that more trouble async def bot_check(self, ctx): @@ -126,59 +116,3 @@ class Mod( guild_ignored = await self.settings.guild(ctx.guild).ignored() chann_ignored = await self.settings.channel(ctx.channel).ignored() return not (guild_ignored or chann_ignored and not perms.manage_channels) - - @classmethod - async def get_audit_entry_info( - cls, guild: discord.Guild, action: discord.AuditLogAction, target - ): - """Get info about an audit log entry. - - Parameters - ---------- - guild : discord.Guild - Same as ``guild`` in `get_audit_log_entry`. - action : int - Same as ``action`` in `get_audit_log_entry`. - target : `discord.User` or `discord.Member` - Same as ``target`` in `get_audit_log_entry`. - - Returns - ------- - tuple - A tuple in the form``(mod: discord.Member, reason: str, - date_created: datetime.datetime)``. Returns ``(None, None, None)`` - if the audit log entry could not be found. - """ - try: - entry = await cls.get_audit_log_entry(guild, action=action, target=target) - except discord.HTTPException: - entry = None - if entry is None: - return None, None, None - return entry.user, entry.reason, entry.created_at - - @staticmethod - async def get_audit_log_entry(guild: discord.Guild, action: discord.AuditLogAction, target): - """Get an audit log entry. - - Any exceptions encountered when looking through the audit log will be - propogated out of this function. - - Parameters - ---------- - guild : discord.Guild - The guild for the audit log. - action : int - The audit log action (see `discord.AuditLogAction`). - target : `discord.Member` or `discord.User` - The target of the audit log action. - - Returns - ------- - discord.AuditLogEntry - The audit log entry. Returns ``None`` if not found. - - """ - async for entry in guild.audit_logs(action=action): - if entry.target == target: - return entry diff --git a/redbot/core/generic_casetypes.py b/redbot/core/generic_casetypes.py new file mode 100644 index 000000000..22e54fa41 --- /dev/null +++ b/redbot/core/generic_casetypes.py @@ -0,0 +1,111 @@ +""" +Contains generic mod action casetypes for use in Red and 3rd party cogs. +These do not need to be registered to the modlog, as it is done for you. +""" + +ban = {"name": "ban", "default_setting": True, "image": "\N{HAMMER}", "case_str": "Ban"} + +kick = {"name": "kick", "default_setting": True, "image": "\N{WOMANS BOOTS}", "case_str": "Kick"} + +hackban = { + "name": "hackban", + "default_setting": True, + "image": "\N{BUST IN SILHOUETTE}\N{HAMMER}", + "case_str": "Hackban", +} + +tempban = { + "name": "tempban", + "default_setting": True, + "image": "\N{ALARM CLOCK}\N{HAMMER}", + "case_str": "Tempban", +} + +softban = { + "name": "softban", + "default_setting": True, + "image": "\N{DASH SYMBOL}\N{HAMMER}", + "case_str": "Softban", +} +unban = { + "name": "unban", + "default_setting": True, + "image": "\N{DOVE OF PEACE}", + "case_str": "Unban", +} +voiceban = { + "name": "voiceban", + "default_setting": True, + "image": "\N{SPEAKER WITH CANCELLATION STROKE}", + "case_str": "Voice Ban", +} +voiceunban = { + "name": "voiceunban", + "default_setting": True, + "image": "\N{SPEAKER}", + "case_str": "Voice Unban", +} +voicemute = { + "name": "vmute", + "default_setting": False, + "image": "\N{SPEAKER WITH CANCELLATION STROKE}", + "case_str": "Voice Mute", +} + +channelmute = { + "name": "cmute", + "default_setting": False, + "image": "\N{SPEAKER WITH CANCELLATION STROKE}", + "case_str": "Channel Mute", +} + +servermute = { + "name": "smute", + "default_setting": True, + "image": "\N{SPEAKER WITH CANCELLATION STROKE}", + "case_str": "Server Mute", +} + +voiceunmute = { + "name": "vunmute", + "default_setting": False, + "image": "\N{SPEAKER}", + "case_str": "Voice Unmute", +} +channelunmute = { + "name": "cunmute", + "default_setting": False, + "image": "\N{SPEAKER}", + "case_str": "Channel Unmute", +} +serverunmute = { + "name": "sunmute", + "default_setting": True, + "image": "\N{SPEAKER}", + "case_str": "Server Unmute", +} + +voicekick = { + "name": "vkick", + "default_setting": False, + "image": "\N{SPEAKER WITH CANCELLATION STROKE}", + "case_str": "Voice Kick", +} + +all_generics = ( + ban, + kick, + hackban, + tempban, + softban, + unban, + voiceban, + voiceunban, + voicemute, + channelmute, + servermute, + voiceunmute, + serverunmute, + channelunmute, + voicekick, +) diff --git a/redbot/core/modlog.py b/redbot/core/modlog.py index ed272dd4e..0fbfae4a2 100644 --- a/redbot/core/modlog.py +++ b/redbot/core/modlog.py @@ -1,4 +1,5 @@ -from datetime import datetime +import asyncio +from datetime import datetime, timedelta from typing import List, Union, Optional, cast import discord @@ -14,6 +15,8 @@ from .utils.common_filters import ( ) from .i18n import Translator +from .generic_casetypes import all_generics + __all__ = [ "Case", "CaseType", @@ -32,17 +35,20 @@ __all__ = [ ] _conf: Optional[Config] = None +_bot_ref: Optional[Red] = None _CASETYPES = "CASETYPES" _CASES = "CASES" -_SCHEMA_VERSION = 2 +_SCHEMA_VERSION = 3 _ = Translator("ModLog", __file__) -async def _init(): +async def _init(bot: Red): global _conf + global _bot_ref + _bot_ref = bot _conf = Config.get_conf(None, 1354799444, cog_name="ModLog") _conf.register_global(schema_version=1) _conf.register_guild(mod_log=None, casetypes={}) @@ -51,12 +57,88 @@ async def _init(): _conf.register_custom(_CASETYPES) _conf.register_custom(_CASES) await _migrate_config(from_version=await _conf.schema_version(), to_version=_SCHEMA_VERSION) + await register_casetypes(all_generics) + + async def on_member_ban(guild: discord.Guild, member: discord.Member): + + if not guild.me.guild_permissions.view_audit_log: + return + + try: + await get_modlog_channel(guild) + except RuntimeError: + return # No modlog channel so no point in continuing + + when = datetime.utcnow() + before = when + timedelta(minutes=1) + after = when - timedelta(minutes=1) + await asyncio.sleep(10) # prevent small delays from causing a 5 minute delay on entry + + attempts = 0 + while attempts < 12: # wait up to an hour to find a matching case + attempts += 1 + try: + entry = await guild.audit_logs( + action=discord.AuditLogAction.ban, before=before, after=after + ).find(lambda e: e.target.id == member.id and after < e.created_at < before) + except discord.Forbidden: + break + except discord.HTTPException: + pass + else: + if entry: + if entry.user.id != guild.me.id: + # Don't create modlog entires for the bot's own bans, cogs do this. + mod, reason, date = entry.user, entry.reason, entry.created_at + await create_case(_bot_ref, guild, date, "ban", member, mod, reason) + return + + await asyncio.sleep(300) + + async def on_member_unban(guild: discord.Guild, user: discord.User): + if not guild.me.guild_permissions.view_audit_log: + return + + try: + await get_modlog_channel(guild) + except RuntimeError: + return # No modlog channel so no point in continuing + + when = datetime.utcnow() + before = when + timedelta(minutes=1) + after = when - timedelta(minutes=1) + await asyncio.sleep(10) # prevent small delays from causing a 5 minute delay on entry + + attempts = 0 + while attempts < 12: # wait up to an hour to find a matching case + attempts += 1 + try: + entry = await guild.audit_logs( + action=discord.AuditLogAction.unban, before=before, after=after + ).find(lambda e: e.target.id == user.id and after < e.created_at < before) + except discord.Forbidden: + break + except discord.HTTPException: + pass + else: + if entry: + if entry.user.id != guild.me.id: + # Don't create modlog entires for the bot's own unbans, cogs do this. + mod, reason, date = entry.user, entry.reason, entry.created_at + await create_case(_bot_ref, guild, date, "unban", user, mod, reason) + return + + await asyncio.sleep(300) + + bot.add_listener(on_member_ban) + bot.add_listener(on_member_unban) async def _migrate_config(from_version: int, to_version: int): if from_version == to_version: return - elif from_version < to_version: + + if from_version < 2 <= to_version: # casetypes go from GLOBAL -> casetypes to CASETYPES all_casetypes = await _conf.get_raw("casetypes", default={}) if all_casetypes: @@ -72,13 +154,26 @@ async def _migrate_config(from_version: int, to_version: int): await _conf.custom(_CASES).set(all_cases) # new schema is now in place - await _conf.schema_version.set(_SCHEMA_VERSION) + await _conf.schema_version.set(2) # migration done, now let's delete all the old stuff await _conf.clear_raw("casetypes") for guild_id in all_guild_data: await _conf.guild(cast(discord.Guild, discord.Object(id=guild_id))).clear_raw("cases") + if from_version < 3 <= to_version: + all_casetypes = { + casetype_name: { + inner_key: inner_value + for inner_key, inner_value in casetype_data.items() + if inner_key != "audit_type" + } + for casetype_name, casetype_data in (await _conf.custom(_CASETYPES).all()).items() + } + + await _conf.custom(_CASETYPES).set(all_casetypes) + await _conf.schema_version.set(3) + class Case: """A single mod log case""" @@ -131,6 +226,17 @@ class Case: await _conf.custom(_CASES, str(self.guild.id), str(self.case_number)).set(self.to_json()) self.bot.dispatch("modlog_case_edit", self) + if not self.message: + return + try: + use_embed = await self.bot.embed_requested(self.message.channel, self.guild.me) + case_content = await self.message_content(use_embed) + if use_embed: + await self.message.edit(embed=case_content) + else: + await self.message.edit(content=case_content) + finally: + return None async def message_content(self, embed: bool = True): """ @@ -371,9 +477,7 @@ class CaseType: The emoji to use for the case type (for example, :boot:) case_str: str The string representation of the case (example: Ban) - audit_type: `str`, optional - The action type of the action as it would appear in the - audit log + """ def __init__( @@ -382,14 +486,12 @@ class CaseType: default_setting: bool, image: str, case_str: str, - audit_type: Optional[str] = None, guild: Optional[discord.Guild] = None, ): self.name = name self.default_setting = default_setting self.image = image self.case_str = case_str - self.audit_type = audit_type self.guild = guild async def to_json(self): @@ -398,7 +500,6 @@ class CaseType: "default_setting": self.default_setting, "image": self.image, "case_str": self.case_str, - "audit_type": self.audit_type, } await _conf.custom(_CASETYPES, self.name).set(data) @@ -663,7 +764,19 @@ async def create_case( ) await _conf.custom(_CASES, str(guild.id), str(next_case_number)).set(case.to_json()) bot.dispatch("modlog_case_create", case) - return case + try: + mod_channel = await get_modlog_channel(case.guild) + use_embeds = await case.bot.embed_requested(mod_channel, case.guild.me) + case_content = await case.message_content(use_embeds) + if use_embeds: + msg = await mod_channel.send(embed=case_content) + else: + msg = await mod_channel.send(case_content) + await case.edit({"message": msg}) + except (RuntimeError, discord.HTTPException): + pass + finally: + return case async def get_casetype(name: str, guild: Optional[discord.Guild] = None) -> Optional[CaseType]: @@ -706,7 +819,7 @@ async def get_all_casetypes(guild: discord.Guild = None) -> List[CaseType]: async def register_casetype( - name: str, default_setting: bool, image: str, case_str: str, audit_type: str = None + name: str, default_setting: bool, image: str, case_str: str ) -> CaseType: """ Registers a case type. If the case type exists and @@ -725,9 +838,6 @@ async def register_casetype( The emoji to use for the case type (for example, :boot:) case_str: str The string representation of the case (example: Ban) - audit_type: `str`, optional - The action type of the action as it would appear in the - audit log Returns ------- @@ -742,8 +852,6 @@ async def register_casetype( If a parameter is missing ValueError If a parameter's value is not valid - AttributeError - If the audit_type is not an attribute of `discord.AuditLogAction` """ if not isinstance(name, str): @@ -754,16 +862,10 @@ async def register_casetype( raise ValueError("The 'image' is not a string!") if not isinstance(case_str, str): raise ValueError("The 'case_str' is not a string!") - if audit_type is not None: - if not isinstance(audit_type, str): - raise ValueError("The 'audit_type' is not a string!") - try: - getattr(discord.AuditLogAction, audit_type) - except AttributeError: - raise + ct = await get_casetype(name) if ct is None: - casetype = CaseType(name, default_setting, image, case_str, audit_type) + casetype = CaseType(name, default_setting, image, case_str) await casetype.to_json() return casetype else: @@ -779,9 +881,6 @@ async def register_casetype( if ct.case_str != case_str: ct.case_str = case_str changed = True - if ct.audit_type != audit_type: - ct.audit_type = audit_type - changed = True if changed: await ct.to_json() return ct diff --git a/redbot/pytest/mod.py b/redbot/pytest/mod.py index 243af525b..7cadeba98 100644 --- a/redbot/pytest/mod.py +++ b/redbot/pytest/mod.py @@ -5,11 +5,11 @@ __all__ = ["mod"] @pytest.fixture -async def mod(config, monkeypatch): +async def mod(config, monkeypatch, red): from redbot.core import Config with monkeypatch.context() as m: m.setattr(Config, "get_conf", lambda *args, **kwargs: config) - await modlog._init() + await modlog._init(red) return modlog diff --git a/tests/cogs/test_mod.py b/tests/cogs/test_mod.py index 3cfe13a58..a82ed27c3 100644 --- a/tests/cogs/test_mod.py +++ b/tests/cogs/test_mod.py @@ -5,13 +5,7 @@ from redbot.pytest.mod import * @pytest.mark.asyncio async def test_modlog_register_casetype(mod): - ct = { - "name": "ban", - "default_setting": True, - "image": ":hammer:", - "case_str": "Ban", - "audit_type": "ban", - } + ct = {"name": "ban", "default_setting": True, "image": ":hammer:", "case_str": "Ban"} casetype = await mod.register_casetype(**ct) assert casetype is not None