Official TriX Proxy Bot Script

A bot script for territorial.io that simulates typing on proxy sites. Enforces one tab per proxy IP. | Supported Proxies: CroxyProxy

Fra og med 17.09.2025. Se den nyeste version.

// ==UserScript==
// @name         Official TriX Proxy Bot Script
// @version      0.3.0.2
// @description  A bot script for territorial.io that simulates typing on proxy sites. Enforces one tab per proxy IP. | Supported Proxies: CroxyProxy
// @author       painsel
// @license      MIT
// @homepageURL  https://greasyfork.org/en/scripts/549132-trix-executor-beta-for-territorial-io
// @match        *://*/*?__cpo=*
// @grant        GM_addStyle
// @namespace    http://tampermonkey.net/
// ==/UserScript==

(function() {
    'use strict';

    // --- 1. Conditional Execution & Config ---
    const urlParams = new URLSearchParams(window.location.search);
    if (!urlParams.has('__cpo')) return;
    try {
        if (!atob(urlParams.get('__cpo')).includes('territorial.io')) return;
    } catch (e) { return; }

    const CONFIG = {
        STORAGE_KEY: 'd122_bot_settings',
        MAIN_TAB_KEY_PREFIX: 'trix_main_tab_for_',
        HEARTBEAT_INTERVAL: 2500, // 2.5 seconds
        AI_NAMES: [
            "Apex", "Vortex", "Shadow", "Nova", "Cipher", "Blaze", "Reaper", "Phantom", "Genesis", "Specter", "Warden", "Rogue", "Titan", "Fury", "Serpent", "Oracle", "Zenith", "Pulsar", "Jester", "Mirage", "Nomad", "Havoc", "Crux", "Wraith", "Glitch", "Vector", "Flux", "Byte", "Pixel", "Matrix", "Kernel", "Grid", "Node", "Syntax", "Griffin", "Wyvern", "Goliath", "Leviathan", "Hydra", "Phoenix", "Golem", "Paladin", "Warlock", "Rune", "Storm", "Quake", "Inferno", "Frost", "Cyclone", "Meteor", "Comet", "Abyss", "Ember", "Tempest", "Striker", "Raider", "Vanguard", "Sentinel", "Commando", "Juggernaut", "Marshal", "Legion", "Phalanx", "Blitz", "Saber", "Echo", "Impulse", "Catalyst", "Paradox", "Rift", "Karma", "Legacy", "Valor", "Creed", "Requiem", "Solstice", "Equinox", "Infinity", "Axiom", "Enigma", "Viper", "Cobra", "Hawk", "Falcon", "Tiger", "Wolf", "Panther", "Jackal", "Goliath", "Javelin", "Overload", "Torrent", "Cascade", "Helix", "Ion", "Ronin", "Monarch", "Zealot", "Herald", "Jinx", "Quasar"
        ]
    };

    const tabId = Date.now() + Math.random().toString(36).substring(2);
    const proxyIp = window.location.hostname;
    const mainTabKey = `${CONFIG.MAIN_TAB_KEY_PREFIX}${proxyIp}`;

    // --- 2. Duplicate Tab Modal ---
    function showDuplicateTabModal() {
        const modalCSS = `
            #trix-duplicate-overlay {
                position: fixed; top: 0; left: 0; width: 100%; height: 100%;
                background-color: rgba(0, 0, 0, 0.75); z-index: 100000;
                display: flex; align-items: center; justify-content: center;
                font-family: 'Consolas', 'Menlo', 'Courier New', monospace;
            }
            #trix-duplicate-modal {
                background-color: #1e1e1e; color: #d4d4d4;
                padding: 30px 40px; border-radius: 8px; text-align: center;
                border: 1px solid #3c3c3c; box-shadow: 0 5px 25px rgba(0,0,0,0.5);
            }
            #trix-duplicate-modal h2 { margin-top: 0; color: #e74c3c; font-size: 24px; }
            #trix-duplicate-modal p { margin-bottom: 0; font-size: 16px; }
        `;
        GM_addStyle(modalCSS);
        const modalHTML = `
            <div id="trix-duplicate-overlay">
                <div id="trix-duplicate-modal">
                    <h2>Duplicate Tab Detected</h2>
                    <p>A Main Tab for this proxy is already running. Please close this tab.</p>
                </div>
            </div>
        `;
        document.body.insertAdjacentHTML('beforeend', modalHTML);
    }

    // --- 3. Main Bot Logic (Only runs on the Main Tab) ---
    function runMainBot() {
        document.title = "[Main] " + document.title;

        // Heartbeat to maintain main tab status
        setInterval(() => {
            localStorage.setItem(mainTabKey, tabId);
        }, CONFIG.HEARTBEAT_INTERVAL);

        // Relinquish role on clean exit
        window.addEventListener('beforeunload', () => {
            localStorage.removeItem(mainTabKey);
        });

        const trixCSS = `
            :root{--trix-bg:#1e1e1e;--trix-bg-light:#252526;--trix-header-bg:#333333;--trix-border:#3c3c3c;--trix-text:#d4d4d4;--trix-text-secondary:#cccccc;--trix-blue-accent:#007acc;--trix-button-bg:#0e639c;--trix-button-hover-bg:#1177bb;--trix-input-bg:#3c3c3c}
            #trix-container{position:fixed;top:20px;left:20px;width:280px;background-color:var(--trix-bg-light);border:1px solid var(--trix-border);border-radius:6px;color:var(--trix-text);font-family:'Consolas','Menlo','Courier New',monospace;font-size:14px;z-index:99999;box-shadow:0 5px 20px rgba(0,0,0,0.5);user-select:none;overflow:hidden}
            #trix-header{background-color:var(--trix-header-bg);padding:8px 12px;cursor:move;font-weight:bold;border-bottom:1px solid var(--trix-border)}
            #trix-header a{color:var(--trix-blue-accent);text-decoration:none}#trix-header a:hover{text-decoration:underline}
            #trix-body{padding:15px;display:flex;flex-direction:column;gap:15px}
            .trix-input-group{display:flex;flex-direction:column;gap:5px}
            .trix-input-group label{font-size:13px;color:var(--trix-text-secondary)}
            .trix-input-group input[type="text"]{background-color:var(--trix-input-bg);border:1px solid var(--trix-border);color:var(--trix-text);padding:8px;border-radius:4px;font-family:inherit;outline:none;transition:border-color .2s,background-color .2s}
            .trix-input-group input[type="text"]:focus{border-color:var(--trix-blue-accent)}
            .trix-input-group input[type="text"]:disabled{background-color:#2a2a2a;color:#888;cursor:not-allowed}
            #trix-start-btn{background-color:var(--trix-button-bg);color:white;border:none;padding:10px;border-radius:4px;font-family:inherit;font-size:14px;font-weight:bold;cursor:pointer;transition:background-color .2s}
            #trix-start-btn:hover{background-color:var(--trix-button-hover-bg)}
            #trix-start-btn:disabled{background-color:#5a5a5a;cursor:not-allowed}
            .trix-radio-group{display:flex;gap:15px}
            .trix-radio-group label{display:flex;align-items:center;gap:5px;cursor:pointer}`;
        GM_addStyle(trixCSS);

        const trixHTML = `
            <div id="trix-container">
                <div id="trix-header">Official TriX Proxy Bot [MAIN]</div>
                <div id="trix-body">
                    <div class="trix-input-group">
                        <label for="trix-clan-tag">Clan Tag (Customizable in AI mode)</label>
                        <input type="text" id="trix-clan-tag" placeholder="e.g., TriX">
                    </div>
                    <div class="trix-input-group">
                        <label for="trix-username">Username</label>
                        <input type="text" id="trix-username" placeholder="Enter your name">
                    </div>
                    <div class="trix-input-group">
                        <label>Mode</label>
                        <div class="trix-radio-group">
                            <label><input type="radio" name="trix-typing-style" value="custom" checked> Custom</label>
                            <label><input type="radio" name="trix-typing-style" value="ai"> AI</label>
                        </div>
                    </div>
                    <button id="trix-start-btn">Begin Typing Simulation</button>
                </div>
            </div>`;
        document.body.insertAdjacentHTML('beforeend', trixHTML);

        const clanTagInput = document.getElementById('trix-clan-tag'), usernameInput = document.getElementById('trix-username'), radioButtons = document.querySelectorAll('input[name="trix-typing-style"]'), startBtn = document.getElementById('trix-start-btn');
        const toggleUsernameInput = () => { const mode = document.querySelector('input[name="trix-typing-style"]:checked').value; usernameInput.disabled = (mode === 'ai'); usernameInput.placeholder = (mode === 'ai') ? "AI will generate a name" : "Enter your name"; };
        radioButtons.forEach(radio => radio.addEventListener('change', toggleUsernameInput));
        const saveSettings = () => { localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify({ clanTag: clanTagInput.value, username: usernameInput.value })); };
        const loadSettings = () => { const settings = JSON.parse(localStorage.getItem(CONFIG.STORAGE_KEY) || '{}'); clanTagInput.value = settings.clanTag || ''; usernameInput.value = settings.username || ''; };
        loadSettings(); toggleUsernameInput(); clanTagInput.addEventListener('input', saveSettings); usernameInput.addEventListener('input', saveSettings);
        startBtn.addEventListener('click', () => {
            const nameInput = document.querySelector('#input0'); if (!nameInput) { alert('[TriX Bot] Could not find the game\'s name input field.'); return; }
            const mode = document.querySelector('input[name="trix-typing-style"]:checked').value;
            const clanTag = clanTagInput.value.trim();
            let fullName, username;
            if (mode === 'ai') {
                username = CONFIG.AI_NAMES[Math.floor(Math.random() * CONFIG.AI_NAMES.length)];
                if (!clanTag) { fullName = username; } else {
                    const formats = ['standard', 'possessive', 'lowerTag', 'tagAfter']; const chosenFormat = formats[Math.floor(Math.random() * formats.length)];
                    switch(chosenFormat) { case 'standard': fullName = `[${clanTag}] ${username}`; break; case 'possessive': fullName = `[${clanTag}]'s ${username}`; break; case 'lowerTag': fullName = `[${clanTag.toLowerCase()}] ${username}`; break; case 'tagAfter': fullName = `${username} [${clanTag.toLowerCase()}]`; break; }
                }
            } else {
                username = usernameInput.value.trim(); if (!username) { alert('[TriX Bot] Please enter a username for Custom mode.'); return; }
                fullName = clanTag ? `[${clanTag}] ${username}` : username;
            }
            startBtn.disabled = true; startBtn.textContent = 'Typing...';
            simulateTyping(nameInput, fullName, () => { startBtn.disabled = false; startBtn.textContent = 'Begin Typing Simulation'; });
        });
        const container = document.getElementById('trix-container'), header = document.getElementById('trix-header'); let isDragging = false, offsetX, offsetY;
        header.onmousedown = (e) => { isDragging = true; offsetX = e.clientX - container.offsetLeft; offsetY = e.clientY - container.offsetTop; document.onmousemove = (ev) => { if (isDragging) { container.style.left = `${ev.clientX - offsetX}px`; container.style.top = `${ev.clientY - offsetY}px`; } }; document.onmouseup = () => { isDragging = false; document.onmousemove = null; document.onmouseup = null; }; };
    }

    function simulateTyping(element, text, onComplete) {
        let i = 0; element.value = ''; element.focus();
        function typeCharacter() {
            if (i < text.length) { element.value += text.charAt(i); element.dispatchEvent(new Event('input', { bubbles: true })); i++; setTimeout(typeCharacter, Math.random() * 150 + 50); }
            else { element.blur(); if (onComplete) onComplete(); }
        } typeCharacter();
    }

    // --- 4. Initialization and Role Election ---
    function findMultiplayerButton() { for (const btn of document.querySelectorAll('button')) { if (btn.innerText && btn.innerText.includes('Multiplayer')) return btn; } return null; }
    function waitForGameUI(callback) { if (findMultiplayerButton()) { callback(); return; } const observer = new MutationObserver((mutations, obs) => { if (findMultiplayerButton()) { obs.disconnect(); callback(); } }); observer.observe(document.documentElement, { childList: true, subtree: true }); }

    function electRole() {
        const existingMainId = localStorage.getItem(mainTabKey);
        // If no main tab exists for this IP, or if this tab IS the main tab (e.g., on refresh), become the main tab.
        if (!existingMainId || existingMainId === tabId) {
            localStorage.setItem(mainTabKey, tabId);
            waitForGameUI(runMainBot);
        } else {
            // This is a duplicate tab.
            document.title = "[Duplicate] " + document.title;
            waitForGameUI(showDuplicateTabModal);
        }
    }

    electRole();

    /*
    The MIT License (MIT)
    Copyright (c) 2024 painsel
    ... (license text) ...
    */
})();