diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 163a097..e77b9c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,13 @@ repos: - repo: https://github.com/ambv/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black - args: [--line-length=100] + args: [--line-length=88] language_version: python3 - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.270 + rev: v0.0.280 hooks: - id: ruff args: [--line-length=100, --ignore=E501] diff --git a/cogs/configuration.py b/cogs/configuration.py index 84b73b7..64c4b7f 100644 --- a/cogs/configuration.py +++ b/cogs/configuration.py @@ -85,7 +85,9 @@ async def greeter_toggle(self, ctx: commands.Context, value: bool): await util.send_success(ctx, "Greeter is now **disabled**") @greeter.command(name="channel") - async def greeter_channel(self, ctx: commands.Context, *, channel: discord.TextChannel): + async def greeter_channel( + self, ctx: commands.Context, *, channel: discord.TextChannel + ): """Set the greeter channel""" await queries.update_setting(ctx, "greeter_settings", "channel_id", channel.id) await util.send_success(ctx, f"Greeter channel is now {channel.mention}") @@ -132,10 +134,14 @@ async def goodbye_toggle(self, ctx: commands.Context, value: bool): await util.send_success(ctx, "Goodbye messages are now **disabled**") @goodbyemessage.command(name="channel") - async def goodbye_channel(self, ctx: commands.Context, *, channel: discord.TextChannel): + async def goodbye_channel( + self, ctx: commands.Context, *, channel: discord.TextChannel + ): """Set the goodbye message channel""" await queries.update_setting(ctx, "goodbye_settings", "channel_id", channel.id) - await util.send_success(ctx, f"Goodbye messages channel is now {channel.mention}") + await util.send_success( + ctx, f"Goodbye messages channel is now {channel.mention}" + ) @goodbyemessage.command(name="message", usage="") async def goodbye_message(self, ctx: commands.Context, *, message): @@ -171,7 +177,10 @@ async def logger(self, ctx: commands.Context): @logger.command(name="members", usage="") async def logger_members( - self, ctx: commands.Context, *, channel: Annotated[discord.TextChannel, ChannelSetting] + self, + ctx: commands.Context, + *, + channel: Annotated[discord.TextChannel, ChannelSetting], ): """ Set channel for the membership log @@ -188,11 +197,16 @@ async def logger_members( if channel is None: await util.send_success(ctx, "Members logging **disabled**") else: - await util.send_success(ctx, f"Member changes will now be logged to {channel.mention}") + await util.send_success( + ctx, f"Member changes will now be logged to {channel.mention}" + ) @logger.command(name="bans", usage="") async def logger_bans( - self, ctx: commands.Context, *, channel: Annotated[discord.TextChannel, ChannelSetting] + self, + ctx: commands.Context, + *, + channel: Annotated[discord.TextChannel, ChannelSetting], ): """ Set channel where bans are logged @@ -209,7 +223,9 @@ async def logger_bans( if channel is None: await util.send_success(ctx, "Bans logging **disabled**") else: - await util.send_success(ctx, f"Bans will now be logged to {channel.mention}") + await util.send_success( + ctx, f"Bans will now be logged to {channel.mention}" + ) @logger.group(name="deleted") async def logger_deleted(self, ctx: commands.Context): @@ -218,7 +234,10 @@ async def logger_deleted(self, ctx: commands.Context): @logger_deleted.command(name="channel", usage="") async def deleted_channel( - self, ctx: commands.Context, *, channel: Annotated[discord.TextChannel, ChannelSetting] + self, + ctx: commands.Context, + *, + channel: Annotated[discord.TextChannel, ChannelSetting], ): """ Set channel for message log @@ -240,7 +259,9 @@ async def deleted_channel( ) @logger_deleted.command(name="ignore") - async def deleted_ignore(self, ctx: commands.Context, *, channel: discord.TextChannel): + async def deleted_ignore( + self, ctx: commands.Context, *, channel: discord.TextChannel + ): """Ignore a channel from being logged in message log""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -250,10 +271,14 @@ async def deleted_ignore(self, ctx: commands.Context, *, channel: discord.TextCh ctx.guild.id, channel.id, ) - await util.send_success(ctx, f"No longer logging any messages deleted in {channel.mention}") + await util.send_success( + ctx, f"No longer logging any messages deleted in {channel.mention}" + ) @logger_deleted.command(name="unignore") - async def deleted_unignore(self, ctx: commands.Context, *, channel: discord.TextChannel): + async def deleted_unignore( + self, ctx: commands.Context, *, channel: discord.TextChannel + ): """Unignore a channel from being logged in message log""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -276,9 +301,13 @@ async def starboard(self, ctx: commands.Context): await util.command_group_help(ctx) @starboard.command(name="channel") - async def starboard_channel(self, ctx: commands.Context, channel: discord.TextChannel): + async def starboard_channel( + self, ctx: commands.Context, channel: discord.TextChannel + ): """Set the starboard channel""" - await queries.update_setting(ctx, "starboard_settings", "channel_id", channel.id) + await queries.update_setting( + ctx, "starboard_settings", "channel_id", channel.id + ) await util.send_success(ctx, f"Starboard channel is now {channel.mention}") await self.bot.cache.cache_starboard_settings() @@ -288,7 +317,9 @@ async def starboard_amount(self, ctx: commands.Context, amount: int): if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") - await queries.update_setting(ctx, "starboard_settings", "reaction_count", amount) + await queries.update_setting( + ctx, "starboard_settings", "reaction_count", amount + ) emoji_name, emoji_id, emoji_type = await self.bot.db.fetch_row( """ SELECT emoji_name, emoji_id, emoji_type @@ -372,19 +403,29 @@ async def starboard_emoji(self, ctx: commands.Context, emoji): @starboard.command(name="log", usage="") async def starboard_log( - self, ctx: commands.Context, channel: Annotated[discord.TextChannel, ChannelSetting] + self, + ctx: commands.Context, + channel: Annotated[discord.TextChannel, ChannelSetting], ): """Set starboard logging channel to log starring events""" if channel is None: - await queries.update_setting(ctx, "starboard_settings", "log_channel_id", None) + await queries.update_setting( + ctx, "starboard_settings", "log_channel_id", None + ) await util.send_success(ctx, "Starboard log is now disabled") else: - await queries.update_setting(ctx, "starboard_settings", "log_channel_id", channel.id) - await util.send_success(ctx, f"Starboard log channel is now {channel.mention}") + await queries.update_setting( + ctx, "starboard_settings", "log_channel_id", channel.id + ) + await util.send_success( + ctx, f"Starboard log channel is now {channel.mention}" + ) await self.bot.cache.cache_starboard_settings() @starboard.command(name="blacklist") - async def starboard_blacklist(self, ctx: commands.Context, channel: discord.TextChannel): + async def starboard_blacklist( + self, ctx: commands.Context, channel: discord.TextChannel + ): """Blacklist a channel from being counted for starboard""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -399,11 +440,15 @@ async def starboard_blacklist(self, ctx: commands.Context, channel: discord.Text ctx.guild.id, channel.id, ) - await util.send_success(ctx, f"Stars are no longer counted in {channel.mention}") + await util.send_success( + ctx, f"Stars are no longer counted in {channel.mention}" + ) await self.bot.cache.cache_starboard_settings() @starboard.command(name="unblacklist") - async def starboard_unblacklist(self, ctx: commands.Context, channel: discord.TextChannel): + async def starboard_unblacklist( + self, ctx: commands.Context, channel: discord.TextChannel + ): """Unblacklist a channel from being counted for starboard""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -415,7 +460,9 @@ async def starboard_unblacklist(self, ctx: commands.Context, channel: discord.Te ctx.guild.id, channel.id, ) - await util.send_success(ctx, f"Stars are now again counted in {channel.mention}") + await util.send_success( + ctx, f"Stars are now again counted in {channel.mention}" + ) await self.bot.cache.cache_starboard_settings() @starboard.command(name="current") @@ -426,7 +473,9 @@ async def starboard_current(self, ctx: commands.Context): starboard_settings = self.bot.cache.starboard_settings.get(str(ctx.guild.id)) if not starboard_settings: - raise exceptions.CommandWarning("Nothing has been configured on this server yet!") + raise exceptions.CommandWarning( + "Nothing has been configured on this server yet!" + ) ( is_enabled, @@ -446,7 +495,9 @@ async def starboard_current(self, ctx: commands.Context): ctx.guild.id, ) - content = discord.Embed(title=":star: Current starboard settings", color=int("ffac33", 16)) + content = discord.Embed( + title=":star: Current starboard settings", color=int("ffac33", 16) + ) content.add_field( name="State", value=":white_check_mark: Enabled" if is_enabled else ":x: Disabled", @@ -476,7 +527,9 @@ async def starboard_current(self, ctx: commands.Context): async def muterole(self, ctx: commands.Context, *, role: discord.Role): """Set the role given when muting people using the mute command""" await queries.update_setting(ctx, "guild_settings", "mute_role_id", role.id) - await util.send_success(ctx, f"Muting someone now gives them the role {role.mention}") + await util.send_success( + ctx, f"Muting someone now gives them the role {role.mention}" + ) @commands.group() @commands.guild_only() @@ -496,7 +549,9 @@ async def autorole_add(self, ctx: commands.Context, *, role: discord.Role): ctx.guild.id, role.id, ) - await util.send_success(ctx, f"New members will now automatically get {role.mention}") + await util.send_success( + ctx, f"New members will now automatically get {role.mention}" + ) @autorole.command(name="remove") async def autorole_remove(self, ctx: commands.Context, *, role): @@ -557,11 +612,17 @@ async def blacklist(self, ctx: commands.Context): @commands.has_permissions(manage_guild=True) async def blacklist_delete(self, ctx: commands.Context, value: bool): """Toggle whether to delete the message on blacklist trigger""" - await queries.update_setting(ctx, "guild_settings", "delete_blacklisted_usage", value) + await queries.update_setting( + ctx, "guild_settings", "delete_blacklisted_usage", value + ) if value: - await util.send_success(ctx, "Now deleting messages that trigger any blacklists.") + await util.send_success( + ctx, "Now deleting messages that trigger any blacklists." + ) else: - await util.send_success(ctx, "No longer deleting messages that trigger blacklists.") + await util.send_success( + ctx, "No longer deleting messages that trigger blacklists." + ) @blacklist.command(name="show") @commands.has_permissions(manage_guild=True) @@ -639,7 +700,9 @@ async def blacklist_channel(self, ctx: commands.Context, *channels): fails = [] for channel_arg in channels: try: - channel = await commands.TextChannelConverter().convert(ctx, channel_arg) + channel = await commands.TextChannelConverter().convert( + ctx, channel_arg + ) except commands.errors.BadArgument: fails.append(f"Cannot find channel {channel_arg}") else: @@ -707,7 +770,9 @@ async def blacklist_command(self, ctx: commands.Context, *, command): cmd = self.bot.get_command(command) if cmd is None: - raise exceptions.CommandWarning(f"Command `{ctx.prefix}{command}` not found.") + raise exceptions.CommandWarning( + f"Command `{ctx.prefix}{command}` not found." + ) await self.bot.db.execute( "INSERT IGNORE blacklisted_command VALUES (%s, %s)", @@ -715,7 +780,9 @@ async def blacklist_command(self, ctx: commands.Context, *, command): ctx.guild.id, ) try: - self.bot.cache.blacklist[str(ctx.guild.id)]["command"].add(cmd.qualified_name.lower()) + self.bot.cache.blacklist[str(ctx.guild.id)]["command"].add( + cmd.qualified_name.lower() + ) except KeyError: self.bot.cache.blacklist[str(ctx.guild.id)] = { "member": set(), @@ -727,9 +794,13 @@ async def blacklist_command(self, ctx: commands.Context, *, command): @blacklist.command(name="global", hidden=True) @commands.is_owner() - async def blacklist_global(self, ctx: commands.Context, user: discord.User, *, reason): + async def blacklist_global( + self, ctx: commands.Context, user: discord.User, *, reason + ): """Blacklist someone globally from Miso Bot""" - await self.bot.db.execute("INSERT IGNORE blacklisted_user VALUES (%s, %s)", user.id, reason) + await self.bot.db.execute( + "INSERT IGNORE blacklisted_user VALUES (%s, %s)", user.id, reason + ) self.bot.cache.blacklist["global"]["user"].add(user.id) await util.send_success(ctx, f"**{user}** can no longer use Miso Bot!") @@ -764,7 +835,9 @@ async def unblacklist_channel(self, ctx: commands.Context, *channels): fails = [] for channel_arg in channels: try: - channel = await commands.TextChannelConverter().convert(ctx, channel_arg) + channel = await commands.TextChannelConverter().convert( + ctx, channel_arg + ) except commands.errors.BadArgument: fails.append(f"Cannot find channel {channel_arg}") else: @@ -812,7 +885,9 @@ async def unblacklist_command(self, ctx: commands.Context, *, command): cmd = self.bot.get_command(command) if cmd is None: - raise exceptions.CommandWarning(f"Command `{ctx.prefix}{command}` not found.") + raise exceptions.CommandWarning( + f"Command `{ctx.prefix}{command}` not found." + ) await self.bot.db.execute( """ @@ -821,7 +896,9 @@ async def unblacklist_command(self, ctx: commands.Context, *, command): ctx.guild.id, cmd.qualified_name, ) - self.bot.cache.blacklist[str(ctx.guild.id)]["command"].discard(cmd.qualified_name.lower()) + self.bot.cache.blacklist[str(ctx.guild.id)]["command"].discard( + cmd.qualified_name.lower() + ) await util.send_success(ctx, f"`{ctx.prefix}{cmd}` is no longer blacklisted.") @unblacklist.command(name="global", hidden=True) @@ -848,7 +925,9 @@ async def unblacklist_guild(self, ctx: commands.Context, guild_id: int): guild_id, ) self.bot.cache.blacklist["global"]["guild"].discard(guild_id) - await util.send_success(ctx, f"Guild with id `{guild_id}` can use Miso Bot again!") + await util.send_success( + ctx, f"Guild with id `{guild_id}` can use Miso Bot again!" + ) async def setup(bot): diff --git a/cogs/customcommands.py b/cogs/customcommands.py index c3c6570..c480055 100644 --- a/cogs/customcommands.py +++ b/cogs/customcommands.py @@ -58,7 +58,9 @@ async def custom_command_list(self, guild_id, match=""): "SELECT command_trigger FROM custom_command WHERE guild_id = %s", guild_id ) return { - command_trigger for command_trigger in data if match == "" or match in command_trigger + command_trigger + for command_trigger in data + if match == "" or match in command_trigger } @commands.Cog.listener() @@ -107,14 +109,19 @@ async def add(self, ctx: commands.Context, name, *, response): if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") - if not ctx.author.guild_permissions.manage_guild and await self.bot.db.fetch_value( - "SELECT restrict_custom_commands FROM guild_settings WHERE guild_id = %s", - ctx.guild.id, + if ( + not ctx.author.guild_permissions.manage_guild + and await self.bot.db.fetch_value( + "SELECT restrict_custom_commands FROM guild_settings WHERE guild_id = %s", + ctx.guild.id, + ) ): raise commands.MissingPermissions(["manage_server"]) if name in self.bot_command_list(): - raise exceptions.CommandWarning(f"`{ctx.prefix}{name}` is already a built in command!") + raise exceptions.CommandWarning( + f"`{ctx.prefix}{name}` is already a built in command!" + ) if await self.bot.db.fetch_value( "SELECT content FROM custom_command WHERE guild_id = %s AND command_trigger = %s", ctx.guild.id, @@ -153,7 +160,9 @@ async def command_remove(self, ctx: commands.Context, name): ctx.guild.id, ) if not owner_id: - raise exceptions.CommandWarning(f"Custom command `{ctx.prefix}{name}` does not exist") + raise exceptions.CommandWarning( + f"Custom command `{ctx.prefix}{name}` does not exist" + ) owner = ctx.guild.get_member(owner_id) if ( @@ -173,7 +182,9 @@ async def command_remove(self, ctx: commands.Context, name): ctx.guild.id, name, ) - await util.send_success(ctx, f"Custom command `{ctx.prefix}{name}` has been deleted") + await util.send_success( + ctx, f"Custom command `{ctx.prefix}{name}` has been deleted" + ) @command.command(name="search") async def command_search(self, ctx: commands.Context, name): @@ -207,13 +218,16 @@ async def command_list(self, ctx: commands.Context): raise exceptions.CommandError("Unable to get current guild") rows = [ - f"{ctx.prefix}{command}" for command in await self.custom_command_list(ctx.guild.id) + f"{ctx.prefix}{command}" + for command in await self.custom_command_list(ctx.guild.id) ] if rows: content = discord.Embed(title=f"{ctx.guild.name} custom commands") await util.send_as_pages(ctx, content, rows) else: - raise exceptions.CommandInfo("No custom commands have been added on this server yet") + raise exceptions.CommandInfo( + "No custom commands have been added on this server yet" + ) @command.command(name="import") @commands.has_permissions(manage_guild=True) @@ -233,7 +247,9 @@ async def command_import(self, ctx: commands.Context): ``` """ if not ctx.message.attachments: - raise exceptions.CommandWarning("Please attach a `.json` file to the message") + raise exceptions.CommandWarning( + "Please attach a `.json` file to the message" + ) jsonfile = ctx.message.attachments[0] imported = json.loads(await jsonfile.read()) @@ -264,7 +280,9 @@ async def command_export(self, ctx: commands.Context): ctx.guild.id, ) if not data: - raise exceptions.CommandInfo("No custom commands have been added on this server yet") + raise exceptions.CommandInfo( + "No custom commands have been added on this server yet" + ) jsondata = [ { @@ -299,7 +317,10 @@ async def import_command(self, ctx: commands.Context, command: dict): ctx.guild.id, name, ): - return False, f"Custom command `{ctx.prefix}{name}` already exists on this server!" + return ( + False, + f"Custom command `{ctx.prefix}{name}` already exists on this server!", + ) await self.bot.db.execute( "INSERT INTO custom_command VALUES(%s, %s, %s, %s, %s)", @@ -315,7 +336,9 @@ async def import_command(self, ctx: commands.Context, command: dict): @commands.has_permissions(manage_guild=True) async def command_restrict(self, ctx: commands.Context, value: bool): """Restrict command management to only people with manage_server permission""" - await queries.update_setting(ctx, "guild_settings", "restrict_custom_commands", value) + await queries.update_setting( + ctx, "guild_settings", "restrict_custom_commands", value + ) if value: await util.send_success( ctx, "Adding custom commands is now restricted to server managers." @@ -345,7 +368,9 @@ async def command_clear(self, ctx: commands.Context): if count < 1: raise exceptions.CommandWarning("This server has no custom commands yet!") - content = discord.Embed(title=":warning: Are you sure?", color=int("ffcc4d", 16)) + content = discord.Embed( + title=":warning: Are you sure?", color=int("ffcc4d", 16) + ) content.description = ( f"This action will delete all **{count}** custom commands on " "this server and is **irreversible**." @@ -370,7 +395,9 @@ async def cancel(): functions = {"✅": confirm, "❌": cancel} asyncio.ensure_future( - util.reaction_buttons(ctx, msg, functions, only_author=True, single_use=True) + util.reaction_buttons( + ctx, msg, functions, only_author=True, single_use=True + ) ) diff --git a/cogs/errorhandler.py b/cogs/errorhandler.py index 6d1b47d..23476cd 100644 --- a/cogs/errorhandler.py +++ b/cogs/errorhandler.py @@ -8,21 +8,27 @@ import discord from discord.ext import commands from loguru import logger - -from modules import emojis, exceptions, queries, util from modules.instagram import InstagramError from modules.misobot import MisoBot from modules.tiktok import TiktokError +from modules import emojis, exceptions, queries, util + @dataclass class ErrorMessages: - disabled_command = "This command is temporarily disabled, sorry for the inconvenience!" + disabled_command = ( + "This command is temporarily disabled, sorry for the inconvenience!" + ) no_private_message = "This command cannot be used in a DM!" missing_permissions = "You require {0} permission to use this command!" - bot_missing_permissions = "Unable execute command due to missing permissions! (I need {0})" + bot_missing_permissions = ( + "Unable execute command due to missing permissions! (I need {0})" + ) not_donator = "This command is exclusive to Miso Bot donators! Consider donating to get access" - server_too_big = "This command cannot be used in large servers for performance reasons!" + server_too_big = ( + "This command cannot be used in large servers for performance reasons!" + ) not_allowed = "You cannot use this command." max_concurrency = "Stop spamming! >:(" command_on_cooldown = "You are on cooldown! Please wait `{0:.0f} seconds.`" @@ -35,7 +41,9 @@ def __init__(self, bot): self.bot: MisoBot = bot @staticmethod - def log_format(ctx: commands.Context, error: Exception | None, message: str | None = None): + def log_format( + ctx: commands.Context, error: Exception | None, message: str | None = None + ): return ( f"{ctx.guild} @ {ctx.author} : {ctx.message.content} " f"=> {type(error).__name__}: {message or str(error)}" @@ -68,16 +76,26 @@ async def send_embed( **kwargs, ) except discord.Forbidden: - logger.warning(f"403 Forbidden when trying to send error message : {message}") + logger.warning( + f"403 Forbidden when trying to send error message : {message}" + ) async def send_info( - self, ctx: commands.Context, message: str, error: Exception | None = None, **kwargs + self, + ctx: commands.Context, + message: str, + error: Exception | None = None, + **kwargs, ): logger.info(self.log_format(ctx, error, message)) await self.send_embed(ctx, message, ":information_source:", "3b88c3", **kwargs) async def send_warning( - self, ctx: commands.Context, message: str, error: Exception | None = None, **kwargs + self, + ctx: commands.Context, + message: str, + error: Exception | None = None, + **kwargs, ): logger.warning(self.log_format(ctx, error, message)) await self.send_embed(ctx, message, ":warning:", "ffcc4d", **kwargs) @@ -91,9 +109,13 @@ async def send_error( **kwargs, ): logger.error(self.log_format(ctx, error, message)) - await self.send_embed(ctx, f"```{language}\n{message}```", color="be1931", **kwargs) + await self.send_embed( + ctx, f"```{language}\n{message}```", color="be1931", **kwargs + ) - async def send_lastfm_error(self, ctx: commands.Context, error: exceptions.LastFMError): + async def send_lastfm_error( + self, ctx: commands.Context, error: exceptions.LastFMError + ): match error.error_code: case 8: message = ( @@ -112,7 +134,9 @@ async def send_lastfm_error(self, ctx: commands.Context, error: exceptions.LastF await self.send_embed(ctx, message, emojis.LASTFM, "b90000") - async def handle_blacklist(self, ctx: commands.Context, error: exceptions.Blacklist): + async def handle_blacklist( + self, ctx: commands.Context, error: exceptions.Blacklist + ): if ctx.author.id == ctx.bot.owner_id or ( isinstance( error, @@ -145,7 +169,9 @@ async def handle_blacklist(self, ctx: commands.Context, error: exceptions.Blackl await asyncio.sleep(5) await ctx.message.delete() - async def handle_cooldown(self, ctx: commands.Context, error: commands.CommandOnCooldown): + async def handle_cooldown( + self, ctx: commands.Context, error: commands.CommandOnCooldown + ): if ( ctx.author.id == ctx.bot.owner_id or await queries.is_donator(ctx, ctx.author, 2) @@ -161,7 +187,9 @@ async def handle_cooldown(self, ctx: commands.Context, error: commands.CommandOn ) @commands.Cog.listener() - async def on_command_error(self, ctx: commands.Context, error_wrapper: commands.CommandError): + async def on_command_error( + self, ctx: commands.Context, error_wrapper: commands.CommandError + ): """The event triggered when an error is raised while invoking a command""" # extract the original error from the CommandError wrapper @@ -255,7 +283,9 @@ async def on_command_error(self, ctx: commands.Context, error_wrapper: commands. await self.send_warning(ctx, error.message) case _: - await self.send_error(ctx, f"{type(error).__name__}: {error}", error, language="ex") + await self.send_error( + ctx, f"{type(error).__name__}: {error}", error, language="ex" + ) logger.opt(exception=error).error("Unhandled exception traceback:") diff --git a/cogs/events.py b/cogs/events.py index 6a229c4..5c9ea9e 100644 --- a/cogs/events.py +++ b/cogs/events.py @@ -64,7 +64,9 @@ async def on_command_completion(self, ctx: commands.Context): if ctx.guild is not None: await queries.save_command_usage(ctx) - if random.randint(1, 69) == 1 and not await queries.is_donator(ctx, ctx.author): + if random.randint(1, 69) == 1 and not await queries.is_donator( + ctx, ctx.author + ): logger.info("Sending donation beg message") await util.send_donation_beg(ctx.channel) @@ -79,7 +81,9 @@ async def on_guild_join(self, guild): guild.id, ) if blacklisted: - logger.info(f"Tried to join guild {guild}. Reason for blacklist: {blacklisted}") + logger.info( + f"Tried to join guild {guild}. Reason for blacklist: {blacklisted}" + ) return await guild.leave() logger.info(f"New guild : {guild}") @@ -129,7 +133,9 @@ async def on_member_join(self, member): """Called when a new member joins a guild""" await self.bot.wait_until_ready() logging_channel_id = None - if logging_settings := self.bot.cache.logging_settings.get(str(member.guild.id)): + if logging_settings := self.bot.cache.logging_settings.get( + str(member.guild.id) + ): logging_channel_id = logging_settings.get("member_log_channel_id") if logging_channel_id: @@ -168,7 +174,9 @@ async def on_member_join(self, member): if greeter_channel is not None: try: await greeter_channel.send( - embed=util.create_welcome_embed(member, member.guild, message_format) + embed=util.create_welcome_embed( + member, member.guild, message_format + ) ) except discord.errors.Forbidden: pass @@ -200,7 +208,9 @@ async def on_member_remove(self, member): """Called when member leaves a guild""" await self.bot.wait_until_ready() logging_channel_id = None - if logging_settings := self.bot.cache.logging_settings.get(str(member.guild.id)): + if logging_settings := self.bot.cache.logging_settings.get( + str(member.guild.id) + ): logging_channel_id = logging_settings.get("member_log_channel_id") if logging_channel_id: @@ -231,7 +241,9 @@ async def on_member_remove(self, member): try: await channel.send( - util.create_goodbye_message(member, member.guild, message_format) + util.create_goodbye_message( + member, member.guild, message_format + ) ) except discord.errors.Forbidden: pass @@ -262,7 +274,9 @@ async def on_raw_message_delete(self, payload): return channel_id = None - if logging_settings := self.bot.cache.logging_settings.get(str(message.guild.id)): + if logging_settings := self.bot.cache.logging_settings.get( + str(message.guild.id) + ): channel_id = logging_settings.get("message_log_channel_id") if channel_id: log_channel = message.guild.get_channel(channel_id) @@ -320,7 +334,9 @@ async def get_autoembed_options( return None, None - async def parse_media_auto_embed(self, message: discord.Message, media_settings: dict): + async def parse_media_auto_embed( + self, message: discord.Message, media_settings: dict + ): if media_settings["instagram"]: embedder = InstagramEmbedder(self.bot) posts = embedder.extract_links(message.content, include_shortcodes=False) @@ -430,7 +446,9 @@ async def on_raw_reaction_add(self, payload): if payload.channel_id in self.bot.cache.starboard_blacklisted_channels: return - starboard_settings = self.bot.cache.starboard_settings.get(str(payload.guild_id)) + starboard_settings = self.bot.cache.starboard_settings.get( + str(payload.guild_id) + ) if not starboard_settings: return @@ -499,7 +517,9 @@ async def on_raw_reaction_add(self, payload): payload.message_id, ) emoji_display = ( - "⭐" if emoji_type == "custom" else emoji_literals.NAME_TO_UNICODE[emoji_name] + "⭐" + if emoji_type == "custom" + else emoji_literals.NAME_TO_UNICODE[emoji_name] ) board_message = None @@ -549,7 +569,9 @@ async def on_raw_reaction_add(self, payload): value="\n".join(str(x) for x in reacted_users)[:1023], inline=False, ) - log_content.add_field(name="Most recent reaction by", value=str(user)) + log_content.add_field( + name="Most recent reaction by", value=str(user) + ) try: await log_channel.send(embed=log_content) except discord.HTTPException: @@ -570,7 +592,9 @@ def starboard_embed(message: discord.Message, reaction_count: int, emoji: str): name=f"{message.author}", icon_url=message.author.display_avatar.url, ) - content.set_footer(text=f"{reaction_count} {emoji} {util.displaychannel(message.channel)}") + content.set_footer( + text=f"{reaction_count} {emoji} {util.displaychannel(message.channel)}" + ) if message.attachments: content.set_image(url=message.attachments[0].url) diff --git a/cogs/fishy.py b/cogs/fishy.py index 2b7d359..dcdcae8 100644 --- a/cogs/fishy.py +++ b/cogs/fishy.py @@ -8,9 +8,9 @@ import discord import humanize from discord.ext import commands +from modules.misobot import MisoBot from modules import exceptions, util -from modules.misobot import MisoBot class Fishy(commands.Cog): @@ -105,7 +105,9 @@ def __init__(self, bot): # idk why this doesnt work but it gets stuck all the time # @commands.max_concurrency(1, per=commands.BucketType.user) @commands.cooldown(1, 5, type=commands.BucketType.user) - @commands.command(aliases=["fish", "fihy", "fisy", "foshy", "fisyh", "fsihy", "fin", "fush"]) + @commands.command( + aliases=["fish", "fihy", "fisy", "foshy", "fisyh", "fsihy", "fin", "fush"] + ) async def fishy(self, ctx: commands.Context, user: Optional[discord.Member] = None): """Go fishing""" receiver = user or ctx.author @@ -126,7 +128,9 @@ async def fishy(self, ctx: commands.Context, user: Optional[discord.Member] = No else: last_fishy = cached_last_fishy if last_fishy: - time_since_fishy = ctx.message.created_at.timestamp() - last_fishy.timestamp() + time_since_fishy = ( + ctx.message.created_at.timestamp() - last_fishy.timestamp() + ) else: time_since_fishy = self.COOLDOWN @@ -180,12 +184,18 @@ async def fishytimer(self, ctx: commands.Context): ctx.author.id, ) if last_fishy: - time_since_fishy = ctx.message.created_at.timestamp() - last_fishy.timestamp() + time_since_fishy = ( + ctx.message.created_at.timestamp() - last_fishy.timestamp() + ) if time_since_fishy < self.COOLDOWN: remaining = self.COOLDOWN - time_since_fishy wait_time = humanize.precisedelta(remaining) - clock_face = f":clock{int(util.map_to_range(remaining, 7200, 0, 1, 12))}:" - await ctx.send(f"{clock_face} You need to wait **{wait_time}** to fish again.") + clock_face = ( + f":clock{int(util.map_to_range(remaining, 7200, 0, 1, 12))}:" + ) + await ctx.send( + f"{clock_face} You need to wait **{wait_time}** to fish again." + ) else: await ctx.send(":sparkles: Good news! You can fish right now!") else: diff --git a/cogs/information.py b/cogs/information.py index df642c3..d63d80b 100644 --- a/cogs/information.py +++ b/cogs/information.py @@ -70,7 +70,9 @@ async def donate(self, ctx: commands.Context): value="`ltc1qsxmy8q8ptdlhdcamypa2uspj8zf8m0ukua9vn5`", inline=False, ) - content.set_footer(text="Donations will be used to pay for server and upkeep costs") + content.set_footer( + text="Donations will be used to pay for server and upkeep costs" + ) await ctx.send(embed=content) @commands.command(aliases=["patrons", "supporters", "sponsors"]) @@ -94,7 +96,9 @@ async def donators(self, ctx: commands.Context): donators.append(f"**{user}**") n = 20 - chunks = [donators[i * n : (i + 1) * n] for i in range((len(donators) + n - 1) // n)] + chunks = [ + donators[i * n : (i + 1) * n] for i in range((len(donators) + n - 1) // n) + ] if not donators: raise exceptions.CommandInfo( @@ -140,8 +144,12 @@ async def info(self, ctx: commands.Context): ) content.set_thumbnail(url=self.bot.user.display_avatar.url) content.add_field(name="Website", value="https://misobot.xyz", inline=False) - content.add_field(name="Github", value="https://github.com/joinemm/miso-bot", inline=False) - content.add_field(name="Discord", value="https://discord.gg/RzDW3Ne", inline=False) + content.add_field( + name="Github", value="https://github.com/joinemm/miso-bot", inline=False + ) + content.add_field( + name="Discord", value="https://discord.gg/RzDW3Ne", inline=False + ) data = await self.get_commits("joinemm", "miso-bot") last_update = data[0]["commit"]["author"].get("date") @@ -153,7 +161,9 @@ async def info(self, ctx: commands.Context): async def ping(self, ctx: commands.Context): """Get the bot's ping""" test_message = await ctx.send(":ping_pong:") - cmd_lat = (test_message.created_at - ctx.message.created_at).total_seconds() * 1000 + cmd_lat = ( + test_message.created_at - ctx.message.created_at + ).total_seconds() * 1000 discord_lat = self.bot.latency * 1000 content = discord.Embed( colour=discord.Color.red(), @@ -193,10 +203,18 @@ async def shardinfo(self, ctx: commands.Context): content = discord.Embed(title=f"Running {len(self.bot.shards)} shards") shards = [] for shard in self.bot.shards.values(): - emoji = emojis.Status["offline"] if shard.is_closed() else emojis.Status["online"] + emoji = ( + emojis.Status["offline"] + if shard.is_closed() + else emojis.Status["online"] + ) shards.append( f"{emoji.value} **Shard `{shard.id}`** - `{shard.latency * 1000:.2f}` ms" - + (" :point_left:" if ctx.guild and ctx.guild.shard_id == shard.id else "") + + ( + " :point_left:" + if ctx.guild and ctx.guild.shard_id == shard.id + else "" + ) ) content.description = "\n".join(shards) @@ -258,7 +276,9 @@ async def roleinfo(self, ctx: commands.Context, *, role: discord.Role): content.colour = role.color member_count = len(role.members) percentage = ( - int(member_count / ctx.guild.member_count * 100) if ctx.guild.member_count else None + int(member_count / ctx.guild.member_count * 100) + if ctx.guild.member_count + else None ) if isinstance(role.icon, discord.Asset): @@ -273,7 +293,9 @@ async def roleinfo(self, ctx: commands.Context, *, role: discord.Role): if ctx.guild.member_count else member_count, ) - content.add_field(name="Created at", value=role.created_at.strftime("%d/%m/%Y %H:%M")) + content.add_field( + name="Created at", value=role.created_at.strftime("%d/%m/%Y %H:%M") + ) content.add_field(name="Hoisted", value=str(role.hoist)) content.add_field(name="Mentionable", value=role.mentionable) content.add_field(name="Mention", value=role.mention) @@ -288,8 +310,12 @@ async def roleinfo(self, ctx: commands.Context, *, role: discord.Role): manager = "UNKNOWN" content.add_field(name="Managed by", value=manager) - if perms := [f"`{perm.upper()}`" for perm, allow in iter(role.permissions) if allow]: - content.add_field(name="Allowed permissions", value=" ".join(perms), inline=False) + if perms := [ + f"`{perm.upper()}`" for perm, allow in iter(role.permissions) if allow + ]: + content.add_field( + name="Allowed permissions", value=" ".join(perms), inline=False + ) await ctx.send(embed=content) @@ -357,7 +383,8 @@ async def commandstats_global( ): """Most used commands globally""" content = discord.Embed( - title=":bar_chart: Most used commands" + ("" if user is None else f" by {user}") + title=":bar_chart: Most used commands" + + ("" if user is None else f" by {user}") ) opt = [user.id] if user is not None else [] data = await self.bot.db.fetch( @@ -396,9 +423,13 @@ async def commandstats_single(self, ctx: commands.Context, command_name): """Stats of a single command""" command = self.bot.get_command(command_name) if command is None: - raise exceptions.CommandInfo(f"Command `{ctx.prefix}{command_name}` does not exist!") + raise exceptions.CommandInfo( + f"Command `{ctx.prefix}{command_name}` does not exist!" + ) - content = discord.Embed(title=f":bar_chart: `{ctx.prefix}{command.qualified_name}`") + content = discord.Embed( + title=f":bar_chart: `{ctx.prefix}{command.qualified_name}`" + ) # set command name to be tuple of subcommands if this is a command group group = hasattr(command, "commands") @@ -479,7 +510,9 @@ async def commandstats_single(self, ctx: commands.Context, command_name): # additional data for command groups if group: content.description = "Command Group" - subcommands_tuple = tuple(f"{command.name} {x.name}" for x in command.commands) + subcommands_tuple = tuple( + f"{command.name} {x.name}" for x in command.commands + ) subcommand_usage = await self.bot.db.fetch( """ SELECT command_name, SUM(uses) FROM command_usage diff --git a/cogs/kpop.py b/cogs/kpop.py index a668d2f..515fd84 100644 --- a/cogs/kpop.py +++ b/cogs/kpop.py @@ -13,9 +13,9 @@ import humanize from bs4 import BeautifulSoup from discord.ext import commands +from modules.misobot import MisoBot from modules import exceptions, util -from modules.misobot import MisoBot class Kpop(commands.Cog): @@ -38,7 +38,9 @@ async def shutdown(self): async def google_image_search(self, keyword): try: - results = await self.google_client.search(keyword, safesearch=False, image_search=True) + results = await self.google_client.search( + keyword, safesearch=False, image_search=True + ) except async_cse.search.APIError: return "" return results[0].image_url if results else "" @@ -158,7 +160,9 @@ async def send_idol(self, ctx: commands.Context, idol_id): ) content.set_image(url=image_url) content.add_field(name="Full name", value=full_name) - content.add_field(name="Korean name", value=f"{korean_stage_name} ({korean_name})") + content.add_field( + name="Korean name", value=f"{korean_stage_name} ({korean_name})" + ) content.add_field( name="Birthday", value=arrow.get(date_of_birth).format("YYYY-MM-DD") + f" (age {age})", @@ -208,11 +212,17 @@ async def scrape(category, url): artists = [] async with self.bot.session.get(url) as response: soup = BeautifulSoup(await response.text(), "lxml") - content = soup.find("div", {"class": "entry-content herald-entry-content"}) + content = soup.find( + "div", {"class": "entry-content herald-entry-content"} + ) outer = content.find_all("p") # type: ignore for p in outer: for artist in p.find_all("a"): - artist = artist.text.replace("Profile", "").replace("profile", "").strip() + artist = ( + artist.text.replace("Profile", "") + .replace("profile", "") + .strip() + ) if artist != "": artists.append([artist, category]) return artists @@ -232,7 +242,8 @@ async def scrape(category, url): await util.send_success( ctx, - f"**Artist list updated**\n" f"Stannable artist count: **{len(new_artist_list)}**", + f"**Artist list updated**\n" + f"Stannable artist count: **{len(new_artist_list)}**", ) @commands.is_owner() diff --git a/cogs/lastfm.py b/cogs/lastfm.py index 5b47670..c262557 100644 --- a/cogs/lastfm.py +++ b/cogs/lastfm.py @@ -136,7 +136,9 @@ async def fm_blacklist_add(self, ctx: commands.Context, *, member: discord.Membe ) @fm_blacklist.command(name="remove") - async def fm_blacklist_remove(self, ctx: commands.Context, *, member: discord.Member): + async def fm_blacklist_remove( + self, ctx: commands.Context, *, member: discord.Member + ): """Remove a member from the blacklist""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -154,11 +156,15 @@ async def fm_blacklist_remove(self, ctx: commands.Context, *, member: discord.Me async def set(self, ctx: commands.Context, username): """Save your Last.fm username""" if ctx.foreign_target: # type: ignore - raise exceptions.CommandWarning("You cannot set Last.fm username for someone else!") + raise exceptions.CommandWarning( + "You cannot set Last.fm username for someone else!" + ) content = await self.get_userinfo_embed(username) if content is None: - raise exceptions.CommandWarning(f"Last.fm profile `{username}` was not found") + raise exceptions.CommandWarning( + f"Last.fm profile `{username}` was not found" + ) await self.bot.db.execute( """ @@ -179,7 +185,9 @@ async def set(self, ctx: commands.Context, username): async def unset(self, ctx: commands.Context): """Unlink your Last.fm""" if ctx.foreign_target: # type: ignore - raise exceptions.CommandWarning("You cannot unset someone else's Last.fm username!") + raise exceptions.CommandWarning( + "You cannot unset someone else's Last.fm username!" + ) await self.bot.db.execute( """ @@ -198,7 +206,9 @@ async def profile(self, ctx: commands.Context): """See your Last.fm profile""" content = await self.get_userinfo_embed(ctx.username) if content is None: - raise exceptions.CommandError(f"Could not get your lastfm profile (`{ctx.username}`)") + raise exceptions.CommandError( + f"Could not get your lastfm profile (`{ctx.username}`)" + ) await ctx.send(embed=content) # type: ignore @fm.command() @@ -577,7 +587,9 @@ async def recent(self, ctx: commands.Context, size: int = 15): await util.send_as_pages(ctx, content, rows, 15) @fm.command(usage="[timeframe] ") - async def artist(self, ctx: commands.Context, timeframe, datatype, *, artistname=""): + async def artist( + self, ctx: commands.Context, timeframe, datatype, *, artistname="" + ): """ Artist specific data. @@ -596,7 +608,9 @@ async def artist(self, ctx: commands.Context, timeframe, datatype, *, artistname if artistname.lower() == "np": artistname = (await self.getnowplaying(ctx))["artist"] if artistname is None: - raise exceptions.CommandWarning("Could not get currently playing artist!") + raise exceptions.CommandWarning( + "Could not get currently playing artist!" + ) if artistname == "": return await ctx.send("Missing artist name!") @@ -680,14 +694,18 @@ async def album(self, ctx: commands.Context, *, album): albumname = npd["album"] artistname = npd["artist"] if None in [albumname, artistname]: - raise exceptions.CommandWarning("Could not get currently playing album!") + raise exceptions.CommandWarning( + "Could not get currently playing album!" + ) else: try: albumname, artistname = [x.strip() for x in album.split("|")] if "" in (albumname, artistname): raise ValueError except ValueError: - raise exceptions.CommandWarning("Incorrect format! use `album | artist`") + raise exceptions.CommandWarning( + "Incorrect format! use `album | artist`" + ) album, data = await self.album_top_tracks(ctx, period, artistname, albumname) if album is None or not data: @@ -728,7 +746,9 @@ async def album(self, ctx: commands.Context, *, album): await util.send_as_pages(ctx, content, rows) - async def album_top_tracks(self, ctx: commands.Context, period, artistname, albumname): + async def album_top_tracks( + self, ctx: commands.Context, period, artistname, albumname + ): """Scrape the top tracks of given album from lastfm library page""" artistname = urllib.parse.quote_plus(artistname) albumname = urllib.parse.quote_plus(albumname) @@ -747,7 +767,9 @@ async def album_top_tracks(self, ctx: commands.Context, period, artistname, albu .find("img") .get("src") .replace("64s", "300s"), - "formatted_name": soup.find("h2", {"class": "library-header-title"}).text.strip(), + "formatted_name": soup.find( + "h2", {"class": "library-header-title"} + ).text.strip(), "artist": soup.find("header", {"class": "library-header"}) .find("a", {"class": "text-colour-link"}) .text.strip(), @@ -776,7 +798,9 @@ async def artist_top(self, ctx: commands.Context, period, artistname, datatype): .find("img") .get("src") .replace("avatar70s", "avatar300s"), - "formatted_name": soup.find("a", {"class": "library-header-crumb"}).text.strip(), + "formatted_name": soup.find( + "a", {"class": "library-header-crumb"} + ).text.strip(), } all_results = get_list_contents(soup) @@ -789,7 +813,9 @@ async def artist_overview(self, ctx: commands.Context, period, artistname): albums = [] tracks = [] metadata = [None, None, None] - artistinfo = await self.api_request({"method": "artist.getInfo", "artist": artistname}) + artistinfo = await self.api_request( + {"method": "artist.getInfo", "artist": artistname} + ) url = ( f"https://last.fm/user/{ctx.username}/library/music/" f"{urllib.parse.quote_plus(artistname)}" @@ -801,7 +827,9 @@ async def artist_overview(self, ctx: commands.Context, period, artistname): soup = BeautifulSoup(data, "lxml") try: - albumsdiv, tracksdiv, _ = soup.findAll("tbody", {"data-playlisting-add-entries": ""}) + albumsdiv, tracksdiv, _ = soup.findAll( + "tbody", {"data-playlisting-add-entries": ""} + ) except ValueError: artistname = discord.utils.escape_markdown(artistname) @@ -814,7 +842,9 @@ async def artist_overview(self, ctx: commands.Context, period, artistname): for container, destination in zip([albumsdiv, tracksdiv], [albums, tracks]): items = container.findAll("tr", {"class": "chartlist-row"}) for item in items: - name = item.find("td", {"class": "chartlist-name"}).find("a").get("title") + name = ( + item.find("td", {"class": "chartlist-name"}).find("a").get("title") + ) playcount = ( item.find("span", {"class": "chartlist-count-bar-value"}) .text.replace("scrobbles", "") @@ -834,7 +864,9 @@ async def artist_overview(self, ctx: commands.Context, period, artistname): .find("img") .get("src") .replace("avatar70s", "avatar300s"), - "formatted_name": soup.find("h2", {"class": "library-header-title"}).text.strip(), + "formatted_name": soup.find( + "h2", {"class": "library-header-title"} + ).text.strip(), } artistname = urllib.parse.quote_plus(artistname) @@ -889,7 +921,9 @@ async def artist_overview(self, ctx: commands.Context, period, artistname): ) if similar: - content.add_field(name="Similar artists", value=", ".join(similar), inline=False) + content.add_field( + name="Similar artists", value=", ".join(similar), inline=False + ) await ctx.send(embed=content) @@ -910,7 +944,9 @@ async def get_image(url): if image is None: return None - colors = await self.bot.loop.run_in_executor(None, lambda: colorgram.extract(image, 1)) + colors = await self.bot.loop.run_in_executor( + None, lambda: colorgram.extract(image, 1) + ) dominant_color = colors[0].rgb return ( @@ -982,7 +1018,9 @@ async def colorchart(self, ctx: commands.Context, colour, size="3x3"): albums.add(album_art_id) if not albums: - raise exceptions.CommandError("There was an unknown error while getting your albums!") + raise exceptions.CommandError( + "There was an unknown error while getting your albums!" + ) to_fetch = [] albumcolors = await self.bot.db.fetch( @@ -1006,7 +1044,9 @@ async def colorchart(self, ctx: commands.Context, colour, size="3x3"): if to_fetch: to_cache = [] - tasks = [self.fetch_color(self.bot.session, image_id) for image_id in to_fetch] + tasks = [ + self.fetch_color(self.bot.session, image_id) for image_id in to_fetch + ] if len(tasks) > 500: warn = await ctx.send( ":exclamation:Your library includes over 500 uncached album colours, " @@ -1054,7 +1094,9 @@ async def colorchart(self, ctx: commands.Context, colour, size="3x3"): (148, 0, 211), # violet ] ) - chunks = [list(tree.search_knn(rgb, width + height)) for rgb in rainbow_colors] + chunks = [ + list(tree.search_knn(rgb, width + height)) for rgb in rainbow_colors + ] random_offset = random.randint(0, 6) final_albums = [] for album_index in range(width * height): @@ -1085,7 +1127,9 @@ async def colorchart(self, ctx: commands.Context, colour, size="3x3"): for alb in nearest ] - buffer = await self.chart_factory(final_albums, width, height, show_labels=False) + buffer = await self.chart_factory( + final_albums, width, height, show_labels=False + ) if rainbow: colour = f"{'diagonal ' if diagonal else ''}rainbow" @@ -1102,7 +1146,9 @@ async def colorchart(self, ctx: commands.Context, colour, size="3x3"): if warn is not None: await warn.delete() - @fm.command(aliases=["collage"], usage="[album | artist] [timeframe] [size] 'notitle'") + @fm.command( + aliases=["collage"], usage="[album | artist] [timeframe] [size] 'notitle'" + ) async def chart(self, ctx: commands.Context, *args): """ Collage of your top albums or artists @@ -1156,7 +1202,9 @@ async def chart(self, ctx: commands.Context, *args): for i, artist in enumerate(artists): name = artist["name"] plays = artist["playcount"] - chart.append((scraped_images[i], f"{plays} {format_plays(plays)}
{name}")) + chart.append( + (scraped_images[i], f"{plays} {format_plays(plays)}
{name}") + ) elif arguments["method"] == "user.getrecenttracks": chart_type = "recent tracks" @@ -1183,11 +1231,15 @@ async def chart(self, ctx: commands.Context, *args): async def chart_factory(self, chart_items, width, height, show_labels=True): if show_labels: - img_div_template = '

{1}

' + img_div_template = ( + '

{1}

' + ) else: img_div_template = '
' - img_divs = "\n".join(img_div_template.format(*chart_item) for chart_item in chart_items) + img_divs = "\n".join( + img_div_template.format(*chart_item) for chart_item in chart_items + ) replacements = { "WIDTH": 300 * width, @@ -1204,7 +1256,9 @@ async def chart_factory(self, chart_items, width, height, show_labels=True): return await util.render_html(self.bot, payload) - async def server_lastfm_usernames(self, ctx: commands.Context, filter_blacklisted=False): + async def server_lastfm_usernames( + self, ctx: commands.Context, filter_blacklisted=False + ): guild_user_ids = [user.id for user in ctx.guild.members] args = [guild_user_ids] if filter_blacklisted: @@ -1232,7 +1286,9 @@ async def server(self, ctx: commands.Context): await util.command_group_help(ctx) @server.command( - name="chart", aliases=["collage"], usage="[album | artist] [timeframe] [size] 'notitle'" + name="chart", + aliases=["collage"], + usage="[album | artist] [timeframe] [size] 'notitle'", ) async def server_chart(self, ctx: commands.Context, *args): """ @@ -1268,7 +1324,9 @@ async def server_chart(self, ctx: commands.Context, *args): chart_type = "ERROR" content_map = {} if not tasks: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) data = await asyncio.gather(*tasks) chart = [] @@ -1286,7 +1344,10 @@ async def server_chart(self, ctx: commands.Context, *args): if name in content_map: content_map[name]["plays"] += plays else: - content_map[name] = {"plays": plays, "image": album["image"][3]["#text"]} + content_map[name] = { + "plays": plays, + "image": album["image"][3]["#text"], + } elif arguments["method"] == "user.gettopartists": chart_type = "top artist" @@ -1344,12 +1405,18 @@ async def server_nowplaying(self, ctx: commands.Context): total_linked = len(tasks) if not tasks: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) data = await asyncio.gather(*tasks) - listeners = [(song, member_ref) for song, member_ref in data if song is not None] + listeners = [ + (song, member_ref) for song, member_ref in data if song is not None + ] if not listeners: - return await ctx.send("Nobody on this server is listening to anything at the moment!") + return await ctx.send( + "Nobody on this server is listening to anything at the moment!" + ) total_listening = len(listeners) maxlen = 0 @@ -1399,12 +1466,18 @@ async def server_recent(self, ctx: commands.Context): total_listening += 1 listeners.append((song, member_ref)) else: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) if not listeners: - return await ctx.send("Nobody on this server is listening to anything at the moment!") + return await ctx.send( + "Nobody on this server is listening to anything at the moment!" + ) - listeners = sorted(listeners, key=lambda listener: listener[0].get("date"), reverse=True) + listeners = sorted( + listeners, key=lambda listener: listener[0].get("date"), reverse=True + ) rows = [] for song, member in listeners: suffix = "" @@ -1447,7 +1520,11 @@ async def server_topartists(self, ctx: commands.Context, *args): if member is None: continue - tasks.append(self.get_server_top(lastfm_username, "artist", period=arguments["period"])) + tasks.append( + self.get_server_top( + lastfm_username, "artist", period=arguments["period"] + ) + ) if tasks: data = await asyncio.gather(*tasks) @@ -1464,7 +1541,9 @@ async def server_topartists(self, ctx: commands.Context, *args): else: artist_map[name] = plays else: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) rows = [] formatted_timeframe = humanized_period(arguments["period"]).capitalize() @@ -1473,7 +1552,9 @@ async def server_topartists(self, ctx: commands.Context, *args): name=f"{ctx.guild} — {formatted_timeframe} top artists", icon_url=ctx.guild.icon, ) - content.set_footer(text=f"Taking into account top 100 artists of {total_users} members") + content.set_footer( + text=f"Taking into account top 100 artists of {total_users} members" + ) for i, (artistname, playcount) in enumerate( sorted(artist_map.items(), key=lambda x: x[1], reverse=True), start=1 ): @@ -1503,7 +1584,11 @@ async def server_topalbums(self, ctx: commands.Context, *args): if member is None: continue - tasks.append(self.get_server_top(lastfm_username, "album", period=arguments["period"])) + tasks.append( + self.get_server_top( + lastfm_username, "album", period=arguments["period"] + ) + ) if tasks: data = await asyncio.gather(*tasks) @@ -1521,7 +1606,9 @@ async def server_topalbums(self, ctx: commands.Context, *args): else: album_map[name] = {"plays": plays, "image": image_url} else: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) rows = [] formatted_timeframe = humanized_period(arguments["period"]).capitalize() @@ -1530,7 +1617,9 @@ async def server_topalbums(self, ctx: commands.Context, *args): name=f"{ctx.guild} — {formatted_timeframe} top albums", icon_url=ctx.guild.icon, ) - content.set_footer(text=f"Taking into account top 100 albums of {total_users} members") + content.set_footer( + text=f"Taking into account top 100 albums of {total_users} members" + ) for i, (albumname, albumdata) in enumerate( sorted(album_map.items(), key=lambda x: x[1]["plays"], reverse=True), start=1, @@ -1541,7 +1630,9 @@ async def server_topalbums(self, ctx: commands.Context, *args): content.set_thumbnail(url=image_url) playcount = albumdata["plays"] - rows.append(f"`#{i:2}` **{playcount}** {format_plays(playcount)} : **{albumname}**") + rows.append( + f"`#{i:2}` **{playcount}** {format_plays(playcount)} : **{albumname}**" + ) await util.send_as_pages(ctx, content, rows, 15) @@ -1560,7 +1651,11 @@ async def server_toptracks(self, ctx: commands.Context, *args): if member is None: continue - tasks.append(self.get_server_top(lastfm_username, "track", period=arguments["period"])) + tasks.append( + self.get_server_top( + lastfm_username, "track", period=arguments["period"] + ) + ) if tasks: data = await asyncio.gather(*tasks) @@ -1578,7 +1673,9 @@ async def server_toptracks(self, ctx: commands.Context, *args): else: track_map[name] = {"plays": plays, "artist": artistname} else: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) rows = [] formatted_timeframe = humanized_period(arguments["period"]).capitalize() @@ -1587,7 +1684,9 @@ async def server_toptracks(self, ctx: commands.Context, *args): name=f"{ctx.guild} — {formatted_timeframe} top tracks", icon_url=ctx.guild.icon, ) - content.set_footer(text=f"Taking into account top 100 tracks of {total_users} members") + content.set_footer( + text=f"Taking into account top 100 tracks of {total_users} members" + ) for i, (trackname, trackdata) in enumerate( sorted(track_map.items(), key=lambda x: x[1]["plays"], reverse=True), start=1, @@ -1598,7 +1697,9 @@ async def server_toptracks(self, ctx: commands.Context, *args): content.set_thumbnail(url=image_url) playcount = trackdata["plays"] - rows.append(f"`#{i:2}` **{playcount}** {format_plays(playcount)} : **{trackname}**") + rows.append( + f"`#{i:2}` **{playcount}** {format_plays(playcount)} : **{trackname}**" + ) await util.send_as_pages(ctx, content, rows, 15) @@ -1654,7 +1755,9 @@ async def whoknows(self, ctx: commands.Context, *, artistname): if artistname.lower() == "np": artistname = (await self.getnowplaying(ctx))["artist"] if artistname is None: - raise exceptions.CommandWarning("Could not get currently playing artist!") + raise exceptions.CommandWarning( + "Could not get currently playing artist!" + ) listeners = [] tasks = [] @@ -1674,7 +1777,9 @@ async def whoknows(self, ctx: commands.Context, *, artistname): if playcount > 0: listeners.append((playcount, member)) else: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) artistname = discord.utils.escape_markdown(artistname) @@ -1716,7 +1821,9 @@ async def whoknows(self, ctx: commands.Context, *, artistname): total += playcount if not rows: - return await ctx.send(f"Nobody on this server has listened to **{artistname}**") + return await ctx.send( + f"Nobody on this server has listened to **{artistname}**" + ) content = discord.Embed(title=f"Who knows **{artistname}**?") image_url = await self.get_artist_image(artistname) @@ -1733,7 +1840,9 @@ async def whoknows(self, ctx: commands.Context, *, artistname): f"> **{util.displayname(new_king)}** just stole the **{artistname}** crown from **{util.displayname(old_king)}**" ) - @commands.command(aliases=["wkt", "whomstknowstrack"], usage=" | 'np'") + @commands.command( + aliases=["wkt", "whomstknowstrack"], usage=" | 'np'" + ) @commands.guild_only() @is_small_server() @commands.cooldown(2, 60, type=commands.BucketType.user) @@ -1751,14 +1860,18 @@ async def whoknowstrack(self, ctx: commands.Context, *, track): trackname = npd["track"] artistname = npd["artist"] if None in [trackname, artistname]: - raise exceptions.CommandWarning("Could not get currently playing track!") + raise exceptions.CommandWarning( + "Could not get currently playing track!" + ) else: try: trackname, artistname = [x.strip() for x in track.split("|")] if "" in (trackname, artistname): raise ValueError except ValueError: - raise exceptions.CommandWarning("Incorrect format! use `track | artist`") + raise exceptions.CommandWarning( + "Incorrect format! use `track | artist`" + ) listeners = [] tasks = [] @@ -1769,7 +1882,9 @@ async def whoknowstrack(self, ctx: commands.Context, *, track): if member is None: continue - tasks.append(self.get_playcount_track(artistname, trackname, lastfm_username, member)) + tasks.append( + self.get_playcount_track(artistname, trackname, lastfm_username, member) + ) if tasks: data = await asyncio.gather(*tasks) @@ -1778,7 +1893,9 @@ async def whoknowstrack(self, ctx: commands.Context, *, track): if playcount > 0: listeners.append((playcount, user)) else: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) artistname = discord.utils.escape_markdown(artistname) trackname = discord.utils.escape_markdown(trackname) @@ -1809,7 +1926,9 @@ async def whoknowstrack(self, ctx: commands.Context, *, track): await util.send_as_pages(ctx, content, rows) - @commands.command(aliases=["wka", "whomstknowsalbum"], usage=" | 'np'") + @commands.command( + aliases=["wka", "whomstknowsalbum"], usage=" | 'np'" + ) @commands.guild_only() @is_small_server() @commands.cooldown(2, 60, type=commands.BucketType.user) @@ -1827,14 +1946,18 @@ async def whoknowsalbum(self, ctx: commands.Context, *, album): albumname = npd["album"] artistname = npd["artist"] if None in [albumname, artistname]: - raise exceptions.CommandWarning("Could not get currently playing album!") + raise exceptions.CommandWarning( + "Could not get currently playing album!" + ) else: try: albumname, artistname = [x.strip() for x in album.split("|")] if "" in (albumname, artistname): raise ValueError except ValueError: - raise exceptions.CommandWarning("Incorrect format! use `album | artist`") + raise exceptions.CommandWarning( + "Incorrect format! use `album | artist`" + ) listeners = [] tasks = [] @@ -1845,7 +1968,9 @@ async def whoknowsalbum(self, ctx: commands.Context, *, album): if member is None: continue - tasks.append(self.get_playcount_album(artistname, albumname, lastfm_username, member)) + tasks.append( + self.get_playcount_album(artistname, albumname, lastfm_username, member) + ) if tasks: data = await asyncio.gather(*tasks) @@ -1854,7 +1979,9 @@ async def whoknowsalbum(self, ctx: commands.Context, *, album): if playcount > 0: listeners.append((playcount, user)) else: - return await ctx.send("Nobody on this server has connected their last.fm account yet!") + return await ctx.send( + "Nobody on this server has connected their last.fm account yet!" + ) artistname = discord.utils.escape_markdown(artistname) albumname = discord.utils.escape_markdown(albumname) @@ -1930,7 +2057,9 @@ async def lyrics(self, ctx: commands.Context, *, query): trackname = npd["track"] artistname = npd["artist"] if None in [trackname, artistname]: - return await ctx.send(":warning: Could not get currently playing track!") + return await ctx.send( + ":warning: Could not get currently playing track!" + ) query = artistname + " " + trackname genius = Genius(self.bot) @@ -2157,7 +2286,9 @@ async def api_request(self, params, ignore_errors=False): if ignore_errors: return None text = await response.text() - raise exceptions.LastFMError(error_code=response.status, message=text) + raise exceptions.LastFMError( + error_code=response.status, message=text + ) if content is None: raise exceptions.LastFMError( @@ -2218,7 +2349,9 @@ async def custom_period(self, user, group_by, shift_hours=24, limit=None): "image": track["image"], } - albumsdata = sorted(formatted_data.values(), key=lambda x: x["playcount"], reverse=True) + albumsdata = sorted( + formatted_data.values(), key=lambda x: x["playcount"], reverse=True + ) if limit: albumsdata = albumsdata[:limit] return { @@ -2245,7 +2378,9 @@ async def custom_period(self, user, group_by, shift_hours=24, limit=None): "image": track["image"], } - tracksdata = sorted(formatted_data.values(), key=lambda x: x["playcount"], reverse=True) + tracksdata = sorted( + formatted_data.values(), key=lambda x: x["playcount"], reverse=True + ) if limit: tracksdata = tracksdata[:limit] return { @@ -2270,7 +2405,9 @@ async def custom_period(self, user, group_by, shift_hours=24, limit=None): "image": track["image"], } - artistdata = sorted(formatted_data.values(), key=lambda x: x["playcount"], reverse=True) + artistdata = sorted( + formatted_data.values(), key=lambda x: x["playcount"], reverse=True + ) if limit: artistdata = artistdata[:limit] return { @@ -2292,7 +2429,11 @@ async def get_np(self, username, ref): if data is not None: try: tracks = data["recenttracks"]["track"] - if tracks and "@attr" in tracks[0] and "nowplaying" in tracks[0]["@attr"]: + if ( + tracks + and "@attr" in tracks[0] + and "nowplaying" in tracks[0]["@attr"] + ): song = { "artist": tracks[0]["artist"]["#text"], "name": tracks[0]["name"], @@ -2426,7 +2567,9 @@ async def get_playcount(self, artist, username, reference=None): return count, reference, name async def scrape_artist_image(self, artist): - url = f"https://www.last.fm/music/{urllib.parse.quote_plus(str(artist))}/+images" + url = ( + f"https://www.last.fm/music/{urllib.parse.quote_plus(str(artist))}/+images" + ) data, error = await fetch_html(self.bot, url) if error: return None @@ -2462,7 +2605,8 @@ async def scrape_artists_for_chart(self, username, period, amount): soup = BeautifulSoup(data, "lxml") imagedivs = soup.findAll("td", {"class": "chartlist-image"}) images += [ - div.find("img")["src"].replace("/avatar70s/", "/300x300/") for div in imagedivs + div.find("img")["src"].replace("/avatar70s/", "/300x300/") + for div in imagedivs ] return images @@ -2649,7 +2793,8 @@ async def username_to_ctx(ctx: commands.Context): ctx.usertarget.id, ) if not ctx.username and ( - not ctx.invoked_subcommand or ctx.invoked_subcommand.name not in ["set", "blacklist"] + not ctx.invoked_subcommand + or ctx.invoked_subcommand.name not in ["set", "blacklist"] ): if not ctx.foreign_target: msg = f"No last.fm username saved! Please use `{ctx.prefix}fm set` to save your username (last.fm account required)" diff --git a/cogs/media.py b/cogs/media.py index 0836ede..7a40d2e 100644 --- a/cogs/media.py +++ b/cogs/media.py @@ -50,7 +50,10 @@ async def youtube(self, ctx: commands.Context, *, query): await util.paginate_list( ctx, - [f"https://youtube.com/watch?v={item['id']['videoId']}" for item in data.get("items")], + [ + f"https://youtube.com/watch?v={item['id']['videoId']}" + for item in data.get("items") + ], use_locking=True, only_author=True, index_entries=True, @@ -58,7 +61,9 @@ async def youtube(self, ctx: commands.Context, *, query): @util.patrons_only() @commands.group() - async def autoembedder(self, ctx: commands.Context, provider: Literal["instagram", "tiktok"]): + async def autoembedder( + self, ctx: commands.Context, provider: Literal["instagram", "tiktok"] + ): """Set up automatic embeds for various media sources The links will be expanded automatically when detected in chat, @@ -236,9 +241,15 @@ async def extract_scripts(session, url): scripts = [] tasks = [] if len(query.split(" ")) == 1: - tasks.append(extract_scripts(self.bot.session, f"https://gfycat.com/gifs/tag/{query}")) + tasks.append( + extract_scripts( + self.bot.session, f"https://gfycat.com/gifs/tag/{query}" + ) + ) - tasks.append(extract_scripts(self.bot.session, f"https://gfycat.com/gifs/search/{query}")) + tasks.append( + extract_scripts(self.bot.session, f"https://gfycat.com/gifs/search/{query}") + ) scripts = sum(await asyncio.gather(*tasks), []) urls = [] @@ -305,7 +316,9 @@ async def melon(self, ctx: commands.Context, timeframe): if not title or not artist: raise exceptions.CommandError("Failure parsing Melon page") - rows.append(f"`#{i:2}` **{artist.attrs['title']}** — ***{title.attrs['title']}***") + rows.append( + f"`#{i:2}` **{artist.attrs['title']}** — ***{title.attrs['title']}***" + ) content = discord.Embed(color=discord.Color.from_rgb(0, 205, 60)) content.set_author( @@ -353,17 +366,23 @@ async def run(self, ctx: commands.Context): self.message = await ctx.send(random.choice(self.gifs)["url"], view=self) @discord.ui.button(emoji=emojis.REMOVE, style=discord.ButtonStyle.danger) - async def toggle(self, interaction: discord.Interaction, _button: discord.ui.Button): + async def toggle( + self, interaction: discord.Interaction, _button: discord.ui.Button + ): await interaction.response.defer() await self.message.delete() @discord.ui.button(emoji=emojis.REPEAT, style=discord.ButtonStyle.primary) - async def randomize(self, interaction: discord.Interaction, _button: discord.ui.Button): + async def randomize( + self, interaction: discord.Interaction, _button: discord.ui.Button + ): await interaction.response.defer() await self.message.edit(content=random.choice(self.gifs)["url"]) @discord.ui.button(emoji=emojis.CONFIRM, style=discord.ButtonStyle.secondary) - async def confirm(self, interaction: discord.Interaction, _button: discord.ui.Button): + async def confirm( + self, interaction: discord.Interaction, _button: discord.ui.Button + ): await interaction.response.defer() await self.remove_ui() diff --git a/cogs/misc.py b/cogs/misc.py index 30db1ac..94965e8 100644 --- a/cogs/misc.py +++ b/cogs/misc.py @@ -16,10 +16,10 @@ from aiohttp import ClientResponseError from bs4 import BeautifulSoup from discord.ext import commands +from modules.misobot import MisoBot from PIL import Image, ImageDraw, ImageFont, UnidentifiedImageError from modules import emoji_literals, exceptions, util -from modules.misobot import MisoBot EMOJIFIER_HOST = os.environ.get("EMOJIFIER_HOST") @@ -116,7 +116,9 @@ async def rng(self, ctx: commands.Context, *, number_range): try: values = [int(x) for x in number_range.split("-")] except ValueError: - return await ctx.send(":warning: Please give a valid number range to choose from") + return await ctx.send( + ":warning: Please give a valid number range to choose from" + ) if len(values) == 2: start, end = values else: @@ -162,7 +164,9 @@ async def joke(self, ctx: commands.Context): @commands.command(aliases=["imbored"]) async def iambored(self, ctx: commands.Context): """Get something to do""" - async with self.bot.session.get("http://www.boredapi.com/api/activity/") as response: + async with self.bot.session.get( + "http://www.boredapi.com/api/activity/" + ) as response: data = await response.json(loads=orjson.loads) # https://www.boredapi.com/documentation @@ -258,7 +262,9 @@ async def ship(self, ctx: commands.Context, *, names): lovenums = newnums it = 0 - maxit = 100 # Maximum iterations allowed in below algorithm to attempt convergence + maxit = ( + 100 # Maximum iterations allowed in below algorithm to attempt convergence + ) maxlen = 100 # Maximum length of generated list allowed (some cases grow list infinitely) while len(lovenums) > 2 and it < maxit and len(lovenums) < maxlen: newnums = [] @@ -439,7 +445,9 @@ async def horoscope_monthly(self, ctx: commands.Context): async def send_hs( self, ctx: commands.Context, - variant: Literal["daily-yesterday", "daily-today", "daily-tomorrow", "weekly", "monthly"], + variant: Literal[ + "daily-yesterday", "daily-today", "daily-tomorrow", "weekly", "monthly" + ], ): sunsign = await self.bot.db.fetch_value( "SELECT sunsign FROM user_settings WHERE user_id = %s", @@ -462,7 +470,9 @@ async def send_hs( paragraph = soup.select_one("p") if paragraph is None: - raise exceptions.CommandError("Something went wrong trying to get horoscope text") + raise exceptions.CommandError( + "Something went wrong trying to get horoscope text" + ) date_node = paragraph.find("strong") if date_node is not None: @@ -525,7 +535,9 @@ async def horoscope_set(self, ctx: commands.Context, sign): ctx.author.id, sign, ) - await ctx.send(f"Zodiac saved as **{sign.capitalize()}** {self.hs[sign]['emoji']}") + await ctx.send( + f"Zodiac saved as **{sign.capitalize()}** {self.hs[sign]['emoji']}" + ) @horoscope.command(name="list") async def horoscope_list(self, ctx: commands.Context): @@ -540,7 +552,9 @@ async def horoscope_list(self, ctx: commands.Context): ) return await ctx.send(embed=content) - @commands.command(aliases=["colour"], usage=" ...") + @commands.command( + aliases=["colour"], usage=" ..." + ) async def color( self, ctx: commands.Context, @@ -571,7 +585,9 @@ async def color( if next_is_random_count and isinstance(source, int): slots = 50 - len(colors) amount = min(source, slots) - colors += ["{:06x}".format(random.randint(0, 0xFFFFFF)) for _ in range(amount)] + colors += [ + "{:06x}".format(random.randint(0, 0xFFFFFF)) for _ in range(amount) + ] next_is_random_count = False # member or role color elif isinstance(source, (discord.Member, discord.Role)): @@ -763,7 +779,9 @@ async def stealsticker(self, ctx: commands.Context): fetched_sticker = await sticker.fetch() if not isinstance(fetched_sticker, discord.GuildSticker): - raise exceptions.CommandWarning("I cannot steal default discord stickers!") + raise exceptions.CommandWarning( + "I cannot steal default discord stickers!" + ) sticker_file = await fetched_sticker.to_file() @@ -784,7 +802,9 @@ async def stealsticker(self, ctx: commands.Context): await ctx.send(embed=content) @commands.command() - async def emojify(self, ctx: commands.Context, *, text: Union[discord.Message, str]): + async def emojify( + self, ctx: commands.Context, *, text: Union[discord.Message, str] + ): """Emojify your message Usage: @@ -811,7 +831,9 @@ async def emojify(self, ctx: commands.Context, *, text: Union[discord.Message, s try: await ctx.send(result) except discord.errors.HTTPException: - raise exceptions.CommandWarning("Your text once emojified is too long to send!") + raise exceptions.CommandWarning( + "Your text once emojified is too long to send!" + ) @commands.command() async def meme(self, ctx: commands.Context, template: str, *, content): @@ -924,14 +946,14 @@ def parse_emoji(self, emoji_str: str | int): elif custom_emoji_match := re.search(r"<(a?)?:(\w+):(\d+)>", emoji_str): # is a custom emoji animated, emoji_name, emoji_id = custom_emoji_match.groups() - my_emoji.url = ( - f"https://cdn.discordapp.com/emojis/{emoji_id}.{'gif' if animated else 'png'}" - ) + my_emoji.url = f"https://cdn.discordapp.com/emojis/{emoji_id}.{'gif' if animated else 'png'}" my_emoji.name = emoji_name my_emoji.animated = animated == "a" my_emoji.id = int(emoji_id) elif emoji_name := emoji_literals.UNICODE_TO_NAME.get(emoji_str): - codepoint = "-".join(f"{ord(e):x}" for e in emoji_literals.NAME_TO_UNICODE[emoji_name]) + codepoint = "-".join( + f"{ord(e):x}" for e in emoji_literals.NAME_TO_UNICODE[emoji_name] + ) my_emoji.name = emoji_name.strip(":") my_emoji.url = f"https://twemoji.maxcdn.com/v/13.0.1/72x72/{codepoint}.png" else: @@ -994,7 +1016,8 @@ def write_box(self, x, y, width, height, color, text, angle=0): lines.append(line) if len(word.split("\n")) > 2: lines.extend( - [newline_words[i]] for i in range(1, len(word.split("\n")) - 1) + [newline_words[i]] + for i in range(1, len(word.split("\n")) - 1) ) line = [newline_words[-1]] else: diff --git a/cogs/mod.py b/cogs/mod.py index c22f30f..a9393ef 100644 --- a/cogs/mod.py +++ b/cogs/mod.py @@ -107,7 +107,9 @@ async def check_mutes(self): ) ) except discord.errors.Forbidden: - logger.warning("Unable to send unmuting message due to missing permissions!") + logger.warning( + "Unable to send unmuting message due to missing permissions!" + ) @commands.command(aliases=["clean"], usage=" [@mentions...]") @commands.guild_only() @@ -125,7 +127,9 @@ async def purge(self, ctx: commands.Context, amount: int): raise exceptions.CommandWarning("This command cannot be used here.") if amount > 100: - raise exceptions.CommandWarning("You cannot delete more than 100 messages at a time.") + raise exceptions.CommandWarning( + "You cannot delete more than 100 messages at a time." + ) await ctx.message.delete() @@ -154,7 +158,10 @@ async def purge(self, ctx: commands.Context, amount: int): @commands.guild_only() @commands.has_permissions(manage_roles=True) async def giverole( - self, ctx: commands.Context, role: discord.Role, members: commands.Greedy[discord.Member] + self, + ctx: commands.Context, + role: discord.Role, + members: commands.Greedy[discord.Member], ): """Give a role to multiple people""" success = [] @@ -172,12 +179,16 @@ async def giverole( @commands.command() @commands.guild_only() @commands.has_permissions(moderate_members=True) - async def timeout(self, ctx: commands.Context, member: discord.Member, *, duration="1 hour"): + async def timeout( + self, ctx: commands.Context, member: discord.Member, *, duration="1 hour" + ): """Timeout user. Pass 'remove' as the duration to remove""" if member.is_timed_out(): if duration and duration.strip().lower() == "remove": await member.timeout(None) - return await util.send_success(ctx, f"Removed timeout from {member.mention}") + return await util.send_success( + ctx, f"Removed timeout from {member.mention}" + ) seconds = member.timeout.timestamp() - arrow.now().int_timestamp raise exceptions.CommandInfo( @@ -198,7 +209,9 @@ async def timeout(self, ctx: commands.Context, member: discord.Member, *, durati @commands.command() @commands.guild_only() @commands.has_permissions(manage_roles=True) - async def mute(self, ctx: commands.Context, member: discord.Member, *, duration=None): + async def mute( + self, ctx: commands.Context, member: discord.Member, *, duration=None + ): """Mute user""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -227,10 +240,14 @@ async def mute(self, ctx: commands.Context, member: discord.Member, *, duration= raise exceptions.CommandWarning(f'Invalid mute duration "{duration}"') if seconds < 60: - raise exceptions.CommandInfo("The minimum duration of a mute is **1 minute**") + raise exceptions.CommandInfo( + "The minimum duration of a mute is **1 minute**" + ) if seconds > 604800: - raise exceptions.CommandInfo("The maximum duration of a mute is **1 week**") + raise exceptions.CommandInfo( + "The maximum duration of a mute is **1 week**" + ) try: await member.add_roles(mute_role) @@ -242,7 +259,11 @@ async def mute(self, ctx: commands.Context, member: discord.Member, *, duration= await util.send_success( ctx, f"Muted {member.mention}" - + (f" for **{util.stringfromtime(seconds)}**" if seconds is not None else ""), + + ( + f" for **{util.stringfromtime(seconds)}**" + if seconds is not None + else "" + ), ) if seconds is not None: @@ -364,7 +385,10 @@ async def fastban(self, ctx: commands.Context, *discord_users): success.append(f"`{user}` Banned :hammer:") await util.send_tasks_result_list( - ctx, success, failure, f":hammer: Attempting to ban {len(discord_users)} users..." + ctx, + success, + failure, + f":hammer: Attempting to ban {len(discord_users)} users...", ) @commands.command() @@ -435,7 +459,9 @@ async def ban(self, ctx: commands.Context, *discord_users): @staticmethod async def send_ban_confirmation(ctx: commands.Context, user): content = discord.Embed(title=":hammer: Ban user?", color=int("f4900c", 16)) - content.description = f"{user.mention}\n**{user.name}#{user.discriminator}**\n{user.id}" + content.description = ( + f"{user.mention}\n**{user.name}#{user.discriminator}**\n{user.id}" + ) msg = await ctx.send(embed=content) async def confirm_ban(): @@ -458,7 +484,9 @@ async def cancel_ban(): functions = {"✅": confirm_ban, "❌": cancel_ban} asyncio.ensure_future( - util.reaction_buttons(ctx, msg, functions, only_author=True, single_use=True) + util.reaction_buttons( + ctx, msg, functions, only_author=True, single_use=True + ) ) @commands.command() @@ -485,9 +513,13 @@ async def unban(self, ctx: commands.Context, *discord_users): f"It seems I don't have the permission to unban **{user}** {user.mention}" ) except discord.errors.NotFound: - raise exceptions.CommandWarning(f"Unable to unban. **{user}** is not banned") + raise exceptions.CommandWarning( + f"Unable to unban. **{user}** is not banned" + ) else: - return await util.send_success(ctx, f"Unbanned **{user}** {user.mention}") + return await util.send_success( + ctx, f"Unbanned **{user}** {user.mention}" + ) success = [] failure = [] @@ -510,7 +542,10 @@ async def unban(self, ctx: commands.Context, *discord_users): success.append(f"`{user}` Unbanned") await util.send_tasks_result_list( - ctx, success, failure, f":memo: Attempting to unban {len(discord_users)} users..." + ctx, + success, + failure, + f":memo: Attempting to unban {len(discord_users)} users...", ) diff --git a/cogs/notifications.py b/cogs/notifications.py index ff93721..80787b2 100644 --- a/cogs/notifications.py +++ b/cogs/notifications.py @@ -54,9 +54,15 @@ async def send_notification( return content = discord.Embed(color=message.author.color) - content.set_author(name=f"{message.author}", icon_url=message.author.display_avatar.url) - pattern = regex.compile(self.keyword_regex, words=keywords, flags=regex.IGNORECASE) - highlighted_text = regex.sub(pattern, lambda x: f"**{x.group(0)}**", message.content) + content.set_author( + name=f"{message.author}", icon_url=message.author.display_avatar.url + ) + pattern = regex.compile( + self.keyword_regex, words=keywords, flags=regex.IGNORECASE + ) + highlighted_text = regex.sub( + pattern, lambda x: f"**{x.group(0)}**", message.content + ) content.description = highlighted_text[:2047] content.add_field( @@ -147,8 +153,13 @@ async def on_message(self, message: discord.Message): await self.create_cache() continue - if member is not None and message.channel.permissions_for(member).read_messages: - asyncio.ensure_future(self.send_notification(member, message, users_words)) + if ( + member is not None + and message.channel.permissions_for(member).read_messages + ): + asyncio.ensure_future( + self.send_notification(member, message, users_words) + ) @commands.group(case_insensitive=True, aliases=["noti", "notif", "notifications"]) async def notification(self, ctx: commands.Context): @@ -214,7 +225,9 @@ async def notification_add(self, ctx: commands.Context, *, keyword: str): # remake notification cache await self.create_cache() - await util.send_success(ctx, f"New notification set! Check your DM {emojis.VIVISMIRK}") + await util.send_success( + ctx, f"New notification set! Check your DM {emojis.VIVISMIRK}" + ) @notification.command(name="remove") async def notification_remove(self, ctx: commands.Context, *, keyword: str): @@ -264,7 +277,9 @@ async def notification_remove(self, ctx: commands.Context, *, keyword: str): # remake notification cache await self.create_cache() - await util.send_success(ctx, f"Removed a notification! Check your DM {emojis.VIVISMIRK}") + await util.send_success( + ctx, f"Removed a notification! Check your DM {emojis.VIVISMIRK}" + ) @notification.command(name="list") async def notification_list(self, ctx: commands.Context): @@ -292,17 +307,23 @@ async def notification_list(self, ctx: commands.Context): if guild is None: guild = f"[Unknown server `{guild_id}`]" - rows.append(f"**{guild}** : `{keyword}` - Triggered **{times_triggered}** times") + rows.append( + f"**{guild}** : `{keyword}` - Triggered **{times_triggered}** times" + ) try: - await util.send_as_pages(ctx, content, rows, maxpages=1, maxrows=50, send_to=ctx.author) + await util.send_as_pages( + ctx, content, rows, maxpages=1, maxrows=50, send_to=ctx.author + ) except discord.errors.Forbidden: raise exceptions.CommandWarning( "I was unable to send you a DM! Please change your settings." ) if ctx.guild is not None: - await util.send_success(ctx, f"Notification list sent to your DM {emojis.VIVISMIRK}") + await util.send_success( + ctx, f"Notification list sent to your DM {emojis.VIVISMIRK}" + ) @notification.command(name="clear") async def notification_clear(self, ctx: commands.Context): @@ -317,7 +338,9 @@ async def notification_clear(self, ctx: commands.Context): """, ctx.author.id, ) - await util.send_success(ctx, "Cleared all of your notifications in all servers!") + await util.send_success( + ctx, "Cleared all of your notifications in all servers!" + ) else: await self.bot.db.execute( """ @@ -326,7 +349,9 @@ async def notification_clear(self, ctx: commands.Context): ctx.author.id, ctx.guild.id, ) - await util.send_success(ctx, "Cleared all of your notifications in this server!") + await util.send_success( + ctx, "Cleared all of your notifications in this server!" + ) # remake notification cache await self.create_cache() @@ -361,7 +386,9 @@ async def notification_test( ctx.author.id, ) - pattern = regex.compile(self.keyword_regex, words=keywords, flags=regex.IGNORECASE) + pattern = regex.compile( + self.keyword_regex, words=keywords, flags=regex.IGNORECASE + ) if finds := pattern.findall(message.content): keywords = list(set(finds)) diff --git a/cogs/owner.py b/cogs/owner.py index c4e6727..e9a651e 100644 --- a/cogs/owner.py +++ b/cogs/owner.py @@ -34,7 +34,9 @@ async def say( ): """Makes the bot say something in the given channel""" channel = self.bot.get_partial_messageable(channel_id) - await ctx.send(f"Sending message to **{channel.guild}** <#{channel.id}>\n> {message}") + await ctx.send( + f"Sending message to **{channel.guild}** <#{channel.id}>\n> {message}" + ) await channel.send(message) @commands.command(rest_is_raw=True) @@ -58,7 +60,9 @@ async def guilds(self, ctx: commands.Context): rows = [ f"[`{guild.id}`] **{guild.member_count}** members : **{guild.name}**" - for guild in sorted(self.bot.guilds, key=lambda x: x.member_count or 0, reverse=True) + for guild in sorted( + self.bot.guilds, key=lambda x: x.member_count or 0, reverse=True + ) ] await util.send_as_pages(ctx, content, rows) @@ -67,22 +71,32 @@ async def findguild(self, ctx: commands.Context, *, search_term): """Find a guild by name""" rows = [ f"[`{guild.id}`] **{guild.member_count}** members : **{guild.name}**" - for guild in sorted(self.bot.guilds, key=lambda x: x.member_count or 0, reverse=True) + for guild in sorted( + self.bot.guilds, key=lambda x: x.member_count or 0, reverse=True + ) if search_term.lower() in guild.name.lower() ] - content = discord.Embed(title=f"Found **{len(rows)}** guilds matching search term") + content = discord.Embed( + title=f"Found **{len(rows)}** guilds matching search term" + ) await util.send_as_pages(ctx, content, rows) @commands.command() async def userguilds(self, ctx: commands.Context, user: discord.User): """Get all guilds user is part of""" rows = [] - for guild in sorted(self.bot.guilds, key=lambda x: x.member_count or 0, reverse=True): + for guild in sorted( + self.bot.guilds, key=lambda x: x.member_count or 0, reverse=True + ): guildmember = guild.get_member(user.id) if guildmember is not None: - rows.append(f"[`{guild.id}`] **{guild.member_count}** members : **{guild.name}**") + rows.append( + f"[`{guild.id}`] **{guild.member_count}** members : **{guild.name}**" + ) - content = discord.Embed(title=f"User **{user}** found in **{len(rows)}** guilds") + content = discord.Embed( + title=f"User **{user}** found in **{len(rows)}** guilds" + ) await util.send_as_pages(ctx, content, rows) @commands.command() @@ -109,7 +123,12 @@ async def donator(self, ctx: commands.Context): @donator.command(name="addsingle") async def donator_addsingle( - self, ctx: commands.Context, user: discord.User, platform, amount: float, ts=None + self, + ctx: commands.Context, + user: discord.User, + platform, + amount: float, + ts=None, ): """Add a new single time donation""" ts = arrow.utcnow().datetime if ts is None else arrow.get(ts).datetime @@ -127,7 +146,14 @@ async def donator_addsingle( @donator.command(name="add") async def donator_add( - self, ctx, user: discord.User, username, platform, tier: int, amount: int, since_ts=None + self, + ctx, + user: discord.User, + username, + platform, + tier: int, + amount: int, + since_ts=None, ): """Add a new monthly donator""" if since_ts is None: @@ -176,7 +202,9 @@ async def donator_toggle(self, ctx: commands.Context, user: discord.User): await util.send_success(ctx, f"**{user}** donator status changed.") @donator.command(name="tier") - async def donator_tier(self, ctx: commands.Context, user: discord.User, new_tier: int): + async def donator_tier( + self, ctx: commands.Context, user: discord.User, new_tier: int + ): """Change user's donation tier""" await self.bot.db.execute( """ @@ -185,7 +213,9 @@ async def donator_tier(self, ctx: commands.Context, user: discord.User, new_tier new_tier, user.id, ) - await util.send_success(ctx, f"**{user}** donation changed to **Tier {new_tier}**") + await util.send_success( + ctx, f"**{user}** donation changed to **Tier {new_tier}**" + ) @commands.command(name="db", aliases=["dbe", "dbq"]) @commands.is_owner() diff --git a/cogs/roles.py b/cogs/roles.py index f94795e..3de1f5a 100644 --- a/cogs/roles.py +++ b/cogs/roles.py @@ -5,11 +5,11 @@ import asyncio import discord +from cogs.errorhandler import ErrorHander from discord.ext import commands +from modules.misobot import MisoBot -from cogs.errorhandler import ErrorHander from modules import emojis, exceptions, queries, util -from modules.misobot import MisoBot class Roles(commands.Cog): @@ -98,7 +98,9 @@ async def confirm(): ctx.guild.id, # type: ignore ) - content.title = f":white_check_mark: Deleted all {len(matching_roles)} color roles" + content.title = ( + f":white_check_mark: Deleted all {len(matching_roles)} color roles" + ) content.description = "" content.color = int("77b255", 16) await msg.edit(content=None, embed=content) @@ -145,7 +147,8 @@ async def baserole(self, ctx: commands.Context, role: discord.Role): role.id, ) await util.send_success( - ctx, f"New color roles will now inherit permissions and position from {role.mention}" + ctx, + f"New color roles will now inherit permissions and position from {role.mention}", ) @commands.guild_only() @@ -199,10 +202,16 @@ async def colorme(self, ctx: commands.Context, hex_color: str): color_role = None if existing_roles is not None: existing_role_id: int | None = dict(existing_roles).get(str(color)) - color_role = ctx.guild.get_role(existing_role_id) if existing_role_id else None + color_role = ( + ctx.guild.get_role(existing_role_id) if existing_role_id else None + ) - if old_roles := list(filter(lambda r: r.id in existing_roles_ids, ctx.author.roles)): - await ctx.author.remove_roles(*old_roles, atomic=True, reason="Changed color") + if old_roles := list( + filter(lambda r: r.id in existing_roles_ids, ctx.author.roles) + ): + await ctx.author.remove_roles( + *old_roles, atomic=True, reason="Changed color" + ) # remove manually deleted roles for role_id in existing_roles_ids: @@ -270,7 +279,8 @@ async def colorme(self, ctx: commands.Context, hex_color: str): # clean up any roles that are left with 0 users unused_roles = filter( - lambda r: r.id in [x[1] for x in existing_roles or []] and len(r.members) == 0, + lambda r: r.id in [x[1] for x in existing_roles or []] + and len(r.members) == 0, ctx.guild.roles, ) for role in unused_roles: @@ -336,9 +346,13 @@ async def rolepicker_remove(self, ctx: commands.Context, *, name): ) @rolepicker.command(name="channel") - async def rolepicker_channel(self, ctx: commands.Context, channel: discord.TextChannel): + async def rolepicker_channel( + self, ctx: commands.Context, channel: discord.TextChannel + ): """Set the channel you want to add and remove roles in""" - await queries.update_setting(ctx, "rolepicker_settings", "channel_id", channel.id) + await queries.update_setting( + ctx, "rolepicker_settings", "channel_id", channel.id + ) self.bot.cache.rolepickers.add(channel.id) await util.send_success( ctx, @@ -366,7 +380,9 @@ async def rolepicker_list(self, ctx: commands.Context): title=f":scroll: Available roles in {ctx.guild.name}", color=int("ffd983", 16), ) - if rows := [f"`{role_name}` : <@&{role_id}>" for role_name, role_id in sorted(data)]: + if rows := [ + f"`{role_name}` : <@&{role_id}>" for role_name, role_id in sorted(data) + ]: await util.send_as_pages(ctx, content, rows) else: content.description = "Nothing yet!" @@ -376,7 +392,9 @@ async def rolepicker_list(self, ctx: commands.Context): async def rolepicker_enabled(self, ctx: commands.Context, value: bool): """Enable or disable the rolepicker""" await queries.update_setting(ctx, "rolepicker_settings", "is_enabled", value) - await util.send_success(ctx, f"Rolepicker is now **{'enabled' if value else 'disabled'}**") + await util.send_success( + ctx, f"Rolepicker is now **{'enabled' if value else 'disabled'}**" + ) @commands.Cog.listener() async def on_message(self, message: discord.Message): @@ -430,7 +448,9 @@ async def on_message(self, message: discord.Message): try: await message.author.add_roles(role) except discord.errors.Forbidden: - await message.reply(":warning: I don't have permission to give you this role!") + await message.reply( + ":warning: I don't have permission to give you this role!" + ) else: await message.channel.send( embed=discord.Embed( diff --git a/cogs/typings.py b/cogs/typings.py index 7eb14e0..4350557 100644 --- a/cogs/typings.py +++ b/cogs/typings.py @@ -10,9 +10,9 @@ import arrow import discord from discord.ext import commands +from modules.misobot import MisoBot from modules import exceptions, util -from modules.misobot import MisoBot class Typings(commands.Cog): @@ -33,7 +33,9 @@ def obfuscate(self, text): return "".join(letter_dict.get(letter, letter) for letter in text) def anticheat(self, message): - remainder = "".join(set(message.content).intersection(self.font + "".join(self.separators))) + remainder = "".join( + set(message.content).intersection(self.font + "".join(self.separators)) + ) return remainder != "" @commands.group() @@ -42,7 +44,9 @@ async def typing(self, ctx: commands.Context): await util.command_group_help(ctx) @typing.command(name="test") - async def typing_test(self, ctx: commands.Context, language=None, wordcount: int = 25): + async def typing_test( + self, ctx: commands.Context, language=None, wordcount: int = 25 + ): """Take a typing test""" if language is None: language = wordcount @@ -65,7 +69,9 @@ async def typing_test(self, ctx: commands.Context, language=None, wordcount: int f"Currently supported languages are:\n>>> {langs}" ) - words_message = await ctx.reply(f"```\n{self.obfuscate(' '.join(wordlist))}\n```") + words_message = await ctx.reply( + f"```\n{self.obfuscate(' '.join(wordlist))}\n```" + ) def check(_message): return _message.author == ctx.author and _message.channel == ctx.channel @@ -76,7 +82,9 @@ def check(_message): return await ctx.send(f"{ctx.author.mention} Too slow.") else: - wpm, accuracy, not_long_enough = calculate_entry(message, words_message, wordlist) + wpm, accuracy, not_long_enough = calculate_entry( + message, words_message, wordlist + ) if self.anticheat(message) or wpm > 300: return await message.reply("Stop cheating >:(") @@ -91,7 +99,9 @@ def check(_message): ) @typing.command(name="race") - async def typing_race(self, ctx: commands.Context, language=None, wordcount: int = 25): + async def typing_race( + self, ctx: commands.Context, language=None, wordcount: int = 25 + ): """Challenge your friends into a typing race""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -126,7 +136,9 @@ async def typing_race(self, ctx: commands.Context, language=None, wordcount: int "React with :white_check_mark: to start the race." ) - content.add_field(name="Participants", value=f"**{util.displayname(ctx.author)}**") + content.add_field( + name="Participants", value=f"**{util.displayname(ctx.author)}**" + ) enter_message = await ctx.send(embed=content) note_emoji = "🗒" @@ -147,11 +159,15 @@ def check(_reaction, _user): while not race_in_progress: try: - reaction, user = await ctx.bot.wait_for("reaction_add", timeout=120.0, check=check) + reaction, user = await ctx.bot.wait_for( + "reaction_add", timeout=120.0, check=check + ) except asyncio.TimeoutError: try: for emoji in [note_emoji, check_emoji]: - asyncio.ensure_future(enter_message.remove_reaction(emoji, ctx.bot.user)) + asyncio.ensure_future( + enter_message.remove_reaction(emoji, ctx.bot.user) + ) except (discord.errors.NotFound, discord.errors.Forbidden): pass break @@ -194,7 +210,9 @@ def check(_reaction, _user): await asyncio.sleep(1) await words_message.delete() - words_message = await ctx.send(f"```\n{self.obfuscate(' '.join(wordlist))}\n```") + words_message = await ctx.send( + f"```\n{self.obfuscate(' '.join(wordlist))}\n```" + ) tasks = [] for player in players: @@ -206,7 +224,9 @@ def check(_reaction, _user): results = await asyncio.gather(*tasks) - content = discord.Embed(title=":checkered_flag: Race complete!", color=int("e1e8ed", 16)) + content = discord.Embed( + title=":checkered_flag: Race complete!", color=int("e1e8ed", 16) + ) rows = [] values = [] player: discord.Member @@ -216,7 +236,11 @@ def check(_reaction, _user): values.append((ctx.guild.id, player.id, 1, 1 if i == 1 else 0)) rows.append( f"{f'`#{i}`' if i > 1 else ':trophy:'} **{util.displayname(player)}** — " - + (f"**{int(wpm)} WPM / {int(accuracy)}% Accuracy**" if wpm != 0 else ":x:") + + ( + f"**{int(wpm)} WPM / {int(accuracy)}% Accuracy**" + if wpm != 0 + else ":x:" + ) ) await self.bot.db.executemany( @@ -239,12 +263,16 @@ def progress_check(_message): return _message.author == player and _message.channel == ctx.channel try: - message = await self.bot.wait_for("message", timeout=300.0, check=progress_check) + message = await self.bot.wait_for( + "message", timeout=300.0, check=progress_check + ) except asyncio.TimeoutError: await ctx.send(f"{player.mention} too slow!") return player, 0, 0 else: - wpm, accuracy, not_long_enough = calculate_entry(message, words_message, wordlist) + wpm, accuracy, not_long_enough = calculate_entry( + message, words_message, wordlist + ) if self.anticheat(message) or wpm > 300: await message.reply("Stop cheating >:(") return player, 0, 0 @@ -302,10 +330,10 @@ async def typing_history( @typing.command(name="cleardata") async def typing_clear(self, ctx: commands.Context): """Clear your typing data""" - content = discord.Embed(title=":warning: Are you sure?", color=int("ffcc4d", 16)) - content.description = ( - "This action will delete *all* of your saved typing data and is **irreversible**." + content = discord.Embed( + title=":warning: Are you sure?", color=int("ffcc4d", 16) ) + content.description = "This action will delete *all* of your saved typing data and is **irreversible**." msg = await ctx.send(embed=content) async def confirm(): @@ -334,7 +362,9 @@ async def cancel(): functions = {"✅": confirm, "❌": cancel} asyncio.ensure_future( - util.reaction_buttons(ctx, msg, functions, only_author=True, single_use=True) + util.reaction_buttons( + ctx, msg, functions, only_author=True, single_use=True + ) ) @typing.command(name="stats") diff --git a/cogs/user.py b/cogs/user.py index 0a32733..c735e26 100644 --- a/cogs/user.py +++ b/cogs/user.py @@ -28,7 +28,10 @@ def __init__(self, bot): @commands.command(aliases=["dp", "av", "pfp"]) async def avatar( - self, ctx: commands.Context, *, user: Union[discord.Member, discord.User, None] = None + self, + ctx: commands.Context, + *, + user: Union[discord.Member, discord.User, None] = None, ): """Get user's profile picture""" if ctx.guild is None: @@ -80,7 +83,10 @@ async def switch(): @commands.command(aliases=["uinfo"]) @commands.cooldown(3, 30, type=commands.BucketType.user) async def userinfo( - self, ctx: commands.Context, *, user: Union[discord.Member, discord.User, None] = None + self, + ctx: commands.Context, + *, + user: Union[discord.Member, discord.User, None] = None, ): """Get information about discord user""" if ctx.guild is None: @@ -102,7 +108,9 @@ async def userinfo( content.add_field(name="Badges", value=" ".join(user_badges + other_badges)) content.add_field(name="Mention", value=user.mention) - content.add_field(name="Account created", value=user.created_at.strftime("%d/%m/%Y %H:%M")) + content.add_field( + name="Account created", value=user.created_at.strftime("%d/%m/%Y %H:%M") + ) if isinstance(user, discord.Member): content.colour = user.color @@ -110,29 +118,41 @@ async def userinfo( member_number = 1 + sum( 1 for member in ctx.guild.members - if member.joined_at and user.joined_at and member.joined_at < user.joined_at + if member.joined_at + and user.joined_at + and member.joined_at < user.joined_at ) boosting_date = None if user.premium_since: - boosting_date = humanize.naturaldelta(discord.utils.utcnow() - user.premium_since) + boosting_date = humanize.naturaldelta( + discord.utils.utcnow() - user.premium_since + ) - content.add_field(name="Member", value=f"#{member_number} / {len(ctx.guild.members)}") + content.add_field( + name="Member", value=f"#{member_number} / {len(ctx.guild.members)}" + ) content.add_field( name="Boosting", value=f"For {boosting_date}" if boosting_date else "No" ) content.add_field( name="Joined server", - value=user.joined_at.strftime("%d/%m/%Y %H:%M") if user.joined_at else "Unknown", + value=user.joined_at.strftime("%d/%m/%Y %H:%M") + if user.joined_at + else "Unknown", ) if self.bot.intents.presences: activity_display = util.UserActivity(user.activities).display() status = "mobile" if user.is_on_mobile() else user.status.name - status_display = f"{emojis.Status[status].value} {user.status.name.capitalize()}" + status_display = ( + f"{emojis.Status[status].value} {user.status.name.capitalize()}" + ) content.add_field(name="Status", value=status_display) - content.add_field(name="Activity", value=activity_display or "Unavailable") + content.add_field( + name="Activity", value=activity_display or "Unavailable" + ) content.add_field( name="Roles", @@ -171,7 +191,9 @@ async def members(self, ctx: commands.Context): if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") - sorted_members = sorted(ctx.guild.members, key=lambda x: x.joined_at or 0, reverse=True) + sorted_members = sorted( + ctx.guild.members, key=lambda x: x.joined_at or 0, reverse=True + ) membercount = len(sorted_members) content = discord.Embed(title=f"{ctx.guild.name} members") rows = [] @@ -183,7 +205,9 @@ async def members(self, ctx: commands.Context): await util.send_as_pages(ctx, content, rows) @commands.command() - async def banner(self, ctx: commands.Context, *, user: Optional[discord.User] = None): + async def banner( + self, ctx: commands.Context, *, user: Optional[discord.User] = None + ): """Get user's banner""" # banners are not cached so an api call is required user = await self.bot.fetch_user(user.id if user else ctx.author.id) @@ -192,7 +216,9 @@ async def banner(self, ctx: commands.Context, *, user: Optional[discord.User] = if not user.banner: if not user.accent_color: - raise exceptions.CommandWarning(f"**{user}** has not set banner or accent color.") + raise exceptions.CommandWarning( + f"**{user}** has not set banner or accent color." + ) content.color = user.accent_color content.description = f":art: Solid color `{user.accent_color}`" @@ -201,7 +227,9 @@ async def banner(self, ctx: commands.Context, *, user: Optional[discord.User] = banner_url = util.asset_full_size(user.banner) - content.set_author(name=f"{user} Banner", url=banner_url, icon_url=user.display_avatar.url) + content.set_author( + name=f"{user} Banner", url=banner_url, icon_url=user.display_avatar.url + ) content.set_image(url=banner_url) stats = await util.image_info_from_url(self.bot.session, banner_url) color = await util.color_from_image_url( @@ -216,7 +244,9 @@ async def banner(self, ctx: commands.Context, *, user: Optional[discord.User] = await ctx.send(embed=content) @commands.command(aliases=["sbanner", "guildbanner"]) - async def serverbanner(self, ctx: commands.Context, *, guild: Optional[discord.Guild] = None): + async def serverbanner( + self, ctx: commands.Context, *, guild: Optional[discord.Guild] = None + ): """Get server's banner""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -252,7 +282,9 @@ async def serverbanner(self, ctx: commands.Context, *, guild: Optional[discord.G await ctx.send(embed=content) @commands.command(aliases=["sinfo", "guildinfo"]) - async def serverinfo(self, ctx: commands.Context, *, guild: Optional[discord.Guild] = None): + async def serverinfo( + self, ctx: commands.Context, *, guild: Optional[discord.Guild] = None + ): """Get various information on server""" if ctx.guild is None: raise exceptions.CommandError("Unable to get current guild") @@ -281,14 +313,22 @@ async def serverinfo(self, ctx: commands.Context, *, guild: Optional[discord.Gui content.add_field(name="Members", value=str(guild.member_count)) content.add_field( name="Channels", - value=(f"{len(guild.text_channels)} Text, {len(guild.voice_channels)} Voice"), + value=( + f"{len(guild.text_channels)} Text, {len(guild.voice_channels)} Voice" + ), ) content.add_field(name="Roles", value=str(len(guild.roles))) content.add_field(name="Threads", value=str(len(guild.threads))) content.add_field(name="NSFW filter", value=guild.explicit_content_filter.name) - content.add_field(name="Emojis", value=f"{len(guild.emojis)} / {guild.emoji_limit}") - content.add_field(name="Stickers", value=f"{len(guild.stickers)} / {guild.sticker_limit}") - content.add_field(name="Created at", value=guild.created_at.strftime("%d/%m/%Y %H:%M")) + content.add_field( + name="Emojis", value=f"{len(guild.emojis)} / {guild.emoji_limit}" + ) + content.add_field( + name="Stickers", value=f"{len(guild.stickers)} / {guild.sticker_limit}" + ) + content.add_field( + name="Created at", value=guild.created_at.strftime("%d/%m/%Y %H:%M") + ) if guild.features: content.add_field( name="Features", @@ -351,7 +391,8 @@ async def leaderboard_fishy_gifted(self, ctx: commands.Context, scope=""): content = discord.Embed( title=( - f":fish: {'Global' if global_data else ctx.guild.name} " "gifted fishy leaderboard" + f":fish: {'Global' if global_data else ctx.guild.name} " + "gifted fishy leaderboard" ), color=int("55acee", 16), ) @@ -382,7 +423,9 @@ async def leaderboard_fishy(self, ctx: commands.Context, scope=""): continue ranking = medal_emoji[i - 1] if i <= len(medal_emoji) else f"`#{i:2}`" - rows.append(f"{ranking} **{util.displayname(user)}** — **{fishy_count}** fishy") + rows.append( + f"{ranking} **{util.displayname(user)}** — **{fishy_count}** fishy" + ) i += 1 if not rows: raise exceptions.CommandInfo("Nobody has any fish yet!") @@ -412,7 +455,11 @@ async def leaderboard_wpm(self, ctx: commands.Context, scope=""): if data: i = 1 for userid, wpm, test_date, word_count in data: - user = self.bot.get_user(userid) if _global_ else ctx.guild.get_member(userid) + user = ( + self.bot.get_user(userid) + if _global_ + else ctx.guild.get_member(userid) + ) if user is None or user.bot: continue @@ -461,7 +508,9 @@ async def leaderboard_crowns(self, ctx: commands.Context): else: ranking = f"`#{i:2}`" - rows.append(f"{ranking} **{util.displayname(user)}** — **{amount}** crowns") + rows.append( + f"{ranking} **{util.displayname(user)}** — **{amount}** crowns" + ) if not rows: rows = ["No data."] @@ -474,7 +523,9 @@ async def leaderboard_crowns(self, ctx: commands.Context): @commands.command(enabled=False) async def profile( - self, ctx: commands.Context, user: Union[discord.Member, discord.User, None] = None + self, + ctx: commands.Context, + user: Union[discord.Member, discord.User, None] = None, ): """Your personal customizable user profile""" if user is None: @@ -597,7 +648,9 @@ def make_badge(classname): "imageFormat": "png", } buffer = await util.render_html(self.bot, payload) - await ctx.send(file=discord.File(fp=buffer, filename=f"profile_{user.name}.png")) + await ctx.send( + file=discord.File(fp=buffer, filename=f"profile_{user.name}.png") + ) @commands.command() async def marry(self, ctx: commands.Context, user: discord.Member): @@ -652,7 +705,9 @@ async def marry(self, ctx: commands.Context, user: discord.Member): ), ) ) - new_proposals = {el for el in self.proposals if el[0] not in [user.id, ctx.author.id]} + new_proposals = { + el for el in self.proposals if el[0] not in [user.id, ctx.author.id] + } self.proposals = new_proposals else: self.proposals.add((ctx.author.id, user.id)) @@ -683,7 +738,9 @@ async def divorce(self, ctx: commands.Context): if partner is None: return await ctx.send(":thinking: You are not married!") - partner = ctx.guild.get_member(partner) or await util.find_user(self.bot, partner) + partner = ctx.guild.get_member(partner) or await util.find_user( + self.bot, partner + ) content = discord.Embed( description=":broken_heart:" @@ -716,7 +773,9 @@ async def cancel(): functions = {"✅": confirm, "❌": cancel} asyncio.ensure_future( - util.reaction_buttons(ctx, msg, functions, only_author=True, single_use=True) + util.reaction_buttons( + ctx, msg, functions, only_author=True, single_use=True + ) ) @commands.command() @@ -740,9 +799,13 @@ async def marriage( ) if data: if data[0] == member.id: - partner = ctx.guild.get_member(data[1]) or await util.find_user(self.bot, data[1]) + partner = ctx.guild.get_member(data[1]) or await util.find_user( + self.bot, data[1] + ) else: - partner = ctx.guild.get_member(data[0]) or await util.find_user(self.bot, data[0]) + partner = ctx.guild.get_member(data[0]) or await util.find_user( + self.bot, data[0] + ) marriage_date = data[2] length = humanize.naturaldelta( arrow.utcnow().timestamp() - marriage_date.timestamp(), months=False diff --git a/cogs/utility.py b/cogs/utility.py index 61aa1b6..551d806 100644 --- a/cogs/utility.py +++ b/cogs/utility.py @@ -137,7 +137,9 @@ async def check_reminders(self): date = arrow.get(created_on) if now_ts - reminder_ts > 21600: date_fmt = date.format("DD/MM/YYYY HH:mm:ss") - logger.info(f"Deleting reminder set for {date_fmt} for being over 6 hours late") + logger.info( + f"Deleting reminder set for {date_fmt} for being over 6 hours late" + ) else: embed = discord.Embed( color=int("d3a940", 16), @@ -155,7 +157,9 @@ async def check_reminders(self): await user.send(embed=embed) logger.info(f'Reminded {user} to "{content}"') except discord.errors.Forbidden: - logger.warning(f"Unable to remind {user}, missing DM permissions!") + logger.warning( + f"Unable to remind {user}, missing DM permissions!" + ) else: logger.info(f"Deleted expired reminder by unknown user {user_id}") @@ -174,9 +178,9 @@ async def check_reminders(self): async def on_command_error(self, ctx: commands.Context, error): """only for CommandNotFound""" error = getattr(error, "original", error) - if isinstance(error, commands.CommandNotFound) and ctx.message.content.startswith( - f"{ctx.prefix}!" - ): + if isinstance( + error, commands.CommandNotFound + ) and ctx.message.content.startswith(f"{ctx.prefix}!"): # type ignores everywhere because this is so hacky ctx.timer = time() # type: ignore ctx.iscallback = True # type: ignore @@ -244,7 +248,9 @@ async def shazam(self, ctx: commands.Context, url_or_attachment: Optional[str]): and ctx.message.reference.message_id and isinstance(ctx.channel, (discord.Thread, discord.TextChannel)) ): - reply_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) + reply_message = await ctx.channel.fetch_message( + ctx.message.reference.message_id + ) if not reply_message.attachments: raise exceptions.CommandWarning("Referenced message has no attachments") attachment = await reply_message.attachments[0].to_file() @@ -253,7 +259,9 @@ async def shazam(self, ctx: commands.Context, url_or_attachment: Optional[str]): return await util.send_command_help(ctx) if result is None: - raise exceptions.CommandWarning("I was unable to recognize any music from this") + raise exceptions.CommandWarning( + "I was unable to recognize any music from this" + ) metadata = "\n".join([f'`{m["title"]}:` {m["text"]}' for m in result.metadata]) content = discord.Embed( @@ -271,7 +279,9 @@ async def shazam(self, ctx: commands.Context, url_or_attachment: Optional[str]): content.set_thumbnail(url=result.cover_art) await ctx.send(embed=content) - @commands.command(usage="<'in' | 'on'>