Torn Russian Roulette Helper

Assists in tracking users in Russian Roulette in Torn.

目前為 2023-08-29 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Torn Russian Roulette Helper
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Assists in tracking users in Russian Roulette in Torn.
// @author       ErrorNullTag
// @match        https://www.torn.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license      GPU AGPLv3
// ==/UserScript==



(function() {
    'use strict';

    if (!window.location.href.includes("russianRoulette")) return;
let API_KEY = GM_getValue("API_KEY", ""); // Try to retrieve the API key from storage

// If API key is not set, ask the user for it
if (!API_KEY) {
    API_KEY = window.prompt("Please enter your API key:", "");

    // If the user provides a value, save it for future use
    if (API_KEY) {
        GM_setValue("API_KEY", API_KEY);
    } else {
        // If the user cancels the prompt or provides an empty value, reject the promise
        return Promise.reject("API key is required.");
    }
}

    let betAmount = "N/A";

    function attack() {
        let api = API_KEY;
        let url = window.location.href;
        if(url.includes("sid=attack"))
        {
            url = new URL(url);
            let attackId = url.searchParams.get("user2ID");
            console.log(`https://api.torn.com/user/${attackId}?selections=profile,personalstats&key=${api}`);
            fetch(`https://api.torn.com/user/${attackId}?selections=profile,personalstats&key=${api}`)
            .then(function(response) {
                if (response.status !== 200) {
                    console.log(`fetch error ${response.status}`);
                    return;
                }
                response.json().then(function(data) {
                    let joinBtn = $("button:contains(\"Start fight\"), button:contains(\"Join fight\")").closest("button");
                    if($(joinBtn).length) {
                        $(joinBtn).after(`
                            <div id='attackInfo'>
                                <br />Attacks: <font color='green'>[W] ${parseInt(data.personalstats.attackswon) || 0}</font> <font color='red'>[L] ${parseInt(data.personalstats.attackslost) || 0}</font>
                                <br />Defends: <font color='green'>[W] ${parseInt(data.personalstats.defendswon) || 0}</font> <font color='red'>[L] ${parseInt(data.personalstats.defendslost) || 0}</font>
                                <br />Drugs: ${parseInt(data.personalstats.drugsused) || 0} used (${parseInt(data.personalstats.xantaken) || 0} xan)
                                <br />Consumables: ${parseInt(data.personalstats.consumablesused) || 0} used
                                <br />Refills: ${parseInt(data.personalstats.refills) || 0} used
                                <br />Networth: $${data.personalstats.networth.toLocaleString("en")}
                                <br />Last action: ${data.last_action.relative}
                                <br />Faction: <a href='https://www.torn.com/factions.php?step=profile&ID=${data.faction.faction_id}'>${data.faction.faction_name}</a>
                            </div>`);
                    }
                }).catch((err) => { console.log(err); });
            }).catch(function(err) {
                console.log(`fetch error ${err}`);
            });
        }
    }
    // attack(); // Call the attack function at the start

    const BLACKLIST_DURATION = 60 * 1000;
    const blacklist = new Map();

    function isBlacklisted(userId) {
        if (blacklist.has(userId)) {
            if (Date.now() - blacklist.get(userId) < BLACKLIST_DURATION) {
                return true;
            } else {
                blacklist.delete(userId); // Remove the user from blacklist after the duration
            }
        }
        return false;
    }

function getBetAmountFromElement(userStatusWrap) {
    const betAmountElement = userStatusWrap.querySelector('.betBlock___wz9ED.columnItem___hnwxL');
    console.log("Bet Amount Element:", betAmountElement);  // Debugging log

    if (betAmountElement) {
        const betText = betAmountElement.textContent;
        console.log("Bet Text:", betText);  // Debugging log

        const betMatches = betText.match(/\$(\d{1,3}(?:,\d{3})*)(?!\d)/g);

        console.log("Bet Matches:", betMatches);  // Debugging log

        if (betMatches && betMatches.length) {
            const mainBet = betMatches[0].replace('$', '');
            return mainBet;
        }
    }

    return "N/A"; // Return "N/A" if nothing matches
}



async function fetchUserData(userId) {
    if (isBlacklisted(userId)) {
        return {
            username: idList.get(userId).username, // Added username
            level: idList.get(userId).level,
            healthStatus: 'Blacklisted'
        };
    }

    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: "GET",
            url: `https://api.torn.com/user/${userId}?selections=profile&key=${API_KEY}`,
            onload: function(response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data.status && data.status.state) {
                        if (data.status.state === 'Hospital') {
                            blacklist.set(userId, Date.now()); // Blacklist the user
                            resolve({
                                username: data.name, // Fetch the username
                                level: data.level,
                                healthStatus: 'Blacklisted'
                            });
                        } else {
                            resolve({
                                ...data,
                                username: data.name, // Fetch the username
                                healthStatus: data.status.state
                            });
                        }
                    } else {
                        resolve({
                            ...data,
                            username: data.name, // Fetch the username
                            healthStatus: 'Error: Status unavailable or missing'
                        });
                    }
                } catch (error) {
                    console.error('Error parsing user data:', error);
                    reject(error);
                }
            },
            onerror: function(error) {
                console.error('Error fetching user data:', error);
                reject(error);
            }
        });
    });
}
    function isMobile() {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

function initBox() {
    const updateBoxDimensions = () => {
        if (isMobile()) {
            // If on mobile
            box.style.width = '100%';  // Full width
            box.style.height = '60vh';  // 60% of viewport height
        } else {
            // If on desktop
            let width = window.innerWidth * 0.2;  // 20% of window width
            let maxHeight = window.innerHeight * 0.5;  // 50% of window height

            box.style.width = `${width}px`;
            box.style.maxHeight = `${maxHeight}px`;
        }
    }

    box.style.overflowY = 'scroll';
    box.style.background = 'black';
    box.style.color = 'green';
    box.style.marginTop = '10px'; // Provide spacing from the games list
    box.style.padding = '10px';
    box.style.border = '2px solid green';
    box.innerHTML = '<div style="font-size: 20px; color: gold;">Phantom Scripting</div><br>';

    // Finding the main content container
    const contentWrapper = document.querySelector('.content-wrapper[role="main"]');
    if (contentWrapper) {
        contentWrapper.appendChild(box);  // Append the box to the end of the container
    } else {
        // If for some reason the main container isn't found, the box will be added to the body
        document.body.appendChild(box);
    }

    // Call the update function initially
    updateBoxDimensions();

    // Adjust dimensions when window is resized
    window.addEventListener('resize', updateBoxDimensions);
}

    const box = document.createElement('div');
    initBox();

    const idList = new Map();

   function refreshBox() {
        while (box.firstChild && box.childElementCount > 1) {
            box.lastChild.remove();
        }

        if (!idList.size) {
            box.innerHTML += 'No Users Found';
            return;
        }

        idList.forEach((data, userId) => {
            const username = data.username || 'N/A';
            const healthStatus = data.healthStatus || 'Error: Unable to retrieve status';
            const level = data.level !== undefined ? data.level : 'N/A';
            const betAmount = data.betAmount !== "N/A" ? data.betAmount : `<span style="color:red;">${data.betAmount}</span>`;
            const entry = document.createElement('div');
            entry.style.marginBottom = '10px';

            if (healthStatus === 'Blacklisted') {
                entry.style.color = 'red';  // Set the color to red if the user is blacklisted
            }

            entry.innerHTML = `<strong>Username:</strong> ${username}<br><strong>ID:</strong> ${userId}<br><strong>Level:</strong> ${level}<br><strong>Health Status:</strong> ${healthStatus}<br><strong>Bet Amount:</strong> ${betAmount}`;

            if (!data.isPresent) {
                const mugBtn = document.createElement('button');
                mugBtn.innerText = 'Mug';
                mugBtn.style.backgroundColor = 'green';
                mugBtn.style.marginLeft = '10px';
                mugBtn.addEventListener('click', () => {
                    fetchUserData(userId)
                        .then(userData => {
                            idList.set(userId, {
                                ...userData,
                                isPresent: true,
                                lastSeen: Date.now()
                            });
                            refreshBox();
                            window.location.href = `https://www.torn.com/loader.php?sid=attack&user2ID=${userId}`;
                        })
                        .catch(error => {
                            console.error('Error fetching user data:', error);
                        });
                });
                entry.appendChild(mugBtn);
            }

            box.appendChild(entry);
        });
    }
function formatCurrency(amount) {
    return `$${Number(amount).toLocaleString('en-US')}`;
}

function monitorChanges() {
    const userInfoWraps = document.querySelectorAll('.userStatusWrap___ljSJG');
    const currentTime = Date.now();

    if (!userInfoWraps.length) {
        setTimeout(monitorChanges, 2000);
        return;
    }

    const newIDs = new Set();
    userInfoWraps.forEach(userStatusWrap => {
        const userId = userStatusWrap.getAttribute('id').split('_')[0];

        // Extract bet amount from sibling element with aria-label attribute
        let betAmount = "N/A";
        const betElement = userStatusWrap.closest('.row___CHcax').querySelector('.betBlock___wz9ED');
        if (betElement) {
            const betAriaLabel = betElement.getAttribute('aria-label');
            const betMatch = betAriaLabel.match(/Bet amount: (\d+)/);
            if (betMatch && betMatch[1]) {
                betAmount = formatCurrency(betMatch[1]);
            }
        }

        if (userId && !idList.has(userId)) {
            idList.set(userId, { isPresent: true, lastSeen: currentTime, betAmount });
            fetchUserData(userId)
                .then(userData => {
                    idList.set(userId, {
                        isPresent: true,
                        ...userData,
                        lastSeen: currentTime,
                        betAmount
                    });
                    refreshBox();
                })
                .catch(error => {
                    console.error('Error fetching user data:', error);
                });
        } else if (idList.has(userId)) {
            idList.get(userId).lastSeen = currentTime;
            if (betAmount !== "N/A") idList.get(userId).betAmount = betAmount;
        }
        newIDs.add(userId);
    });

    idList.forEach((data, userId) => {
        if (!newIDs.has(userId) && data.isPresent) {
            data.isPresent = false;
            data.leftTime = currentTime;
        }
    });

    removeLeftUsers(currentTime);
    setTimeout(monitorChanges, 2000);
}




    function removeLeftUsers(currentTime) {
        const THIRTY_SECONDS = 30 * 1000;
        const leftUsers = [];

        idList.forEach((data, userId) => {
            if (!data.isPresent && (currentTime - data.leftTime) > THIRTY_SECONDS) {
                leftUsers.push(userId);
            }
        });

        leftUsers.forEach(userId => {
            idList.delete(userId);
        });

        refreshBox();
    }

    monitorChanges();
})();