YAD Multiple Auto Clicker

Automates clicks at various locations on the screen with customizable intervals

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

您需要先安裝使用者腳本管理器擴展,如 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    http://tampermonkey.net/
// @version      2.2
// @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
        }));
        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 = '#ff0000cc';
        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
        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 = '#fff';
        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 = '#333';
        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 = '#555';
            label.style.marginBottom = '5px';
            fieldWrapper.appendChild(label);

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

            return { fieldWrapper, input };
        };

        const intervalField = createInputField('Interval (ms):', 'number', 1000);
        const clicksField = createInputField('Number of clicks:', 'number', 1);
        form.appendChild(intervalField.fieldWrapper);
        form.appendChild(clicksField.fieldWrapper);

        const buttonWrapper = document.createElement('div');
        buttonWrapper.style.display = 'flex';
        buttonWrapper.style.justifyContent = 'center';
        buttonWrapper.style.marginTop = '15px';
        modal.appendChild(buttonWrapper);

        const saveButton = document.createElement('button');
        saveButton.textContent = 'Save';
        saveButton.style.padding = '8px 16px';
        saveButton.style.backgroundColor = '#007bff';
        saveButton.style.color = 'white';
        saveButton.style.border = 'none';
        saveButton.style.borderRadius = '4px';
        saveButton.style.cursor = 'pointer';
        buttonWrapper.appendChild(saveButton);

        const closeButton = document.createElement('button');
        closeButton.textContent = 'Close';
        closeButton.style.padding = '8px 16px';
        closeButton.style.backgroundColor = '#dc3545';
        closeButton.style.color = 'white';
        closeButton.style.border = 'none';
        closeButton.style.borderRadius = '4px';
        closeButton.style.cursor = 'pointer';
        buttonWrapper.appendChild(closeButton);

        document.body.appendChild(modal);

        saveButton.addEventListener('click', (e) => {
            e.preventDefault();
            button.dataset.interval = intervalField.input.value;
            button.dataset.clicks = clicksField.input.value;
            modal.remove();
        });

        closeButton.addEventListener('click', (e) => {
            e.preventDefault();
            modal.remove();
        });
    }

    // 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;

            for (let i = 0; i < clicks; i++) {
                simulateClick(elemUnderButton);
            }

            // Delay between clicks
            return new Promise(resolve => setTimeout(resolve, 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();
        }
    }

    // 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() {
        isRunning = false;
    }

    // Initialize the script on page load
    const buttonData = loadButtonData();
    buttonData.forEach(data => createAutoClickButton(data));
    isRunning = loadRunningState();
    if (isRunning) {
        startStopButton.textContent = '🛑';
        startAutoClick();
    }
})();