您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Live WPM/accuracy box for Klavia + sandbagging tool
// ==UserScript== // @name Klavia Statistics Box + Sandbagging Tool // @namespace http://tampermonkey.net/ // @version 1.11 // @description Live WPM/accuracy box for Klavia + sandbagging tool // @match *://ntcomps.com/race* // @match *://klavia.io/race* // @match *://playklavia.com/race* // @license MIT // @author JayJacksonGH + ChatGPT // ==/UserScript== (function() { 'use strict'; // ============================ // CONFIG // ============================ const config = { targetWPM: 79.4, // Desired sandbag WPM indicateWithin: 5, // Highlight WPM if within range refreshInterval: 50, // Box update frequency (ms) corner: 'bottom-right' // Options: 'bottom-right', 'bottom-left' }; // ============================ // VARIABLES // ============================ let box; let totalLetters = 0; let mistakes = 0; let correctCharacters = 0; let initialized = false; let waitingForReset = false; let raceStarted = false; let firstLetterAppeared = false; let lastLetterAppeared = false; let lastLetter = null; let reloaded = true; let raceResultsEmpty = true; let exactAcc = 0; let startTime = null; let endTime = null; let exactWpm = 0; let bestLiveWpm = 0; let endingthing = false; let elapsedMinutes = 0; // ============================ // CREATE NITRO-STYLE BOX // ============================ function createBox() { if (!document.body.contains(box)) { box = document.createElement('div'); box.id = 'sandbagBox'; Object.assign(box.style, { position: 'fixed', padding: '8px 12px', background: 'rgba(0,0,0,0.7)', color: '#fff', fontFamily: 'monospace', fontSize: '14px', borderRadius: '8px', boxShadow: '0 0 8px rgba(0,0,0,0.5)', zIndex: '9999', whiteSpace: 'pre-line', userSelect: 'none', cursor: 'move' }); if (config.corner === 'bottom-right') { box.style.bottom = '20px'; box.style.right = '20px'; } else if (config.corner === 'bottom-left') { box.style.bottom = '20px'; box.style.left = '20px'; } document.body.appendChild(box); // Drag functionality like Nitro Type let isDragging = false, offsetX = 0, offsetY = 0; box.addEventListener('mousedown', e => { isDragging = true; offsetX = e.clientX - box.getBoundingClientRect().left; offsetY = e.clientY - box.getBoundingClientRect().top; }); window.addEventListener('mousemove', e => { if (isDragging) { box.style.left = `${e.clientX - offsetX}px`; box.style.top = `${e.clientY - offsetY}px`; box.style.right = 'auto'; box.style.bottom = 'auto'; } }); window.addEventListener('mouseup', () => isDragging = false); } } // ============================ // INITIALIZE RACE // ============================ function initialize() { const letters = document.querySelectorAll("#typing-text .typing-letter"); if (letters.length > 0) { totalLetters = letters.length; lastLetter = letters[totalLetters - 1]; startTime = null; endTime = null; raceStarted = false; exactWpm = 0; endingthing = true; firstLetterAppeared = letters[0].classList.contains("highlight-letter"); lastLetterAppeared = lastLetter.classList.contains("highlight-letter"); initialized = true; createBox(); } else { if (document.body.contains(box)) box.remove(); box = null; } } // ============================ // ACCURACY / WPM CALCULATION // ============================ function calculateScriptAccuracy() { const letters = document.querySelectorAll("#typing-text .typing-letter"); if (!letters.length) return 100; let correct = 0; let total = 0; letters.forEach(letter => { const typed = !letter.classList.contains("highlight-letter"); if (!typed) return; total++; if (!letter.classList.contains("incorrect-letter")) correct++; }); return total === 0 ? 100 : (correct / total) * 100; } function updateAccuracy() { const scriptAcc = calculateScriptAccuracy(); if (raceStarted || scriptAcc < 100) { if (scriptAcc < 100 && !waitingForReset) { mistakes++; waitingForReset = true; } else if (scriptAcc === 100) { waitingForReset = false; correctCharacters++; } } } // ============================ // RACE START / END // ============================ function checkRaceStart() { const quickChat = document.querySelector('#quick-chat'); if (!quickChat) return; if (quickChat.style.display === 'flex') raceStarted = false; else if (quickChat.style.display === 'none' && !raceStarted) { raceStarted = true; endingthing = true; startTime = Date.now(); } } function checkRaceEnd() { if (!lastLetter) return; if (!lastLetterAppeared && lastLetter.classList.contains("highlight-letter")) lastLetterAppeared = true; if (lastLetterAppeared && raceStarted && !lastLetter.classList.contains("highlight-letter")) { lastLetterAppeared = false; endingthing = false; endTime = Date.now(); elapsedMinutes = (endTime - startTime) / 60000; exactWpm = elapsedMinutes > 0 ? (correctCharacters / 5) / elapsedMinutes : 0; } } function checkFirstLetterDisappearance() { const firstLetter = document.querySelector('#typing-text .typing-letter[data-index="0"]'); if (!firstLetter) { resetCounters(); if (document.body.contains(box)) box.remove(); box = null; exactAcc = correctCharacters > 0 ? ((correctCharacters - mistakes) / correctCharacters) * 100 : 100; } } function resetCounters() { totalLetters = 0; mistakes = 0; endingthing = false; correctCharacters = 0; waitingForReset = false; firstLetterAppeared = false; lastLetterAppeared = false; lastLetter = null; initialized = false; } function checkPostRace() { const raceResults = document.querySelector('#race-results'); if (!raceResults) return; const isEmptyNow = raceResults.innerHTML.trim() === ""; if (!isEmptyNow && raceResultsEmpty) reloaded = false; else if (isEmptyNow && !raceResultsEmpty) { reloaded = true; resetCounters(); if (box) box.remove(); box = null; exactAcc = correctCharacters > 0 ? ((correctCharacters - mistakes) / correctCharacters) * 100 : 100; } raceResultsEmpty = isEmptyNow; } // ============================ // BOX UPDATE // ============================ function boxUpdate() { if (!initialized) initialize(); if (endingthing && raceStarted) { endTime = Date.now(); elapsedMinutes = (endTime - startTime) / 60000; exactWpm = elapsedMinutes > 0 ? (correctCharacters / 5) / elapsedMinutes : 0; } else if (!raceStarted) { elapsedMinutes = 0; } if (exactWpm > bestLiveWpm) bestLiveWpm = exactWpm; if (box) { createBox(); const targetTimeSec = (totalLetters / 5) / config.targetWPM * 60; box.innerHTML = ` Target WPM: <span style="color:#ffe275">${config.targetWPM}</span> Live WPM: <span style="color:${Math.abs(exactWpm-config.targetWPM)<=config.indicateWithin?'#76ff76':'#fff'}">${exactWpm.toFixed(2)}</span> Best WPM: ${bestLiveWpm.toFixed(2)} Accuracy: ${exactAcc.toFixed(2)}% Mistakes: ${mistakes} Chars: ${totalLetters} Target Time: ${targetTimeSec.toFixed(2)}s Timer: ${(elapsedMinutes*60).toFixed(2)}s `; } } // ============================ // EVENT LISTENERS // ============================ document.addEventListener("keydown", e => { if (e.key === "Shift") return; if (!raceStarted || !reloaded) return; updateAccuracy(); exactAcc = correctCharacters > 0 ? ((correctCharacters - mistakes) / correctCharacters) * 100 : 100; }); // ============================ // INTERVALS // ============================ setInterval(checkFirstLetterDisappearance, 100); setInterval(checkRaceStart, 20); setInterval(checkRaceEnd, 20); setInterval(boxUpdate, config.refreshInterval); setInterval(checkPostRace, 100); // ============================ // OBSERVER // ============================ const observer = new MutationObserver(() => { initialize(); createBox(); }); observer.observe(document.querySelector('#typing-text-container'), { childList: true, subtree: true }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址