import discord from pytimeparse2 import disable_dateutil, parse from discord import ui from redbot.core import Config, commands, app_commands class Shortmute(commands.Cog): """Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature.""" def __init__(self, bot) -> None: self.bot = bot self.config = Config.get_conf(self, identifier=25781647388294, force_registration=True) self.config.register_guild( dm = True, logging_channels = [], immune_roles = [] ) @app_commands.command() @app_commands.rename(target='member') async def shortmute(self, interaction: discord.Interaction, target: discord.Member, duration: int, reason: str, evidence_link: str = None, evidence_image: discord.Attachment = None, skip_confirmation: bool = False): """Shortmute someone for up to 30m. Parameters ----------- target: discord.Member The member to shortmute duration: int The duration of the shortmute reason: str The reason for the shortmute evidence_link: str = None An image link to evidence for the shortmute, do not use with evidence_image evidence_image: discord.Attachment = None An image file used as evidence for the shortmute, do not use with evidence_link skip_confirmation: bool = False This allows you skip the confirmation prompt and immediately shortmute the user. """ disable_dateutil() timedelta = parse(f'{duration} minutes', as_timedelta=True) passed_info = { "target": target, "timedelta": timedelta, "reason": reason, "interaction": interaction, "color": await self.bot.get_embed_color(None) } if evidence_image and evidence_link: await interaction.response.send_message(content="You've provided both the `evidence_image` and the `evidence_link` arguments! Please only use one or the other.", ephemeral=True) return elif evidence_link: evidence = evidence_link elif evidence_image: evidence = str(evidence_image) else: evidence = None immune_roles_list = await self.config.guild(interaction.guild).immune_roles() for role_id in immune_roles_list: role = interaction.guild.get_role(role_id) if role in target.roles: await interaction.response.send_message(content="You're trying to shortmute someone who is immune from shortmuting.", ephemeral=True) return if duration == 1 or duration == -1: readable_duration = f"{duration} minute" passed_info.update({ "readable_duration": readable_duration }) else: readable_duration = f"{duration} minutes" passed_info.update({ "readable_duration": readable_duration }) if duration > 30: await interaction.response.send_message(content=f"{readable_duration} is longer than the 30 minutes you are allowed to shortmute users for.", ephemeral=True) return elif duration < 1: await interaction.response.send_message(content=f"Please shortmute the user for longer than {readable_duration}! The maximum duration is 30 minutes.", ephemeral=True) return if skip_confirmation is False: embed = discord.Embed(title="Are you sure?", description=f"**Moderator:** {interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=await self.bot.get_embed_color(None)) embed.set_footer(text="/shortmute") if evidence: embed.set_image(url=evidence) passed_info.update({ "evidence": evidence }) await interaction.response.send_message(embed=embed, view=self.ShortmuteButtons(timeout=180, passed_info=passed_info), ephemeral=True) elif skip_confirmation is True: edit_embed = discord.Embed(title="Shortmute confirmed!", description=f"**Moderator:** {interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=await self.bot.get_embed_color(None)) edit_embed.set_footer(text="/shortmute") if evidence: edit_embed.set_image(url=evidence) message = await interaction.response.send_message(embed=edit_embed, ephemeral=True) await target.timeout(timedelta, reason=f"User shortmuted for {readable_duration} by {interaction.user.name} ({interaction.user.id}) for: {reason}") await interaction.channel.send(content=f"{target.mention} was shortmuted for {readable_duration} by {interaction.user.mention} for: `{reason}`") if await self.config.guild(interaction.guild).dm() is True: dm_embed = discord.Embed(title=f"You've been shortmuted in {interaction.guild.name}!", description=f"**Moderator:** {interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=await self.bot.get_embed_color(None)) dm_embed.set_footer(text="/shortmute") if evidence: dm_embed.set_image(url=evidence) try: await target.send(embed=dm_embed) except discord.HTTPException as error: await message.edit(content="Could not message the target, user most likely has Direct Messages disabled.") logging_channels_list = await self.config.guild(interaction.guild).logging_channels() if logging_channels_list: logging_embed = discord.Embed(title="User Shortmuted", description=f"**Moderator:** {interaction.user.mention} ({interaction.user.id})\n**Target:** {target.mention} ({target.id})\n**Duration:** {readable_duration}\n**Reason:** `{reason}`\n**Confirmation Skipped:** True", color=await self.bot.get_embed_color(None)) logging_embed.set_footer(text="/shortmute") if evidence: logging_embed.set_image(url=evidence) for channel_id in logging_channels_list: channel_obj = interaction.guild.get_channel(channel_id) await channel_obj.send(embed=logging_embed) class ShortmuteButtons(ui.View): def __init__(self, timeout, passed_info: dict): super().__init__() self.timeout = timeout self.passed_info = passed_info self.config = Config.get_conf(None, cog_name='Shortmute', identifier=25781647388294) @ui.button(label="Yes", style=discord.ButtonStyle.success) async def shortmute_button_yes(self, button: ui.Button, interaction: discord.Interaction): disable_dateutil() target = self.passed_info['target'] readable_duration = self.passed_info['readable_duration'] reason = self.passed_info['reason'] old_interaction = self.passed_info['interaction'] color = self.passed_info['color'] timedelta = self.passed_info['timedelta'] evidence = self.passed_info['evidence'] edit_embed = discord.Embed(title="Shortmute confirmed!", description=f"**Moderator:** {old_interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=color) if evidence: edit_embed.set_image(url=evidence) old_message = await old_interaction.edit_original_response(embed=edit_embed, view=None) await target.timeout(timedelta, reason=f"User shortmuted for {readable_duration} by {old_interaction.user.name} ({old_interaction.user.id}) for: {reason}") await old_interaction.channel.send(content=f"{target.mention} was shortmuted for {readable_duration} by {old_interaction.user.mention} for: `{reason}`") if await self.config.guild(old_interaction.guild).dm() is True: dm_embed = discord.Embed(title=f"You've been shortmuted in {old_interaction.guild.name}!", description=f"**Moderator:** {old_interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=color) dm_embed.set_footer(text="/shortmute") if evidence: dm_embed.set_image(url=evidence) try: await target.send(embed=dm_embed) except discord.HTTPException as error: await old_message.edit(content="Could not message the target, user most likely has Direct Messages disabled.") logging_channels_list = await self.config.guild(old_interaction.guild).logging_channels() if logging_channels_list: logging_embed = discord.Embed(title="User Shortmuted", description=f"**Moderator:** {old_interaction.user.mention} ({old_interaction.user.id})\n**Target:** {target.mention} ({target.id})\n**Duration:** {readable_duration}\n**Reason:** `{reason}`\n**Confirmation Skipped:** False", color=color) logging_embed.set_footer(text="/shortmute") if evidence: logging_embed.set_image(url=evidence) for channel_id in logging_channels_list: channel_obj = old_interaction.guild.get_channel(channel_id) await channel_obj.send(embed=logging_embed) @ui.button(label="No", style=discord.ButtonStyle.danger) async def shortmute_button_no(self, button: ui.Button, interaction: discord.Interaction): message = await self.passed_info['interaction'].edit_original_response(content="Command cancelled.", view=None, embed=None) await message.delete(delay=3) @commands.group(name='shortmuteset', autohelp=True) @commands.guild_only() @commands.admin() async def shortmute_config(self, ctx: commands.Context): """This command group is used to configure the `/shortmute` slash command.""" @shortmute_config.command(name='addchannel') @commands.guild_only() @commands.admin() async def shortmute_config_addchannel(self, ctx: commands.Context, channel: discord.TextChannel = None): """This command changes where the `/shortmute` slash command will log shortmutes. You can set multiple channels as well!""" current_list = await self.config.guild(ctx.guild).logging_channels() if channel: if channel.id in current_list: await ctx.send("This channel is already in the logging channel list!") return else: current_list.append(channel.id) await self.config.guild(ctx.guild).logging_channels.set(current_list) await ctx.send(f"{channel.mention} has been added to the logging channels list.") else: already_in_list = [] for channel_id in current_list: channel_obj = ctx.guild.get_channel(channel_id) if channel_obj: already_in_list.append(channel_obj.mention) if already_in_list: await ctx.send("Channels already in the list:\n" + "\n".join(already_in_list)) else: await ctx.send("Please provide a valid channel.") @shortmute_config.command(name='removechannel') @commands.guild_only() @commands.admin() async def shortmute_config_removechannel(self, ctx: commands.Context, channel: discord.TextChannel = None): """This command removes a channel from the `/shortmute`slash command's logging channels list.""" current_list = await self.config.guild(ctx.guild).logging_channels() if channel.id in current_list: current_list.remove(channel.id) await self.config.guild(ctx.guild).logging_channels.set(current_list) await ctx.send(f"{channel.mention} has been removed from the logging channels list.") else: await ctx.send("Please provide a valid channel that exists in the logging channels list.") @shortmute_config.command(name='addrole') @commands.guild_only() @commands.admin() async def shortmute_config_addrole(self, ctx: commands.Context, role: discord.Role = None): """This command adds roles to the immune roles list for immunity from the the `/shortmute` slash command.""" current_list = await self.config.guild(ctx.guild).immune_roles() if role: if role.id in current_list: await ctx.send("This role is already in the immune roles list!") return else: current_list.append(role.id) await self.config.guild(ctx.guild).immune_roles.set(current_list) await ctx.send(f"{role.mention} has been added to the immune roles list.", allowed_mentions = discord.AllowedMentions(roles=False)) else: already_in_list = [] for role_id in current_list: role_obj = ctx.guild.get_role(role_id) if role_obj: already_in_list.append(role_obj.mention) if already_in_list: await ctx.send("Roles already in the immune roles list:\n" + "\n".join(already_in_list), allowed_mentions = discord.AllowedMentions(roles=False)) else: await ctx.send("Please provide a valid role.") @shortmute_config.command(name='removerole') @commands.guild_only() @commands.admin() async def shortmute_config_removerole(self, ctx: commands.Context, role: discord.Role = None): """This command removes roles from the immune roles list to remove immunity from the the `/shortmute` slash command.""" current_list = await self.config.guild(ctx.guild).immune_roles() if role.id in current_list: current_list.remove(role.id) await self.config.guild(ctx.guild).immune_roles.set(current_list) await ctx.send(f"{role.mention} has been removed from the immune roles list.", allowed_mentions = discord.AllowedMentions(roles=False)) else: await ctx.send("Please provide a valid role that exists in the immune roles list.") @shortmute_config.command(name='dm') @commands.guild_only() @commands.admin() async def shortmute_config_dm(self, ctx: commands.Context, enabled: bool = None): """This command changes if the `/shortmute` slash command Direct Messages its target.""" old_value = await self.config.guild(ctx.guild).dm() if enabled: await self.config.guild(ctx.guild).dm.set(enabled) await ctx.send(content=f"Shortmute Direct Message setting changed!\nOld value: `{old_value}`\nNew value: `{enabled}`") elif old_value is True: await ctx.send(content="Shortmute Direct Messages are currently enabled!") elif old_value is False: await ctx.send(content="Shortmute Direct Messages are currently disabled!")