diff --git a/embed/embedcolor.py b/embed/embedcolor.py index df19031..b8c07fc 100644 --- a/embed/embedcolor.py +++ b/embed/embedcolor.py @@ -61,8 +61,14 @@ class EmbedColor(commands.Cog): db.commit() db.close() - await interaction.response.send_message(f"Your embed color has been set to `{hex_color}`") + embed = discord.Embed( + title="✅ Embed Color Updated", + description=f"Your embed color has been set to `{hex_color}`.\nHere's how it looks!", + color=discord.Color(int(hex_color[1:], 16)) + ) + await interaction.response.send_message(embed=embed) + @embed_group.command(name="view", description="View your current embed color") async def view_color(self, interaction: discord.Interaction): color = self.get_user_color(interaction.user) diff --git a/profiles/profiles.py b/profiles/profiles.py index 969495a..d57f254 100644 --- a/profiles/profiles.py +++ b/profiles/profiles.py @@ -24,7 +24,7 @@ class Profiles(commands.Cog): self.bot = bot setup_db() - @app_commands.command(name="listfonts", description="List all avaliable fonts for profiles.") + @app_commands.command(name="listfonts", description="List all available fonts with a visual preview.") async def list_fonts_cmd(self, interaction: discord.Interaction): await interaction.response.defer(thinking=True) @@ -32,7 +32,66 @@ class Profiles(commands.Cog): if not fonts: await interaction.followup.send("No fonts found in the fonts folder.") return - await interaction.followup.send("**Avaliable Fonts:**\n" + "\n".join(f"- `{f}`" for f in fonts)) + + async def generate_font_preview(): + def _generate(): + font_sample_text = "The quick brown fox jumps over the lazy dog" + margin = 40 + spacing = 90 + img_width = 1600 + img_height = max(400, len(fonts) * spacing + 60) + img = Image.new("RGB", (img_width, img_height), "#1e1e1e") + draw = ImageDraw.Draw(img) + + y = margin + for name in fonts: + font_path_ttf = os.path.join(FONTS_PATH, f"{name}.ttf") + font_path_otf = os.path.join(FONTS_PATH, f"{name}.otf") + + try: + if os.path.exists(font_path_ttf): + preview_font = ImageFont.truetype(font_path_ttf, 36) + elif os.path.exists(font_path_otf): + preview_font = ImageFont.truetype(font_path_otf, 36) + else: + preview_font = ImageFont.load_default() + except Exception: + preview_font = ImageFont.load_default() + + # Slightly smaller font name for balance + try: + if os.path.exists(font_path_ttf): + name_font = ImageFont.truetype(font_path_ttf, 32) + elif os.path.exists(font_path_otf): + name_font = ImageFont.truetype(font_path_otf, 32) + else: + name_font = ImageFont.load_default() + except Exception: + name_font = ImageFont.load_default() + + # Draw font name in lilac-blue + draw.text((margin, y), f"{name}:", font=name_font, fill="#7289DA") + + # Draw preview text with more horizontal space + draw.text((350, y), font_sample_text, font=preview_font, fill="#FFFFFF") + + # Optional divider line for clarity + draw.line([(margin, y + spacing - 15), (img_width - margin, y + spacing - 15)], fill="#2f3136", width=2) + + y += spacing + + buffer = io.BytesIO() + img.save(buffer, format="PNG") + buffer.seek(0) + return buffer + + return await asyncio.to_thread(_generate) + + buffer = await generate_font_preview() + await interaction.followup.send( + content="Here are all available fonts:", + file=discord.File(buffer, "font_preview.png") + ) @app_commands.command(name="setprofile", description="Set your profile information.") @app_commands.describe( @@ -45,16 +104,7 @@ class Profiles(commands.Cog): birthday="Your birthday (MM-DD)", font_name="Font to use (see /listfonts)" ) - async def setprofile(self, interaction: discord.Interaction, - pronouns: str = None, - about_me: str = None, - fav_color: str = None, - bg_color: str = None, - fav_game: str = None, - fav_artist: str = None, - birthday: str = None, - font_name: str = None): - + async def setprofile(self, interaction: discord.Interaction, pronouns: str = None, about_me: str = None, fav_color: str = None, bg_color: str = None, fav_game: str = None, fav_artist: str = None, birthday: str = None, font_name: str = None): # Defer the response to show "thinking" await interaction.response.defer(thinking=True) @@ -76,7 +126,7 @@ class Profiles(commands.Cog): db = get_db() cursor = db.cursor() - + # Build update query dynamically to only update provided fields update_fields = [] values = [] @@ -126,10 +176,10 @@ class Profiles(commands.Cog): placeholders = ["?"] * len(fields) query = f"INSERT INTO profiles ({', '.join(fields)}) VALUES ({', '.join(placeholders)})" cursor.execute(query, [interaction.user.id] + values) - + db.commit() db.close() - + await interaction.followup.send("Your profile has been saved!") @app_commands.command(name="profile", description="View your or another user's profile.") @@ -138,22 +188,22 @@ class Profiles(commands.Cog): await interaction.response.defer(thinking=True) member = member or interaction.user - + db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM profiles WHERE user_id=?", (member.id,)) row = cursor.fetchone() db.close() - + if not row: await interaction.followup.send("This user hasn't set up a profile yet. Use /setprofile to add information to your profile") return _, pronouns, about_me, fav_color, bg_color, fav_game, fav_artist, birthday, font_name = row - + avatar_asset = member.display_avatar.with_size(256) avatar_bytes = await avatar_asset.read() - + def generate_image(): try: if font_name: @@ -181,7 +231,7 @@ class Profiles(commands.Cog): font = ImageFont.load_default() font_bold = ImageFont.load_default() font_label = ImageFont.load_default() - + img_width, img_height = 1000, 550 # Parse background color - only accept hex with # @@ -205,7 +255,7 @@ class Profiles(commands.Cog): radius=20, fill=(40, 45, 52, 200) ) - + # Avatar setup - centered in left panel avatar_size = 200 panel_center = panel_left_x + panel_left_width // 2 @@ -215,23 +265,21 @@ class Profiles(commands.Cog): # Draw glowing border around avatar for offset in range(3): draw.rounded_rectangle( - [(avatar_x - 6 - offset, avatar_y - 6 - offset), + [(avatar_x - 6 - offset, avatar_y - 6 - offset), (avatar_x + avatar_size + 6 + offset, avatar_y + avatar_size + 6 + offset)], radius=15, outline=(88, 101, 242, 100 - offset * 30), width=2 ) - + # Paste avatar with rounded corners avatar = Image.open(io.BytesIO(avatar_bytes)).convert("RGBA") avatar = avatar.resize((avatar_size, avatar_size)) - mask = Image.new('L', (avatar_size, avatar_size), 0) mask_draw = ImageDraw.Draw(mask) mask_draw.rounded_rectangle([(0, 0), (avatar_size, avatar_size)], radius=12, fill=255) - img.paste(avatar, (avatar_x, avatar_y), mask) - + # Draw username with shadow effect - centered in panel with dynamic sizing username_y = avatar_y + avatar_size + 25 @@ -267,34 +315,52 @@ class Profiles(commands.Cog): if text_width <= max_username_width: break - + username_font_size -= 2 # Shadow - draw.text((panel_center + 2, username_y + 2), member.display_name, - fill=(0, 0, 0, 100), font=username_font, anchor="mt") + draw.text((panel_center + 2, username_y + 2), member.display_name, fill=(0, 0, 0, 100), font=username_font, anchor="mt") # Main text - draw.text((panel_center, username_y), member.display_name, - fill="#FFFFFF", font=username_font, anchor="mt") + draw.text((panel_center, username_y), member.display_name, fill="#FFFFFF", font=username_font, anchor="mt") - # Draw pronouns below username if available - centered in panel + # Draw pronouns below username if available - centered in panel with wrapping current_y = username_y + 40 if pronouns: - draw.text((panel_center, current_y), pronouns, - fill="#B9BBBE", font=font, anchor="mt") - current_y += 35 + # Wrap pronouns text to fit panel width + max_pronouns_width = panel_left_width - 20 # Leave some padding + words = pronouns.split() + line = "" + + for word in words: + test_line = f"{line} {word}".strip() + bbox = draw.textbbox((0, 0), test_line, font=font) + line_width = bbox[2] - bbox[0] + + if line_width <= max_pronouns_width: + line = test_line + else: + if line: + draw.text((panel_center, current_y), line, fill="#B9BBBE", font=font, anchor="mt") + current_y += 25 # Move to next line + line = word + + # Draw any remaining text + if line: + draw.text((panel_center, current_y), line, fill="#B9BBBE", font=font, anchor="mt") + + current_y += 35 # Space before roles # Draw roles below pronouns roles = [role for role in member.roles if role.name != "@everyone"] if roles: # Sort roles by position (highest first, like Discord does) roles.sort(key=lambda r: r.position, reverse=True) - # Limit to top 5 roles to avoid overflow display_roles = roles[:5] role_y = current_y role_font_size = 14 + try: if font_name: font_path_ttf = os.path.join(FONTS_PATH, f"{font_name}.ttf") @@ -317,10 +383,9 @@ class Profiles(commands.Cog): # Draw role name centered role_text = f"• {role.name}" - draw.text((panel_center, role_y), role_text, - fill=role_color, font=role_font, anchor="mt") + draw.text((panel_center, role_y), role_text, fill=role_color, font=role_font, anchor="mt") role_y += 22 - + # Right side panel panel_x = 360 panel_y = 50 @@ -332,10 +397,10 @@ class Profiles(commands.Cog): radius=20, fill=(40, 45, 52, 150) ) - + line_spacing = 50 max_text_width = panel_width - 60 - + fields = [ ("Favorite Color", fav_color), ("Favorite Game", fav_game), @@ -343,7 +408,7 @@ class Profiles(commands.Cog): ("Birthday", birthday), ("About Me", about_me) ] - + for label, value in fields: if value: if label == "About Me": @@ -359,10 +424,12 @@ class Profiles(commands.Cog): words = value.split() line = "" first_line = True + for word in words: test_line = f"{line} {word}".strip() bbox = draw.textbbox((0, 0), test_line, font=font) line_width = bbox[2] - bbox[0] + available_width = max_text_width - (label_width if first_line else 0) if line_width <= available_width: @@ -374,9 +441,11 @@ class Profiles(commands.Cog): first_line = False value_x = panel_x line = word + if line: draw.text((value_x if first_line else panel_x, panel_y), line, fill="#FFFFFF", font=font) - panel_y += line_spacing + + panel_y += line_spacing else: # Draw label and value on same line draw.text((panel_x, panel_y), f"{label}: ", fill="#7289DA", font=font_label) @@ -387,13 +456,14 @@ class Profiles(commands.Cog): # Draw value right after label draw.text((panel_x + label_width, panel_y), str(value), fill="#FFFFFF", font=font) + panel_y += line_spacing - + buffer = io.BytesIO() img.save(buffer, format="PNG") buffer.seek(0) return buffer - + buffer = await asyncio.to_thread(generate_image) await interaction.followup.send(file=discord.File(buffer, "profile.png"))