1
Fork 0
mirror of https://github.com/allthingslinux/tux.git synced 2024-10-02 16:43:12 +00:00

refactor: convert methods to static methods in various classes for improved clarity and usability

This commit is contained in:
kzndotsh 2024-09-21 01:14:32 -04:00
parent cb135e4468
commit edb0b5b29b
17 changed files with 134 additions and 106 deletions

View file

@ -99,7 +99,8 @@ class Mail(commands.Cog):
delete_after=30,
)
def _generate_password(self) -> str:
@staticmethod
def _generate_password() -> str:
password = "changeme" + "".join(str(random.randint(0, 9)) for _ in range(6))
password += "".join(random.choice("!@#$%^&*") for _ in range(4))
return password
@ -161,7 +162,8 @@ class Mail(commands.Cog):
delete_after=30,
)
def _extract_mailbox_info(self, result: list[dict[str, str | None]]) -> str | None:
@staticmethod
def _extract_mailbox_info(result: list[dict[str, str | None]]) -> str | None:
for item in result:
if "msg" in item:
msg = item["msg"]
@ -173,8 +175,8 @@ class Mail(commands.Cog):
return None
@staticmethod
async def _send_dm(
self,
interaction: discord.Interaction,
member: discord.Member,
mailbox_info: str,

View file

@ -14,71 +14,46 @@ from tux.ui.embeds import EmbedCreator
class ImgEffect(commands.Cog):
def __init__(self, bot: Tux) -> None:
self.bot = bot
self.allowed_mimetypes = [
"image/jpeg",
"image/png",
]
self.allowed_mimetypes = ["image/jpeg", "image/png"]
imgeffect = app_commands.Group(name="imgeffect", description="Image effects")
@imgeffect.command(
name="deepfry",
description="Deepfry an image",
)
@imgeffect.command(name="deepfry", description="Deepfry an image")
async def deepfry(self, interaction: discord.Interaction, image: discord.Attachment) -> None:
"""
Deepfry an image.
Parameters
----------
interaction : discord.Interaction
The interaction object for the command.
image : discord.File
The image to deepfry.
"""
# check if the image is a image
logger.info(f"Content type: {image.content_type}, Filename: {image.filename}, URL: {image.url}")
if image.content_type not in self.allowed_mimetypes:
logger.error("The file is not a permitted image.")
embed = EmbedCreator.create_embed(
bot=self.bot,
embed_type=EmbedCreator.ERROR,
user_name=interaction.user.name,
user_display_avatar=interaction.user.display_avatar.url,
title="Invalid File",
description="The file must be an image. Allowed types are PNG, JPEG, and JPG.",
)
await interaction.response.send_message(embed=embed, ephemeral=True)
if not self.is_valid_image(image):
await self.send_invalid_image_response(interaction)
return
# say that the image is being processed
logger.info("Processing image...")
await interaction.response.defer(ephemeral=True)
# open url with PIL
logger.info("Opening image with PIL and HTTPX...")
pil_image = await self.fetch_image(image.url)
if pil_image:
deepfried_image = self.deepfry_image(pil_image)
await self.send_deepfried_image(interaction, deepfried_image)
else:
await self.send_error_response(interaction)
def is_valid_image(self, image: discord.Attachment) -> bool:
logger.info(f"Content type: {image.content_type}, Filename: {image.filename}, URL: {image.url}")
return image.content_type in self.allowed_mimetypes
@staticmethod
async def fetch_image(url: str) -> Image.Image:
logger.info("Fetching image from URL with HTTPX...")
async with httpx.AsyncClient() as client:
response = await client.get(image.url)
response = await client.get(url)
pil_image = Image.open(io.BytesIO(response.content))
pil_image = pil_image.convert("RGB")
logger.info("Image opened with PIL.")
return Image.open(io.BytesIO(response.content)).convert("RGB")
# resize image to 25% then back to original size
logger.info("Resizing image...")
@staticmethod
def deepfry_image(pil_image: Image.Image) -> Image.Image:
pil_image = pil_image.resize((int(pil_image.width * 0.25), int(pil_image.height * 0.25)))
logger.info("Image resized.")
# increase sharpness
logger.info("Increasing sharpness...")
pil_image = ImageEnhance.Sharpness(pil_image).enhance(100.0)
logger.info("Sharpness increased.")
logger.info("Adjusting color...")
r = pil_image.split()[0]
r = ImageEnhance.Contrast(r).enhance(2.0)
r = ImageEnhance.Brightness(r).enhance(1.5)
@ -86,14 +61,43 @@ class ImgEffect(commands.Cog):
colours = ((254, 0, 2), (255, 255, 15))
r = ImageOps.colorize(r, colours[0], colours[1])
pil_image = Image.blend(pil_image, r, 0.75)
logger.info("Color adjustment complete.")
# send image
logger.info("Sending image...")
pil_image = pil_image.resize((int(pil_image.width * 4), int(pil_image.height * 4)))
return pil_image.resize((int(pil_image.width * 4), int(pil_image.height * 4)))
async def send_invalid_image_response(self, interaction: discord.Interaction) -> None:
logger.error("The file is not a permitted image.")
embed = EmbedCreator.create_embed(
bot=self.bot,
embed_type=EmbedCreator.ERROR,
user_name=interaction.user.name,
user_display_avatar=interaction.user.display_avatar.url,
title="Invalid File",
description="The file must be an image. Allowed types are PNG, JPEG, and JPG.",
)
await interaction.response.send_message(embed=embed, ephemeral=True)
async def send_error_response(self, interaction: discord.Interaction) -> None:
logger.error("Error processing the image.")
embed = EmbedCreator.create_embed(
bot=self.bot,
embed_type=EmbedCreator.ERROR,
user_name=interaction.user.name,
user_display_avatar=interaction.user.display_avatar.url,
title="Error",
description="An error occurred while processing the image.",
)
await interaction.response.send_message(embed=embed, ephemeral=True)
@staticmethod
async def send_deepfried_image(interaction: discord.Interaction, deepfried_image: Image.Image) -> None:
arr = io.BytesIO()
pil_image.save(arr, format="JPEG", quality=1)
deepfried_image.save(arr, format="JPEG", quality=1)
arr.seek(0)
file = discord.File(arr, filename="deepfried.jpg")
await interaction.followup.send(file=file, ephemeral=True)

View file

@ -57,7 +57,13 @@ class Random(commands.Cog):
aliases=["eightball", "8b"],
)
@commands.guild_only()
async def eight_ball(self, ctx: commands.Context[Tux], *, question: str, cow: bool = False) -> None:
async def eight_ball(
self,
ctx: commands.Context[Tux],
*,
question: str,
cow: bool = False,
) -> None:
"""
Ask the magic 8ball a question.

View file

@ -65,7 +65,7 @@ class Info(commands.Cog):
custom_color=discord.Color.blurple(),
custom_author_text="Server Information",
custom_author_icon_url=guild.icon.url,
custom_footer_text=f"ID: {guild.id} | Created: {guild.created_at.strftime('%B %d, %Y')}",
custom_footer_text=f"ID: {guild.id} | Created: {guild.created_at.strftime("%B %d, %Y")}",
)
.add_field(name="Owner", value=str(guild.owner.mention) if guild.owner else "Unknown")
.add_field(name="Vanity URL", value=guild.vanity_url_code or "None")
@ -73,7 +73,7 @@ class Info(commands.Cog):
.add_field(name="Text Channels", value=len(guild.text_channels))
.add_field(name="Voice Channels", value=len(guild.voice_channels))
.add_field(name="Forum Channels", value=len(guild.forums))
.add_field(name="Emojis", value=f"{len(guild.emojis)}/{2*guild.emoji_limit}")
.add_field(name="Emojis", value=f"{len(guild.emojis)}/{2 * guild.emoji_limit}")
.add_field(name="Stickers", value=f"{len(guild.stickers)}/{guild.sticker_limit}")
.add_field(name="Roles", value=len(guild.roles))
.add_field(name="Humans", value=sum(not member.bot for member in guild.members))
@ -213,7 +213,7 @@ class Info(commands.Cog):
menu: ViewMenu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed)
for chunk in chunks:
page_embed: discord.Embed = embed.copy()
page_embed.description = f"{list_type.capitalize()} list for {guild_name}:\n{' '.join(chunk)}"
page_embed.description = f"{list_type.capitalize()} list for {guild_name}:\n{" ".join(chunk)}"
menu.add_page(page_embed)
buttons = [
@ -229,7 +229,8 @@ class Info(commands.Cog):
await menu.start()
def _chunks(self, it: Iterator[str], size: int) -> Generator[list[str], None, None]:
@staticmethod
def _chunks(it: Iterator[str], size: int) -> Generator[list[str], None, None]:
"""
Split an iterator into chunks of a specified size.

View file

@ -100,8 +100,8 @@ class ModerationCogBase(commands.Cog):
if isinstance(log_channel, discord.TextChannel):
await log_channel.send(embed=embed)
@staticmethod
async def send_dm(
self,
ctx: commands.Context[Tux],
silent: bool,
user: discord.Member,

View file

@ -319,8 +319,8 @@ class Cases(ModerationCogBase):
await menu.start()
@staticmethod
def _create_case_fields(
self,
moderator: discord.Member,
user: discord.Member | discord.User,
reason: str,
@ -361,7 +361,8 @@ class Cases(ModerationCogBase):
return embed
def _format_emoji(self, emoji: discord.Emoji | None) -> str:
@staticmethod
def _format_emoji(emoji: discord.Emoji | None) -> str:
return f"<:{emoji.name}:{emoji.id}>" if emoji else ""
def _get_case_status_emoji(self, case_status: bool | None) -> discord.Emoji | None:
@ -392,16 +393,16 @@ class Cases(ModerationCogBase):
def _get_case_action_emoji(self, case_type: CaseType) -> discord.Emoji | None:
action = None
if case_type in [
if case_type in {
CaseType.BAN,
CaseType.KICK,
CaseType.TIMEOUT,
CaseType.WARN,
CaseType.JAIL,
CaseType.SNIPPETBAN,
]:
}:
action = "added"
elif case_type in [CaseType.UNBAN, CaseType.UNTIMEOUT, CaseType.UNJAIL, CaseType.SNIPPETUNBAN]:
elif case_type in {CaseType.UNBAN, CaseType.UNTIMEOUT, CaseType.UNJAIL, CaseType.SNIPPETUNBAN}:
action = "removed"
if action is not None:
@ -410,8 +411,8 @@ class Cases(ModerationCogBase):
return self.bot.get_emoji(emoji_id)
return None
@staticmethod
def _get_case_description(
self,
case: Case,
case_status_emoji: str,
case_type_emoji: str,

View file

@ -105,8 +105,8 @@ class Jail(ModerationCogBase):
dm_sent = await self.send_dm(ctx, flags.silent, member, flags.reason, "jailed")
await self.handle_case_response(ctx, CaseType.JAIL, case.case_number, flags.reason, member, dm_sent)
@staticmethod
def _get_manageable_roles(
self,
member: discord.Member,
jail_role: discord.Role,
) -> list[discord.Role]:

View file

@ -15,7 +15,8 @@ class Slowmode(commands.Cog):
@commands.hybrid_command(
name="slowmode",
aliases=["sm"],
usage="slowmode <delay|get> [channel]\nor slowmode [channel] <delay|get>", # only place where generate_usage shouldn't be used
# only place where generate_usage shouldn't be used:
usage="slowmode <delay|get> [channel]\nor slowmode [channel] <delay|get>",
)
@commands.guild_only()
@checks.has_pl(2)
@ -83,11 +84,12 @@ class Slowmode(commands.Cog):
return action, channel
def _get_channel(self, ctx: commands.Context[Tux]) -> discord.TextChannel | discord.Thread | None:
@staticmethod
def _get_channel(ctx: commands.Context[Tux]) -> discord.TextChannel | discord.Thread | None:
return ctx.channel if isinstance(ctx.channel, discord.TextChannel | discord.Thread) else None
@staticmethod
async def _get_slowmode(
self,
ctx: commands.Context[Tux],
channel: discord.TextChannel | discord.Thread,
) -> None:
@ -135,7 +137,8 @@ class Slowmode(commands.Cog):
await ctx.send(f"Failed to set slowmode. Error: {error}", delete_after=30, ephemeral=True)
logger.error(f"Failed to set slowmode. Error: {error}")
def _parse_delay(self, delay: str) -> int | None:
@staticmethod
def _parse_delay(delay: str) -> int | None:
try:
if delay.endswith("s"):
delay = delay[:-1]

View file

@ -52,7 +52,7 @@ class Bookmarks(commands.Cog):
message: discord.Message,
) -> discord.Embed:
if len(message.content) > CONST.EMBED_MAX_DESC_LENGTH:
message.content = f"{message.content[:CONST.EMBED_MAX_DESC_LENGTH - 3]}..."
message.content = f"{message.content[: CONST.EMBED_MAX_DESC_LENGTH - 3]}..."
embed = EmbedCreator.create_embed(
bot=self.bot,
@ -71,8 +71,8 @@ class Bookmarks(commands.Cog):
return embed
@staticmethod
async def _send_bookmark(
self,
user: discord.User,
message: discord.Message,
embed: discord.Embed,

View file

@ -46,7 +46,8 @@ class Run(commands.Cog):
self.run.usage = generate_usage(self.run)
self.languages.usage = generate_usage(self.languages)
def remove_ansi(self, ansi: str) -> str:
@staticmethod
def remove_ansi(ansi: str) -> str:
"""
Converts ANSI encoded text into non-ANSI.
@ -63,7 +64,8 @@ class Run(commands.Cog):
return ansi_re.sub("", ansi)
def remove_backticks(self, st: str) -> str:
@staticmethod
def remove_backticks(st: str) -> str:
"""
Removes backticks from the provided string.
@ -277,7 +279,7 @@ class Run(commands.Cog):
code,
)
await msg.delete()
if filtered_output == "" and gen_one == "" and normalized_lang == "":
if not filtered_output and not gen_one and not normalized_lang:
return
await self.send_embedded_reply(
ctx,
@ -340,7 +342,7 @@ class Run(commands.Cog):
user_name=ctx.author.name,
user_display_avatar=ctx.author.display_avatar.url,
title="Supported Languages",
description=f"```{', '.join(compiler_map.keys())}```",
description=f"```{", ".join(compiler_map.keys())}```",
)
await ctx.send(embed=embed)

View file

@ -152,7 +152,7 @@ class Snippets(commands.Cog):
text = "```\n"
for i, snippet in enumerate(snippets[:10]):
text += f"{i+1}. {snippet.snippet_name.ljust(20)} | uses: {snippet.uses}\n"
text += f"{i + 1}. {snippet.snippet_name.ljust(20)} | uses: {snippet.uses}\n"
text += "```"
# only show top 10, no pagination
@ -321,7 +321,7 @@ class Snippets(commands.Cog):
embed.add_field(name="Name", value=snippet.snippet_name, inline=False)
embed.add_field(
name="Author",
value=f"{author.mention if author else f'<@!{snippet.snippet_user_id}>'}",
value=f"{author.mention if author else f"<@!{snippet.snippet_user_id}>"}",
inline=False,
)
embed.add_field(name="Content", value=f"> {snippet.snippet_content}", inline=False)
@ -495,7 +495,7 @@ class Snippets(commands.Cog):
if author := self.bot.get_user(snippet.snippet_user_id):
with contextlib.suppress(discord.Forbidden):
await author.send(
f"""Your snippet `{snippet.snippet_name}` has been {'locked' if status.locked else 'unlocked'}.
f"""Your snippet `{snippet.snippet_name}` has been {"locked" if status.locked else "unlocked"}.
**What does this mean?**
If a snippet is locked, it cannot be edited by anyone other than moderators. This means that you can no longer edit this snippet.

View file

@ -135,7 +135,8 @@ class Tldr(commands.Cog):
return self._run_subprocess(["tldr", "--list"], "No TLDR pages found.").split("\n")
def _run_subprocess(self, command_list: list[str], default_response: str) -> str:
@staticmethod
def _run_subprocess(command_list: list[str], default_response: str) -> str:
"""
Helper method to run subprocesses for CLI interactions.

View file

@ -13,7 +13,8 @@ class ActivityHandler(commands.Cog):
self.delay = delay
self.activities = self.build_activity_list()
def build_activity_list(self) -> list[discord.Activity | discord.Streaming]:
@staticmethod
def build_activity_list() -> list[discord.Activity | discord.Streaming]:
activity_data = [
{"type": discord.ActivityType.watching, "name": "{member_count} members"},
{"type": discord.ActivityType.watching, "name": "All Things Linux"},

View file

@ -273,7 +273,8 @@ class ErrorHandler(commands.Cog):
return error_map.get(type(error), self.error_message).format(error=error)
def log_error_traceback(self, error: Exception) -> None:
@staticmethod
def log_error_traceback(error: Exception) -> None:
"""
Log the error traceback.

View file

@ -20,7 +20,8 @@ class EventHandler(commands.Cog):
async def on_guild_remove(self, guild: discord.Guild) -> None:
await self.db.guild.delete_guild_by_id(guild.id)
async def handle_harmful_message(self, message: discord.Message) -> None:
@staticmethod
async def handle_harmful_message(message: discord.Message) -> None:
if message.author.bot:
return

View file

@ -38,7 +38,8 @@ class TuxHelp(commands.HelpCommand):
return self.context.clean_prefix or CONST.DEFAULT_PREFIX
def _embed_base(self, title: str, description: str | None = None) -> discord.Embed:
@staticmethod
def _embed_base(title: str, description: str | None = None) -> discord.Embed:
"""
Creates a base embed with uniform styling.
@ -61,8 +62,8 @@ class TuxHelp(commands.HelpCommand):
color=CONST.EMBED_COLORS["DEFAULT"],
)
@staticmethod
def _add_command_field(
self,
embed: discord.Embed,
command: commands.Command[Any, Any, Any],
prefix: str,
@ -84,11 +85,12 @@ class TuxHelp(commands.HelpCommand):
embed.add_field(
name=f"{prefix}{command.qualified_name} ({command_aliases})",
value=f"> {command.short_doc or 'No documentation summary.'}",
value=f"> {command.short_doc or "No documentation summary."}",
inline=False,
)
def _get_flag_type(self, flag_annotation: Any) -> str:
@staticmethod
def _get_flag_type(flag_annotation: Any) -> str:
"""
Determines the type of a flag based on its annotation.
@ -111,7 +113,8 @@ class TuxHelp(commands.HelpCommand):
case _:
return str(flag_annotation)
def _format_flag_name(self, flag: commands.Flag) -> str:
@staticmethod
def _format_flag_name(flag: commands.Flag) -> str:
"""
Formats the flag name based on whether it is required.
@ -158,11 +161,11 @@ class TuxHelp(commands.HelpCommand):
flag_str = self._format_flag_name(flag)
if flag.aliases:
flag_str += f" ({', '.join(flag.aliases)})"
flag_str += f" ({", ".join(flag.aliases)})"
# else:
# flag_str += f" : {flag_type}"
flag_str += f"\n\t{flag.description or 'No description provided.'}"
flag_str += f"\n\t{flag.description or "No description provided."}"
if flag.default is not discord.utils.MISSING:
flag_str += f"\n\tDefault: {flag.default}"
@ -289,12 +292,13 @@ class TuxHelp(commands.HelpCommand):
for command in mapping_commands:
cmd_name_and_aliases = f"`{command.name}`"
if command.aliases:
cmd_name_and_aliases += f" ({', '.join(f'`{alias}`' for alias in command.aliases)})"
cmd_name_and_aliases += f" ({", ".join(f"`{alias}`" for alias in command.aliases)})"
command_categories[cog_group][command.name] = cmd_name_and_aliases
return command_categories
def _get_cog_groups(self) -> list[str]:
@staticmethod
def _get_cog_groups() -> list[str]:
"""
Retrieves a list of cog groups from the 'cogs' folder.
@ -349,8 +353,8 @@ class TuxHelp(commands.HelpCommand):
return select_options
@staticmethod
def _add_navigation_and_selection(
self,
menu: ViewMenu,
select_options: dict[discord.SelectOption, list[Page]],
) -> None:
@ -368,7 +372,8 @@ class TuxHelp(commands.HelpCommand):
menu.add_select(ViewSelect(title="Command Categories", options=select_options))
menu.add_button(ViewButton.end_session())
def _extract_cog_group(self, cog: commands.Cog) -> str | None:
@staticmethod
def _extract_cog_group(cog: commands.Cog) -> str | None:
"""
Extracts the cog group from a cog's string representation.
@ -424,7 +429,7 @@ class TuxHelp(commands.HelpCommand):
embed = self._embed_base(
title=f"{prefix}{command.qualified_name}",
description=f"> {command.help or 'No documentation available.'}",
description=f"> {command.help or "No documentation available."}",
)
await self._add_command_help_fields(embed, command)
@ -450,12 +455,12 @@ class TuxHelp(commands.HelpCommand):
embed.add_field(
name="Usage",
value=f"`{prefix}{command.usage or 'No usage.'}`",
value=f"`{prefix}{command.usage or "No usage."}`",
inline=False,
)
embed.add_field(
name="Aliases",
value=(f"`{', '.join(command.aliases)}`" if command.aliases else "No aliases."),
value=(f"`{", ".join(command.aliases)}`" if command.aliases else "No aliases."),
inline=False,
)
@ -471,7 +476,7 @@ class TuxHelp(commands.HelpCommand):
prefix = await self._get_prefix()
embed = self._embed_base(f"{group.name}", f"> {group.help or 'No documentation available.'}")
embed = self._embed_base(f"{group.name}", f"> {group.help or "No documentation available."}")
await self._add_command_help_fields(embed, group)
for command in group.commands:

View file

@ -37,7 +37,7 @@ def generate_usage(
flags: dict[str, commands.Flag] = flag_converter.get_flags() if flag_converter else {}
for param_name, param in parameters.items():
if param_name in ["ctx", "flags"]:
if param_name in {"ctx", "flags"}:
continue
is_required = param.default == inspect.Parameter.empty
matching_string = get_matching_string(param_name)
@ -60,7 +60,7 @@ def generate_usage(
usage += f" {flag}"
if optional_flags:
usage += f" [{' | '.join(optional_flags)}]"
usage += f" [{" | ".join(optional_flags)}]"
return usage