IMVU Badge Granting Automation (Improved UI with URL-based Username)

Automate badge granting on IMVU avatars page with pop-out interface and clean layout

目前為 2025-02-20 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         IMVU Badge Granting Automation (Improved UI with URL-based Username)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Automate badge granting on IMVU avatars page with pop-out interface and clean layout
// @author       heapsofjoy
// @match        https://avatars.imvu.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Extract username from the URL
    const getUsernameFromUrl = () => {
        const url = window.location.href; // Get the current URL
        console.log("Current URL:", url); // Debugging: check if URL is correct
        const match = url.match(/https:\/\/avatars\.imvu\.com\/([A-Za-z0-9_]+)/); // Match the username part in the URL
        if (match) {
            console.log("Username extracted from URL:", match[1]); // Debugging: show username extracted
            return match[1];
        } else {
            console.error("Username extraction failed from URL."); // Error if username isn't found
            return null;
        }
    };

    // Fetch CID for the entered username
    const getCid = (name) => {
        return fetch(`https://api.imvu.com/user?username=${name}`, { credentials: 'include' })
            .then(response => response.json())
            .then(data => {
                return Object.entries(data.denormalized)[0][1].data.legacy_cid;
            })
            .catch(err => {
                console.error("Error fetching CID:", err); // Debugging: if fetch fails
            });
    };

    // Start granting badges
    let cancelGranting = false; // Flag to control the cancellation of the badge granting process
    const startGrantingBadges = async (referrer, outputBox, titleElement) => {
        const cid = await getCid(referrer);
        if (!cid) {
            showProgress(outputBox, 'Error: Unable to get CID for the username.');
            return;
        }

        // Function to grant a badge
        const func = async (i) => {
            if (cancelGranting) return; // Stop if the user has clicked the stop button

            // Grant the badge
            const response = await fetch('https://avatars.imvu.com/api/service/grant_badge.php', {
                credentials: 'include', // Include credentials (cookies/session info)
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0',
                    'Accept': '*/*',
                    'Accept-Language': 'en-US,en;q=0.5',
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    'X-Requested-With': 'XMLHttpRequest',
                },
                referrer: `https://avatars.imvu.com/${referrer}`,
                body: `sauce=${IMVU.sauce}&badgeid=badge-${cid}-${i}`,
                method: 'POST',
                mode: 'cors',
            });

            // If the badge was granted (check for successful response)
            if (response.ok) {
                showProgress(outputBox, `Granted badge #${i}`);
            } else {
                showProgress(outputBox, `Failed to grant badge #${i}. Trying next.`);
            }
        };

        // Recursive function to iterate badge granting
        const recursive = async (i) => {
            if (cancelGranting) return; // Stop if the user has clicked the stop button
            setTimeout(async () => {
                await func(i); // Try granting the badge
                recursive(i + 1); // Move to the next badge
            }, 2500);  // Delay between badge granting
        };

        // Update the title to indicate the process is running
        titleElement.innerText = `Granting badges for ${referrer}...`;

        // Start the recursive badge granting process
        recursive(1);
    };

    // Show progress in the output box
    const showProgress = (outputBox, message) => {
        outputBox.textContent += `\n${message}`;
        outputBox.scrollTop = outputBox.scrollHeight;  // Scroll to the bottom
    };

    // Create pop-out button to toggle the interface visibility
    const createPopoutButton = () => {
        const button = document.createElement('button');
        button.innerText = '▶'; // Use a play button icon for a more intuitive look
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.width = '40px';
        button.style.height = '40px';
        button.style.borderRadius = '50%';
        button.style.backgroundColor = '#4CAF50';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.fontSize = '20px';
        button.style.cursor = 'pointer';
        button.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
        button.style.transition = 'background-color 0.3s, transform 0.3s';

        // Hover effect
        button.addEventListener('mouseover', () => {
            button.style.backgroundColor = '#45a049';
            button.style.transform = 'scale(1.1)';
        });
        button.addEventListener('mouseout', () => {
            button.style.backgroundColor = '#4CAF50';
            button.style.transform = 'scale(1)';
        });

        // Toggle pop-out box visibility
        button.addEventListener('click', () => {
            const popoutBox = document.getElementById('badge-granting-box');
            popoutBox.style.display = (popoutBox.style.display === 'none' || !popoutBox.style.display) ? 'block' : 'none';
        });

        document.body.appendChild(button);
    };

    // Create the interface container
    const createInterfaceBox = () => {
        const box = document.createElement('div');
        box.id = 'badge-granting-box';
        box.style.position = 'fixed';
        box.style.bottom = '20px';
        box.style.right = '20px';
        box.style.width = '320px';
        box.style.height = '400px';
        box.style.backgroundColor = '#fff';
        box.style.border = '1px solid #ccc';
        box.style.borderRadius = '8px';
        box.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
        box.style.padding = '15px';
        box.style.display = 'none'; // Initially hidden

        // Add Title Above Output Box
        const titleElement = document.createElement('h3');
        titleElement.innerText = 'Initializing...';
        titleElement.style.textAlign = 'center';
        titleElement.style.marginBottom = '10px';
        titleElement.style.fontSize = '18px';
        titleElement.style.fontWeight = 'bold';
        box.appendChild(titleElement);

        // Create output box for displaying progress
        const outputBox = document.createElement('div');
        outputBox.style.height = '250px';
        outputBox.style.overflowY = 'auto';
        outputBox.style.backgroundColor = '#f8f8f8';
        outputBox.style.border = '1px solid #ccc';
        outputBox.style.borderRadius = '5px';
        outputBox.style.fontSize = '14px';
        outputBox.style.padding = '10px';
        outputBox.style.whiteSpace = 'pre-wrap'; // Preserve line breaks
        box.appendChild(outputBox);

        // Create run and stop buttons
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.justifyContent = 'space-between';
        buttonContainer.style.marginTop = '10px';

        // Run Button
        const runButton = document.createElement('button');
        runButton.innerText = 'Run';
        runButton.style.flex = '1';
        runButton.style.padding = '10px';
        runButton.style.backgroundColor = '#4CAF50';
        runButton.style.color = 'white';
        runButton.style.border = 'none';
        runButton.style.borderRadius = '5px';
        runButton.style.cursor = 'pointer';
        runButton.addEventListener('click', () => {
            const username = getUsernameFromUrl(); // Get username from the URL
            if (username) {
                showProgress(outputBox, 'Starting badge granting...');
                startGrantingBadges(username, outputBox, titleElement);
            } else {
                showProgress(outputBox, 'Error: Could not extract username.');
            }
        });

        // Stop Button
        const stopButton = document.createElement('button');
        stopButton.innerText = 'Stop';
        stopButton.style.flex = '1';
        stopButton.style.padding = '10px';
        stopButton.style.backgroundColor = '#f44336';
        stopButton.style.color = 'white';
        stopButton.style.border = 'none';
        stopButton.style.borderRadius = '5px';
        stopButton.style.cursor = 'pointer';
        stopButton.addEventListener('click', () => {
            cancelGranting = true; // Stop the granting process
            showProgress(outputBox, 'Process stopped.');
            titleElement.innerText = 'Stopped';
        });

        buttonContainer.appendChild(runButton);
        buttonContainer.appendChild(stopButton);
        box.appendChild(buttonContainer);

        document.body.appendChild(box);
    };

    // Initialize everything
    createPopoutButton();
    createInterfaceBox();
})();