您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Types text in a human-like manner with adjustable speed, typos, and duration.
当前为
// ==UserScript== // @name Human-Typer (Enhanced) - Google Docs & Slides // @namespace http://tampermonkey.net/ // @version 1.5 // @description Types text in a human-like manner with adjustable speed, typos, and duration. // @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")) { 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.style.cursor = "pointer"; humanTyperButton.style.padding = "10px"; humanTyperButton.style.backgroundColor = "#1a73e8"; humanTyperButton.style.color = "white"; humanTyperButton.style.borderRadius = "4px"; humanTyperButton.id = "human-typer-button"; // 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.padding = "10px"; stopButton.style.borderRadius = "4px"; stopButton.id = "stop-button"; stopButton.style.display = "none"; // Insert the buttons into the page const helpMenu = document.getElementById("docs-help-menu"); if (helpMenu) { helpMenu.parentNode.insertBefore(humanTyperButton, helpMenu); humanTyperButton.parentNode.insertBefore(stopButton, humanTyperButton.nextSibling); } else { console.error("Help menu not found."); } let cancelTyping = false; let typingInProgress = false; let lowerBoundWPM = 30; // Default lower bound WPM let upperBoundWPM = 60; // Default upper bound WPM let typingDuration = 1; // Default duration in hours let introduceTypos = false; let fluctuateSpeed = false; // Function to create and show the overlay function showOverlay() { return new Promise((resolve) => { 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 = "Configure typing options below:"; description.style.fontSize = "14px"; description.style.marginBottom = "15px"; const durationLabel = document.createElement("label"); durationLabel.textContent = "Duration (hours): "; const durationSelect = document.createElement("select"); [1, 2, 3, 4].forEach((val) => { const option = document.createElement("option"); option.value = val; option.textContent = `${val} hr${val > 1 ? 's' : ''}`; durationSelect.appendChild(option); }); durationSelect.value = typingDuration; 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; 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; upperBoundInput.style.marginRight = "10px"; upperBoundInput.style.padding = "6px"; upperBoundInput.style.border = "1px solid #ccc"; upperBoundInput.style.borderRadius = "4px"; const typoCheckbox = document.createElement("input"); typoCheckbox.type = "checkbox"; typoCheckbox.id = "typo-checkbox"; const typoLabel = document.createElement("label"); typoLabel.textContent = "Introduce typos"; typoLabel.htmlFor = "typo-checkbox"; typoLabel.style.marginRight = "10px"; const fluctuationCheckbox = document.createElement("input"); fluctuationCheckbox.type = "checkbox"; fluctuationCheckbox.id = "fluctuation-checkbox"; const fluctuationLabel = document.createElement("label"); fluctuationLabel.textContent = "Fluctuate speed"; fluctuationLabel.htmlFor = "fluctuation-checkbox"; fluctuationLabel.style.marginRight = "10px"; 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(durationLabel); overlay.appendChild(durationSelect); overlay.appendChild(randomDelayLabel); overlay.appendChild(lowerBoundLabel); overlay.appendChild(lowerBoundInput); overlay.appendChild(upperBoundLabel); overlay.appendChild(upperBoundInput); overlay.appendChild(document.createElement("br")); overlay.appendChild(typoCheckbox); overlay.appendChild(typoLabel); overlay.appendChild(fluctuationCheckbox); overlay.appendChild(fluctuationLabel); overlay.appendChild(document.createElement("br")); overlay.appendChild(confirmButton); document.body.appendChild(overlay); const updateRandomDelayLabel = () => { const charCount = textField.value.length; const etaLowerBound = Math.ceil((charCount * 60) / (parseInt(lowerBoundInput.value) * 5)); const etaUpperBound = Math.ceil((charCount * 60) / (parseInt(upperBoundInput.value) * 5)); randomDelayLabel.textContent = `ETA: ${etaLowerBound} - ${etaUpperBound} minutes`; }; const handleConfirmClick = () => { const userInput = textField.value.trim(); lowerBoundWPM = parseInt(lowerBoundInput.value); upperBoundWPM = parseInt(upperBoundInput.value); typingDuration = parseInt(durationSelect.value); introduceTypos = typoCheckbox.checked; fluctuateSpeed = fluctuationCheckbox.checked; if (userInput === "" || isNaN(lowerBoundWPM) || isNaN(upperBoundWPM) || lowerBoundWPM < 0 || upperBoundWPM < lowerBoundWPM) { document.body.removeChild(overlay); 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); }); } 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; 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 words = string.split(" "); const totalChars = string.length; const startTime = Date.now(); const totalDuration = typingDuration * 3600000; // Convert hours to milliseconds for (let i = 0; i < words.length; i++) { if (cancelTyping) { stopButton.style.display = "none"; console.log("Typing cancelled"); return; } const word = words[i]; const baseWPM = (Math.random() * (upperBoundWPM - lowerBoundWPM)) + lowerBoundWPM; const delayPerWord = (60 / baseWPM) * 60000; // Convert WPM to milliseconds for (let j = 0; j < word.length; j++) { const char = word[j]; const typoChar = introduceTypos && Math.random() < 0.05 ? String.fromCharCode(Math.random() * 26 + 65) : char; // Random typo const randomDelay = fluctuateSpeed ? delayPerWord * (0.5 + Math.random()) : delayPerWord; await simulateTyping(inputElement, typoChar, randomDelay); } if (i < words.length - 1) { await simulateTyping(inputElement, " ", 0); // Space between words } // Check if typing exceeds the duration if (Date.now() - startTime > totalDuration) { cancelTyping = true; break; } } typingInProgress = false; // Typing has finished stopButton.style.display = "none"; // Hide the stop button } typeStringWithRandomDelay(input, userInput); } }); } else { console.log("Document not open, Human-Typer not available."); }