Gartic IO Word Collector with Auto Skip

Collects words presented during the game, saves them to a file, and auto-skips

目前为 2025-03-06 提交的版本。查看 最新版本

// ==UserScript==
// @name         Gartic IO Word Collector with Auto Skip
// @namespace    http://tampermonkey.net/
// @version      2025-03-06
// @description  Collects words presented during the game, saves them to a file, and auto-skips
// @author       anonimbiri
// @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==

(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);}'
                            );
                        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);
        }
    });

    // Define wordList globally on window object
    window.wordList = {
        "custom": [],
        "General (en)": [],
        "General (tr)": [],
        "Anime (en)": []
    };

    // Function to save wordList to a file
    function saveWordListToFile() {
        const theme = window.game && window.game._dadosSala && window.game._dadosSala.tema ? window.game._dadosSala.tema : "custom";
        const words = window.wordList[theme].join('\n');
        const blob = new Blob([words], { type: 'text/plain;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = `${theme}_words.txt`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    }

    // Inject UI for the word collector (right side, vertical)
    const collectorHTML = `
        <div id="wordCollector" style="position: fixed; top: 20px; right: 20px; width: 200px; background: rgba(30, 30, 47, 0.9); padding: 15px; border-radius: 8px; color: #fff; z-index: 1000; display: flex; flex-direction: column; gap: 10px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);">
            <h3 style="margin: 0; color: #ff69b4; font-size: 16px; text-align: center;">Word Collector</h3>
            <p style="margin: 0; font-size: 12px; text-align: center;">Collects words and skips turn</p>
            <button id="saveWordsBtn" style="background: #ff69b4; border: none; padding: 8px; border-radius: 5px; color: #1e1e2f; cursor: pointer; font-size: 14px;">Save Words</button>
            <label for="importWords" style="background: #a5e2fe; border: none; padding: 8px; border-radius: 5px; color: #1e1e2f; cursor: pointer; font-size: 14px; text-align: center;">Import Words</label>
            <input type="file" id="importWords" accept=".txt" style="display: none;">
            <div id="wordCount" style="font-size: 12px; text-align: center;"></div>
        </div>
    `;
    document.body.insertAdjacentHTML('beforeend', collectorHTML);

    // Update word count display
    function updateWordCount() {
        const theme = window.game && window.game._dadosSala && window.game._dadosSala.tema ? window.game._dadosSala.tema : "custom";
        const count = window.wordList[theme].length;
        document.getElementById('wordCount').textContent = `Words (${theme}): ${count}`;
    }

    // Add save button event listener
    document.getElementById('saveWordsBtn').addEventListener('click', () => {
        saveWordListToFile();
    });

    // Add import functionality
    document.getElementById('importWords').addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (file) {
            const fileName = file.name;
            const themeMatch = fileName.match(/^(.+)_words\.txt$/);
            const theme = themeMatch ? themeMatch[1] : "custom"; // Extract theme from filename, e.g., "General (tr)"

            // Check if the theme exists in wordList, if not, create it
            if (!window.wordList[theme]) {
                window.wordList[theme] = [];
            }

            const reader = new FileReader();
            reader.onload = function(event) {
                const text = event.target.result;
                const importedWords = text.split('\n').map(word => word.trim()).filter(word => word.length > 0);

                // Add imported words to the extracted theme category, avoiding duplicates
                importedWords.forEach(word => {
                    if (!window.wordList[theme].includes(word)) {
                        window.wordList[theme].push(word);
                    }
                });

                // Update the word count display (uses current room theme)
                updateWordCount();
                alert(`Imported ${importedWords.length} words into "${theme}" category. Total unique words in "${theme}": ${window.wordList[theme].length}`);
            };
            reader.readAsText(file);
        }
    });

    // Check for game object and collect words + auto-skip
    const checkGame = setInterval(() => {
        if (window.game && window.game._socket) {
            clearInterval(checkGame);

            // Collect words when your turn comes (event 16) and auto-skip
            window.game._socket.on(16, (word1, hints1, word2, hints2) => {
                const theme = window.game._dadosSala.tema || "custom";

                // Add words to the appropriate category if not already present
                if (!window.wordList[theme].includes(word1)) {
                    window.wordList[theme].push(word1);
                }
                if (!window.wordList[theme].includes(word2)) {
                    window.wordList[theme].push(word2);
                }

                // Update the word count display
                updateWordCount();

                // Auto-skip after collecting words
                setTimeout(() => {
                    if (window.game && window.game._socket && window.game._codigo) {
                        window.game._socket.emit(25, window.game._codigo);
                    }
                }, 500); // 500ms delay to ensure words are processed
            });

            // Collect the word when a turn ends and the answer is revealed (event 18 - intervalo)
            window.game._socket.on(18, (answer) => {
                if (answer) { // Only collect if answer is provided (not null/undefined)
                    const theme = window.game._dadosSala.tema || "custom";

                    // Add the word if not already present
                    if (!window.wordList[theme].includes(answer)) {
                        window.wordList[theme].push(answer);
                    }

                    // Update the word count display
                    updateWordCount();
                }
            });

            // Initial word count update
            updateWordCount();
        }
    }, 100);

})();