commit 3871c2304dcb1e8af4baed9c1ab06b969a6f3daa Author: elias Date: Fri Oct 3 10:49:00 2025 +0200 Add script.js diff --git a/script.js b/script.js new file mode 100644 index 0000000..f772570 --- /dev/null +++ b/script.js @@ -0,0 +1,129 @@ +// ==UserScript== +// @name monkeytype +// @version 1.0 +// @description Auto typing bot +// @author elias +// @match https://monkeytype.com/* +// @grant none +// @run-at document-start +// ==/UserScript== + +(function() { + 'use strict'; + + const CONFIG = { + wpmTarget: 200, + accuracy: 1, + toggleKey: 'Slash', // "-" on german keyboard + autoStart: false, + pauseBetweenWords: 50 + }; + + let isActive = CONFIG.autoStart; + const InputEvents = {}; + + // believable misstype + const adjacent = { + 'a': 'sqw', 'b': 'vghn', 'c': 'xdfv', 'd': 'erfcxs', 'e': 'wsdr', + 'f': 'rtgvcd', 'g': 'tyhbvf', 'h': 'yubjng', 'i': 'uojk', 'j': 'ikmnuh', + 'k': 'olmji', 'l': 'pk', 'm': 'njk', 'n': 'bhjm', 'o': 'iplk', + 'p': 'ol', 'q': 'wa', 'r': 'etdf', 's': 'awedxz', 't': 'ryfg', + 'u': 'yihj', 'v': 'cfgb', 'w': 'qase', 'x': 'zsdc', 'y': 'tugh', + 'z': 'asx' + }; + + function getDelay() { + const charsPerSecond = (CONFIG.wpmTarget * 5) / 60; + const baseDelay = 1000 / charsPerSecond; + const variance = baseDelay * 0.3; + return baseDelay + (Math.random() * variance * 2 - variance); + } + + function getNextCharacter() { + const currentWord = document.querySelector(".word.active"); + for (const letter of currentWord.children) { + if (letter.className === "") return letter.textContent; + } + return " "; + } + + function pressKey(key) { + const wordsInput = document.getElementById("wordsInput"); + const KeyboardEvent = Object.assign({}, DEFAULT_INPUT_OPTIONS, { target: wordsInput, data: key }); + const InputEvent = Object.assign({}, DEFAULT_KEY_OPTIONS, { target: wordsInput, key: key }); + + wordsInput.value += key; + InputEvents.beforeinput(InputEvent); + InputEvents.input(InputEvent); + InputEvents.keyup(KeyboardEvent); + } + + function typeCharacter() { + const typingTest = document.getElementById("typingTest"); + if (!isActive || (typingTest && typingTest.classList.contains("hidden"))) return; + + const nextChar = getNextCharacter(); + + if (Math.random() > CONFIG.accuracy && nextChar !== " ") { + const wrongChar = adjacent[nextChar.toLowerCase()]?.[0] || 'x'; + pressKey(wrongChar); + setTimeout(() => { + pressKey('\b'); + setTimeout(() => { + pressKey(nextChar); + setTimeout(typeCharacter, getDelay()); + }, 50); + }, 100); + } else { + pressKey(nextChar); + const delay = nextChar === " " ? CONFIG.pauseBetweenWords : getDelay(); + setTimeout(typeCharacter, delay); + } + } + + window.addEventListener("keydown", function(event) { + if (event.code === CONFIG.toggleKey) { + event.preventDefault(); + if (event.repeat) return; + isActive = !isActive; + if (isActive) typeCharacter(); + } + }); + + function hook(element) { + element.addEventListener = new Proxy(element.addEventListener, { + apply(target, _this, args) { + const [type, listener, ...options] = args; + if (_this.id === "wordsInput") { + InputEvents[type] = listener; + } + return target.apply(_this, args); + } + }); + } + + hook(HTMLInputElement.prototype); + + // no idea what all this is + const DEFAULT_KEY_OPTIONS = { + key: "", code: "", keyCode: 0, which: 0, isTrusted: true, altKey: false, + bubbles: true, cancelBubble: false, cancelable: true, charCode: 0, + composed: true, ctrlKey: false, currentTarget: null, defaultPrevented: false, + detail: 0, eventPhase: 0, isComposing: false, location: 0, metaKey: false, + path: null, repeat: false, returnValue: true, shiftKey: false, srcElement: null, + target: null, timeStamp: 6338.5, type: "", view: window, + }; + + const DEFAULT_INPUT_OPTIONS = { + isTrusted: true, bubbles: true, cancelBubble: false, cancelable: false, + composed: true, data: "", dataTransfer: null, defaultPrevented: false, + detail: 0, eventPhase: 0, inputType: "insertText", isComposing: false, + path: null, returnValue: true, sourceCapabilities: null, srcElement: null, + target: null, currentTarget: null, timeStamp: 11543, type: "input", + view: null, which: 0 + }; + + setTimeout(() => { + typeCharacter(); + }, 1000); +})(); \ No newline at end of file