Greasy Fork

Greasy Fork is available in English.

通用自动点击脚本(定时版 + 状态显示)

支持定时自动点击,并通过鼠标右键选择目标元素,实时显示运行状态、执行次数等信息。

当前为 2024-11-23 提交的版本,查看 最新版本

// ==UserScript==
// @name         通用自动点击脚本(定时版 + 状态显示)
// @namespace    http://tampermonkey.net/
// @version      3.2
// @description  支持定时自动点击,并通过鼠标右键选择目标元素,实时显示运行状态、执行次数等信息。
// @author       Universal
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const CONFIG = {
        CONTROL_PANEL_ID: 'autoClickControl', // 控制面板ID
        OVERLAY_ID: 'elementOverlay', // 高亮层ID
        CLICK_INTERVAL: 500, // 每次点击间隔 (毫秒)
        MAX_CLICKS: 20, // 最大点击次数
    };

    const State = {
        targetSelector: GM_getValue('targetSelector', ''), // 目标选择器
        isPicking: false, // 是否正在选择目标元素
        isRunning: false, // 是否正在运行点击
        timer: null, // 定时器
        clickCount: 0, // 当前点击次数
        executeTime: GM_getValue('executeTime', ''), // 定时执行时间
        debug: true, // 是否启用调试日志
    };

    // 调试日志
    function debugLog(...args) {
        if (State.debug) console.log('[Debug]', ...args);
    }

    // 添加样式
    function addStyles() {
        GM_addStyle(`
            #autoClickControl {
                position: fixed;
                top: 10px;
                right: 10px;
                z-index: 9999;
                background: #fff;
                border: 1px solid #ccc;
                padding: 15px;
                border-radius: 5px;
                box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                font-family: Arial, sans-serif;
                width: 300px;
            }
            #autoClickControl input, #autoClickControl button {
                margin-bottom: 10px;
            }
            #autoClickControl input {
                width: 100%;
                padding: 5px;
                border: 1px solid #ddd;
                border-radius: 3px;
            }
            #autoClickControl button {
                padding: 5px 10px;
                border: none;
                border-radius: 3px;
                cursor: pointer;
            }
            #autoClickControl #startButton {
                background-color: #28a745;
                color: white;
            }
            #autoClickControl #pickButton {
                background-color: #007bff;
                color: white;
            }
            #autoClickControl #stopButton {
                background-color: #dc3545;
                color: white;
            }
            #elementOverlay {
                position: absolute;
                border: 2px dashed #007bff;
                background-color: rgba(0, 123, 255, 0.2);
                pointer-events: none;
                z-index: 9998;
                display: none;
            }
            #statusText {
                font-size: 12px;
                color: #333;
            }
        `);
    }

    // 创建控制面板
    function createControlPanel() {
        if (document.getElementById(CONFIG.CONTROL_PANEL_ID)) return;

        const controlPanel = document.createElement('div');
        controlPanel.id = CONFIG.CONTROL_PANEL_ID;
        controlPanel.innerHTML = `
            <label>目标选择器 (CSS选择器或XPath):</label>
            <input type="text" id="targetSelectorInput" placeholder="通过右键选择或手动输入" value="${State.targetSelector}">
            <label>执行时间:</label>
            <input type="datetime-local" id="executeTimeInput" value="${State.executeTime}">
            <button id="pickButton">右键选择目标</button>
            <button id="startButton">开始</button>
            <button id="stopButton" disabled>停止</button>
            <div id="statusText">状态: 未运行 | 点击次数: 0</div>
        `;
        document.body.appendChild(controlPanel);

        // 绑定事件
        document.getElementById('pickButton').addEventListener('click', enableElementPicker);
        document.getElementById('startButton').addEventListener('click', startClicking);
        document.getElementById('stopButton').addEventListener('click', stopClicking);
        document.getElementById('targetSelectorInput').addEventListener('input', (event) => {
            State.targetSelector = event.target.value.trim();
            GM_setValue('targetSelector', State.targetSelector);
        });
        document.getElementById('executeTimeInput').addEventListener('change', (event) => {
            State.executeTime = event.target.value.trim();
            GM_setValue('executeTime', State.executeTime);
        });
    }

    // 更新状态文本
    function updateStatus(message) {
        const statusText = document.getElementById('statusText');
        statusText.innerText = `状态: ${message} | 点击次数: ${State.clickCount}`;
    }

    // 启用目标选择
    function enableElementPicker() {
        alert('请右键单击页面上的目标元素来选择它。');
        State.isPicking = true;

        document.addEventListener('mouseover', highlightElement, true);
        document.addEventListener('contextmenu', selectElement, true);
    }

    // 停用目标选择
    function disableElementPicker() {
        State.isPicking = false;
        const overlay = document.getElementById(CONFIG.OVERLAY_ID);
        overlay.style.display = 'none';

        document.removeEventListener('mouseover', highlightElement, true);
        document.removeEventListener('contextmenu', selectElement, true);
    }

    // 高亮鼠标悬停的元素
    function highlightElement(event) {
        if (!State.isPicking) return;

        const overlay = document.getElementById(CONFIG.OVERLAY_ID);
        const target = event.target;

        if (!target) return;
        const rect = target.getBoundingClientRect();
        overlay.style.display = 'block';
        overlay.style.top = `${rect.top + window.scrollY}px`;
        overlay.style.left = `${rect.left + window.scrollX}px`;
        overlay.style.width = `${rect.width}px`;
        overlay.style.height = `${rect.height}px`;
        debugLog('Highlighting element:', target);
    }

    // 选择目标元素
    function selectElement(event) {
        if (!State.isPicking) return;

        event.preventDefault();
        const target = event.target;

        const selector = generateSelector(target);
        State.targetSelector = selector;
        GM_setValue('targetSelector', selector);

        document.getElementById('targetSelectorInput').value = selector;
        disableElementPicker();
        alert(`目标元素已选择: ${selector}`);
        debugLog('Selected element:', target, 'Generated selector:', selector);
    }

    // 自动生成 CSS 选择器
    function generateSelector(element) {
        if (!element) return null;

        let selector = element.tagName.toLowerCase();
        if (element.id) {
            selector += `#${element.id}`;
        } else if (element.className) {
            const className = element.className.trim().split(/\s+/).join('.');
            selector += `.${className}`;
        }
        return selector;
    }

    // 开始点击
    function startClicking() {
        if (State.isRunning) return;

        const executeTime = new Date(State.executeTime).getTime();
        const now = Date.now();

        if (!State.targetSelector) {
            alert('请设置目标选择器!');
            return;
        }

        if (!executeTime || executeTime <= now) {
            alert('请设置一个未来的执行时间!');
            return;
        }

        const delay = executeTime - now;
        updateStatus(`等待 ${Math.ceil(delay / 1000)} 秒后执行`);
        debugLog('Scheduled to start in:', delay, 'ms');

        State.timer = setTimeout(() => {
            State.isRunning = true;
            State.clickCount = 0;
            executeClicks();
        }, delay);

        document.getElementById('stopButton').disabled = false;
    }

    // 停止点击
    function stopClicking() {
        if (State.timer) clearTimeout(State.timer);
        State.isRunning = false;
        updateStatus('已停止');
        document.getElementById('stopButton').disabled = true;
        debugLog('Stopped clicking.');
    }

    // 执行点击
    async function executeClicks() {
        const elements = document.querySelectorAll(State.targetSelector);
        if (elements.length === 0) {
            alert('未找到目标元素,点击操作终止!');
            stopClicking();
            return;
        }

        while (State.isRunning && State.clickCount < CONFIG.MAX_CLICKS) {
            for (const element of elements) {
                if (!State.isRunning) break;
                element.click();
                State.clickCount++;
                updateStatus('运行中');
                debugLog('Clicked element:', element, `Count: ${State.clickCount}`);
                await new Promise((resolve) => setTimeout(resolve, CONFIG.CLICK_INTERVAL));
            }
        }

        stopClicking();
        alert('点击操作完成!');
    }

    // 创建高亮层
    function createOverlay() {
        if (document.getElementById(CONFIG.OVERLAY_ID)) return;

        const overlay = document.createElement('div');
        overlay.id = CONFIG.OVERLAY_ID;
        document.body.appendChild(overlay);
    }

    function initialize() {
        addStyles();
        createControlPanel();
        createOverlay();
    }

    initialize();
})();