您需要先安装一个扩展,例如 篡改猴、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.");
- }