淘宝自动点击脚本

在指定时间自动点击淘宝网站上的指定元素,可控制开始和停止

// ==UserScript==
// @name         淘宝自动点击脚本
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description  在指定时间自动点击淘宝网站上的指定元素,可控制开始和停止
// @author       Vincent Ko (https://vincentko.top | https://github.com/forrany)
// @match        *://*.taobao.com/*
// @match        *://*.tmall.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    // 配置常量
    const CONFIG = {
        MAX_CLICKS: 20,
        MAX_FIND_ATTEMPTS: 100,
        FIND_INTERVAL: 100,
        STORAGE_KEY: 'autoClickState',
        CONTROL_PANEL_ID: 'autoClickControl'
    };

    // 核心状态管理
    const State = {
        executeTime: GM_getValue('executeTime', ''),
        targetSelector: GM_getValue('targetSelector', ''),
        isRunning: false,
        timer: null,
        clickCount: 0,
        
        init() {
            const storedState = localStorage.getItem(CONFIG.STORAGE_KEY);
            if (storedState) {
                const state = JSON.parse(storedState);
                this.isRunning = state.isRunning;
                this.clickCount = state.clickCount;
            }
        },
        
        save() {
            localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify({
                isRunning: this.isRunning,
                clickCount: this.clickCount
            }));
        }
    };

    // UI 控制器
    const UI = {
        init() {
            this.injectStyles();
            this.createControlPanel();
            this.bindEvents();
            this.initializeElementPicker();
            this.updateStatus();
        },

        injectStyles() {
            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);
                    min-width: 300px;
                    font-family: Arial, sans-serif;
                }
                #autoClickControl .control-header {
                    font-weight: bold;
                    margin-bottom: 10px;
                    padding-bottom: 5px;
                    border-bottom: 1px solid #eee;
                }
                #autoClickControl .control-item {
                    margin-bottom: 10px;
                }
                #autoClickControl .control-item label {
                    display: block;
                    margin-bottom: 5px;
                    color: #666;
                    font-size: 12px;
                }
                #autoClickControl input {
                    width: 100%;
                    padding: 5px;
                    border: 1px solid #ddd;
                    border-radius: 3px;
                    margin-bottom: 5px;
                }
                #autoClickControl .button-group {
                    display: flex;
                    justify-content: space-between;
                    margin-top: 10px;
                }
                #autoClickControl button {
                    padding: 5px 15px;
                    border: none;
                    border-radius: 3px;
                    cursor: pointer;
                    font-size: 12px;
                    transition: all 0.3s;
                }
                #startAutoClick {
                    background: #1890ff;
                    color: white;
                }
                #stopAutoClick {
                    background: #ff4d4f;
                    color: white;
                }
                #startAutoClick:hover {
                    background: #40a9ff;
                }
                #stopAutoClick:hover {
                    background: #ff7875;
                }
                .status-text {
                    font-size: 12px;
                    color: #666;
                    margin-top: 5px;
                    text-align: center;
                }
                #autoClickControl .minimize-btn {
                    position: absolute;
                    right: 5px;
                    top: 5px;
                    cursor: pointer;
                    padding: 2px 6px;
                    background: #f0f0f0;
                    border-radius: 3px;
                    font-size: 12px;
                }
                #autoClickControl.minimized {
                    min-width: auto;
                    padding: 5px;
                }
                #autoClickControl.minimized .control-content {
                    display: none;
                }
            `);
        },

        createControlPanel() {
            const controlDiv = document.createElement('div');
            controlDiv.id = CONFIG.CONTROL_PANEL_ID;
            controlDiv.innerHTML = `
                <div class="minimize-btn">_</div>
                <div class="control-content">
                    <div class="control-header">自动点击控制面板</div>
                    <div class="control-item">
                        <label>执行时间:</label>
                        <input type="datetime-local" id="executeTimeInput" step="1">
                    </div>
                    <div class="control-item">
                        <label>目标元素 (CSS选择器):</label>
                        <input type="text" id="targetSelectorInput" placeholder="请输入CSS选择器">
                        <button id="pickElement" style="width: auto; margin-top: 5px;">选择元素</button>
                    </div>
                    <div class="button-group">
                        <button id="startAutoClick">开始</button>
                        <button id="stopAutoClick">停止</button>
                    </div>
                    <div class="status-text" id="statusText"></div>
                </div>
            `;
            document.body.appendChild(controlDiv);

            // 初始化输入框值
            if (State.executeTime) {
                document.getElementById('executeTimeInput').value = State.executeTime.replace(' ', 'T');
            }
            if (State.targetSelector) {
                document.getElementById('targetSelectorInput').value = State.targetSelector;
            }
        },

        bindEvents() {
            // 最小化功能
            const minimizeBtn = document.querySelector('.minimize-btn');
            minimizeBtn.addEventListener('click', () => {
                const controlDiv = document.getElementById(CONFIG.CONTROL_PANEL_ID);
                controlDiv.classList.toggle('minimized');
                minimizeBtn.textContent = controlDiv.classList.contains('minimized') ? '+' : '_';
            });

            // 输入框事件
            document.getElementById('executeTimeInput').addEventListener('change', function() {
                State.executeTime = this.value.replace('T', ' ');
                GM_setValue('executeTime', State.executeTime);
                UI.updateStatus();
            });

            document.getElementById('targetSelectorInput').addEventListener('change', function() {
                State.targetSelector = this.value;
                GM_setValue('targetSelector', State.targetSelector);
                UI.updateStatus();
            });

            // 控按钮事件
            document.getElementById('startAutoClick').addEventListener('click', () => Controller.start());
            document.getElementById('stopAutoClick').addEventListener('click', () => Controller.stop());
        },

        updateStatus() {
            const statusText = document.getElementById('statusText');
            const timeStr = State.executeTime ? new Date(State.executeTime).toLocaleString() : '未设置';
            const selectorStr = State.targetSelector || '未设置';
            const elements = document.querySelectorAll(State.targetSelector);
            const totalElements = elements.length;
            
            statusText.innerHTML = `
                当前状态: ${State.isRunning ? '运行中' : '已停止'}<br>
                执行时间: ${timeStr}<br>
                目标元素: ${selectorStr} (匹配到 ${totalElements} 个元素)<br>
                点击次数: ${State.clickCount}/${CONFIG.MAX_CLICKS * Math.max(1, totalElements)}
            `;
        },

        updateButtonState() {
            document.getElementById('startAutoClick').disabled = State.isRunning;
            document.getElementById('stopAutoClick').disabled = !State.isRunning;
        },

        initializeElementPicker() {
            const pickButton = document.getElementById('pickElement');
            pickButton.addEventListener('click', () => ElementPicker.enable());
        }
    };

    // 点击控制器
    const ClickController = {
        findAttempts: 0,

        async performClicks() {
            this.findAttempts = 0;
            
            for (let i = 0; i < CONFIG.MAX_CLICKS; i++) {
                if (!State.isRunning) return false;
                
                const elements = await this.findElements();
                if (!elements) {
                    Controller.stop();
                    alert(`未找到目标元素,已尝试 ${CONFIG.MAX_FIND_ATTEMPTS} 次,脚本已停止`);
                    return false;
                }

                let allElementsProcessed = true;
                for (const element of elements) {
                    if (!State.isRunning) return false;
                    
                    if (this.checkElementInnerHTML(element)) {
                        console.log('元素内容为"去使用",跳过点击');
                        continue;
                    }
                    
                    element.click();
                    State.clickCount++;
                    console.log(`点击元素 (${State.clickCount}/${CONFIG.MAX_CLICKS * elements.length})`);
                    
                    await new Promise(resolve => setTimeout(resolve, 10));
                    
                    if (this.checkElementInnerHTML(element)) {
                        allElementsProcessed = false;
                        break;
                    }
                }
                
                UI.updateStatus();
                
                if (allElementsProcessed) {
                    console.log('所有元素处理完成,停止脚本');
                    Controller.stop();
                    alert('所有元素处理完成,脚本已停止');
                    return false;
                }
            }
            
            // 达到最大点击次数,停止脚本
            if (State.isRunning) {
                Controller.stop();
                alert('已达到最大点击次数,脚本已停止');
            }
            return false;
        },
        
        async findElements() {
            while (this.findAttempts < CONFIG.MAX_FIND_ATTEMPTS) {
                const elements = document.querySelectorAll(State.targetSelector);
                if (elements.length > 0) {
                    console.log(`找到 ${elements.length} 个目标元素`);
                    return elements;
                }
                
                this.findAttempts++;
                console.log(`未找到目标元素,第 ${this.findAttempts} 次尝试`);
                
                document.getElementById('statusText').innerHTML = `
                    当前状态: 查找目标元素中 (${this.findAttempts}/${CONFIG.MAX_FIND_ATTEMPTS})<br>
                    目标选择器: ${State.targetSelector}
                `;
                
                await new Promise(resolve => setTimeout(resolve, CONFIG.FIND_INTERVAL));
            }
            
            return null;
        },
        
        checkElementInnerHTML(element) {
            return element && element.innerHTML.includes("去使用");
        }
    };

    // 元素选择器工具
    const ElementPicker = {
        isActive: false,
        overlay: null,
        highlightElement: null,
        currentPanel: null,
        
        enable() {
            if (this.isActive) return;
            
            this.isActive = true;
            const controlPanel = document.getElementById(CONFIG.CONTROL_PANEL_ID);
            controlPanel.style.pointerEvents = 'none';

            this.overlay = this.createOverlay();
            this.highlightElement = this.createHighlightElement();

            document.body.appendChild(this.overlay);
            document.body.appendChild(this.highlightElement);

            this.setupOverlayEvents(controlPanel);
        },

        cleanup() {
            if (this.overlay && this.overlay.parentNode) {
                this.overlay.parentNode.removeChild(this.overlay);
            }
            if (this.highlightElement && this.highlightElement.parentNode) {
                this.highlightElement.parentNode.removeChild(this.highlightElement);
            }
            
            const panels = document.querySelectorAll('div[style*="z-index: 10002"]');
            panels.forEach(panel => {
                if (panel.parentNode) {
                    panel.parentNode.removeChild(panel);
                }
            });
            
            const controlPanel = document.getElementById(CONFIG.CONTROL_PANEL_ID);
            if (controlPanel) {
                controlPanel.style.pointerEvents = 'auto';
            }
            
            this.isActive = false;
            this.overlay = null;
            this.highlightElement = null;
            this.currentPanel = null;

            document.removeEventListener('keydown', this.handleKeyPress);
        },

        createOverlay() {
            const overlay = document.createElement('div');
            overlay.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.3);
                cursor: crosshair;
                z-index: 10000;
            `;
            return overlay;
        },

        createHighlightElement() {
            const highlightElement = document.createElement('div');
            highlightElement.style.cssText = `
                position: fixed;
                border: 2px solid #ff0000;
                background: rgba(255, 0, 0, 0.2);
                pointer-events: none;
                z-index: 10001;
                display: none;
            `;
            return highlightElement;
        },

        setupOverlayEvents(controlPanel) {
            let hoveredElement = null;

            this.overlay.addEventListener('mousemove', (e) => {
                e.stopPropagation();
                this.overlay.style.pointerEvents = 'none';
                hoveredElement = document.elementFromPoint(e.clientX, e.clientY);
                this.overlay.style.pointerEvents = 'auto';
                this.updateHighlight(hoveredElement, controlPanel);
            });

            this.overlay.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();

                if (hoveredElement && hoveredElement !== this.overlay) {
                    if (this.currentPanel && this.currentPanel.parentNode) {
                        this.currentPanel.parentNode.removeChild(this.currentPanel);
                    }

                    const selectors = this.generateSelectors(hoveredElement);
                    const panel = this.createSelectorPanel(selectors, hoveredElement, e.clientX, e.clientY);
                    document.body.appendChild(panel);
                    this.currentPanel = panel;
                }
            });

            document.addEventListener('keydown', this.handleKeyPress);
        },

        updateHighlight(element, controlPanel) {
            if (!element || element === this.overlay || element === controlPanel) {
                this.highlightElement.style.display = 'none';
                return;
            }

            const rect = element.getBoundingClientRect();
            this.highlightElement.style.display = 'block';
            this.highlightElement.style.top = rect.top + 'px';
            this.highlightElement.style.left = rect.left + 'px';
            this.highlightElement.style.width = rect.width + 'px';
            this.highlightElement.style.height = rect.height + 'px';
        },

        generateSelectors(element) {
            const selectors = [];
            
            // 基础选择器
            if (element.className) {
                const classes = element.className.trim().split(/\s+/);
                if (classes[0]) {
                    selectors.push(`${element.tagName.toLowerCase()}.${classes[0]}`);
                }
            }
            
            // 带父元素的选择器
            let parent = element.parentElement;
            if (parent && parent.className) {
                const parentClass = parent.className.trim().split(/\s+/)[0];
                selectors.push(`.${parentClass} ${selectors[0]}`);
            }
            
            // 带couponOuterWrapper的选择器
            let wrapper = element.closest('.couponOuterWrapper_88763c3f');
            if (wrapper) {
                const allWrappers = Array.from(document.querySelectorAll('.couponOuterWrapper_88763c3f'));
                const wrapperIndex = allWrappers.indexOf(wrapper);
                if (wrapperIndex !== -1) {
                    selectors.push(`.couponOuterWrapper_88763c3f:nth-of-type(${wrapperIndex + 1}) ${selectors[0]}`);
                }
            }
            
            return selectors;
        },

        createSelectorPanel(selectors, element, x, y) {
            const panel = document.createElement('div');
            panel.style.cssText = `
                position: fixed;
                left: ${x}px;
                top: ${y}px;
                background: white;
                border: 1px solid #ccc;
                border-radius: 4px;
                padding: 10px;
                box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                z-index: 10002;
                max-width: 300px;
            `;
            
            panel.innerHTML = `
                <div style="margin-bottom: 8px; font-weight: bold;">请选择合适的选择器:</div>
                <div class="selector-list" style="max-height: 200px; overflow-y: auto;"></div>
            `;
            
            const selectorList = panel.querySelector('.selector-list');
            
            selectors.forEach(selector => {
                const count = document.querySelectorAll(selector).length;
                const item = document.createElement('div');
                item.style.cssText = `
                    padding: 5px;
                    margin: 2px 0;
                    cursor: pointer;
                    border-radius: 3px;
                    background: #f5f5f5;
                `;
                item.innerHTML = `
                    <div style="font-size: 12px; color: #666;">匹配到 ${count} 个元素</div>
                    <div style="word-break: break-all;">${selector}</div>
                `;
                
                item.addEventListener('mouseover', () => {
                    item.style.background = '#e6f7ff';
                });
                
                item.addEventListener('mouseout', () => {
                    item.style.background = '#f5f5f5';
                });
                
                item.addEventListener('click', (e) => {
                    e.stopPropagation();
                    const targetSelectorInput = document.getElementById('targetSelectorInput');
                    targetSelectorInput.value = selector;
                    State.targetSelector = selector;
                    GM_setValue('targetSelector', selector);
                    UI.updateStatus();
                    
                    this.cleanup();
                });
                
                selectorList.appendChild(item);
            });
            
            return panel;
        },

        handleKeyPress(e) {
            if (e.key === 'Escape') {
                ElementPicker.cleanup();
            }
        }
    };

    // 主控制器
    const Controller = {
        async start() {
            if (!State.executeTime || !State.targetSelector) {
                alert('请先设置执行时间和目标元素');
                return;
            }
            
            State.isRunning = true;
            State.clickCount = 0;
            this.main();
            UI.updateStatus();
            UI.updateButtonState();
            State.save();
        },
        
        stop() {
            State.isRunning = false;
            if (State.timer) {
                clearTimeout(State.timer);
                State.timer = null;
            }
            UI.updateStatus();
            UI.updateButtonState();
            State.save();
        },
        
        async main() {
            if (!State.executeTime || !State.targetSelector) {
                document.getElementById('statusText').textContent = '请先设置执行时间和目标元素';
                return;
            }

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

            if (now < targetTime) {
                const delay = targetTime - now;
                document.getElementById('statusText').textContent = 
                    `将在 ${Math.floor(delay/1000)} 秒后执行点击操作`;
                State.timer = setTimeout(() => this.executeClickSequence(), delay);
            } else {
                document.getElementById('statusText').textContent = '指定时间已过,立即执行点击操作';
                this.executeClickSequence();
            }
        },

        async executeClickSequence() {
            if (State.isRunning) {
                await ClickController.performClicks();
            }
        }
    };

    // 初始化
    function initialize() {
        State.init();
        UI.init();
        
        if (State.isRunning) {
            Controller.main();
        }
    }

    initialize();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址