YAD Multiple Auto Clicker

Automates clicks at various locations on the screen with customizable intervals

目前为 2024-10-17 提交的版本。查看 最新版本

// ==UserScript==
// @name         YAD Multiple Auto Clicker
// @namespace    https://greasyfork.org/en/users/781396
// @version      2.6
// @description  Automates clicks at various locations on the screen with customizable intervals
// @author       YAD
// @license      MIT
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let buttons = [];
    let buttonId = 1;
    let isRunning = false;

    // Load button positions and settings from localStorage
    const loadButtonData = () => JSON.parse(localStorage.getItem('buttonData') || '[]');

    // Save button positions and settings to localStorage
    const saveButtonData = () => {
        const buttonData = buttons.map(button => ({
            id: button.id,
            top: button.style.top,
            left: button.style.left,
            interval: button.dataset.interval,
            clicks: button.dataset.clicks
        }));
        localStorage.setItem('buttonData', JSON.stringify(buttonData));
    };

    // Save the current running state in localStorage
    const saveRunningState = () => {
        localStorage.setItem('isRunning', JSON.stringify(isRunning));
    };

    // Load the running state from localStorage on page reload
    const loadRunningState = () => {
        return JSON.parse(localStorage.getItem('isRunning') || 'false');
    };

    // Create the main interface
    const mainInterface = document.createElement('div');
    mainInterface.style.position = 'fixed';
    mainInterface.style.right = '10px';
    mainInterface.style.top = '50%';
    mainInterface.style.transform = 'translateY(-50%)';
    mainInterface.style.zIndex = '9999';

    // Plus button to add new auto-click buttons
    const plusButton = document.createElement('button');
    plusButton.textContent = '➕';
    plusButton.style.display = 'block';
    plusButton.style.padding = '10px';
    plusButton.style.marginBottom = '5px';
    plusButton.style.fontSize = '16px';
    plusButton.style.cursor = 'pointer';
    plusButton.style.backgroundColor = '#28a745'; // Green background
    plusButton.style.border = 'none';
    plusButton.style.color = 'transparent';
    plusButton.style.textShadow = '0 0 0 white';
    plusButton.style.borderRadius = '5px';
    mainInterface.appendChild(plusButton);

    // Start/Stop button
    const startStopButton = document.createElement('button');
    startStopButton.textContent = '👆';
    startStopButton.style.display = 'block';
    startStopButton.style.padding = '10px';
    startStopButton.style.marginBottom = '5px';
    startStopButton.style.fontSize = '16px';
    startStopButton.style.cursor = 'pointer';
    startStopButton.style.backgroundColor = '#007bff'; // Blue background
    startStopButton.style.border = 'none';
    startStopButton.style.color = 'white';
    startStopButton.style.borderRadius = '5px';
    mainInterface.appendChild(startStopButton);

    // Reset button
    const resetButton = document.createElement('button');
    resetButton.textContent = '♻️';
    resetButton.style.display = 'block';
    resetButton.style.padding = '10px';
    resetButton.style.fontSize = '16px';
    resetButton.style.cursor = 'pointer';
    resetButton.style.backgroundColor = '#dc3545'; // Red background
    resetButton.style.border = 'none';
    resetButton.style.color = 'transparent';
    resetButton.style.textShadow = '0 0 0 white';
    resetButton.style.borderRadius = '5px';
    mainInterface.appendChild(resetButton);

    document.body.appendChild(mainInterface);

    // Plus button functionality
    plusButton.addEventListener('click', () => {
        createAutoClickButton();
    });

    // Start/Stop button functionality
    startStopButton.addEventListener('click', () => {
        isRunning = !isRunning;
        saveRunningState();
        if (isRunning) {
            startStopButton.textContent = '🛑';
            startAutoClick();
        } else {
            startStopButton.textContent = '👆';
            stopAutoClick();
        }
    });

    // Reset button functionality
    resetButton.addEventListener('click', () => {
        buttons.forEach(button => button.remove());
        buttons = [];
        localStorage.removeItem('buttonData');
        buttonId = 1;
        stopAutoClick();
        startStopButton.textContent = '👆';
        saveRunningState();
    });

    // Function to create a new auto-click button
    function createAutoClickButton(buttonData = null) {
        const autoButton = document.createElement('div');
        autoButton.id = buttonId++;
        autoButton.textContent = autoButton.id;
        autoButton.style.width = '50px';
        autoButton.style.height = '50px';
        autoButton.style.borderRadius = '50%';
        autoButton.style.backgroundColor = '#0061ffcc';
        autoButton.style.position = 'absolute';
        autoButton.style.top = buttonData?.top || '50%';
        autoButton.style.left = buttonData?.left || '50%';
        autoButton.style.display = 'flex';
        autoButton.style.alignItems = 'center';
        autoButton.style.justifyContent = 'center';
        autoButton.style.cursor = 'pointer';
        autoButton.style.zIndex = '9999';
        autoButton.style.color = 'white'; // Ensure text is visible
        autoButton.dataset.interval = buttonData?.interval || 1000; // Default interval
        autoButton.dataset.clicks = buttonData?.clicks || 1; // Default number of clicks
        document.body.appendChild(autoButton);
        makeDraggable(autoButton);

        buttons.push(autoButton);
        saveButtonData();

        // Open settings modal on right-click
        autoButton.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            openSettingsModal(autoButton);
        });
    }

    // Function to make the buttons draggable
    function makeDraggable(element) {
        let posX = 0, posY = 0, mouseX = 0, mouseY = 0;
        element.onmousedown = function (e) {
            e.preventDefault();
            mouseX = e.clientX;
            mouseY = e.clientY;
            document.onmousemove = moveElement;
            document.onmouseup = stopMovingElement;
        };

        function moveElement(e) {
            posX = mouseX - e.clientX;
            posY = mouseY - e.clientY;
            mouseX = e.clientX;
            mouseY = e.clientY;
            element.style.top = (element.offsetTop - posY) + 'px';
            element.style.left = (element.offsetLeft - posX) + 'px';
        }

        function stopMovingElement() {
            document.onmouseup = null;
            document.onmousemove = null;
            saveButtonData();
        }
    }

    // Function to open settings modal
    function openSettingsModal(button) {
        const modal = document.createElement('div');
        modal.style.position = 'fixed';
        modal.style.top = '50%';
        modal.style.left = '50%';
        modal.style.transform = 'translate(-50%, -50%)';
        modal.style.zIndex = '10000';
        modal.style.padding = '15px';
        modal.style.backgroundColor = '#6f42c1';
        modal.style.borderRadius = '8px';
        modal.style.boxShadow = '0px 4px 12px rgba(0, 0, 0, 0.1)';
        modal.style.width = '300px'; // Make it more compact
        modal.style.fontFamily = 'Arial, sans-serif';

        const title = document.createElement('h3');
        title.textContent = 'Settings';
        title.style.marginTop = '0';
        title.style.marginBottom = '15px';
        title.style.color = '#fff';
        title.style.textAlign = 'center';
        title.style.fontSize = '18px';
        modal.appendChild(title);

        const form = document.createElement('form');
        form.style.display = 'flex';
        form.style.flexDirection = 'column';
        form.style.gap = '10px';
        modal.appendChild(form);

        const createInputField = (labelText, inputType, inputValue) => {
            const fieldWrapper = document.createElement('div');
            fieldWrapper.style.display = 'flex';
            fieldWrapper.style.flexDirection = 'column';

            const label = document.createElement('label');
            label.textContent = labelText;
            label.style.fontSize = '14px';
            label.style.color = '#fff';
            label.style.marginBottom = '5px';
            fieldWrapper.appendChild(label);

            const input = document.createElement('input');
            input.type = inputType;
            input.value = inputValue;
            input.style.padding = '5px';
            input.style.borderRadius = '4px';
            input.style.border = '1px solid #ccc';
            input.style.fontSize = '14px';
            fieldWrapper.appendChild(input);

            form.appendChild(fieldWrapper);

            return input;
        };

        const intervalInput = createInputField('Interval (ms):', 'number', button.dataset.interval);
        const clicksInput = createInputField('Clicks per Interval:', 'number', button.dataset.clicks);

        const saveButton = document.createElement('button');
        saveButton.textContent = 'Save';
        saveButton.style.padding = '10px';
        saveButton.style.borderRadius = '5px';
        saveButton.style.border = 'none';
        saveButton.style.backgroundColor = '#28a745';
        saveButton.style.color = 'white';
        saveButton.style.cursor = 'pointer';
        form.appendChild(saveButton);

        saveButton.addEventListener('click', (e) => {
            e.preventDefault();
            button.dataset.interval = intervalInput.value;
            button.dataset.clicks = clicksInput.value;
            saveButtonData();
            document.body.removeChild(modal);
        });

        const cancelButton = document.createElement('button');
        cancelButton.textContent = 'Cancel';
        cancelButton.style.padding = '10px';
        cancelButton.style.borderRadius = '5px';
        cancelButton.style.border = 'none';
        cancelButton.style.backgroundColor = '#dc3545';
        cancelButton.style.color = 'white';
        cancelButton.style.cursor = 'pointer';
        form.appendChild(cancelButton);

        cancelButton.addEventListener('click', (e) => {
            e.preventDefault();
            document.body.removeChild(modal);
        });

        document.body.appendChild(modal);
    }

    // Function to simulate real auto-clicks on elements
    function autoClick(button) {
        const rect = button.getBoundingClientRect();
        button.style.visibility = 'hidden';

        const elemUnderButton = document.elementFromPoint(
            rect.left + rect.width / 2,
            rect.top + rect.height / 2
        );

        button.style.visibility = 'visible';

        if (elemUnderButton) {
            const interval = button.dataset.interval ? parseInt(button.dataset.interval) : 1000;
            const clicks = button.dataset.clicks ? parseInt(button.dataset.clicks) : 1;

            // Reduce opacity to indicate clicking
            button.style.opacity = '0.6';

            // Delay before the first click set
            return new Promise(resolve => {
                setTimeout(() => {
                    for (let i = 0; i < clicks; i++) {
                        simulateClick(elemUnderButton);
                    }

                    // Restore opacity after the clicks
                    button.style.opacity = '1';

                    // Continue after clicks
                    resolve();
                }, interval);  // Wait for the interval before the first click
            });
        }
    }



    // Function to simulate clicks, including handling iframes and shadow DOMs
    function simulateClick(target) {
        const eventNames = ['mousedown', 'mouseup', 'click'];
        eventNames.forEach(eventName => {
            const event = new MouseEvent(eventName, {
                bubbles: true,
                cancelable: true,
                view: window
            });
            target.dispatchEvent(event);
        });

        // Handle iframes
        if (target.tagName === 'IFRAME') {
            const iframeDoc = target.contentDocument || target.contentWindow.document;
            const clickableElement = iframeDoc.querySelector('selector'); // Update this selector for specific iframe content
            clickableElement?.click();
        }

        // Handle Shadow DOM
        if (target.shadowRoot) {
            const clickableElement = target.shadowRoot.querySelector('selector'); // Update this selector for Shadow DOM
            clickableElement?.click();
        }
    }

    // Function to start auto-clicking in sequence
    async function startAutoClick() {
        if (buttons.length === 0) return;

        while (isRunning) {
            for (let button of buttons) {
                await autoClick(button);
            }
        }
    }

    // Function to stop auto-clicking
    function stopAutoClick() {
        buttons.forEach((button) => {
            clearInterval(button.autoClickInterval);
        });
    }

    // Restore buttons and state on page reload
    window.onload = function () {
        const buttonData = loadButtonData();
        if (buttonData.length) {
            buttonData.forEach(data => createAutoClickButton(data));
        }
        isRunning = loadRunningState();
        if (isRunning) {
            startStopButton.textContent = '🛑';
            startAutoClick();
        }
    };
})();