// ==UserScript== // @name GreasyFork/SleazyFork Install Tracker // @name:de GreasyFork/SleazyFork Installations-Tracker // @name:fr GreasyFork/SleazyFork Install Tracker // @name:es GreasyFork/SleazyFork Rastreador de Instalaciones // @name:it GreasyFork/SleazyFork Tracker di Installazioni // @name:ru GreasyFork/SleazyFork Трекер установок // @name:zh-CN GreasyFork/SleazyFork 安装 Tracker // // @description Track your own Gf/Sf install counts and get desktop notifications via API. // @description:de Behalte deine eigenen Gf/Sf-Installationszahlen via API im Blick und erhalte Desktop-Benachrichtigungen. // @description:fr Suivez vos propres compteurs d'installation Gf/Sf via API et recevez des notifications de bureau. // @description:es Siga sus propios recuentos de instalación de Gf/Sf a través de la API und reciba notificaciones. // @description:it Tieni traccia dei tuoi conteggi di installazione di Gf/Sf tramite API e ricevi notifiche desktop. // @description:ru Отслеживайте собственные счетчики установок Gf/Sf через API und получайте уведомления. // @description:zh-CN 通过 API 跟踪您 eigenen Gf/Sf 安装计数 und 获取桌面通知. // // @version 0.1.3 beta // @author Wack.3gp (https://greasyfork.org/users/4792) // @copyright 2026+, Wack.3gp // @namespace https://greasyfork.org/users/4792 // @license CC BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/ // @icon https://greasyfork.org/vite/assets/blacklogo16-DftkYuVe.png // // @match https://greasyfork.org/*users/* // @match https://sleazyfork.org/*users/* // // @grant GM_setValue // @grant GM_getValue // @grant GM_notification // @grant GM_xmlhttpRequest // @connect api.greasyfork.org // @connect api.sleazyfork.org // // @supportURL https://greasyfork.org/scripts/575950/feedback // @compatible Chrome tested with Tampermonkey // @contributionURL https://www.paypal.com/donate?hosted_button_id=BYW9D395KJWZ2 // @contributionAmount €1.00 // @downloadURL https://update.greasyfork.icu/scripts/575950/GreasyForkSleazyFork%20Install%20Tracker.user.js // @updateURL https://update.greasyfork.icu/scripts/575950/GreasyForkSleazyFork%20Install%20Tracker.meta.js // ==/UserScript== /* jshint esversion: 11 */ (function() { 'use strict'; const isSleazy = window.location.hostname.includes('sleazyfork.org'); const contributionURL = "https://www.paypal.com/donate?hosted_button_id=BYW9D395KJWZ2"; const notificationIcon = isSleazy ? 'https://sleazyfork.org/vite/assets/blacklogo96-CxYTSM_T.png' : 'https://greasyfork.org/vite/assets/blacklogo96-CxYTSM_T.png'; const checkStats = async () => { const userLink = document.querySelector('#user-control-links .user-profile-link a, .user-profile-link a'); if (!userLink) { console.warn(`[Tracker] No logged-in user detected. Tracker is idle.`); return; } const myId = userLink.getAttribute('href').match(/\/users\/(\d+)/)?.[1]; if (!window.location.href.includes(`/users/${myId}`)) { console.info(`[Tracker] Current page is not the user's own profile. Skipping scan.`); return; } const scriptElements = Array.from(document.querySelectorAll('#user-script-list li[data-script-id], #user-unlisted-script-list li[data-script-id]')); if (scriptElements.length === 0) return; console.info(`[Tracker] Starting analysis for ${scriptElements.length} scripts...`); let totalNew = 0; let updateDetails = []; let processedCount = 0; for (const s of scriptElements) { const scriptId = s.getAttribute('data-script-id'); const scriptName = s.querySelector('.script-link')?.innerText.trim() || "Unknown"; const scriptUrl = s.querySelector('.script-link')?.href; if (isSleazy) { try { const response = await fetch(scriptUrl, { method: 'HEAD' }); if (response.ok) { fetchStats(scriptId, scriptName, "api.sleazyfork.org"); } else { fetchStats(scriptId, scriptName, "api.greasyfork.org"); } } catch (e) { fetchStats(scriptId, scriptName, "api.greasyfork.org"); } } else { fetchStats(scriptId, scriptName, "api.greasyfork.org"); } } function fetchStats(scriptId, scriptName, domain) { const apiUrl = `https://${domain}/scripts/${scriptId}/stats.json`; GM_xmlhttpRequest({ method: "GET", url: apiUrl, timeout: 10000, onload: function(response) { if (!isSleazy && response.status === 404 && domain === "api.greasyfork.org") { fetchStats(scriptId, scriptName, "api.sleazyfork.org"); return; } try { if (response.status !== 200) throw new Error(`HTTP ${response.status}`); const stats = JSON.parse(response.responseText); let currentTotal = 0; for (let date in stats) { if (stats[date] && typeof stats[date].installs === 'number') { currentTotal += stats[date].installs; } } const lastTotal = GM_getValue("api_sum_id_" + scriptId, -1); console.info(`[Tracker] ID: ${scriptId} | ${scriptName} | Source: ${domain} | Total: ${currentTotal}`); if (lastTotal !== -1 && currentTotal > lastTotal) { const diff = currentTotal - lastTotal; totalNew += diff; updateDetails.push(`${scriptName}: +${diff}`); } GM_setValue("api_sum_id_" + scriptId, currentTotal); } catch (e) { console.error(`[Tracker] Error for ID: ${scriptId} (${scriptName}): ${e.message}`); } finally { checkCompletion(); } }, onerror: () => { checkCompletion(); }, ontimeout: () => { checkCompletion(); } }); } function checkCompletion() { processedCount++; if (processedCount === scriptElements.length) { if (totalNew > 0) { console.info(`[Tracker] Scan complete. ${totalNew} new installs found.`); showNotification(totalNew, updateDetails); } else { console.info(`[Tracker] Scan complete. No new installs found.`); } } } }; const showNotification = (total, details) => { const fullText = details.join('\n'); GM_notification({ title: `🚀 ${total} New Installs Detected`, text: fullText, image: notificationIcon, silent: true, onclick: () => { showCentralModal(total, fullText); } }); }; const showCentralModal = (total, detailsText) => { const oldOverlay = document.querySelector('#tracker-modal-overlay'); if (oldOverlay) oldOverlay.remove(); const overlay = document.createElement('div'); overlay.id = 'tracker-modal-overlay'; overlay.style = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.75); display: flex; align-items: center; justify-content: center; z-index: 999999; font-family: sans-serif; backdrop-filter: blur(4px);'; overlay.innerHTML = `

Installation Update

You have ${total} new installs!

${detailsText}

Support my work with a coffee? ☕

☕ Buy me a coffee
`; document.body.appendChild(overlay); const close = () => overlay.remove(); overlay.querySelector('#tracker-x-btn').onclick = close; overlay.querySelector('#tracker-ok-btn').onclick = close; overlay.querySelector('#tracker-coffee-btn').onclick = close; overlay.onclick = (e) => { if (e.target === overlay) close(); }; }; setTimeout(checkStats, 2000); })();