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

refactor(info.py): remove unnecessary line of code to improve readability and maintainability

refactor(info.py): simplify command decorators for readability
refactor(info.py): change ctx type from commands.Context[commands.Bot] to commands.Context for simplicity
feat(info.py): add paginated_embed method to handle paginated embeds in a more reusable way
fix(info.py): check if guild exists before proceeding with commands to prevent errors
feat(info.py): add more detailed server and member information to embeds
refactor(info.py): streamline embed creation for cleaner code
refactor(info.py): remove unused _create_embed_desc method
refactor(info.py): update _chunks method to return list of strings for consistency
refactor(info.py): update _add_buttons_to_menu method to return ViewMenu for consistency

refactor: replace individual button addition with loop to improve code readability and maintainability
This commit is contained in:
kzndotsh 2024-08-31 18:04:36 +00:00
parent cd30e811ef
commit ab01455674

View file

@ -9,233 +9,232 @@ class Info(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@commands.hybrid_group(
name="info",
aliases=["i"],
usage="info <subcommand>",
)
@commands.hybrid_group(name="info", aliases=["i"], usage="info <subcommand>")
async def info(self, ctx: commands.Context[commands.Bot]) -> None:
"""
Information commands.
Parameters
----------
ctx : commands.Context[commands.Bot]
The discord context object.
ctx : commands.Context
The context object associated with the command.
"""
if ctx.invoked_subcommand is None:
await ctx.send_help("info")
@info.command(
name="server",
aliases=["s"],
usage="info server",
)
@info.command(name="server", aliases=["s"], usage="info server")
async def server(self, ctx: commands.Context[commands.Bot]) -> None:
"""
Show information about the server.
Parameters
----------
ctx : commands.Context[commands.Bot]
The discord context object.
ctx : commands.Context
The context object associated with the command.
"""
if not ctx.guild:
return
guild = ctx.guild
if not guild:
return
embed = discord.Embed(
title=ctx.guild.name,
description=guild.description or "No description available.",
color=discord.Color.blurple(),
embed: discord.Embed = (
discord.Embed(
title=guild.name,
description=guild.description or "No description available.",
color=discord.Color.blurple(),
)
.set_author(name="Server Information", icon_url=guild.icon)
.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")
.add_field(name="Boosts", value=guild.premium_subscription_count)
.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)}/{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))
.add_field(name="Bots", value=sum(member.bot for member in guild.members))
.add_field(name="Bans", value=len([entry async for entry in guild.bans(limit=2000)]))
.set_footer(text=f"ID: {guild.id} | Created: {guild.created_at.strftime('%B %d, %Y')}")
)
embed.set_author(name="Server Information", icon_url=guild.icon)
embed.add_field(name="Owner", value=str(guild.owner.mention) if guild.owner else "Unknown")
embed.add_field(name="Vanity URL", value=guild.vanity_url_code or "None")
embed.add_field(name="Boosts", value=guild.premium_subscription_count)
embed.add_field(name="Text Channels", value=len(guild.text_channels))
embed.add_field(name="Voice Channels", value=len(guild.voice_channels))
embed.add_field(name="Forum Channels", value=len(guild.forums))
embed.add_field(name="Emojis", value=f"{len(guild.emojis)}/{guild.emoji_limit}")
embed.add_field(name="Stickers", value=f"{len(guild.stickers)}/{guild.sticker_limit}")
embed.add_field(name="Roles", value=len(guild.roles))
embed.add_field(name="Humans", value=sum(not member.bot for member in guild.members))
embed.add_field(name="Bots", value=sum(member.bot for member in guild.members))
embed.add_field(name="Bans", value=len([entry async for entry in guild.bans(limit=2000)]))
embed.set_footer(text=f"ID: {guild.id} | Created: {guild.created_at.strftime('%B %d, %Y')}")
await ctx.send(embed=embed)
@info.command(
name="member",
aliases=["m", "user", "u"],
usage="info member [member]",
)
@info.command(name="member", aliases=["m", "user", "u"], usage="info member [member]")
async def member(self, ctx: commands.Context[commands.Bot], member: discord.Member) -> None:
"""
Show information about a member.
Parameters
----------
ctx : commands.Context[commands.Bot]
The discord context object.
ctx : commands.Context
The context object associated with the command.
member : discord.Member
The member to get information about.
"""
bot_status = "" if member.bot else ""
joined = discord.utils.format_dt(member.joined_at, "R") if member.joined_at else "Unknown"
created = discord.utils.format_dt(member.created_at, "R") if member.created_at else "Unknown"
roles = ", ".join(role.mention for role in member.roles[1:]) if member.roles[1:] else "No roles"
fetched_member = await self.bot.fetch_user(member.id)
embed = discord.Embed(
title=member.display_name,
description="Here is some information about the member.",
color=discord.Color.blurple(),
embed: discord.Embed = (
discord.Embed(
title=member.display_name,
description="Here is some information about the member.",
color=discord.Color.blurple(),
)
.set_thumbnail(url=member.display_avatar.url)
.set_image(
url=(await self.bot.fetch_user(member.id)).banner, # Fetched member's banner
)
.add_field(name="Bot?", value="" if member.bot else "", inline=False)
.add_field(name="Username", value=member.name, inline=False)
.add_field(name="ID", value=str(member.id), inline=False)
.add_field(
name="Joined",
value=discord.utils.format_dt(member.joined_at, "R") if member.joined_at else "Unknown",
inline=False,
)
.add_field(
name="Registered",
value=discord.utils.format_dt(member.created_at, "R") if member.created_at else "Unknown",
inline=False,
)
.add_field(
name="Roles",
value=", ".join(role.mention for role in member.roles[1:]) if member.roles[1:] else "No roles",
inline=False,
)
)
embed.set_thumbnail(url=member.display_avatar.url)
embed.set_image(url=fetched_member.banner)
embed.add_field(name="Bot?", value=bot_status, inline=False)
embed.add_field(name="Username", value=member.name, inline=False)
embed.add_field(name="ID", value=str(member.id), inline=False)
embed.add_field(name="Joined", value=joined, inline=False)
embed.add_field(name="Registered", value=created, inline=False)
embed.add_field(name="Roles", value=roles, inline=False)
await ctx.send(embed=embed)
@info.command(
name="roles",
aliases=["r"],
usage="info roles",
)
@info.command(name="roles", aliases=["r"], usage="info roles")
async def roles(self, ctx: commands.Context[commands.Bot]) -> None:
"""
List all roles in the server.
Parameters
----------
ctx : commands.Context[commands.Bot]
The discord context object.
ctx : commands.Context
The context object associated with the command.
"""
if not ctx.guild:
return
guild = ctx.guild
roles = [role.mention for role in guild.roles]
embed = discord.Embed(
title="Server Roles",
color=discord.Color.blurple(),
)
chunk_size = 32
if not len(roles) > chunk_size:
embed.description = self._create_embed_desc("roles", guild.name, roles)
await ctx.send(embed=embed)
if not guild:
return
chunks = self._chunks(iter(roles), chunk_size)
menu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed)
roles: list[str] = [role.mention for role in guild.roles]
for chunk in chunks:
embed = embed.copy()
await self.paginated_embed(ctx, "Server Roles", "roles", guild.name, roles, 32)
embed.description = self._create_embed_desc("roles", guild.name, chunk)
menu.add_page(embed)
menu = self._add_buttons_to_menu(menu)
await menu.start()
@info.command(
name="emotes",
aliases=["e"],
usage="info emotes",
)
@info.command(name="emotes", aliases=["e"], usage="info emotes")
async def emotes(self, ctx: commands.Context[commands.Bot]) -> None:
"""
List all emotes in the server.
Parameters
----------
ctx : commands.Context[commands.Bot]
The discord context object.
ctx : commands.Context
The context object associated with the command.
"""
if not ctx.guild:
return
guild = ctx.guild
emotes: list[str] = [str(emote) for emote in guild.emojis]
embed = discord.Embed(
title="Server Emotes",
color=discord.Color.blurple(),
)
chunk_size = 128
if not len(emotes) > chunk_size:
embed.description = self._create_embed_desc("emotes", guild.name, emotes)
await ctx.send(embed=embed)
if not guild:
return
chunks = self._chunks(iter(emotes), chunk_size)
menu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed)
emotes: list[str] = [str(emote) for emote in guild.emojis]
await self.paginated_embed(ctx, "Server Emotes", "emotes", guild.name, emotes, 128)
for chunk in chunks:
embed = embed.copy()
embed.description = self._create_embed_desc("emotes", guild.name, chunk)
menu.add_page(embed)
menu = self._add_buttons_to_menu(menu)
await menu.start()
def _chunks[T](self, it: Iterator[T], size: int) -> Generator[list[T], None, None]:
async def paginated_embed(
self,
ctx: commands.Context[commands.Bot],
title: str,
list_type: str,
guild_name: str,
items: Iterable[str],
chunk_size: int,
) -> None:
"""
Split an iterator into chunks of a specified size.
This function takes an iterator and divides it into chunks of a given size.
Any remaining elements that do not fill a complete chunk are included in the
final chunk, which may be smaller than the specified size.
Send a paginated embed.
Parameters
----------
it : Iterator[T]
ctx : commands.Context
The context object associated with the command.
title : str
The title of the embed.
list_type : str
The type of list (e.g., roles, emotes).
guild_name : str
The name of the guild.
items : Iterable[str]
The items to display in the embed.
chunk_size : int
The size of each chunk for pagination.
"""
embed: discord.Embed = discord.Embed(title=title, color=discord.Color.blurple())
chunks: list[list[str]] = list(self._chunks(iter(items), chunk_size))
if not chunks:
embed.description = "No items available."
await ctx.send(embed=embed)
return
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)}"
menu.add_page(page_embed)
self._add_buttons_to_menu(menu)
await menu.start()
def _chunks(self, it: Iterator[str], size: int) -> Generator[list[str], None, None]:
"""
Split an iterator into chunks of a specified size.
Parameters
----------
it : Iterator[str]
The input iterator to be split into chunks.
size : int
The size of each chunk.
Yields
-------
list[T]
------
List[str]
A list containing a chunk of elements from the input iterator. The last
list may contain fewer elements if there are not enough remaining to fill
a complete chunk.
"""
chunk: list[T] = []
chunk: list[str] = []
for item in it:
chunk.append(item)
if len(chunk) == size:
yield chunk
chunk = []
if chunk: # if last chunk has any items
if chunk:
yield chunk
def _create_embed_desc(self, list_type: str, guild_name: str, items: Iterable[str]) -> str:
return (
f"{list_type.capitalize()} list for {guild_name}:\n {" ".join(items) if items else "No items available."}"
)
def _add_buttons_to_menu(self, menu: ViewMenu) -> ViewMenu:
"""
Add buttons to the menu.
def _add_buttons_to_menu[T: ViewMenu](self, menu: T) -> T:
menu.add_button(ViewButton.go_to_first_page())
menu.add_button(ViewButton.back())
menu.add_button(ViewButton.next())
menu.add_button(ViewButton.go_to_last_page())
menu.add_button(ViewButton.end_session())
Parameters
----------
menu : ViewMenu
The menu to add buttons to.
Returns
-------
ViewMenu
The menu with buttons added.
"""
buttons = [
ViewButton.go_to_first_page(),
ViewButton.back(),
ViewButton.next(),
ViewButton.go_to_last_page(),
ViewButton.end_session(),
]
for button in buttons:
menu.add_button(button)
return menu