您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Display today's script installations and update checks.
当前为
// ==UserScript== // @name Greasyfork/Sleazyfork Update Checks Display // @description Display today's script installations and update checks. // @icon https://greasyfork.org/vite/assets/blacklogo96-CxYTSM_T.png // @version 1.0 // @author afkarxyz // @namespace https://github.com/afkarxyz/misc-scripts/ // @supportURL https://github.com/afkarxyz/misc-scripts/issues // @license MIT // @match https://greasyfork.org/*/users/* // @match https://sleazyfork.org/*/users/* // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; const CACHE_DURATION = 10 * 60 * 1000; const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const collectScriptLinks = () => Array.from(document.querySelectorAll('li[data-script-id]')).map(element => ({ url: `https://greasyfork.org/en/scripts/${element.getAttribute('data-script-id')}-${ element.getAttribute('data-script-name') .toLowerCase() .replace(/\s+/g, '-') .replace(/[^a-z0-9-]/g, '') }/stats`, name: element.getAttribute('data-script-name'), id: element.getAttribute('data-script-id'), element: element })); const fetchWithProxy = async (url) => { try { const response = await fetch(`https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`); return response.ok ? await response.text() : null; } catch (error) { console.error(`Error fetching ${url}:`, error); return null; } }; const parseStatsFromHTML = (html) => { if (!html) return null; const doc = new DOMParser().parseFromString(html, 'text/html'); const rows = doc.querySelectorAll('.stats-table tbody tr'); if (!rows.length) return null; const cells = rows[rows.length - 1].querySelectorAll('td.numeric'); return { installs: parseInt(cells[0]?.textContent?.trim()) || 0, updateChecks: parseInt(cells[1]?.textContent?.trim()) || 0 }; }; const insertOrUpdateStats = (element, label, value, className) => { const metadataList = element.querySelector('.inline-script-stats'); if (!metadataList) return; const lastRow = metadataList.lastElementChild; if (!lastRow) return; let termElement = metadataList.querySelector(`dt.${className}`); let descElement = metadataList.querySelector(`dd.${className}`); if (!termElement) { termElement = document.createElement('dt'); termElement.className = className; lastRow.parentNode.insertBefore(termElement, lastRow.nextSibling); } if (!descElement) { descElement = document.createElement('dd'); descElement.className = className; termElement.after(descElement); } termElement.textContent = label; descElement.textContent = value; }; const initializeStatsLabels = (scripts) => { scripts.forEach(scriptInfo => { insertOrUpdateStats( scriptInfo.element, 'Installs', '⌛ Checking...', 'script-list-installs' ); insertOrUpdateStats( scriptInfo.element, 'Checks', '⌛ Checking...', 'script-list-update-checks' ); }); }; const getCachedStats = (scriptId) => { const cacheKey = `script_stats_${scriptId}`; const cachedData = GM_getValue(cacheKey); if (cachedData) { const { timestamp, stats } = JSON.parse(cachedData); const now = Date.now(); if (now - timestamp < CACHE_DURATION) { return stats; } } return null; }; const setCachedStats = (scriptId, stats) => { const cacheKey = `script_stats_${scriptId}`; const cacheEntry = JSON.stringify({ timestamp: Date.now(), stats: stats }); GM_setValue(cacheKey, cacheEntry); }; const processSingleScript = async (scriptInfo) => { console.log(`Processing: ${scriptInfo.name}`); const cachedStats = getCachedStats(scriptInfo.id); if (cachedStats) { insertOrUpdateStats( scriptInfo.element, 'Installs', cachedStats.installs.toLocaleString(), 'script-list-installs' ); insertOrUpdateStats( scriptInfo.element, 'Checks', cachedStats.updateChecks.toLocaleString(), 'script-list-update-checks' ); return { ...scriptInfo, ...cachedStats, cached: true }; } const html = await fetchWithProxy(scriptInfo.url); const stats = parseStatsFromHTML(html); const result = { ...scriptInfo, ...(stats || { installs: 0, updateChecks: 0 }), error: !stats ? 'Failed to fetch stats' : null }; if (!result.error) { setCachedStats(scriptInfo.id, { installs: result.installs, updateChecks: result.updateChecks }); insertOrUpdateStats( scriptInfo.element, 'Installs', result.installs.toLocaleString(), 'script-list-installs' ); insertOrUpdateStats( scriptInfo.element, 'Checks', result.updateChecks.toLocaleString(), 'script-list-update-checks' ); } else { insertOrUpdateStats( scriptInfo.element, 'Installs', 'Failed to load', 'script-list-installs' ); insertOrUpdateStats( scriptInfo.element, 'Checks', 'Failed to load', 'script-list-update-checks' ); } return result; }; const initScriptStats = async () => { try { const scripts = collectScriptLinks(); console.log(`Found ${scripts.length} scripts to process`); initializeStatsLabels(scripts); const results = []; for (const scriptInfo of scripts) { const result = await processSingleScript(scriptInfo); results.push(result); console.log('Result:', result); await sleep(result.cached ? 100 : 1000); } const totals = results.reduce((acc, curr) => ({ totalInstalls: acc.totalInstalls + (curr.error ? 0 : curr.installs), totalUpdateChecks: acc.totalUpdateChecks + (curr.error ? 0 : curr.updateChecks), successCount: acc.successCount + (curr.error ? 0 : 1), errorCount: acc.errorCount + (curr.error ? 1 : 0), cachedCount: acc.cachedCount + (curr.cached ? 1 : 0) }), { totalInstalls: 0, totalUpdateChecks: 0, successCount: 0, errorCount: 0, cachedCount: 0 }); console.log('\nAll results:', results); console.log('\nTotals:', totals); return { results, totals }; } catch (error) { console.error('Error in initScriptStats:', error); } }; const style = document.createElement('style'); style.textContent = ` .script-list-installs, .script-list-update-checks { opacity: 0.7; font-style: italic; } .script-list-installs:not(:empty), .script-list-update-checks:not(:empty) { opacity: 1; font-style: normal; } `; document.head.appendChild(style); document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', initScriptStats) : initScriptStats(); })();