-
-
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); |
¿Alguien ha encontrado la solución? Creo que ya no funciona, probablemente necesitemos hacer otro código
https://github.com/DonMarmolejo727/TypingClub-Extractor.git
Hello brother, try using this code that was developed by a great team of programmers.
Que alguien cree un código nuevo, por favor
Alright bro, check out my repository :)
someone pls make a new code
Yeah we need one, pls someone make one
Did you or someone find the solution??? Im trying to fix it but this is a little more difficult and I can't find a solution
Yeah np just asking C:
maybe cuz its not your code? Wowie.
can any one share the new code pls
// Typing Club Automator - AUTO START
(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...');
const gui = document.createElement('div');
gui.id = 'typing-bot-gui';
gui.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
width: 340px;
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: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
color: white;
`;
gui.innerHTML = `
<div style="padding: 15px 20px; border-bottom: 1px solid rgba(255,255,255,0.2); display: flex; justify-content: space-between; align-items: center; cursor: move;" id="bot-header">
<div style="font-weight: 700; font-size: 16px;">🎯 Typing Club Bot</div>
<button id="bot-close" style="background: rgba(255,255,255,0.2); border: none; color: white; width: 28px; height: 28px; border-radius: 50%; cursor: pointer; font-size: 18px;">×</button>
</div>
<div 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="190" 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="92" 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</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;">LEVELS</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 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 header = document.getElementById('bot-header');
let botRunning = false;
let charsTypedCount = 0;
let levelsCompleted = 0;
speedSlider.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) => {
initialX = e.clientX - gui.offsetLeft;
initialY = e.clientY - gui.offsetTop;
isDragging = true;
});
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';
}
});
document.addEventListener('mouseup', () => isDragging = false);
function detectLevel() {
// Try to find typing text
const typable = document.querySelector('div.typable');
if (typable && typable.textContent) {
const text = typable.textContent.trim().replace(/\s+/g, ' ');
levelInfo.textContent = `${text.length} chars detected`;
return { text, type: 'typing' };
}
// Check for games - look for game container or canvas
const gameContainer = document.querySelector('.game-container, .game-canvas, canvas, #game, [class*="game"]');
if (gameContainer) {
levelInfo.textContent = 'Game detected';
return { text: null, type: 'game' };
}
return { text: null, type: null };
}
function typeChar(char, field) {
const keyCode = char.charCodeAt(0);
let code = 'Unidentified';
let shiftKey = false;
// Map special characters properly
const specialChars = {
' ': { code: 'Space', keyCode: 32 },
'"': { code: 'Quote', keyCode: 222, shiftKey: true },
"'": { code: 'Quote', keyCode: 222, shiftKey: false },
',': { code: 'Comma', keyCode: 188 },
'.': { code: 'Period', keyCode: 190 },
'!': { code: 'Digit1', keyCode: 49, shiftKey: true },
'?': { code: 'Slash', keyCode: 191, shiftKey: true },
':': { code: 'Semicolon', keyCode: 186, shiftKey: true },
';': { code: 'Semicolon', keyCode: 186 },
'-': { code: 'Minus', keyCode: 189 },
'(': { code: 'Digit9', keyCode: 57, shiftKey: true },
')': { code: 'Digit0', keyCode: 48, shiftKey: true }
};
if (specialChars[char]) {
code = specialChars[char].code;
shiftKey = specialChars[char].shiftKey || false;
} else if (/[a-z]/.test(char)) {
code = `Key${char.toUpperCase()}`;
shiftKey = false;
} else if (/[A-Z]/.test(char)) {
code = `Key${char}`;
shiftKey = true;
} else if (/[0-9]/.test(char)) {
code = `Digit${char}`;
shiftKey = false;
}
const opts = {
key: char,
code: code,
keyCode: keyCode,
which: keyCode,
shiftKey: shiftKey,
bubbles: true,
cancelable: true,
view: window
};
field.dispatchEvent(new KeyboardEvent('keydown', opts));
field.dispatchEvent(new KeyboardEvent('keypress', opts));
field.value += char;
field.dispatchEvent(new Event('input', { bubbles: true }));
field.dispatchEvent(new Event('change', { bubbles: true }));
field.dispatchEvent(new KeyboardEvent('keyup', opts));
}
async function processLevel() {
if (!botRunning) return;
const level = detectLevel();
if (!level.text && level.type !== 'game') {
status.textContent = '⚠️ No text or game found';
console.log('❌ Could not find text or game');
stopBot();
return;
}
// Handle games
if (level.type === 'game') {
console.log('🎮 Game detected, auto-playing...');
status.textContent = '🎮 Playing game...';
// Wait for game to load
await new Promise(r => setTimeout(r, 2000));
// Spam keys that games commonly use
const gameKeys = ['Space', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter', 'KeyW', 'KeyA', 'KeyS', 'KeyD'];
for (let i = 0; i < 50; i++) {
if (!botRunning) return;
// Press random game key
const randomKey = gameKeys[Math.floor(Math.random() * gameKeys.length)];
const keyOpts = {
key: randomKey.includes('Arrow') || randomKey.includes('Space') ? randomKey.replace('Arrow', '').replace('Key', '') : randomKey.replace('Key', '').toLowerCase(),
code: randomKey,
keyCode: randomKey === 'Space' ? 32 : randomKey.includes('Arrow') ? (randomKey === 'ArrowUp' ? 38 : randomKey === 'ArrowDown' ? 40 : randomKey === 'ArrowLeft' ? 37 : 39) : randomKey.charCodeAt(3),
bubbles: true,
cancelable: true,
view: window
};
document.dispatchEvent(new KeyboardEvent('keydown', keyOpts));
document.dispatchEvent(new KeyboardEvent('keyup', keyOpts));
// Also click randomly on screen
if (i % 5 === 0) {
const x = Math.random() * window.innerWidth;
const y = Math.random() * window.innerHeight;
const el = document.elementFromPoint(x, y);
if (el && !el.closest('#typing-bot-gui')) {
el.click();
}
}
await new Promise(r => setTimeout(r, 100));
}
console.log('🎮 Game sequence complete');
status.textContent = '✅ Game complete!';
if (autoAdvance.checked) {
await new Promise(r => setTimeout(r, 6000));
// Click to advance
document.body.click();
const clickables = document.querySelectorAll('button, .btn, [role="button"]');
clickables.forEach(el => {
if (el.offsetParent !== null && !el.closest('#typing-bot-gui')) {
el.click();
}
});
await new Promise(r => setTimeout(r, 1000));
if (botRunning) processLevel();
} else {
stopBot();
}
return;
}
// Handle typing (existing code)
const text = level.text;
console.log('📝 Text:', text);
status.textContent = '👆 Starting lesson...';
// Try multiple ways to trigger the lesson
// Method 1: Click the typing area
const typingArea = document.querySelector('.tpmodes, .typable, .inview');
if (typingArea) {
console.log('Clicking typing area...');
typingArea.click();
}
// Method 2: Click anywhere on body
document.body.click();
// Method 3: Press a key to trigger
document.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', keyCode: 32, bubbles: true }));
await new Promise(r => setTimeout(r, 500));
// Find the input field (might take a moment to appear)
let input = null;
for (let attempt = 0; attempt < 30; attempt++) {
const allInputs = document.querySelectorAll('input[type="text"]');
console.log(`Attempt ${attempt}: Found ${allInputs.length} inputs`);
for (const inp of allInputs) {
console.log(' Input:', {
visible: inp.offsetParent !== null,
ariaHidden: inp.getAttribute('aria-hidden'),
style: inp.style.cssText
});
// Accept ANY text input, even if hidden or aria-hidden
if (inp.type === 'text') {
input = inp;
console.log('✅ Using this input!');
break;
}
}
if (input) break;
await new Promise(r => setTimeout(r, 100));
}
if (!input) {
status.textContent = '⚠️ Input not found';
console.log('❌ No input field found at all');
stopBot();
return;
}
console.log('🚀 Typing...');
input.value = '';
input.focus();
await new Promise(r => setTimeout(r, 100));
const wpm = parseInt(speedSlider.value);
const accuracy = parseInt(accuracySlider.value);
const baseDelay = 60000 / (wpm * 5);
status.textContent = `⌨️ Typing...`;
for (let i = 0; i < text.length; i++) {
if (!botRunning) return;
const char = text[i];
const shouldError = Math.random() * 100 > accuracy;
if (shouldError && /[a-z]/i.test(char)) {
const wrong = String.fromCharCode(char.charCodeAt(0) + (Math.random() > 0.5 ? 1 : -1));
typeChar(wrong, input);
await new Promise(r => setTimeout(r, baseDelay * 0.5));
input.value = input.value.slice(0, -1);
await new Promise(r => setTimeout(r, baseDelay * 0.3));
}
typeChar(char, input);
charsTypedCount++;
charsTypedEl.textContent = charsTypedCount;
let delay = baseDelay * (0.8 + Math.random() * 0.4);
if (['.', '!', '?'].includes(char)) delay *= 2;
else if (char === ' ') delay *= 1.3;
await new Promise(r => setTimeout(r, delay));
}
console.log('✅ Complete!');
levelsCompleted++;
levelsCompletedEl.textContent = levelsCompleted;
status.textContent = '✅ Complete! Waiting...';
if (autoAdvance.checked) {
// Wait 6 seconds for level to finish processing
await new Promise(r => setTimeout(r, 6000));
// ONE TIME: Spam Enter on everything
console.log('Pressing Enter to advance...');
const enterOpts = {
key: 'Enter',
code: 'Enter',
keyCode: 13,
which: 13,
bubbles: true,
cancelable: true,
view: window
};
// Fire on document, input, and body
document.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
document.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
document.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
if (input) {
input.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
input.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
input.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
}
document.body.dispatchEvent(new KeyboardEvent('keydown', enterOpts));
document.body.dispatchEvent(new KeyboardEvent('keypress', enterOpts));
document.body.dispatchEvent(new KeyboardEvent('keyup', enterOpts));
// ONE TIME: Click everything
console.log('Clicking to advance...');
document.body.click();
// Click center of screen
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
const centerEl = document.elementFromPoint(centerX, centerY);
if (centerEl && !centerEl.closest('#typing-bot-gui')) {
centerEl.click();
}
// Click all buttons
const clickables = document.querySelectorAll('button, .btn, [role="button"], div[onclick], a');
clickables.forEach(el => {
if (el.offsetParent !== null && !el.closest('#typing-bot-gui')) {
el.click();
}
});
// Wait then restart
await new Promise(r => setTimeout(r, 1000));
if (botRunning) processLevel();
} else {
stopBot();
}
}
function startBot() {
botRunning = true;
startBtn.disabled = true;
stopBtn.disabled = false;
stopBtn.style.opacity = '1';
console.log('🤖 Bot started');
processLevel();
}
function stopBot() {
botRunning = false;
startBtn.disabled = false;
stopBtn.disabled = true;
stopBtn.style.opacity = '0.5';
status.textContent = '⏹ Stopped';
}
function closeBot() {
stopBot();
gui.remove();
window.typingClubBot = null;
}
startBtn.addEventListener('click', startBot);
stopBtn.addEventListener('click', stopBot);
closeBtn.addEventListener('click', closeBtn);
window.typingClubBot = { start: startBot, stop: stopBot, close: closeBot };
setTimeout(detectLevel, 500);
console.log('✅ Bot loaded! Just click Start Bot.');
})();
god bless you guys man THNAK YOU
this might sound dum but what do i copy and paste from that code? , because i copied all of it but i didn't work
Yea i know that but when i paste it in the console it dosen't work.
/*
-
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.');
})();

someone pls make a new code