gacha-bot / cogs /admin.py
Amit
Clean Cloud Deployment
732ca45
Raw
History Blame Contribute Delete
6.64 kB
# 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))