Human-Typer (Automatic) - Google Docs & Slides

Types your text in a human-like manner with random errors and corrections.

  1. // ==UserScript==
  2. // @name Human-Typer (Automatic) - Google Docs & Slides
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.9
  5. // @description Types your text in a human-like manner with random errors and corrections.
  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. console.log("Document opened, Human-Typer available!");
  15.  
  16. // Create the "Human-Typer" button
  17. const humanTyperButton = document.createElement("div");
  18. humanTyperButton.textContent = "Human-Typer";
  19. humanTyperButton.classList.add("menu-button", "goog-control", "goog-inline-block");
  20. humanTyperButton.style.userSelect = "none";
  21. humanTyperButton.setAttribute("aria-haspopup", "true");
  22. humanTyperButton.setAttribute("aria-expanded", "false");
  23. humanTyperButton.setAttribute("aria-disabled", "false");
  24. humanTyperButton.setAttribute("role", "menuitem");
  25. humanTyperButton.id = "human-typer-button";
  26. humanTyperButton.style.transition = "color 0.3s";
  27.  
  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.  
  39. // Insert the buttons into the page
  40. const helpMenu = document.getElementById("docs-help-menu");
  41. helpMenu.parentNode.insertBefore(humanTyperButton, helpMenu);
  42. humanTyperButton.parentNode.insertBefore(stopButton, humanTyperButton.nextSibling);
  43.  
  44. let cancelTyping = false;
  45. let typingInProgress = false;
  46. let lowerBoundValue = 60; // Default lower bound value
  47. let upperBoundValue = 140; // Default upper bound value
  48.  
  49. function showOverlay() {
  50. const overlay = document.createElement("div");
  51. overlay.style.position = "fixed";
  52. overlay.style.top = "50%";
  53. overlay.style.left = "50%";
  54. overlay.style.transform = "translate(-50%, -50%)";
  55. overlay.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
  56. overlay.style.padding = "20px";
  57. overlay.style.borderRadius = "8px";
  58. overlay.style.boxShadow = "0px 2px 10px rgba(0, 0, 0, 0.1)";
  59. overlay.style.zIndex = "9999";
  60. overlay.style.display = "flex";
  61. overlay.style.flexDirection = "column";
  62. overlay.style.alignItems = "center";
  63. overlay.style.width = "320px";
  64.  
  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.  
  76. const description = document.createElement("p");
  77. description.textContent = "It's necessary to keep this tab open; otherwise, the script will pause and will resume once you return to it (this behavior is caused by the way the browser functions). Lower bound is the minimum time in milliseconds per character. Upper bound is the maximum time in milliseconds per character. A random delay value will be selected between these bounds for every character in your text, ensuring that the typing appears natural and human-like.";
  78. description.style.fontSize = "14px";
  79. description.style.marginBottom = "15px";
  80.  
  81. const randomDelayLabel = document.createElement("div");
  82. randomDelayLabel.style.marginBottom = "5px";
  83.  
  84. const lowerBoundLabel = document.createElement("label");
  85. lowerBoundLabel.textContent = "Lower Bound (ms): ";
  86. const lowerBoundInput = document.createElement("input");
  87. lowerBoundInput.type = "number";
  88. lowerBoundInput.min = "0";
  89. lowerBoundInput.value = lowerBoundValue;
  90. lowerBoundInput.style.marginRight = "10px";
  91. lowerBoundInput.style.padding = "6px";
  92. lowerBoundInput.style.border = "1px solid #ccc";
  93. lowerBoundInput.style.borderRadius = "4px";
  94.  
  95. const upperBoundLabel = document.createElement("label");
  96. upperBoundLabel.textContent = "Upper Bound (ms): ";
  97. const upperBoundInput = document.createElement("input");
  98. upperBoundInput.type = "number";
  99. upperBoundInput.min = "0";
  100. upperBoundInput.value = upperBoundValue;
  101. upperBoundInput.style.marginRight = "10px";
  102. upperBoundInput.style.padding = "6px";
  103. upperBoundInput.style.border = "1px solid #ccc";
  104. upperBoundInput.style.borderRadius = "4px";
  105.  
  106. const confirmButton = document.createElement("button");
  107. confirmButton.textContent = textField.value.trim() === "" ? "Cancel" : "Confirm";
  108. confirmButton.style.padding = "8px 16px";
  109. confirmButton.style.backgroundColor = "#1a73e8";
  110. confirmButton.style.color = "white";
  111. confirmButton.style.border = "none";
  112. confirmButton.style.borderRadius = "4px";
  113. confirmButton.style.cursor = "pointer";
  114. confirmButton.style.transition = "background-color 0.3s";
  115.  
  116. overlay.appendChild(description);
  117. overlay.appendChild(textField);
  118. overlay.appendChild(randomDelayLabel);
  119. overlay.appendChild(lowerBoundLabel);
  120. overlay.appendChild(lowerBoundInput);
  121. overlay.appendChild(upperBoundLabel);
  122. overlay.appendChild(upperBoundInput);
  123. overlay.appendChild(document.createElement("br"));
  124. overlay.appendChild(confirmButton);
  125. document.body.appendChild(overlay);
  126.  
  127. return new Promise((resolve) => {
  128. const updateRandomDelayLabel = () => {
  129. const charCount = textField.value.length;
  130. const etaLowerBound = Math.ceil((charCount * parseInt(lowerBoundInput.value)) / 60000);
  131. const etaUpperBound = Math.ceil((charCount * parseInt(upperBoundInput.value)) / 60000);
  132. randomDelayLabel.textContent = `ETA: ${etaLowerBound} - ${etaUpperBound} minutes`;
  133. };
  134.  
  135. const handleCancelClick = () => {
  136. cancelTyping = true;
  137. stopButton.style.display = "none";
  138. };
  139.  
  140. confirmButton.addEventListener("click", () => {
  141. const userInput = textField.value.trim();
  142. lowerBoundValue = parseInt(lowerBoundInput.value);
  143. upperBoundValue = parseInt(upperBoundInput.value);
  144.  
  145. if (userInput === "") {
  146. document.body.removeChild(overlay);
  147. return;
  148. }
  149.  
  150. if (isNaN(lowerBoundValue) || isNaN(upperBoundValue) || lowerBoundValue < 0 || upperBoundValue < lowerBoundValue) return;
  151.  
  152. typingInProgress = true;
  153. stopButton.style.display = "inline";
  154. document.body.removeChild(overlay);
  155. resolve({ userInput });
  156. });
  157.  
  158. textField.addEventListener("input", () => {
  159. confirmButton.textContent = textField.value.trim() === "" ? "Cancel" : "Confirm";
  160. updateRandomDelayLabel();
  161. });
  162.  
  163. lowerBoundInput.addEventListener("input", updateRandomDelayLabel);
  164. upperBoundInput.addEventListener("input", updateRandomDelayLabel);
  165.  
  166. stopButton.addEventListener("click", handleCancelClick);
  167. });
  168. }
  169.  
  170. async function simulateTypingError(inputElement, char, delay) {
  171. return new Promise((resolve) => {
  172. setTimeout(() => {
  173. let eventObj;
  174. if (char === "\n") {
  175. eventObj = new KeyboardEvent("keydown", {
  176. bubbles: true,
  177. key: "Enter",
  178. code: "Enter",
  179. keyCode: 13,
  180. which: 13,
  181. charCode: 13,
  182. });
  183. } else {
  184. eventObj = new KeyboardEvent("keypress", {
  185. bubbles: true,
  186. key: char,
  187. charCode: char.charCodeAt(0),
  188. keyCode: char.charCodeAt(0),
  189. which: char.charCodeAt(0),
  190. });
  191. }
  192.  
  193. inputElement.dispatchEvent(eventObj);
  194. console.log(`Typed: ${char}, Delay: ${delay}ms`);
  195. resolve();
  196. }, delay);
  197. });
  198. }
  199.  
  200. async function typeStringWithRandomDelay(inputElement, string) {
  201. const errorChance = 0.1; // 10% chance to simulate an error
  202. const correctionDelay = 500; // Delay to correct an error
  203.  
  204. for (let i = 0; i < string.length; i++) {
  205. if (cancelTyping) break;
  206.  
  207. const char = string[i];
  208. let randomDelay = Math.floor(Math.random() * (upperBoundValue - lowerBoundValue + 1)) + lowerBoundValue;
  209.  
  210. if (Math.random() < errorChance) {
  211. // Simulate typing an incorrect character
  212. const incorrectChar = String.fromCharCode(Math.floor(Math.random() * 26) + 97); // Random lowercase letter
  213. await simulateTypingError(inputElement, incorrectChar, randomDelay);
  214. await new Promise(resolve => setTimeout(resolve, correctionDelay)); // Simulate the delay for correction
  215. await simulateTypingError(inputElement, char, randomDelay);
  216. } else {
  217. await simulateTypingError(inputElement, char, randomDelay);
  218. }
  219. }
  220.  
  221. typingInProgress = false;
  222. stopButton.style.display = "none";
  223. }
  224.  
  225. humanTyperButton.addEventListener("click", async () => {
  226. if (typingInProgress) {
  227. alert("Typing is already in progress.");
  228. return;
  229. }
  230.  
  231. const { userInput } = await showOverlay();
  232. const inputElement = document.activeElement;
  233.  
  234. if (inputElement.tagName === "TEXTAREA" || (inputElement.tagName === "DIV" && inputElement.hasAttribute("contenteditable"))) {
  235. await typeStringWithRandomDelay(inputElement, userInput);
  236. } else {
  237. alert("Please click inside a text area or editable content before starting.");
  238. }
  239. });
  240.  
  241. stopButton.addEventListener("click", () => {
  242. cancelTyping = true;
  243. });
  244. }