awsdasdawd

awdasdawdasd

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu betiği yüklemek için bir betik yöneticisi eklentisi yüklemeniz gerekecektir.

(Zaten bir betik yöneticim var, hadi yükleyelim!)

Advertisement:

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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);

})();