discordbot/cogs/bot_appearance_cog.py

146 lines
8.5 KiB
Python

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))