test / bot /theme.py
mtaaz's picture
Upload 91 files
2b5eba7 verified
"""
Enhanced Theme and styling utilities for the bot.
Provides neon colors, progress bars, and decorative text formatting with rich visual elements.
Version 2.0 - Improved dividers, colors, headers, and emojis
"""
import random
from typing import Literal
from urllib.parse import urlparse
import discord
from bot.emojis import ui
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# NEON COLOR PALETTE - Extended Cyberpunk aesthetic
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# Primary Neon Colors
NEON_CYAN = discord.Color.from_rgb(0, 255, 255)
NEON_PINK = discord.Color.from_rgb(255, 0, 200)
NEON_PURPLE = discord.Color.from_rgb(145, 70, 255)
NEON_LIME = discord.Color.from_rgb(57, 255, 20)
NEON_ORANGE = discord.Color.from_rgb(255, 140, 0)
NEON_YELLOW = discord.Color.from_rgb(255, 255, 0)
NEON_RED = discord.Color.from_rgb(255, 50, 50)
NEON_BLUE = discord.Color.from_rgb(50, 150, 255)
NEON_MAGENTA = discord.Color.from_rgb(255, 0, 255)
NEON_TEAL = discord.Color.from_rgb(0, 255, 200)
# NEW: Extended Neon Colors
NEON_GOLD = discord.Color.from_rgb(255, 215, 0)
NEON_SILVER = discord.Color.from_rgb(192, 192, 255)
NEON_ROSE = discord.Color.from_rgb(255, 102, 178)
NEON_SKY = discord.Color.from_rgb(135, 206, 250)
NEON_CORAL = discord.Color.from_rgb(255, 127, 80)
NEON_VIOLET = discord.Color.from_rgb(138, 43, 226)
NEON_MINT = discord.Color.from_rgb(152, 255, 152)
NEON_PEACH = discord.Color.from_rgb(255, 218, 185)
NEON_LAVENDER = discord.Color.from_rgb(230, 190, 255)
NEON_AQUA = discord.Color.from_rgb(0, 255, 255)
NEON_CRIMSON = discord.Color.from_rgb(220, 20, 60)
NEON_EMERALD = discord.Color.from_rgb(80, 200, 120)
NEON_AMBER = discord.Color.from_rgb(255, 191, 0)
NEON_INDIGO = discord.Color.from_rgb(75, 0, 130)
# Color Palettes
NEON_PALETTE = [NEON_CYAN, NEON_PINK, NEON_PURPLE, NEON_LIME, NEON_ORANGE, NEON_YELLOW]
NEON_PASTEL_PALETTE = [NEON_LAVENDER, NEON_PEACH, NEON_MINT, NEON_SKY, NEON_ROSE]
NEON_DARK_PALETTE = [NEON_INDIGO, NEON_VIOLET, NEON_CRIMSON, NEON_MAGENTA]
# Emoji collections for decoration
GLOW_EMOJIS = ["โœจ", "โšก", "๐ŸŒ€", "๐ŸŒ", "๐Ÿ’ซ", "โœ…", "โœ…", "โœฆ", "๐Ÿ’ ", "๐Ÿ”ฎ"]
SPARKLE_EMOJIS = ["โœจ", "โ‡๏ธ", "โœด๏ธ", "โ„๏ธ", "๐Ÿ’ ", "โœ…", "โœ…"]
FIRE_EMOJIS = ["๐Ÿ”ฅ", "๐Ÿ’ฅ", "โšก", "โ˜„๏ธ", "โœ…", "๐Ÿงก", "๐Ÿ’›"]
HEART_EMOJIS = ["โœ…", "<:animatedarrowgreen:1477261279428087979>", "๐Ÿ’™", "<:animatedarrowgreen:1477261279428087979>", "๐Ÿ’›", "๐Ÿงก", "<:animatedarrowpink:1477261266690113651>", "๐Ÿ’–"]
# Arrow emojis for dividers (Unicode - work everywhere)
ARROW_BLUE = "๐Ÿ”ท"
ARROW_GREEN = "<:animatedarrowgreen:1477261279428087979>"
ARROW_PINK = "<:animatedarrowpink:1477261266690113651>"
ARROW_PURPLE = "<:animatedarrowgreen:1477261279428087979>"
ARROW_ORANGE = "๐ŸŸ "
ARROW_YELLOW = "<:animatedarrowyellow:1477261257592668271>"
ARROW_CYAN = "๐Ÿ’ "
ARROW_RED = "๐Ÿ”ด"
ARROW_WHITE = "โšช"
ARROW_BLACK = "โšซ"
def pick_neon_color(seed: int | None = None) -> discord.Color:
"""Pick a neon color, optionally based on a seed for consistency."""
if seed is None:
return random.choice(NEON_PALETTE)
return NEON_PALETTE[seed % len(NEON_PALETTE)]
def pick_pastel_color(seed: int | None = None) -> discord.Color:
"""Pick a pastel neon color."""
if seed is None:
return random.choice(NEON_PASTEL_PALETTE)
return NEON_PASTEL_PALETTE[seed % len(NEON_PASTEL_PALETTE)]
def pick_dark_color(seed: int | None = None) -> discord.Color:
"""Pick a dark neon color."""
if seed is None:
return random.choice(NEON_DARK_PALETTE)
return NEON_DARK_PALETTE[seed % len(NEON_DARK_PALETTE)]
def pick_accent_color() -> discord.Color:
"""Pick a random accent color."""
accents = [NEON_CYAN, NEON_PINK, NEON_PURPLE, NEON_LIME, NEON_GOLD, NEON_ROSE]
return random.choice(accents)
def random_neon() -> discord.Color:
"""Get a completely random neon color from all palettes."""
all_colors = NEON_PALETTE + NEON_PASTEL_PALETTE + NEON_DARK_PALETTE
return random.choice(all_colors)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# PROGRESS BARS & VISUAL ELEMENTS
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def progress_bar(
current: int,
total: int,
*,
size: int = 12,
filled_char: str = "โ– ",
empty_char: str = "โ–ก",
show_percentage: bool = True
) -> str:
"""Create a customizable text-based progress bar."""
total = max(1, total)
ratio = max(0.0, min(1.0, current / total))
filled = round(ratio * size)
bar = f"{filled_char * filled}{empty_char * (size - filled)}"
if show_percentage:
return f"{bar} **{int(ratio * 100)}%**"
return bar
def xp_progress_bar(current: int, total: int, level: int) -> str:
"""Create an XP progress bar with level display."""
total = max(1, total)
ratio = max(0.0, min(1.0, current / total))
filled = round(ratio * 10)
bar = f"{'โ–ˆ' * filled}{'โ–‘' * (10 - filled)}"
return f"Level **{level}** โ€ข `{bar}` **{int(ratio * 100)}%**"
def health_bar(current: int, total: int, size: int = 10) -> str:
"""Create a health/HP style progress bar."""
total = max(1, total)
ratio = max(0.0, min(1.0, current / total))
filled = round(ratio * size)
if ratio > 0.7:
color = "๐ŸŸฉ"
elif ratio > 0.4:
color = "๐ŸŸจ"
else:
color = "๐ŸŸฅ"
bar = f"{color * filled}{'โฌ›' * (size - filled)}"
return f"{bar} `{current}/{total}`"
def cooldown_bar(remaining: int, total: int) -> str:
"""Create a cooldown timer bar."""
total = max(1, total)
elapsed = max(0, total - remaining)
ratio = elapsed / total
filled = round(ratio * 8)
bar = f"{'โณ' * filled}{'โŒ›' * (8 - filled)}"
return f"{bar} **{remaining}s** remaining"
def fancy_progress_bar(current: int, total: int, style: str = "default") -> str:
"""Create a fancy styled progress bar."""
total = max(1, total)
ratio = max(0.0, min(1.0, current / total))
filled = round(ratio * 10)
styles = {
"default": ("โ–ˆ", "โ–‘"),
"blocks": ("โ–“", "โ–‘"),
"circles": ("โ—", "โ—‹"),
"diamonds": ("โ—†", "โ—‡"),
"stars": ("โ˜…", "โ˜†"),
"hearts": ("โœ…", "๐Ÿ–ค"),
"fire": ("๐Ÿ”ฅ", "๐Ÿ’จ"),
"arrows": ("โ–ถ", "โ–ท"),
}
filled_char, empty_char = styles.get(style, styles["default"])
bar = f"{filled_char * filled}{empty_char * (10 - filled)}"
return f"`{bar}` **{int(ratio * 100)}%**"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# DECORATIVE HEADERS & TITLES - NEW IMPROVED STYLES
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def shimmer(text: str) -> str:
"""Add shimmer effects around text."""
return f"{random.choice(GLOW_EMOJIS)} {text} {random.choice(GLOW_EMOJIS)}"
def fancy_header(text: str, style: str = "default") -> str:
"""Create a fancy header with decorative brackets - 20 styles available."""
styles = {
"default": f"ใ€Ž {text} ใ€",
"double": f"ใ€ {text} ใ€‘",
"stars": f"โ˜… {text} โ˜…",
"sparkle": f"โœจ {text} โœจ",
"fire": f"๐Ÿ”ฅ {text} ๐Ÿ”ฅ",
"diamond": f"โ—† {text} โ—†",
"arrow": f"โžค {text} โžค",
"crown": f"๐Ÿ‘‘ {text} ๐Ÿ‘‘",
"glow": f"โ–“โ–“ {text} โ–“โ–“",
"cyber": f"ใ€Œ {text} ใ€",
"neon": f"โ–ˆโ–€โ–€ {text} โ–€โ–€โ–ˆ",
# NEW STYLES
"bullet": f"โ€ข โ€ข โ€ข {text} โ€ข โ€ข โ€ข",
"wave": f"๏ฝž๏ฝž๏ฝž {text} ๏ฝž๏ฝž๏ฝž",
"box": f"โ•”โ•โ• {text} โ•โ•โ•—",
"hex": f"โฌก {text} โฌก",
"infinity": f"โˆž {text} โˆž",
"lightning": f"โšก {text} โšก",
"heart": f"๐Ÿ’– {text} ๐Ÿ’–",
"moon": f"๐ŸŒ™ {text} ๐ŸŒ™",
"sun": f"โ˜€๏ธ {text} โ˜€๏ธ",
"flower": f"๐ŸŒธ {text} ๐ŸŒธ",
"gem": f"๐Ÿ’Ž {text} ๐Ÿ’Ž",
"music": f"๐ŸŽต {text} ๐ŸŽต",
"game": f"๐ŸŽฎ {text} ๐ŸŽฎ",
"rocket": f"๐Ÿš€ {text} ๐Ÿš€",
"shield": f"๐Ÿ›ก๏ธ {text} ๐Ÿ›ก๏ธ",
"trophy": f"๐Ÿ† {text} ๐Ÿ†",
"party": f"๐ŸŽ‰ {text} ๐ŸŽ‰",
"cloud": f"โ˜๏ธ {text} โ˜๏ธ",
"star2": f"โœ… {text} โœ…",
}
return styles.get(style, styles["default"])
def mega_title(text: str, style: str = "default") -> str:
"""Create an eye-catching mega title - multiple styles."""
styles = {
"default": f"โ–ˆโ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–ˆ\n **{text}**\nโ–ˆโ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–ˆ",
"double": f"โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—\n **{text}**\nโ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•",
"stars": f"โ˜… โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• โ•\n **{text}**\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• โ˜…",
"fire": f"๐Ÿ”ฅ โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ ๐Ÿ”ฅ\n **{text}**\n๐Ÿ”ฅ โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„ ๐Ÿ”ฅ",
"simple": f"โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n **{text}**\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”",
"dots": f"โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข\n **{text}**\nโ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข โ€ข",
}
return styles.get(style, styles["default"])
def panel_title(emoji: str, text: str, style: str = "default") -> str:
"""Create a panel title with emoji decoration - 10 styles."""
styles = {
"default": f"{emoji} **{text}**",
"fancy": f"โœง {emoji} **{text}** โœง",
"bold": f"โ–ธ {emoji} **{text}** โ—‚",
"sparkle": f"โœจ {emoji} **{text}** โœจ",
"fire": f"๐Ÿ”ฅ {emoji} **{text}** ๐Ÿ”ฅ",
"crown": f"๐Ÿ‘‘ {emoji} **{text}** ๐Ÿ‘‘",
"star": f"โœ… {emoji} **{text}** โœ…",
"diamond": f"๐Ÿ’Ž {emoji} **{text}** ๐Ÿ’Ž",
"heart": f"๐Ÿ’– {emoji} **{text}** ๐Ÿ’–",
"arrow": f"โžค {emoji} **{text}**",
}
return styles.get(style, styles["default"])
def animated_header(text: str) -> str:
"""Create a header with animated-style decoration."""
emojis = random.sample(GLOW_EMOJIS, 2)
return f"{emojis[0]} โ–‘โ–’โ–“โ–ˆ **{text}** โ–ˆโ–“โ–’โ–‘ {emojis[1]}"
def gradient_header(text: str) -> str:
"""Create a gradient-style header."""
return f"โ–‘โ–’โ–“โ–ˆ **{text}** โ–ˆโ–“โ–’โ–‘"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# DIVIDERS & SEPARATORS - NEW IMPROVED STYLES
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def section_bar(emoji: str = "โ–ฌ", length: int = 24) -> str:
"""Create a section divider bar."""
return f"{emoji}" * length
def panel_divider(color: Literal["blue", "green", "pink", "purple", "orange", "yellow", "cyan", "lime", "red", "white", "gold", "rainbow"] = "blue", length: int = 8) -> str:
"""Create a colored divider using emojis (two-line style for clarity).
Colors: blue, green, pink, purple, orange, yellow, cyan, lime, red, white, gold, rainbow
Length: 4-12 emojis per line (default 8)
"""
arrows = {
"blue": ARROW_BLUE,
"green": ARROW_GREEN,
"pink": ARROW_PINK,
"purple": ARROW_PURPLE,
"orange": ARROW_ORANGE,
"yellow": ARROW_YELLOW,
"cyan": ARROW_CYAN,
"lime": "<:animatedarrowgreen:1477261279428087979>",
"red": ARROW_RED,
"white": ARROW_WHITE,
"gold": "<:animatedarrowyellow:1477261257592668271>",
"rainbow": None, # Special handling
}
length = max(4, min(12, length)) # Clamp between 4-12
if color == "rainbow":
colors = ["๐Ÿ”ด", "๐ŸŸ ", "<:animatedarrowyellow:1477261257592668271>", "<:animatedarrowgreen:1477261279428087979>", "๐Ÿ”ต", "๐ŸŸฃ"]
line1 = " ".join(colors[:length//2])
line2 = " ".join(colors[length//2:length])
return f"{line1}\n{line2}"
arrow = arrows.get(color, ARROW_BLUE)
line = " ".join([arrow] * length)
# Two-line divider for better visual clarity
return f"{line}\n{line}"
def fancy_divider(style: Literal["stars", "dots", "lines", "diamonds", "hearts", "waves", "fire", "lightning", "flowers", "moons", "clouds", "music", "gaming"] = "stars", length: int = 20) -> str:
"""Create a fancy two-line divider with various styles for better visibility."""
length = max(10, min(30, length))
half = length // 2
dividers = {
"stars": f"โ˜… {'โ•' * half} โ˜…\nโ˜… {'โ•' * half} โ˜…",
"dots": f"โ€ข โ€ข โ€ข {'โ”€' * (half-3)} โ€ข โ€ข โ€ข\nโ€ข โ€ข โ€ข {'โ”€' * (half-3)} โ€ข โ€ข โ€ข",
"lines": f"{'โ”' * length}\n{'โ”' * length}",
"diamonds": f"โ—† {'โ•' * half} โ—†\nโ—† {'โ•' * half} โ—†",
"hearts": f"๐Ÿ’– {'โ•' * (half-2)} ๐Ÿ’–\n๐Ÿ’– {'โ•' * (half-2)} ๐Ÿ’–",
"waves": f"{'ใ€œ' * length}\n{'ใ€œ' * length}",
"fire": f"๐Ÿ”ฅ {'โ•' * half} ๐Ÿ”ฅ\n๐Ÿ”ฅ {'โ•' * half} ๐Ÿ”ฅ",
"lightning": f"โšก {'โ•' * half} โšก\nโšก {'โ•' * half} โšก",
"flowers": f"๐ŸŒธ {'โ•' * half} ๐ŸŒธ\n๐ŸŒธ {'โ•' * half} ๐ŸŒธ",
"moons": f"๐ŸŒ™ {'โ•' * half} ๐ŸŒ™\n๐ŸŒ™ {'โ•' * half} ๐ŸŒ™",
"clouds": f"โ˜๏ธ {'โ•' * half} โ˜๏ธ\nโ˜๏ธ {'โ•' * half} โ˜๏ธ",
"music": f"๐ŸŽต {'โ•' * half} ๐ŸŽต\n๐ŸŽต {'โ•' * half} ๐ŸŽต",
"gaming": f"๐ŸŽฎ {'โ•' * half} ๐ŸŽฎ\n๐ŸŽฎ {'โ•' * half} ๐ŸŽฎ",
}
return dividers.get(style, dividers["stars"])
def double_line(length: int = 40) -> str:
"""Create a double line divider."""
return "โ•" * max(10, min(50, length))
def triple_line(length: int = 40) -> str:
"""Create a triple line divider with center decoration."""
length = max(10, min(50, length))
return f"{'โ•' * (length//2)} โœฆ {'โ•' * (length//2)}"
def dotted_line(length: int = 30) -> str:
"""Create a dotted line divider."""
return "โ€ข" * max(5, min(40, length))
def gradient_line(length: int = 20) -> str:
"""Create a gradient-style line."""
return f"{'โ–‘' * 5}{'โ–’' * 5}{'โ–“' * 5}{'โ–ˆ' * 5}"
def arrow_line(direction: str = "right", length: int = 10) -> str:
"""Create an arrow line divider."""
if direction == "right":
return "โ–ถ " + "โ”€" * length + " โ–ถ"
elif direction == "left":
return "โ—€ " + "โ”€" * length + " โ—€"
else:
return "โ—€ " + "โ”€" * length + " โ–ถ"
def box_divider(text: str = "", width: int = 30) -> str:
"""Create a box-style divider with optional text."""
if text:
return f"โ•”{'โ•' * (width-2)}โ•—\nโ•‘ {text.center(width-4)} โ•‘\nโ•š{'โ•' * (width-2)}โ•"
return f"โ•”{'โ•' * (width-2)}โ•—\nโ• {'โ•' * (width-2)}โ•ฃ\nโ•š{'โ•' * (width-2)}โ•"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# SECTION FORMATTING
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def section_header(emoji: str, text: str, color: str = "default") -> str:
"""Create a section header with emoji and text."""
colors = {
"default": "",
"cyan": "๐Ÿฉต",
"pink": "<:animatedarrowpink:1477261266690113651>",
"purple": "<:animatedarrowgreen:1477261279428087979>",
"green": "<:animatedarrowgreen:1477261279428087979>",
"yellow": "๐Ÿ’›",
"orange": "๐Ÿงก",
"red": "โœ…",
"blue": "๐Ÿ’™",
"gold": "๐Ÿช™",
"silver": "๐Ÿชฉ",
}
accent = colors.get(color, "")
if accent:
return f"{accent} {emoji} **{text}** {accent}"
return f"{emoji} **{text}**"
def boxed_section(title: str, content: str, emoji: str = "๐Ÿ“Œ") -> str:
"""Create a boxed section with title and content."""
return (
f"โ•ญโ”€โ”€โ”€ {emoji} **{title}** โ”€โ”€โ”€โ•ฎ\n"
f"โ”‚ {content}\n"
f"โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ"
)
def card(title: str, content: str, emoji: str = "๐ŸŽด", color: str = "blue") -> str:
"""Create a card-style display."""
divider = panel_divider(color)
return f"{divider}\n{emoji} **{title}**\n{content}\n{divider}"
def info_box(title: str, content: str, style: str = "info") -> str:
"""Create an info box with style."""
styles = {
"info": ("โ„น๏ธ", "๐Ÿ”ท"),
"success": ("โœ…", "<:animatedarrowgreen:1477261279428087979>"),
"warning": ("โš ๏ธ", "๐ŸŸ "),
"error": ("โŒ", "๐Ÿ”ด"),
"tip": ("๐Ÿ’ก", "<:animatedarrowyellow:1477261257592668271>"),
}
emoji, border = styles.get(style, styles["info"])
return f"{border}{border}{border}{border}{border}\n{emoji} **{title}**\n{content}\n{border}{border}{border}{border}{border}"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# DISPLAY HELPERS
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def coin_display(amount: int) -> str:
"""Display coins with diamond emoji."""
return f"๐Ÿ’Ž `{amount:,}`"
def xp_display(amount: int, level: int) -> str:
"""Display XP and level with star emoji."""
return f"โœ… Level `{level}` โ€ข XP `{amount:,}`"
def money_display(wallet: int, bank: int = 0) -> str:
"""Display wallet and bank amounts beautifully."""
result = f"๐Ÿ’ฐ **Wallet:** `{wallet:,}`"
if bank > 0:
result += f"\n๐Ÿฆ **Bank:** `{bank:,}`"
return result
def rank_display(rank: int) -> str:
"""Display rank with medal/badge."""
badges = {1: "๐Ÿฅ‡", 2: "๐Ÿฅˆ", 3: "๐Ÿฅ‰"}
badge = badges.get(rank, f"#{rank}")
return f"{badge} **Rank #{rank}**"
def level_display(level: int) -> str:
"""Display level with decoration."""
if level >= 100:
return f"๐Ÿ‘‘ **Level {level}**"
elif level >= 50:
return f"โœ… **Level {level}**"
elif level >= 25:
return f"โœจ **Level {level}**"
return f"๐ŸŒฑ **Level {level}**"
def timestamp_display(seconds: int) -> str:
"""Display duration in human-readable format."""
if seconds < 60:
return f"โฑ๏ธ `{seconds}s`"
elif seconds < 3600:
mins = seconds // 60
secs = seconds % 60
return f"โฑ๏ธ `{mins}m {secs}s`"
else:
hours = seconds // 3600
mins = (seconds % 3600) // 60
return f"โฑ๏ธ `{hours}h {mins}m`"
def status_display(status: str) -> str:
"""Display status with colored indicator."""
statuses = {
"online": "<:animatedarrowgreen:1477261279428087979> Online",
"idle": "<:animatedarrowyellow:1477261257592668271> Idle",
"dnd": "๐Ÿ”ด Do Not Disturb",
"offline": "โšซ Offline",
"playing": "๐ŸŽฎ Playing",
"listening": "๐ŸŽต Listening",
"watching": "๐Ÿ‘€ Watching",
"streaming": "๐Ÿ“บ Streaming",
}
return statuses.get(status.lower(), f"โ“ {status}")
def badge_display(badge_type: str) -> str:
"""Display a badge by type."""
badges = {
"owner": "๐Ÿ‘‘ Owner",
"admin": "๐Ÿ›ก๏ธ Admin",
"mod": "โš”๏ธ Moderator",
"vip": "๐Ÿ’Ž VIP",
"boost": "๐Ÿš€ Booster",
"verified": "โœ… Verified",
"bot": "๐Ÿค– Bot",
"developer": "๐Ÿ’ป Developer",
"premium": "โœ… Premium",
"founder": "๐Ÿ… Founder",
}
return badges.get(badge_type.lower(), f"๐Ÿ“Œ {badge_type}")
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# MUSIC FORMATTING
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def music_now_playing(title: str, requester: str, duration: str = "") -> str:
"""Format a 'now playing' message for music."""
result = f"๐ŸŽต **Now Playing**\n{panel_divider('cyan')}\n๐ŸŽถ **{title}**"
if duration:
result += f"\nโฑ๏ธ Duration: `{duration}`"
result += f"\n๐Ÿ‘ค Requested by: {requester}"
return result
def music_queue_item(position: int, title: str, requester: str) -> str:
"""Format a queue item display."""
return f"`{position}.` ๐ŸŽต **{title[:40]}{'...' if len(title) > 40 else ''}** โ€ข {requester}"
def music_controls_display() -> str:
"""Display music control instructions."""
return (
f"{panel_divider('green')}\n"
f"โฎ๏ธ **Previous** | โ–ถ๏ธ **Play** | โธ๏ธ **Pause** | โญ๏ธ **Skip** | โน๏ธ **Stop**\n"
f"{panel_divider('green')}"
)
def music_status(playing: bool, paused: bool = False, queue_count: int = 0) -> str:
"""Display music status indicator."""
if paused:
return "โธ๏ธ **Paused**"
elif playing:
return f"โ–ถ๏ธ **Playing** โ€ข {queue_count} in queue"
else:
return "โน๏ธ **Not Playing**"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# ECONOMY FORMATTING
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def economy_wallet_display(wallet: int, bank: int) -> str:
"""Format wallet and bank display for economy panel."""
return (
f"๐Ÿ’Ž **Wallet:** `{wallet:,}` ๐Ÿ’ฐ\n"
f"๐Ÿฆ **Bank:** `{bank:,}` ๐Ÿ’Ž"
)
def economy_transaction(amount: int, action: str) -> str:
"""Format an economy transaction display."""
actions = {
"daily": ("๐ŸŒ… Daily Reward", NEON_LIME),
"work": ("๐Ÿ’ผ Work Payment", NEON_CYAN),
"gamble_win": ("๐ŸŽฐ Gamble Win!", NEON_LIME),
"gamble_loss": ("๐ŸŽฐ Gamble Loss", NEON_RED),
"rob_success": ("๐Ÿฆ Robbery Success!", NEON_LIME),
"rob_fail": ("๐Ÿšจ Robbery Failed!", NEON_RED),
"deposit": ("๐Ÿ“ฅ Deposit", NEON_BLUE),
"withdraw": ("๐Ÿ“ค Withdraw", NEON_PURPLE),
"shop_buy": ("๐Ÿ›’ Purchase", NEON_ORANGE),
"shop_sell": ("๐Ÿ’ฐ Sale", NEON_YELLOW),
"gift_sent": ("๐ŸŽ Gift Sent", NEON_PINK),
"gift_received": ("๐ŸŽ Gift Received", NEON_LIME),
}
label, _ = actions.get(action, ("๐Ÿ’ฐ Transaction", NEON_CYAN))
return f"{label}: `{amount:,}`"
def economy_leaderboard(position: int, name: str, balance: int) -> str:
"""Format economy leaderboard entry."""
badges = {1: "๐Ÿฅ‡", 2: "๐Ÿฅˆ", 3: "๐Ÿฅ‰"}
badge = badges.get(position, f"#{position}")
return f"{badge} **{name}** โ€” ๐Ÿ’ฐ `{balance:,}`"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# EMBED BUILDERS - ALL IMPROVED
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def success_embed(title: str, description: str = "") -> discord.Embed:
"""Create a success embed with green color and decorations."""
embed = discord.Embed(
title=f"โœ… {title}",
description=f"{panel_divider('green')}\n{description}" if description else panel_divider('green'),
color=NEON_LIME,
)
return embed
def error_embed(title: str, description: str = "") -> discord.Embed:
"""Create an error embed with red/pink color."""
embed = discord.Embed(
title=f"โŒ {title}",
description=f"{panel_divider('red')}\n{description}" if description else panel_divider('red'),
color=NEON_RED,
)
return embed
def warning_embed(title: str, description: str = "") -> discord.Embed:
"""Create a warning embed with orange color."""
embed = discord.Embed(
title=f"โš ๏ธ {title}",
description=f"{panel_divider('orange')}\n{description}" if description else panel_divider('orange'),
color=NEON_ORANGE,
)
return embed
def info_embed(title: str, description: str = "") -> discord.Embed:
"""Create an info embed with cyan color."""
embed = discord.Embed(
title=f"โ„น๏ธ {title}",
description=f"{panel_divider('blue')}\n{description}" if description else panel_divider('blue'),
color=NEON_CYAN,
)
return embed
def idle_text(summary: str, details: str = "") -> str:
"""Format a compact idle-state text block for panels/commands."""
body = f"{status_display('idle')} {summary}"
if details:
return f"{body}\n{panel_divider('orange')}\n{details}"
return body
def idle_embed(title: str = "Idle State", details: str = "", hint: str = "") -> discord.Embed:
"""Create a standardized idle-state embed used across commands/panels."""
content = details or "No active items yet."
embed = discord.Embed(
title=f"{status_display('idle')} ใ€Ž {title} ใ€",
description=(
f"{panel_divider('orange')}\n"
f"โ€ข {content}\n"
f"{panel_divider('orange')}"
),
color=NEON_ORANGE,
)
if hint:
embed.set_footer(text=hint)
return embed
async def idle_embed_for_guild(
title: str = "Idle State",
details: str = "",
hint: str = "",
*,
guild: discord.Guild | None = None,
bot = None,
) -> discord.Embed:
"""Create idle embed and attach guild banner/icon when available."""
embed = idle_embed(title, details, hint)
if guild is not None:
await add_banner_to_embed(embed, guild, bot)
return embed
def loading_embed(title: str = "Loading...", description: str = "") -> discord.Embed:
"""Create a loading embed with animation indication."""
embed = discord.Embed(
title=f"โณ {title}",
description=description or "Please wait...",
color=NEON_PURPLE,
)
return embed
def gaming_embed(title: str, description: str = "") -> discord.Embed:
"""Create a gaming-themed embed."""
embed = discord.Embed(
title=f"๐ŸŽฎ {title}",
description=f"{fancy_divider('gaming')}\n{description}" if description else fancy_divider('gaming'),
color=NEON_PURPLE,
)
return embed
def economy_embed(title: str, description: str = "") -> discord.Embed:
"""Create an economy-themed embed."""
embed = discord.Embed(
title=f"๐Ÿ’ฐ {title}",
description=f"{panel_divider('gold')}\n{description}" if description else panel_divider('gold'),
color=NEON_GOLD,
)
return embed
def music_embed(title: str, description: str = "") -> discord.Embed:
"""Create a music-themed embed."""
embed = discord.Embed(
title=f"๐ŸŽต {title}",
description=f"{fancy_divider('music')}\n{description}" if description else fancy_divider('music'),
color=NEON_CYAN,
)
return embed
def leaderboard_embed(title: str = "Leaderboard", description: str = "") -> discord.Embed:
"""Create a leaderboard embed with trophy theme."""
embed = discord.Embed(
title=f"๐Ÿ† {title}",
description=f"{panel_divider('yellow')}\n{description}" if description else panel_divider('yellow'),
color=NEON_YELLOW,
)
return embed
def profile_embed(username: str, avatar_url: str | None = None) -> discord.Embed:
"""Create a profile embed base."""
embed = discord.Embed(
title=f"๐Ÿ‘ค {username}'s Profile",
description=panel_divider('purple'),
color=NEON_PURPLE,
)
if avatar_url:
embed.set_thumbnail(url=avatar_url)
return embed
def tournament_embed(title: str, description: str = "") -> discord.Embed:
"""Create a tournament embed."""
embed = discord.Embed(
title=f"๐Ÿ {title}",
description=f"{panel_divider('lime')}\n{description}" if description else panel_divider('lime'),
color=NEON_LIME,
)
return embed
def shop_embed(title: str = "Shop", description: str = "") -> discord.Embed:
"""Create a shop embed."""
embed = discord.Embed(
title=f"๐Ÿ›’ {title}",
description=f"{panel_divider('gold')}\n{description}" if description else panel_divider('gold'),
color=NEON_GOLD,
)
return embed
def ticket_embed(title: str, description: str = "") -> discord.Embed:
"""Create a ticket/support embed."""
embed = discord.Embed(
title=f"๐ŸŽซ {title}",
description=f"{panel_divider('cyan')}\n{description}" if description else panel_divider('cyan'),
color=NEON_CYAN,
)
return embed
def giveaway_embed(title: str, description: str = "") -> discord.Embed:
"""Create a giveaway embed."""
embed = discord.Embed(
title=f"๐ŸŽ‰ {title}",
description=f"{fancy_divider('stars')}\n{description}" if description else fancy_divider('stars'),
color=NEON_PINK,
)
return embed
def poll_embed(title: str, description: str = "") -> discord.Embed:
"""Create a poll embed."""
embed = discord.Embed(
title=f"๐Ÿ“Š {title}",
description=f"{panel_divider('blue')}\n{description}" if description else panel_divider('blue'),
color=NEON_BLUE,
)
return embed
def moderation_embed(title: str, description: str = "") -> discord.Embed:
"""Create a moderation embed."""
embed = discord.Embed(
title=f"๐Ÿ›ก๏ธ {title}",
description=f"{panel_divider('red')}\n{description}" if description else panel_divider('red'),
color=NEON_RED,
)
return embed
def welcome_embed(username: str, server_name: str) -> discord.Embed:
"""Create a welcome embed."""
embed = discord.Embed(
title=f"๐Ÿ‘‹ Welcome to {server_name}!",
description=f"{fancy_divider('hearts')}\nHey **{username}**, welcome to our server! ๐ŸŽ‰\nWe're glad to have you here!",
color=NEON_LIME,
)
return embed
def goodbye_embed(username: str, server_name: str) -> discord.Embed:
"""Create a goodbye embed."""
embed = discord.Embed(
title=f"๐Ÿ‘‹ Goodbye!",
description=f"{fancy_divider('waves')}\n**{username}** has left {server_name}.\nWe hope to see you again! <:animatedarrowgreen:1477261279428087979>",
color=NEON_ORANGE,
)
return embed
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# LIST FORMATTING
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def format_leaderboard(entries: list[tuple[int, str, int]], emoji: str = "๐Ÿ†") -> str:
"""Format a leaderboard from entries (rank, name, score)."""
lines = []
for rank, name, score in entries:
badge = {1: "๐Ÿฅ‡", 2: "๐Ÿฅˆ", 3: "๐Ÿฅ‰"}.get(rank, f"#{rank}")
lines.append(f"{badge} **{name}** โ€” `{score:,}`")
return "\n".join(lines)
def format_list(items: list[str], style: str = "bullet") -> str:
"""Format a list of items with various styles."""
styles = {
"bullet": "โ€ข",
"arrow": "โ†’",
"star": "โ˜…",
"diamond": "โ—†",
"check": "โœ“",
"number": "{i}.",
"heart": "๐Ÿ’–",
"fire": "๐Ÿ”ฅ",
"music": "๐ŸŽต",
}
prefix = styles.get(style, "โ€ข")
lines = []
for i, item in enumerate(items, 1):
if "{i}" in prefix:
lines.append(f"{prefix.format(i=i)} {item}")
else:
lines.append(f"{prefix} {item}")
return "\n".join(lines)
def format_queue(tracks: list[str], max_display: int = 10) -> str:
"""Format a music queue display."""
if not tracks:
return "Queue is empty"
lines = []
for i, track in enumerate(tracks[:max_display], 1):
lines.append(f"`{i}.` ๐ŸŽต {track[:45]}{'...' if len(track) > 45 else ''}")
if len(tracks) > max_display:
remaining = len(tracks) - max_display
lines.append(f"\n*...and {remaining} more tracks*")
return "\n".join(lines)
def format_permissions(permissions: list[str]) -> str:
"""Format a list of permissions nicely."""
return " โ€ข ".join(f"โœ… {p}" for p in permissions)
def format_settings(settings: dict[str, str | bool | int]) -> str:
"""Format settings dictionary nicely."""
lines = []
for key, value in settings.items():
if isinstance(value, bool):
status = "โœ…" if value else "โŒ"
lines.append(f"{status} **{key}**")
else:
lines.append(f"โš™๏ธ **{key}:** `{value}`")
return "\n".join(lines)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# NEW UTILITY FUNCTIONS
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def create_field(name: str, value: str, inline: bool = False) -> dict:
"""Create a field dict for embed.add_field()."""
return {"name": name, "value": value, "inline": inline}
def format_number(num: int) -> str:
"""Format a number with commas and appropriate suffix."""
if num >= 1_000_000_000:
return f"{num/1_000_000_000:.1f}B"
elif num >= 1_000_000:
return f"{num/1_000_000:.1f}M"
elif num >= 1_000:
return f"{num/1_000:.1f}K"
return f"{num:,}"
def truncate(text: str, length: int = 50, suffix: str = "...") -> str:
"""Truncate text to a specific length."""
if len(text) <= length:
return text
return text[:length-len(suffix)] + suffix
def highlight(text: str, style: str = "bold") -> str:
"""Highlight text with markdown."""
styles = {
"bold": f"**{text}**",
"italic": f"*{text}*",
"underline": f"__{text}__",
"strike": f"~~{text}~~",
"code": f"`{text}`",
"codeblock": f"```\n{text}\n```",
"quote": f"> {text}",
"spoiler": f"||{text}||",
}
return styles.get(style, text)
def code_block(code: str, language: str = "") -> str:
"""Create a code block."""
return f"```{language}\n{code}\n```"
def quote(text: str) -> str:
"""Create a quote."""
lines = text.split("\n")
return "\n".join(f"> {line}" for line in lines)
def spoiler(text: str) -> str:
"""Create spoiler text."""
return f"||{text}||"
def mention_user(user_id: int) -> str:
"""Create a user mention."""
return f"<@{user_id}>"
def mention_role(role_id: int) -> str:
"""Create a role mention."""
return f"<@&{role_id}>"
def mention_channel(channel_id: int) -> str:
"""Create a channel mention."""
return f"<#{channel_id}>"
def timestamp(time: int, style: str = "R") -> str:
"""Create a Discord timestamp."""
styles = {
"short": "t", # 16:20
"long": "T", # 16:20:30
"short_date": "d", # 20/04/2021
"long_date": "D", # 20 April 2021
"short_full": "f", # 20 April 2021 16:20
"long_full": "F", # Tuesday, 20 April 2021 16:20
"relative": "R", # 2 months ago
}
return f"<t:{time}:{styles.get(style, 'R')}>"
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# NEW: BEAUTIFUL DECORATIONS V3.0
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# Beautiful border styles
BORDER_STYLES = {
"single": ("โ”Œ", "โ”€", "โ”", "โ”‚", "โ””", "โ”˜"),
"double": ("โ•”", "โ•", "โ•—", "โ•‘", "โ•š", "โ•"),
"rounded": ("โ•ญ", "โ”€", "โ•ฎ", "โ”‚", "โ•ฐ", "โ•ฏ"),
"bold": ("โ”", "โ”", "โ”“", "โ”ƒ", "โ”—", "โ”›"),
"stars": ("โ˜…", "โ•", "โ˜…", "โ”‚", "โ˜…", "โ˜…"),
"diamonds": ("โ—†", "โ•", "โ—†", "โ”‚", "โ—†", "โ—†"),
"hearts": ("๐Ÿ’–", "โ•", "๐Ÿ’–", "โ”‚", "๐Ÿ’–", "๐Ÿ’–"),
"fire": ("๐Ÿ”ฅ", "โ”€", "๐Ÿ”ฅ", "โ”‚", "๐Ÿ”ฅ", "๐Ÿ”ฅ"),
"lightning": ("โšก", "โ”€", "โšก", "โ”‚", "โšก", "โšก"),
"sparkle": ("โœจ", "โ”€", "โœจ", "โ”‚", "โœจ", "โœจ"),
}
# Beautiful bullet styles for lists
BULLET_STYLES = {
"star": "โ˜…",
"diamond": "โ—†",
"arrow": "โžค",
"check": "โœ“",
"cross": "โœ—",
"heart": "โœ…",
"sparkle": "โœฆ",
"fire": "๐Ÿ”ฅ",
"music": "๐ŸŽต",
"gaming": "๐ŸŽฎ",
"trophy": "๐Ÿ†",
"crown": "๐Ÿ‘‘",
"gem": "๐Ÿ’Ž",
"rocket": "๐Ÿš€",
"lightning": "โšก",
"flower": "๐ŸŒธ",
"moon": "๐ŸŒ™",
"sun": "โ˜€๏ธ",
"cloud": "โ˜๏ธ",
"rainbow": "๐ŸŒˆ",
}
def beautiful_border(text: str, style: str = "double", padding: int = 2) -> str:
"""Create a beautiful bordered text box.
Styles: single, double, rounded, bold, stars, diamonds, hearts, fire, lightning, sparkle
"""
tl, h, tr, v, bl, br = BORDER_STYLES.get(style, BORDER_STYLES["double"])
lines = text.split("\n")
max_len = max(len(line) for line in lines)
width = max_len + padding * 2
result = []
result.append(f"{tl}{h * width}{tr}")
for line in lines:
result.append(f"{v}{' ' * padding}{line.ljust(max_len)}{' ' * padding}{v}")
result.append(f"{bl}{h * width}{br}")
return "\n".join(result)
def command_card(command_name: str, description: str, emoji: str = "โœจ", category: str = "") -> str:
"""Create a beautiful command card display."""
border = "โ•ญ" + "โ”€" * 40 + "โ•ฎ"
border_end = "โ•ฐ" + "โ”€" * 40 + "โ•ฏ"
lines = [
border,
f"โ”‚ {emoji} **/{command_name}**",
]
if category:
lines.append(f"โ”‚ ๐Ÿ“ Category: {category}")
lines.append(f"โ”‚ ๐Ÿ’ฌ {description[:50]}{'...' if len(description) > 50 else ''}")
lines.append(border_end)
return "\n".join(lines)
def beautiful_list(items: list[str], style: str = "sparkle", numbered: bool = False) -> str:
"""Create a beautifully formatted list.
Styles: star, diamond, arrow, check, heart, sparkle, fire, music, gaming, etc.
"""
bullet = BULLET_STYLES.get(style, "โœฆ")
lines = []
for i, item in enumerate(items, 1):
if numbered:
lines.append(f"**{i}.** {bullet} {item}")
else:
lines.append(f"{bullet} {item}")
return "\n".join(lines)
def banner_text(text: str, style: str = "neon") -> str:
"""Create a beautiful banner text with multiple styles.
Styles: neon, fire, ice, rainbow, galaxy, cyber, elegant, gaming, music
"""
styles = {
"neon": f"โ–‘โ–’โ–“โ–ˆ **{text}** โ–ˆโ–“โ–’โ–‘",
"fire": f"๐Ÿ”ฅ โ–€โ–€โ–€โ–€โ–€โ–€โ–€ **{text}** โ–€โ–€โ–€โ–€โ–€โ–€โ–€ ๐Ÿ”ฅ",
"ice": f"โ„๏ธ โ•โ•โ•โ•โ•โ•โ•โ•โ• **{text}** โ•โ•โ•โ•โ•โ•โ•โ•โ• โ„๏ธ",
"rainbow": f"๐ŸŒˆ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ **{text}** โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐ŸŒˆ",
"galaxy": f"๐ŸŒŒ โ”€โ•โ”€โ•โ”€โ•โ”€โ• **{text}** โ”€โ•โ”€โ•โ”€โ•โ”€โ• ๐ŸŒŒ",
"cyber": f"โšก โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ **{text}** โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ โšก",
"elegant": f"โœจ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ **{text}** โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โœจ",
"gaming": f"๐ŸŽฎ โ–‚โ–‚โ–‚โ–‚โ–‚โ–‚โ–‚ **{text}** โ–‚โ–‚โ–‚โ–‚โ–‚โ–‚โ–‚ ๐ŸŽฎ",
"music": f"๐ŸŽต ~~~~~~~ **{text}** ~~~~~~~ ๐ŸŽต",
"crown": f"๐Ÿ‘‘ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ **{text}** โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐Ÿ‘‘",
"diamond": f"๐Ÿ’Ž โ—‡โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ **{text}** โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ—‡ ๐Ÿ’Ž",
"trophy": f"๐Ÿ† โ”€โ•โ•โ•โ”€ **{text}** โ”€โ•โ•โ•โ”€ ๐Ÿ†",
}
return styles.get(style, styles["neon"])
def category_header(category_name: str, emoji: str, count: int = 0) -> str:
"""Create a beautiful category header with count."""
if count > 0:
return f"โ•ญโ”€ {emoji} **{category_name}** โ”€โ•ฎ\nโ”‚ ๐Ÿ“Š `{count}` commands available\nโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ"
return f"โ•ญโ”€ {emoji} **{category_name}** โ”€โ•ฎ\nโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ"
def divider_emoji(style: str = "wave", length: int = 10) -> str:
"""Create a beautiful emoji-based divider.
Styles: wave, sparkle, fire, heart, star, diamond, moon, sun, rainbow, gaming, music
"""
dividers = {
"wave": " ๐ŸŒŠ ",
"sparkle": " โœจ ",
"fire": " ๐Ÿ”ฅ ",
"heart": " ๐Ÿ’– ",
"star": " โœ… ",
"diamond": " ๐Ÿ’Ž ",
"moon": " ๐ŸŒ™ ",
"sun": " โ˜€๏ธ ",
"rainbow": " ๐ŸŒˆ ",
"gaming": " ๐ŸŽฎ ",
"music": " ๐ŸŽต ",
"trophy": " ๐Ÿ† ",
"crown": " ๐Ÿ‘‘ ",
"rocket": " ๐Ÿš€ ",
"lightning": " โšก ",
"flower": " ๐ŸŒธ ",
"cloud": " โ˜๏ธ ",
"gem": " ๐Ÿ’  ",
}
emoji = dividers.get(style, " โœฆ ")
return emoji * length
def status_box(status: str, message: str, details: str = "") -> str:
"""Create a beautiful status box.
Status types: success, error, warning, info, loading
"""
styles = {
"success": ("โœ…", "<:animatedarrowgreen:1477261279428087979>", NEON_LIME),
"error": ("โŒ", "๐Ÿ”ด", NEON_RED),
"warning": ("โš ๏ธ", "<:animatedarrowyellow:1477261257592668271>", NEON_YELLOW),
"info": ("โ„น๏ธ", "๐Ÿ”ต", NEON_BLUE),
"loading": ("โณ", "๐ŸŸฃ", NEON_PURPLE),
}
emoji, indicator, _ = styles.get(status, styles["info"])
lines = [
f"โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—",
f"โ•‘ {emoji} **{message}**",
]
if details:
lines.append(f"โ•‘ โ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆ")
lines.append(f"โ•‘ {details[:50]}")
lines.append(f"โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")
return "\n".join(lines)
def mini_card(title: str, value: str, emoji: str = "๐Ÿ“Š") -> str:
"""Create a mini stat card."""
return f"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ\nโ”‚ {emoji} **{title}**\nโ”‚ โžค `{value}`\nโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ"
def progress_ring(percentage: int, size: int = 10) -> str:
"""Create a circular-style progress indicator using emojis."""
if percentage >= 100:
return "<:animatedarrowgreen:1477261279428087979>" * size
elif percentage >= 75:
return "<:animatedarrowgreen:1477261279428087979>" * (size - 2) + "<:animatedarrowyellow:1477261257592668271>" * 2
elif percentage >= 50:
return "<:animatedarrowgreen:1477261279428087979>" * (size // 2) + "<:animatedarrowyellow:1477261257592668271>" * (size // 4) + "๐Ÿ”ด" * (size - size // 2 - size // 4)
elif percentage >= 25:
return "<:animatedarrowyellow:1477261257592668271>" * (size // 4) + "๐Ÿ”ด" * (size - size // 4)
else:
return "๐Ÿ”ด" * size
def numbered_list(items: list[str], start: int = 1, emoji_style: str = "sparkle") -> str:
"""Create a beautifully numbered list with emoji decoration."""
emoji = BULLET_STYLES.get(emoji_style, "โœฆ")
lines = []
for i, item in enumerate(items, start):
lines.append(f"**`{i:02d}`** {emoji} {item}")
return "\n".join(lines)
def table_row(columns: list[str], widths: list[int]) -> str:
"""Create a table row with specified column widths."""
cells = []
for col, width in zip(columns, widths):
cells.append(f" {col[:width-2].ljust(width-2)} ")
return "โ”‚".join(cells)
def table_header(columns: list[str], widths: list[int]) -> str:
"""Create a table header with separator."""
top = "โ”Œ" + "โ”€" * (widths[0] - 2) + "โ”ฌ"
for w in widths[1:-1]:
top += "โ”€" * (w - 2) + "โ”ฌ"
top += "โ”€" * (widths[-1] - 2) + "โ”"
cells = []
for col, width in zip(columns, widths):
cells.append(f" {col[:width-2].ljust(width-2)} ")
middle = "โ”‚".join(cells)
bottom = "โ”œ" + "โ”€" * (widths[0] - 2) + "โ”ผ"
for w in widths[1:-1]:
bottom += "โ”€" * (w - 2) + "โ”ผ"
bottom += "โ”€" * (widths[-1] - 2) + "โ”ค"
return f"{top}\n{middle}\n{bottom}"
def table_footer(widths: list[int]) -> str:
"""Create a table footer."""
footer = "โ””" + "โ”€" * (widths[0] - 2) + "โ”ด"
for w in widths[1:-1]:
footer += "โ”€" * (w - 2) + "โ”ด"
footer += "โ”€" * (widths[-1] - 2) + "โ”˜"
return footer
def emoji_separator(emoji: str, count: int = 10) -> str:
"""Create a simple emoji separator line."""
return emoji * count
def glow_text(text: str, intensity: int = 1) -> str:
"""Add glow effect to text using special characters."""
glow_chars = ["โ–‘", "โ–’", "โ–“", "โ–ˆ"]
prefix = glow_chars[min(intensity, len(glow_chars) - 1)]
return f"{prefix * intensity} **{text}** {prefix * intensity}"
def rainbow_text(text: str) -> str:
"""Add rainbow effect indication to text."""
colors = ["๐Ÿ”ด", "๐ŸŸ ", "<:animatedarrowyellow:1477261257592668271>", "<:animatedarrowgreen:1477261279428087979>", "๐Ÿ”ต", "๐ŸŸฃ"]
return f"{colors[0]}{colors[1]}{colors[2]} **{text}** {colors[3]}{colors[4]}{colors[5]}"
def sparkle_line(length: int = 20) -> str:
"""Create a sparkle effect line."""
chars = ["โœจ", "โœ…", "โœ…", "๐Ÿ’ซ", "โœฆ"]
result = []
import random
for _ in range(length):
result.append(random.choice(chars))
return " ".join(result)
def menu_header(title: str, subtitle: str = "", style: str = "default") -> str:
"""Create a beautiful menu header.
Styles: default, cyber, elegant, gaming, neon
"""
styles = {
"default": f"โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“\nโ”ƒ ๐Ÿ“š **{title}**\n{'โ”ƒ ' + subtitle if subtitle else ''}โ”—โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”›",
"cyber": f"โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—\nโ•‘ โšก **{title}**\n{'โ•‘ ' + subtitle if subtitle else ''}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•",
"elegant": f"โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ\nโ”‚ โœจ **{title}**\n{'โ”‚ ' + subtitle if subtitle else ''}โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ",
"gaming": f"โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”\nโ”‚ ๐ŸŽฎ **{title}**\n{'โ”‚ ' + subtitle if subtitle else ''}โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜",
"neon": f"โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“\n๐Ÿ’ซ **{title}**\n{' ' + subtitle if subtitle else ''}โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“",
}
return styles.get(style, styles["default"])
def quick_stat(label: str, value: str, emoji: str = "๐Ÿ“Š") -> str:
"""Create a quick stat display."""
return f"{emoji} **{label}:** `{value}`"
def quick_stats_grid(stats: list[tuple[str, str, str]], columns: int = 2) -> str:
"""Create a grid of quick stats.
Args:
stats: List of (label, value, emoji) tuples
columns: Number of columns in the grid
"""
lines = []
row = []
for label, value, emoji in stats:
row.append(f"{emoji} **{label}:** `{value}`")
if len(row) >= columns:
lines.append(" โ”‚ ".join(row))
row = []
if row:
lines.append(" โ”‚ ".join(row))
return "\n".join(lines)
# Additional color presets for specific use cases
COLOR_PRESETS = {
"music": NEON_CYAN,
"admin": NEON_RED,
"fun": NEON_PINK,
"utility": NEON_BLUE,
"economy": NEON_GOLD,
"moderation": NEON_ORANGE,
"welcome": NEON_LIME,
"giveaway": NEON_MAGENTA,
"tournament": NEON_PURPLE,
"verification": NEON_TEAL,
"ai": NEON_VIOLET,
"gaming": NEON_EMERALD,
"config": NEON_AMBER,
"default": NEON_CYAN,
}
def get_category_color(category: str) -> discord.Color:
"""Get the appropriate color for a command category."""
return COLOR_PRESETS.get(category.lower(), COLOR_PRESETS["default"])
async def add_banner_to_embed(embed: discord.Embed, guild: discord.Guild | None = None, bot = None) -> discord.Embed:
"""Add server banner to embed if available.
This function checks if the guild has a custom banner in the database first,
then falls back to Discord's native banner, and finally uses the guild icon as thumbnail.
Args:
embed: The discord.Embed to add the banner to
guild: The discord.Guild to get the banner from
bot: The bot instance (optional, for database access)
Returns:
The modified embed with banner/image
"""
if guild is None:
return embed
# Try to infer bot/client if not passed explicitly.
if bot is None:
state = getattr(guild, "_state", None)
get_client = getattr(state, "_get_client", None)
if callable(get_client):
try:
bot = get_client()
except Exception:
bot = None
# Try custom banner from database first
if bot and getattr(bot, "db", None):
try:
row = await bot.db.fetchone(
"SELECT custom_banner_url FROM guild_config WHERE guild_id = ?",
guild.id,
)
custom_banner = row[0] if row else None
if custom_banner:
parsed = urlparse(str(custom_banner))
valid = parsed.scheme in {"http", "https"} and bool(parsed.netloc)
valid = valid and str(parsed.path).lower().endswith((".png", ".jpg", ".jpeg", ".webp", ".gif"))
if not valid:
custom_banner = None
if custom_banner:
embed.set_image(url=custom_banner)
return embed
except Exception:
pass
# Fall back to Discord's native banner
if guild.banner:
embed.set_image(url=guild.banner.url)
# Add icon as thumbnail if available
if guild.icon:
embed.set_thumbnail(url=guild.icon.url)
return embed