Speed-Up Timers

Patch setInterval/setTimeout to speed up client-side countdowns so the "wait" completes faster.

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Speed-Up Timers
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Patch setInterval/setTimeout to speed up client-side countdowns so the "wait" completes faster.
// @match        https://discovery.to-travel.net/*
// @match        https://*.to-travel.net/*
// @match        https://gold-24.net/*
// @match        https://*.gold-24.net/*
// @match        https://yjiur.xyz/*
// @match        https://*.yjiur.xyz/*
// @match        https://serverguidez.com/*
// @match        https://*.serverguidez.com/*
// @match        https://uploadhaven.com/download/*
// @match        https://transfaze.com/*
// @match        https://cheaplann.com/*
// @match        https://info.quizrent.com/*
// @match        https://fileknot.io/*
// @match        https://ADD_YOUR_SITE_HERE.example/*
// @grant        none
// @icon         https://raw.githubusercontent.com/Accroon/userscript-assets/main/download.png
// @run-at       document-start
// @license      MIT
// ==/UserScript==


(function () {
    'use strict';
    const LOG = '[TimerSpeedup]';
    const SPEED_FACTOR = 16;    // how much faster timers should run (8 => 20s ≈ 2.5s). Increase to go faster.
    const TARGET_SECONDS = 3;  // visible seconds you expect to see (used for logs/stop condition)
    const CHECK_INTERVAL = 200; // ms - how often we check for the countdown / Next button
    const MAX_RUNTIME_MS = 250 * 1000; // safety cutoff

    // Save originals
    const realSetTimeout = window.setTimeout.bind(window);
    const realSetInterval = window.setInterval.bind(window);
    const realRequestAnimationFrame = window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : null;
    const realPerformanceNow = performance && performance.now ? performance.now.bind(performance) : null;

    let patched = false;
    let checkerId = null;
    let stopTimer = null;

    // Simple helpers to detect countdown and next button
    function findCountdownNode() {
        try {
            const xpath = "//*[contains(translate(text(),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'wait') or contains(translate(text(),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'seconds') or contains(translate(text(),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'loading link')]";
            const walker = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
            for (let i = 0; i < walker.snapshotLength; i++) {
                const el = walker.snapshotItem(i);
                if (!el) continue;
                const txt = (el.innerText || el.textContent || '').trim();
                if (/\d+\s*seconds?/i.test(txt) || /\bplease wait\b/i.test(txt) || /loading link/i.test(txt)) return el;
            }
        } catch (e) { /* ignore */ }
        return null;
    }
    function parseSeconds(text) {
        if (!text) return null;
        const m = text.match(/(\d+)\s*seconds?/i);
        return m ? parseInt(m[1], 10) : null;
    }
    function findNextButton() {
        try {
            const cands = Array.from(document.querySelectorAll('button, a, input[type="button"], input[type="submit"]'));
            for (const c of cands) {
                const txt = (c.innerText || c.value || '').trim();
                if (/^next$/i.test(txt) || /next|continue|get link|proceed|continue to/i.test(txt)) return c;
            }
            return document.querySelector('#next, .next, .btn-next, [aria-label="Next"], [data-role="next"], a[href*="next"]');
        } catch (e) { return null; }
    }

    // Apply patches
    function applyPatches() {
        if (patched) return;
        patched = true;

        window.setTimeout = function (fn, delay, ...args) {
            try {
                // some calls do not pass numeric delay
                const d = typeof delay === 'number' && delay > 0 ? Math.max(0, Math.floor(delay / SPEED_FACTOR)) : delay;
                return realSetTimeout(fn, d, ...args);
            } catch (e) { return realSetTimeout(fn, delay, ...args); }
        };

        window.setInterval = function (fn, delay, ...args) {
            try {
                const d = typeof delay === 'number' && delay > 0 ? Math.max(1, Math.floor(delay / SPEED_FACTOR)) : delay;
                return realSetInterval(fn, d, ...args);
            } catch (e) { return realSetInterval(fn, delay, ...args); }
        };

        if (realRequestAnimationFrame) {
            window.requestAnimationFrame = function (cb) {
                // call rAF, keeping default behavior (we don't accelerate rAF here)
                return realRequestAnimationFrame(function (t) {
                    try { cb(t); } catch (e) { }
                });
            };
        }

        console.log(LOG, 'patched setTimeout/setInterval (SPEED_FACTOR=' + SPEED_FACTOR + ')');
    }

    // Restore originals
    function restoreOriginals() {
        if (!patched) return;
        try {
            window.setTimeout = realSetTimeout;
            window.setInterval = realSetInterval;
            if (realRequestAnimationFrame) window.requestAnimationFrame = realRequestAnimationFrame;
            patched = false;
            console.log(LOG, 'restored timer originals');
        } catch (e) {
            console.warn(LOG, 'restore error', e);
        }
    }

    // Watcher - look for countdown and Next button; stop when Next enabled or timeout
    function startWatcher() {
        const startedAt = realPerformanceNow ? realPerformanceNow() : Date.now();
        let appliedLog = false;

        checkerId = realSetInterval(function () {
            try {
                // safety cutoff
                const nowDelta = realPerformanceNow ? (realPerformanceNow() - startedAt) : (Date.now() - (startedAt || 0));
                if (nowDelta > MAX_RUNTIME_MS) {
                    console.log(LOG, 'max runtime reached, restoring originals');
                    stopAll();
                    return;
                }

                const node = findCountdownNode();
                if (node) {
                    const text = node.innerText || node.textContent || '';
                    const sec = parseSeconds(text);
                    if (sec !== null && !appliedLog) {
                        console.log(LOG, 'detected countdown =', sec + 's; speeding timers by', SPEED_FACTOR + 'x');
                        appliedLog = true;
                    }
                }

                const next = findNextButton();
                if (next) {
                    const rect = next.getBoundingClientRect ? next.getBoundingClientRect() : null;
                    const visible = rect && rect.width > 1 && rect.height > 1;
                    const disabled = next.disabled || (next.getAttribute && next.getAttribute('disabled') !== null);
                    if (visible && !disabled) {
                        console.log(LOG, 'Next visible & enabled — restoring and stopping.');
                        // optionally auto-click:
                        // try { next.click(); console.log(LOG, 'auto-clicked Next'); } catch(e){}
                        stopAll();
                    }
                }
            } catch (e) {
                console.debug(LOG, 'checker error', e);
            }
        }, CHECK_INTERVAL);
    }

    function stopAll() {
        try { if (checkerId) clearInterval(checkerId); } catch (e) {}
        restoreOriginals();
        if (stopTimer) { clearTimeout(stopTimer); stopTimer = null; }
        if (checkerId) { clearInterval(checkerId); checkerId = null; }
    }

    // Kickoff: patch ASAP and start watcher. If script is injected late, still patch and try.
    try {
        applyPatches();
        startWatcher();
        stopTimer = realSetTimeout(() => {
            console.log(LOG, 'safety timeout reached — restoring originals');
            stopAll();
        }, MAX_RUNTIME_MS + 2000);
    } catch (e) {
        console.error(LOG, 'startup error', e);
        restoreOriginals();
    }

    // restore on page unload
    window.addEventListener('beforeunload', function () {
        stopAll();
    });

})();