Torn Keno Random Selector

Places a Torn-styled block under BET & ROUNDS, centered, to pick random Keno numbers.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Torn Keno Random Selector
// @namespace    https://greasyfork.org/en/scripts/531078-torn-keno-random-selector
// @version      03.28.2025.19.10
// @description  Places a Torn-styled block under BET & ROUNDS, centered, to pick random Keno numbers.
// @author       KillerCleat
// @match        https://www.torn.com/page.php?sid=keno*
// @grant        none
// @license      MIT License
// ==/UserScript==

(function() {
    'use strict';

    // 1. Randomly pick 'count' unique items from 'arr'
    function getRandomElements(arr, count) {
        const arrCopy = [...arr];
        const result = [];
        for (let i = 0; i < count; i++) {
            if (arrCopy.length === 0) break;
            const index = Math.floor(Math.random() * arrCopy.length);
            result.push(arrCopy[index]);
            arrCopy.splice(index, 1); // Remove to ensure uniqueness
        }
        return result;
    }

    // 2. Inject minimal CSS to center our Torn-styled block under BET & ROUNDS
    function injectStyles() {
        const style = document.createElement('style');
        style.innerHTML = `
            /* This container replicates the "cancel" style block but is placed under #controls */
            #randomButtonsContainer {
                width: 100%;
                text-align: center;  /* Center horizontally */
                margin-top: 10px;    /* Space above */
                margin-bottom: 10px; /* Optional space below */
            }
            /* The .desc and .cancel-btn-wrap come from Torn's classes */
            #randomButtonsContainer .desc p {
                margin: 0;
                padding: 0;
            }
            /* Force buttons in a single horizontal row */
            #randomButtonsContainer .cancel-btn-wrap {
                display: inline-block;   /* Keep the buttons together horizontally */
                white-space: nowrap;     /* Prevent wrapping */
                text-align: left;        /* Buttons align left inside the inline-block */
                margin-top: 5px;         /* Space between text and buttons */
            }
            /* Minor spacing for each .btn-wrap.orange so they appear side by side */
            .btn-wrap.orange {
                display: inline-block;
                margin: 0 5px 5px 0;  /* Right & bottom spacing */
            }
        `;
        document.head.appendChild(style);
    }

    // 3. Build the Torn-styled block: text + row of 10 buttons
    function createRandomButtons() {
        // Avoid reinserting if it already exists
        if (document.getElementById('randomButtonsContainer')) return;

        // The outer container with Torn's "cancel" class
        const container = document.createElement('div');
        container.id = 'randomButtonsContainer';
        container.className = 'cancel';

        // The text area (Torn's "desc" class)
        const desc = document.createElement('div');
        desc.className = 'desc';
        desc.tabIndex = 0;
        desc.innerHTML = '<p>Select how many random numbers to pick:</p>';
        container.appendChild(desc);

        // The wrapper that holds the row of buttons
        const cancelBtnWrap = document.createElement('div');
        cancelBtnWrap.className = 'cancel-btn-wrap';
        container.appendChild(cancelBtnWrap);

        // Create 10 Torn-style buttons in a horizontal row
        for (let i = 1; i <= 10; i++) {
            const btnWrap = document.createElement('div');
            btnWrap.className = 'btn-wrap orange';  // Torn class

            const btnDiv = document.createElement('div');
            btnDiv.className = 'btn';

            const button = document.createElement('button');
            button.className = 'torn-btn orange';    // Torn button style
            button.setAttribute('aria-label', `Select ${i}`);
            button.setAttribute('data-count', i);
            button.textContent = i;

            btnDiv.appendChild(button);
            btnWrap.appendChild(btnDiv);
            cancelBtnWrap.appendChild(btnWrap);
        }

        // Insert our container below BET & ROUNDS (inside #controls)
        const controls = document.getElementById('controls');
        if (controls) {
            controls.appendChild(container);
        } else {
            // Fallback if #controls not found
            document.body.appendChild(container);
        }

        // Attach click handlers to pick random Keno numbers
        const boardSpans = document.querySelectorAll('#boardContainer span');
        const allButtons = container.querySelectorAll('button');
        allButtons.forEach(btn => {
            btn.addEventListener('click', function() {
                const count = parseInt(this.getAttribute('data-count'), 10);
                // Filter out any already-marked numbers
                const unmarkedSpans = Array.from(boardSpans).filter(
                    span => !span.classList.contains('marked')
                );

                if (unmarkedSpans.length < count) {
                    alert(`Not enough unmarked numbers available. Only ${unmarkedSpans.length} left.`);
                    return;
                }

                // Randomly select the requested number
                const selectedSpans = getRandomElements(unmarkedSpans, count);
                // Simulate clicks so Torn's code marks them
                selectedSpans.forEach(span => span.click());
            });
        });
    }

    // 4. Wait until the Keno board & controls exist, then insert our block
    function waitForKenoElements() {
        const boardContainer = document.getElementById('boardContainer');
        const controls = document.getElementById('controls');
        if (boardContainer && controls) {
            createRandomButtons();
            clearInterval(checkInterval);
        }
    }

    // 5. Inject styles & poll every 500ms for #controls to appear
    injectStyles();
    const checkInterval = setInterval(waitForKenoElements, 500);
})();