diff --git a/minato_namikaze/bot_files/cogs/moderation/moderation.py b/minato_namikaze/bot_files/cogs/moderation/moderation.py index 413f6da7..98a0123f 100644 --- a/minato_namikaze/bot_files/cogs/moderation/moderation.py +++ b/minato_namikaze/bot_files/cogs/moderation/moderation.py @@ -13,11 +13,11 @@ BannedMember, EmbedPaginator, ErrorEmbed, + FutureTime, MemberID, check_if_warning_system_setup, + format_relative, has_permissions, - FutureTime, - format_relative ) @@ -68,7 +68,7 @@ async def kick(self, *, reason=None): """A command which kicks a given user""" - + if not await ctx.prompt( f"Are you sure that you want to **kick** {member} from the guild?", author_id=ctx.author.id, @@ -97,7 +97,7 @@ async def ban( reason: ActionReason = None, ): """A command which bans a given user""" - + if not await ctx.prompt( f"Are you sure that you want to **ban** {member} from the guild?", author_id=ctx.author.id, @@ -247,11 +247,13 @@ async def banlist(self, ctx, *, member: Optional[Union[str, MemberID, @commands.command() @commands.guild_only() @has_permissions(kick_members=True) - async def softban(self, - ctx, - member: Union[MemberID, discord.Member], - *, - reason: ActionReason = None): + async def softban( + self, + ctx, + member: Union[MemberID, discord.Member], + *, + reason: ActionReason = None, + ): """Soft bans a member from the server. A softban is basically banning the member from the server but @@ -263,7 +265,6 @@ async def softban(self, To use this command you must have Kick Members permissions. """ - if not await ctx.prompt( f"Are you sure that you want to **softban** {member} from the guild?", author_id=ctx.author.id, @@ -329,7 +330,7 @@ async def ar( """Adds a role to a given user""" if member is None: member = ctx.message.author - + role = ctx.get_roles(role) if not await ctx.prompt( @@ -346,11 +347,15 @@ async def ar( description=f"I have added the role '{role.mention}' to {member.mention}!", ) await ctx.send(embed=embed) - + @commands.command() @commands.guild_only() @has_permissions(ban_members=True) - async def multiban(self, ctx, members: commands.Greedy[MemberID], *, reason: ActionReason = None): + async def multiban(self, + ctx, + members: commands.Greedy[MemberID], + *, + reason: ActionReason = None): """Bans multiple members from the server. This only works through banning via ID. In order for this to work, the bot must have Ban Member permissions. @@ -358,19 +363,23 @@ async def multiban(self, ctx, members: commands.Greedy[MemberID], *, reason: Act """ if not await ctx.prompt( "Are you sure that you want to **ban multiple** members?", - author_id=ctx.author.id): + author_id=ctx.author.id, + ): return if reason is None: - reason = f'Action done by {ctx.author} (ID: {ctx.author.id})' + reason = f"Action done by {ctx.author} (ID: {ctx.author.id})" total_members = len(members) if total_members == 0: - return await ctx.send('Missing members to ban.') + return await ctx.send("Missing members to ban.") - confirm = await ctx.prompt(f'This will ban **{plural(total_members):member}**. Are you sure?', reacquire=False) + confirm = await ctx.prompt( + f"This will ban **{plural(total_members):member}**. Are you sure?", + reacquire=False, + ) if not confirm: - return await ctx.send('Aborting.') + return await ctx.send("Aborting.") failed = 0 for member in members: @@ -379,7 +388,8 @@ async def multiban(self, ctx, members: commands.Greedy[MemberID], *, reason: Act except discord.HTTPException: failed += 1 - await ctx.send(f'Banned {total_members - failed}/{total_members} members.') + await ctx.send( + f"Banned {total_members - failed}/{total_members} members.") @commands.command() @commands.guild_only() @@ -412,9 +422,8 @@ async def massban(self, ctx, *, args): `--embeds`: Checks if the message has embeds (no arguments). """ - if not await ctx.prompt( - f"Are you sure that you want to **massban**?", - author_id=ctx.author.id): + if not await ctx.prompt(f"Are you sure that you want to **massban**?", + author_id=ctx.author.id): return # For some reason there are cases due to caching that ctx.author @@ -425,30 +434,36 @@ async def massban(self, ctx, *, args): try: author = await ctx.guild.fetch_member(ctx.author.id) except discord.HTTPException: - return await ctx.send('Somehow, Discord does not seem to think you are in this server.') + return await ctx.send( + "Somehow, Discord does not seem to think you are in this server." + ) else: author = ctx.author parser = Arguments(add_help=False, allow_abbrev=False) - parser.add_argument('--channel', '-c') - parser.add_argument('--reason', '-r') - parser.add_argument('--search', type=int, default=100) - parser.add_argument('--regex') - parser.add_argument('--no-avatar', action='store_true') - parser.add_argument('--no-roles', action='store_true') - parser.add_argument('--created', type=int) - parser.add_argument('--joined', type=int) - parser.add_argument('--joined-before', type=int) - parser.add_argument('--joined-after', type=int) - parser.add_argument('--contains') - parser.add_argument('--starts') - parser.add_argument('--ends') - parser.add_argument('--match') - parser.add_argument('--show', action='store_true') - parser.add_argument('--embeds', action='store_const', const=lambda m: len(m.embeds)) - parser.add_argument('--files', action='store_const', const=lambda m: len(m.attachments)) - parser.add_argument('--after', type=int) - parser.add_argument('--before', type=int) + parser.add_argument("--channel", "-c") + parser.add_argument("--reason", "-r") + parser.add_argument("--search", type=int, default=100) + parser.add_argument("--regex") + parser.add_argument("--no-avatar", action="store_true") + parser.add_argument("--no-roles", action="store_true") + parser.add_argument("--created", type=int) + parser.add_argument("--joined", type=int) + parser.add_argument("--joined-before", type=int) + parser.add_argument("--joined-after", type=int) + parser.add_argument("--contains") + parser.add_argument("--starts") + parser.add_argument("--ends") + parser.add_argument("--match") + parser.add_argument("--show", action="store_true") + parser.add_argument("--embeds", + action="store_const", + const=lambda m: len(m.embeds)) + parser.add_argument("--files", + action="store_const", + const=lambda m: len(m.attachments)) + parser.add_argument("--after", type=int) + parser.add_argument("--before", type=int) try: args = parser.parse_args(shlex.split(args)) @@ -458,7 +473,8 @@ async def massban(self, ctx, *, args): members = [] if args.channel: - channel = await commands.TextChannelConverter().convert(ctx, args.channel) + channel = await commands.TextChannelConverter().convert( + ctx, args.channel) before = args.before and discord.Object(id=args.before) after = args.after and discord.Object(id=args.after) predicates = [] @@ -472,7 +488,8 @@ async def massban(self, ctx, *, args): try: _match = re.compile(args.match) except re.error as e: - return await ctx.send(f'Invalid regex passed to `--match`: {e}') + return await ctx.send( + f"Invalid regex passed to `--match`: {e}") else: predicates.append(lambda m, x=_match: x.match(m.content)) if args.embeds: @@ -480,7 +497,10 @@ async def massban(self, ctx, *, args): if args.files: predicates.append(args.files) - async for message in channel.history(limit=min(max(1, args.search), 2000), before=before, after=after): + async for message in channel.history(limit=min( + max(1, args.search), 2000), + before=before, + after=after): if all(p(message) for p in predicates): members.append(message.author) else: @@ -493,9 +513,10 @@ async def massban(self, ctx, *, args): # member filters predicates = [ - lambda m: isinstance(m, discord.Member) and can_execute_action(ctx, author, m), # Only if applicable - lambda m: not m.bot, # No bots - lambda m: m.discriminator != '0000', # No deleted users + lambda m: isinstance(m, discord.Member) and can_execute_action( + ctx, author, m), # Only if applicable + lambda m: not m.bot, # No bots + lambda m: m.discriminator != "0000", # No deleted users ] converter = commands.MemberConverter() @@ -504,57 +525,78 @@ async def massban(self, ctx, *, args): try: _regex = re.compile(args.regex) except re.error as e: - return await ctx.send(f'Invalid regex passed to `--regex`: {e}') + return await ctx.send(f"Invalid regex passed to `--regex`: {e}" + ) else: predicates.append(lambda m, x=_regex: x.match(m.name)) if args.no_avatar: predicates.append(lambda m: m.avatar is None) if args.no_roles: - predicates.append(lambda m: len(getattr(m, 'roles', [])) <= 1) + predicates.append(lambda m: len(getattr(m, "roles", [])) <= 1) now = discord.utils.utcnow() if args.created: - def created(member, *, offset=now - datetime.timedelta(minutes=args.created)): + + def created(member, + *, + offset=now - datetime.timedelta(minutes=args.created)): return member.created_at > offset + predicates.append(created) if args.joined: - def joined(member, *, offset=now - datetime.timedelta(minutes=args.joined)): + + def joined(member, + *, + offset=now - datetime.timedelta(minutes=args.joined)): if isinstance(member, discord.User): # If the member is a user then they left already return True return member.joined_at and member.joined_at > offset + predicates.append(joined) if args.joined_after: - _joined_after_member = await converter.convert(ctx, str(args.joined_after)) + _joined_after_member = await converter.convert( + ctx, str(args.joined_after)) + def joined_after(member, *, _other=_joined_after_member): - return member.joined_at and _other.joined_at and member.joined_at > _other.joined_at + return (member.joined_at and _other.joined_at + and member.joined_at > _other.joined_at) + predicates.append(joined_after) if args.joined_before: - _joined_before_member = await converter.convert(ctx, str(args.joined_before)) + _joined_before_member = await converter.convert( + ctx, str(args.joined_before)) + def joined_before(member, *, _other=_joined_before_member): - return member.joined_at and _other.joined_at and member.joined_at < _other.joined_at + return (member.joined_at and _other.joined_at + and member.joined_at < _other.joined_at) + predicates.append(joined_before) members = {m for m in members if all(p(m) for p in predicates)} if len(members) == 0: - return await ctx.send('No members found matching criteria.') + return await ctx.send("No members found matching criteria.") if args.show: members = sorted(members, key=lambda m: m.joined_at or now) - fmt = "\n".join(f'{m.id}\tJoined: {m.joined_at}\tCreated: {m.created_at}\t{m}' for m in members) - content = f'Current Time: {discord.utils.utcnow()}\nTotal members: {len(members)}\n{fmt}' - file = discord.File(io.BytesIO(content.encode('utf-8')), filename='members.txt') + fmt = "\n".join( + f"{m.id}\tJoined: {m.joined_at}\tCreated: {m.created_at}\t{m}" + for m in members) + content = f"Current Time: {discord.utils.utcnow()}\nTotal members: {len(members)}\n{fmt}" + file = discord.File(io.BytesIO(content.encode("utf-8")), + filename="members.txt") return await ctx.send(file=file) if args.reason is None: - return await ctx.send('--reason flag is required.') + return await ctx.send("--reason flag is required.") else: reason = await ActionReason().convert(ctx, args.reason) - confirm = await ctx.prompt(f'This will ban **{plural(len(members)):member}**. Are you sure?') + confirm = await ctx.prompt( + f"This will ban **{plural(len(members)):member}**. Are you sure?") if not confirm: - return await ctx.send('Aborting.') + return await ctx.send("Aborting.") count = 0 for member in members: @@ -565,7 +607,7 @@ def joined_before(member, *, _other=_joined_before_member): else: count += 1 - await ctx.send(f'Banned {count}/{len(members)}') + await ctx.send(f"Banned {count}/{len(members)}") # Warn @commands.command(pass_context=True, @@ -579,7 +621,7 @@ async def warn(self, *, reason: str = None): """Warn a user""" - + if not await ctx.prompt( f"Are you sure that you want to **warn** {member}?", author_id=ctx.author.id): @@ -1053,17 +1095,26 @@ def predicate(m): before=args.before, after=args.after) - @commands.command(aliases=['mute']) + @commands.command(aliases=["mute"]) @commands.guild_only() @commands.has_guild_permissions(timeout_members=True) @has_permissions(timeout_members=True) - async def timeout(self, ctx, duration: FutureTime, member: Union[MemberID, discord.Member], *, reason: ActionReason = None): + async def timeout( + self, + ctx, + duration: FutureTime, + member: Union[MemberID, discord.Member], + *, + reason: ActionReason = None, + ): if not await ctx.prompt( f"Are you sure that you want to **time out** {member} until {format_relative(duration.dt)}?", - author_id=ctx.author.id): + author_id=ctx.author.id, + ): return await member.edit(timed_out_until=duration.dt) - await ctx.send(embed=discord.Embed(description=f'**Timed out** {member} until {format_relative(duration.dt)}')) + await ctx.send(embed=discord.Embed( + description=f"**Timed out** {member} until {format_relative(duration.dt)}")) def setup(bot): diff --git a/minato_namikaze/bot_files/lib/classes/setup_classes.py b/minato_namikaze/bot_files/lib/classes/setup_classes.py index 81c037b3..dfcb5073 100644 --- a/minato_namikaze/bot_files/lib/classes/setup_classes.py +++ b/minato_namikaze/bot_files/lib/classes/setup_classes.py @@ -11,20 +11,23 @@ warns_topic = SetupVars.warns.value -async def check():pass +async def check(): + pass + class Ban: def __init__(self, ctx, timeout: int, channel: discord.TextChannel): self.ctx = ctx self.channel = channel self.timeout = timeout - + async def start(self): if not await self.ctx.prompt( f"Want to **log bans** for the *{self.ctx.guild.name}* ?", timeout=self.timeout, author_id=ctx.author.id, - channel=self.channel): + channel=self.channel, + ): return bingo = (discord.utils.get( self.ctx.guild.categories, name="Bingo Book") if discord.utils.get( diff --git a/minato_namikaze/bot_files/lib/util/context.py b/minato_namikaze/bot_files/lib/util/context.py index ee7297b9..543f7d77 100644 --- a/minato_namikaze/bot_files/lib/util/context.py +++ b/minato_namikaze/bot_files/lib/util/context.py @@ -30,13 +30,13 @@ def __init__( self.reacquire: bool = reacquire self.message: Optional[discord.Message] = None - async def interaction_check(self, interaction: discord.Interaction) -> bool: + async def interaction_check(self, + interaction: discord.Interaction) -> bool: if interaction.user and interaction.user.id == self.author_id: return True else: await interaction.response.send_message( - "This confirmation dialog is not for you.", ephemeral=True - ) + "This confirmation dialog is not for you.", ephemeral=True) return False async def on_timeout(self) -> None: @@ -46,9 +46,8 @@ async def on_timeout(self) -> None: await self.message.delete() @discord.ui.button(label="Confirm", style=discord.ButtonStyle.green) - async def confirm( - self, button: discord.ui.Button, interaction: discord.Interaction - ): + async def confirm(self, button: discord.ui.Button, + interaction: discord.Interaction): self.value = True await interaction.response.defer() if self.delete_after: @@ -56,7 +55,8 @@ async def confirm( self.stop() @discord.ui.button(label="Cancel", style=discord.ButtonStyle.red) - async def cancel(self, button: discord.ui.Button, interaction: discord.Interaction): + async def cancel(self, button: discord.ui.Button, + interaction: discord.Interaction): self.value = False await interaction.response.defer() if self.delete_after: @@ -104,7 +104,7 @@ async def prompt( delete_after: bool = True, reacquire: bool = True, author_id: Optional[int] = None, - channel: Optional[discord.TextChannel] = None + channel: Optional[discord.TextChannel] = None, ) -> Optional[bool]: """An interactive reaction confirmation dialog. Parameters @@ -175,9 +175,9 @@ async def safe_send(self, content, *, escape_mentions=True, **kwargs): if len(content) > 2000: fp = io.BytesIO(content.encode()) kwargs.pop("file", None) - return await self.send( - file=discord.File(fp, filename="message_too_long.txt"), **kwargs - ) + return await self.send(file=discord.File( + fp, filename="message_too_long.txt"), + **kwargs) else: return await self.send(content) @@ -237,12 +237,14 @@ def get_roles(self, role: Union[int, discord.Role]): role = discord.utils.get(self.guild.roles, id=role) return role - def get_emoji(self, emoji: Union[int, discord.Emoji, discord.PartialEmoji]): + def get_emoji(self, emoji: Union[int, discord.Emoji, + discord.PartialEmoji]): if isinstance(emoji, int): emoji = discord.utils.get(self.guild.emojis, id=emoji) return emoji - def get_guild(self, guild: Union[int, discord.Guild, discord.PartialInviteGuild]): + def get_guild(self, guild: Union[int, discord.Guild, + discord.PartialInviteGuild]): if isinstance(guild, int): guild = self.bot.get_guild(guild) return guild @@ -261,12 +263,12 @@ def get_config_emoji_by_name_or_id(self, emoji: Union[int, str]): def get_config_channel_by_name_or_id(self, channel: Union[int, str]): if isinstance(channel, str): guild1 = self.get_guild(ChannelAndMessageId.server_id.value) - channel_model = discord.utils.get( - guild1.text_channels, name=channel) + channel_model = discord.utils.get(guild1.text_channels, + name=channel) if not channel: guild2 = self.get_guild(ChannelAndMessageId.server_id2.value) - channel_model = discord.utils.get( - guild2.text_channels, name=channel) + channel_model = discord.utils.get(guild2.text_channels, + name=channel) return channel_model else: return self.bot.get_channel(channel) @@ -281,9 +283,8 @@ def get_random_image_from_tag(self, tag_name: str) -> Optional[str]: return api_model = TenGiphPy.Giphy(token=Tokens.giphy.value) try: - return api_model.random(str(tag_name.lower()))["data"]["images"][ - "downsized_large" - ]["url"] + return api_model.random(str( + tag_name.lower()))["data"]["images"]["downsized_large"]["url"] except: return @@ -297,9 +298,9 @@ async def get_random_image_from_tag(self, tag_name: str) -> Optional[str]: return api_model = TenGiphPy.Giphy(token=Tokens.giphy.value) try: - return (await api_model.arandom(tag=str(tag_name.lower())))["data"][ - "images" - ]["downsized_large"]["url"] + return (await api_model.arandom( + tag=str(tag_name.lower()) + ))["data"]["images"]["downsized_large"]["url"] except: return @@ -313,9 +314,8 @@ def tenor(self, tag_name: str) -> Optional[str]: def giphy(self, tag_name: str) -> Optional[str]: api_model = TenGiphPy.Giphy(token=Tokens.giphy.value) try: - return api_model.random(str(tag_name.lower()))["data"]["images"][ - "downsized_large" - ]["url"] + return api_model.random(str( + tag_name.lower()))["data"]["images"]["downsized_large"]["url"] except: return @@ -329,8 +329,8 @@ async def tenor(self, tag_name: str) -> Optional[str]: async def giphy(self, tag_name: str) -> Optional[str]: api_model = TenGiphPy.Giphy(token=Tokens.giphy.value) try: - return (await api_model.arandom(tag=str(tag_name.lower())))["data"][ - "images" - ]["downsized_large"]["url"] + return (await api_model.arandom( + tag=str(tag_name.lower()) + ))["data"]["images"]["downsized_large"]["url"] except: return