Spaces:
Runtime error
Runtime error
| # cogs/admin.py | |
| import discord | |
| from discord import app_commands | |
| from discord.ext import commands | |
| import os | |
| import time | |
| import aiohttp | |
| import urllib.parse | |
| import logging | |
| import asyncio | |
| from database.db import db | |
| from utils.image_prep import prepare_database_image | |
| logger = logging.getLogger(__name__) | |
| class AdminCog(commands.Cog): | |
| def __init__(self, bot): | |
| self.bot = bot | |
| # ========================================================== | |
| # 👤 ADD SINGLE CHARACTER | |
| # ========================================================== | |
| async def add_character(self, interaction: discord.Interaction, name: str, series: str, rarity: app_commands.Choice[str], element: str, atk: int, defense: int, image: discord.Attachment, variant: str = None): | |
| await interaction.response.defer(ephemeral=True) | |
| base_name = name.strip() | |
| final_name = f"{base_name} ({variant.strip()})" if variant else base_name | |
| async with db.pool.acquire() as conn: | |
| exists = await conn.fetchval("SELECT 1 FROM characters WHERE name ILIKE $1", final_name) | |
| if exists: | |
| if variant: | |
| return await interaction.followup.send(f"❌ Error: The variant **{final_name}** already exists!", ephemeral=True) | |
| else: | |
| count = await conn.fetchval("SELECT COUNT(*) FROM characters WHERE name ILIKE $1", f"{base_name} (Alt %)") | |
| alt_num = count + 1 if count else 1 | |
| final_name = f"{base_name} (Alt {alt_num})" | |
| while await conn.fetchval("SELECT 1 FROM characters WHERE name ILIKE $1", final_name): | |
| alt_num += 1 | |
| final_name = f"{base_name} (Alt {alt_num})" | |
| supabase_url = os.getenv("SUPABASE_URL") | |
| supabase_key = os.getenv("SUPABASE_KEY") | |
| bucket_name = os.getenv("SUPABASE_BUCKET", "gacha-images") | |
| if not supabase_url or not supabase_key: | |
| return await interaction.followup.send("❌ Error: Supabase keys are missing from your .env file.", ephemeral=True) | |
| raw_image_bytes = await image.read() | |
| try: | |
| optimized_bytes = await asyncio.to_thread(prepare_database_image, raw_image_bytes) | |
| safe_filename = f"single_{int(time.time())}_{final_name.replace(' ', '_').lower()}.webp" | |
| upload_url = f"{supabase_url}/storage/v1/object/{bucket_name}/{safe_filename}" | |
| headers = { | |
| "Authorization": f"Bearer {supabase_key}", | |
| "apikey": supabase_key, | |
| "Content-Type": "image/webp" | |
| } | |
| async with aiohttp.ClientSession() as session: | |
| async with session.post(upload_url, headers=headers, data=optimized_bytes) as resp: | |
| if resp.status not in [200, 201]: | |
| error_json = await resp.json() | |
| logger.error(f"Supabase upload failed: {error_json}") | |
| return await interaction.followup.send("❌ Supabase API rejected the upload.", ephemeral=True) | |
| public_url = f"{supabase_url}/storage/v1/object/public/{bucket_name}/{safe_filename}" | |
| async with db.pool.acquire() as conn: | |
| await conn.execute( | |
| """ | |
| INSERT INTO characters (name, series, rarity, element, atk, def, image_url) | |
| VALUES ($1, $2, $3, $4, $5, $6, $7) | |
| """, | |
| final_name, series, rarity.value, element, atk, defense, public_url | |
| ) | |
| embed = discord.Embed(title="✅ Character Added Safely!", color=discord.Color.green()) | |
| embed.add_field(name="Name", value=final_name, inline=True) | |
| embed.add_field(name="Series", value=series, inline=True) | |
| embed.add_field(name="Rarity", value=rarity.name, inline=True) | |
| embed.set_thumbnail(url=public_url) | |
| await interaction.followup.send(embed=embed) | |
| except Exception as e: | |
| logger.error(f"Add Character Error: {e}", exc_info=True) | |
| await interaction.followup.send("❌ An unexpected error occurred.", ephemeral=True) | |
| async def add_frame(self, interaction: discord.Interaction, rarity: app_commands.Choice[str], frame_image: discord.Attachment): | |
| await interaction.response.defer(ephemeral=True) | |
| if not frame_image.filename.lower().endswith('.png'): | |
| return await interaction.followup.send("❌ Error: Frame images MUST be `.png` files.", ephemeral=True) | |
| base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| frames_dir = os.path.join(base_dir, "assets", "frames") | |
| os.makedirs(frames_dir, exist_ok=True) | |
| save_path = os.path.join(frames_dir, f"{rarity.value}.png") | |
| try: | |
| await frame_image.save(save_path) | |
| await interaction.followup.send(f"✅ The **{rarity.name}** frame has been uploaded!") | |
| except Exception as e: | |
| await interaction.followup.send("❌ Failed to save the frame.", ephemeral=True) | |
| async def setup(bot): | |
| await bot.add_cog(AdminCog(bot)) | |