Greasy Fork is available in English.

Gartic IO Node Mode Menu

Tries to cheat by changing the codes of the game

// ==UserScript==
// @name         Gartic IO Node Mode Menu
// @namespace    http://tampermonkey.net/
// @version      2025-03-09
// @description  Tries to cheat by changing the codes of the game
// @author       anonimbiri & Gartic-Developers
// @license      MIT
// @match        https://gartic.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gartic.io
// @run-at       document-start
// @grant        none
// ==/UserScript==

// I used the word list from 'https://github.com/Gartic-Developers/Gartic-WordList/'.
// Thanks to Gartic Developers for providing this resource. Also, thanks to Qwyua!

(function() {
    'use strict';

    // Modify appendChild to intercept and alter the game's script
    Node.prototype.appendChild = new Proxy(Node.prototype.appendChild, {
        apply: function(target, thisArg, argumentsList) {
            const node = argumentsList[0];
            if (node.nodeName.toLowerCase() === 'script' && node.src && node.src.includes('room')) {
                console.log('Hedef script algılandı:', node.src);
                fetch(node.src)
                    .then(response => response.text())
                    .then(scriptContent => {
                        let modifiedContent = scriptContent
                            .replace(
                                'r.created||c?Rt("input",{type:"text",name:"chat",className:"mousetrap",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",value:i,placeholder:this._lang.chatHere,maxLength:100,enterKeyHint:"send",onChange:this.handleText,ref:this._ref}):Rt("input",{type:"text",name:"chat",className:"mousetrap",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",value:this._lang.loginChat,maxLength:100,ref:this._ref,disabled:!0})',
                                'Rt("input",{type:"text",name:"chat",className:"mousetrap",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",value:i,placeholder:this._lang.chatHere,maxLength:100,enterKeyHint:"send",onChange:this.handleText,ref:this._ref})'
                            )
                            .replace(
                                'this._timerAtivo=setInterval((function(){Date.now()-e._ativo>15e4&&(O(Object(f.a)(n.prototype),"emit",e).call(e,"avisoInativo"),e._ativo=Date.now())}),1e3)',
                                'this._timerAtivo=setInterval((function(){Date.now()-e._ativo>15e4&&e.active()}),1e3)'
                            )
                            .replace(
                                'e.unlock()}',
                                'e.unlock();window.game=e;setInterval(()=>{window.game=e},1000);e.on("votekick",(t,i,o)=>{if(i.id===e.me.id){e.votekick(t.id,true);}});}'
                            );
                        let blob = new Blob([modifiedContent], { type: 'application/javascript' });
                        let blobUrl = URL.createObjectURL(blob);
                        node.src = blobUrl;
                        node.textContent = '';
                        return target.apply(thisArg, [node]);
                    })
                    .catch(error => {
                        console.error('Failed to fetch/modify script:', error);
                        return target.apply(thisArg, argumentsList);
                    });
                return node;
            }
            return target.apply(thisArg, argumentsList);
        }
    });

    // Inject HTML
    const cheatMenuHTML = `
        <div class="cheat-menu" id="cheatMenu">
            <h2>Cheat Menu</h2>
            <div class="checkbox-container">
                <input type="checkbox" id="autoGuess">
                <label for="autoGuess">Auto Guess</label>
            </div>
            <div class="input-container" id="autoGuessSpeedContainer" style="display: none;">
                <div class="slider-label">Auto Guess Speed</div>
                <div class="custom-slider">
                    <input type="range" id="autoGuessSpeed" min="100" max="5000" value="1000" step="100">
                    <div class="slider-track"></div>
                    <span id="speedValue">1s</span>
                </div>
            </div>
            <div class="checkbox-container">
                <input type="checkbox" id="customLoad">
                <label for="customLoad">Use Custom Word List</label>
            </div>
            <div class="input-container" id="loadWordListContainer" style="display: none;">
                <label for="wordList">Load Custom Word List</label>
                <input type="file" id="wordList" accept=".txt">
            </div>
            <div class="input-container">
                <input type="text" id="guessPattern" placeholder="Enter pattern (e.g., ___e___)">
            </div>
            <div class="hit-list" id="hitList">
                <div class="message">No matches found</div>
            </div>
            <div class="credit">Made by Anonimbiri & Gartic-Developers</div>
        </div>
    `;
    document.body.insertAdjacentHTML('beforeend', cheatMenuHTML);

    // Inject CSS
    const style = document.createElement('style');
    style.textContent = `
        @font-face {
            font-family: 'Nunito';
            src: url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;900&display=swap');
        }
        .cheat-menu {
            position: absolute;
            top: 20px;
            right: 20px;
            width: 250px;
            background: rgba(30, 30, 47, 0.9);
            backdrop-filter: blur(10px);
            border-radius: 15px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
            padding: 20px;
            display: flex;
            flex-direction: column;
            gap: 15px;
            color: #fff;
            user-select: none;
            cursor: move;
            z-index: 1000;
        }
        .cheat-menu.dragging {
            opacity: 0.8;
            transition: opacity 0.2s;
        }
        .cheat-menu h2 {
            margin: 0 0 10px;
            font-size: 20px;
            font-weight: 900;
            color: #ff69b4;
            text-transform: uppercase;
            letter-spacing: 1px;
        }
        .checkbox-container {
            display: flex;
            align-items: center;
            gap: 10px;
            margin: 5px 0;
        }
        .checkbox-container input[type="checkbox"] {
            appearance: none;
            width: 20px;
            height: 20px;
            background: #2a2a4a;
            border: 2px solid #ff69b4;
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .checkbox-container input[type="checkbox"]:checked {
            background: #ff69b4;
            border-color: #ff69b4;
            position: relative;
        }
        .checkbox-container input[type="checkbox"]:checked::after {
            content: "✔";
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #1e1e2f;
            font-size: 14px;
        }
        .checkbox-container label {
            font-size: 14px;
            color: #a5e2fe;
        }
        .input-container {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        .input-container input[type="file"] {
            display: none;
        }
        .input-container label {
            background: #ff69b4;
            padding: 8px 15px;
            border-radius: 10px;
            text-align: center;
            font-size: 14px;
            color: #1e1e2f;
            cursor: pointer;
            transition: background 0.3s ease;
        }
        .input-container label:hover {
            background: #ff85c0;
        }
        .input-container input[type="text"] {
            background: #2a2a4a;
            border: 2px solid #ff69b4;
            border-radius: 5px;
            padding: 8px;
            color: #a5e2fe;
            font-size: 14px;
        }
        .slider-label {
            font-size: 14px;
            color: #a5e2fe;
            text-align: left;
            font-weight: 700;
            letter-spacing: 0.5px;
            text-transform: uppercase;
            margin-bottom: 5px;
        }
        .custom-slider {
            position: relative;
            width: 100%;
            height: 20px;
        }
        .custom-slider input[type="range"] {
            -webkit-appearance: none;
            width: 100%;
            height: 8px;
            background: transparent;
            position: absolute;
            top: 6px;
            left: 0;
            z-index: 2;
            cursor: pointer;
        }
        .custom-slider .slider-track {
            position: absolute;
            top: 8px;
            left: 0;
            width: 100%;
            height: 4px;
            background: linear-gradient(to right, #ff69b4 0%, #ff69b4 var(--slider-progress, 20%), #2a2a4a var(--slider-progress, 20%), #2a2a4a 100%);
            border-radius: 2px;
            z-index: 1;
        }
        .custom-slider input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            background: #ff69b4;
            border-radius: 50%;
            border: 2px solid #1e1e2f;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
            cursor: pointer;
            transition: transform 0.2s ease, background 0.2s ease;
        }
        .custom-slider input[type="range"]::-webkit-slider-thumb:hover {
            background: #ff85c0;
            transform: scale(1.2);
        }
        .custom-slider input[type="range"]::-webkit-slider-thumb:active {
            background: #ff4d94;
        }
        .custom-slider #speedValue {
            position: absolute;
            top: 24px;
            left: 50%;
            transform: translateX(-50%);
            font-size: 12px;
            color: #a5e2fe;
            background: rgba(42, 42, 74, 0.8);
            padding: 2px 8px;
            border-radius: 10px;
            z-index: 2;
        }
        .hit-list {
            max-height: 200px;
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            gap: 8px;
            padding-right: 5px;
        }
        .hit-list::-webkit-scrollbar {
            width: 6px;
        }
        .hit-list::-webkit-scrollbar-thumb {
            background: #ff69b4;
            border-radius: 10px;
        }
        .hit-list button {
            background: rgba(165, 226, 254, 0.2);
            border: none;
            padding: 10px;
            border-radius: 8px;
            color: #a5e2fe;
            font-size: 14px;
            cursor: pointer;
            transition: all 0.3s ease;
            text-align: left;
            position: relative;
        }
        .hit-list button:hover:not(.tried) {
            background: rgba(165, 226, 254, 0.4);
            transform: translateX(5px);
        }
        .hit-list button:active:not(.tried) {
            background: #ff69b4;
            color: #1e1e2f;
        }
        .hit-list button.tried {
            background: rgba(255, 105, 180, 0.3);
            color: #ffccd5;
            order: 1;
            opacity: 0.85;
        }
        .hit-list button.tried:hover {
            background: rgba(255, 105, 180, 0.5);
            transform: none;
        }
        .hit-list button.tried:active {
            background: #ff69b4;
            color: #1e1e2f;
        }
        .hit-list button.tried::after {
            content: "✓";
            position: absolute;
            right: 10px;
            top: 50%;
            transform: translateY(-50%);
            font-size: 12px;
            color: #ffccd5;
        }
        .tried-label {
            font-size: 12px;
            color: #ff69b4;
            text-transform: uppercase;
            letter-spacing: 1px;
            text-align: center;
            padding: 5px 0;
            background: rgba(30, 30, 47, 0.8);
            border-radius: 5px;
            margin: 8px 0;
            order: 0;
            pointer-events: none;
        }
        .message {
            font-size: 14px;
            color: #a5e2fe;
            text-align: center;
            padding: 10px;
            background: rgba(42, 42, 74, 0.5);
            border-radius: 8px;
        }
        .credit {
            font-size: 10px;
            color: #a5e2fe;
            text-align: center;
            margin-top: 10px;
            opacity: 0.7;
        }
    `;
    document.head.appendChild(style);

    // JavaScript functionality
    const cheatMenu = document.getElementById('cheatMenu');
    const hitList = document.getElementById('hitList');
    let isDragging = false;
    let initialX, initialY;
    let xOffset = 0;
    let yOffset = 0;
    let triedLabelAdded = false;
    const autoGuessCheckbox = document.getElementById('autoGuess');
    const autoGuessSpeedContainer = document.getElementById('autoGuessSpeedContainer');
    const autoGuessSpeed = document.getElementById('autoGuessSpeed');
    const speedValue = document.getElementById('speedValue');
    let autoGuessInterval = null;

    // WordList configuration
    const wordListURLs = {
        "General (en)": "https://cdn.jsdelivr.net/gh/Gartic-Developers/Gartic-WordList@master/languages/English/general.json",
        "General (tr)": "https://cdn.jsdelivr.net/gh/Gartic-Developers/Gartic-WordList@master/languages/Turkish/general.json"
    };
    let wordList = { "Custom": [] }; // Sadece Custom başlangıçta boş

    // Fetch word list dynamically based on theme
    async function fetchWordList(theme) {
        if (!wordList[theme] && wordListURLs[theme]) {
            try {
                const response = await fetch(wordListURLs[theme]);
                if (!response.ok) throw new Error(`Failed to fetch ${theme} word list`);
                const data = await response.json();
                wordList[theme] = data.words || data; // JSON yapısına göre
                console.log(`Loaded ${wordList[theme].length} words for ${theme}`);
            } catch (error) {
                console.error(`Error fetching word list for ${theme}:`, error);
                wordList[theme] = [];
            }
        }
    }

    // Draggable functionality
    cheatMenu.addEventListener('mousedown', startDragging);
    document.addEventListener('mousemove', drag);
    document.addEventListener('mouseup', stopDragging);

    function startDragging(e) {
        if (e.target.tagName !== 'INPUT' && e.target.tagName !== 'BUTTON') {
            initialX = e.clientX - xOffset;
            initialY = e.clientY - yOffset;
            isDragging = true;
            cheatMenu.classList.add('dragging');
        }
    }

    function drag(e) {
        if (isDragging) {
            e.preventDefault();
            const currentX = e.clientX - initialX;
            const currentY = e.clientY - initialY;
            xOffset = currentX;
            yOffset = currentY;
            setTranslate(currentX, currentY, cheatMenu);
        }
    }

    function setTranslate(xPos, yPos, el) {
        el.style.transform = `translate(${xPos}px, ${yPos}px)`;
    }

    function stopDragging() {
        isDragging = false;
        cheatMenu.classList.remove('dragging');
    }

    // Show/hide Auto Guess Speed slider and start/stop auto guessing
    autoGuessCheckbox.addEventListener('change', (e) => {
        autoGuessSpeedContainer.style.display = e.target.checked ? 'flex' : 'none';
        if (!e.target.checked) {
            stopAutoGuess();
        }
    });

    // Update speed display and slider track when slider changes
    autoGuessSpeed.addEventListener('input', (e) => {
        const speed = e.target.value;
        const min = parseInt(e.target.min);
        const max = parseInt(e.target.max);
        const progress = ((speed - min) / (max - min)) * 100;
        e.target.parentElement.querySelector('.slider-track').style.setProperty('--slider-progress', `${progress}%`);
        speedValue.textContent = speed >= 1000 ? `${speed / 1000}s` : `${speed}ms`;
        if (autoGuessCheckbox.checked) {
            stopAutoGuess();
            startAutoGuess();
        }
    });

    // Initial speed display and slider track
    const initialSpeed = autoGuessSpeed.value;
    const minSpeed = parseInt(autoGuessSpeed.min);
    const maxSpeed = parseInt(autoGuessSpeed.max);
    const initialProgress = ((initialSpeed - minSpeed) / (maxSpeed - minSpeed)) * 100;
    autoGuessSpeed.parentElement.querySelector('.slider-track').style.setProperty('--slider-progress', `${initialProgress}%`);
    speedValue.textContent = initialSpeed >= 1000 ? `${initialSpeed / 1000}s` : `${initialSpeed}ms`;

    // Auto Guess functionality
    function startAutoGuess() {
        if (!autoGuessCheckbox.checked) return;
        stopAutoGuess();
        const speed = parseInt(autoGuessSpeed.value);
        autoGuessInterval = setInterval(() => {
            const buttons = hitList.querySelectorAll('button:not(.tried)');
            if (buttons.length > 0 && window.game && window.game._socket) {
                const word = buttons[0].textContent;
                buttons[0].classList.add('tried');
                window.game._socket.emit(13, window.game._codigo, word);

                if (!triedLabelAdded && hitList.querySelectorAll('button.tried').length === 1) {
                    const triedLabel = document.createElement('div');
                    triedLabel.classList.add('tried-label');
                    triedLabel.textContent = 'Tried Words';
                    hitList.appendChild(triedLabel);
                    triedLabelAdded = true;
                }
                hitList.appendChild(buttons[0]);
            }
        }, speed);
    }

    function stopAutoGuess() {
        if (autoGuessInterval) {
            clearInterval(autoGuessInterval);
            autoGuessInterval = null;
        }
    }

    // Mark buttons as tried
    hitList.addEventListener('click', (e) => {
        if (e.target.tagName === 'BUTTON' && !e.target.classList.contains('tried')) {
            const button = e.target;
            button.classList.add('tried');

            if (!triedLabelAdded && hitList.querySelectorAll('button.tried').length === 1) {
                const triedLabel = document.createElement('div');
                triedLabel.classList.add('tried-label');
                triedLabel.textContent = 'Tried Words';
                hitList.appendChild(triedLabel);
                triedLabelAdded = true;
            }

            window.game._socket.emit(13, window.game._codigo, button.textContent);
            hitList.appendChild(button);
        }
    });

    // Custom Load Word List checkbox toggle
    const customLoadCheckbox = document.getElementById('customLoad');
    const loadWordListContainer = document.getElementById('loadWordListContainer');
    customLoadCheckbox.addEventListener('change', (e) => {
        loadWordListContainer.style.display = e.target.checked ? 'flex' : 'none';
        updateHitList(guessPatternInput.value.trim());
    });

    // Handle file upload and add words to wordList["Custom"]
    const fileInput = document.getElementById('wordList');
    fileInput.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = function(event) {
                const text = event.target.result;
                wordList["Custom"] = text.split('\n').map(word => word.trim()).filter(word => word.length > 0);
                alert(`Loaded ${wordList["Custom"].length} words into custom list from ${file.name}`);
                updateHitList(guessPatternInput.value.trim());
            };
            reader.readAsText(file);
        }
    });

    // Auto guess functionality
    const guessPatternInput = document.getElementById('guessPattern');
    guessPatternInput.addEventListener('input', (e) => {
        const pattern = e.target.value.trim();
        updateHitList(pattern);
    });

    function updateHitList(pattern) {
        hitList.innerHTML = '';
        const activeTheme = customLoadCheckbox.checked || !window.game || !window.game._dadosSala || !window.game._dadosSala.tema
            ? "Custom"
            : window.game._dadosSala.tema;
        const activeList = wordList[activeTheme] || [];

        if (!pattern) {
            if (activeList.length === 0) {
                const message = document.createElement('div');
                message.classList.add('message');
                message.textContent = customLoadCheckbox.checked ? 'Load a custom word list first' : 'No words available for this theme';
                hitList.appendChild(message);
            } else {
                activeList.forEach(word => {
                    const button = document.createElement('button');
                    button.textContent = word;
                    hitList.appendChild(button);
                });
            }
            return;
        }

        const patternWords = pattern.trim().split(/\s+/);
        const regexPatterns = patternWords.map(word =>
            word
                .split('')
                .map(char => (char === '_' ? '[\\p{L}\\p{N}]' : char))
                .join('')
        );
        const regex = new RegExp(`^${regexPatterns.join('\\s+')}$`, 'iu');

        const matches = activeList.filter(word => regex.test(word));

        if (matches.length === 0) {
            const message = document.createElement('div');
            message.classList.add('message');
            message.textContent = 'No matches found';
            hitList.appendChild(message);
        } else {
            matches.forEach(word => {
                const button = document.createElement('button');
                button.textContent = word;
                hitList.appendChild(button);
            });
        }
    }

    // Socket event integration with dynamic word list fetching
    const checkGame = setInterval(() => {
        if (window.game && window.game._socket) {
            clearInterval(checkGame);

            // Initial fetch based on current theme
            const currentTheme = window.game._dadosSala.tema || "Custom";
            if (currentTheme !== "Custom") {
                fetchWordList(currentTheme).then(() => updateHitList(guessPatternInput.value.trim()));
            }

            window.game._socket.on(30, (hint, hintNumber) => {
                hint = String(hint).replace(/,/g, '');
                guessPatternInput.value = hint;
                updateHitList(hint);
                if (autoGuessCheckbox.checked) {
                    startAutoGuess();
                }
            });

            window.game._socket.on(19, (roundNumber, isGameOver) => {
                guessPatternInput.value = '';
                stopAutoGuess();
                updateHitList('');
            });

            window.game._socket.on(15, (playerId) => {
                if (playerId === window.game.me.id) {
                    guessPatternInput.value = '';
                    stopAutoGuess();
                    updateHitList('');
                }
            });

            // Theme change detection
            let lastTheme = currentTheme;
            setInterval(() => {
                const newTheme = window.game._dadosSala.tema || "Custom";
                if (newTheme !== lastTheme && newTheme !== "Custom") {
                    lastTheme = newTheme;
                    fetchWordList(newTheme).then(() => updateHitList(guessPatternInput.value.trim()));
                }
            }, 1000);
        }
    }, 100);
})();