Human-Typer (Advanced) - Google Docs & Slides

Types your text in a human-like manner with typos and variable speed. https://greasyfork.org/en/users/449798-ace-dx

当前为 2024-09-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Human-Typer (Advanced) - Google Docs & Slides
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3
  5. // @description Types your text in a human-like manner with typos and variable speed. https://greasyfork.org/en/users/449798-ace-dx
  6. // @author ∫(Ace)³dx
  7. // @match https://docs.google.com/*
  8. // @icon https://i.imgur.com/z2gxKWZ.png
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. if (window.location.href.includes("docs.google.com/document/d") || window.location.href.includes("docs.google.com/presentation/d")) {
  14. // Your script code here
  15. console.log("Document opened, Human-Typer available!");
  16.  
  17. // Create the "Human-Typer" button
  18. const humanTyperButton = document.createElement("div");
  19. humanTyperButton.textContent = "Human-Typer";
  20. humanTyperButton.classList.add("menu-button", "goog-control", "goog-inline-block");
  21. humanTyperButton.style.userSelect = "none";
  22. humanTyperButton.setAttribute("aria-haspopup", "true");
  23. humanTyperButton.setAttribute("aria-expanded", "false");
  24. humanTyperButton.setAttribute("aria-disabled", "false");
  25. humanTyperButton.setAttribute("role", "menuitem");
  26. humanTyperButton.id = "human-typer-button";
  27. humanTyperButton.style.transition = "color 0.3s";
  28. // Create the "Stop" button
  29. const stopButton = document.createElement("div");
  30. stopButton.textContent = "Stop";
  31. stopButton.classList.add("menu-button", "goog-control", "goog-inline-block");
  32. stopButton.style.userSelect = "none";
  33. stopButton.style.color = "red";
  34. stopButton.style.cursor = "pointer";
  35. stopButton.style.transition = "color 0.3s";
  36. stopButton.id = "stop-button";
  37. stopButton.style.display = "none";
  38. // Insert the buttons into the page
  39. const helpMenu = document.getElementById("docs-help-menu");
  40. helpMenu.parentNode.insertBefore(humanTyperButton, helpMenu);
  41. humanTyperButton.parentNode.insertBefore(stopButton, humanTyperButton.nextSibling);
  42. let cancelTyping = false;
  43. let typingInProgress = false;
  44. let lowerBoundWPM = 20; // Default lower bound WPM
  45. let upperBoundWPM = 60; // Default upper bound WPM
  46. let typingDuration = 60; // Default typing duration in minutes
  47. let introduceTypos = false;
  48. let fluctuateSpeed = false;
  49. // Function to create and show the overlay
  50. function showOverlay() {
  51. const overlay = document.createElement("div");
  52. overlay.style.position = "fixed";
  53. overlay.style.top = "50%";
  54. overlay.style.left = "50%";
  55. overlay.style.transform = "translate(-50%, -50%)";
  56. overlay.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
  57. overlay.style.padding = "20px";
  58. overlay.style.borderRadius = "8px";
  59. overlay.style.boxShadow = "0px 2px 10px rgba(0, 0, 0, 0.1)";
  60. overlay.style.zIndex = "9999";
  61. overlay.style.display = "flex";
  62. overlay.style.flexDirection = "column";
  63. overlay.style.alignItems = "center";
  64. overlay.style.width = "320px";
  65. const textField = document.createElement("textarea");
  66. textField.rows = "5";
  67. textField.cols = "40";
  68. textField.placeholder = "Enter your text...";
  69. textField.style.marginBottom = "10px";
  70. textField.style.width = "100%";
  71. textField.style.padding = "8px";
  72. textField.style.border = "1px solid #ccc";
  73. textField.style.borderRadius = "4px";
  74. textField.style.resize = "vertical";
  75. const description = document.createElement("p");
  76. description.textContent = "Keep this tab open; otherwise, the script will pause and resume when you return. Customize typing speed and duration.";
  77. description.style.fontSize = "14px";
  78. description.style.marginBottom = "15px";
  79. const randomDelayLabel = document.createElement("div");
  80. randomDelayLabel.style.marginBottom = "5px";
  81. const lowerBoundLabel = document.createElement("label");
  82. lowerBoundLabel.textContent = "Lower Bound WPM: ";
  83. const lowerBoundInput = document.createElement("input");
  84. lowerBoundInput.type = "number";
  85. lowerBoundInput.min = "0";
  86. lowerBoundInput.value = lowerBoundWPM; // Set the value from the stored variable
  87. lowerBoundInput.style.marginRight = "10px";
  88. lowerBoundInput.style.padding = "6px";
  89. lowerBoundInput.style.border = "1px solid #ccc";
  90. lowerBoundInput.style.borderRadius = "4px";
  91. const upperBoundLabel = document.createElement("label");
  92. upperBoundLabel.textContent = "Upper Bound WPM: ";
  93. const upperBoundInput = document.createElement("input");
  94. upperBoundInput.type = "number";
  95. upperBoundInput.min = "0";
  96. upperBoundInput.value = upperBoundWPM; // Set the value from the stored variable
  97. upperBoundInput.style.marginRight = "10px";
  98. upperBoundInput.style.padding = "6px";
  99. upperBoundInput.style.border = "1px solid #ccc";
  100. upperBoundInput.style.borderRadius = "4px";
  101. const durationLabel = document.createElement("label");
  102. durationLabel.textContent = "Duration (minutes): ";
  103. const durationSelect = document.createElement("select");
  104. [60, 120, 180, 240].forEach((min) => {
  105. const option = document.createElement("option");
  106. option.value = min;
  107. option.textContent = `${min / 60} hour${min > 60 ? 's' : ''}`;
  108. durationSelect.appendChild(option);
  109. });
  110. durationSelect.value = typingDuration; // Set the value from the stored variable
  111. const typoCheckbox = document.createElement("input");
  112. typoCheckbox.type = "checkbox";
  113. typoCheckbox.id = "introduce-typos";
  114. typoCheckbox.checked = introduceTypos;
  115. const typoLabel = document.createElement("label");
  116. typoLabel.htmlFor = "introduce-typos";
  117. typoLabel.textContent = "Introduce typos";
  118. const fluctuationCheckbox = document.createElement("input");
  119. fluctuationCheckbox.type = "checkbox";
  120. fluctuationCheckbox.id = "fluctuate-speed";
  121. fluctuationCheckbox.checked = fluctuateSpeed;
  122. const fluctuationLabel = document.createElement("label");
  123. fluctuationLabel.htmlFor = "fluctuate-speed";
  124. fluctuationLabel.textContent = "Fluctuate typing speed";
  125. const confirmButton = document.createElement("button");
  126. confirmButton.textContent = "Confirm";
  127. confirmButton.style.padding = "8px 16px";
  128. confirmButton.style.backgroundColor = "#1a73e8";
  129. confirmButton.style.color = "white";
  130. confirmButton.style.border = "none";
  131. confirmButton.style.borderRadius = "4px";
  132. confirmButton.style.cursor = "pointer";
  133. confirmButton.style.transition = "background-color 0.3s";
  134. overlay.appendChild(description);
  135. overlay.appendChild(textField);
  136. overlay.appendChild(randomDelayLabel);
  137. overlay.appendChild(lowerBoundLabel);
  138. overlay.appendChild(lowerBoundInput);
  139. overlay.appendChild(upperBoundLabel);
  140. overlay.appendChild(upperBoundInput);
  141. overlay.appendChild(document.createElement("br"));
  142. overlay.appendChild(durationLabel);
  143. overlay.appendChild(durationSelect);
  144. overlay.appendChild(document.createElement("br"));
  145. overlay.appendChild(typoCheckbox);
  146. overlay.appendChild(typoLabel);
  147. overlay.appendChild(document.createElement("br"));
  148. overlay.appendChild(fluctuationCheckbox);
  149. overlay.appendChild(fluctuationLabel);
  150. overlay.appendChild(document.createElement("br"));
  151. overlay.appendChild(confirmButton);
  152. document.body.appendChild(overlay);
  153. return new Promise((resolve) => {
  154. const updateRandomDelayLabel = () => {
  155. const charCount = textField.value.length;
  156. const eta = Math.ceil((charCount / (lowerBoundWPM * 5 / 60)) / 60); // Estimate in hours
  157. randomDelayLabel.textContent = `ETA: ${eta} hour(s)`;
  158. };
  159. const handleConfirmClick = () => {
  160. const userInput = textField.value.trim();
  161. lowerBoundWPM = parseInt(lowerBoundInput.value); // Store the updated value
  162. upperBoundWPM = parseInt(upperBoundInput.value); // Store the updated value
  163. typingDuration = parseInt(durationSelect.value); // Store the updated value
  164. introduceTypos = typoCheckbox.checked;
  165. fluctuateSpeed = fluctuationCheckbox.checked;
  166. if (userInput === "") {
  167. document.body.removeChild(overlay);
  168. return;
  169. }
  170. if (isNaN(lowerBoundWPM) || isNaN(upperBoundWPM) || lowerBoundWPM < 0 || upperBoundWPM < lowerBoundWPM) return;
  171. typingInProgress = true; // Typing has started
  172. stopButton.style.display = "inline"; // Show the stop button
  173. document.body.removeChild(overlay);
  174. resolve({ userInput });
  175. };
  176. confirmButton.addEventListener("click", handleConfirmClick);
  177. textField.addEventListener("input", updateRandomDelayLabel);
  178. lowerBoundInput.addEventListener("input", updateRandomDelayLabel);
  179. upperBoundInput.addEventListener("input", updateRandomDelayLabel);
  180. durationSelect.addEventListener("change", updateRandomDelayLabel);
  181. });
  182. }
  183. function introduceTypingErrors(text) {
  184. // Introduce random typos
  185. let typoText = "";
  186. for (let i = 0; i < text.length; i++) {
  187. if (Math.random() < 0.05) { // 5% chance of typo
  188. typoText += String.fromCharCode(Math.floor(Math.random() * 26) + 97); // Random lowercase letter
  189. } else {
  190. typoText += text[i];
  191. }
  192. }
  193. return typoText;
  194. }
  195. function correctTypos(text, originalText) {
  196. // Correct typos
  197. let correctedText = "";
  198. let typoIndex = 0;
  199. for (let i = 0; i < originalText.length; i++) {
  200. if (typoIndex < text.length && text[typoIndex] !== originalText[i]) {
  201. correctedText += originalText[i];
  202. typoIndex++;
  203. } else {
  204. correctedText += text[typoIndex] || "";
  205. typoIndex++;
  206. }
  207. }
  208. return correctedText;
  209. }
  210. function calculateTypingDelay(wpm) {
  211. const charsPerWord = 5; // Average word length
  212. const minutesPerHour = 60;
  213. const msPerMinute = 60000;
  214. const totalCharsPerMinute = wpm * charsPerWord;
  215. return msPerMinute / totalCharsPerMinute;
  216. }
  217. function getRandomDelay() {
  218. const baseDelay = calculateTypingDelay((Math.random() * (upperBoundWPM - lowerBoundWPM)) + lowerBoundWPM);
  219. return fluctuateSpeed ? baseDelay * (0.5 + Math.random()) : baseDelay;
  220. }
  221. async function simulateTyping(inputElement, char, delay) {
  222. return new Promise((resolve) => {
  223. if (cancelTyping) {
  224. stopButton.style.display = "none";
  225. console.log("Typing cancelled");
  226. resolve();
  227. return;
  228. }
  229. setTimeout(() => {
  230. let eventObj;
  231. if (char === "\n") {
  232. eventObj = new KeyboardEvent("keydown", {
  233. bubbles: true,
  234. key: "Enter",
  235. code: "Enter",
  236. keyCode: 13,
  237. which: 13,
  238. charCode: 13,
  239. });
  240. } else {
  241. eventObj = new KeyboardEvent("keypress", {
  242. bubbles: true,
  243. key: char,
  244. charCode: char.charCodeAt(0),
  245. keyCode: char.charCodeAt(0),
  246. which: char.charCodeAt(0),
  247. });
  248. }
  249. inputElement.dispatchEvent(eventObj);
  250. console.log(`Typed: ${char}, Delay: ${delay}ms`);
  251. resolve();
  252. }, delay);
  253. });
  254. }
  255. async function typeStringWithRandomDelay(inputElement, string) {
  256. const startTime = Date.now();
  257. const endTime = startTime + (typingDuration * 60 * 60 * 1000); // Convert duration to milliseconds
  258. let currentText = "";
  259. while (Date.now() < endTime) {
  260. for (let i = 0; i < string.length; i++) {
  261. const char = string[i];
  262. const delay = getRandomDelay();
  263. if (cancelTyping) {
  264. stopButton.style.display = "none";
  265. console.log("Typing cancelled");
  266. return;
  267. }
  268. await simulateTyping(inputElement, char, delay);
  269. if (introduceTypos && Math.random() < 0.05) {
  270. currentText = introduceTypingErrors(currentText);
  271. }
  272. if (correctTypos(currentText, string) === string) {
  273. continue;
  274. }
  275. }
  276. }
  277. typingInProgress = false; // Typing has finished
  278. stopButton.style.display = "none"; // Hide the stop button
  279. }
  280. humanTyperButton.addEventListener("mouseenter", () => {
  281. humanTyperButton.classList.add("goog-control-hover");
  282. });
  283. humanTyperButton.addEventListener("mouseleave", () => {
  284. humanTyperButton.classList.remove("goog-control-hover");
  285. });
  286. stopButton.addEventListener("mouseenter", () => {
  287. stopButton.classList.add("goog-control-hover");
  288. });
  289. stopButton.addEventListener("mouseleave", () => {
  290. stopButton.classList.remove("goog-control-hover");
  291. });
  292. humanTyperButton.addEventListener("click", async () => {
  293. if (typingInProgress) {
  294. console.log("Typing in progress, please wait...");
  295. return;
  296. }
  297. cancelTyping = false;
  298. stopButton.style.display = "none"; // Hide the stop button
  299. const { userInput } = await showOverlay();
  300. if (userInput !== "") {
  301. const input = document.querySelector(".docs-texteventtarget-iframe").contentDocument.activeElement;
  302. typeStringWithRandomDelay(input, userInput);
  303. }
  304. });
  305. } else {
  306. console.log("Document not open, Human-Typer not available.");
  307. }