diff --git a/jarvis/cogs/admin/__init__.py b/jarvis/cogs/admin/__init__.py index b11ccd4..daaca31 100644 --- a/jarvis/cogs/admin/__init__.py +++ b/jarvis/cogs/admin/__init__.py @@ -1,15 +1,15 @@ """J.A.R.V.I.S. Admin Cogs.""" from dis_snek import Snake -from jarvis.cogs.admin import ban, kick, mute, purge, roleping, warning +from jarvis.cogs.admin import ban, kick, lock, lockdown, mute, purge, roleping, warning def setup(bot: Snake) -> None: """Add admin cogs to J.A.R.V.I.S.""" ban.BanCog(bot) kick.KickCog(bot) - # lock.LockCog(bot) - # lockdown.LockdownCog(bot) + lock.LockCog(bot) + lockdown.LockdownCog(bot) mute.MuteCog(bot) purge.PurgeCog(bot) roleping.RolepingCog(bot) diff --git a/jarvis/cogs/admin/ban.py b/jarvis/cogs/admin/ban.py index f4742cb..9b98ec9 100644 --- a/jarvis/cogs/admin/ban.py +++ b/jarvis/cogs/admin/ban.py @@ -1,7 +1,7 @@ """J.A.R.V.I.S. BanCog.""" import re -from dis_snek import InteractionContext, Permissions, Scale +from dis_snek import InteractionContext, Permissions from dis_snek.client.utils.misc_utils import find, find_all from dis_snek.ext.paginators import Paginator from dis_snek.models.discord.embed import EmbedField @@ -17,10 +17,11 @@ from jarvis_core.db import q from jarvis_core.db.models import Ban, Unban from jarvis.utils import build_embed +from jarvis.utils.cogs import ModcaseCog from jarvis.utils.permissions import admin_or_permissions -class BanCog(Scale): +class BanCog(ModcaseCog): """J.A.R.V.I.S. BanCog.""" async def discord_apply_ban( @@ -105,6 +106,12 @@ class BanCog(Scale): SlashCommandChoice(name="Soft", value="soft"), ], ) + @slash_option( + name="duration", + description="Temp ban duration in hours", + opt_type=OptionTypes.INTEGER, + required=False, + ) @check(admin_or_permissions(Permissions.BAN_MEMBERS)) async def _ban( self, diff --git a/jarvis/cogs/admin/kick.py b/jarvis/cogs/admin/kick.py index 3bb7cfb..8a3a6b1 100644 --- a/jarvis/cogs/admin/kick.py +++ b/jarvis/cogs/admin/kick.py @@ -1,5 +1,5 @@ """J.A.R.V.I.S. KickCog.""" -from dis_snek import InteractionContext, Permissions, Scale +from dis_snek import InteractionContext, Permissions from dis_snek.models.discord.embed import EmbedField from dis_snek.models.discord.user import User from dis_snek.models.snek.application_commands import ( @@ -11,10 +11,11 @@ from dis_snek.models.snek.command import check from jarvis_core.db.models import Kick from jarvis.utils import build_embed +from jarvis.utils.cogs import ModcaseCog from jarvis.utils.permissions import admin_or_permissions -class KickCog(Scale): +class KickCog(ModcaseCog): """J.A.R.V.I.S. KickCog.""" @slash_command(name="kick", description="Kick a user") diff --git a/jarvis/cogs/admin/mute.py b/jarvis/cogs/admin/mute.py index 5ac5ec5..ffb634f 100644 --- a/jarvis/cogs/admin/mute.py +++ b/jarvis/cogs/admin/mute.py @@ -1,7 +1,7 @@ """J.A.R.V.I.S. MuteCog.""" from datetime import datetime -from dis_snek import InteractionContext, Permissions, Scale, Snake +from dis_snek import InteractionContext, Permissions from dis_snek.models.discord.embed import EmbedField from dis_snek.models.discord.user import Member from dis_snek.models.snek.application_commands import ( @@ -14,15 +14,13 @@ from dis_snek.models.snek.command import check from jarvis_core.db.models import Mute from jarvis.utils import build_embed +from jarvis.utils.cogs import ModcaseCog from jarvis.utils.permissions import admin_or_permissions -class MuteCog(Scale): +class MuteCog(ModcaseCog): """J.A.R.V.I.S. MuteCog.""" - def __init__(self, bot: Snake): - self.bot = bot - @slash_command(name="mute", description="Mute a user") @slash_option(name="user", description="User to mute", opt_type=OptionTypes.USER, required=True) @slash_option( diff --git a/jarvis/cogs/admin/warning.py b/jarvis/cogs/admin/warning.py index 7e60987..0ef4d62 100644 --- a/jarvis/cogs/admin/warning.py +++ b/jarvis/cogs/admin/warning.py @@ -1,5 +1,5 @@ """J.A.R.V.I.S. WarningCog.""" -from dis_snek import InteractionContext, Permissions, Scale +from dis_snek import InteractionContext, Permissions from dis_snek.client.utils.misc_utils import get_all from dis_snek.ext.paginators import Paginator from dis_snek.models.discord.embed import EmbedField @@ -14,11 +14,12 @@ from dis_snek.models.snek.command import check from jarvis_core.db.models import Warning from jarvis.utils import build_embed +from jarvis.utils.cogs import ModcaseCog from jarvis.utils.embeds import warning_embed from jarvis.utils.permissions import admin_or_permissions -class WarningCog(Scale): +class WarningCog(ModcaseCog): """J.A.R.V.I.S. WarningCog.""" @slash_command(name="warn", description="Warn a user") diff --git a/jarvis/utils/cachecog.py b/jarvis/utils/cachecog.py deleted file mode 100644 index d7be74b..0000000 --- a/jarvis/utils/cachecog.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Cog wrapper for command caching.""" -from datetime import datetime, timedelta - -from dis_snek import InteractionContext, Scale, Snake -from dis_snek.client.utils.misc_utils import find -from dis_snek.ext.tasks.task import Task -from dis_snek.ext.tasks.triggers import IntervalTrigger - - -class CacheCog(Scale): - """Cog wrapper for command caching.""" - - def __init__(self, bot: Snake): - self.bot = bot - self.cache = {} - self._expire_interaction.start() - - def check_cache(self, ctx: InteractionContext, **kwargs: dict) -> dict: - """Check the cache.""" - if not kwargs: - kwargs = {} - return find( - lambda x: x["command"] == ctx.subcommand_name # noqa: W503 - and x["user"] == ctx.author.id # noqa: W503 - and x["guild"] == ctx.guild.id # noqa: W503 - and all(x[k] == v for k, v in kwargs.items()), # noqa: W503 - self.cache.values(), - ) - - @Task.create(IntervalTrigger(minutes=1)) - async def _expire_interaction(self) -> None: - keys = list(self.cache.keys()) - for key in keys: - if self.cache[key]["timeout"] <= datetime.utcnow() + timedelta(minutes=1): - del self.cache[key] diff --git a/jarvis/utils/cogs.py b/jarvis/utils/cogs.py new file mode 100644 index 0000000..f9361c3 --- /dev/null +++ b/jarvis/utils/cogs.py @@ -0,0 +1,75 @@ +"""Cog wrapper for command caching.""" +from datetime import datetime, timedelta + +from dis_snek import Context, Scale, Snake +from dis_snek.client.utils.misc_utils import find +from dis_snek.models.snek.tasks.task import Task +from dis_snek.models.snek.tasks.triggers import IntervalTrigger +from jarvis_core.db import q +from jarvis_core.db.models import Action, Ban, Kick, Modlog, Mute, Note, Warning + +MODLOG_LOOKUP = {"Ban": Ban, "Kick": Kick, "Mute": Mute, "Warning": Warning} + + +class CacheCog(Scale): + """Cog wrapper for command caching.""" + + def __init__(self, bot: Snake): + self.bot = bot + self.cache = {} + self._expire_interaction.start() + + def check_cache(self, ctx: Context, **kwargs: dict) -> dict: + """Check the cache.""" + if not kwargs: + kwargs = {} + return find( + lambda x: x["command"] == ctx.subcommand_name # noqa: W503 + and x["user"] == ctx.author.id # noqa: W503 + and x["guild"] == ctx.guild.id # noqa: W503 + and all(x[k] == v for k, v in kwargs.items()), # noqa: W503 + self.cache.values(), + ) + + @Task.create(IntervalTrigger(minutes=1)) + async def _expire_interaction(self) -> None: + keys = list(self.cache.keys()) + for key in keys: + if self.cache[key]["timeout"] <= datetime.utcnow() + timedelta(minutes=1): + del self.cache[key] + + +class ModcaseCog(Scale): + """Cog wrapper for moderation case logging.""" + + def __init__(self, bot: Snake): + self.bot = bot + self.add_scale_postrun(self.log) + + async def log(self, ctx: Context, *args: list, **kwargs: dict) -> None: + """ + Log a moderation activity in a moderation case. + + Args: + ctx: Command context + """ + name = self.__name__.replace("Cog", "") + + if name not in ["Lock", "Lockdown", "Purge", "Roleping"]: + user = kwargs.pop("user", None) + if not user: + # Log warning about missing user + return + coll = MODLOG_LOOKUP.get(name, None) + if not coll: + # Log warning about unsupported action + return + + action = await coll.find_one(q(user=user.id, guild=ctx.guild_id, active=True)) + if not action: + # Log warning about missing action + return + + action = Action(action_type=name.lower(), parent=action.id) + note = Note(admin=self.bot.user.id, content="Moderation case opened automatically") + await Modlog(user=user.id, admin=ctx.author.id, actions=[action], notes=[note]).commit()