diff --git a/tux/cogs/fun/fact.py b/tux/cogs/fun/fact.py index 8c635dc..12b135c 100644 --- a/tux/cogs/fun/fact.py +++ b/tux/cogs/fun/fact.py @@ -53,14 +53,17 @@ class Fact(commands.Cog): user_display_avatar=ctx.author.display_avatar.url, title="Fun Fact", description=random.choice(self.facts), + custom_author_text="Click here to submit more facts here!", + custom_author_text_url="https://github.com/allthingslinux/tux/blob/main/tux/cogs/fun/fact.py", + custom_author_icon_url="https://github.com/allthingslinux/tux/blob/main/assets/emojis/tux_info.png?raw=true", ) - # set author - embed.set_author( - name="Submit more facts here!", - url="https://github.com/allthingslinux/tux/blob/main/tux/cogs/fun/fact.py", - icon_url="https://github.com/allthingslinux/tux/blob/main/assets/emojis/tux_info.png?raw=true", - ) + # # set author + # embed.set_author( + # name="Submit more facts here!", + # url="https://github.com/allthingslinux/tux/blob/main/tux/cogs/fun/fact.py", + # icon_url="https://github.com/allthingslinux/tux/blob/main/assets/emojis/tux_info.png?raw=true", + # ) await ctx.send(embed=embed) diff --git a/tux/cogs/fun/xkcd.py b/tux/cogs/fun/xkcd.py index 5d8740f..f229d23 100644 --- a/tux/cogs/fun/xkcd.py +++ b/tux/cogs/fun/xkcd.py @@ -128,10 +128,10 @@ class Xkcd(commands.Cog): embed_type=EmbedCreator.INFO, title="", description=f"\n\n> {comic.description.strip()}" if comic.description else "", + custom_author_text=f"xkcd {comic.id} - {comic.title}", + image_url=comic.image_url, ) - embed.set_author(name=f"xkcd {comic.id} - {comic.title}") - embed.set_image(url=comic.image_url) ephemeral = False except xkcd.HttpError: diff --git a/tux/cogs/guild/config.py b/tux/cogs/guild/config.py index d3a9fe1..404fa49 100644 --- a/tux/cogs/guild/config.py +++ b/tux/cogs/guild/config.py @@ -6,7 +6,7 @@ from discord.ext import commands from tux.bot import Tux from tux.database.controllers import DatabaseController -from tux.ui.embeds import EmbedCreator +from tux.ui.embeds import EmbedCreator, EmbedType from tux.ui.views.config import ConfigSetChannels, ConfigSetPrivateLogs, ConfigSetPublicLogs from tux.utils.constants import CONST @@ -183,10 +183,11 @@ class Config(commands.GroupCog, group_name="config"): assert interaction.guild - embed = discord.Embed( + embed = EmbedCreator.create_embed( title="Config - Roles", - color=discord.Color.blue(), - timestamp=discord.utils.utcnow(), + embed_type=EmbedType.INFO, + custom_color=discord.Color.blue(), + message_timestamp=discord.utils.utcnow(), ) jail_role_id = await self.db.get_jail_role_id(interaction.guild.id) @@ -213,10 +214,11 @@ class Config(commands.GroupCog, group_name="config"): assert interaction.guild - embed = discord.Embed( + embed = EmbedCreator.create_embed( + embed_type=EmbedType.INFO, + custom_color=discord.Color.blue(), title="Config - Permission Level Roles", - color=discord.Color.blue(), - timestamp=discord.utils.utcnow(), + message_timestamp=discord.utils.utcnow(), ) for i in range(8): @@ -245,10 +247,11 @@ class Config(commands.GroupCog, group_name="config"): assert interaction.guild - embed = discord.Embed( + embed = EmbedCreator.create_embed( title="Config - Channels", - color=discord.Color.blue(), - timestamp=discord.utils.utcnow(), + embed_type=EmbedType.INFO, + custom_color=discord.Color.blue(), + message_timestamp=discord.utils.utcnow(), ) jail_channel_id = await self.db.get_jail_channel_id(interaction.guild.id) @@ -283,10 +286,11 @@ class Config(commands.GroupCog, group_name="config"): assert interaction.guild - embed = discord.Embed( + embed = EmbedCreator.create_embed( title="Config - Logs", - color=discord.Color.blue(), - timestamp=discord.utils.utcnow(), + embed_type=EmbedType.INFO, + custom_color=discord.Color.blue(), + message_timestamp=discord.utils.utcnow(), ) join_log_id = await self.db.get_join_log_id(interaction.guild.id) diff --git a/tux/cogs/info/info.py b/tux/cogs/info/info.py index 6ab1ef5..c4fd3db 100644 --- a/tux/cogs/info/info.py +++ b/tux/cogs/info/info.py @@ -5,12 +5,14 @@ from discord.ext import commands from reactionmenu import ViewButton, ViewMenu from tux.bot import Tux +from tux.ui.embeds import EmbedCreator, EmbedType class Info(commands.Cog): def __init__(self, bot: Tux) -> None: self.bot = bot + @commands.guild_only() @commands.hybrid_group(name="info", aliases=["i"], usage="info ") async def info(self, ctx: commands.Context[Tux]) -> None: """ @@ -25,6 +27,7 @@ class Info(commands.Cog): if ctx.invoked_subcommand is None: await ctx.send_help("info") + @commands.guild_only() @info.command(name="server", aliases=["s"], usage="info server") async def server(self, ctx: commands.Context[Tux]) -> None: """ @@ -37,14 +40,18 @@ class Info(commands.Cog): """ guild = ctx.guild assert guild + assert guild.icon embed: discord.Embed = ( - discord.Embed( + EmbedCreator.create_embed( + embed_type=EmbedType.INFO, title=guild.name, description=guild.description or "No description available.", - color=discord.Color.blurple(), + 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')}", ) - .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) @@ -57,11 +64,11 @@ class Info(commands.Cog): .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')}") ) await ctx.send(embed=embed) + @commands.guild_only() @info.command(name="member", aliases=["m", "user", "u"], usage="info member [member]") async def member(self, ctx: commands.Context[Tux], member: discord.Member) -> None: """ @@ -74,15 +81,15 @@ class Info(commands.Cog): member : discord.Member The member to get information about. """ + user = await self.bot.fetch_user(member.id) embed: discord.Embed = ( - discord.Embed( + EmbedCreator.create_embed( + embed_type=EmbedType.INFO, title=member.display_name, + custom_color=discord.Color.blurple(), 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 + thumbnail_url=member.display_avatar.url, + image_url=user.banner.url if user.banner else None, ) .add_field(name="Bot?", value="✅" if member.bot else "❌", inline=False) .add_field(name="Username", value=member.name, inline=False) @@ -106,6 +113,7 @@ class Info(commands.Cog): await ctx.send(embed=embed) + @commands.guild_only() @info.command(name="roles", aliases=["r"], usage="info roles") async def roles(self, ctx: commands.Context[Tux]) -> None: """ @@ -166,7 +174,11 @@ class Info(commands.Cog): chunk_size : int The size of each chunk for pagination. """ - embed: discord.Embed = discord.Embed(title=title, color=discord.Color.blurple()) + embed: discord.Embed = EmbedCreator.create_embed( + embed_type=EmbedType.INFO, + title=title, + custom_color=discord.Color.blurple(), + ) chunks: list[list[str]] = list(self._chunks(iter(items), chunk_size)) if not chunks: diff --git a/tux/cogs/moderation/__init__.py b/tux/cogs/moderation/__init__.py index acc1297..ca4f4d8 100644 --- a/tux/cogs/moderation/__init__.py +++ b/tux/cogs/moderation/__init__.py @@ -7,7 +7,7 @@ from loguru import logger from prisma.enums import CaseType from tux.bot import Tux from tux.database.controllers import DatabaseController -from tux.ui.embeds import EmbedCreator +from tux.ui.embeds import EmbedCreator, EmbedType from tux.utils.constants import Constants as CONST @@ -17,6 +17,7 @@ class ModerationCogBase(commands.Cog): self.db = DatabaseController() self.config = DatabaseController().guild_config + # TODO: Get rid of create_embed in the ModerationCogBase command as its only being used once and is replacable def create_embed( self, ctx: commands.Context[Tux], @@ -50,17 +51,22 @@ class ModerationCogBase(commands.Cog): discord.Embed The embed for the moderation action. """ - - embed = discord.Embed(color=color, timestamp=timestamp or ctx.message.created_at) - embed.set_author(name=title, icon_url=icon_url) - embed.set_thumbnail(url=thumbnail_url) - footer_text, footer_icon_url = EmbedCreator.get_footer( bot=self.bot, user_name=ctx.author.name, user_display_avatar=ctx.author.display_avatar.url, ) - embed.set_footer(text=footer_text, icon_url=footer_icon_url) + + embed = EmbedCreator.create_embed( + embed_type=EmbedType.INFO, + custom_color=color, + message_timestamp=timestamp or ctx.message.created_at, + custom_author_text=title, + custom_author_icon_url=icon_url, + thumbnail_url=thumbnail_url, + custom_footer_text=footer_text, + custom_footer_icon_url=footer_icon_url, + ) for name, value, inline in fields: embed.add_field(name=name, value=value, inline=inline) diff --git a/tux/cogs/moderation/cases.py b/tux/cogs/moderation/cases.py index 26d66f8..137dcfe 100644 --- a/tux/cogs/moderation/cases.py +++ b/tux/cogs/moderation/cases.py @@ -6,7 +6,7 @@ from prisma.enums import CaseType from prisma.models import Case from prisma.types import CaseWhereInput from tux.bot import Tux -from tux.ui.embeds import EmbedCreator +from tux.ui.embeds import EmbedCreator, EmbedType from tux.utils import checks from tux.utils.constants import Constants as CONST from tux.utils.flags import CaseModifyFlags, CasesViewFlags, generate_usage @@ -276,10 +276,10 @@ class Cases(ModerationCogBase): ) embed.set_thumbnail(url=user.avatar) else: - embed = discord.Embed( + embed = EmbedCreator.create_embed( + embed_type=EmbedType.ERROR, title=f"Case {action}", description="Failed to find case.", - color=CONST.EMBED_COLORS["ERROR"], ) await ctx.send(embed=embed, delete_after=30, ephemeral=True) @@ -293,10 +293,10 @@ class Cases(ModerationCogBase): menu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed, all_can_click=True, delete_on_timeout=True) if not cases: - embed = discord.Embed( + embed = EmbedCreator.create_embed( + embed_type=EmbedType.ERROR, title="Cases", description="No cases found.", - color=CONST.EMBED_COLORS["ERROR"], ) await ctx.send(embed=embed, delete_after=30, ephemeral=True) return @@ -337,21 +337,24 @@ class Cases(ModerationCogBase): cases: list[Case], total_cases: int, ) -> discord.Embed: - embed = discord.Embed( - title=f"Total Cases ({total_cases})", - description="", - color=CONST.EMBED_COLORS["CASE"], - ) - - if ctx.guild: - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) + assert ctx.guild + assert ctx.guild.icon footer_text, footer_icon_url = EmbedCreator.get_footer( bot=self.bot, user_name=ctx.author.name, user_display_avatar=ctx.author.display_avatar.url, ) - embed.set_footer(text=footer_text, icon_url=footer_icon_url) + + embed = EmbedCreator.create_embed( + title=f"Total Cases ({total_cases})", + description="", + embed_type=EmbedType.CASE, + custom_author_text=ctx.guild.name, + custom_author_icon_url=ctx.guild.icon.url, + custom_footer_text=footer_text, + custom_footer_icon_url=footer_icon_url, + ) for case in cases: self._add_case_to_embed(embed, case) diff --git a/tux/cogs/services/starboard.py b/tux/cogs/services/starboard.py index 7d05c27..5c1a7c2 100644 --- a/tux/cogs/services/starboard.py +++ b/tux/cogs/services/starboard.py @@ -6,7 +6,7 @@ from loguru import logger from tux.bot import Tux from tux.database.controllers.starboard import StarboardController, StarboardMessageController -from tux.ui.embeds import EmbedCreator +from tux.ui.embeds import EmbedCreator, EmbedType from tux.utils import checks @@ -263,20 +263,17 @@ class Starboard(commands.Cog): if not starboard: return - embed = discord.Embed( - description=original_message.content, - color=discord.Color.gold(), - timestamp=original_message.created_at, + embed = EmbedCreator.create_embed( + embed_type=EmbedType.INFO, + custom_color=discord.Color.gold(), + message_timestamp=original_message.created_at, + custom_author_text=original_message.author.display_name, + custom_author_icon_url=original_message.author.avatar.url if original_message.author.avatar else None, + custom_footer_text=f"{reaction_count} {starboard.starboard_emoji}", + image_url=original_message.attachments[0].url if original_message.attachments else None, ) - embed.set_author( - name=original_message.author.display_name, - icon_url=original_message.author.avatar.url if original_message.author.avatar else None, - ) - embed.add_field(name="Source", value=f"[Jump to message]({original_message.jump_url})") - embed.set_footer(text=f"{reaction_count} {starboard.starboard_emoji}") - if original_message.attachments: - embed.set_image(url=original_message.attachments[0].url) + embed.add_field(name="Source", value=f"[Jump to message]({original_message.jump_url})") starboard_message = await self.get_existing_starboard_message(starboard_channel, original_message) diff --git a/tux/cogs/utility/query.py b/tux/cogs/utility/query.py index f84164d..e5a93d4 100644 --- a/tux/cogs/utility/query.py +++ b/tux/cogs/utility/query.py @@ -107,6 +107,8 @@ class Query(commands.Cog): user_display_avatar=ctx.author.display_avatar.url, title=f'Answer to "{search_term}"', description=f"{data['Abstract']}\n\nData from **{data['AbstractURL']}**", + custom_footer_text="Data via DuckDuckGo API.", + custom_footer_icon_url="https://duckduckgo.com/favicon.png", ) embed.set_author( @@ -115,11 +117,6 @@ class Query(commands.Cog): icon_url=f"https://duckduckgo.com{data['Image']}" if data["Image"] else CONST.EMBED_ICONS["DEFAULT"], ) - embed.set_footer( - text="Data via DuckDuckGo API.", - icon_url="https://duckduckgo.com/favicon.png", - ) - if redirect: embed.add_field( name="Search Term Changed", diff --git a/tux/cogs/utility/snippets.py b/tux/cogs/utility/snippets.py index 9011a82..90cfcdf 100644 --- a/tux/cogs/utility/snippets.py +++ b/tux/cogs/utility/snippets.py @@ -12,9 +12,8 @@ from prisma.enums import CaseType from prisma.models import Snippet from tux.bot import Tux from tux.database.controllers import CaseController, DatabaseController -from tux.ui.embeds import EmbedCreator +from tux.ui.embeds import EmbedCreator, EmbedType from tux.utils import checks -from tux.utils.constants import Constants as CONST class Snippets(commands.Cog): @@ -56,18 +55,10 @@ class Snippets(commands.Cog): # Remove snippets that are not in the current server snippets = [snippet for snippet in snippets if snippet.guild_id == ctx.guild.id] - # If there are no snippets, send an error message + # If there are no snippets, return if not snippets: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="No snippets found.", - ) - await ctx.send(embed=embed, delete_after=30) + await self.send_snippet_error(ctx, description="No snippets found.") return - menu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed) snippets_per_page = 10 @@ -89,22 +80,8 @@ class Snippets(commands.Cog): snippets: list[Snippet], total_snippets: int, ) -> discord.Embed: - embed = discord.Embed( - title=f"Total Snippets ({total_snippets})", - description="", - color=CONST.EMBED_COLORS["DEFAULT"], - ) - - if ctx.guild: - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - - footer_text, footer_icon_url = EmbedCreator.get_footer( - bot=ctx.bot, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - ) - embed.set_footer(text=footer_text, icon_url=footer_icon_url) - embed.timestamp = ctx.message.created_at + assert ctx.guild + assert ctx.guild.icon description = "```\n" @@ -114,9 +91,22 @@ class Snippets(commands.Cog): description += "```" - embed.description = description + footer_text, footer_icon_url = EmbedCreator.get_footer( + bot=ctx.bot, + user_name=ctx.author.name, + user_display_avatar=ctx.author.display_avatar.url, + ) - return embed + return EmbedCreator.create_embed( + embed_type=EmbedType.DEFAULT, + title=f"Total Snippets ({total_snippets})", + description=description, + custom_author_text=ctx.guild.name, + custom_author_icon_url=ctx.guild.icon.url, + message_timestamp=ctx.message.created_at, + custom_footer_text=footer_text, + custom_footer_icon_url=footer_icon_url, + ) @commands.command( name="topsnippets", @@ -138,16 +128,9 @@ class Snippets(commands.Cog): # find the top 10 snippets by uses snippets: list[Snippet] = await self.db.get_all_snippets_by_guild_id(ctx.guild.id) - # If there are no snippets, send an error message + # If there are no snippets, return if not snippets: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="No snippets found.", - ) - await ctx.send(embed=embed, delete_after=30) + await self.send_snippet_error(ctx, description="No snippets found.") return # sort the snippets by uses @@ -165,10 +148,11 @@ class Snippets(commands.Cog): text += "```" # only show top 10, no pagination - embed = discord.Embed( + embed = EmbedCreator.create_embed( + embed_type=EmbedType.DEFAULT, title="Top Snippets", description=text, - color=CONST.EMBED_COLORS["DEFAULT"], + hide_author=True, ) await ctx.send(embed=embed) @@ -196,39 +180,21 @@ class Snippets(commands.Cog): snippet = await self.db.get_snippet_by_name_and_guild_id(name, ctx.guild.id) if snippet is None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet not found.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="Snippet not found.") return # check if the snippet is locked if snippet.locked: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, + await self.send_snippet_error( + ctx, description="This snippet is locked and cannot be deleted. If you are a moderator you can use the `forcedeletesnippet` command.", ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) return # Check if the author of the snippet is the same as the user who wants to delete it and if theres no author don't allow deletion author_id = snippet.snippet_user_id or 0 if author_id != ctx.author.id: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="You can only delete your own snippets.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="You can only delete your own snippets.") return await self.db.delete_snippet_by_id(snippet.snippet_id) @@ -260,14 +226,7 @@ class Snippets(commands.Cog): snippet = await self.db.get_snippet_by_name_and_guild_id(name, ctx.guild.id) if snippet is None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet not found.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="Snippet not found.") return await self.db.delete_snippet_by_id(snippet.snippet_id) @@ -297,30 +256,16 @@ class Snippets(commands.Cog): snippet = await self.db.get_snippet_by_name_and_guild_id(name, ctx.guild.id) + # check if the name contains an underscore if "_" in name: - snippet = None # this is a bad fix, but it works for now - if snippet is None and "_" in name: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet not found. Did you mean to use `-` instead of `_`? Due to a recent change, `_` is no longer allowed in snippet names.", + await self.send_snippet_error( + ctx, + description="Did you mean to use `-` instead of `_`? Due to a recent change, `_` is no longer allowed in snippet names.", ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) return if snippet is None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet not found.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="No snippets found.") return - - # increment the usage count of the snippet await self.db.increment_snippet_uses(snippet.snippet_id) # example text: @@ -355,14 +300,7 @@ class Snippets(commands.Cog): snippet = await self.db.get_snippet_by_name_and_guild_id(name, ctx.guild.id) if snippet is None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet not found.", - ) - await ctx.send(embed=embed, delete_after=30) + await self.send_snippet_error(ctx, description="Snippet not found.") return author = self.bot.get_user(snippet.snippet_user_id) @@ -373,6 +311,7 @@ class Snippets(commands.Cog): user_name=ctx.author.name, user_display_avatar=ctx.author.display_avatar.url, title="Snippet Information", + message_timestamp=snippet.snippet_created_at or datetime.datetime.fromtimestamp(0, datetime.UTC), ) embed.add_field(name="Name", value=snippet.snippet_name, inline=False) @@ -385,11 +324,6 @@ class Snippets(commands.Cog): embed.add_field(name="Uses", value=snippet.uses, inline=False) embed.add_field(name="Locked", value="Yes" if snippet.locked else "No", inline=False) - embed.timestamp = snippet.snippet_created_at or datetime.datetime.fromtimestamp( - 0, - datetime.UTC, - ) - await ctx.send(embed=embed) @commands.command( @@ -418,14 +352,7 @@ class Snippets(commands.Cog): args = arg.split(" ") if len(args) < 2: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Please provide a name and content for the snippet.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="Please provide a name and content for the snippet.") return name = args[0] @@ -436,28 +363,17 @@ class Snippets(commands.Cog): # Check if the snippet already exists if await self.db.get_snippet_by_name_and_guild_id(name, ctx.guild.id) is not None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet already exists.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="Snippet already exists.") return # Check if the name is longer than 20 characters and includes non-alphanumeric characters (except -) rules = set(string.ascii_letters + string.digits + "-") if len(name) > 20 or any(char not in rules for char in name): - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet name must be alphanumeric (allows dashes and underscores) and less than 20 characters.", + await self.send_snippet_error( + ctx, + description="Snippet name must be alphanumeric (allows dashes only) and less than 20 characters.", ) - await ctx.send(embed=embed) return await self.db.create_snippet( @@ -493,14 +409,7 @@ class Snippets(commands.Cog): args = arg.split(" ") if len(args) < 2: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Please provide a name and content for the snippet.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="Please provide a name and content for the snippet.") return name = args[0] @@ -509,14 +418,7 @@ class Snippets(commands.Cog): snippet = await self.db.get_snippet_by_name_and_guild_id(name, ctx.guild.id) if snippet is None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet not found.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="Snippet not found.") return if await self.is_snippetbanned(ctx.guild.id, ctx.author.id): @@ -532,28 +434,17 @@ class Snippets(commands.Cog): try: await checks.has_pl(2).predicate(ctx) except commands.CheckFailure: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, + await self.send_snippet_error( + ctx, description="This snippet is locked and cannot be edited. If you are a moderator you can use the `forcedeletesnippet` command.", ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) return logger.info(f"{ctx.author} has the permission level to edit locked snippets.") # Check if the author of the snippet is the same as the user who wants to edit it and if theres no author don't allow editing author_id = snippet.snippet_user_id or 0 if author_id != ctx.author.id: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="You can only edit your own snippets.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="You can only edit your own snippets.") return await self.db.update_snippet_by_id( @@ -588,27 +479,16 @@ class Snippets(commands.Cog): snippet = await self.db.get_snippet_by_name_and_guild_id(name, ctx.guild.id) if snippet is None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="Snippet not found.", - ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) + await self.send_snippet_error(ctx, description="Snippet not found.") return status = await self.db.toggle_snippet_lock_by_id(snippet.snippet_id) if status is None: - embed = EmbedCreator.create_embed( - bot=self.bot, - embed_type=EmbedCreator.ERROR, - user_name=ctx.author.name, - user_display_avatar=ctx.author.display_avatar.url, - description="No return value from locking the snippet. It may still have been locked.", + await self.send_snippet_error( + ctx, + "No return value from locking the snippet. It may still have been locked.", ) - await ctx.send(embed=embed, delete_after=30, ephemeral=True) return if author := self.bot.get_user(snippet.snippet_user_id): @@ -626,6 +506,24 @@ Snippets are usually locked by moderators if they are important to usual use of await ctx.send("Snippet lock toggled.", delete_after=30, ephemeral=True) logger.info(f"{ctx.author} toggled the lock of the snippet with the name {name}.") + async def send_snippet_error(self, ctx: commands.Context[Tux], description: str) -> None: + """ + Send an error message to the channel if there are no snippets found. + + Parameters + ---------- + ctx : commands.Context[Tux] + The context object. + """ + embed = EmbedCreator.create_embed( + bot=self.bot, + embed_type=EmbedCreator.ERROR, + user_name=ctx.author.name, + user_display_avatar=ctx.author.display_avatar.url, + description=description, + ) + await ctx.send(embed=embed, delete_after=30) + async def setup(bot: Tux) -> None: await bot.add_cog(Snippets(bot)) diff --git a/tux/cogs/utility/timezones.py b/tux/cogs/utility/timezones.py index 166840b..dc3df02 100644 --- a/tux/cogs/utility/timezones.py +++ b/tux/cogs/utility/timezones.py @@ -6,6 +6,7 @@ from discord.ext import commands from reactionmenu import Page, ViewButton, ViewMenu, ViewSelect from tux.bot import Tux +from tux.ui.embeds import EmbedCreator, EmbedType timezones = { "North America": [ @@ -108,7 +109,11 @@ class Timezones(commands.Cog): pages = [tz_list[i : i + 9] for i in range(0, len(tz_list), 9)] for page in pages: - embed = discord.Embed(title=f"Timezones in {continent}", color=discord.Color.blurple()) + embed = EmbedCreator.create_embed( + embed_type=EmbedType.INFO, + title=f"Timezones in {continent}", + custom_color=discord.Color.blurple(), + ) for flag, _country, tz_name, abbr, utc_offset in page: tz = pytz.timezone(tz_name) diff --git a/tux/handlers/event.py b/tux/handlers/event.py index c64204d..4f5a068 100644 --- a/tux/handlers/event.py +++ b/tux/handlers/event.py @@ -3,6 +3,7 @@ from discord.ext import commands from tux.bot import Tux from tux.database.controllers import DatabaseController +from tux.ui.embeds import EmbedCreator, EmbedType from tux.utils.functions import is_harmful, strip_formatting @@ -87,7 +88,12 @@ class EventHandler(commands.Cog): else: msg = f"<:tux_notify:1274504953666474025> **New support thread created** - help is appreciated!\n{thread.mention} by {owner_mention}" - embed = discord.Embed(description=msg, color=discord.Color.random()) + embed = EmbedCreator.create_embed( + embed_type=EmbedType.INFO, + description=msg, + custom_color=discord.Color.random(), + hide_author=True, + ) general_chat = 1172245377395728467 channel = self.bot.get_channel(general_chat) diff --git a/tux/ui/embeds.py b/tux/ui/embeds.py index e52852c..e3937e5 100644 --- a/tux/ui/embeds.py +++ b/tux/ui/embeds.py @@ -43,8 +43,11 @@ class EmbedCreator: custom_footer_text: str | None = None, custom_footer_icon_url: str | None = None, custom_author_text: str | None = None, + custom_author_text_url: str | None = None, custom_author_icon_url: str | None = None, - custom_color: int | None = None, + custom_color: int | discord.Colour | None = None, + hide_author: bool = False, + hide_timestamp: bool = False, ) -> discord.Embed: """ Create a customized Discord embed based on the specified type and parameters. @@ -62,8 +65,10 @@ class EmbedCreator: custom_footer_text (str | None): Overrides default footer text if provided. custom_footer_icon_url (str | None): Overrides default footer icon if provided. custom_author_text (str | None): Overrides default author text if provided. + custom_author_text_url (str | None): Adds author URL if provided. custom_author_icon_url (str | None): Overrides default author icon if provided. - custom_color (int | None): Overrides default color for the embed type if provided. + hide_author (bool): If True, removes the author from the embed. + custom_color (int | Colour |None): Overrides default color for the embed type if provided. Note: Custom parameters (prefixed with 'custom_') override default values. @@ -84,10 +89,12 @@ class EmbedCreator: embed.color = custom_color or type_settings[embed_type][0] - embed.set_author( - name=custom_author_text or type_settings[embed_type][2], - icon_url=custom_author_icon_url or type_settings[embed_type][1], - ) + if not hide_author: + embed.set_author( + name=custom_author_text or type_settings[embed_type][2], + icon_url=custom_author_icon_url or type_settings[embed_type][1], + url=custom_author_text_url, + ) if custom_footer_text: embed.set_footer(text=custom_footer_text, icon_url=custom_footer_icon_url) @@ -101,7 +108,8 @@ class EmbedCreator: if thumbnail_url: embed.set_thumbnail(url=thumbnail_url) - embed.timestamp = message_timestamp or discord.utils.utcnow() + if not hide_timestamp: + embed.timestamp = message_timestamp or discord.utils.utcnow() except Exception as e: logger.debug("Error in create_embed", exc_info=e) diff --git a/tux/utils/exports.py b/tux/utils/exports.py index b02587d..94172df 100644 --- a/tux/utils/exports.py +++ b/tux/utils/exports.py @@ -69,12 +69,7 @@ async def get_help_embed( return EmbedCreator.create_embed( embed_type=EmbedCreator.INFO, title=title, - description="Use any combination of the following flags to " - + f"export a list of {data_description} to a CSV file:" - + "\n```" - + "\n--all\n" - + "\n".join([f"--{flag}" for flag in valid_flags]) - + "\n```", + description=f"Use any combination of the following flags to export a list of {data_description} to a CSV file:\n```--all\n{'\n'.join([f'--{flag}' for flag in valid_flags])}```", )