# utils/generators/factory.py import io import aiohttp import discord import logging import asyncio from database.db import db logger = logging.getLogger(__name__) HF_API_URL = "https://amit9011-gacha-forge.hf.space/generate" # 🚦 THE NETWORK THROTTLE: Protects your IP from Hugging Face DDoS limits. # Allows exactly 15 simultaneous renders. The rest wait patiently in line. NETWORK_QUEUE = asyncio.Semaphore(15) async def generate_card_factory(image_url: str, char_name: str, print_num: int, rarity: str, element: str) -> discord.File: async with db.pool.acquire() as conn: custom_frame_url = await conn.fetchval("SELECT custom_frame_url FROM characters WHERE name = $1", char_name) payload = { "image_url": image_url, "char_name": char_name, "print_num": print_num, "rarity": rarity, "element": element, "custom_frame_url": custom_frame_url } # Wait in line to ensure we don't trigger server firewalls async with NETWORK_QUEUE: try: async with aiohttp.ClientSession() as session: # 25 second timeout covers cold-starts and queued renders async with session.post(HF_API_URL, json=payload, timeout=25) as resp: if resp.status != 200: error_text = await resp.text() logger.error(f"Cloud Forge Error [{resp.status}]: {error_text}") raise ValueError("The rendering server is currently overloaded.") # Receive the finished WEBP bytes image_bytes = await resp.read() except Exception as e: logger.error(f"Failed to reach Cloud Forge: {e}") raise ValueError("The rendering server is offline.") safe_name = char_name.replace(" ", "_").replace("-", "_").lower() return discord.File(fp=io.BytesIO(image_bytes), filename=f"{safe_name}.webp")