Greasy Fork is available in English.

Human-Typer by Warrior

Simulate human typing in Google Docs and Slides with customizable settings such as typing speed, errors, and breaks.

// ==UserScript==
// @name         Human-Typer by Warrior
// @description  Simulate human typing in Google Docs and Slides with customizable settings such as typing speed, errors, and breaks.
// @version      1.3
// @namespace    http://yournamespace.com/human-typer
// @match        *://docs.google.com/document/*
// @match        *://docs.google.com/presentation/*
// @include      *://docs.google.com/document/*
// @include      *://docs.google.com/presentation/*
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Function to create a button on the Google Docs/Slides toolbar
    function createButton() {
        const toolbar = document.querySelector('.docs-titlebar-right');
        if (!toolbar) {
            return;
        }

        const button = document.createElement("button");
        button.textContent = "Human-Typer";
        button.style.marginLeft = "10px";
        button.style.backgroundColor = "#000";
        button.style.color = "#fff";
        button.style.border = "none";
        button.style.padding = "8px 12px";
        button.style.borderRadius = "4px";
        button.style.cursor = "pointer";
        button.id = "human-typer-button";

        // Add event listener to show UI overlay when button is clicked
        button.addEventListener("click", showUIOverlay);
        toolbar.appendChild(button);
    }

    // Function to create and display the UI overlay for user customization
    function showUIOverlay() {
        // Check if an overlay already exists; if so, remove it
        const existingOverlay = document.getElementById("human-typer-overlay");
        if (existingOverlay) {
            document.body.removeChild(existingOverlay);
            return;
        }

        // Create an overlay div for user input and customization options
        const overlay = document.createElement("div");
        overlay.id = "human-typer-overlay";
        overlay.style.position = "fixed";
        overlay.style.bottom = "10px";
        overlay.style.right = "10px";
        overlay.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
        overlay.style.padding = "20px";
        overlay.style.border = "1px solid #ccc";
        overlay.style.borderRadius = "8px";
        overlay.style.boxShadow = "0px 2px 8px rgba(0, 0, 0, 0.1)";
        overlay.style.zIndex = "10000";

        // Create input field for text input
        const textInput = document.createElement("textarea");
        textInput.rows = "4";
        textInput.placeholder = "Enter text to type...";
        textInput.style.width = "100%";
        textInput.style.marginBottom = "10px";
        textInput.style.padding = "8px";
        textInput.style.border = "1px solid #ccc";
        textInput.style.borderRadius = "4px";

        // Create dropdown for typing speed selection
        const speedLabel = document.createElement("p");
        speedLabel.textContent = "Typing Speed:";
        const speedSelect = document.createElement("select");
        speedSelect.options.add(new Option("Fast", "fast"));
        speedSelect.options.add(new Option("Medium", "medium"));
        speedSelect.options.add(new Option("Normal", "normal"));
        speedSelect.options.add(new Option("Slow", "slow"));
        speedSelect.style.marginBottom = "10px";

        // Create input for number of typing errors
        const errorLabel = document.createElement("p");
        errorLabel.textContent = "Number of Typing Errors:";
        const errorInput = document.createElement("input");
        errorInput.type = "number";
        errorInput.min = "0";
        errorInput.value = "0";
        errorInput.style.width = "100%";
        errorInput.style.marginBottom = "10px";
        errorInput.style.padding = "8px";
        errorInput.style.border = "1px solid #ccc";
        errorInput.style.borderRadius = "4px";

        // Create input for the number of breaks
        const breakLabel = document.createElement("p");
        breakLabel.textContent = "Number of Breaks:";
        const breakInput = document.createElement("input");
        breakInput.type = "number";
        breakInput.min = "0";
        breakInput.value = "0";
        breakInput.style.width = "100%";
        breakInput.style.marginBottom = "10px";
        breakInput.style.padding = "8px";
        breakInput.style.border = "1px solid #ccc";
        breakInput.style.borderRadius = "4px";

        // Create input for the duration of each break in minutes
        const breakDurationLabel = document.createElement("p");
        breakDurationLabel.textContent = "Duration of Each Break (minutes):";
        const breakDurationInput = document.createElement("input");
        breakDurationInput.type = "number";
        breakDurationInput.min = "0";
        breakDurationInput.value = "0";
        breakDurationInput.style.width = "100%";
        breakDurationInput.style.marginBottom = "10px";
        breakDurationInput.style.padding = "8px";
        breakDurationInput.style.border = "1px solid #ccc";
        breakDurationInput.style.borderRadius = "4px";

        // Create a button to start typing
        const startButton = document.createElement("button");
        startButton.textContent = "Start Typing";
        startButton.style.padding = "8px 16px";
        startButton.style.backgroundColor = "#1a73e8";
        startButton.style.color = "#fff";
        startButton.style.border = "none";
        startButton.style.borderRadius = "4px";
        startButton.style.cursor = "pointer";

        // Append elements to the overlay
        overlay.appendChild(textInput);
        overlay.appendChild(speedLabel);
        overlay.appendChild(speedSelect);
        overlay.appendChild(errorLabel);
        overlay.appendChild(errorInput);
        overlay.appendChild(breakLabel);
        overlay.appendChild(breakInput);
        overlay.appendChild(breakDurationLabel);
        overlay.appendChild(breakDurationInput);
        overlay.appendChild(startButton);

        // Append the overlay to the body
        document.body.appendChild(overlay);

        // Add an event listener to the start button
        startButton.addEventListener("click", () => {
            // Get user input values from the overlay
            const text = textInput.value.trim();
            const speed = speedSelect.value;
            const numErrors = parseInt(errorInput.value);
            const numBreaks = parseInt(breakInput.value);
            const breakDuration = parseInt(breakDurationInput.value);

            // Remove the overlay from the page
            document.body.removeChild(overlay);

            // Call function to start typing with user-defined options
            startTyping(text, speed, numErrors, numBreaks, breakDuration);
        });
    }

    // Function to simulate typing with errors and breaks
    function startTyping(text, speed, numErrors, numBreaks, breakDuration) {
        // Select the active element in the iframe to simulate typing
        const inputElement = document.querySelector(".docs-texteventtarget-iframe").contentDocument.activeElement;

        // Define speed settings based on user choice
        const speedSettings = {
            fast: { lowerBound: 50, upperBound: 150 },
            medium: { lowerBound: 60, upperBound: 220 },
            normal: { lowerBound: 70, upperBound: 200 },
            slow: { lowerBound: 80, upperBound: 250 }
        };

        // Get the lower and upper bounds for the chosen typing speed
        const lowerBound = speedSettings[speed].lowerBound;
        const upperBound = speedSettings[speed].upperBound;

        let currentErrorCount = 0; // Track current error count
        let currentBreakCount = 0; // Track current break count

        // Calculate interval for breaks based on the number of breaks
        let breakInterval = numBreaks > 0 ? Math.floor(text.length / numBreaks) : text.length;

        // Convert break duration from minutes to milliseconds
        let breakTime = breakDuration * 60 * 1000;

        // Function to type a character with optional errors and delay
        async function typeCharacter(character, delay) {
            return new Promise((resolve) => {
                if (currentErrorCount < numErrors && Math.random() < 0.05) {
                    // Introduce a random typing error
                    inputElement.value += character;
                    inputElement.value = inputElement.value.slice(0, -2);
                    currentErrorCount++;
                } else {
                    inputElement.value += character;
                }

                setTimeout(resolve, delay);
            });
        }

        // Function to type the text with the defined speed, errors, and breaks
        async function typeText(text) {
            for (let i = 0; i < text.length; i++) {
                // Generate a random delay based on the chosen speed
                const delay = Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound;

                // Check if it's time to take a break
                if (currentBreakCount < numBreaks && i > 0 && i % breakInterval === 0) {
                    // Take a break
                    await new Promise((resolve) => setTimeout(resolve, breakTime));
                    currentBreakCount++;
                }

                // Type the current character with a delay
                await typeCharacter(text[i], delay);
            }
        }

        // Start typing the text
        typeText(text);
    }

    // Create the button on the toolbar
    createButton();
})();