From 6d6bb575e7010453b28ffc54cf9090cbc81556d8 Mon Sep 17 00:00:00 2001 From: "Jerry.py" Date: Mon, 26 Jun 2023 18:31:04 -0400 Subject: [PATCH 1/2] Refactor Code The code is refactored but you might need to check if it works. --- cogs/fun.py | 4 +- cogs/general.py | 5 +- cogs/helper.py | 69 ++++++++++------------- cogs/listeners.py | 100 ++++++++++++++++----------------- cogs/misc.py | 19 +++---- cogs/moderation.py | 135 ++++++++++++++++++++------------------------- cogs/tasks.py | 15 ++--- ext/helpers.py | 49 +++++++--------- ext/http.py | 33 ++++++++--- ext/models.py | 16 ++---- ext/ui/view.py | 17 +++--- 11 files changed, 212 insertions(+), 250 deletions(-) diff --git a/cogs/fun.py b/cogs/fun.py index 0587403..0ba46a8 100644 --- a/cogs/fun.py +++ b/cogs/fun.py @@ -256,8 +256,8 @@ async def _beerparty( ): reason = ("\nReason: " + reason) if reason else "" msg = await ctx.send( - "Open invite to beerparty! React with 🍻 to join!" + reason - ) + f"Open invite to beerparty! React with 🍻 to join!{reason}" + ) await msg.add_reaction("\U0001f37b") await asyncio.sleep(60) msg = await ctx.channel.fetch_message(msg.id) diff --git a/cogs/general.py b/cogs/general.py index f7c46c1..08b715c 100644 --- a/cogs/general.py +++ b/cogs/general.py @@ -48,8 +48,7 @@ async def _source( src = type(self.bot.help_command) module = src.__module__ filename = inspect.getsourcefile(src) - help_command = self.bot.get_command("help") - if help_command: + if help_command := self.bot.get_command("help"): embed.description = help_command.help.format(prefix=ctx.prefix) else: obj = self.bot.get_command(command.replace(".", " ")) @@ -97,7 +96,7 @@ async def define(self, ctx: commands.Context[CodingBot], *, word: str): return await ctx.send(f"Could not find definition for `{word}`") definition = definition[0] embed = discord.Embed( - title="Definition of {}".format(word), + title=f"Definition of {word}", description=f"**Meaning**: {definition.meaning}\n", color=discord.Color.random(), ) diff --git a/cogs/helper.py b/cogs/helper.py index 6f26bfb..9602145 100644 --- a/cogs/helper.py +++ b/cogs/helper.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import contextlib import datetime from io import BytesIO from typing import TYPE_CHECKING, Any, Optional @@ -53,9 +54,7 @@ async def cog_check(self, ctx: commands.Context[CodingBot]) -> bool: official_helper_role = ctx.guild.get_role(OFFICIAL_HELPER_ROLE_ID) - if official_helper_role not in ctx.author.roles: - return False - return True + return official_helper_role in ctx.author.roles async def capture_evidence( self, ctx: commands.Context[CodingBot] @@ -80,28 +79,27 @@ async def capture_evidence( ) view_touched = not (await view.wait()) evidence_byts = None - if view_touched: - if view.confirmed: - initial_mess = await ctx.author.send( - "Please send the evidence in the form of an attachment." + if view_touched and view.confirmed: + initial_mess = await ctx.author.send( + "Please send the evidence in the form of an attachment." + ) + try: + wait_mess = await self.bot.wait_for( + "message", + check=lambda m: m.author == ctx.author + and m.channel == initial_mess.channel + and m.attachments + and not m.guild, + timeout=60, ) - try: - wait_mess = await self.bot.wait_for( - "message", - check=lambda m: m.author == ctx.author - and m.channel == initial_mess.channel - and m.attachments - and not m.guild, - timeout=60, - ) - except asyncio.TimeoutError: - await initial_mess.delete() - await ctx.author.send( - "You didn't send any evidence in time. " - "Proceeding with the ban without evidence." - ) - else: - evidence_byts = await wait_mess.attachments[0].read() + except asyncio.TimeoutError: + await initial_mess.delete() + await ctx.author.send( + "You didn't send any evidence in time. " + "Proceeding with the ban without evidence." + ) + else: + evidence_byts = await wait_mess.attachments[0].read() return evidence_byts async def log( @@ -168,15 +166,15 @@ async def log( raise undo_action action_string = ( - action_info["action"] if not undo else action_info["undo_action"] + action_info["undo_action"] if undo else action_info["action"] ) - icon = action_info["icon"] if not undo else action_info["undo_icon"] + icon = action_info["undo_icon"] if undo else action_info["icon"] color = discord.Color.green() if undo else action_info.get("color") embed = discord.Embed(color=color, timestamp=discord.utils.utcnow()) - embed.description = "{} **Action:** {}\n**Reason:** {}\n".format( - icon, action_string.title(), reason + embed.description = ( + f"{icon} **Action:** {action_string.title()}\n**Reason:** {reason}\n" ) if duration: embed.description += "**Duration:** {}\n".format( @@ -272,12 +270,9 @@ async def help_warnings( for i, warning in enumerate(records, 1): helper = ctx.guild.get_member(warning.helper_id) - if helper: - helper = helper.mention - else: - helper = "Unknown" + helper = helper.mention if helper else "Unknown" embed.add_field( - name="`{}.` Reason: {}".format(i, warning.reason), + name=f"`{i}.` Reason: {warning.reason}", value=f"Issued by: {helper} - ", inline=False, ) @@ -357,10 +352,8 @@ async def help_ban( await member.add_roles(help_ban_role) await self.bot.reply(ctx, f"help-banned {member.mention} with reason: {reason}") - try: + with contextlib.suppress(discord.Forbidden): await member.send(f"You have been help-banned with reason: {reason}") - except discord.Forbidden: - pass @helper.command(name="unban") async def help_unban( @@ -383,11 +376,9 @@ async def help_unban( await member.remove_roles(help_ban_role) await self.bot.reply(ctx, f"help-unbanned {member.mention}") - try: + with contextlib.suppress(discord.Forbidden): await member.send("You have been help-unbanned") await self.log(action="ban", undo=True, member=member, helper=ctx.author) - except discord.Forbidden: - pass @helper.command(name="verify") async def help_verify( diff --git a/cogs/listeners.py b/cogs/listeners.py index 44dc298..aa8e46f 100644 --- a/cogs/listeners.py +++ b/cogs/listeners.py @@ -3,11 +3,13 @@ import re import sys import asyncio +import contextlib import traceback from datetime import datetime from typing import TYPE_CHECKING import discord +from datetime import timezone from discord.ext import commands from ext.consts import TCR_STAFF_ROLE_ID from ext.helpers import check_invite @@ -51,17 +53,16 @@ async def thank_message(self, message: discord.Message): if message.author.bot or not message.guild: return - if message.channel.category.id == 754710748353265745: - if ( - "thanks" in message.content.lower() - or "thank you" in message.content.lower() - or "thx" in message.content.lower() - or "thnx" in message.content.lower() - ): - await message.reply( - "If someone has helped you, " - "you can thank them by using the `.thank` command." - ) + if message.channel.category.id == 754710748353265745 and ( + "thanks" in message.content.lower() + or "thank you" in message.content.lower() + or "thx" in message.content.lower() + or "thnx" in message.content.lower() + ): + await message.reply( + "If someone has helped you, " + "you can thank them by using the `.thank` command." + ) @commands.Cog.listener("on_message") async def afk_user_messaage(self, message: discord.Message): @@ -81,39 +82,36 @@ async def afk_user_messaage(self, message: discord.Message): record = self.bot.afk_cache.get(message.guild.id) if record: record = record.get(message.author.id) - if record: - _, time = record - if (datetime.utcnow() - datetime.utcfromtimestamp(time)).seconds < 30: - return - await self.bot.conn.delete_record( - "afk", - table="afk", - where=("user_id",), - values=(message.author.id,), - ) - try: - if "[AFK]" in message.author.display_name: - name = message.author.display_name.split(" ")[1:] - await message.author.edit(nick=" ".join(name)) - except (discord.HTTPException, discord.Forbidden): - pass - - staff_role = message.guild.get_role(795145820210462771) - if staff_role and staff_role in message.author.roles: - on_pat_staff = message.guild.get_role(726441123966484600) - try: - await message.author.add_roles(on_pat_staff) - except (discord.Forbidden, discord.HTTPException): - pass - del self.bot.afk_cache[message.guild.id][message.author.id] - em = discord.Embed( - description=f"{message.author.mention} Welcome back, " - "I removed your AFK!", - color=discord.Color.dark_gold(), - ) - msg = await message.reply(embed=em) - await asyncio.sleep(5) - await msg.delete() + if record: + _, time = record + if ( + datetime.now(timezone.utc) - datetime.utcfromtimestamp(time) + ).seconds < 30: + return + await self.bot.conn.delete_record( + "afk", + table="afk", + where=("user_id",), + values=(message.author.id,), + ) + with contextlib.suppress(discord.HTTPException, discord.Forbidden): + if "[AFK]" in message.author.display_name: + name = message.author.display_name.split(" ")[1:] + await message.author.edit(nick=" ".join(name)) + staff_role = message.guild.get_role(795145820210462771) + if staff_role and staff_role in message.author.roles: + on_pat_staff = message.guild.get_role(726441123966484600) + with contextlib.suppress(discord.Forbidden, discord.HTTPException): + await message.author.add_roles(on_pat_staff) + del self.bot.afk_cache[message.guild.id][message.author.id] + em = discord.Embed( + description=f"{message.author.mention} Welcome back, " + "I removed your AFK!", + color=discord.Color.dark_gold(), + ) + msg = await message.reply(embed=em) + await asyncio.sleep(5) + await msg.delete() @commands.Cog.listener("on_message") async def user_mentioned(self, message: discord.Message): @@ -191,10 +189,7 @@ async def on_command_error(self, ctx: commands.Context, error: Exception) -> Non ) return await ctx.send(embed=embed, ephemeral=True) else: - print( - "Ignoring exception in command {}:".format(ctx.command), - file=sys.stderr, - ) + print(f"Ignoring exception in command {ctx.command}:", file=sys.stderr) traceback.print_exception( type(error), error, error.__traceback__, file=sys.stderr ) @@ -209,9 +204,12 @@ async def track_sent_message(self, message: discord.Message): return values = [message.author.id, message.guild.id, 1, 1] - columns = ["user_id", "guild_id", "message_count"] - - columns.append(status := message.author.status.name) + columns = [ + "user_id", + "guild_id", + "message_count", + status := message.author.status.name, + ] staff_role = message.guild.get_role(TCR_STAFF_ROLE_ID) if staff_role and staff_role in message.author.roles: # type: ignore diff --git a/cogs/misc.py b/cogs/misc.py index 6242096..b18c8cf 100644 --- a/cogs/misc.py +++ b/cogs/misc.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Optional import button_paginator as pg +import contextlib import discord from discord.ext import commands from ext.helpers import Spotify, grouper, ordinal_suffix_of @@ -80,10 +81,8 @@ async def afk( ) # "on_patrol_staff" role if staff_role in member.roles: - try: - await member.remove_roles(on_pat_staff) - except (discord.Forbidden, discord.HTTPException): - pass + with contextlib.suppress(discord.Forbidden, discord.HTTPException): + await member.remove_roles(on_pat_staff) if ctx.guild.id not in self.bot.afk_cache: self.bot.afk_cache[ctx.guild.id] = {} if member.id not in self.bot.afk_cache.get(ctx.guild.id): @@ -93,10 +92,8 @@ async def afk( values=(member.id, reason, int(ctx.message.created_at.timestamp())), columns=["user_id", "reason", "afk_time"], ) - try: + with contextlib.suppress(Exception): await member.edit(nick=f"[AFK] {member.display_name}") - except Exception: - pass try: self.bot.afk_cache[ctx.guild.id][member.id] = ( reason, @@ -400,14 +397,12 @@ async def trainee_list(self, ctx: commands.Context[CodingBot]): """ trainee_role = ctx.guild.get_role(729537643951554583) # type: ignore - members = trainee_role.members - - if not members: - trainees = "No trainees yet." - else: + if members := trainee_role.members: trainees = "\n".join( f"{i}. {member.mention}" for i, member in enumerate(members, 1) ) + else: + trainees = "No trainees yet." embed = discord.Embed( title="Trainees list", description=trainees, color=discord.Color.blue() ) diff --git a/cogs/moderation.py b/cogs/moderation.py index a99b4be..0d07625 100644 --- a/cogs/moderation.py +++ b/cogs/moderation.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import contextlib import datetime from io import BytesIO from typing import Any, Dict, Optional, Union @@ -17,9 +18,8 @@ def trainee_check(): def wrapper(ctx: commands.Context[CodingBot]): trainee_role = ctx.guild.get_role(729537643951554583) # type: ignore - if trainee_role: - if ctx.author.top_role.position >= trainee_role.position: # type: ignore - return True + if trainee_role and ctx.author.top_role.position >= trainee_role.position: + return True raise InsufficientPrivilegeError( "{}, you don't have the permission to use this command.".format( ctx.author.mention @@ -78,28 +78,27 @@ async def capture_evidence( ) view_touched = not (await view.wait()) evidence_byts = None - if view_touched: - if view.confirmed: - initial_mess = await ctx.author.send( - "Please send the evidence in the form of an attachment." + if view_touched and view.confirmed: + initial_mess = await ctx.author.send( + "Please send the evidence in the form of an attachment." + ) + try: + wait_mess = await self.bot.wait_for( + "message", + check=lambda m: m.author == ctx.author + and m.channel == initial_mess.channel + and m.attachments + and not m.guild, + timeout=60, ) - try: - wait_mess = await self.bot.wait_for( - "message", - check=lambda m: m.author == ctx.author - and m.channel == initial_mess.channel - and m.attachments - and not m.guild, - timeout=60, - ) - except asyncio.TimeoutError: - await initial_mess.delete() - await ctx.author.send( - "You didn't send any evidence in time. " - "Proceeding with the ban without evidence." - ) - else: - evidence_byts = await wait_mess.attachments[0].read() + except asyncio.TimeoutError: + await initial_mess.delete() + await ctx.author.send( + "You didn't send any evidence in time. " + "Proceeding with the ban without evidence." + ) + else: + evidence_byts = await wait_mess.attachments[0].read() return evidence_byts async def log( @@ -173,15 +172,15 @@ async def log( raise undo_action action_string = ( - action_info["action"] if not undo else action_info["undo_action"] + action_info["undo_action"] if undo else action_info["action"] ) - icon = action_info["icon"] if not undo else action_info["undo_icon"] + icon = action_info["undo_icon"] if undo else action_info["icon"] color = discord.Color.green() if undo else action_info.get("color") embed = discord.Embed(color=color, timestamp=discord.utils.utcnow()) - embed.description = "{} **Action:** {}\n**Reason:** {}\n".format( - icon, action_string.title(), reason + embed.description = ( + f"{icon} **Action:** {action_string.title()}\n**Reason:** {reason}\n" ) if duration: embed.description += "**Duration:** {}\n".format( @@ -224,8 +223,7 @@ async def kick( {prefix}kick {user} Because I don't like them """ assert ctx.guild is not None - check_made = self.check_member_permission(ctx, member) - if check_made: + if check_made := self.check_member_permission(ctx, member): return await self.bot.reply(ctx, check_made) try: await member.send( @@ -262,22 +260,18 @@ async def ban( {prefix}ban {user} spamming """ assert ctx.guild is not None - check_made = self.check_member_permission(ctx, member) - if check_made: + if check_made := self.check_member_permission(ctx, member): return await self.bot.reply(ctx, check_made) - else: - try: - await member.send( - "You have been :hammer: **Banned** :hammer: from " - f"**{ctx.guild.name}**. \nReason: {reason}" - ) - except (discord.Forbidden, discord.HTTPException): - pass - await ctx.guild.ban(member, reason=reason, delete_message_days=7) - await self.bot.reply(ctx, f"Banned {member.mention}") - evidence = await self.capture_evidence(ctx) - await self.log(action="ban", moderator=ctx.author, member=member, - undo=False, reason=reason, duration=None, evidence=evidence) + with contextlib.suppress(discord.Forbidden, discord.HTTPException): + await member.send( + "You have been :hammer: **Banned** :hammer: from " + f"**{ctx.guild.name}**. \nReason: {reason}" + ) + await ctx.guild.ban(member, reason=reason, delete_message_days=7) + await self.bot.reply(ctx, f"Banned {member.mention}") + evidence = await self.capture_evidence(ctx) + await self.log(action="ban", moderator=ctx.author, member=member, + undo=False, reason=reason, duration=None, evidence=evidence) @commands.hybrid_command(name="unban") @commands.has_permissions(ban_members=True) @@ -327,8 +321,7 @@ async def mute( {prefix}mute [reason] """ assert ctx.guild is not None - check_made = self.check_member_permission(ctx, member) - if check_made: + if check_made := self.check_member_permission(ctx, member): return await self.bot.reply(ctx, check_made) try: @@ -366,8 +359,7 @@ async def unmute( Example: {prefix}unmute {user} I am feeling generous """ - check_made = self.check_member_permission(ctx, member) - if check_made: + if check_made := self.check_member_permission(ctx, member): return await self.bot.reply(ctx, check_made) try: await member.timeout(None) @@ -402,16 +394,13 @@ async def massban( ) counter_dict: Dict[str, Any] = {"banned": [], "not_banned": []} for user in users: - check_made = self.check_member_permission(ctx, user) - if check_made: + if check_made := self.check_member_permission(ctx, user): counter_dict["not_banned"].append(user) continue await ctx.guild.ban(user) # type: ignore counter_dict["banned"].append(user) embed = discord.Embed(color=discord.Color.red()) - description = "Following members were banned:\n{}".format( - ", ".join(f"{user.mention}" for user in counter_dict["banned"]) - ) + description = f'Following members were banned:\n{", ".join(f"{user.mention}" for user in counter_dict["banned"])}' if counter_dict["not_banned"]: embed.color = discord.Color.yellow() description += "\n\nFollowing members were not banned:\n" @@ -437,8 +426,7 @@ async def warn( Example: {prefix}warn {user} broke rules """ - check = self.check_member_permission(ctx, member, priv_level=0) - if check: + if check := self.check_member_permission(ctx, member, priv_level=0): return await self.bot.reply(ctx, check) assert ctx.guild is not None if not reason: @@ -506,12 +494,9 @@ async def warnings(self, ctx: commands.Context[CodingBot], member: discord.Membe for i, warning in enumerate(records, 1): moderator = ctx.guild.get_member(warning.moderator_id) - if moderator: - moderator = moderator.mention - else: - moderator = "Unknown" + moderator = moderator.mention if moderator else "Unknown" embed.add_field( - name="`{}.` Reason: {}".format(i, warning.reason), + name=f"`{i}.` Reason: {warning.reason}", value=f"Issued by: {moderator} - ", inline=False, ) @@ -862,22 +847,22 @@ async def raid_mode_enable(self, ctx: commands.Context[CodingBot]) -> None: This will ban all members that have joined during the raid. """ - if not self.bot.raid_mode_enabled: - if self.bot.raid_checker.possible_raid: - self.bot.raid_mode_enabled = True - await self.bot.reply(ctx, "Raid mode is now enabled.") - for member in self.bot.raid_checker.cache: - if self.bot.raid_checker.check(member): - await member.ban( - reason="Raid mode enabled and met raid criteria." - ) - else: - await self.bot.reply( - ctx, "There is no raid that has been detected yet." - ) - else: + if self.bot.raid_mode_enabled: await self.bot.reply(ctx, "Raid mode is already enabled.") + elif self.bot.raid_checker.possible_raid: + self.bot.raid_mode_enabled = True + await self.bot.reply(ctx, "Raid mode is now enabled.") + for member in self.bot.raid_checker.cache: + if self.bot.raid_checker.check(member): + await member.ban( + reason="Raid mode enabled and met raid criteria." + ) + else: + await self.bot.reply( + ctx, "There is no raid that has been detected yet." + ) + @raid_mode.command(name="disable") async def raid_mode_disable(self, ctx: commands.Context[CodingBot]) -> None: if self.bot.raid_mode_enabled: diff --git a/cogs/tasks.py b/cogs/tasks.py index 19ad6a2..4e5238f 100644 --- a/cogs/tasks.py +++ b/cogs/tasks.py @@ -72,25 +72,20 @@ async def status_change(self): "bob get a gf", "a documentary", ] - tcr = self.bot.get_guild(681882711945641997) - if tcr: + if tcr := self.bot.get_guild(681882711945641997): if tcr.get_role(795145820210462771): statuses.append( random.choice(tcr.get_role(795145820210462771).members).name ) # type: ignore if tcr.get_role(737517726737629214): statuses.append( - random.choice(tcr.get_role(737517726737629214).members).name - + " (Server Booster)" - ) # type: ignore + f"{random.choice(tcr.get_role(737517726737629214).members).name} (Server Booster)" + ) await self.bot.change_presence( activity=discord.Activity( type=discord.ActivityType.watching, - name=random.choice(statuses) - + " | " - + self.bot.default_prefixes[0] - + "help", + name=f"{random.choice(statuses)} | {self.bot.default_prefixes[0]}help", ) ) @@ -110,7 +105,7 @@ async def remove_inactive_warns(self): where=("guild_id",), values=(TCR_GUILD_ID,), ) - now = datetime.datetime.utcnow().timestamp() + now = datetime.datetime.now(datetime.timezone.utc).timestamp() if records: for record in records: if record.date + (60 * 60 * 24 * 31) < now: diff --git a/ext/helpers.py b/ext/helpers.py index e0b94ad..10ce3ce 100644 --- a/ext/helpers.py +++ b/ext/helpers.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import contextlib import datetime as dt import functools import itertools @@ -52,9 +53,8 @@ async def check_invite(bot, content, channel): invite = await bot.fetch_invite(code) except discord.errors.NotFound: invite = None - if invite: - if invite.guild.id not in whitelisted: - return True + if invite and invite.guild.id not in whitelisted: + return True return False @@ -68,10 +68,10 @@ async def find_anime_source(session, source_image: str): def grouper(n, iterable): it = iter(iterable) while True: - chunk = tuple(itertools.islice(it, n)) - if not chunk: + if chunk := tuple(itertools.islice(it, n)): + yield chunk + else: return - yield chunk def ordinal_suffix_of(i): @@ -88,7 +88,7 @@ def ordinal_suffix_of(i): async def log_error(bot: CodingBot, event_method: str, *args: Any, **kwargs: Any): channel = bot.get_channel(826861610173333595) try: - title = "Ignoring exception in {}".format(event_method) + title = f"Ignoring exception in {event_method}" err = "".join(traceback.format_exc()) embed = discord.Embed( title=title, @@ -100,7 +100,7 @@ async def log_error(bot: CodingBot, event_method: str, *args: Any, **kwargs: Any # channel is always a Messageable await channel.send(embed=embed) # type: ignore except (discord.errors.Forbidden, AttributeError): - print("Ignoring exception in {}".format(event_method), file=sys.stderr) + print(f"Ignoring exception in {event_method}", file=sys.stderr) traceback.print_exc() @@ -135,9 +135,7 @@ def create_trash_meme(member_avatar: BytesIO, author_avatar: BytesIO) -> discord buffer = BytesIO() background.save(buffer, format="PNG") buffer.seek(0) - file = discord.File(buffer, filename="Trash.png") - - return file + return discord.File(buffer, filename="Trash.png") class WelcomeBanner: @@ -236,8 +234,7 @@ def generate_image(self, member: discord.Member, **kwargs: Any) -> discord.File: buf = BytesIO() txt.save(buf, format="png") buf.seek(0) - file = discord.File(buf, filename="welcome.png") - return file + return discord.File(buf, filename="welcome.png") async def construct_image(self, **kwargs: Any) -> discord.File: member = kwargs.pop("member") @@ -251,10 +248,8 @@ async def construct_image(self, **kwargs: Any) -> discord.File: if i.inviter and i.inviter.id == inviter.id ) else: - try: + with contextlib.suppress(Exception): vanity = await member.guild.vanity_invite() - except Exception: - pass ago = dt.datetime.now(dt.timezone.utc) - member.created_at img = BytesIO( await member.display_avatar.with_format("png").with_size(128).read() @@ -266,7 +261,7 @@ async def construct_image(self, **kwargs: Any) -> discord.File: except AttributeError: banner = "./storage/banner.png" - file = await self.generate_image( + return await self.generate_image( member, inviter=inviter, vanity=vanity, @@ -275,7 +270,6 @@ async def construct_image(self, **kwargs: Any) -> discord.File: banner=banner, ago=ago, ) - return file class UrbanDefinition: @@ -360,10 +354,9 @@ def get_example(self, soup: BeautifulSoup, references: list) -> str: for example in examples: final_example = example for key, value in references: - if key not in final_example: - if final_example not in final_examples: - final_examples.append(final_example) - continue + if key not in final_example and final_example not in final_examples: + final_examples.append(final_example) + continue final_example = final_example.replace( key, f"[{key}]({self.BASE_URL}{value})" ) @@ -398,9 +391,8 @@ def get_meanings( for meaning in meanings: final_meaning = meaning for key, value in references: - if key not in final_meaning: - if final_meaning not in final_meanings: - continue + if key not in final_meaning and final_meaning not in final_meanings: + continue final_meaning = final_meaning.replace( key, f"[{key}]({self.BASE_URL}{value})" ) @@ -476,8 +468,7 @@ async def define(self, word: str, results: int = 1) -> List[UrbanDefinition]: raise Exception("Failed to get definition") else: text = await resp.text() - result = await self.parse(text, results) - return result + return await self.parse(text, results) class Spotify: @@ -593,7 +584,7 @@ async def get_from_local(self, bot, act: discord.Spotify) -> discord.File: s = tuple(f"{string.ascii_letters}{string.digits}{string.punctuation} ") artists = ", ".join(act.artists) artists = "".join([x for x in artists if x in s]) - artists = artists[0:36] + "..." if len(artists) > 36 else artists + artists = f"{artists[:36]}..." if len(artists) > 36 else artists time = act.duration.seconds time_at = ( dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc) - act.start @@ -601,7 +592,7 @@ async def get_from_local(self, bot, act: discord.Spotify) -> discord.File: track = time_at / time time = f"{time // 60:02d}:{time % 60:02d}" time_at = f"{int((time_at if time_at > 0 else 0) // 60):02d}:"\ - f"{int((time_at if time_at > 0 else 0) % 60):02d}" + f"{int((time_at if time_at > 0 else 0) % 60):02d}" pog = act.album_cover_url name = "".join([x for x in act.title if x in s]) name = name[0:21] + "..." if len(name) > 21 else name diff --git a/ext/http.py b/ext/http.py index deacd36..6dca49d 100644 --- a/ext/http.py +++ b/ext/http.py @@ -21,17 +21,27 @@ def __init__(self, session: aiohttp.ClientSession): # }, "numbers": { "random": lambda _type="trivia": self.api["numbers"][ - "random_" + _type + f"random_{_type}" ](), "number": lambda _type="trivia": self.api["numbers"][_type](), "random_trivia": lambda: self.get( "http://numbersapi.com/random/trivia" ), - "random_math": lambda: self.get("http://numbersapi.com/random/math"), - "random_date": lambda: self.get("http://numbersapi.com/random/date"), - "random_year": lambda: self.get("http://numbersapi.com/random/year"), - "date": lambda date: self.get(f"http://numbersapi.com/{date}/date"), - "year": lambda year: self.get(f"http://numbersapi.com/{year}/year"), + "random_math": lambda: self.get( + "http://numbersapi.com/random/math" + ), + "random_date": lambda: self.get( + "http://numbersapi.com/random/date" + ), + "random_year": lambda: self.get( + "http://numbersapi.com/random/year" + ), + "date": lambda date: self.get( + f"http://numbersapi.com/{date}/date" + ), + "year": lambda year: self.get( + f"http://numbersapi.com/{year}/year" + ), "trivia": lambda num: self.get(f"http://numbersapi.com/{num}"), "math": lambda num: self.get(f"http://numbersapi.com/{num}/math"), }, @@ -47,7 +57,9 @@ def __init__(self, session: aiohttp.ClientSession): ), }, "meme-api": { - "gimme": lambda: self.get("https://meme-api.com/gimme/", _json=True) + "gimme": lambda: self.get( + "https://meme-api.com/gimme/", _json=True + ) }, "some-random-api": { "bottoken": lambda: self.get( @@ -65,7 +77,9 @@ def __init__(self, session: aiohttp.ClientSession): "lyrics": lambda query: self.get( f"https://some-random-api.ml/lyrics?title={query}" ), - "joke": lambda: self.get("https://some-random-api.ml/joke", _json=True), + "joke": lambda: self.get( + "https://some-random-api.ml/joke", _json=True + ), "filters": { "invert": lambda pfp: f"https://some-random-api.ml/canvas/invert?avatar={pfp}", "greyscale": lambda pfp: f"https://some-random-api.ml/canvas/greyscale?avatar={pfp}", @@ -76,7 +90,8 @@ def __init__(self, session: aiohttp.ClientSession): }, "joke": { "api": lambda: self.get( - "https://backend-omega-seven.vercel.app/api/getjoke", _json=True + "https://backend-omega-seven.vercel.app/api/getjoke", + _json=True, ), }, } diff --git a/ext/models.py b/ext/models.py index b6aa674..f1d6592 100644 --- a/ext/models.py +++ b/ext/models.py @@ -188,18 +188,16 @@ async def select_record( values: Optional[Tuple[Any, ...]] = None, extras: Optional[List[str]] = None, ) -> Optional[List[Record]]: - statement = """SELECT {} FROM {}""".format(", ".join(arguments), table) + statement = f"""SELECT {", ".join(arguments)} FROM {table}""" if where is not None: assign_question = map(lambda x: f"{x} = ?", where) - statement += " WHERE {}".format(" AND ".join(assign_question)) + statement += f' WHERE {" AND ".join(assign_question)}' if extras: for stuff in extras: statement += f" {stuff}" async with self.cursor(connection) as cursor: await cursor.execute(statement, values or ()) - # type: ignore # Type checker cries unnecessarily. - rows: List[aiosqlite.Row[Any]] = [i async for i in cursor] - if rows: + if rows := [i async for i in cursor]: return [Record.from_tuple(arguments, row) for row in rows] return None @@ -215,7 +213,7 @@ async def delete_record( delete_statement = f"DELETE FROM {table}" if where is not None: assign_question = map(lambda x: f"{x} = ?", where) - delete_statement += " WHERE {}".format(" AND ".join(assign_question)) + delete_statement += f' WHERE {" AND ".join(assign_question)}' async with self.cursor(connection) as cursor: await cursor.execute(delete_statement, values or ()) @@ -390,8 +388,7 @@ async def setup_hook(self) -> None: os.environ["JISHAKU_NO_UNDERSCORE"] = "True" await self.load_extension("jishaku") self.logger.info("Loaded Jishaku Cog") - jishaku = self.get_cog("Jishaku") - if jishaku: + if jishaku := self.get_cog("Jishaku"): jishaku.hidden = True async def start(self, token: str, *, reconnect: bool = True) -> None: @@ -424,8 +421,7 @@ async def on_member_join(self, member: discord.Member) -> None: if banned: return - rules = member.guild.rules_channel - if rules: + if rules := member.guild.rules_channel: rules_channel = rules.mention else: rules_channel = "No official rule channel set yet." diff --git a/ext/ui/view.py b/ext/ui/view.py index 066bcf8..a4fe804 100644 --- a/ext/ui/view.py +++ b/ext/ui/view.py @@ -44,9 +44,7 @@ async def timer(self): return await self.msg.edit( embed=self.cog.bot.embed( - title="compiling **[** {} **]**".format( - int(time.time()) - self.timestamp - ) + title=f"compiling **[** {int(time.time()) - self.timestamp} **]**" ) ) @@ -67,14 +65,13 @@ async def get_code_out(self): for line in lines: if len(line) > 500: output.extend(sliced(line, 500)) - else: - if len(output) > 0: - if len(output[-1].split("\n")) > 15: - output.append(line + "\n") - else: - output[-1] += line + "\n" - else: + elif output: + if len(output[-1].split("\n")) > 15: output.append(line + "\n") + else: + output[-1] += line + "\n" + else: + output.append(line + "\n") self.output = output self.ran = self.res["ran"] self.is_compiled = True From 7f14d53cd221c022d9b2c06862eebd9f74a43d7d Mon Sep 17 00:00:00 2001 From: ImgBotApp Date: Tue, 27 Jun 2023 09:06:43 +0000 Subject: [PATCH 2/2] [ImgBot] Optimize images /storage/Trash.png -- 1,873.96kb -> 383.95kb (79.51%) Signed-off-by: ImgBotApp --- storage/Trash.png | Bin 1918934 -> 393168 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/storage/Trash.png b/storage/Trash.png index a4950fac1510e521e8306781bc91f6adb5c5337f..3eaee6cb2b5190a2ab1c522cf2ad38624bd29d84 100644 GIT binary patch literal 393168 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