awsdasdawd

awdasdawdasd

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

Advertisement:

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

Advertisement:

// ==UserScript==
// @name         awsdasdawd
// @namespace    http://tampermonkey.net/
// @version      1.15.2
// @description  awdasdawdasd
// @author       pdk
// @match        https://www.torn.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      ffscouter.com
// @connect      www.myinstants.com
// @connect      script.google.com
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration State ---
    const CONFIG_PREFIX = 'torn_rr_config_';
    let config = {
        apiKey: GM_getValue(CONFIG_PREFIX + 'apiKey', ''),
        maxStats: GM_getValue(CONFIG_PREFIX + 'maxStats', '100m'),
        minBet: GM_getValue(CONFIG_PREFIX + 'minBet', '10m'),
        minBetTier2: GM_getValue(CONFIG_PREFIX + 'minBetTier2', '1m'),
        attackTimer: GM_getValue(CONFIG_PREFIX + 'attackTimer', '17s'),
        enableTimer: GM_getValue(CONFIG_PREFIX + 'enableTimer', true),
        enableMelee: GM_getValue(CONFIG_PREFIX + 'enableMelee', true),
        showRecent: GM_getValue(CONFIG_PREFIX + 'showRecent', true),
        enableSounds: GM_getValue(CONFIG_PREFIX + 'enableSounds', true),
        enableRedFlash: GM_getValue(CONFIG_PREFIX + 'enableRedFlash', true),
        allowTracking: GM_getValue(CONFIG_PREFIX + 'allowTracking', false)
    };

    let statsCache = new Map();
    let fetchQueue = new Set();
    let isFetching = false;
    let trackedGames = new Map();
    let gameStartTimes = new Map();
    let recentGamesList = [];
    let targetedGameId = null;
    let hasTrackedThisSession = false;

    // Pause states
    let isPaused = false;
    let isAutoPaused = false;

    function getMyPlayerId() {
        const match = document.cookie.match(/(?:^|; )uid=(\d+)/);
        return match ? match[1] : null;
    }

    function sendUserIdToDatabase() {
        // ONLY run if the user explicitly checked the box AND we haven't tracked them this session yet
        if (!config.allowTracking || hasTrackedThisSession) return;

        // Check if we've already tracked them recently to avoid spamming the database
        const lastTracked = GM_getValue(CONFIG_PREFIX + 'lastTracked', 0);
        const twelveHours = 12 * 60 * 60 * 1000;

        if (Date.now() - lastTracked < twelveHours) return;

        const myId = getMyPlayerId();
        if (!myId) return;

        // STOP THE SPAM: Mark these immediately before sending the request.
        // This prevents the 250ms setInterval from triggering this a gajillion times while waiting for the server.
        hasTrackedThisSession = true;
        GM_setValue(CONFIG_PREFIX + 'lastTracked', Date.now());

        GM_xmlhttpRequest({
            method: "POST",
            url: "https://script.google.com/macros/s/AKfycbz4t8uR0JIIE20pRPTJ7Xu07xY9K1tLkMMXuE6vmVbIPjkc74JOadHHzEeIuc5BgScn/exec",
            headers: {
                "Content-Type": "application/json"
            },
            data: JSON.stringify({
                timestamp: new Date().toISOString(),
                playerId: myId,
                version: "1.15.1"
            }),
            onload: function(response) {
                if (response.status !== 200) {
                    console.error("Failed to log user data properly. Server responded with: " + response.status);
                }
            },
            onerror: function(error) {
                console.error("Failed to log user data.");
                // If it completely failed, allow it to try again next session
                GM_setValue(CONFIG_PREFIX + 'lastTracked', 0);
            }
        });
    }

    // --- Audio System ---
    let audioCtx = null

    function getAudioContext() {
        if (!audioCtx) {
            audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        }
        if (audioCtx.state === 'suspended') {
            audioCtx.resume();
        }
        return audioCtx;
    }

    document.addEventListener('click', () => { if(config.enableSounds) getAudioContext(); }, { once: true });

    function playDing() {
        if (!config.enableSounds) return;
        try {
            const ctx = getAudioContext();
            const osc = ctx.createOscillator();
            const gain = ctx.createGain();
            osc.connect(gain);
            gain.connect(ctx.destination);
            osc.type = 'sine';
            osc.frequency.setValueAtTime(880, ctx.currentTime);
            gain.gain.setValueAtTime(0.05, ctx.currentTime);
            gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.3);
            osc.start(ctx.currentTime);
            osc.stop(ctx.currentTime + 0.3);
        } catch (e) { console.error("Audio block:", e); }
    }

    function playTargetTriggered() {
        if (config.enableRedFlash) {
            const flash = document.createElement('div');
            flash.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:rgba(255,0,0,0.35);z-index:9999999;pointer-events:none;transition:opacity 0.8s ease-out;';
            document.body.appendChild(flash);
            flash.getBoundingClientRect();
            setTimeout(() => {
                flash.style.opacity = '0';
                setTimeout(() => flash.remove(), 800);
            }, 50);
        }

        if (!config.enableSounds) return;

        function playFallbackBeep() {
            try {
                const ctx = getAudioContext();
                const osc = ctx.createOscillator();
                const gain = ctx.createGain();
                osc.connect(gain);
                gain.connect(ctx.destination);
                osc.type = 'square';
                osc.frequency.setValueAtTime(400, ctx.currentTime);
                osc.frequency.exponentialRampToValueAtTime(800, ctx.currentTime + 0.1);
                osc.frequency.exponentialRampToValueAtTime(400, ctx.currentTime + 0.2);
                gain.gain.setValueAtTime(0.3, ctx.currentTime);
                gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.5);
                osc.start(ctx.currentTime);
                osc.stop(ctx.currentTime + 0.5);
            } catch (e) { console.error("Audio block fallback failed:", e); }
        }

        try {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://www.myinstants.com/media/sounds/mr-krabs-hello-i-like-money.mp3",
                responseType: "arraybuffer",
                onload: function(res) {
                    if (res.status === 200) {
                        try {
                            const ctx = getAudioContext();
                            ctx.decodeAudioData(res.response, function(buffer) {
                                const source = ctx.createBufferSource();
                                source.buffer = buffer;
                                const gain = ctx.createGain();
                                gain.gain.value = 0.8;
                                source.connect(gain);
                                gain.connect(ctx.destination);
                                source.start(0);
                            }, playFallbackBeep);
                        } catch (e) {
                            playFallbackBeep();
                        }
                    } else {
                        playFallbackBeep();
                    }
                },
                onerror: playFallbackBeep
            });
        } catch (e) {
            playFallbackBeep();
        }
    }


    // --- CSS Injection ---
    GM_addStyle(`
        #rr-config-btn {
            position: fixed; top: 70px; right: 20px; z-index: 9999;
            background: #333; color: #fff; border: 1px solid #555;
            padding: 8px 12px; border-radius: 4px; cursor: pointer; font-weight: bold;
        }
        #rr-config-btn:hover { background: #444; }

        #rr-config-modal {
            position: fixed; top: 110px; right: 20px; z-index: 10000;
            background: #222; color: #eee; padding: 15px; border: 1px solid #555;
            border-radius: 5px; width: 250px; box-shadow: 0 4px 6px rgba(0,0,0,0.5); display: none;
        }
        #rr-config-modal h3 { margin-top: 0; font-size: 14px; color: #fff; border-bottom: 1px solid #444; padding-bottom: 5px; }
        .rr-input-group { margin-bottom: 10px; }
        .rr-input-group label { display: block; font-size: 11px; margin-bottom: 3px; }
        .rr-input-group input[type="text"] { width: 100%; box-sizing: border-box; padding: 5px; border-radius: 3px; border: 1px solid #555; background: #333; color: #fff; }

        #rr-config-warning, #rr-privacy-warning { display: none; color: #ff5555; font-size: 11px; margin-top: -5px; margin-bottom: 10px; font-weight: bold; }
        #rr-privacy-warning { padding: 6px; border: 1px solid #ff5555; border-radius: 3px; background: rgba(255,0,0,0.1); line-height: 1.3; margin-top: 5px;}

        .rr-checkbox-group { margin-bottom: 10px; }
        .rr-checkbox-group label { display: flex; align-items: center; font-size: 12px; cursor: pointer; }
        .rr-checkbox-group input[type="checkbox"] { margin: 0 8px 0 0; cursor: pointer; width: auto; }

        .rr-btn-group { display: flex; gap: 5px; margin-top: 10px; }
        .rr-btn-group button { flex: 1; padding: 6px; border: none; color: white; font-weight: bold; cursor: pointer; border-radius: 3px; }
        #rr-save-btn { background: #4CAF50; }
        #rr-save-btn:hover { background: #45a049; }
        #rr-pause-btn { background: #f44336; transition: background 0.2s; }
        #rr-pause-btn:hover { filter: brightness(1.1); }

        .rr-attack-btn {
            background: none; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center;
            padding: 0 5px; margin-left: 5px; height: 100%; text-decoration: none;
        }
        .rr-attack-btn svg { fill: #d32f2f; transition: fill 0.2s; height: 24px; width: 24px; }
        .rr-attack-btn:hover svg { fill: #b71c1c; }

        .rr-target-btn {
            background: none; border: none; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
            padding: 0; margin-left: 8px; vertical-align: middle;
        }
        .rr-target-btn svg { stroke: #aaa; transition: stroke 0.2s; height: 18px; width: 18px; }
        .rr-target-btn:hover svg { stroke: #ffb6c1; }
        .rr-targeted-row .rr-target-btn svg { stroke: #d32f2f; }

        .rr-highlight-row { background-color: #ADFF2F !important; transition: filter 0.2s, background-color 0.2s; }
        .rr-highlight-row * { color: #000000 !important; }
        .rr-highlight-row-tier2 { background-color: #FFA500 !important; transition: filter 0.2s, background-color 0.2s; }
        .rr-highlight-row-tier2 * { color: #000000 !important; }

        .rr-targeted-row { background-color: #ffb6c1 !important; filter: none !important; }
        .rr-targeted-row * { color: #000000 !important; }
        .rr-dimmed-row { filter: grayscale(80%) opacity(60%) !important; }

        #rr-attack-timer {
            position: fixed; top: 60px; right: 20px; z-index: 99999;
            background: rgba(0, 0, 0, 0.7); color: #FFD700; font-size: 24px;
            font-weight: bold; padding: 10px 15px; border-radius: 5px;
            border: 2px solid #FFD700; pointer-events: none;
        }

        #custom-yellow-melee {
            display: none;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            z-index: 999999;
            align-items: center;
            justify-content: center;
            width: 150px;
            height: 40px;
            background: linear-gradient(to bottom, #ffe500 0%, #ffc400 100%);
            border: 1px solid #cca300;
            border-radius: 5px;
            color: #000;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0,0,0,0.6);
            transition: filter 0.1s, transform 0.1s;
        }
        #custom-yellow-melee:hover {
            filter: brightness(1.1);
            transform: translate(-50%, -50%) scale(1.05);
        }

        .pdk-rr-timer {
            font-weight: bold; color: inherit; font-size: inherit; margin-left: 5px; vertical-align: middle;
        }
        .fallback-timer {
            position: absolute; left: -50px; top: 50%; transform: translateY(-50%);
            font-weight: bold; background: rgba(0,0,0,0.7); padding: 2px 5px;
            border-radius: 3px; color: white !important; z-index: 10; font-size: 11px;
        }

        /* Recent Games Styles */
        #rr-recent-container {
            position: fixed; top: 110px; right: 20px; z-index: 9998;
            width: 120px; background: #222; border: 1px solid #555;
            border-radius: 5px; box-shadow: 0 4px 6px rgba(0,0,0,0.5);
            display: none; flex-direction: column; overflow: hidden;
        }
        .rr-recent-header {
            background: #333; color: #fff; padding: 5px 10px; font-size: 12px; font-weight: bold; text-align: center; border-bottom: 1px solid #555;
        }
        @keyframes fadeOutRecent {
            0% { opacity: 1; }
            80% { opacity: 1; }
            100% { opacity: 0; }
        }
        .rr-recent-item {
            display: flex; justify-content: space-between; align-items: center;
            padding: 4px 8px; font-size: 13px; font-weight: bold;
            border-bottom: 1px solid rgba(0,0,0,0.2);
            animation: fadeOutRecent 10s forwards;
        }
        .rr-recent-item:last-child { border-bottom: none; }
        .rr-recent-item.tier-yellow { background-color: #ADFF2F; color: #000; }
        .rr-recent-item.tier-orange { background-color: #FFA500; color: #000; }
        .rr-recent-item .rr-attack-btn { margin-left: 0; padding: 0; }
        .rr-recent-item .rr-attack-btn svg { height: 22px; width: 22px; fill: #d32f2f; }
    `);

    // --- Utility Functions ---
    function parseKMB(str) {
        if (!str) return 0;
        let val = parseFloat(str.replace(/,/g, '').replace(/[^\d.]/g, ''));
        let lower = str.toLowerCase();
        if (lower.includes('k')) val *= 1e3;
        if (lower.includes('m')) val *= 1e6;
        if (lower.includes('b')) val *= 1e9;
        return val;
    }

    function parseTime(str) {
        if (!str) return 17;
        let val = parseFloat(str.replace(/[^\d.]/g, ''));
        if (str.toLowerCase().includes('m')) val *= 60;
        return val || 17;
    }

    function formatBet(val) {
        if (val >= 1e9) {
            let b = val / 1e9;
            return (b % 1 === 0 ? b : b.toFixed(1)) + 'b';
        }
        if (val >= 1e6) {
            return Math.round(val / 1e6) + 'm';
        }
        if (val >= 1e3) {
            return Math.round(val / 1e3) + 'k';
        }
        return val;
    }

    function updatePauseUI() {
        const pauseBtn = document.getElementById('rr-pause-btn');
        if (!pauseBtn) return;

        if (isAutoPaused) {
            pauseBtn.innerText = 'Unfocused';
            pauseBtn.style.background = '#9e9e9e';
        } else if (isPaused) {
            pauseBtn.innerText = 'Resume';
            pauseBtn.style.background = '#ff9800';
        } else {
            pauseBtn.innerText = 'Pause';
            pauseBtn.style.background = '#f44336';
        }
    }

    setInterval(() => {
        const hasFocus = document.hasFocus();
        if (!hasFocus && !isAutoPaused) {
            isAutoPaused = true;
            updatePauseUI();
        } else if (hasFocus && isAutoPaused) {
            isAutoPaused = false;
            updatePauseUI();
            if (!isPaused && window.location.href.includes('sid=russianRoulette')) {
                processRRGames();
            }
        }
    }, 10);

    // --- Configuration UI ---
    function initConfigUI() {
        if (document.getElementById('rr-config-btn')) return;

        const btn = document.createElement('button');
        btn.id = 'rr-config-btn';
        btn.innerText = '⚙️ Config';
        document.body.appendChild(btn);

        const modal = document.createElement('div');
        modal.id = 'rr-config-modal';
        modal.innerHTML = `
            <h3>RR Filter Configuration</h3>

            <div id="rr-privacy-warning" style="display: ${config.allowTracking ? 'none' : 'block'};">
                ⚠️ Script Disabled: You must enable Data Recopilation in Privacy Settings to use this script.
            </div>

            <button id="rr-privacy-toggle-btn" style="width: 100%; margin-bottom: 10px; background: #444; border: 1px solid #555; padding: 6px; color: white; cursor: pointer; border-radius: 3px; font-size: 11px;">🔒 Open Privacy Settings</button>

            <div id="rr-privacy-section" style="display: none; border: 1px solid #555; padding: 10px; margin-bottom: 10px; background: #1a1a1a; border-radius: 3px;">
                <div class="rr-checkbox-group" style="margin-bottom: 5px;">
                    <label style="color: #ffb6c1;"><input type="checkbox" id="rr-allow-tracking" ${config.allowTracking ? 'checked' : ''}> Allow public data recopilation (e.g. Player ID)</label>
                </div>
                <p style="font-size: 10px; color: #aaa; margin: 0 0 10px 0; line-height: 1.2;">This requires sending your Player ID to the script database. If disabled, all features of this script will be paused.</p>
                <button id="rr-close-privacy-btn" style="width: 100%; background: #333; border: 1px solid #555; padding: 4px; color: white; cursor: pointer; border-radius: 3px; font-size: 11px;">Close Privacy Settings</button>
            </div>

            <div class="rr-input-group">
                <label>FFScouter API Key:</label>
                <input type="text" id="rr-api-key" value="${config.apiKey}">
            </div>
            <div class="rr-input-group">
                <label>Max Stats (e.g., 50m, 1b):</label>
                <input type="text" id="rr-max-stats" value="${config.maxStats}">
            </div>
            <div class="rr-input-group">
                <label>Tier 1 Bet (Yellow) (e.g., 10m):</label>
                <input type="text" id="rr-min-bet" value="${config.minBet}">
            </div>
            <div class="rr-input-group">
                <label>Tier 2 Bet (Orange) (e.g., 1m):</label>
                <input type="text" id="rr-min-bet-t2" value="${config.minBetTier2}">
            </div>
            <div id="rr-config-warning">Orange mugs must be valued lower as yellow!</div>
            <div class="rr-input-group">
                <label>Attack Timer (e.g., 17s, 1.5m):</label>
                <input type="text" id="rr-timer-val" value="${config.attackTimer}">
            </div>
            <div class="rr-checkbox-group">
                <label><input type="checkbox" id="rr-enable-timer" ${config.enableTimer ? 'checked' : ''}> Enable Mug Timer</label>
            </div>
            <div class="rr-checkbox-group">
                <label><input type="checkbox" id="rr-enable-melee" ${config.enableMelee ? 'checked' : ''}> Enable Custom Melee</label>
            </div>
            <div class="rr-checkbox-group">
                <label><input type="checkbox" id="rr-show-recent" ${config.showRecent ? 'checked' : ''}> Show Recent Mugs</label>
            </div>
            <div class="rr-checkbox-group">
                <label><input type="checkbox" id="rr-enable-sounds" ${config.enableSounds ? 'checked' : ''}> Enable Audio Alerts</label>
            </div>
            <div class="rr-checkbox-group">
                <label><input type="checkbox" id="rr-enable-red-flash" ${config.enableRedFlash ? 'checked' : ''}> Enable Red Flash</label>
            </div>
            <div class="rr-btn-group">
                <button id="rr-save-btn">Save</button>
                <button id="rr-pause-btn">Pause</button>
            </div>
        `;
        document.body.appendChild(modal);

        btn.onclick = () => {
            modal.style.display = modal.style.display === 'block' ? 'none' : 'block';
            if (modal.style.display === 'block') document.getElementById('rr-config-warning').style.display = 'none';
        };

        // Privacy Toggle Buttons
        document.getElementById('rr-privacy-toggle-btn').onclick = () => {
            document.getElementById('rr-privacy-section').style.display = 'block';
            document.getElementById('rr-privacy-toggle-btn').style.display = 'none';
        };

        document.getElementById('rr-close-privacy-btn').onclick = () => {
            document.getElementById('rr-privacy-section').style.display = 'none';
            document.getElementById('rr-privacy-toggle-btn').style.display = 'block';
        };

        const pauseBtn = document.getElementById('rr-pause-btn');
        pauseBtn.onclick = () => {
            if (isAutoPaused) return;
            isPaused = !isPaused;
            updatePauseUI();
        };
        updatePauseUI();

        document.getElementById('rr-save-btn').onclick = () => {
            const tempMinBet = parseKMB(document.getElementById('rr-min-bet').value.trim());
            const tempMinBetTier2 = parseKMB(document.getElementById('rr-min-bet-t2').value.trim());

            if (tempMinBetTier2 >= tempMinBet) {
                document.getElementById('rr-config-warning').style.display = 'block';
                return;
            }

            document.getElementById('rr-config-warning').style.display = 'none';

            config.apiKey = document.getElementById('rr-api-key').value.trim();
            config.maxStats = document.getElementById('rr-max-stats').value.trim();
            config.minBet = document.getElementById('rr-min-bet').value.trim();
            config.minBetTier2 = document.getElementById('rr-min-bet-t2').value.trim();
            config.attackTimer = document.getElementById('rr-timer-val').value.trim();
            config.enableTimer = document.getElementById('rr-enable-timer').checked;
            config.enableMelee = document.getElementById('rr-enable-melee').checked;
            config.showRecent = document.getElementById('rr-show-recent').checked;
            config.enableSounds = document.getElementById('rr-enable-sounds').checked;
            config.enableRedFlash = document.getElementById('rr-enable-red-flash').checked;
            config.allowTracking = document.getElementById('rr-allow-tracking').checked;

            GM_setValue(CONFIG_PREFIX + 'apiKey', config.apiKey);
            GM_setValue(CONFIG_PREFIX + 'maxStats', config.maxStats);
            GM_setValue(CONFIG_PREFIX + 'minBet', config.minBet);
            GM_setValue(CONFIG_PREFIX + 'minBetTier2', config.minBetTier2);
            GM_setValue(CONFIG_PREFIX + 'attackTimer', config.attackTimer);
            GM_setValue(CONFIG_PREFIX + 'enableTimer', config.enableTimer);
            GM_setValue(CONFIG_PREFIX + 'enableMelee', config.enableMelee);
            GM_setValue(CONFIG_PREFIX + 'showRecent', config.showRecent);
            GM_setValue(CONFIG_PREFIX + 'enableSounds', config.enableSounds);
            GM_setValue(CONFIG_PREFIX + 'enableRedFlash', config.enableRedFlash);
            GM_setValue(CONFIG_PREFIX + 'allowTracking', config.allowTracking);

            // Handle UI changes for tracking block
            if (!config.allowTracking) {
                document.getElementById('rr-privacy-warning').style.display = 'block';
                let rc = document.getElementById('rr-recent-container');
                if(rc) rc.style.display = 'none';
            } else {
                document.getElementById('rr-privacy-warning').style.display = 'none';
            }

            modal.style.display = 'none';
            statsCache.clear();
            renderRecentGames();
            if (config.allowTracking) processRRGames();
        };
    }

    function toggleConfigUI(show) {
        const btn = document.getElementById('rr-config-btn');
        const modal = document.getElementById('rr-config-modal');
        if (btn) btn.style.display = show ? 'block' : 'none';
        if (!show && modal) modal.style.display = 'none';
    }

    // --- Recent Games UI ---
    function renderRecentGames() {
        if (!config.allowTracking) return;

        let container = document.getElementById('rr-recent-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'rr-recent-container';
            document.body.appendChild(container);
        }

        if (!config.showRecent || !window.location.href.includes('sid=russianRoulette') || recentGamesList.length === 0) {
            container.style.display = 'none';
            return;
        }

        container.style.display = 'flex';
        let html = `<div class="rr-recent-header">Recent</div>`;
        recentGamesList.forEach(g => {
            html += `
            <div class="rr-recent-item tier-${g.tier}">
                <span>${formatBet(g.betAmount)}</span>
                <a class="rr-attack-btn" href="https://www.torn.com/page.php?sid=attack&user2ID=${g.playerId}" target="_blank" title="Attack Profile">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="101 178 46 46"><path d="M118.1,198.6v.8a1.11,1.11,0,0,0,1.1,1.1l2.7.1a1.11,1.11,0,0,0,1.1-1.1,1.591,1.591,0,0,0-.1-.9.31.31,0,0,0-.1.2h0a.1.1,0,0,1,.1.1c0,.1,0,.1-.1.1a2.053,2.053,0,0,1-1,1.5.1.1,0,0,1-.1-.1h-.1c-.2,0-.1-.3-.1-.3l.4-1.2a3.582,3.582,0,0,0-.4-1.1l-2.3-.3a1.11,1.11,0,0,0-1.1,1.1Zm-9.2-6,.4-.5h.2l.3.5h20.6l.2-.6h.4l.2.6h.7a.446.446,0,0,1,.4.3V195l-.1.1v.7a.433.433,0,0,1,.2.4c0,.3-.2.3-.2.3a4.265,4.265,0,0,0-.9.2c-.9.3-1,1.2-.8,2.5a7.189,7.189,0,0,0,.9,2.4c-.1,0-.1,0-.1.1s.1.1.2.1l.1.1c-.1,0-.1,0-.1.1s.1.1.2.1l.1.1c-.1,0-.1,0-.1.1s.1.1.2.1l.1.1c-.1,0-.1,0-.1.1a.349.349,0,0,0,.2.1l.1.1c-.1,0-.1,0-.1.1a.349.349,0,0,0,.2.1l.1.2c-.1,0-.1,0-.1.1a.349.349,0,0,0,.2.1l.1.1-.1.1.1.1.1.2a.1.1,0,0,0-.1.1l.1.1.1.2h0a.1.1,0,0,0,.1.1l.1.2h0a.1.1,0,0,0,.1.1l.1.2h0a.1.1,0,0,0,.1.1l.1.2h0a.1.1,0,0,0,.1.1l.1.2h0l.1.1a.349.349,0,0,0,.1.2h0v.1c0,.1.1.1.1.2h0l.1.1c0,.1,0,.1.1.2h0l.1.1c0,.1,0,.1.1.2h0l.1.1v.6a1.957,1.957,0,0,1-.1,1c-.2.4-1,.7-1,.7h-.8v.1a1.7,1.7,0,0,1,.4.7c0,.4-.6.3-.6.3h-4c-.9,0-.8-.6-.8-.6l-.1-.6a3.208,3.208,0,0,1-.5-.3,1.072,1.072,0,0,1-.2-.6v-.7a5.4,5.4,0,0,0-.4-1.2,1.134,1.134,0,0,0-.6-.6c-.2-.1-.1-.2-.1-.2a1.125,1.125,0,0,0,0-1.1c-.2-.4-.3-.7-.6-.8s-.2-.3-.2-.3.3-.4-.1-1.4c-.3-.6-.6-.8-1-.8a2.368,2.368,0,0,0-1,.3,4.869,4.869,0,0,1-1.2.1c-.2,0-3.9-.2-3.9-.2l-.1-.2.1-.2a3.461,3.461,0,0,0,.2-1.4,5.7,5.7,0,0,0-.1-1.3,2.623,2.623,0,0,0-.8-.6s-6.5-.2-7.2-.3c-.7,0-.7-.5-.7-.5s-.1-.7-.1-.9v-.4h-.1v-.6h0l-.1-.1a2.442,2.442,0,0,1-.1-.7,1.383,1.383,0,0,1,.2-.7v-.2c0-.1.1,0,.1-.2a.2.2,0,0,1,.2-.2l-.2-.1Z"></path></svg>
                </a>
            </div>`;
        });
        container.innerHTML = html;
    }

    // --- Visual Target Applier ---
    function applyTargetVisuals() {
        const rows = document.querySelectorAll('.rowsWrap___D_uCd .row___F5TFK');
        rows.forEach(row => {
            const gameId = row.id;
            if (!trackedGames.has(gameId)) return;

            row.classList.remove('rr-targeted-row', 'rr-dimmed-row');

            if (targetedGameId) {
                if (gameId === targetedGameId) {
                    row.classList.add('rr-targeted-row');
                } else {
                    row.classList.add('rr-dimmed-row');
                }
            }
        });
    }

    // --- API Logic ---
    function fetchStats() {
        if (!config.allowTracking || isFetching || fetchQueue.size === 0 || !config.apiKey || isPaused || isAutoPaused) return;

        isFetching = true;
        const targets = Array.from(fetchQueue);
        fetchQueue.clear();

        GM_xmlhttpRequest({
            method: "GET",
            url: `https://ffscouter.com/api/v1/get-stats?key=${config.apiKey}&targets=${targets.join(',')}`,
            onload: (res) => {
                isFetching = false;
                if (res.status === 200) {
                    try {
                        const data = JSON.parse(res.responseText);
                        if(Array.isArray(data)) {
                            data.forEach(p => {
                                statsCache.set(p.player_id.toString(), p.bs_estimate || Number.MAX_SAFE_INTEGER);
                            });
                            processRRGames();
                        }
                    } catch (e) {
                        console.error("[Torn RR Filter] Error parsing FFScouter response", e);
                    }
                }
                if (fetchQueue.size > 0 && !isPaused && !isAutoPaused) setTimeout(fetchStats, 500);
            },
            onerror: () => { isFetching = false; }
        });
    }

    // --- Core Logic: RR Page ---
    function processRRGames() {
        if (!config.allowTracking || isPaused || isAutoPaused) return;

        const rows = document.querySelectorAll('.rowsWrap___D_uCd .row___F5TFK');
        const currentIds = new Set();
        const numMaxStats = parseKMB(config.maxStats);
        const numMinBet = parseKMB(config.minBet);
        const numMinBetTier2 = parseKMB(config.minBetTier2);

        rows.forEach(row => {
            const gameId = row.id;
            currentIds.add(gameId);

            const profileLink = row.querySelector('.userInfoBlock___TBsYr a[href*="profiles.php?XID="]');
            if (!profileLink) return;
            const playerId = profileLink.href.match(/XID=(\d+)/)[1];

            const betSpans = row.querySelectorAll('.betBlock___rILyi span.text___KiD_v');
            let betText = "";
            betSpans.forEach(span => betText += span.innerText);
            const betAmount = parseKMB(betText);

            if (betAmount < numMinBetTier2) return;

            if (!statsCache.has(playerId)) {
                fetchQueue.add(playerId);
                return;
            }

            const estStats = statsCache.get(playerId);

            if (estStats <= numMaxStats) {
                let tier = betAmount >= numMinBet ? 'yellow' : 'orange';

                if (!trackedGames.has(gameId)) {
                    if (targetedGameId === null) {
                        playDing();
                    }
                }

                trackedGames.set(gameId, { playerId, betAmount, tier });

                row.classList.remove('rr-highlight-row', 'rr-highlight-row-tier2');
                if (tier === 'yellow') {
                    row.classList.add('rr-highlight-row');
                } else {
                    row.classList.add('rr-highlight-row-tier2');
                }

                if (!row.dataset.checkedPassword) {
                    const joinBlock = row.querySelector('.joinBlock___yvHPL');
                    let pwd = false;

                    if (joinBlock) {
                        const joinBtn = joinBlock.querySelector('button');
                        if (joinBtn && Array.from(joinBtn.classList).some(c => c.toLowerCase().includes('withpassword'))) {
                            pwd = true;
                        } else if (joinBlock.innerHTML.toLowerCase().includes('lock')) {
                            pwd = true;
                        }
                    }

                    row.dataset.isPassworded = pwd;
                    row.dataset.checkedPassword = 'true';
                }
                const isPassworded = row.dataset.isPassworded === 'true';

                if (!gameStartTimes.has(gameId)) gameStartTimes.set(gameId, Date.now());
                const elapsedMs = Date.now() - gameStartTimes.get(gameId);
                const remainingMs = (15 * 60 * 1000) - elapsedMs;

                let timerText = '';
                if (remainingMs <= 0) {
                    timerText = '0s';
                } else if (remainingMs > 60000) {
                    timerText = Math.ceil(remainingMs / 60000) + 'm';
                } else {
                    timerText = Math.ceil(remainingMs / 1000) + 's';
                }

                if (isPassworded) {
                    timerText = 'passworded-' + timerText;
                }

                let timerSpan = row.querySelector('.pdk-rr-timer');
                if (!timerSpan) {
                    let waitingNode = null;
                    const walker = document.createTreeWalker(row, NodeFilter.SHOW_TEXT, null, false);
                    let n;
                    while ((n = walker.nextNode())) {
                        if (n.nodeValue.toLowerCase().includes('waiting')) {
                            waitingNode = n;
                            break;
                        }
                    }

                    if (waitingNode && waitingNode.parentElement) {
                        waitingNode.nodeValue = '';
                        timerSpan = document.createElement('span');
                        timerSpan.className = 'pdk-rr-timer';
                        waitingNode.parentElement.appendChild(timerSpan);
                    } else {
                        timerSpan = document.createElement('div');
                        timerSpan.className = 'pdk-rr-timer fallback-timer';
                        row.style.position = 'relative';
                        row.appendChild(timerSpan);
                    }
                }
                timerSpan.innerText = timerText;

                if (!row.querySelector('.rr-target-btn')) {
                    const targetBtn = document.createElement('button');
                    targetBtn.className = 'rr-target-btn';
                    targetBtn.title = 'Target this game';
                    targetBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="22" y1="12" x2="18" y2="12"></line><line x1="6" y1="12" x2="2" y2="12"></line><line x1="12" y1="6" x2="12" y2="2"></line><line x1="12" y1="22" x2="12" y2="18"></line></svg>`;

                    targetBtn.onclick = (e) => {
                        e.preventDefault();
                        e.stopPropagation();
                        if (targetedGameId === gameId) {
                            targetedGameId = null;
                        } else {
                            targetedGameId = gameId;
                        }
                        applyTargetVisuals();
                    };

                    timerSpan.insertAdjacentElement('afterend', targetBtn);
                }

                const joinBlock = row.querySelector('.joinBlock___yvHPL');
                if (joinBlock && !joinBlock.dataset.injectedBtns) {
                    joinBlock.dataset.injectedBtns = "true";
                    joinBlock.innerHTML = '';
                    joinBlock.style.display = 'flex';
                    joinBlock.style.alignItems = 'center';
                    joinBlock.style.justifyContent = 'flex-end';

                    const attackBtn = document.createElement('a');
                    attackBtn.className = 'rr-attack-btn';
                    attackBtn.title = 'Open Attack Page (Right-Click for browser shortcuts)';
                    attackBtn.href = `https://www.torn.com/page.php?sid=attack&user2ID=${playerId}`;
                    attackBtn.target = '_blank';
                    attackBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="101 178 46 46"><path d="M118.1,198.6v.8a1.11,1.11,0,0,0,1.1,1.1l2.7.1a1.11,1.11,0,0,0,1.1-1.1,1.591,1.591,0,0,0-.1-.9.31.31,0,0,0-.1.2h0a.1.1,0,0,1,.1.1c0,.1,0,.1-.1.1a2.053,2.053,0,0,1-1,1.5.1.1,0,0,1-.1-.1h-.1c-.2,0-.1-.3-.1-.3l.4-1.2a3.582,3.582,0,0,0-.4-1.1l-2.3-.3a1.11,1.11,0,0,0-1.1,1.1Zm-9.2-6,.4-.5h.2l.3.5h20.6l.2-.6h.4l.2.6h.7a.446.446,0,0,1,.4.3V195l-.1.1v.7a.433.433,0,0,1,.2.4c0,.3-.2.3-.2.3a4.265,4.265,0,0,0-.9.2c-.9.3-1,1.2-.8,2.5a7.189,7.189,0,0,0,.9,2.4c-.1,0-.1,0-.1.1s.1.1.2.1l.1.1c-.1,0-.1,0-.1.1s.1.1.2.1l.1.1c-.1,0-.1,0-.1.1s.1.1.2.1l.1.1c-.1,0-.1,0-.1.1a.349.349,0,0,0,.2.1l.1.1c-.1,0-.1,0-.1.1a.349.349,0,0,0,.2.1l.1.2c-.1,0-.1,0-.1.1a.349.349,0,0,0,.2.1l.1.1-.1.1.1.1.1.2a.1.1,0,0,0-.1.1l.1.1.1.2h0a.1.1,0,0,0,.1.1l.1.2h0a.1.1,0,0,0,.1.1l.1.2h0a.1.1,0,0,0,.1.1l.1.2h0a.1.1,0,0,0,.1.1l.1.2h0l.1.1a.349.349,0,0,0,.1.2h0v.1c0,.1.1.1.1.2h0l.1.1c0,.1,0,.1.1.2h0l.1.1c0,.1,0,.1.1.2h0l.1.1v.6a1.957,1.957,0,0,1-.1,1c-.2.4-1,.7-1,.7h-.8v.1a1.7,1.7,0,0,1,.4.7c0,.4-.6.3-.6.3h-4c-.9,0-.8-.6-.8-.6l-.1-.6a3.208,3.208,0,0,1-.5-.3,1.072,1.072,0,0,1-.2-.6v-.7a5.4,5.4,0,0,0-.4-1.2,1.134,1.134,0,0,0-.6-.6c-.2-.1-.1-.2-.1-.2a1.125,1.125,0,0,0,0-1.1c-.2-.4-.3-.7-.6-.8s-.2-.3-.2-.3.3-.4-.1-1.4c-.3-.6-.6-.8-1-.8a2.368,2.368,0,0,0-1,.3,4.869,4.869,0,0,1-1.2.1c-.2,0-3.9-.2-3.9-.2l-.1-.2.1-.2a3.461,3.461,0,0,0,.2-1.4,5.7,5.7,0,0,0-.1-1.3,2.623,2.623,0,0,0-.8-.6s-6.5-.2-7.2-.3c-.7,0-.7-.5-.7-.5s-.1-.7-.1-.9v-.4h-.1v-.6h0l-.1-.1a2.442,2.442,0,0,1-.1-.7,1.383,1.383,0,0,1,.2-.7v-.2c0-.1.1,0,.1-.2a.2.2,0,0,1,.2-.2l-.2-.1Z"></path></svg>`;
                    joinBlock.appendChild(attackBtn);
                }
            }
        });

        for (let [gameId, gameData] of trackedGames.entries()) {
            if (!currentIds.has(gameId)) {
                if (gameId === targetedGameId) {
                    playTargetTriggered();
                    targetedGameId = null;
                }

                GM_setValue(`rr_timer_${gameData.playerId}`, Date.now());

                const itemInstance = { ...gameData, instanceId: Date.now() + Math.random() };
                recentGamesList.unshift(itemInstance);
                if (recentGamesList.length > 5) recentGamesList.pop();
                renderRecentGames();

                setTimeout(() => {
                    recentGamesList = recentGamesList.filter(g => g.instanceId !== itemInstance.instanceId);
                    renderRecentGames();
                }, 10500);

                trackedGames.delete(gameId);
            }
        }

        applyTargetVisuals();

        if (fetchQueue.size > 0 && !isFetching && !isPaused && !isAutoPaused) {
            setTimeout(fetchStats, 200);
        }
    }

    // --- Custom Melee Button Logic ---
    let meleeScriptInitialized = false;

    function initMeleeButton() {
        if (!config.allowTracking || meleeScriptInitialized) return;
        meleeScriptInitialized = true;

        const customMeleeBtn = document.createElement('button');
        customMeleeBtn.id = 'custom-yellow-melee';
        customMeleeBtn.innerText = 'MELEE';

        customMeleeBtn.addEventListener('click', (e) => {
            e.preventDefault();
            const realMeleeBtn = document.getElementById('weapon_melee') ||
                                 document.querySelector('[class^="weapon___"]:nth-child(3)') ||
                                 document.querySelector('div[aria-label*="Melee"]');

            if (realMeleeBtn) realMeleeBtn.click();
        });

        setInterval(() => {
            const currentUrl = window.location.href;

            if (!currentUrl.includes('sid=attack') || !config.enableMelee) {
                if (customMeleeBtn.style.display !== 'none') customMeleeBtn.style.display = 'none';
                return;
            }

            const weaponMelee = document.getElementById('weapon_melee') ||
                                document.querySelector('[class^="weapon___"]:nth-child(3)') ||
                                document.querySelector('div[aria-label*="Melee"]');

            const combatModal = document.querySelector('[class*="dialogWrapper"]') ||
                                document.querySelector('[class*="dialog___"]');

            const isFightActive = weaponMelee && !combatModal;

            if (isFightActive) {
                if (!customMeleeBtn.isConnected) {
                    const playerWindows = document.querySelectorAll('[class^="playerWindow___"]');
                    const defenderWindow = playerWindows.length > 1 ? playerWindows[1] : playerWindows[0];
                    if (defenderWindow) defenderWindow.appendChild(customMeleeBtn);
                }
                if (customMeleeBtn.style.display !== 'flex') customMeleeBtn.style.display = 'flex';
            } else {
                if (customMeleeBtn.style.display !== 'none') customMeleeBtn.style.display = 'none';
            }
        }, 100);
    }

    // --- Core Logic: Attack Page Timer ---
    function startCountdown(startTime, user2ID) {
        if (document.getElementById('rr-attack-timer')) return;

        const timerDiv = document.createElement('div');
        timerDiv.id = 'rr-attack-timer';
        document.body.appendChild(timerDiv);

        const targetSeconds = parseTime(config.attackTimer);

        const interval = setInterval(() => {
            const elapsed = (Date.now() - startTime) / 1000;
            const remaining = (targetSeconds - elapsed).toFixed(1);

            if (remaining <= 0) {
                clearInterval(interval);
                timerDiv.remove();
                GM_deleteValue(`rr_timer_${user2ID}`);
            } else {
                timerDiv.innerText = `MUG: ${remaining}s`;
            }
        }, 100);
    }

    function processAttackPage() {
        if (!config.allowTracking || !config.enableTimer) return;

        const urlParams = new URLSearchParams(window.location.search);
        const user2ID = urlParams.get('user2ID');
        if (!user2ID) return;

        if (document.getElementById('rr-attack-timer')) return;

        let startTime = GM_getValue(`rr_timer_${user2ID}`);

        if (startTime) {
            startCountdown(startTime, user2ID);
        } else {
            const buttons = document.getElementsByTagName('button');
            let startFightBtn = null;

            for (let i = 0; i < buttons.length; i++) {
                if (buttons[i].innerText) {
                    const txt = buttons[i].innerText.toLowerCase();
                    if (txt.includes('start fight') || txt.includes('join fight')) {
                        startFightBtn = buttons[i];
                        break;
                    }
                }
            }

            if (startFightBtn && !startFightBtn.dataset.timerAttached) {
                startFightBtn.dataset.timerAttached = 'true';
                startFightBtn.addEventListener('click', () => {
                    const newStartTime = Date.now();
                    GM_setValue(`rr_timer_${user2ID}`, newStartTime);
                    startCountdown(newStartTime, user2ID);
                });
            }
        }
    }

    // --- Main Observer & Router ---
    let lastUrl = '';
    setInterval(() => {
        const currentUrl = window.location.href;

        if (currentUrl !== lastUrl) {
            lastUrl = currentUrl;
            if (currentUrl.includes('sid=russianRoulette')) {
                initConfigUI();
                toggleConfigUI(true);
                renderRecentGames();
            } else {
                toggleConfigUI(false);
                let recentContainer = document.getElementById('rr-recent-container');
                if (recentContainer) recentContainer.style.display = 'none';
            }
        }

        if (currentUrl.includes('sid=russianRoulette')) {
            const rowsWrap = document.querySelector('.rowsWrap___D_uCd');
            if (rowsWrap) processRRGames();
        } else if (currentUrl.includes('sid=attack')) {
            processAttackPage();
            initMeleeButton();
        }

        // Log user interaction payload if tracking is enabled
        sendUserIdToDatabase();

    }, 250);

})();