import discord from discord.ext import commands from discord import app_commands import httpx import io # --- Helper: Owner Check --- async def is_owner_check(interaction: discord.Interaction) -> bool: """Checks if the interacting user is the bot owner.""" return interaction.user.id == interaction.client.owner_id class BotAppearanceCog(commands.Cog): def __init__(self, bot): self.bot = bot @commands.command( name="change_nickname", help="Changes the bot's nickname in the current server. Admin only.", ) @commands.has_permissions(administrator=True) async def change_nickname(self, ctx: commands.Context, *, new_nickname: str): """Changes the bot's nickname in the current server.""" try: await ctx.guild.me.edit(nick=new_nickname) await ctx.send( f"My nickname has been changed to '{new_nickname}' in this server." ) except discord.Forbidden: await ctx.send("I don't have permission to change my nickname here.") except Exception as e: await ctx.send(f"An error occurred: {e}") @app_commands.command( name="change_nickname", description="Changes the bot's nickname in the current server.", ) @app_commands.describe(new_nickname="The new nickname for the bot.") @app_commands.checks.has_permissions(administrator=True) async def slash_change_nickname( self, interaction: discord.Interaction, new_nickname: str ): """Changes the bot's nickname in the current server.""" try: await interaction.guild.me.edit(nick=new_nickname) await interaction.response.send_message( f"My nickname has been changed to '{new_nickname}' in this server.", ephemeral=True, ) except discord.Forbidden: await interaction.response.send_message( "I don't have permission to change my nickname here.", ephemeral=True ) except Exception as e: await interaction.response.send_message( f"An error occurred: {e}", ephemeral=True ) @commands.command( name="change_avatar", help="Changes the bot's global avatar. Owner only. Provide a direct image URL.", ) @commands.is_owner() async def change_avatar(self, ctx: commands.Context, image_url: str): """Changes the bot's global avatar. Requires a direct image URL.""" if not (image_url.startswith("http://") or image_url.startswith("https://")): await ctx.send( "Invalid URL. Please provide a direct link to an image (http:// or https://)." ) return try: async with httpx.AsyncClient() as client: response = await client.get(image_url) response.raise_for_status() # Raise an exception for bad status codes image_bytes = await response.aread() await self.bot.user.edit(avatar=image_bytes) await ctx.send("My avatar has been updated!") except httpx.RequestError as e: await ctx.send(f"Could not fetch the image from the URL: {e}") except discord.Forbidden: await ctx.send( "I don't have permission to change my avatar. This might be due to rate limits or other restrictions." ) except discord.HTTPException as e: await ctx.send(f"Failed to change avatar. Discord API error: {e}") except Exception as e: await ctx.send(f"An unexpected error occurred: {e}") @app_commands.command( name="change_avatar", description="Changes the bot's global avatar using a URL or an uploaded image.", ) @app_commands.describe( image_url="A direct URL to the image for the new avatar (optional if attachment is provided).", attachment="An image file to use as the new avatar (optional if URL is provided).", ) @app_commands.check(is_owner_check) async def slash_change_avatar( self, interaction: discord.Interaction, image_url: str = None, attachment: discord.Attachment = None, ): """Changes the bot's global avatar. Accepts a direct image URL or an attachment.""" await interaction.response.defer(ephemeral=True) image_bytes = None if attachment: if not attachment.content_type or not attachment.content_type.startswith( "image/" ): await interaction.response.send_message( "Invalid file type. Please upload an image.", ephemeral=True ) return try: image_bytes = await attachment.read() except Exception as e: await interaction.response.send_message( f"Could not read the attached image: {e}", ephemeral=True ) return elif image_url: if not ( image_url.startswith("http://") or image_url.startswith("https://") ): await interaction.response.send_message( "Invalid URL. Please provide a direct link to an image (http:// or https://).", ephemeral=True, ) return try: async with httpx.AsyncClient() as client: response = await client.get(image_url) response.raise_for_status() # Raise an exception for bad status codes image_bytes = await response.aread() except httpx.RequestError as e: await interaction.response.send_message( f"Could not fetch the image from the URL: {e}", ephemeral=True ) return else: await interaction.response.send_message( "Please provide either an image URL or an attachment.", ephemeral=True ) return if image_bytes: try: await self.bot.user.edit(avatar=image_bytes) await interaction.response.send_message( "My avatar has been updated!", ephemeral=True ) except discord.Forbidden: await interaction.response.send_message( "I don't have permission to change my avatar. This might be due to rate limits or other restrictions.", ephemeral=True, ) except discord.HTTPException as e: await interaction.response.send_message( f"Failed to change avatar. Discord API error: {e}", ephemeral=True ) except Exception as e: await interaction.response.send_message( f"An unexpected error occurred: {e}", ephemeral=True ) # This else should ideally not be reached if logic above is correct, but as a fallback: else: await interaction.response.send_message( "Failed to process the image.", ephemeral=True ) @change_nickname.error @change_avatar.error async def on_command_error(self, ctx: commands.Context, error): if isinstance(error, commands.MissingPermissions): await ctx.send( "You don't have the required permissions (Administrator) to use this command." ) elif isinstance(error, commands.NotOwner): await ctx.send( "This command can only be used by the bot owner. If you wish to customize your bot's appearance, please set up a custom bot on the [web dashboard.](https://slipstreamm.dev/dashboard/)" ) elif isinstance(error, commands.MissingRequiredArgument): await ctx.send( f"Missing required argument: `{error.param.name}`. Please check the command's help." ) else: print(f"Error in BotAppearanceCog: {error}") # Log other errors to console await ctx.send("An internal error occurred. Please check the logs.") # It's generally better to handle app command errors with a cog-level error handler # or within each command if specific handling is needed. # For simplicity, adding a basic error handler for app_commands. async def cog_app_command_error( self, interaction: discord.Interaction, error: app_commands.AppCommandError ): if isinstance(error, app_commands.MissingPermissions): await interaction.response.send_message( "You don't have the required permissions (Administrator) to use this command.", ephemeral=True, ) elif isinstance(error, app_commands.CheckFailure): await interaction.response.send_message( "This command can only be used by the bot owner. If you wish to customize your bot's appearance, please set up a custom bot on the web dashboard.", ephemeral=True, ) else: print( f"Error in BotAppearanceCog (app_command): {error}" ) # Log other errors to console if not interaction.response.is_done(): await interaction.response.send_message( "An internal error occurred. Please check the logs.", ephemeral=True ) else: await interaction.followup.send( "An internal error occurred. Please check the logs.", ephemeral=True ) async def setup(bot): await bot.add_cog(BotAppearanceCog(bot))