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