discordbot/cogs/bot_appearance_cog.py
2025-06-05 21:31:06 -06:00

227 lines
9.6 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))