Drawaria Buttons Fixed Improved

Asegura que los botones de subida estén siempre visibles y funcionales. Implementa un cooldown interno para evitar errores del servidor.

// ==UserScript==
// @name         Drawaria Buttons Fixed Improved
// @namespace    http://tampermonkey.net/
// @version      1.01
// @description  Asegura que los botones de subida estén siempre visibles y funcionales. Implementa un cooldown interno para evitar errores del servidor.
// @author       YouTubeDrawaria
// @match        https://drawaria.online/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      picsum.photos
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// ==/UserScript==

(function() {
    'use-strict';

    const COOLDOWN_DURATION = 60 * 1000; // 60 segundos en milisegundos
    let lastUploadTime = 0; // Marca de tiempo de la última subida
    let cooldownDisplayTimeout = null; // Para limpiar el mensaje de cooldown

    // --- Inyectamos estilos para que los botones se vean bien ---
    GM_addStyle(`
        #superSendButton, #downloadCanvasButton, #uploadTenTimesButton, #uploadTwentyTimesButton, #uploadRandomSketchButton {
            margin-bottom: 0.5em; /* Espacio debajo de los botones */
        }
        #superSendButton .spinner-border, #downloadCanvasButton .spinner-border, #uploadTenTimesButton .spinner-border, #uploadTwentyTimesButton .spinner-border, #uploadRandomSketchButton .spinner-border {
            width: 1em;
            height: 1em;
            margin-left: 0.5em;
            vertical-align: text-bottom;
        }
        #uploadStatusSection {
            font-size: 0.85em;
            color: #555;
            text-align: center;
            margin-top: -0.2em;
            margin-bottom: 0.5em;
            min-height: 1.2em; /* Para mantener el espacio */
        }
        .cooldown-active-msg {
            color: orange;
            font-weight: bold;
        }
        .upload-error-msg {
            color: red;
            font-weight: bold;
        }
        .upload-success-msg {
            color: green;
            font-weight: bold;
        }
    `);

    /**
     * Espera a que un elemento DOM esté disponible.
     * @param {string} selector - Selector CSS del elemento.
     * @param {function} callback - Función a ejecutar cuando el elemento se encuentre.
     * @param {number} [intervalTime=100] - Intervalo de chequeo en ms.
     */
    function waitForElement(selector, callback, intervalTime = 100) {
        const intervalId = setInterval(() => {
            const element = document.querySelector(selector);
            if (element) {
                clearInterval(intervalId);
                callback(element);
            }
        }, intervalTime);
    }

    /**
     * Obtiene el valor de una flag de modo de juego de forma segura.
     * Si las variables de Drawaria no están disponibles, devuelve `false`.
     * @param {string} flagConstName - Nombre de la constante global (ej. 'PGMODE_STENCILS').
     * @returns {boolean} - True si el modo está activo, false en caso contrario.
     */
    const getGameModeFlag = (flagConstName) => {
        // Accede directamente a window.Fn y window[flagConstName] si existen.
        if (typeof window.Fn !== 'undefined' && typeof window[flagConstName] !== 'undefined') {
            const flagValue = window.Fn[window[flagConstName]];
            return !!flagValue; // Asegura que siempre sea un booleano.
        }
        return false; // Valor por defecto si las variables del juego no están disponibles.
    };

    /**
     * Inyecta y ejecuta un fragmento de script en el contexto de la página.
     * Esto permite acceder a variables y funciones que no son accesibles desde el entorno aislado de Tampermonkey.
     * @param {string} codeToInject - El código JavaScript a inyectar y ejecutar.
     */
    function injectScriptInPageContext(codeToInject) {
        const script = document.createElement('script');
        script.textContent = `(function() { ${codeToInject} })();`; // Envuelve el código en un IIFE.
        (document.head || document.documentElement).appendChild(script);
        script.remove(); // Limpia el elemento script del DOM inmediatamente después de la ejecución.
    }

    /**
     * Actualiza el mensaje de estado de subida (cooldown, errores, etc.).
     * @param {string} message - El mensaje a mostrar.
     * @param {string} [type=''] - Clase CSS para el mensaje (ej. 'cooldown-active-msg', 'upload-error-msg').
     * @param {number} [duration=3000] - Duración en ms antes de limpiar el mensaje (0 para no limpiar).
     */
    function setUploadStatusMessage(message, type = '', duration = 3000) {
        const statusSection = document.getElementById('uploadStatusSection');
        if (statusSection) {
            statusSection.innerHTML = message;
            statusSection.className = `upload-status-section ${type}`;
            if (cooldownDisplayTimeout) {
                clearTimeout(cooldownDisplayTimeout);
            }
            if (duration > 0) {
                cooldownDisplayTimeout = setTimeout(() => {
                    statusSection.innerHTML = '';
                    statusSection.className = 'upload-status-section';
                }, duration);
            }
        }
    }


    /**
     * Inicia el temporizador de cooldown global después de una subida.
     */
    function startGlobalCooldown() {
        lastUploadTime = Date.now();
        localStorage.setItem('drawariaLastUploadTime', lastUploadTime); // Guardar en localStorage
        setUploadStatusMessage(`Cooldown: ${Math.ceil(COOLDOWN_DURATION / 1000)}s restantes.`, 'cooldown-active-msg', COOLDOWN_DURATION + 1000);
    }

    /**
     * Función para descargar el dibujo actual del canvas como PNG.
     */
    function downloadDrawing() {
        const downloadButton = document.getElementById('downloadCanvasButton');
        const spinner = downloadButton.querySelector('.spinner-border');

        spinner.style.display = 'inline-block';
        downloadButton.disabled = true; // Deshabilitar solo este botón durante la descarga

        try {
            const mainCanvas = document.getElementById('canvas');
            if (!mainCanvas) {
                throw new Error("No se encontró el canvas principal para descargar.");
            }

            let sourceCanvas = mainCanvas;
            const targetWordElement = document.getElementById('targetword');
            const isWordGuessMode = (targetWordElement && targetWordElement.style.display !== 'none');

            if (!isWordGuessMode && typeof window.ze !== 'undefined' && window.ze instanceof HTMLCanvasElement && window.ze.width > 0 && window.ze.height > 0) {
                sourceCanvas = window.ze;
                console.log("Usando el canvas auto-guardado (window.ze) para la descarga.");
            } else {
                console.log("Usando el canvas visible (mainCanvas) para la descarga.");
            }

            // Usar canvas.toBlob() para obtener el Blob del dibujo.
            sourceCanvas.toBlob((blob) => {
                if (blob) {
                    const filename = `Drawaria-online-${new Date().toISOString().slice(0,10)}.png`;
                    // Utilizar la función saveAs de FileSaver.js, que Drawaria ya carga.
                    if (typeof window.saveAs === 'function') {
                        window.saveAs(blob, filename);
                        console.log(`Dibujo descargado como ${filename}`);
                        setUploadStatusMessage('Dibujo descargado con éxito.', 'upload-success-msg');
                    } else {
                        const url = URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.href = url;
                        a.download = filename;
                        document.body.appendChild(a);
                        a.click();
                        document.body.removeChild(a);
                        URL.revokeObjectURL(url);
                        console.warn("window.saveAs no disponible, usando fallback para descargar.");
                        setUploadStatusMessage('Dibujo descargado (fallback).', 'upload-success-msg');
                    }
                } else {
                    throw new Error("No se pudo crear el Blob del canvas.");
                }
            }, 'image/png'); // Siempre descargar como PNG.

        } catch (error) {
            console.error("Error al descargar el dibujo:", error);
            setUploadStatusMessage(`Error: ${error.message}`, 'upload-error-msg');
            alert("Ocurrió un error al descargar el dibujo: " + error.message);
        } finally {
            spinner.style.display = 'none';
            downloadButton.disabled = false;
        }
    }

    /**
     * Procesa una imagen (ya sea del canvas del juego o externa) y la convierte a DataURLs para la subida.
     * @param {HTMLCanvasElement|HTMLImageElement} sourceImage - El canvas del juego o un objeto Image ya cargado.
     * @param {boolean} isSourceCanvas - True si sourceImage es un HTMLCanvasElement, false si es un HTMLImageElement.
     * @returns {Promise<{imageData: string, thumbData: string}>} DataURLs procesados.
     */
    async function processImageForUpload(sourceImage, isSourceCanvas) {
        let imgToDraw = sourceImage;

        // Si la fuente es un canvas, convertimos primero a DataURL (PNG) y luego a Image.
        // Esto sirve como "purificador" para canvas complejos.
        if (isSourceCanvas) {
            const sourceDataURL = sourceImage.toDataURL('image/png');
            imgToDraw = await new Promise((resolve, reject) => {
                const tempImg = new Image();
                tempImg.onload = () => resolve(tempImg);
                tempImg.onerror = (e) => reject(new Error("Error al cargar la imagen del canvas fuente para purificación."));
                tempImg.src = sourceDataURL;
            });
        }

        // --- Procesamiento de la imagen principal para subir (JPEG 1200x1000) ---
        const uploadCanvas = document.createElement("canvas");
        const uploadCtx = uploadCanvas.getContext("2d");
        uploadCanvas.width = 1200;
        uploadCanvas.height = 1000;
        uploadCtx.fillStyle = "#ffffff";
        uploadCtx.fillRect(0, 0, uploadCanvas.width, uploadCanvas.height);
        uploadCtx.drawImage(imgToDraw, 0, 0, uploadCanvas.width, uploadCanvas.height); // Dibujar Image object.
        if (getGameModeFlag('PGMODE_PIXELART')) {
            uploadCtx.imageSmoothingEnabled = false;
        } else {
            uploadCtx.imageSmoothingEnabled = true;
        }

        // --- Procesamiento de la miniatura para subir (JPEG 300x[proporción]) ---
        const thumbCanvas = document.createElement("canvas");
        const thumbCtx = thumbCanvas.getContext("2d");
        thumbCanvas.width = 300;
        thumbCanvas.height = 300 / 1.2;
        thumbCtx.fillStyle = "#ffffff";
        thumbCtx.fillRect(0, 0, thumbCanvas.width, thumbCanvas.height);
        thumbCtx.drawImage(imgToDraw, 0, 0, thumbCanvas.width, thumbCanvas.height); // Dibujar Image object.
        if (getGameModeFlag('PGMODE_PIXELART')) {
            thumbCtx.imageSmoothingEnabled = false;
        } else {
            thumbCtx.imageSmoothingEnabled = true;
        }

        // Convertir los canvases a DataURL (JPEG con compresión).
        const imageData = uploadCanvas.toDataURL("image/jpeg", 0.8);
        const thumbData = thumbCanvas.toDataURL("image/jpeg", 0.8);

        // --- IMPORTANTE: Extraer solo la parte base64 si el servidor lo espera así. ---
        const base64ImageData = imageData.split(',')[1];
        const base64ThumbData = thumbData.split(',')[1];

        return { imageData: base64ImageData, thumbData: base64ThumbData };
    }


    /**
     * Función base para subir un dibujo a la galería.
     * @param {HTMLCanvasElement|HTMLImageElement} sourceContent - El canvas del juego o un objeto Image ya cargado.
     * @param {boolean} isSourceCanvas - True si sourceContent es un HTMLCanvasElement, false si es un HTMLImageElement.
     * @param {boolean} shouldRedirect - Si la página debe redirigir después de la subida.
     * @returns {Promise<boolean>} Resuelve a true si la subida fue exitosa, false si hubo un error.
     */
    async function uploadDrawingToGallery(sourceContent, isSourceCanvas, shouldRedirect = true) {
        // Mostrar spinner para el botón principal si es una subida individual
        const superSendButton = document.getElementById('superSendButton');
        const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null;
        if (spinner && shouldRedirect) {
            spinner.style.display = 'inline-block';
        }

        // --- Verificar Cooldown ---
        const now = Date.now();
        const timeLeft = lastUploadTime + COOLDOWN_DURATION - now;
        if (timeLeft > 0) {
            const secondsLeft = Math.ceil(timeLeft / 1000);
            const msg = `Cooldown: ${secondsLeft}s. Por favor, espera.`;
            setUploadStatusMessage(msg, 'cooldown-active-msg', 0); // No limpiar automáticamente
            console.warn(msg);
            if (spinner && shouldRedirect) spinner.style.display = 'none'; // Quitar spinner
            return false; // Fallo debido a cooldown
        }

        try {
            const processedData = await processImageForUpload(sourceContent, isSourceCanvas);
            const base64ImageData = processedData.imageData;
            const base64ThumbData = processedData.thumbData;

            // --- Preparación y envío de la petición HTTP ---
            let roomId = (window.location.pathname.match(/room\/([^/]+)/) || [])[1] || '';
            const galleryHost = "//" + (window.qn || "gallery.drawaria.online");
            const sessionId = (document.cookie.match(/sid1=([^;]+)/) || [])[1] || "";
            const postUrl = `${galleryHost}/gallery/uploadimage/?sessionid=${sessionId}`;

            const formData = new URLSearchParams();
            formData.append('imagedata', base64ImageData);
            formData.append('imagedata1', base64ThumbData);
            formData.append('room', roomId);
            formData.append('targetword', '');
            formData.append('guestplayernames', null);
            formData.append('playeruids', null);
            formData.append('stencils', JSON.stringify(getGameModeFlag('PGMODE_STENCILS')));
            formData.append('pixelart', JSON.stringify(getGameModeFlag('PGMODE_PIXELART')));
            formData.append('advtools', JSON.stringify(getGameModeFlag('PGMODE_ADVTOOLS')));

            const response = await fetch(postUrl, {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: formData
            });

            if (!response.ok) {
                let errorDetails = await response.text();
                try { const errorJson = JSON.parse(errorDetails); errorDetails = errorJson.error || errorDetails; } catch (e) { /* no es JSON */ }
                throw new Error(`Error de red o servidor (${response.status}): ${errorDetails}`);
            }

            const result = await response.json();
            if (result.error) { throw new Error(result.error); }

            // Éxito: iniciar cooldown
            startGlobalCooldown();

            // --- Notificación interna del juego (inyección de script, mejor esfuerzo) ---
            if (shouldRedirect) { // Solo inyecta si es una subida individual que lleva a redirigir
                const notificationScriptCode = `
                    (function() {
                        if (typeof window.Je !== 'undefined') window.Je = true;
                        if (typeof window.Ze !== 'undefined') window.Ze = Date.now();
                        let notificationSent = false;
                        const imageId = '${result.imageid}';
                        if (typeof window.Hr === 'function') { try { window.Hr('clientnotify', null, 11, [imageId]); notificationSent = true; } catch (e) { console.error("Injected: Error calling Hr:", e); } }
                        if (!notificationSent && typeof window.Nt === 'object' && window.Nt !== null && typeof window.Nt.emit === 'function') {
                            try { window.Nt.emit('clientnotify', null, 11, [imageId]); notificationSent = true; } catch (e) { console.error("Injected: Error calling Nt.emit:", e); }
                        }
                        if (!notificationSent) { console.warn("Injected: No se pudo enviar la notificación interna del juego."); }
                    })();
                `;
                injectScriptInPageContext(notificationScriptCode);
                setUploadStatusMessage('Dibujo subido con éxito. Redirigiendo...', 'upload-success-msg');
                console.log("¡Dibujo subido con éxito! Notificación interna intentada via inyección de script.");
            } else {
                 console.log(`¡Dibujo subido con éxito! ID: ${result.imageid}`);
            }

            // Redirigir si se solicitó.
            if (shouldRedirect && result.imageid) {
                const galleryHost = "//" + (window.qn || "gallery.drawaria.online");
                window.location.href = `${galleryHost}/gallery/img/${result.imageid}`;
            } else if (shouldRedirect) {
                const galleryHost = "//" + (window.qn || "gallery.drawaria.online");
                window.location.href = `${galleryHost}/gallery/new`;
            }
            return true;
        } catch (error) {
            console.error("Error al subir el dibujo:", error);
            setUploadStatusMessage(`Error: ${error.message}`, 'upload-error-msg');
            if (shouldRedirect) {
                alert("Ocurrió un error al subir el dibujo: " + error.message + ". Por favor, inténtalo de nuevo.");
            }
            return false;
        } finally {
            if (spinner && shouldRedirect) {
                spinner.style.display = 'none';
            }
        }
    }

    /**
     * Sube el dibujo actual del canvas del juego.
     */
    async function uploadGameCanvasDrawing() {
        const mainCanvas = document.getElementById('canvas');
        if (!mainCanvas) {
            alert("No se encontró el canvas principal del juego.");
            return;
        }

        let sourceCanvas = mainCanvas;
        const targetWordElement = document.getElementById('targetword');
        const isWordGuessMode = (targetWordElement && targetWordElement.style.display !== 'none');

        // Seleccionar el canvas fuente.
        if (!isWordGuessMode && typeof window.ze !== 'undefined' && window.ze instanceof HTMLCanvasElement && window.ze.width > 0 && window.ze.height > 0) {
            sourceCanvas = window.ze;
        }
        await uploadDrawingToGallery(sourceCanvas, true, true); // Redirige
    }

    /**
     * Sube un boceto aleatorio desde una API externa (una vez).
     */
    async function uploadRandomSketch() {
        const uploadRandomBtn = document.getElementById('uploadRandomSketchButton');
        const superSendButton = document.getElementById('superSendButton'); // Main button to show spinner
        const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null;

        // Mostrar spinner para el botón que se clickeó
        if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block';
        if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'inline-block'; // También en el principal

        // No deshabilitamos los botones para que el usuario pueda intentar de nuevo si quiere
        // Esto se gestiona por el mensaje de cooldown

        // --- Verificar Cooldown ---
        const now = Date.now();
        const timeLeft = lastUploadTime + COOLDOWN_DURATION - now;
        if (timeLeft > 0) {
            const secondsLeft = Math.ceil(timeLeft / 1000);
            const msg = `Cooldown: ${secondsLeft}s. Por favor, espera.`;
            setUploadStatusMessage(msg, 'cooldown-active-msg', 0); // No limpiar automáticamente
            console.warn(msg);
            // Quitar spinners si no se procede
            if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none';
            if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none';
            return;
        }

        try {
            // Fetch la imagen de una API externa
            const imageUrl = 'https://picsum.photos/1200/1000'; // Imagen aleatoria para testing
            const blob = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: imageUrl,
                    responseType: "blob",
                    onload: function(response) {
                        if (response.status === 200) {
                            resolve(response.response);
                        } else {
                            reject(new Error(`Fallo al obtener la imagen: ${response.status} ${response.statusText}`));
                        }
                    },
                    onerror: function(response) {
                        reject(new Error(`Error de red al obtener la imagen: ${response.statusText || response.error}`));
                    }
                });
            });

            const imgElement = await new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => resolve(img);
                img.onerror = (e) => reject(new Error("Fallo al cargar la imagen obtenida en el objeto Image."));
                img.src = URL.createObjectURL(blob);
            });
            URL.revokeObjectURL(imgElement.src);

            console.log("Imagen de API externa cargada. Procediendo con la subida.");
            await uploadDrawingToGallery(imgElement, false, true); // Usa Image object, redirige
        } catch (error) {
            console.error("Error al subir boceto de API:", error);
            setUploadStatusMessage(`Error API: ${error.message}`, 'upload-error-msg');
            alert("Ocurrió un error al subir el boceto de API: " + error.message);
        } finally {
            // Quitar spinners al finalizar
            if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none';
            if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none';
        }
    }


    /**
     * Función para subir N dibujos del canvas del juego.
     * @param {number} count - Número de veces a subir.
     */
    async function uploadGameCanvasNTimes(count) {
        const uploadBtn = document.getElementById(count === 10 ? 'uploadTenTimesButton' : 'uploadTwentyTimesButton');
        const superSendButton = document.getElementById('superSendButton');
        const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null;
        const uploadStatusSection = document.getElementById('uploadStatusSection');

        // Mostrar spinners para todos los botones involucrados
        if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'inline-block';
        if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'inline-block';
        const uploadRandomBtn = document.getElementById('uploadRandomSketchButton');
        if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block';


        // --- Verificar Cooldown ---
        const now = Date.now();
        const timeLeft = lastUploadTime + COOLDOWN_DURATION - now;
        if (timeLeft > 0) {
            const secondsLeft = Math.ceil(timeLeft / 1000);
            const msg = `Cooldown: ${secondsLeft}s. Espera para iniciar la subida múltiple.`;
            setUploadStatusMessage(msg, 'cooldown-active-msg', 0); // No limpiar automáticamente
            console.warn(msg);
            // Quitar spinners si no se procede
            if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none';
            if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none';
            if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block';
            return;
        }

        setUploadStatusMessage(`Iniciando ${count} subidas...`, ''); // Mensaje sin clase especial
        let successfulUploads = 0;

        for (let i = 0; i < count; i++) {
            setUploadStatusMessage(`Subiendo ${i + 1}/${count}... (éxitos: ${successfulUploads})`, '');

            const mainCanvas = document.getElementById('canvas');
            if (!mainCanvas) {
                console.error("No se encontró el canvas principal del juego para subida múltiple. Deteniendo.");
                setUploadStatusMessage('Error: Canvas no encontrado.', 'upload-error-msg');
                break;
            }

            let sourceCanvas = mainCanvas;
            const targetWordElement = document.getElementById('targetword');
            const isWordGuessMode = (targetWordElement && targetWordElement.style.display !== 'none');

            if (!isWordGuessMode && typeof window.ze !== 'undefined' && window.ze instanceof HTMLCanvasElement && window.ze.width > 0 && window.ze.height > 0) {
                sourceCanvas = window.ze;
            }
            // Importante: shouldRedirect = false para que no redirija por cada subida
            const success = await uploadDrawingToGallery(sourceCanvas, true, false);
            if (success) {
                successfulUploads++;
            } else {
                // Si falla una subida, aún esperamos el cooldown para el siguiente intento.
                console.warn(`Subida ${i + 1} falló.`);
            }

            // Pausa obligatoria entre subidas para respetar el cooldown del servidor
            if (i < count - 1) { // No esperar después de la última subida
                setUploadStatusMessage(`Subida ${i + 1} completada. Esperando cooldown...`, 'cooldown-active-msg');
                await new Promise(resolve => setTimeout(resolve, COOLDOWN_DURATION));
            }
        }

        // Quitar spinners y mostrar resultado final
        if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none';
        if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none';
        if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none';

        setUploadStatusMessage(`Completado: ${successfulUploads} de ${count} subidas exitosas.`, successfulUploads === count ? 'upload-success-msg' : 'upload-error-msg');
        alert(`¡Finalizado! ${successfulUploads} de ${count} dibujos subidos exitosamente.`);
    }

    /**
     * Función para subir N bocetos de API.
     * @param {number} count - Número de veces a subir.
     */
    async function uploadRandomSketchNTimes(count) {
        const uploadBtn = document.getElementById(count === 10 ? 'uploadTenTimesButton' : 'uploadTwentyTimesButton');
        const superSendButton = document.getElementById('superSendButton');
        const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null;
        const uploadStatusSection = document.getElementById('uploadStatusSection');
        const uploadRandomBtn = document.getElementById('uploadRandomSketchButton');

        // Mostrar spinners
        if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'inline-block';
        if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'inline-block';
        if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block';


        // --- Verificar Cooldown ---
        const now = Date.now();
        const timeLeft = lastUploadTime + COOLDOWN_DURATION - now;
        if (timeLeft > 0) {
            const secondsLeft = Math.ceil(timeLeft / 1000);
            const msg = `Cooldown: ${secondsLeft}s. Espera para iniciar la subida múltiple.`;
            setUploadStatusMessage(msg, 'cooldown-active-msg', 0);
            console.warn(msg);
            // Quitar spinners si no se procede
            if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none';
            if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none';
            if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none';
            return;
        }

        setUploadStatusMessage(`Iniciando ${count} subidas de bocetos...`, '');
        let successfulUploads = 0;

        for (let i = 0; i < count; i++) {
            setUploadStatusMessage(`Subiendo boceto ${i + 1}/${count}... (éxitos: ${successfulUploads})`, '');

            try {
                const imageUrl = 'https://picsum.photos/1200/1000';
                const blob = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: "GET", url: imageUrl, responseType: "blob",
                        onload: (response) => { if (response.status === 200) resolve(response.response); else reject(new Error(`Failed to fetch: ${response.status}`)); },
                        onerror: (response) => reject(new Error(`Network error: ${response.statusText || response.error}`)),
                    });
                });
                const imgElement = await new Promise((resolve, reject) => {
                    const img = new Image(); img.onload = () => resolve(img); img.onerror = () => reject(new Error("Failed to load fetched image.")); img.src = URL.createObjectURL(blob);
                });
                URL.revokeObjectURL(imgElement.src);

                const success = await uploadDrawingToGallery(imgElement, false, false); // No redirigir
                if (success) {
                    successfulUploads++;
                } else {
                    console.warn(`Subida de boceto ${i + 1} falló.`);
                }
            } catch (error) {
                console.error(`Error en la subida de boceto ${i + 1}:`, error);
                setUploadStatusMessage(`Error: Subida de boceto ${i + 1} falló.`, 'upload-error-msg');
            }

            // Pausa obligatoria entre subidas para respetar el cooldown del servidor
            if (i < count - 1) {
                setUploadStatusMessage(`Boceto ${i + 1} completado. Esperando cooldown...`, 'cooldown-active-msg');
                await new Promise(resolve => setTimeout(resolve, COOLDOWN_DURATION));
            }
        }

        // Quitar spinners y mostrar resultado final
        if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none';
        if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none';
        if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none';

        setUploadStatusMessage(`Completado: ${successfulUploads} de ${count} bocetos subidos exitosamente.`, successfulUploads === count ? 'upload-success-msg' : 'upload-error-msg');
        alert(`¡Finalizado! ${successfulUploads} de ${count} bocetos subidos exitosamente.`);
    }



    /**
     * Configura la interfaz del script: crea los botones personalizados y oculta los originales.
     */
    function setupInterface() {
        const rightbar = document.querySelector('#rightbar');
        if (!rightbar || document.getElementById('superSendButton')) return; // Ya existe o no hay padre.

        // Ocultar botones originales del juego
        const originalSendToGalleryButton = document.getElementById('sendtogallery');
        if (originalSendToGalleryButton) originalSendToGalleryButton.style.display = 'none';

        const originalDownloadCanvasButton = document.getElementById('downloadcanvas');
        if (originalDownloadCanvasButton) originalDownloadCanvasButton.style.display = 'none';

        const originalDropdownButton = document.getElementById('roomcontrols-menu');
        if (originalDropdownButton) originalDropdownButton.style.display = 'none';

        const originalDropdownMenu = document.querySelector('#roomcontrols .dropdown-menu');
        if (originalDropdownMenu) originalDropdownMenu.style.display = 'none';

        // Ocultar el mensaje "Inicie sesión en su cuenta..." si está presente.
        const loginMessage = document.querySelector('#roomcontrols .dropdown-header h6');
        if (loginMessage && loginMessage.textContent.includes('Inicie sesión en su cuenta')) {
            loginMessage.style.display = 'none';
        }

        // --- Crear y añadir el botón "Subir Dibujo Falso" ---
        const superSendButton = document.createElement('button');
        superSendButton.id = 'superSendButton';
        superSendButton.type = 'button';
        superSendButton.className = 'btn btn-primary btn-block';
        superSendButton.innerHTML = `
            <i class="fas fa-cloud-upload-alt"></i>
            <span>Subir Dibujo Falso</span>
            <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div>
        `;
        superSendButton.addEventListener('click', uploadGameCanvasDrawing);
        rightbar.prepend(superSendButton);

        // --- Crear y añadir el botón "Descargar Dibujo" ---
        const downloadCanvasButton = document.createElement('button');
        downloadCanvasButton.id = 'downloadCanvasButton';
        downloadCanvasButton.type = 'button';
        downloadCanvasButton.className = 'btn btn-secondary btn-block';
        downloadCanvasButton.innerHTML = `
            <i class="fas fa-download"></i>
            <span>Descargar Dibujo</span>
            <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div>
        `;
        downloadCanvasButton.addEventListener('click', downloadDrawing);
        superSendButton.after(downloadCanvasButton);

        // --- Crear y añadir el botón "Subir 20 Dibujos Falsos" ---
        const uploadTwentyTimesButton = document.createElement('button');
        uploadTwentyTimesButton.id = 'uploadTwentyTimesButton';
        uploadTwentyTimesButton.type = 'button';
        uploadTwentyTimesButton.className = 'btn btn-success btn-block';
        uploadTwentyTimesButton.innerHTML = `
            <i class="fas fa-image"></i>
            <span>Subir 20 Dibujos Falsos</span>
            <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div>
        `;
        uploadTwentyTimesButton.addEventListener('click', () => uploadRandomSketchNTimes(20));
        downloadCanvasButton.after(uploadTwentyTimesButton);

        // --- Crear y añadir el botón "Subir 10 Dibujos Falsos" ---
        const uploadTenTimesButton = document.createElement('button');
        uploadTenTimesButton.id = 'uploadTenTimesButton';
        uploadTenTimesButton.type = 'button';
        uploadTenTimesButton.className = 'btn btn-info btn-block';
        uploadTenTimesButton.innerHTML = `
            <i class="fas fa-cloud-upload-alt"></i>
            <span>Subir 10 Dibujos Falsos</span>
            <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div>
        `;
        uploadTenTimesButton.addEventListener('click', () => uploadGameCanvasNTimes(10));
        uploadTwentyTimesButton.after(uploadTenTimesButton);

        // --- Mensaje de estado de subidas y cooldown ---
        const uploadStatusSection = document.createElement('div');
        uploadStatusSection.id = 'uploadStatusSection';
        uploadStatusSection.textContent = ''; // Vacío inicialmente
        uploadTenTimesButton.after(uploadStatusSection);

        // Cargar el último tiempo de subida al iniciar el script
    }

    // --- Inicio del Script ---
    // Paso 1: Esperar por la barra lateral (#rightbar) para poder añadir los botones.
    waitForElement('#rightbar', (rightbarElement) => {
        if (rightbarElement) {
            setupInterface();
        }
    });

    // Paso 2: Configurar un observador de mutaciones para mantener la interfaz.
    waitForElement('body', (bodyElement) => {
        const observer = new MutationObserver(() => {
            // Si nuestros botones no están presentes, o un botón original reaparece, re-ejecutamos setupInterface().
            if (!document.getElementById('superSendButton') || !document.getElementById('downloadCanvasButton') || !document.getElementById('uploadTwentyTimesButton') || !document.getElementById('uploadTenTimesButton') || (document.getElementById('sendtogallery') && document.getElementById('sendtogallery').style.display !== 'none')) {
                setupInterface();
            }
        });
        observer.observe(bodyElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'disabled'] });
    });

})();