您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Refreshes IMVU Pended Credits Page to update sales, with UI controls and darkmode neon green styling
// ==UserScript== // @name IMVU Auto Refresh Pended Credits Page // @namespace http://tampermonkey.net/ // @version 1.2 // @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'; let settings = JSON.parse(localStorage.getItem(storageKey)) || { refreshInterval: 300000, width: null, height: null, top: 10, left: 10, running: false, lastRefreshTime: null }; let refreshCount = Number(sessionStorage.getItem('imvuRefreshCount')) || 0; 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; `; if (settings.width && settings.height) { panel.style.width = settings.width + 'px'; panel.style.height = settings.height + 'px'; } 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); 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); (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); const refreshCountDisplay = document.createElement('div'); panel.appendChild(refreshCountDisplay); const lastRefreshDisplay = document.createElement('div'); panel.appendChild(lastRefreshDisplay); const countdownDisplay = document.createElement('div'); panel.appendChild(countdownDisplay); const intervalInput = document.createElement('input'); intervalInput.type = 'number'; intervalInput.min = 5; intervalInput.max = 3600; intervalInput.value = settings.refreshInterval / 1000; 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; `; 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; `; panel.appendChild(intervalInput); panel.appendChild(toggleBtn); document.body.appendChild(panel); function formatDateTime(dateStr) { if (!dateStr) return 'Never'; const date = new Date(dateStr); return date.toLocaleString(); } let countdownInterval = null; let remainingTime = settings.refreshInterval; let lastUpdateTime = Date.now(); 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()); } 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) { refreshCount++; // 🟢 Clear session count every 50 refreshes to free memory if (refreshCount >= 100) { refreshCount = 0; sessionStorage.removeItem('imvuRefreshCount'); } settings.lastRefreshTime = new Date().toISOString(); saveSettingsToStorage(); updateUI(); location.reload(); } else { updateUI(); } }, 1000); } function pauseTimer() { settings.running = false; saveSettingsToStorage(); if (countdownInterval) clearInterval(countdownInterval); updateUI(); } toggleBtn.onclick = () => { 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(); } }; 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(); } }; updateUI(); if (settings.running) startTimer(); })();