| | import pyautogui
|
| | import keyboard
|
| | import time
|
| | import random
|
| | import pyperclip
|
| | import sys
|
| |
|
| |
|
| | pyautogui.PAUSE = 0
|
| |
|
| |
|
| | CONFIG = {
|
| | "min_wpm": 390,
|
| | "max_wpm": 450,
|
| |
|
| | "min_key_hold": 0.01,
|
| | "max_key_hold": 0.03,
|
| |
|
| |
|
| | "wrong_char_rate": 0.0025,
|
| | "adjacent_key_rate": 0.002,
|
| | "double_letter_rate": 0.0015,
|
| | "skip_letter_rate": 0.01,
|
| | "transpose_rate": 0.0012,
|
| | "hesitation_rate": 0.04,
|
| | "burst_rate": 0.08,
|
| |
|
| |
|
| | "hesitation_multiplier": 3.5,
|
| | "burst_speed_multiplier": 0.5,
|
| | "post_mistake_pause": 2.0,
|
| | "word_start_slowdown": 1.5,
|
| | }
|
| |
|
| |
|
| | ADJACENT_KEYS = {
|
| | 'a': 'qwsz', 'b': 'vghn', 'c': 'xdfv', 'd': 'serfcx', 'e': 'wsdr',
|
| | 'f': 'drtgvc', 'g': 'ftyhbv', 'h': 'gyujnb', 'i': 'ujko', 'j': 'huikmn',
|
| | 'k': 'jiolm', 'l': 'kop', 'm': 'njk', 'n': 'bhjm', 'o': 'iklp',
|
| | 'p': 'ol', 'q': 'wa', 'r': 'edft', 's': 'awedxz', 't': 'rfgy',
|
| | 'u': 'yhji', 'v': 'cfgb', 'w': 'qase', 'x': 'zsdc', 'y': 'tghu',
|
| | 'z': 'asx'
|
| | }
|
| |
|
| | def get_keystroke_delay(wpm, in_burst=False):
|
| | """Calculates delay between keystrokes based on WPM."""
|
| | cpm = wpm * 5
|
| | base_delay = 10 / cpm
|
| |
|
| | if in_burst:
|
| | base_delay *= CONFIG["burst_speed_multiplier"]
|
| |
|
| |
|
| | variance = base_delay * 0.25
|
| | noise = random.uniform(-variance, variance)
|
| | return max(0.008, base_delay + noise)
|
| |
|
| | def get_adjacent_key(char):
|
| | """Returns a key adjacent to the given char on QWERTY layout."""
|
| | char_lower = char.lower()
|
| | if char_lower in ADJACENT_KEYS:
|
| | adj = random.choice(ADJACENT_KEYS[char_lower])
|
| | return adj.upper() if char.isupper() else adj
|
| | return char
|
| |
|
| | def type_char(char):
|
| | """Types a single character with human-like key hold duration."""
|
| |
|
| | try:
|
| | pyautogui.keyDown(char)
|
| | time.sleep(random.uniform(CONFIG["min_key_hold"], CONFIG["max_key_hold"]))
|
| | pyautogui.keyUp(char)
|
| | except:
|
| |
|
| | pyautogui.write(char)
|
| |
|
| | def simulate_backspace(count=1):
|
| | """Simulates pressing backspace with hold time."""
|
| | for _ in range(count):
|
| | pyautogui.keyDown('backspace')
|
| | time.sleep(random.uniform(CONFIG["min_key_hold"], CONFIG["max_key_hold"]))
|
| | pyautogui.keyUp('backspace')
|
| | time.sleep(random.uniform(0.05, 0.1))
|
| |
|
| | def mnk_typer():
|
| | print("=== MnkLightning Python Automation ===")
|
| | print("1. Copy text from Monkeytype using injector.js (Press Insert in browser)")
|
| | print("2. Focus the browser window")
|
| | print("3. Press 'Insert' to START typing")
|
| | print("4. Press 'Esc' to STOP")
|
| | print("Waiting for trigger...")
|
| |
|
| | while True:
|
| | if keyboard.is_pressed('esc'):
|
| | print("\nExiting...")
|
| | sys.exit()
|
| |
|
| | if keyboard.is_pressed('insert'):
|
| |
|
| | time.sleep(0.5)
|
| |
|
| |
|
| | text = pyperclip.paste()
|
| | if not text:
|
| | print("Clipboard empty! extraction failed?")
|
| | continue
|
| |
|
| | print(f"Typing {len(text)} characters...")
|
| |
|
| | wpm = random.randint(CONFIG["min_wpm"], CONFIG["max_wpm"])
|
| | print(f"Target WPM: {wpm}")
|
| |
|
| |
|
| | idx = 0
|
| | burst_remaining = 0
|
| |
|
| | try:
|
| | while idx < len(text):
|
| | if keyboard.is_pressed('esc'):
|
| | return
|
| |
|
| | char = text[idx]
|
| | delay = get_keystroke_delay(wpm, burst_remaining > 0)
|
| |
|
| | if burst_remaining > 0:
|
| | burst_remaining -= 1
|
| |
|
| |
|
| |
|
| |
|
| | if random.random() < CONFIG["hesitation_rate"]:
|
| | delay *= CONFIG["hesitation_multiplier"]
|
| |
|
| |
|
| | if burst_remaining == 0 and random.random() < CONFIG["burst_rate"]:
|
| | burst_remaining = 5
|
| |
|
| |
|
| | if random.random() < CONFIG["wrong_char_rate"] and char.isalnum():
|
| | wrong = random.choice("abcdefghijklmnopqrstuvwxyz")
|
| | type_char(wrong)
|
| | time.sleep(delay * 2)
|
| | simulate_backspace(1)
|
| | time.sleep(delay)
|
| |
|
| |
|
| |
|
| | elif random.random() < CONFIG["adjacent_key_rate"] and char.isalpha():
|
| | adj = get_adjacent_key(char)
|
| | type_char(adj)
|
| | time.sleep(delay * 1.5)
|
| | simulate_backspace(1)
|
| | time.sleep(delay)
|
| |
|
| |
|
| | elif random.random() < CONFIG["skip_letter_rate"] and idx + 1 < len(text):
|
| | next_char = text[idx+1]
|
| | type_char(next_char)
|
| | time.sleep(delay * 2.5)
|
| | simulate_backspace(1)
|
| | time.sleep(delay)
|
| |
|
| |
|
| |
|
| | elif random.random() < CONFIG["double_letter_rate"] and char.isalpha():
|
| | type_char(char)
|
| | time.sleep(0.05)
|
| | type_char(char)
|
| | time.sleep(delay * 2)
|
| | simulate_backspace(1)
|
| | idx += 1
|
| | time.sleep(delay)
|
| | continue
|
| |
|
| |
|
| | type_char(char)
|
| | idx += 1
|
| |
|
| |
|
| | if char == ' ':
|
| | delay *= CONFIG["word_start_slowdown"]
|
| |
|
| | time.sleep(delay)
|
| |
|
| | except KeyboardInterrupt:
|
| | pass
|
| |
|
| | print("\nTyping complete.")
|
| | time.sleep(1)
|
| |
|
| | if __name__ == "__main__":
|
| | try:
|
| | mnk_typer()
|
| | except KeyboardInterrupt:
|
| | sys.exit()
|
| |
|