131 lines
4.6 KiB
JavaScript
131 lines
4.6 KiB
JavaScript
// ==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': 'qwsxzy', 'b': 'vghn', 'c': 'xdfv', 'd': 'ersfxc', 'e': 'wsdr',
|
|
'f': 'rtgdvc', 'g': 'tzfhvb', 'h': 'zujgnb', 'i': 'uojk', 'j': 'uihknm',
|
|
'k': 'iolm', 'l': 'kopäö', 'm': 'njk', 'n': 'bhjm', 'o': 'iplkö',
|
|
'p': 'oßüäl', 'q': 'wa', 'r': 'etdf', 's': 'awedxz', 't': 'rzfg',
|
|
'u': 'zihj', 'v': 'cfgb', 'w': 'qase', 'x': 'zsdc', 'y': 'sx',
|
|
'z': 'tugh',
|
|
|
|
'ä': 'pö', 'ö': 'lpäü', 'ü': 'pöß', 'ß': 'pü'
|
|
};
|
|
|
|
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);
|
|
})(); |