Greasy Fork is available in English.

Timing Click

倒计时自动点击,电商抢东西专用

// ==UserScript==
// @name         Timing Click
// @namespace    http://tampermonkey.net/
// @version      0.3.1
// @description  倒计时自动点击,电商抢东西专用
// @author       Cherokeeli
// @match        *://*.taobao.com/*
// @match        *://*.jd.com/*
// @match        *://*.tmall.com/*
// @require      https://code.jquery.com/jquery-latest.js
// @run-at       document-idle
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_xmlhttpRequest
// @connect      githubusercontent.com
// @connect      gitee.com

// ==/UserScript==

(function() {
    'use strict';
    var $ = $ || window.$;
    var _$ = $.noConflict(true);
    var countWoker = "https://gitee.com/erokee/monkey-lib/raw/master/timing-click/count-worker.js";
    var avatar = "https://gitee.com/erokee/monkey-lib/raw/master/timing-click/image/1.pic.png";

    var mainCss = `#ql-panel #counterClickTime {
position:relative;
z-index:999;
height: 20px !important;
width: 150px !important;
font-size: 1em !important;
padding: 5px !important;
border: none !important;
margin: 0 !important;
}

#ql-panel #counterClickSelector {
position:relative;
z-index:999;
height: 20px !important;
width: 150px !important;
font-size: 1em !important;
padding: 5px !important;
border: none !important;
margin: 0 !important;
}

#ql-panel #listenButton {
position:relative;
z-index:999;
width: 100% !important;
border: none !important;
color: red !important;
font-size: 1em !important;
margin: 0 !important;
background-color: #fff;
}

#ql-panel #triggerButton {
height:50px;
width:50px;
position:relative;
z-index:9999;
background-position:center;
background-size:100%;
background-image:url(${avatar});
}

#ql-panel {
position:fixed;
z-index:9999;
top: 10vh;
left: 5vw;
}

#ql-panel #hidePanel {
position:absolute;
z-index:9999;
display:none;
border: solid 1px rgb(238,238,238) !important;
box-shadow: 0 0 5px rgb(213,210,210) !important;
}`;

    /*拖动*/
    class DragObj {
        constructor(dom) {
            this.mouse = {
                x: 0,
                y: 0
            };
            this.obj = {
                x: 0,
                y: 0
            };
            this.isClicked = false;
            if(dom) {
                this.dom = dom;
            } else {
                throw new Error('不存在的dom节点');
            }
        }

        init(options={}) {
            document.addEventListener('mousedown', this.down.bind(this));
            document.addEventListener('mousemove', this.move.bind(this));
            document.addEventListener('mouseup', this.end.bind(this));
            if(typeof options.click ==='function') {
                this.clickCb = options.click;
            }
            if(typeof options.exclude === 'object') {
                this.excludeDom = options.exclude;
            }
            if(typeof options.include === 'object') {
                this.includeDom = options.include;
            }
        }

        down(event) {
            if(this.includeDom.contains(event.target)) {
                this.isClicked = true;
                this.mouse.x = event.clientX;
                this.mouse.y = event.clientY;
                this.obj.x = this.dom.offsetLeft;
                this.obj.y = this.dom.offsetTop;
            }
        }

        move(event) {
            if(this.isClicked) {
                let moveX = event.clientX - this.mouse.x;
                let moveY = event.clientY - this.mouse.y;
                this.dom.style.left = `${this.obj.x+moveX}px`;
                this.dom.style.top = `${this.obj.y+moveY}px`;
            }
        }

        end(event) {
            this.isClicked = false;
            if(this.clickCb && (event.clientX === this.mouse.x && event.clientY===this.mouse.y)) {
                if(!this.excludeDom.contains(event.target) && this.includeDom.contains(event.target)) {
                    this.clickCb(event);
                }
            }
        }
    }

    GM_addStyle(mainCss);

    let timeInput = _$('<input id="counterClickTime" placeholder="输入开抢时间" type="datetime-local" step="1" value="2019-10-12T07:22:00" />');
    let selectorInput = _$('<input id="counterClickSelector" placeholder="输入抢按钮选择器" type="text" />');
    let listenButton = _$('<button id="listenButton">准备开抢</button>');
    let triggerButton = _$('<div id="triggerButton"></div>');
    let panel = _$('<div id="ql-panel"></div>');
    let hidePanel = _$('<div id="hidePanel"></div>');

    hidePanel.append(timeInput.val(dateFormatter.call(new Date(), 'yyyy-MM-ddThh:mm:ss')));
    hidePanel.append(selectorInput);
    hidePanel.append(listenButton);

    panel.append(triggerButton);
    panel.append(hidePanel);

    _$(document.body).append(panel);

    (new DragObj(panel[0])).init({
        click: function(event) {
            hidePanel.toggle('slow');
        },
        exclude: hidePanel[0],
        include: panel[0]
    });

    function dateFormatter(fmt) {
        var o = {
            "M+": this.getMonth() + 1,                 //月份
            "d+": this.getDate(),                    //日
            "h+": this.getHours(),                   //小时
            "m+": this.getMinutes(),                 //分
            "s+": this.getSeconds(),                 //秒
            "q+": Math.floor((this.getMonth() + 3) / 3), //季度
            "S": this.getMilliseconds()             //毫秒
        };
        if (/(y+)/.test(fmt))
            fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        for (var k in o)
            if (new RegExp("(" + k + ")").test(fmt))
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
        return fmt;
    }

    function fireEvent(dom, eventName) {
        let event = new MouseEvent(eventName);
        return dom.dispatchEvent(event);
    }

    function createWorkerFromExternalURL(url, callback) {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: function (response) {
                var script, dataURL, worker = null;
                if (response.status === 200) {
                    script = response.responseText;
                    dataURL = 'data:text/javascript;base64,' + btoa(script);
                    worker = new unsafeWindow.Worker(dataURL);
                }
                callback(worker);
            },
            onerror: function () {
                callback(null);
            }
        });
    }

    function stepClick(times, stepInterval, clickFn) {
        if (!times) return;
        if (clickFn) clickFn();
        setTimeout(function () {
            stepClick(--times, stepInterval, clickFn);
        }, stepInterval);
    }

    /*开始抢*/
    listenButton.click(function(e) {
        let time = timeInput.val();
        let targetTime = Date.parse(new Date(time));
        let currentTime = Date.now();
        let timeout = targetTime-currentTime;
        console.log(timeout, selectorInput.val());
        createWorkerFromExternalURL(countWoker, function (worker) {
            if (!worker) throw Error('Create webworker failed');
            let btn = listenButton[0];
            worker.onmessage = function (event) {
                if (event.data === -1) {
                    btn.disabled = false;
                    stepClick(3, 100, function () {
                        _$(selectorInput.val()).trigger('click');
                        fireEvent(document.querySelector(selectorInput.val()), 'click');
                    });
                    worker.terminate();
                } else {
                    btn.disabled = true;
                    btn.innerHTML = `距离开抢还有${Math.ceil(event.data / 1000)}秒`;
                }
            };
            worker.postMessage(timeout);
        });
    });

})();