From 2b11fea0cbcddff58ee0ce0527ee988d69c57b62 Mon Sep 17 00:00:00 2001 From: Zevaryx Date: Tue, 29 Mar 2022 22:12:05 -0600 Subject: [PATCH] Work on context menu muting --- jarvis/__init__.py | 4 +- jarvis/cogs/admin/mute.py | 122 +++++++++++++++++++++++++++++++------- 2 files changed, 100 insertions(+), 26 deletions(-) diff --git a/jarvis/__init__.py b/jarvis/__init__.py index 425519e..34f5af8 100644 --- a/jarvis/__init__.py +++ b/jarvis/__init__.py @@ -16,7 +16,6 @@ except Exception: __version__ = "0.0.0" jconfig = JarvisConfig.from_yaml() - logger = get_logger("jarvis") logger.setLevel(jconfig.log_level) file_handler = logging.FileHandler(filename="jarvis.log", encoding="UTF-8", mode="w") @@ -28,8 +27,7 @@ logger.addHandler(file_handler) intents = Intents.DEFAULT | Intents.MESSAGES | Intents.GUILD_MEMBERS | Intents.GUILD_MESSAGES restart_ctx = None - -jarvis = Jarvis(intents=intents, default_prefix="!", sync_interactions=jconfig.sync) +jarvis = Jarvis(intents=intents, sync_interactions=jconfig.sync) async def run() -> None: diff --git a/jarvis/cogs/admin/mute.py b/jarvis/cogs/admin/mute.py index 4ccf7f1..7cc022b 100644 --- a/jarvis/cogs/admin/mute.py +++ b/jarvis/cogs/admin/mute.py @@ -1,13 +1,19 @@ """J.A.R.V.I.S. MuteCog.""" +import asyncio import logging from datetime import datetime, timedelta, timezone +from dateparser import parse +from dateparser_data.settings import default_parsers from dis_snek import InteractionContext, Permissions, Snake from dis_snek.models.discord.embed import EmbedField +from dis_snek.models.discord.modal import InputText, Modal, TextStyles from dis_snek.models.discord.user import Member from dis_snek.models.snek.application_commands import ( + CommandTypes, OptionTypes, SlashCommandChoice, + context_menu, slash_command, slash_option, ) @@ -26,6 +32,96 @@ class MuteCog(ModcaseCog): super().__init__(bot) self.logger = logging.getLogger(__name__) + async def _apply_timeout( + self, ctx: InteractionContext, user: Member, reason: str, until: datetime + ) -> None: + await user.timeout(communication_disabled_until=until, reason=reason) + duration = int((until - datetime.now(tz=timezone.utc)).seconds / 60) + await Mute( + user=user.id, + reason=reason, + admin=ctx.author.id, + guild=ctx.guild.id, + duration=duration, + active=True, + ).commit() + ts = int(until.timestamp()) + + embed = build_embed( + title="User Muted", + description=f"{user.mention} has been muted", + fields=[ + EmbedField(name="Reason", value=reason), + EmbedField(name="Until", value=f" "), + ], + ) + embed.set_author(name=user.display_name, icon_url=user.display_avatar.url) + embed.set_thumbnail(url=user.display_avatar.url) + embed.set_footer(text=f"{user.username}#{user.discriminator} | {user.id}") + return embed + + @context_menu(name="Mute User", context_type=CommandTypes.USER) + @check( + admin_or_permissions( + Permissions.MUTE_MEMBERS, Permissions.BAN_MEMBERS, Permissions.KICK_MEMBERS + ) + ) + async def _timeout_cm(self, ctx: InteractionContext) -> None: + modal = Modal( + title=f"Muting {ctx.target.mention}", + components=[ + InputText( + label="Reason?", + placeholder="Spamming, harrassment, etc", + style=TextStyles.SHORT, + custom_id="reason", + max_length=100, + ), + InputText( + label="Duration", + placeholder="1h 30m | in 5 minutes | in 4 weeks", + style=TextStyles.SHORT, + custom_id="until", + max_length=100, + ), + ], + ) + await ctx.send_modal(modal) + try: + response = await self.bot.wait_for_modal(modal, author=ctx.author.id, timeout=60 * 5) + reason = response.responses.get("reason") + until = response.responses.get("until") + except asyncio.TimeoutError: + return + base_settings = { + "PREFER_DATES_FROM": "future", + "TIMEZONE": "UTC", + "RETURN_AS_TIMEZONE_AWARE": True, + } + rt_settings = base_settings.copy() + rt_settings["PARSERS"] = [ + x for x in default_parsers if x not in ["absolute-time", "timestamp"] + ] + + rt_until = parse(until, settings=rt_settings) + + at_settings = base_settings.copy() + at_settings["PARSERS"] = [x for x in default_parsers if x != "relative-time"] + at_until = parse(until, settings=at_settings) + + if rt_until: + until = rt_until + elif at_until: + until = at_until + else: + self.logger.debug(f"Failed to parse delay: {until}") + await response.send( + f"`{until}` is not a parsable date, please try again", ephemeral=True + ) + return + embed = await self._apply_timeout(ctx, ctx.target, reason, until) + await response.send(embed=embed) + @slash_command(name="mute", description="Mute a user") @slash_option(name="user", description="User to mute", opt_type=OptionTypes.USER, required=True) @slash_option( @@ -77,29 +173,7 @@ class MuteCog(ModcaseCog): return until = datetime.now(tz=timezone.utc) + timedelta(minutes=duration) - await user.timeout(communication_disabled_until=until, reason=reason) - m = Mute( - user=user.id, - reason=reason, - admin=ctx.author.id, - guild=ctx.guild.id, - duration=duration, - active=True, - ) - await m.commit() - ts = int(until.timestamp()) - - embed = build_embed( - title="User Muted", - description=f"{user.mention} has been muted", - fields=[ - EmbedField(name="Reason", value=reason), - EmbedField(name="Until", value=f" "), - ], - ) - embed.set_author(name=user.display_name, icon_url=user.display_avatar.url) - embed.set_thumbnail(url=user.display_avatar.url) - embed.set_footer(text=f"{user.username}#{user.discriminator} | {user.id}") + embed = await self._apply_timeout(ctx, user, reason, until) await ctx.send(embed=embed) @slash_command(name="unmute", description="Unmute a user") @@ -120,6 +194,8 @@ class MuteCog(ModcaseCog): await ctx.send("User is not muted", ephemeral=True) return + await user.timeout(communication_disabled_until=0) + embed = build_embed( title="User Unmuted", description=f"{user.mention} has been unmuted",