| """ |
| Server Banner Management Cog - Allows servers to set custom banners |
| """ |
|
|
| from __future__ import annotations |
|
|
| import sqlite3 |
| from urllib.parse import urlparse |
|
|
| import discord |
| from discord.ext import commands |
| from discord import app_commands |
|
|
| from bot.theme import NEON_CYAN, NEON_GOLD, NEON_LIME, NEON_RED, add_banner_to_embed |
| from bot.emojis import ui |
|
|
|
|
| class SetBannerModal(discord.ui.Modal, title="πΌοΈ Set Server Banner"): |
| banner_url = discord.ui.TextInput( |
| label="Banner Image URL", |
| placeholder="https://example.com/banner.png", |
| required=True, |
| min_length=5, |
| max_length=500, |
| ) |
| |
| def __init__(self, cog: "BannerManager"): |
| super().__init__() |
| self.cog = cog |
| |
| async def on_submit(self, interaction: discord.Interaction) -> None: |
| url = self.banner_url.value.strip().strip("<>") |
| |
| |
| if not self.cog._looks_like_image_url(url): |
| await interaction.response.send_message( |
| "Invalid image URL. Use a public http(s) link ending with png/jpg/jpeg/webp/gif.", |
| ephemeral=True, |
| ) |
| return |
| |
| guild_id = interaction.guild.id if interaction.guild else 0 |
|
|
| await self.cog._ensure_schema() |
| |
| await self.cog.bot.db.execute( |
| "INSERT INTO guild_config(guild_id, custom_banner_url) VALUES (?, ?) " |
| "ON CONFLICT(guild_id) DO UPDATE SET custom_banner_url = excluded.custom_banner_url", |
| guild_id, url, |
| ) |
| |
| embed = discord.Embed( |
| title="β
Banner Set Successfully!", |
| description=f"Your custom banner has been saved for **{interaction.guild.name}**.\n\nThe banner will now appear in all bot panels and commands.", |
| color=NEON_LIME, |
| ) |
| embed.set_image(url=url) |
| embed.set_footer(text="Use /remove_banner to remove the custom banner") |
| |
| await interaction.response.send_message(embed=embed, ephemeral=True) |
|
|
|
|
| class BannerManager(commands.Cog): |
| def __init__(self, bot: commands.Bot) -> None: |
| self.bot = bot |
| self._schema_checked = False |
|
|
| async def _ensure_schema(self) -> None: |
| if self._schema_checked: |
| return |
| try: |
| await self.bot.db.execute( |
| "ALTER TABLE guild_config ADD COLUMN custom_banner_url TEXT" |
| ) |
| except sqlite3.OperationalError as exc: |
| if "duplicate column name" not in str(exc).lower(): |
| raise |
| self._schema_checked = True |
|
|
| @staticmethod |
| def _looks_like_image_url(url: str) -> bool: |
| try: |
| parsed = urlparse(url) |
| except Exception: |
| return False |
| if parsed.scheme not in {"http", "https"} or not parsed.netloc: |
| return False |
| path = (parsed.path or "").lower() |
| return path.endswith((".png", ".jpg", ".jpeg", ".webp", ".gif")) |
| |
| @app_commands.command(name="set_banner", description="Set a custom banner for this server") |
| @app_commands.checks.has_permissions(manage_guild=True) |
| async def set_banner(self, interaction: discord.Interaction) -> None: |
| """Set a custom banner URL for the server.""" |
| if not interaction.guild: |
| await interaction.response.send_message("β This command can only be used in a server.", ephemeral=True) |
| return |
| |
| await self._ensure_schema() |
| await interaction.response.send_modal(SetBannerModal(self)) |
| |
| @app_commands.command(name="remove_banner", description="Remove the custom banner from this server") |
| @app_commands.checks.has_permissions(manage_guild=True) |
| async def remove_banner(self, interaction: discord.Interaction) -> None: |
| """Remove the custom banner from the server.""" |
| if not interaction.guild: |
| await interaction.response.send_message("β This command can only be used in a server.", ephemeral=True) |
| return |
| |
| await self._ensure_schema() |
| guild_id = interaction.guild.id |
| |
| await self.bot.db.execute( |
| "UPDATE guild_config SET custom_banner_url = NULL WHERE guild_id = ?", |
| guild_id, |
| ) |
| |
| embed = discord.Embed( |
| title="ποΈ Banner Removed", |
| description=f"The custom banner has been removed from **{interaction.guild.name}**.\n\nThe bot will now use the default Discord banner (if available).", |
| color=NEON_RED, |
| ) |
| |
| await interaction.response.send_message(embed=embed, ephemeral=True) |
| |
| @app_commands.command(name="view_banner", description="View the current custom banner for this server") |
| async def view_banner(self, interaction: discord.Interaction) -> None: |
| """View the current custom banner.""" |
| if not interaction.guild: |
| await interaction.response.send_message("β This command can only be used in a server.", ephemeral=True) |
| return |
| |
| await self._ensure_schema() |
| guild_id = interaction.guild.id |
| |
| row = await self.bot.db.fetchone( |
| "SELECT custom_banner_url FROM guild_config WHERE guild_id = ?", |
| guild_id, |
| ) |
| |
| custom_banner = row[0] if row else None |
| discord_banner = interaction.guild.banner.url if interaction.guild.banner else None |
| |
| embed = discord.Embed( |
| title="πΌοΈ Server Banner Status", |
| color=NEON_CYAN, |
| ) |
| |
| if custom_banner: |
| embed.add_field( |
| name="β
Custom Banner", |
| value=f"A custom banner is set for this server.\n[View Banner]({custom_banner})", |
| inline=False, |
| ) |
| embed.set_image(url=custom_banner) |
| else: |
| embed.add_field( |
| name="β No Custom Banner", |
| value="No custom banner is set. Use `/set_banner` to add one.", |
| inline=False, |
| ) |
| |
| if discord_banner: |
| embed.add_field( |
| name="π¨ Discord Banner", |
| value=f"Discord banner is available.\n[View Banner]({discord_banner})", |
| inline=False, |
| ) |
| else: |
| embed.add_field( |
| name="βͺ No Discord Banner", |
| value="No Discord banner is set. Upgrade your server to add one!", |
| inline=False, |
| ) |
| |
| embed.set_footer(text="Custom banner takes priority over Discord banner") |
| |
| await interaction.response.send_message(embed=embed, ephemeral=True) |
| |
| @app_commands.command(name="banner_help", description="Learn how to set a custom banner") |
| async def banner_help(self, interaction: discord.Interaction) -> None: |
| """Show help for banner commands.""" |
| embed = discord.Embed( |
| title="πΌοΈ Custom Banner Help", |
| description=( |
| "Set a custom banner for your server that will appear in all bot panels and commands.\n\n" |
| "**Commands:**\n" |
| "π `/set_banner` - Set a custom banner URL\n" |
| "ποΈ `/remove_banner` - Remove the custom banner\n" |
| "ποΈ `/view_banner` - View current banner status\n\n" |
| "**Requirements:**\n" |
| "β’ Manage Server permission required\n" |
| "β’ Image URL must be publicly accessible\n" |
| "β’ Recommended size: 960x540 (16:9 ratio)\n" |
| "β’ Max file size: 10MB\n" |
| "β’ Supported formats: PNG, JPG, GIF" |
| ), |
| color=NEON_GOLD, |
| ) |
| |
| embed.add_field( |
| name="π‘ Tips", |
| value=( |
| "β’ Use high-quality images for best results\n" |
| "β’ Make sure the image URL is public (not private)\n" |
| "β’ Test with Imgur, Discord CDN, or other public hosts\n" |
| "β’ Custom banner overrides Discord's native banner" |
| ), |
| inline=False, |
| ) |
| |
| await interaction.response.send_message(embed=embed, ephemeral=True) |
|
|
|
|
| async def setup(bot: commands.Bot) -> None: |
| await bot.add_cog(BannerManager(bot)) |
|
|