YAD Multiple Auto Clicker

Automates clicks at various locations on the screen with customizable intervals

目前為 2024-10-17 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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

(function () {
    'use strict';

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

    // 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', () => {
        stopAutoClick(); // Ensure all running intervals are stopped
        buttons.forEach(button => button.remove());
        buttons = [];
        localStorage.removeItem('buttonData');
        buttonId = 1;
        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, including canvases
    function autoClick(button) {
        const rect = button.getBoundingClientRect();
        button.style.visibility = 'hidden';

        // If there's a canvas, click based on coordinates
        const elemUnderButton = document.elementFromPoint(
            rect.left + rect.width / 2,
            rect.top + rect.height / 2
        );

        button.style.visibility = 'visible';

        if (elemUnderButton.tagName === 'CANVAS') {
            const canvas = elemUnderButton;
            const ctx = canvas.getContext('2d');

            if (ctx) {
                const canvasClickEvent = new MouseEvent('click', {
                    bubbles: true,
                    cancelable: true,
                    view: window,
                    clientX: rect.left + rect.width / 2,
                    clientY: rect.top + rect.height / 2,
                });
                canvas.dispatchEvent(canvasClickEvent);
            }
        } else 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';
                    resolve();  // Resolve the promise when done
                }, interval);
            });
        }
    }

    // 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();
        }
    }

    // Start auto-clicking in sequence
    async function startAutoClick() {
        for (const button of buttons) {
            const interval = parseInt(button.dataset.interval);
            await autoClick(button);  // Wait for the current button to finish before starting the next
        }
        if (isRunning) {
            setTimeout(startAutoClick, 0);  // Wait 500ms before starting the next sequence if still running
        }
    }

    // Stop auto-clicking
    function stopAutoClick() {
        isRunning = false;
    }

    // Load buttons and state on page load
    window.addEventListener('load', () => {
        loadButtonData().forEach(buttonData => createAutoClickButton(buttonData));
        if (loadRunningState()) {
            isRunning = true;
            startStopButton.textContent = '🛑';
            startAutoClick();
        }
    });
})();