MonkeyType-lightning / javascript /monkeytype_cheat.js
algorembrant's picture
Upload 41 files
364eb96 verified
(function () {
console.log("%c Monkeytype Cheat Initiated (Persistent Mode) ", "background: #222; color: #bada55; font-size: 20px");
const keyMap = {
' ': { code: 'Space', keyCode: 32 },
'\n': { code: 'Enter', keyCode: 13 },
};
let isArmed = false;
function typeChar(char) {
const target = document.activeElement || document.body;
let key = char;
let code, keyCode;
if (keyMap[char]) {
code = keyMap[char].code;
keyCode = keyMap[char].keyCode;
} else {
code = `Key${char.toUpperCase()}`;
keyCode = char.toUpperCase().charCodeAt(0);
}
const eventOptions = {
key: key,
code: code,
keyCode: keyCode,
which: keyCode,
bubbles: true,
cancelable: true,
isTrusted: true
};
target.dispatchEvent(new KeyboardEvent('keydown', eventOptions));
target.dispatchEvent(new KeyboardEvent('keypress', eventOptions));
const inputEvent = new InputEvent('input', {
data: char,
inputType: 'insertText',
bubbles: true,
cancelable: true
});
target.dispatchEvent(inputEvent);
target.dispatchEvent(new KeyboardEvent('keyup', eventOptions));
}
function getDelay() {
return 100 + (Math.random() * 60 - 30);
}
async function cheat(textToType) {
console.log(`Typing ${textToType.length} chars...`);
for (let i = 0; i < textToType.length; i++) {
// Check if test was reset mid-run
if (!document.querySelector('#words .word.active')) {
console.log("Test reset detected. Stopping current run.");
return;
}
typeChar(textToType[i]);
await new Promise(r => setTimeout(r, getDelay()));
}
}
function getFullText() {
const words = document.querySelectorAll('#words .word');
let fullBuffer = "";
words.forEach((word, index) => {
const letters = word.querySelectorAll('letter');
letters.forEach(l => fullBuffer += l.textContent);
if (index < words.length - 1) fullBuffer += " ";
});
return fullBuffer;
}
const triggerHandler = (e) => {
if (!isArmed) return;
if (e.key.length === 1 && !e.ctrlKey && !e.altKey && !e.metaKey) {
const targetText = getFullText();
if (!targetText) return;
const firstChar = targetText[0];
if (firstChar && e.key === firstChar) {
isArmed = false; // Disarm to prevent double trigger
const remainingText = targetText.substring(1);
console.log("Triggered! Starting...");
setTimeout(() => cheat(remainingText), 150);
}
}
};
function armCheat() {
// Debounce slightly to ensure DOM is ready
setTimeout(() => {
const text = getFullText();
if (text.length > 0) {
isArmed = true;
console.log("Cheat RE-ARMED. Waiting for first key...");
} else {
console.log("Training ended or no words found.");
}
}, 500);
}
// LISTENER: Watch for Keydown (Trigger)
// We add this ONCE and keep it capable of firing whenever 'isArmed' is true.
window.addEventListener('keydown', triggerHandler);
// MUTATION OBSERVER: Watch for "Restart Test"
// When #words changes (new words added), we re-arm.
const wordsContainer = document.querySelector('#words');
if (wordsContainer) {
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
// If words are added/removed, it means test reset/changed
if (mutation.type === 'childList') {
// Check if we have new words
const words = document.querySelectorAll('#words .word');
if (words.length > 5) { // Arbitrary threshold to ensure it's a real set of words
armCheat();
// We break to avoid calling armCheat multiple times per batch update
break;
}
}
}
});
observer.observe(wordsContainer, { childList: true });
console.log("Observer attached. Auto-restart enabled.");
// Initial Arm
armCheat();
} else {
console.error("Container #words not found. Is the page loaded?");
}
})();