IMVU Auto Refresh Pended Credits Page

Refreshes IMVU Pended Credits Page to update sales, with UI controls and darkmode neon green styling

Verze ze dne 05. 07. 2025. Zobrazit nejnovější verzi.

// ==UserScript==
// @name         IMVU Auto Refresh Pended Credits Page
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Refreshes IMVU Pended Credits Page to update sales, with UI controls and darkmode neon green styling
// @author       Pythius
// @match        https://www.imvu.com/catalog/developer_report.php?reporttype=pendedcredits
// @icon         https://www.google.com/s2/favicons?sz=64&domain=imvu.com
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const neonColor = '#0d9200';
    const darkBackground = 'rgba(18, 18, 18, 0.9)';
    const storageKey = 'imvuAutoRefreshSettings';

    // Load persistent settings from localStorage (excluding refreshCount)
    let settings = JSON.parse(localStorage.getItem(storageKey)) || {
        refreshInterval: 300000,
        width: null,
        height: null,
        top: 10,
        left: 10,
        running: false,
        lastRefreshTime: null
    };

    // Load refreshCount from sessionStorage (resets on tab close)
    let refreshCount = Number(sessionStorage.getItem('imvuRefreshCount')) || 0;

    // Create UI panel
    const panel = document.createElement('div');
    panel.style.cssText = `
        position: fixed;
        top: ${settings.top}px;
        left: ${settings.left}px;
        background: ${darkBackground};
        color: ${neonColor};
        padding: 6px 8px;
        border-radius: 10px;
        font-family: 'Courier New', monospace;
        font-size: 10px;
        z-index: 9999;
        box-shadow: 0 0 10px ${neonColor};
        border: 1px solid ${neonColor};
        resize: both;
        overflow: hidden;
        user-select: none;
        display: flex;
        flex-direction: column;
        align-items: stretch;
        white-space: nowrap;
    `;

    // Apply saved width and height if available
    if (settings.width && settings.height) {
        panel.style.width = settings.width + 'px';
        panel.style.height = settings.height + 'px';
    }

    // Save settings on resize & move
    const saveSettings = () => {
        settings.width = panel.offsetWidth;
        settings.height = panel.offsetHeight;
        settings.top = panel.offsetTop;
        settings.left = panel.offsetLeft;
        localStorage.setItem(storageKey, JSON.stringify(settings));
    };

    panel.addEventListener('mouseup', saveSettings);
    panel.addEventListener('touchend', saveSettings);

    // Drag handle
    const dragHandle = document.createElement('div');
    dragHandle.textContent = '≡ Drag here';
    dragHandle.style.cssText = `
        cursor: move;
        padding: 2px 4px;
        font-weight: bold;
        color: ${neonColor};
        user-select: none;
        text-align: center;
        margin-bottom: 6px;
    `;
    panel.appendChild(dragHandle);

    // Make panel draggable
    (function makeDraggable(elem) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        dragHandle.onpointerdown = dragMouseDown;

        function dragMouseDown(e) {
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onpointermove = elementDrag;
            document.onpointerup = closeDragElement;
        }

        function elementDrag(e) {
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;

            elem.style.top = (elem.offsetTop - pos2) + "px";
            elem.style.left = (elem.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onpointermove = null;
            document.onpointerup = null;
            saveSettings();
        }
    })(panel);

    // Refresh count display
    const refreshCountDisplay = document.createElement('div');
    panel.appendChild(refreshCountDisplay);

    // Last refresh display
    const lastRefreshDisplay = document.createElement('div');
    panel.appendChild(lastRefreshDisplay);

    // Countdown display
    const countdownDisplay = document.createElement('div');
    panel.appendChild(countdownDisplay);

    // Interval input
    const intervalInput = document.createElement('input');
    intervalInput.type = 'number';
    intervalInput.min = 5;
    intervalInput.max = 3600;
    intervalInput.value = settings.refreshInterval / 1000; // in seconds
    intervalInput.style.cssText = `
        width: 100%;
        background: ${darkBackground};
        border: 1px solid ${neonColor};
        color: ${neonColor};
        font-family: 'Courier New', monospace;
        font-size: 10px;
        padding: 2px 4px;
        box-sizing: border-box;
        border-radius: 4px;
        outline-offset: 0;
        user-select: text;
    `;

    // Start/Pause button
    const toggleBtn = document.createElement('button');
    toggleBtn.textContent = settings.running ? 'Pause' : 'Start';
    toggleBtn.style.cssText = `
        width: 100%;
        background: transparent;
        border: 1px solid ${neonColor};
        color: ${neonColor};
        font-family: 'Courier New', monospace;
        font-size: 10px;
        padding: 4px 0;
        border-radius: 4px;
        cursor: pointer;
        user-select: none;
        margin-top: 6px;  /* button below input with spacing */
    `;

    // Append interval input and button so button is below input
    panel.appendChild(intervalInput);
    panel.appendChild(toggleBtn);

    document.body.appendChild(panel);

    // Format time helper
    function formatDateTime(dateStr) {
        if (!dateStr) return 'Never';
        const date = new Date(dateStr);
        return date.toLocaleString();
    }

    // State variables
    let countdownInterval = null;
    let remainingTime = settings.refreshInterval;
    let lastUpdateTime = Date.now();

    // Update UI info
    function updateUI() {
        refreshCountDisplay.textContent = `Refresh count: ${refreshCount}`;
        lastRefreshDisplay.textContent = `Last refresh: ${formatDateTime(settings.lastRefreshTime)}`;

        if (settings.running) {
            let mins = Math.floor(remainingTime / 60000);
            let secs = Math.floor((remainingTime % 60000) / 1000);
            countdownDisplay.textContent = `Refreshing in: ${mins}m ${secs}s`;
            toggleBtn.textContent = 'Pause';
        } else {
            countdownDisplay.textContent = 'Paused';
            toggleBtn.textContent = 'Start';
        }
    }

    function saveSettingsToStorage() {
        localStorage.setItem(storageKey, JSON.stringify(settings));
        sessionStorage.setItem('imvuRefreshCount', refreshCount.toString());
    }

    // Start countdown
    function startTimer() {
        if (countdownInterval) clearInterval(countdownInterval);

        settings.running = true;
        remainingTime = settings.refreshInterval;
        lastUpdateTime = Date.now();
        saveSettingsToStorage();
        updateUI();

        countdownInterval = setInterval(() => {
            const now = Date.now();
            const elapsed = now - lastUpdateTime;
            lastUpdateTime = now;
            remainingTime -= elapsed;

            if (remainingTime <= 0) {
                // Refresh page
                refreshCount++;
                settings.lastRefreshTime = new Date().toISOString();
                saveSettingsToStorage();
                updateUI();
                location.reload();
            } else {
                updateUI();
            }
        }, 1000);
    }

    // Pause countdown
    function pauseTimer() {
        settings.running = false;
        saveSettingsToStorage();
        if (countdownInterval) clearInterval(countdownInterval);
        updateUI();
    }

    // On toggle button click
    toggleBtn.onclick = () => {
        // Validate input interval
        const val = Number(intervalInput.value);
        if (isNaN(val) || val < 5 || val > 3600) {
            alert('Please enter a refresh interval between 5 and 3600 seconds.');
            intervalInput.value = settings.refreshInterval / 1000;
            return;
        }

        settings.refreshInterval = val * 1000;
        saveSettingsToStorage();

        if (settings.running) {
            pauseTimer();
        } else {
            startTimer();
        }
    };

    // On interval input change: save new interval, restart timer if running
    intervalInput.onchange = () => {
        const val = Number(intervalInput.value);
        if (isNaN(val) || val < 5 || val > 3600) {
            alert('Please enter a refresh interval between 5 and 3600 seconds.');
            intervalInput.value = settings.refreshInterval / 1000;
            return;
        }
        settings.refreshInterval = val * 1000;
        saveSettingsToStorage();

        if (settings.running) {
            startTimer(); // restart timer with new interval
        }
    };

    // Initialize UI and timer state
    updateUI();
    if (settings.running) startTimer();

})();
长期地址
遇到问题?请前往 GitHub 提 Issues。