Spaces:
Runtime error
Runtime error
File size: 6,636 Bytes
732ca45 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | # 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
# ==========================================================
@app_commands.command(name="add_character", description="[ADMIN] Add a character or alternate variant")
@app_commands.default_permissions(administrator=True)
@app_commands.describe(
name="Character Name (e.g. Raiden Shogun)",
series="Anime/Game Series",
rarity="Rarity (Common, Rare, Epic, Legendary, Mythic)",
element="Element type",
atk="Base Attack Stat",
defense="Base Defense Stat",
image="Upload the RAW character image",
variant="Optional: Create an alt version (e.g. Summer)"
)
@app_commands.choices(rarity=[
app_commands.Choice(name="Common", value="C"),
app_commands.Choice(name="Rare", value="R"),
app_commands.Choice(name="Epic", value="SR"),
app_commands.Choice(name="Legendary", value="SSR"),
app_commands.Choice(name="Mythic", value="UR")
])
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)
@app_commands.command(name="add_frame", description="[ADMIN] Upload a card frame directly to your bot's assets")
@app_commands.default_permissions(administrator=True)
@app_commands.choices(rarity=[
app_commands.Choice(name="Common", value="C"),
app_commands.Choice(name="Rare", value="R"),
app_commands.Choice(name="Epic", value="SR"),
app_commands.Choice(name="Legendary", value="SSR"),
app_commands.Choice(name="Mythic", value="UR")
])
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))
|