-
-
Save Domiii/52cf49d780ec8c9f01771973c36197af to your computer and use it in GitHub Desktop.
| /** | |
| * This script types for you automatically on www.typingclub.com: | |
| * 1. Open the website | |
| * 2. Blaze past the tutorials | |
| * 3. Go into a level | |
| * 4. Open Console | |
| * 5. Paste the script and press ENTER | |
| */ | |
| // NOTE: When delay (in ms between two strokes) is too low, the site might bug out and the result page will not be shown | |
| const minDelay = 60; | |
| const maxDelay = 60; | |
| const keyOverrides = { | |
| [String.fromCharCode(160)]: ' ' // convert hardspace to normal space | |
| }; | |
| function getTargetCharacters() { | |
| const els = Array.from(document.querySelectorAll('.token span.token_unit')); | |
| const chrs = els | |
| .map(el => { | |
| // get letter to type from each letter DOM element | |
| if (el.firstChild?.classList?.contains('_enter')) { | |
| // special case: ENTER | |
| return '\n'; | |
| } | |
| let text = el.textContent[0]; | |
| return text; | |
| }) | |
| .map(c => keyOverrides.hasOwnProperty(c) ? keyOverrides[c] : c); // convert special characters | |
| return chrs; | |
| } | |
| function recordKey(chr) { | |
| // send it straight to the internal API | |
| window.core.record_keydown_time(chr); | |
| } | |
| function sleep(ms) { | |
| return new Promise(r => setTimeout(r, ms)); | |
| } | |
| async function autoPlay(finish) { | |
| const chrs = getTargetCharacters(); | |
| for (let i = 0; i < chrs.length - (!finish); ++i) { | |
| const c = chrs[i]; | |
| recordKey(c); | |
| //console.log(c, c.charCodeAt()); | |
| await sleep(Math.random() * (maxDelay - minDelay) + minDelay); | |
| } | |
| } | |
| // ############################################################################################################ | |
| // old utilities | |
| // ############################################################################################################ | |
| // /** | |
| // * @see https://stackoverflow.com/questions/8942678/keyboardevent-in-chrome-keycode-is-0/12522752#12522752 | |
| // */ | |
| // function simulateKey(chr, el) { | |
| // _simulateKey(chr, 'keydown', el); | |
| // _simulateKey(chr, 'keypress', el); | |
| // } | |
| // function _simulateKey(chr, type, el) { | |
| // var eventObj = document.createEventObject ? | |
| // document.createEventObject() : document.createEvent("Events"); | |
| // if (eventObj.initEvent) { | |
| // eventObj.initEvent(type || "keydown", true, true); | |
| // } | |
| // let keyCode = chr.charCodeAt(0); | |
| // eventObj.key = chr[0]; | |
| // eventObj.keyCode = keyCode; | |
| // eventObj.which = keyCode; | |
| // eventObj.isTrusted = true; | |
| // el = el || document.body; | |
| // // console.log(keyCode, eventObj); | |
| // el.dispatchEvent ? el.dispatchEvent(eventObj) : el.fireEvent("onkeydown", eventObj); | |
| // } | |
| // document.addEventListener("keydown", function (e) { | |
| // console.log('down', e); | |
| // }); | |
| // document.addEventListener("keypress", function (e) { | |
| // console.log('press', e); | |
| // }); | |
| //$($('.menu-btn')[0].parentNode).prepend('<button onclick=\'simulateKeyPress("c")\'>sim</button>'); | |
| // simulateKey('a', $('input')[0]); | |
| // ############################################################################################################ | |
| // go! | |
| // ############################################################################################################ | |
| autoPlay(true); |
/*
-
Typing Club Automator - AUTO START
-
Refined by: Lionsbest9
-
Idea Developed by: Edman
*/
(function() {
'use strict';if (window.typingClubBot) {
console.log('Bot already running! Close it first with: window.typingClubBot.close()');
return;
}console.log('🚀 Loading Typing Club Bot v2.0 (Developed by Blaise Adelan, Idea by Yanis)...');
const gui = document.createElement('div');
gui.id = 'typing-bot-gui';
gui.style.cssText =position: fixed; top: 20px; right: 20px; width: 380px; /* Slightly wider */ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); z-index: 2147483647; font-family: 'Segoe UI', Arial, sans-serif; /* More common system fonts */ color: white; transition: width 0.2s ease-in-out, height 0.2s ease-in-out; overflow: hidden; /* Hide content when minimized */;gui.innerHTML = `
🎯 Typing Club Bot
—
×
<div id="bot-content" style="padding: 20px;"> <div style="background: rgba(255,255,255,0.1); padding: 10px; border-radius: 8px; font-size: 11px; margin-bottom: 10px;"> <strong style="display: block; margin-bottom: 5px;">📍 Detected:</strong> <span id="level-info">Waiting...</span> </div> <div style="margin-bottom: 18px;"> <label style="display: block; font-size: 12px; font-weight: 600; margin-bottom: 8px; text-transform: uppercase;">Speed</label> <div style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 10px;"> <input type="range" id="speed-slider" min="30" max="250" value="70" style="width: 100%; margin: 8px 0; cursor: pointer;"> <div style="text-align: center; font-size: 20px; font-weight: 700; margin-top: 5px;"><span id="speed-value">70</span> WPM</div> </div> </div> <div style="margin-bottom: 18px;"> <label style="display: block; font-size: 12px; font-weight: 600; margin-bottom: 8px; text-transform: uppercase;">Accuracy</label> <div style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 10px;"> <input type="range" id="accuracy-slider" min="90" max="100" value="97" style="width: 100%; margin: 8px 0; cursor: pointer;"> <div style="text-align: center; font-size: 20px; font-weight: 700; margin-top: 5px;"><span id="accuracy-value">97</span>%</div> </div> </div> <div style="display: flex; align-items: center; gap: 10px; background: rgba(255,255,255,0.15); padding: 12px; border-radius: 10px; margin-bottom: 10px;"> <input type="checkbox" id="auto-advance" checked style="width: 20px; height: 20px; cursor: pointer;"> <label for="auto-advance" style="cursor: pointer; flex: 1; font-size: 14px;">Auto-advance to next lesson</label> </div> <div style="display: flex; align-items: center; gap: 10px; background: rgba(255,255,255,0.15); padding: 12px; border-radius: 10px; margin-bottom: 10px;"> <input type="checkbox" id="handle-games" style="width: 20px; height: 20px; cursor: pointer;"> <label for="handle-games" style="cursor: pointer; flex: 1; font-size: 14px;">Attempt game auto-skip (unreliable)</label> </div> <button id="start-btn" style="width: 100%; padding: 12px; border: none; border-radius: 10px; font-size: 14px; font-weight: 700; cursor: pointer; text-transform: uppercase; background: #10ac84; color: white; margin-bottom: 10px;">▶ Start Bot</button> <button id="stop-btn" disabled style="width: 100%; padding: 12px; border: none; border-radius: 10px; font-size: 14px; font-weight: 700; cursor: pointer; text-transform: uppercase; background: #ff6b6b; color: white; margin-bottom: 10px; opacity: 0.5;">⏹ Stop</button> <div id="status" style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 10px; text-align: center; font-size: 13px; font-weight: 600; margin-top: 15px;">Ready</div> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 15px;"> <div style="background: rgba(255,255,255,0.15); padding: 10px 8px; border-radius: 8px; text-align: center;"> <div style="font-size: 18px; font-weight: 700;" id="chars-typed">0</div> <div style="font-size: 9px; opacity: 0.8; margin-top: 3px;">CHARS</div> </div> <div style="background: rgba(255,255,255,0.15); padding: 10px 8px; border-radius: 8px; text-align: center;"> <div style="font-size: 18px; font-weight: 700;" id="levels-completed">0</div> <div style="font-size: 9px; opacity: 0.8; margin-top: 3px;">LESSONS</div> </div> </div> </div>`;
document.body.appendChild(gui);
const speedSlider = document.getElementById('speed-slider');
const speedValue = document.getElementById('speed-value');
const accuracySlider = document.getElementById('accuracy-slider');
const accuracyValue = document.getElementById('accuracy-value');
const autoAdvance = document.getElementById('auto-advance');
const handleGames = document.getElementById('handle-games'); // New checkbox
const startBtn = document.getElementById('start-btn');
const stopBtn = document.getElementById('stop-btn');
const status = document.getElementById('status');
const levelInfo = document.getElementById('level-info');
const charsTypedEl = document.getElementById('chars-typed');
const levelsCompletedEl = document.getElementById('levels-completed');
const closeBtn = document.getElementById('bot-close');
const minimizeBtn = document.getElementById('bot-minimize'); // New minimize button
const header = document.getElementById('bot-header');
const botContent = document.getElementById('bot-content'); // Main content arealet botRunning = false;
let charsTypedCount = 0;
let levelsCompleted = 0;
let isMinimized = false;
let originalGuiHeight = gui.offsetHeight; // Store initial heightspeedSlider.addEventListener('input', (e) => speedValue.textContent = e.target.value);
accuracySlider.addEventListener('input', (e) => accuracyValue.textContent = e.target.value);// Draggable
let isDragging = false;
let currentX, currentY, initialX, initialY;header.addEventListener('mousedown', (e) => {
if (!e.target.closest('button')) { // Don't drag if clicking buttons in header
initialX = e.clientX - gui.offsetLeft;
initialY = e.clientY - gui.offsetTop;
isDragging = true;
gui.style.cursor = 'grabbing';
}
});document.addEventListener('mousemove', (e) => {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
gui.style.left = currentX + 'px';
gui.style.top = currentY + 'px';
gui.style.right = 'auto'; // Disable right positioning once dragged
}
});document.addEventListener('mouseup', () => {
isDragging = false;
gui.style.cursor = 'grab';
});minimizeBtn.addEventListener('click', () => {
isMinimized = !isMinimized;
if (isMinimized) {
originalGuiHeight = gui.offsetHeight; // Save current height before minimizing
gui.style.height = header.offsetHeight + 'px';
botContent.style.display = 'none';
minimizeBtn.textContent = '☐'; // Change to square/max button
} else {
gui.style.height = originalGuiHeight + 'px'; // Restore saved height
botContent.style.display = 'block';
minimizeBtn.textContent = '—'; // Change back to minimize
}
});// Key mapping for KeyboardEvent 'code' and 'key' properties
const KEY_MAP = {
' ': { key: ' ', code: 'Space', keyCode: 32 },
',': { key: ',', code: 'Comma', keyCode: 188 },
'.': { key: '.', code: 'Period', keyCode: 190 },
';': { key: ';', code: 'Semicolon', keyCode: 186 },
"'": { key: "'", code: 'Quote', keyCode: 222 },
'-': { key: '-', code: 'Minus', keyCode: 189 },
'/': { key: '/', code: 'Slash', keyCode: 191 },
'[': { key: '[', code: 'BracketLeft', keyCode: 219 },
']': { key: ']', code: 'BracketRight', keyCode: 221 },
'\': { key: '\', code: 'Backslash', keyCode: 220 },
'': { key: '', code: 'Backquote', keyCode: 192 },
'=': { key: '=', code: 'Equal', keyCode: 187 },
'{': { key: '{', code: 'BracketLeft', keyCode: 219, shiftKey: true },
'}': { key: '}', code: 'BracketRight', keyCode: 221, shiftKey: true },
'|': { key: '|', code: 'Backslash', keyCode: 220, shiftKey: true },
':': { key: ':', code: 'Semicolon', keyCode: 186, shiftKey: true },
'"': { key: '"', code: 'Quote', keyCode: 222, shiftKey: true },
'<': { key: '<', code: 'Comma', keyCode: 188, shiftKey: true },
'>': { key: '>', code: 'Period', keyCode: 190, shiftKey: true },
'?': { key: '?', code: 'Slash', keyCode: 191, shiftKey: true },
'': { key: '', code: 'Backquote', keyCode: 192, shiftKey: true },
'!': { key: '!', code: 'Digit1', keyCode: 49, shiftKey: true },
'@': { key: '@', code: 'Digit2', keyCode: 50, shiftKey: true },
'#': { key: '#', code: 'Digit3', keyCode: 51, shiftKey: true },
'$': { key: '$', code: 'Digit4', keyCode: 52, shiftKey: true },
'%': { key: '%', code: 'Digit5', keyCode: 53, shiftKey: true },
'^': { key: '^', code: 'Digit6', keyCode: 54, shiftKey: true },
'&': { key: '&', code: 'Digit7', keyCode: 55, shiftKey: true },
'': { key: '', code: 'Digit8', keyCode: 56, shiftKey: true },
'(': { key: '(', code: 'Digit9', keyCode: 57, shiftKey: true },
')': { key: ')', code: 'Digit0', keyCode: 48, shiftKey: true },
'': { key: '', code: 'Minus', keyCode: 189, shiftKey: true },
'+': { key: '+', code: 'Equal', keyCode: 187, shiftKey: true }
};// Populate Key_MAP for a-z A-Z 0-9
for (let i = 0; i < 26; i++) {
const charLower = String.fromCharCode('a'.charCodeAt(0) + i);
const charUpper = String.fromCharCode('A'.charCodeAt(0) + i);
KEY_MAP[charLower] = { key: charLower, code:Key${charUpper}, keyCode: charUpper.charCodeAt(0) };
KEY_MAP[charUpper] = { key: charUpper, code:Key${charUpper}, keyCode: charUpper.charCodeAt(0), shiftKey: true };
}
for (let i = 0; i < 10; i++) {
const char = String.fromCharCode('0'.charCodeAt(0) + i);
KEY_MAP[char] = { key: char, code:Digit${char}, keyCode: char.charCodeAt(0) };
}function typeChar(char, targetElement) {
let eventProps = KEY_MAP[char];if (!eventProps) { console.warn(`Character '${char}' not found in key map, using fallback.`); eventProps = { key: char, code: `Key${char.toUpperCase()}`, // Best guess keyCode: char.charCodeAt(0), shiftKey: char.toUpperCase() === char && char.toLowerCase() !== char // Guess for uppercase }; } const opts = { key: eventProps.key, code: eventProps.code, keyCode: eventProps.keyCode, which: eventProps.keyCode, shiftKey: eventProps.shiftKey || false, bubbles: true, cancelable: true, view: window }; // Dispatch key events targetElement.dispatchEvent(new KeyboardEvent('keydown', opts)); targetElement.dispatchEvent(new KeyboardEvent('keypress', opts)); // Some apps still listen to keypress // Update target element's content/value if (targetElement.isContentEditable) { const selection = window.getSelection(); const range = document.createRange(); if (selection.focusNode) { range.setStart(selection.focusNode, selection.focusOffset); range.insertNode(document.createTextNode(char)); range.collapse(false); selection.removeAllRanges(); selection.addRange(range); } else { targetElement.textContent += char; // Fallback if no selection } } else if (targetElement.tagName === 'INPUT' || targetElement.tagName === 'TEXTAREA') { const start = targetElement.selectionStart; const end = targetElement.selectionEnd; targetElement.value = targetElement.value.substring(0, start) + char + targetElement.value.substring(end); targetElement.setSelectionRange(start + 1, start + 1); } // Dispatch input/change events for reactivity targetElement.dispatchEvent(new Event('input', { bubbles: true })); targetElement.dispatchEvent(new Event('change', { bubbles: true })); targetElement.dispatchEvent(new KeyboardEvent('keyup', opts));}
function detectLevel() {
// Look for the element that contains the text to be typed
// TypingClub uses divs with 'current-letter' or similar indicators
const currentLetterSpan = document.querySelector('span.current-letter');
let textContainer = currentLetterSpan ? currentLetterSpan.closest('div.sentence') || currentLetterSpan.closest('div.typable') : null;if (!textContainer) { textContainer = document.querySelector('div.word.active') || document.querySelector('div[data-lesson-text]'); } let fullText = ''; if (textContainer) { // Reconstruct text from child nodes, preserving spaces/structure fullText = Array.from(textContainer.querySelectorAll('span, div')).map(el => { if (el.classList.contains('current-cursor') || el.classList.contains('current-letter') || el.classList.contains('typed')) { return ''; // Ignore already typed/cursor elements for full text } return el.textContent; }).join('').replace(/\s+/g, ' ').trim(); // Clean up multiple spaces if (fullText.length > 0) { levelInfo.textContent = `${fullText.length} chars detected`; return { text: fullText, type: 'typing' }; } } // Alternative for when lessons just started and there's a big block of text const alternativeTextContainer = document.querySelector('.typable, .typing-text, [data-lesson-text]'); if (alternativeTextContainer && alternativeTextContainer.textContent.trim().length > 10) { fullText = alternativeTextContainer.textContent.trim().replace(/\s+/g, ' '); levelInfo.textContent = `${fullText.length} chars detected (fallback)`; return { text: fullText, type: 'typing' }; } // Check for games const gameContainer = document.querySelector('.game-container, canvas, #game, [class*="game-screen"]'); if (gameContainer) { levelInfo.textContent = 'Game detected'; return { text: null, type: 'game' }; } levelInfo.textContent = 'No active lesson/game found.'; return { text: null, type: null };}
async function findAndFocusInput() {
let inputField = null;
for (let attempt = 0; attempt < 50; attempt++) { // Increased attempts
// Prioritize the element indicated as the current target
inputField = document.querySelector('span.current-letter, div.current-letter, .user-input, input[type="text"]:focus');// Find an active input field that is currently visible or contenteditable if (!inputField || !inputField.offsetParent) { // If not found or not visible const potentialInputs = document.querySelectorAll('input[type="text"], textarea, [contenteditable="true"], .typingArea'); for (const inp of potentialInputs) { // Check if it's visible and interactable, or if it's the `contenteditable` container if ( (inp.offsetParent !== null && inp.checkVisibility && inp.checkVisibility()) || inp.isContentEditable) { inputField = inp; break; } } } if (inputField) { console.log('✅ Found active input field:', inputField); // Try to focus it if it's not contenteditable (contenteditable can cause issues with focus) if (!inputField.isContentEditable) { inputField.focus(); } // Clear any pre-existing value if it's an input if (inputField.tagName === 'INPUT' || inputField.tagName === 'TEXTAREA') { inputField.value = ''; } else if (inputField.isContentEditable) { inputField.textContent = ''; // Clear contenteditable } return inputField; } await new Promise(r => setTimeout(r, 100)); } return null;}
async function processLevel() {
if (!botRunning) {
console.log("Bot stopped, ceasing level processing.");
return;
}const level = detectLevel(); if (!level.text && level.type !== 'game') { status.textContent = '⚠️ No lesson text or game found. Retrying in 3s...'; console.log('❌ Could not find text or game. Retrying...'); await new Promise(r => setTimeout(r, 3000)); if (botRunning) processLevel(); // Retry return; } // Handle games if (level.type === 'game') { status.textContent = '🎮 Game detected!'; console.log('🎮 Game detected.'); if (handleGames.checked) { status.textContent = '🎮 Attempting to skip game...'; console.warn('Attempting to skip game is highly unreliable and may not work.'); // Try to find common "Skip" or "Next" buttons immediately const skipBtn = document.querySelector('button.skip, a.skip, .skip-button, button.next, a.next, .next-button, .go-button, .btn-primary, [role="button"]'); // Added more skip options if (skipBtn && skipBtn.offsetParent !== null && skipBtn.checkVisibility()) { console.log('Found a potential skip/next button, clicking it.'); skipBtn.click(); await new Promise(r => setTimeout(r, 2000)); // Give time for transition levelsCompleted++; levelsCompletedEl.textContent = levelsCompleted; if (botRunning) processLevel(); return; } else { status.textContent = '🎮 Game found, no clear skip button. Stopped.'; stopBot(); // Stop if no skip button found return; } } else { status.textContent = '🎮 Game found (game handling disabled). Stopped.'; stopBot(); return; } } // Handle typing (existing code) const text = level.text; status.textContent = '👆 Starting lesson...'; // Focus on the typing area initially let typingAreaClickTarget = document.querySelector('.typing-widget, [data-lesson-text], .wrapper span.current-letter'); if (typingAreaClickTarget) { console.log('Attempting to click typing area:', typingAreaClickTarget); typingAreaClickTarget.click(); } else { document.body.click(); // Fallback general click } await new Promise(r => setTimeout(r, 500)); // Small delay for focus to take effect const input = await findAndFocusInput(); if (!input) { status.textContent = '⚠️ Input field not found. Retrying in 3s...'; console.log('❌ No active input field found. Retrying...'); await new Promise(r => setTimeout(r, 3000)); if (botRunning) processLevel(); // Retry return; } console.log('🚀 Typing into:', input); status.textContent = `⌨️ Typing...`; const wpm = parseInt(speedSlider.value); const accuracy = parseInt(accuracySlider.value); const baseDelay = 60000 / (wpm * 5); // Time per char in ms for (let i = 0; i < text.length; i++) { if (!botRunning) return; const char = text[i]; const shouldError = Math.random() * 100 > accuracy; // Simulate mistype and backspace for realism if (shouldError && /[a-zA-Z0-9]/.test(char)) { // Only mistype letters and numbers const wrongCharAscii = char.charCodeAt(0) + (Math.random() > 0.5 ? 1 : -1); const wrong = String.fromCharCode(wrongCharAscii); if (KEY_MAP[wrong]) { // Ensure the wrong character can be mapped typeChar(wrong, input); charsTypedCount++; // Count the wrong char charsTypedEl.textContent = charsTypedCount; await new Promise(r => setTimeout(r, baseDelay * (0.2 + Math.random() * 0.2))); // Delay for typing error // Simulate backspace key const backspaceOpts = { key: 'Backspace', code: 'Backspace', keyCode: 8, which: 8, bubbles: true, cancelable: true, view: window }; input.dispatchEvent(new KeyboardEvent('keydown', backspaceOpts)); if (input.isContentEditable) { const selection = window.getSelection(); if (selection.focusNode && selection.focusOffset > 0) { const range = document.createRange(); range.setStart(selection.focusNode, selection.focusOffset - 1); range.setEnd(selection.focusNode, selection.focusOffset); range.deleteContents(); range.collapse(false); selection.removeAllRanges(); selection.addRange(range); } else { input.textContent = input.textContent.slice(0, -1); } } else if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') { input.value = input.value.slice(0, -1); input.setSelectionRange(input.value.length, input.value.length); } input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new KeyboardEvent('keyup', backspaceOpts)); await new Promise(r => setTimeout(r, baseDelay * (0.2 + Math.random() * 0.2))); // Delay for backspace } } typeChar(char, input); charsTypedCount++; charsTypedEl.textContent = charsTypedCount; let delay = baseDelay * (0.8 + Math.random() * 0.4); // Randomize delay a bit if (['.', '!', '?', '\n'].includes(char)) delay *= 2.5; // Longer pause for punctuation/newlines else if (char === ' ') delay *= 1.3; // Slightly longer for spaces await new Promise(r => setTimeout(r, delay)); } console.log('✅ Typing complete for this lesson!'); levelsCompleted++; levelsCompletedEl.textContent = levelsCompleted; status.textContent = '✅ Lesson complete! Checking next step...'; if (autoAdvance.checked) { // Give the page some time to process results and show next button await new Promise(r => setTimeout(r, 4000)); // Look for common "Continue", "Next Lesson", "Go" buttons or links const advanceButtonSelectors = [ 'button.continue, a.continue, .continue-button, [data-action="next-lesson"]', 'button.next, a.next, .next-button, [data-action="next"]', '.go-button, .btn-primary, [role="button"]' // General buttons ]; let advanceButton = null; for (const selector of advanceButtonSelectors) { advanceButton = document.querySelector(selector); if (advanceButton && advanceButton.offsetParent !== null && advanceButton.checkVisibility()) { console.log('Found auto-advance button:', advanceButton); advanceButton.click(); break; } else if (advanceButton) { console.log('Found button, but not visible or interactable yet:', advanceButton); } advanceButton = null; // Reset for next selector } if (!advanceButton) { console.log('No specific advance button found, trying generic click/Enter...'); document.body.click(); // Click body as a fallback await new Promise(r => setTimeout(r, 500)); // Try sending Enter key const enterOpts = KEY_MAP['\n']; // Or specific enter key code if (enterOpts) { document.dispatchEvent(new KeyboardEvent('keydown', enterOpts)); document.dispatchEvent(new KeyboardEvent('keyup', enterOpts)); } } // Wait a bit for the next lesson to load await new Promise(r => setTimeout(r, 2000)); if (botRunning) processLevel(); // Start processing the next lesson } else { status.textContent = '⏹ Auto-advance disabled. Stopped after lesson.'; stopBot(); }}
function startBot() {
botRunning = true;
charsTypedCount = 0; // Reset metrics on new start
levelsCompleted = 0;
charsTypedEl.textContent = '0';
levelsCompletedEl.textContent = '0';
startBtn.disabled = true;
stopBtn.disabled = false;
stopBtn.style.opacity = '1';
startBtn.style.background = '#4CAF50'; // Green
stopBtn.style.background = '#ff6b6b'; // Red
console.log('🤖 Bot started');
processLevel();
}function stopBot() {
botRunning = false;
startBtn.disabled = false;
stopBtn.disabled = true;
stopBtn.style.opacity = '0.5';
startBtn.style.background = '#10ac84'; // Original green
stopBtn.style.background = '#ff6b6b'; // Red
status.textContent = '⏹ Stopped';
console.log('🤖 Bot stopped.');
}function closeBot() {
stopBot();
gui.remove();
window.typingClubBot = null;
console.log('Bot GUI closed and removed.');
}startBtn.addEventListener('click', startBot);
stopBtn.addEventListener('click', stopBot);
closeBtn.addEventListener('click', closeBot); // Corrected this linewindow.typingClubBot = { start: startBot, stop: stopBot, close: closeBot, gui: gui }; // Expose gui for external control
// Initial detection after a short delay
setTimeout(detectLevel, 1000); // Give page a bit more time to render initially
console.log('✅ Bot loaded! Adjust settings and click "▶ Start Bot" to begin.');
})();
The script is good and it words but it can't detect enter and it cant play the minigames pls fix it if you can
The games and the key ""enter doesnt work on teh bot
i will try to fix mingigames.
can u also make one for the edclub vocabulary and spelling
If you can
About 95 Wmp :(async () => {
const minDelay = 70;
const maxDelay = 110;
const inputTarget = document.querySelector('input, textarea, [contenteditable="true"]') || document.body;
function getTargetCharacters() {
const els = Array.from(document.querySelectorAll('.token span.token_unit'));
return els.map(el => {
if (el.classList.contains('_enter')) return 'Enter';
let char = el.textContent[0];
// Convert special space (char 160) to a standard space
return char.charCodeAt(0) === 160 ? ' ' : char;
});
}
const chars = getTargetCharacters();
console.log(Starting... Found ${chars.length} characters.);
for (const char of chars) {
const key = char === 'Enter' ? 'Enter' : char;
const keyCode = key === 'Enter' ? 13 : key.charCodeAt(0);
const eventSettings = {
key: key,
keyCode: keyCode,
which: keyCode,
code: key === ' ' ? 'Space' : (key === 'Enter' ? 'Enter' : `Key${key.toUpperCase()}`),
bubbles: true,
cancelable: true,
composed: true
};
inputTarget.dispatchEvent(new KeyboardEvent('keydown', eventSettings));
inputTarget.dispatchEvent(new KeyboardEvent('keypress', eventSettings));
// Forcing the space injection
if (inputTarget.value !== undefined) {
inputTarget.value += (key === 'Enter' ? '' : key);
inputTarget.dispatchEvent(new Event('input', { bubbles: true }));
}
inputTarget.dispatchEvent(new KeyboardEvent('keyup', eventSettings));
await new Promise(r => setTimeout(r, Math.random() * (maxDelay - minDelay) + minDelay));
}
console.log("Finished!");
})();
what does the upper code do
hey if anyone could help that would be great how do i use the newer code cause when i try to put it into inspect element it wont work please help! :)
it keeps saying unexpected number / invalid token
locked aria-hidden on an element because its descendant retained focus. The focus must not be hidden from assistive technology users. Avoid using aria-hidden on a focused element or its ancestor. Consider using the inert attribute instead, which will also prevent focus. For more details, see the aria-hidden section of the WAI-ARIA specification at https://w3c.github.io/aria/#aria-hidden.
Element with focus:
Ancestor with aria-hidden:
it wont even let me say allow pasting and this is a personal laptop
?
it keeps saying unexpected number / invalid token
same I also keep on getting this notice
Anyone got an updated one?
Yea i know that but when i paste it in the console it dosen't work.