您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Types your text in a human-like manner with typos and variable speed. https://greasyfork.org/en/users/449798-ace-dx
当前为
// ==UserScript== // @name Human-Typer (Advanced) - Google Docs & Slides // @namespace http://tampermonkey.net/ // @version 1.3 // @description Types your text in a human-like manner with typos and variable speed. https://greasyfork.org/en/users/449798-ace-dx // @author ∫(Ace)³dx // @match https://docs.google.com/* // @icon https://i.imgur.com/z2gxKWZ.png // @grant none // @license MIT // ==/UserScript== if (window.location.href.includes("docs.google.com/document/d") || window.location.href.includes("docs.google.com/presentation/d")) { // Your script code here console.log("Document opened, Human-Typer available!"); // Create the "Human-Typer" button const humanTyperButton = document.createElement("div"); humanTyperButton.textContent = "Human-Typer"; humanTyperButton.classList.add("menu-button", "goog-control", "goog-inline-block"); humanTyperButton.style.userSelect = "none"; humanTyperButton.setAttribute("aria-haspopup", "true"); humanTyperButton.setAttribute("aria-expanded", "false"); humanTyperButton.setAttribute("aria-disabled", "false"); humanTyperButton.setAttribute("role", "menuitem"); humanTyperButton.id = "human-typer-button"; humanTyperButton.style.transition = "color 0.3s"; // Create the "Stop" button const stopButton = document.createElement("div"); stopButton.textContent = "Stop"; stopButton.classList.add("menu-button", "goog-control", "goog-inline-block"); stopButton.style.userSelect = "none"; stopButton.style.color = "red"; stopButton.style.cursor = "pointer"; stopButton.style.transition = "color 0.3s"; stopButton.id = "stop-button"; stopButton.style.display = "none"; // Insert the buttons into the page const helpMenu = document.getElementById("docs-help-menu"); helpMenu.parentNode.insertBefore(humanTyperButton, helpMenu); humanTyperButton.parentNode.insertBefore(stopButton, humanTyperButton.nextSibling); let cancelTyping = false; let typingInProgress = false; let lowerBoundWPM = 20; // Default lower bound WPM let upperBoundWPM = 60; // Default upper bound WPM let typingDuration = 60; // Default typing duration in minutes let introduceTypos = false; let fluctuateSpeed = false; // Function to create and show the overlay function showOverlay() { const overlay = document.createElement("div"); overlay.style.position = "fixed"; overlay.style.top = "50%"; overlay.style.left = "50%"; overlay.style.transform = "translate(-50%, -50%)"; overlay.style.backgroundColor = "rgba(255, 255, 255, 0.9)"; overlay.style.padding = "20px"; overlay.style.borderRadius = "8px"; overlay.style.boxShadow = "0px 2px 10px rgba(0, 0, 0, 0.1)"; overlay.style.zIndex = "9999"; overlay.style.display = "flex"; overlay.style.flexDirection = "column"; overlay.style.alignItems = "center"; overlay.style.width = "320px"; const textField = document.createElement("textarea"); textField.rows = "5"; textField.cols = "40"; textField.placeholder = "Enter your text..."; textField.style.marginBottom = "10px"; textField.style.width = "100%"; textField.style.padding = "8px"; textField.style.border = "1px solid #ccc"; textField.style.borderRadius = "4px"; textField.style.resize = "vertical"; const description = document.createElement("p"); description.textContent = "Keep this tab open; otherwise, the script will pause and resume when you return. Customize typing speed and duration."; description.style.fontSize = "14px"; description.style.marginBottom = "15px"; const randomDelayLabel = document.createElement("div"); randomDelayLabel.style.marginBottom = "5px"; const lowerBoundLabel = document.createElement("label"); lowerBoundLabel.textContent = "Lower Bound WPM: "; const lowerBoundInput = document.createElement("input"); lowerBoundInput.type = "number"; lowerBoundInput.min = "0"; lowerBoundInput.value = lowerBoundWPM; // Set the value from the stored variable lowerBoundInput.style.marginRight = "10px"; lowerBoundInput.style.padding = "6px"; lowerBoundInput.style.border = "1px solid #ccc"; lowerBoundInput.style.borderRadius = "4px"; const upperBoundLabel = document.createElement("label"); upperBoundLabel.textContent = "Upper Bound WPM: "; const upperBoundInput = document.createElement("input"); upperBoundInput.type = "number"; upperBoundInput.min = "0"; upperBoundInput.value = upperBoundWPM; // Set the value from the stored variable upperBoundInput.style.marginRight = "10px"; upperBoundInput.style.padding = "6px"; upperBoundInput.style.border = "1px solid #ccc"; upperBoundInput.style.borderRadius = "4px"; const durationLabel = document.createElement("label"); durationLabel.textContent = "Duration (minutes): "; const durationSelect = document.createElement("select"); [60, 120, 180, 240].forEach((min) => { const option = document.createElement("option"); option.value = min; option.textContent = `${min / 60} hour${min > 60 ? 's' : ''}`; durationSelect.appendChild(option); }); durationSelect.value = typingDuration; // Set the value from the stored variable const typoCheckbox = document.createElement("input"); typoCheckbox.type = "checkbox"; typoCheckbox.id = "introduce-typos"; typoCheckbox.checked = introduceTypos; const typoLabel = document.createElement("label"); typoLabel.htmlFor = "introduce-typos"; typoLabel.textContent = "Introduce typos"; const fluctuationCheckbox = document.createElement("input"); fluctuationCheckbox.type = "checkbox"; fluctuationCheckbox.id = "fluctuate-speed"; fluctuationCheckbox.checked = fluctuateSpeed; const fluctuationLabel = document.createElement("label"); fluctuationLabel.htmlFor = "fluctuate-speed"; fluctuationLabel.textContent = "Fluctuate typing speed"; const confirmButton = document.createElement("button"); confirmButton.textContent = "Confirm"; confirmButton.style.padding = "8px 16px"; confirmButton.style.backgroundColor = "#1a73e8"; confirmButton.style.color = "white"; confirmButton.style.border = "none"; confirmButton.style.borderRadius = "4px"; confirmButton.style.cursor = "pointer"; confirmButton.style.transition = "background-color 0.3s"; overlay.appendChild(description); overlay.appendChild(textField); overlay.appendChild(randomDelayLabel); overlay.appendChild(lowerBoundLabel); overlay.appendChild(lowerBoundInput); overlay.appendChild(upperBoundLabel); overlay.appendChild(upperBoundInput); overlay.appendChild(document.createElement("br")); overlay.appendChild(durationLabel); overlay.appendChild(durationSelect); overlay.appendChild(document.createElement("br")); overlay.appendChild(typoCheckbox); overlay.appendChild(typoLabel); overlay.appendChild(document.createElement("br")); overlay.appendChild(fluctuationCheckbox); overlay.appendChild(fluctuationLabel); overlay.appendChild(document.createElement("br")); overlay.appendChild(confirmButton); document.body.appendChild(overlay); return new Promise((resolve) => { const updateRandomDelayLabel = () => { const charCount = textField.value.length; const eta = Math.ceil((charCount / (lowerBoundWPM * 5 / 60)) / 60); // Estimate in hours randomDelayLabel.textContent = `ETA: ${eta} hour(s)`; }; const handleConfirmClick = () => { const userInput = textField.value.trim(); lowerBoundWPM = parseInt(lowerBoundInput.value); // Store the updated value upperBoundWPM = parseInt(upperBoundInput.value); // Store the updated value typingDuration = parseInt(durationSelect.value); // Store the updated value introduceTypos = typoCheckbox.checked; fluctuateSpeed = fluctuationCheckbox.checked; if (userInput === "") { document.body.removeChild(overlay); return; } if (isNaN(lowerBoundWPM) || isNaN(upperBoundWPM) || lowerBoundWPM < 0 || upperBoundWPM < lowerBoundWPM) return; typingInProgress = true; // Typing has started stopButton.style.display = "inline"; // Show the stop button document.body.removeChild(overlay); resolve({ userInput }); }; confirmButton.addEventListener("click", handleConfirmClick); textField.addEventListener("input", updateRandomDelayLabel); lowerBoundInput.addEventListener("input", updateRandomDelayLabel); upperBoundInput.addEventListener("input", updateRandomDelayLabel); durationSelect.addEventListener("change", updateRandomDelayLabel); }); } function introduceTypingErrors(text) { // Introduce random typos let typoText = ""; for (let i = 0; i < text.length; i++) { if (Math.random() < 0.05) { // 5% chance of typo typoText += String.fromCharCode(Math.floor(Math.random() * 26) + 97); // Random lowercase letter } else { typoText += text[i]; } } return typoText; } function correctTypos(text, originalText) { // Correct typos let correctedText = ""; let typoIndex = 0; for (let i = 0; i < originalText.length; i++) { if (typoIndex < text.length && text[typoIndex] !== originalText[i]) { correctedText += originalText[i]; typoIndex++; } else { correctedText += text[typoIndex] || ""; typoIndex++; } } return correctedText; } function calculateTypingDelay(wpm) { const charsPerWord = 5; // Average word length const minutesPerHour = 60; const msPerMinute = 60000; const totalCharsPerMinute = wpm * charsPerWord; return msPerMinute / totalCharsPerMinute; } function getRandomDelay() { const baseDelay = calculateTypingDelay((Math.random() * (upperBoundWPM - lowerBoundWPM)) + lowerBoundWPM); return fluctuateSpeed ? baseDelay * (0.5 + Math.random()) : baseDelay; } async function simulateTyping(inputElement, char, delay) { return new Promise((resolve) => { if (cancelTyping) { stopButton.style.display = "none"; console.log("Typing cancelled"); resolve(); return; } setTimeout(() => { let eventObj; if (char === "\n") { eventObj = new KeyboardEvent("keydown", { bubbles: true, key: "Enter", code: "Enter", keyCode: 13, which: 13, charCode: 13, }); } else { eventObj = new KeyboardEvent("keypress", { bubbles: true, key: char, charCode: char.charCodeAt(0), keyCode: char.charCodeAt(0), which: char.charCodeAt(0), }); } inputElement.dispatchEvent(eventObj); console.log(`Typed: ${char}, Delay: ${delay}ms`); resolve(); }, delay); }); } async function typeStringWithRandomDelay(inputElement, string) { const startTime = Date.now(); const endTime = startTime + (typingDuration * 60 * 60 * 1000); // Convert duration to milliseconds let currentText = ""; while (Date.now() < endTime) { for (let i = 0; i < string.length; i++) { const char = string[i]; const delay = getRandomDelay(); if (cancelTyping) { stopButton.style.display = "none"; console.log("Typing cancelled"); return; } await simulateTyping(inputElement, char, delay); if (introduceTypos && Math.random() < 0.05) { currentText = introduceTypingErrors(currentText); } if (correctTypos(currentText, string) === string) { continue; } } } typingInProgress = false; // Typing has finished stopButton.style.display = "none"; // Hide the stop button } humanTyperButton.addEventListener("mouseenter", () => { humanTyperButton.classList.add("goog-control-hover"); }); humanTyperButton.addEventListener("mouseleave", () => { humanTyperButton.classList.remove("goog-control-hover"); }); stopButton.addEventListener("mouseenter", () => { stopButton.classList.add("goog-control-hover"); }); stopButton.addEventListener("mouseleave", () => { stopButton.classList.remove("goog-control-hover"); }); humanTyperButton.addEventListener("click", async () => { if (typingInProgress) { console.log("Typing in progress, please wait..."); return; } cancelTyping = false; stopButton.style.display = "none"; // Hide the stop button const { userInput } = await showOverlay(); if (userInput !== "") { const input = document.querySelector(".docs-texteventtarget-iframe").contentDocument.activeElement; typeStringWithRandomDelay(input, userInput); } }); } else { console.log("Document not open, Human-Typer not available."); }