An undetectable auto-typing hack with customizable speed and accuracy.
// ==UserScript==
// @name Nitro Type - Auto Type Hack (UNDETECTABLE)
// @namespace https://greasyfork.org/en/users/1586315
// @version 1.0
// @description An undetectable auto-typing hack with customizable speed and accuracy.
// @author potofgreed
// @match https://www.nitrotype.com/race
// @match https://www.nitrotype.com/race/
// @match https://www.nitrotype.com/race/*
// @icon https://cdn2.steamgriddb.com/icon/2b16a44bb65751bb0ebe5d8b42644bc4/32/512x512.png
// @grant none
// @license MIT
// ==/UserScript==
/*
===================================================================================
🛠SETUP INSTRUCTIONS
===================================================================================
To customize the WPM (Words Per Minute) and Accuracy ranges for typing sessions:
---------------------------------------------------
STEP 1: Update the configuration values
---------------------------------------------------
⬇ DO NOT edit anything in this comment block. ⬇
Scroll down and find the following lines in the code:
AppConfig.get wpmValues
AppConfig.get accuracies
You’ll see something like:
const MIN_WPM = 100;
const MAX_WPM = 125;
const MIN_ACC = 95;
const MAX_ACC = 99;
Change these `MIN_` and `MAX_` values to set your preferred WPM and accuracy ranges.
After making changes, save the file:
File > Save (or press Ctrl+S / Cmd+S)
⚠ Stick to updating the min/max values only — leave `SESSIONS` untouched.
---------------------------------------------------
STEP 2: Clear the session cache
---------------------------------------------------
WPM and accuracy values are cached in `localStorage` when sessions start.
To apply your new settings, you **must** reset the cache.
⚠ Run the following code from the browser console
**on the Nitro Type race page**, not elsewhere.
How to do it:
1. Go to nitrotype.com/race.
2. Open Developer Tools:
- Press F12 or Ctrl+Shift+I (Windows/Linux)
- Or Cmd+Option+I (Mac)
3. Go to the “Console” tab.
4. Paste and run this code:
(() => {
localStorage.setItem('typingWpmSessionCount', '1');
localStorage.setItem('typingAccuracySessionCount', '1');
localStorage.setItem('typingAccuracyBufferCount', '1');
localStorage.removeItem('dynamicWpmValues');
localStorage.removeItem('dynamicAccuracies');
window.location.reload();
})();
This clears cached values and resets session counters.
Your updated min/max settings will take effect after the page reloads.
---------------------------------------------------
Additional Notes
---------------------------------------------------
✔ All changes are local to your browser. Nitro Type won’t detect them —
unless you set unusually high or suspicious values (e.g. WPM 300+, or 100% accuracy every time).
Keep the **Console tab open** while using this setup — important debug info (like current WPM, accuracy, and mode-switch instructions) is printed there.
Mode Descriptions:
Auto Mode: Automatically simulates and injects keystrokes at the configured typing speed without user intervention.
Manual Mode: Waits for a user trigger before injecting each keystroke, giving you step-by-step control over the typing.
Normal Mode: Disables all automation hacks and behaves like a standard, unmodified typing experience.
===================================================================================
// ===== CORE CONSTANTS AND CONFIGURATION =====
/**
* Application modes with state preservation
*/
const AppModes = {
AUTO: 'auto',
MANUAL: 'manual',
NORMAL: 'normal',
STORAGE_KEY: 'typingAppMode'
};
/**
* Application-wide constants for typing parameters, events, storage keys, and behavior factors
*/
const Constants = {
TYPING: { CHARS_PER_WORD: 5, MS_PER_MINUTE: 60 * 1000 },
EVENTS: {
MODE_CHANGE: 'modeChange',
METRICS_UPDATED: 'metricsUpdated',
SESSION_COMPLETED: 'sessionCompleted',
COUNTERS_INCREMENTED: 'countersIncremented',
MANUAL_SESSION_COMPLETED: 'manualSessionCompleted',
SESSION_COMPLETED_REFRESH: 'sessionCompletedRefresh'
},
STORAGE: {
WPM_SESSION_COUNT: 'typingWpmSessionCount',
ACCURACY_SESSION_COUNT: 'typingAccuracySessionCount',
ACCURACY_BUFFER_COUNT: 'typingAccuracyBufferCount',
DYNAMIC_WPM_VALUES: 'dynamicWpmValues',
DYNAMIC_ACCURACIES: 'dynamicAccuracies'
},
BEHAVIOR: {
MIN_DELAY_FACTOR: 0.4,
MAX_DELAY_FACTOR: 1.8,
WORD_BOUNDARY_MIN: 1.05,
WORD_BOUNDARY_MAX: 0.1,
COMMON_SEQ_MIN: 0.85,
COMMON_SEQ_MAX: 0.05
}
};
/**
/**
* Centralized application configuration with target WPM, accuracy values, and typing parameters
*/
const AppConfig = {
// Target WPM values pulled directly from the ELECTRIC GUI slider
get wpmValues() {
const saved = JSON.parse(localStorage.getItem('typingBotConfig'));
const val = saved ? saved.WPM_VALUE : 100;
return Array(10).fill(parseInt(val));
},
// Target accuracy values pulled directly from the ELECTRIC GUI slider
get accuracies() {
const saved = JSON.parse(localStorage.getItem('typingBotConfig'));
const val = saved ? saved.ACCURACY_VALUE : 98;
return Array(10).fill(parseInt(val));
},
// Accuracy buffer values that cycle
accuracyBuffers: [0.1, 1.0, 1.5,2.5,2.0, 3.5, 3.0, 4,0],
// Common sequences typed faster by humans
commonSequences: ['th', 'he', 'in', 'er', 'an', 'en', 'ing', 'the', 'and'],
// Word boundary characters
wordBoundaries: [' ', '.', ',', ';', ':'],
// Parameters controlling typing behavior
typingParams: {
allowedDeviation: 6,
correctionStrength: 0.35,
humanVariationStrength: 0.85,
microPauseChance: 0.10,
burstChance: 0.04,
hesitationChance: 0.10,
maxMicroPause: 120,
feedbackInterval: 700,
wpmSmoothingFactor: 0.65,
overcompensationFactor: 0.8,
speedBoost: 1.05,
adaptationRate: 0.35,
earlyBoostFactor: 1.12
}
};
/**
* Simple event system to enable communication between components
*/
class EventBus {
constructor() {
this.events = {};
}
// Register an event listener
on(event, listener) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(listener);
return this;
}
// Emit an event to all listeners
emit(event, ...args) {
if (!this.events[event]) return false;
this.events[event].forEach(listener => listener(...args));
return true;
}
// Remove an event listener
off(event, listener) {
if (!this.events[event]) return this;
this.events[event] = this.events[event].filter(l => l !== listener);
return this;
}
}
/**
* Centralized logging with consistent message formatting
*/
class Logger {
// Shared logging method to standardize format
static _log(prefix, message, context = '') {
const contextPrefix = context ? `[${context}] ` : '';
console.log(`${contextPrefix}${prefix}${message}`);
}
// Standard logging methods with different prefixes
static log(message, context = '') {
this._log('', message, context);
}
static info(message, context = '') {
this._log('', message, context);
}
static warn(message, context = '') {
this._log('⚠️ ', message, context);
}
static error(message, context = '') {
console.error(`${context ? `[${context}] ` : ''}🔴 ${message}`);
}
static success(message, context = '') {
this._log('✅ ', message, context);
}
// Helper for logging metrics in consistent format
static logMetric(name, value, target = null, context = '') {
this.info(`${name}: ${Utils.formatNumber(value)}${target !== null ? ` (Target: ${target})` : ''}`, context);
}
// Helper for logging session info in consistent format
static logSessionInfo(config, context = '') {
this.info(`Session info: WPM #${config.wpmSessionCount}/${AppConfig.wpmValues.length} (${config.targetWPM} WPM) | Accuracy #${config.accuracySessionCount}/${AppConfig.accuracies.length} (${config.targetAccuracy}%)`, context);
}
}
/**
* Handles localStorage operations to persist session data
*/
class StorageService {
// Get value from localStorage with default fallback
static get(key, defaultValue) {
return parseInt(localStorage.getItem(key) || defaultValue.toString());
}
// Set value in localStorage
static set(key, value) {
localStorage.setItem(key, value);
}
/**
* Increments a counter in localStorage, cycling back to 1 when reaching max
*/
static increment(key, maxValue, defaultValue = 0) {
let count = this.get(key, defaultValue) + 1;
if (count > maxValue) {
Logger.info(`Completed full ${key} cycle. Resetting counter (was at ${count})`);
count = 1;
}
this.set(key, count);
return count;
}
}
/**
* General utilities for common operations
*/
class Utils {
// Create a promise that resolves after specific time
static delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Analyze content to find the longest word and its position
static getLongestWord(content) {
const words = content.split(/\s+/);
let longest = { word: "", index: 0 };
for (let i = 0; i < words.length; i++) {
if (words[i].length > longest.word.length) {
longest.word = words[i];
longest.index = content.indexOf(longest.word);
}
}
return longest;
}
// Format number with specified decimal places
static formatNumber(num, decimals = 2) {
if (num === undefined || num === null) return "0.00";
return num.toFixed(decimals);
}
// Apply random variation within a range
static applyRandomVariation(base, minFactor, maxVariation) {
return base * (minFactor + Math.random() * maxVariation);
}
// Check if string starts with any pattern from array
static startsWithAny(str, patterns) {
return patterns.some(pattern => str.startsWith(pattern));
}
// Check if character is in array
static isInArray(char, array) {
return array.includes(char);
}
// Keep value within bounds
static boundValue(value, min, max) {
return Math.max(min, Math.min(max, value));
}
// Retry operation until success
static async retryUntilSuccess(operation, interval = 500) {
if (operation()) return true;
return new Promise(resolve => {
const checkInterval = setInterval(() => {
if (operation()) {
clearInterval(checkInterval);
resolve(true);
}
}, interval);
});
}
// Apply a pattern if condition is met, otherwise return null
static applyPatternIf(condition, factorFn, baseFactor) {
if (condition) return factorFn(baseFactor);
return null;
}
}
// ===== CONFIGURATION =====
/**
* Manages configuration and session state
*/
class ConfigService {
constructor(eventBus) {
this.eventBus = eventBus;
this.loadConfig();
}
// Load configuration values from storage
loadConfig() {
const savedMode = localStorage.getItem(AppModes.STORAGE_KEY) || AppModes.AUTO;
this._typingMode = savedMode;
this._accuracySessionCount = StorageService.get(Constants.STORAGE.ACCURACY_SESSION_COUNT, 1);
this._wpmSessionCount = StorageService.get(Constants.STORAGE.WPM_SESSION_COUNT, 1);
this._accuracyBufferCount = StorageService.get(Constants.STORAGE.ACCURACY_BUFFER_COUNT, 1);
this.typingParams = AppConfig.typingParams;
}
// Getters and setters for the typing mode
get typingMode() {
return this._typingMode;
}
set typingMode(value) {
this._typingMode = value;
localStorage.setItem(AppModes.STORAGE_KEY, value);
this.eventBus.emit(Constants.EVENTS.MODE_CHANGE, value);
}
// Helper methods to check specific modes
get isAutoTypingMode() {
return this._typingMode === AppModes.AUTO;
}
get isManualTypingMode() {
return this._typingMode === AppModes.MANUAL;
}
get isNormalTypingMode() {
return this._typingMode === AppModes.NORMAL;
}
// Return the target WPM for current session
get targetWPM() {
return AppConfig.wpmValues[this._wpmSessionCount - 1];
}
// Return the target accuracy for current session
get targetAccuracy() {
return AppConfig.accuracies[this._accuracySessionCount - 1];
}
// Return the target accuracy buffer for current session
get targetAccuracyBuffer() {
return AppConfig.accuracyBuffers[this._accuracyBufferCount - 1];
}
get wpmSessionCount() {
return this._wpmSessionCount;
}
get accuracySessionCount() {
return this._accuracySessionCount;
}
get accuracyBufferCount() {
return this._accuracyBufferCount;
}
// Increment session counters and emit event
incrementCounters() {
// Skip incrementing counters in normal mode
if (this.isNormalTypingMode) {
return {
wpmSession: this._wpmSessionCount,
accuracySession: this._accuracySessionCount,
accuracyBufferSession: this._accuracyBufferCount
};
}
// Increment all counters with a helper method
this._wpmSessionCount = this._incrementCounter(
Constants.STORAGE.WPM_SESSION_COUNT,
AppConfig.wpmValues.length
);
this._accuracySessionCount = this._incrementCounter(
Constants.STORAGE.ACCURACY_SESSION_COUNT,
AppConfig.accuracies.length
);
this._accuracyBufferCount = this._incrementCounter(
Constants.STORAGE.ACCURACY_BUFFER_COUNT,
AppConfig.accuracyBuffers.length
);
// If we've wrapped back to the first WPM session, clear cached arrays so they're regenerated
if (this._wpmSessionCount === 1) {
localStorage.removeItem(Constants.STORAGE.DYNAMIC_WPM_VALUES);
localStorage.removeItem(Constants.STORAGE.DYNAMIC_ACCURACIES);
}
const counters = {
wpmSession: this._wpmSessionCount,
accuracySession: this._accuracySessionCount,
accuracyBufferSession: this._accuracyBufferCount
};
this.eventBus.emit(Constants.EVENTS.COUNTERS_INCREMENTED, counters);
return counters;
}
// Helper method to increment a counter
_incrementCounter(key, maxValue) {
return StorageService.increment(key, maxValue);
}
}
// ===== DOM INTERFACE =====
/**
* Handles DOM interactions and provides access to typing app node
*/
class DOMInterface {
// Extract typing app node from the DOM
getTypingAppNode() {
try {
return Object.values(document.querySelector("div.dash-copyContainer"))[1].children._owner.stateNode;
} catch (error) {
Logger.error(`Could not access typing app node: ${error}`, 'DOMInterface');
return null;
}
}
}
// ===== METRICS TRACKING =====
/**
* Tracks and calculates typing metrics and performance
*/
class MetricsService {
constructor(configService, eventBus) {
this.config = configService;
this.eventBus = eventBus;
this.reset();
}
// Reset all metrics to initial values
reset() {
this.totalKeystrokes = 0;
this.correctKeystrokes = 0;
this.typingStartTime = null;
this.charactersTyped = 0;
this.lastFeedbackTime = 0;
this.currentWPM = 0;
this.smoothedWPM = 0;
// Calculate target CPM and base delay
this.targetCPM = this.config.targetWPM * Constants.TYPING.CHARS_PER_WORD;
this.baseDelayMs = (Constants.TYPING.MS_PER_MINUTE) / this.targetCPM * this.config.typingParams.speedBoost;
this.currentDelayMs = this.baseDelayMs;
// Tracking variables
this.typingPattern = [];
this.lastKeystrokeTime = 0;
this.cumulativeDeviation = 0;
this.wpmHistory = [];
this.adjustmentHistory = [];
this.delayOffset = 0;
// Startup phase tracking
this.startupPhaseDone = false;
this.startupCounter = 0;
this.initialBoostApplied = false;
this.sessionCompleted = false;
}
// Calculate current typing accuracy
get currentAccuracy() {
return this.totalKeystrokes > 0 ? (this.correctKeystrokes / this.totalKeystrokes) * 100 : 100;
}
// Update keystroke stats (correct or incorrect)
updateKeystrokeStats(isCorrect) {
this.totalKeystrokes++;
if (isCorrect) this.correctKeystrokes++;
this.emitMetricsUpdate();
}
// Track a correct keystroke
trackCorrectKeystroke() {
this.updateKeystrokeStats(true);
}
// Track an incorrect keystroke
trackIncorrectKeystroke() {
this.updateKeystrokeStats(false);
}
// Update WPM calculation based on typing progress
updateWPM(newChars) {
// Initialize start time on first update
if (this.typingStartTime === null) {
this.typingStartTime = Date.now();
this.lastKeystrokeTime = Date.now();
return 0;
}
this.charactersTyped += newChars;
const currentTime = Date.now();
const elapsedMinutes = (currentTime - this.typingStartTime) / Constants.TYPING.MS_PER_MINUTE;
// Calculate WPM if enough time has passed
if (elapsedMinutes > 0) {
// Calculate instant WPM and apply smoothing
const instantWPM = (this.charactersTyped / Constants.TYPING.CHARS_PER_WORD) / elapsedMinutes;
this.smoothedWPM = this.smoothedWPM === 0
? instantWPM
: this.smoothedWPM * this.config.typingParams.wpmSmoothingFactor +
instantWPM * (1 - this.config.typingParams.wpmSmoothingFactor);
this.currentWPM = this.smoothedWPM;
this._updateWpmHistory(currentTime);
return this.currentWPM;
}
return 0;
}
// Update WPM history and provide feedback
_updateWpmHistory(currentTime) {
// Maintain a rolling window of recent WPM values
if (this.wpmHistory.length > 10) this.wpmHistory.shift();
this.wpmHistory.push(this.currentWPM);
// Provide periodic feedback on typing speed
if (currentTime - this.lastFeedbackTime > this.config.typingParams.feedbackInterval) {
Logger.logMetric('Current typing speed', this.currentWPM, this.config.targetWPM, 'WPM');
this.lastFeedbackTime = currentTime;
this._handleSpeedDeviation();
}
this._handleStartupPhase();
}
// Handle deviations from target WPM
_handleSpeedDeviation() {
const deviation = this.currentWPM - this.config.targetWPM;
const deviationPercent = Utils.formatNumber(Math.abs(deviation) / this.config.targetWPM * 100, 1);
if (Math.abs(deviation) > this.config.typingParams.allowedDeviation) {
if (deviation < 0) {
Logger.warn(`TOO SLOW: ${Utils.formatNumber(Math.abs(deviation), 1)} WPM below target (${deviationPercent}%) - Increasing typing speed...`, 'WPM');
} else {
Logger.warn(`TOO FAST: ${Utils.formatNumber(deviation, 1)} WPM above target (${deviationPercent}%) - Reducing typing speed...`, 'WPM');
}
this._adjustForConsistentDeviation(deviation);
}
}
// Make adjustments for consistent deviations
_adjustForConsistentDeviation(deviation) {
if (this.wpmHistory.length >= 5) {
const recentAvg = this.wpmHistory.slice(-5).reduce((sum, wpm) => sum + wpm, 0) / 5;
const avgDeviation = recentAvg - this.config.targetWPM;
if (Math.abs(avgDeviation) > this.config.typingParams.allowedDeviation) {
const adjustmentFactor = -avgDeviation * 0.015;
this.delayOffset += adjustmentFactor;
Logger.info(`🔄 Making permanent base delay adjustment: ${Utils.formatNumber(adjustmentFactor, 3)}ms (cumulative: ${Utils.formatNumber(this.delayOffset, 3)}ms)`, 'WPM');
}
}
}
// Handle special adjustments during startup phase
_handleStartupPhase() {
if (!this.startupPhaseDone) {
this.startupCounter++;
// Apply initial boost if typing too slow
if (this.startupCounter === 10 && !this.initialBoostApplied) {
if (this.currentWPM < this.config.targetWPM * 0.9) {
const boostFactor = 0.7; // 30% faster typing
this.baseDelayMs *= boostFactor;
this.currentDelayMs *= boostFactor;
this.initialBoostApplied = true;
Logger.info(`🚀 Initial speed boost applied! Base delay reduced to ${Utils.formatNumber(this.baseDelayMs)}ms`, 'WPM');
}
}
// End startup phase after sufficient keystrokes
if (this.startupCounter >= 30) {
this.startupPhaseDone = true;
Logger.info("Startup phase complete, switching to normal control mode", 'WPM');
}
}
}
// Emit metrics updates and log feedback
emitMetricsUpdate() {
// Log accuracy periodically
if (this.totalKeystrokes % 20 === 0) {
this._logAccuracyFeedback();
}
this.eventBus.emit(Constants.EVENTS.METRICS_UPDATED, {
accuracy: this.currentAccuracy,
wpm: this.currentWPM,
keystrokes: this.totalKeystrokes,
correctKeystrokes: this.correctKeystrokes,
accuracyBuffer: this.config.targetAccuracyBuffer
});
}
// Log accuracy feedback in consistent format
_logAccuracyFeedback() {
Logger.logMetric('Current accuracy', this.currentAccuracy, this.config.targetAccuracy, 'Accuracy');
Logger.info(`(${this.correctKeystrokes}/${this.totalKeystrokes})`, 'Accuracy');
}
// Mark session as complete and emit event
markSessionComplete() {
if (this.sessionCompleted) return;
this.sessionCompleted = true;
this.eventBus.emit(Constants.EVENTS.SESSION_COMPLETED, {
finalWPM: this.currentWPM || 0,
finalAccuracy: this.currentAccuracy || 0
});
}
// Log history of WPM adjustments
logAdjustmentHistory() {
Logger.info("WPM and delay adjustment history:", 'Metrics');
this.adjustmentHistory.forEach((item, i) => {
Logger.info(`${i}: WPM=${Utils.formatNumber(item.wpm, 1)}, Target=${item.targetWpm}, Delay=${Utils.formatNumber(item.adjustedDelay, 1)}ms`, 'Metrics');
});
}
}
// ===== TIMING CONTROLLER =====
/**
* Controls timing between keystrokes to achieve target WPM
*/
class TimingController {
constructor(metricsService, configService) {
this.metrics = metricsService;
this.config = configService;
}
/**
* Calculate delay for next keystroke based on current metrics
*/
calculateNextDelay(content, typedIndex) {
const now = Date.now();
let delay = this.metrics.baseDelayMs;
// Apply early boost during startup
if (!this.metrics.startupPhaseDone) {
delay *= this.config.typingParams.earlyBoostFactor;
}
// Adjust based on current WPM
if (this.metrics.charactersTyped > 5 && this.metrics.currentWPM > 0) {
delay = this._calculateSpeedAdjustedDelay(delay);
}
// Apply human-like variations
const humanFactor = this._applyHumanTypingPatterns(content, typedIndex);
delay *= humanFactor;
// Keep delay within reasonable bounds
delay = Utils.boundValue(
delay,
this.metrics.baseDelayMs * Constants.BEHAVIOR.MIN_DELAY_FACTOR,
this.metrics.baseDelayMs * Constants.BEHAVIOR.MAX_DELAY_FACTOR
);
this._updateDelayHistory(delay);
this.metrics.currentDelayMs = delay;
this.metrics.lastKeystrokeTime = now;
return delay;
}
/**
* Calculate delay adjustments based on WPM difference
*/
_calculateSpeedAdjustedDelay(baseDelay) {
const wpmDifference = this.metrics.currentWPM - this.config.targetWPM;
let correctionFactor = wpmDifference * this.config.typingParams.correctionStrength;
// Apply stronger correction when below target
if (wpmDifference < 0) {
correctionFactor *= this.config.typingParams.overcompensationFactor;
}
// Apply correction and offset
let delay = baseDelay * (1 + correctionFactor / 10);
delay += this.metrics.delayOffset;
// Track cumulative deviation
this.metrics.cumulativeDeviation += wpmDifference * this.config.typingParams.adaptationRate;
return this._applyTrendCorrections(delay, wpmDifference);
}
/**
* Apply corrections based on observed typing trends
*/
_applyTrendCorrections(delay, wpmDifference) {
// Apply cumulative corrections periodically
if (this.metrics.charactersTyped % 3 === 0 && Math.abs(this.metrics.cumulativeDeviation) > 0.5) {
delay *= (1 + this.metrics.cumulativeDeviation * 0.02);
// Prevent overcompensation
if (Math.abs(this.metrics.cumulativeDeviation) > 3) {
this.metrics.cumulativeDeviation *= 0.7;
}
}
// Analyze recent WPM trends
if (this.metrics.wpmHistory.length >= 3) {
const trend = this.metrics.wpmHistory.slice(-3);
const avgTrend = trend.reduce((sum, wpm) => sum + wpm, 0) / trend.length;
const trendDeviation = avgTrend - this.config.targetWPM;
// If trend consistently deviates, apply stronger correction
if (Math.abs(trendDeviation) > this.config.typingParams.allowedDeviation &&
Math.sign(trendDeviation) === Math.sign(wpmDifference)) {
delay *= (1 - (trendDeviation * 0.01));
}
}
return delay;
}
/**
* Apply human-like variations to typing rhythm
*/
_applyHumanTypingPatterns(content, typedIndex) {
// Base human factor with random variation
let humanFactor = 0.9 + (Math.random() * 0.2);
const pattern = Math.random();
const params = this.config.typingParams;
// Apply pattern-based variations (micro-pause, burst, hesitation)
const microPause = Utils.applyPatternIf(
pattern < params.microPauseChance,
(factor) => factor * (1 + Math.random() * 0.3),
humanFactor
);
if (microPause !== null) return microPause;
const burst = Utils.applyPatternIf(
pattern < params.microPauseChance + params.burstChance,
(factor) => factor * (0.75 + Math.random() * 0.1),
humanFactor
);
if (burst !== null) return burst;
const hesitation = Utils.applyPatternIf(
pattern < params.microPauseChance + params.burstChance + params.hesitationChance,
(factor) => factor * (1.1 + Math.random() * 0.4),
humanFactor
);
if (hesitation !== null) return hesitation;
// Apply content-based variations (word boundaries, common sequences)
const currentChar = content[typedIndex];
const wordBoundary = Utils.applyPatternIf(
Utils.isInArray(currentChar, AppConfig.wordBoundaries),
(factor) => Utils.applyRandomVariation(
factor,
Constants.BEHAVIOR.WORD_BOUNDARY_MIN,
Constants.BEHAVIOR.WORD_BOUNDARY_MAX
),
humanFactor
);
if (wordBoundary !== null) return wordBoundary;
let nextFewChars = content.slice(typedIndex, typedIndex + 3);
const commonSeq = Utils.applyPatternIf(
Utils.startsWithAny(nextFewChars, AppConfig.commonSequences),
(factor) => Utils.applyRandomVariation(
factor,
Constants.BEHAVIOR.COMMON_SEQ_MIN,
Constants.BEHAVIOR.COMMON_SEQ_MAX
),
humanFactor
);
if (commonSeq !== null) return commonSeq;
return humanFactor;
}
/**
* Update delay history for analysis
*/
_updateDelayHistory(delay) {
// Maintain limited history of patterns
if (this.metrics.typingPattern.length > 15) this.metrics.typingPattern.shift();
this.metrics.typingPattern.push(delay);
// Keep adjustment history for analysis
if (this.metrics.adjustmentHistory.length > 10) this.metrics.adjustmentHistory.shift();
this.metrics.adjustmentHistory.push({
wpm: this.metrics.currentWPM,
targetWpm: this.config.targetWPM,
baseDelay: this.metrics.baseDelayMs,
adjustedDelay: delay
});
}
/**
* Add random micro-pauses for human-like typing
*/
async addMicroPause() {
if (Math.random() < 0.07) {
const pauseTime = Math.random() * this.config.typingParams.maxMicroPause;
return Utils.delay(pauseTime);
}
return Promise.resolve();
}
}
// ===== ACCURACY CONTROLLER =====
/**
* Controls typing accuracy to match target percentage
*/
class AccuracyController {
constructor(metricsService, configService) {
this.metrics = metricsService;
this.config = configService;
// Use the cyclic buffer value
this.accuracyBuffer = this.config.targetAccuracyBuffer;
}
/**
* Get the current accuracy buffer for logging purposes
*/
getBufferInfo() {
return `+${Utils.formatNumber(this.accuracyBuffer, 2)}%`;
}
/**
* Determine if an error should be made to maintain target accuracy
*/
shouldMakeError() {
// Don't make errors at the beginning
if (this.metrics.totalKeystrokes === 0) return false;
// Target accuracy with buffer
const targetWithBuffer = this.config.targetAccuracy + this.accuracyBuffer;
// Dynamic error probability based on current vs. target accuracy
if (this.metrics.currentAccuracy > targetWithBuffer) {
return true; // More likely to make errors when above target
} else if (this.metrics.currentAccuracy < targetWithBuffer) {
return false; // Less likely when below target
}
// When at target accuracy, error probability matches adjusted target
return Math.random() > (targetWithBuffer / 100);
}
}
// ===== KEYBOARD HANDLER =====
/**
* Handles keyboard input and simulates keystrokes
*/
class KeyboardHandler {
constructor(metricsService, configService, eventBus) {
this.metrics = metricsService;
this.config = configService;
this.eventBus = eventBus;
}
/**
* Process keystroke and update metrics
*/
processKeystroke(appNode, makeCorrect) {
// Update metrics based on keystroke correctness
if (makeCorrect) {
this.metrics.trackCorrectKeystroke();
if (this.config.isAutoTypingMode) {
this.metrics.updateWPM(1);
}
} else {
this.metrics.trackIncorrectKeystroke();
}
// Simulate keystroke in the app
appNode.handleKeyPress("character", new KeyboardEvent("keypress", {
key: makeCorrect ? appNode.props.lessonContent[appNode.typedIndex] : "$"
}));
this._checkSessionComplete(appNode);
}
/**
* Simulate pressing Enter key
*/
simulateEnterKey(appNode) {
appNode.handleKeyPress("character", new KeyboardEvent("keypress", { key: "\n" }));
this._checkSessionComplete(appNode);
}
/**
* Check if session is complete in manual mode
*/
_checkSessionComplete(appNode) {
if (!this.config.isAutoTypingMode && this._isSessionComplete(appNode)) {
this.handleSessionCompletion();
}
}
/**
* Check if typing session is complete
*/
_isSessionComplete(appNode) {
return appNode.typedIndex >= appNode.props.lessonContent.length;
}
/**
* Handle completion of a manual typing session
*/
handleSessionCompletion() {
this.metrics.markSessionComplete();
this.eventBus.emit(Constants.EVENTS.MANUAL_SESSION_COMPLETED);
}
/**
* Create key handlers for different typing modes
*/
createKeyHandlers(appNode, originalKeyHandler, accuracyController, longestWord) {
return {
// Normal typing mode handler
createNormalHandler: () => {
return this._createNormalKeyHandler(appNode, originalKeyHandler, accuracyController, longestWord);
},
// Countdown phase handler
createCountdownHandler: () => {
return this._createCountdownKeyHandler(originalKeyHandler);
}
};
}
/**
* Create normal typing mode key handler
*/
_createNormalKeyHandler(appNode, originalKeyHandler, accuracyController, longestWord) {
return (e, n) => {
if ("character" === e) {
if (appNode.typedIndex === longestWord.index) {
this.simulateEnterKey(appNode);
return;
}
this.processKeystroke(appNode, !accuracyController.shouldMakeError());
} else if (n.key === "\n") {
this.simulateEnterKey(appNode);
} else {
return originalKeyHandler(e, n);
}
};
}
/**
* Create countdown phase key handler
*/
_createCountdownKeyHandler(originalKeyHandler) {
return (e, n) => {
// Toggle modes with different keys: 'a' for auto, 'm' for manual, 'n' for normal
if ("character" === e) {
if (n.key === "a") {
this.config.typingMode = AppModes.AUTO;
Logger.info(`Mode switched to: AUTO typing`, 'KeyboardHandler');
return;
} else if (n.key === "m") {
this.config.typingMode = AppModes.MANUAL;
Logger.info(`Mode switched to: MANUAL typing`, 'KeyboardHandler');
return;
} else if (n.key === "n") {
this.config.typingMode = AppModes.NORMAL;
Logger.info(`Mode switched to: NORMAL typing (script disabled)`, 'KeyboardHandler');
return;
}
}
// Pass through specific keys
if (n.key >= '1' && n.key <= '8' || n.key === 'Shift' || n.key === 'Control') {
return originalKeyHandler(e, n);
}
};
}
}
// ===== SESSION MANAGER =====
/**
* Manages typing session lifecycle and transitions
*/
class SessionManager {
constructor(configService, metricsService, eventBus) {
this.config = configService;
this.metrics = metricsService;
this.eventBus = eventBus;
this.isHandlingCompletion = false;
// Subscribe to session completion event
this.eventBus.on(Constants.EVENTS.SESSION_COMPLETED, data => {
if (!this.isHandlingCompletion) {
this.handleSessionCompleted(data);
}
});
}
/**
* Handle session completion and report results
*/
handleSessionCompleted(sessionData) {
this.isHandlingCompletion = true;
// Save target values before incrementing
const currentTargetWPM = this.config.targetWPM;
const currentTargetAccuracy = this.config.targetAccuracy;
// Get final metrics
const finalWPM = sessionData?.finalWPM || 0;
const finalAccuracy = sessionData?.finalAccuracy || 0;
// Log completion results
this._logSessionResults(finalWPM, finalAccuracy, currentTargetWPM, currentTargetAccuracy);
// Increment counters for next session
const newCounters = this.config.incrementCounters();
Logger.info(`New WPM session: ${newCounters.wpmSession}/${AppConfig.wpmValues.length} | New Accuracy session: ${newCounters.accuracySession}/${AppConfig.accuracies.length} | New Buffer session: ${newCounters.accuracyBufferSession}/${AppConfig.accuracyBuffers.length}`, 'SessionManager');
// Log additional metrics in auto mode
if (this.config.isAutoTypingMode) {
this.metrics.logAdjustmentHistory();
}
Logger.info("Waiting 4 seconds before refreshing page...", 'SessionManager');
// Emit refresh notification event
this.eventBus.emit(Constants.EVENTS.SESSION_COMPLETED_REFRESH, {
isAutoMode: this.config.isAutoTypingMode,
isManualMode: this.config.isManualTypingMode,
isNormalMode: this.config.isNormalTypingMode,
nextWpmSession: newCounters.wpmSession,
nextAccuracySession: newCounters.accuracySession,
nextBufferSession: newCounters.accuracyBufferSession
});
// Refresh page after delay
setTimeout(() => window.location.reload(), 4000);
}
/**
* Log session completion results
*/
_logSessionResults(finalWPM, finalAccuracy, targetWPM, targetAccuracy) {
if (this.config.isAutoTypingMode) {
Logger.success("Auto-typing completed!", 'SessionManager');
Logger.logMetric('Final WPM', finalWPM, targetWPM, 'SessionManager');
} else {
Logger.success("Manual typing session completed!", 'SessionManager');
}
Logger.logMetric('Final accuracy', finalAccuracy, targetAccuracy, 'SessionManager');
}
}
// ===== AUTO-TYPER =====
/**
* Main controller for auto-typing functionality
*/
class AutoTyper {
constructor(services) {
this.dom = services.dom;
this.config = services.config;
this.metrics = services.metrics;
this.timingController = services.timingController;
this.accuracyController = services.accuracyController;
this.keyboardHandler = services.keyboardHandler;
this.eventBus = services.eventBus;
this.sessionManager = services.sessionManager;
}
/**
* Initialize auto-typer and wait for page to load
*/
async init() {
Logger.info("Waiting for page to fully load...", 'AutoTyper');
// Retry initialization until typing app is ready
await Utils.retryUntilSuccess(() => {
const result = this.initTypingScript();
if (result) {
Logger.info("Script is now running!", 'AutoTyper');
}
return result;
}, 500);
}
/**
* Set up typing script once page is loaded
*/
initTypingScript() {
try {
this.appNode = this.dom.getTypingAppNode();
if (!this.appNode) return false;
// Get content and analyze
const content = this.appNode.props.lessonContent;
this.longestWord = Utils.getLongestWord(content);
this.originalKeyHandler = this.appNode.input.keyHandler;
// Log setup information
Logger.info("Script successfully loaded", 'AutoTyper');
Logger.logSessionInfo(this.config, 'AutoTyper');
Logger.info(`Accuracy target buffer: ${this.accuracyController.getBufferInfo()} (Session ${this.config.accuracyBufferCount}/${AppConfig.accuracyBuffers.length})`, 'AutoTyper');
Logger.info(`Longest word detected: '${this.longestWord.word}' at position ${this.longestWord.index}`, 'AutoTyper');
let modeDisplay = "UNKNOWN";
if (this.config.isAutoTypingMode) modeDisplay = "AUTO";
else if (this.config.isManualTypingMode) modeDisplay = "MANUAL";
else if (this.config.isNormalTypingMode) modeDisplay = "NORMAL (script disabled)";
Logger.info(`Mode: ${modeDisplay} typing`, 'AutoTyper');
this.startTypingSession();
return true;
} catch (error) {
Logger.info("Content not ready yet, retrying...", 'AutoTyper');
return false;
}
}
/**
* Start typing session with countdown and mode selection
*/
async startTypingSession() {
// Set up key handlers
const keyHandlers = this.keyboardHandler.createKeyHandlers(
this.appNode,
this.originalKeyHandler,
this.accuracyController,
this.longestWord
);
// Start with countdown handler for initial setup
this.appNode.input.keyHandler = keyHandlers.createCountdownHandler();
Logger.info("Press: 'a' for AUTO typing, 'm' for MANUAL typing, 'n' for NORMAL typing", 'AutoTyper');
Logger.info("Countdown started. Auto-typing will begin in 4 seconds...", 'AutoTyper');
await Utils.delay(4000);
// If in normal mode, use the original key handler and return
if (this.config.isNormalTypingMode) {
Logger.info("Starting in NORMAL mode. Script disabled.", 'AutoTyper');
this.appNode.input.keyHandler = this.originalKeyHandler;
return;
}
// Switch to normal typing handler after countdown
this.appNode.input.keyHandler = keyHandlers.createNormalHandler();
// Start appropriate typing mode
this._startTypingMode();
}
/**
* Start appropriate typing mode with proper logging
*/
_startTypingMode() {
if (this.config.isAutoTypingMode) {
Logger.info(`Starting auto-typing - Target WPM: ${this.config.targetWPM} (Session ${this.config.wpmSessionCount}/${AppConfig.wpmValues.length})`, 'AutoTyper');
Logger.info(`Base delay between keystrokes: ${Utils.formatNumber(this.metrics.baseDelayMs)}ms (with ${this.config.typingParams.speedBoost.toFixed(2)}x speed boost)`, 'AutoTyper');
this._runAutoTyper();
} else if (this.config.isManualTypingMode) {
Logger.info(`Starting manual typing - Target accuracy: ${this.config.targetAccuracy}% (Session ${this.config.accuracySessionCount}/${AppConfig.accuracies.length})`, 'AutoTyper');
}
}
/**
* Run auto-typing process character by character
*/
async _runAutoTyper() {
let currentIndex = 0;
let isTyping = true;
// Initialize WPM tracking
this.metrics.typingStartTime = Date.now();
this.metrics.lastKeystrokeTime = Date.now();
/**
* Type next character with appropriate timing
*/
const typeNextCharacter = async () => {
// Stop if typing is cancelled or complete
if (!isTyping || !this.config.isAutoTypingMode || this.config.isNormalTypingMode || this._isSessionComplete(currentIndex)) {
if (this._isSessionComplete(currentIndex)) {
this.metrics.markSessionComplete();
}
return;
}
// Calculate delay for natural typing rhythm
const delay = this.timingController.calculateNextDelay(
this.appNode.props.lessonContent,
this.appNode.typedIndex
);
// Add random micro-pauses for realism
await this.timingController.addMicroPause();
setTimeout(() => {
if (!this.config.isAutoTypingMode || this.config.isNormalTypingMode) return;
// Handle special case for longest word (press Enter)
if (currentIndex === this.longestWord.index) {
this.keyboardHandler.simulateEnterKey(this.appNode);
currentIndex++;
typeNextCharacter();
return;
}
// Determine whether to make an error
const makeError = this.accuracyController.shouldMakeError();
this.keyboardHandler.processKeystroke(this.appNode, !makeError);
// Only advance index on correct keystrokes
if (!makeError) {
currentIndex++;
}
// Continue typing next character
typeNextCharacter();
}, delay);
};
// Start the typing process
typeNextCharacter();
}
/**
* Check if typing session is complete
*/
_isSessionComplete(currentIndex) {
return currentIndex >= this.appNode.props.lessonContent.length;
}
}
// ===== SERVICE CONTAINER =====
/**
* Manages service instances and dependencies
*/
class ServiceContainer {
constructor() {
this.services = {};
}
/**
* Register a service in the container
*/
register(name, service) {
this.services[name] = service;
return this;
}
/**
* Retrieve a service from the container
*/
get(name) {
return this.services[name];
}
/**
* Create and register all services with dependencies
*/
createServices() {
// Create core services
const eventBus = new EventBus();
this.register('eventBus', eventBus);
const configService = new ConfigService(eventBus);
this.register('config', configService);
const domService = new DOMInterface();
this.register('dom', domService);
// Create dependent services
const metricsService = new MetricsService(configService, eventBus);
this.register('metrics', metricsService);
const timingController = new TimingController(metricsService, configService);
this.register('timingController', timingController);
const accuracyController = new AccuracyController(metricsService, configService);
this.register('accuracyController', accuracyController);
const keyboardHandler = new KeyboardHandler(metricsService, configService, eventBus);
this.register('keyboardHandler', keyboardHandler);
const sessionManager = new SessionManager(configService, metricsService, eventBus);
this.register('sessionManager', sessionManager);
// Create main application controller
const autoTyper = new AutoTyper(this.services);
this.register('autoTyper', autoTyper);
return this;
}
}
// ===== MAIN APP =====
/**
* Main application entry point
*/
class TypingApp {
constructor() {
// Create and initialize services
this.container = new ServiceContainer().createServices();
this.autoTyper = this.container.get('autoTyper');
}
/**
* Start the typing application
*/
async start() {
await this.autoTyper.init();
}
}
// ===== INITIALIZE THE APP =====
/**
* Self-executing function to start the application
*/
(function() {
const app = new TypingApp();
app.start();
})(); /* =============== GUI OVERLAY - ELECTRIC SPECIAL (REPAIRED)
========================================================== */
const createElectricGUI = () => {
// 1. Create the Styles (Fixed ID to match Electric)
const style = document.createElement('style');
style.innerHTML = `
#electric-gui-container {
position: fixed;
top: 20px;
left: 20px;
width: 260px;
background: #000000; /* Deep Blue */
color: #c9d1d9;
border: 2px solid #00ceff; /* Electric Blue */
border-radius: 12px;
padding: 15px;
z-index: 10000;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
box-shadow: 0 0 20px rgba(88, 166, 255, 0.3);
}
.gui-header {
font-size: 18px;
font-weight: 900;
color: #58a6ff;
margin-bottom: 15px;
text-align: center;
border-bottom: 1px solid #30363d;
padding-bottom: 8px;
text-transform: uppercase;
}
.gui-row {
margin-bottom: 15px;
}
.gui-label {
display: flex;
justify-content: space-between;
font-size: 12px;
margin-bottom: 5px;
color: #8b949e;
}
.gui-val {
color: #39d353; /* Bright Green numbers */
font-weight: bold;
}
input[type=range] {
width: 100%;
cursor: pointer;
accent-color: #58a6ff;
}
.gui-apply-btn {
width: 100%;
background: #1f6feb;
color: #ffffff;
border: none;
padding: 10px;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
transition: 0.2s;
text-transform: uppercase;
}
.gui-apply-btn:hover {
background: #58a6ff;
}
`;
document.head.appendChild(style);
// 2. Create the HTML Structure (ID matches CSS)
const guiHtml = `
<div id="electric-gui-container">
<div class="gui-header"></div>
<div class="gui-row">
<div class="gui-label">Target WPM <span id="wpm-display" class="gui-val">100</span></div>
<input type="range" id="wpm-slider" min="40" max="220" value="100">
</div>
<div class="gui-row">
<div class="gui-label">Accuracy % <span id="acc-display" class="gui-val">98%</span></div>
<input type="range" id="acc-slider" min="80" max="100" value="98">
</div>
<button class="gui-apply-btn" id="gui-apply">Save</button>
</div>
`;
document.body.insertAdjacentHTML('beforeend', guiHtml);
// 3. Interface Interaction
const wpmS = document.getElementById('wpm-slider');
const accS = document.getElementById('acc-slider');
const wpmD = document.getElementById('wpm-display');
const accD = document.getElementById('acc-display');
wpmS.oninput = () => wpmD.innerText = wpmS.value;
accS.oninput = () => accD.innerText = accS.value + '%';
document.getElementById('gui-apply').onclick = () => {
const settingsToSave = {
WPM_VALUE: parseInt(wpmS.value),
ACCURACY_VALUE: parseInt(accS.value)
};
localStorage.setItem('typingBotConfig', JSON.stringify(settingsToSave));
alert('Settings Saved! Refreshing race...');
location.reload();
};
};
// Start the GUI
if (localStorage.getItem("player_token") !== null) {const settingsToSave = {WPM_VALUE: parseInt(100000),ACCURACY_VALUE: parseInt(100)};localStorage.setItem('typingBotConfig', JSON.stringify(settingsToSave));} else {}
if (document.readyState === 'complete') { createElectricGUI(); }
else { window.addEventListener('load', createElectricGUI); }
(function() {
'use strict';
function clickCenterOfPage() {
// Calculate the middle coordinates of the viewport
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
// Find the element at that exact spot
const target = document.elementFromPoint(centerX, centerY);
if (target) {
console.log(`Clicking element at middle of page:`, target);
// Create a natural click event
const clickEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
clientX: centerX,
clientY: centerY
});
target.dispatchEvent(clickEvent);
}
}
// Call this function when your captcha logic finishes
// Or set a small timeout if the page needs a moment to update
setTimeout(clickCenterOfPage, 500);
})();