Greasy Fork is available in English.

跟单

try to take over the world!

// ==UserScript==
// @name         跟单
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  try to take over the world!
// @author       You
// @include        http*://q1.88666333.com/*
// @grant   GM_xmlhttpRequest
// @grant   GM.getTab
// @grant   GM.saveTab
// @grant   GM.setValue
// @grant   GM.getValue
// @run-at document-body
// ==/UserScript==

(function() {
    'use strict';
    const THREAD_SIZE = 10;
    var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
    function isPromise(obj) {
        return !!obj && ((typeof obj === 'object' && typeof obj.then === 'function') || (typeof obj === 'function' && typeof obj().then === 'function'));
    }
    /**
     * Wait Resource.
     *
     * @param {Function} resourceFn pred function to check resource
     * @param {Object} options
     * @returns Promise
     */
    function waitResource(resourceFn, options) {
        var optionsRes = Object.assign(
            {
                interval: 1000,
                max: 6
            },
            options
        );
        var current = 0;
        return new Promise((resolve, reject) => {
            var timer = setInterval(() => {
                if (isPromise(resourceFn)) {
                    resourceFn().then(res => {
                        if(res) {
                            clearInterval(timer);
                            resolve();
                        }
                    });
                } else if (resourceFn()) {
                    clearInterval(timer);
                    resolve();
                }
                current++;
                if (current >= optionsRes.max) {
                    clearInterval(timer);
                    reject('Time out');
                }
            }, optionsRes.interval);
        });
    }


    function requestAsync(data) {
        // console.log(data);
        return new Promise((resolve, reject) => {
            var reportAJAX_Error = (rspObj) => {
                console.error (`Request error: ${data}`);
                reject(`Request => Error ${data}  RES ${rspObj.status}!  ${rspObj.statusText}`);
            }

            var processJSON_Response = (rspObj) => {
                if (rspObj.status != 200 && rspObj.status != 304) {
                    reportAJAX_Error (rspObj);
                } else {
                    let resJSON = {};
                    try{
                        resJSON = JSON.parse(rspObj.responseText);
                    } catch(err) {
                        // ignore
                        console.log(err);
                    }
                    resolve(resJSON);
                }
            };
            GM_xmlhttpRequest ( {
                method:         "POST",
                url:            document.location.origin + "/!/MemberBet",
                headers: {
                    "Referer": document.location.href,
                    "Content-Type": "application/x-www-form-urlencoded"
                },
                data:           data,
                // responseType:   "json",
                onload:         processJSON_Response,
                onabort:        reportAJAX_Error,
                onerror:        reportAJAX_Error,
                ontimeout:      reportAJAX_Error
            });
        });
    }

    function getOrdersAsync(account, gameType, gameCode, pageIndex) {
        return new Promise((resolve, reject) => {
            $.get(`${document.location.origin}/!/UserBets?Account=${account}&GameType=${gameType}&GameCode=${gameCode}&PageSize=100&PageIndex=${pageIndex}`).done(function(result) {
                resolve(result);
            }).fail(function(jqXHR, textStatus, errorThrown) {
                reject(`Request => Error ${errorThrown}  status ${textStatus}`);
            });
        });
    }
    function resToMask(res) {
        let placeHolders = ['abcXX', 'abXcX', 'abXXc', 'aXbcX', 'aXbXc', 'aXXbc', 'XabcX', 'XabXc', 'XaXbc', 'XXabc'];
        return placeHolders.map(a => {
            let mask = 'XXXXX'.split('');
            for(let i = 0; i < 5; i++) {
                if (a[i] !== 'X') {
                    mask[i] = res[i];
                }
            }
            return mask.join('');
        });
    }

    function signalA(pool) {
        return false;
    }

    async function bet(pools) {
        let count = pools.length;
        // %3D
        //const REQ_DATA = `SourceType=quick-comb&SourceData=%7B%22bet-type%22%3A%22d3%22%2C%22bet-count%22%3A${count}%2C%22dingwei-include%22%3A1%2C%22dingwei-1%22%3A%2212%22%2C%22dingwei-2%22%3A%221%22%2C%22dingwei-3%22%3A%221%22%2C%22hefeng-include%22%3A1%2C%22dan-include%22%3A1%2C%22shuang-include%22%3A1%7D&Game=${GameType}&Pools=${pools.join('%2C')}&Amount=${amount}`;
        const REQ_DATA = `SourceType=txt-import&SourceData=${count}+%E4%B8%AA%E5%8F%B7%E7%A0%81%EF%BC%88%E5%B8%A6%E9%87%91%E9%A2%9D%EF%BC%89&Game=${GameType}&Pools=${pools.join('%2C')}&Amount=`;
        console.log(REQ_DATA);
        return await requestAsync(REQ_DATA);
    }

    function addUI() {
        let div=document.createElement("div");
        div.setAttribute("style", "width:360px");
        let btn=document.createElement("BUTTON");
        btn.setAttribute("id", "betBtn");
        div.appendChild(btn);
        let amount = document.createElement("INPUT");
        amount.setAttribute("id", "sigAccount");
        amount.setAttribute("placeholder", "跟单信号源");
        amount.setAttribute("type", "text");
        div.appendChild(amount);
        let node = document.querySelector('#branch-info');
        let parentNode = node.parentNode;
        parentNode.insertBefore(div, node);
    }

    async function main() {
        if (window !== top) {
            console.log(window.location.href);
            //  let summaryUsersGame = document.querySelector("a[href='summary-users-game.html']");
            if (window.location.pathname.includes("summary-users.html")) {
                while (true) {
                    let summaryUsersGame = document.querySelector("a[href='summary-users-game.html']");
                    if (summaryUsersGame && summaryUsersGame.classList[0] !== 'active') {
                        summaryUsersGame.click();
                    }
                    await wait(3000);
                }
            } else {
                return;
            }
        }

        console.log("START...", new Date());
        if (window.location.pathname === "/") { // login
            let user = await GM.getTab();
            console.log("DEBUG ", user);
            if (!!user && !!user.name) {
                await wait(1000);
                document.querySelector('#login-id').value = user.name;
                await wait(500);
                document.querySelector('#password').value = user.password;
                await wait(500);
                document.querySelector('.btn-primary').click();
            } else {
                document.querySelector('.btn-primary').onclick = async () => {
                    user.name = document.querySelector('#login-id').value;
                    user.password = document.querySelector('#password').value;
                    await GM.saveTab(user);
                }
            }
            return;
        }
        if (window.location.pathname === "/agreement.html") {
            await wait(500);
            document.querySelector('#lnk-agree').click();
            return;
        }
        await waitResource(async () => {
            console.log(document.querySelector('#account'));
            return typeof ENV === "object" && ENV !== null && !!ENV[GameType] && !!ENV[GameType].Resulted;
        });
        if (!!document.querySelector('#branch-info')) // member
        {
            addUI();
            while (true) {
                await GM.setValue("currentGameCode", ENV[GameType].Current.Code);
                let tabObj = await GM.getTab();
                // console.log('tabObj: ', tabObj);
                if (tabObj.started) {
                    if (tabObj.gameType !== GameType) {
                        document.querySelector(`#game-type-${tabObj.gameType}`).click();
                    }
                    document.querySelector('#betBtn').innerText = `结束`;
                    document.querySelector("#sigAccount").value = await GM.getValue('sigAccount');
                    document.querySelector('#betBtn').onclick = async () => {
                        tabObj.started = false;
                        await GM.saveTab(tabObj);
                        await GM.setValue('sigGameType', null);
                        await GM.setValue('sigAccount', null);
                        document.querySelector('#betBtn').innerText = "...";
                    }
                } else {
                    document.querySelector('#betBtn').innerText = "开始";
                    document.querySelector('#betBtn').onclick = async () => {
                        tabObj.started = true;
                        tabObj.gameType = GameType;
                        tabObj.betInfo = {
                            gameCode: ENV[GameType].Current.Code,
                            betIdMap: {}
                        };
                        await GM.saveTab(tabObj);
                        await GM.setValue('sigGameType', GameType);
                        await GM.setValue('sigAccount', document.querySelector("#sigAccount").value);
                        document.querySelector('#betBtn').innerText = "...";
                    }
                }

                if (!tabObj.started || new Date().getTime() / 1000 > ENV[GameType].Current.Close) {
                    await wait(1000);
                    continue;
                }
                let sigOrders = await GM.getValue("sigOrders", {});
                if (sigOrders.gameCode !== ENV[GameType].Current.Code) {
                    console.log("Game code not current");
                    await wait(1000);
                    continue;
                }
                if (tabObj.betInfo.gameCode !== ENV[GameType].Current.Code) {
                    tabObj.betInfo.gameCode = ENV[GameType].Current.Code;
                    tabObj.betInfo.betIdMap = {};
                }
                // work
                let betId = 0;
                Object.keys(sigOrders.pools).forEach((key) => {
                    if (betId === 0 && !tabObj.betInfo.betIdMap[key]) {
                        betId = parseInt(key);
                    }
                });
                //
                if (betId > 0) {
                    // if (true) {
                    let res = await bet(sigOrders.pools[betId]);
                    if (res.Error && res.Error !== '额度不足' && res.Error !== '账号已停用下注') {
                        throw(res);
                    }
                    console.log(res);
                    console.log("betId", betId, tabObj.betInfo);
                    tabObj.betInfo.betIdMap[betId] = true;
                    await GM.saveTab(tabObj);
                    continue;
                }
                // end work
                await wait(1000);
            }
        } else if (!!document.querySelector('#user-link')) { // signal
            while (true) {
                // 打开报表
                if (document.querySelector('a[href="/a/summary-users.html').classList[1] !== 'selected') {
                    document.querySelector('a[href="/a/summary-users.html').click();
                    await wait(5000);
                    continue;
                }
                let sigAccount = await GM.getValue("sigAccount");
                let sigGameType = await GM.getValue("sigGameType");
                console.log(sigAccount, sigGameType);
                if (!sigAccount || !sigGameType) {
                    await wait(1000);
                    continue;
                }
                let sigOrders = await GM.getValue("sigOrders", {});
                console.log("DEBUG sigOrder init", sigOrders);
                if (sigOrders.gameType !== sigGameType || sigOrders.gameCode !== ENV[sigGameType].Current.Code) {
                    sigOrders.gameType = sigGameType;
                    sigOrders.gameCode = ENV[sigGameType].Current.Code;
                    sigOrders.pools = {};
                    sigOrders.total = 0;
                }
                let res = null;
                try {
                    res = await getOrdersAsync(sigAccount,sigGameType, sigOrders.gameCode, 0);
                    await wait(1000);
                } catch (err) {
                    console.log('ignore error:', err);
                    continue;
                }
                let remainPages = Math.ceil((res.Total - sigOrders.total) / 100);
                console.log("remain pages", remainPages, res.Total, sigOrders.total);
                let idx = 0;
                let rows = [];
                let baseIdx = 0;
                while (remainPages > 0) {
                    let jobSize = remainPages <= THREAD_SIZE ? remainPages : THREAD_SIZE;
                    let jobReses = await Promise.all([...Array(jobSize).keys()].map((a) => getOrdersAsync(sigAccount,sigGameType, sigOrders.gameCode, a + baseIdx)));
                    console.log("jobs result", jobReses);
                    jobReses.forEach((res) => {
                        for (let i = 0; i < res.List.length && (rows.length < res.Total - sigOrders.total); i++) {
                            rows.push({
                                betId: res.List[i].BetID,
                                pool: res.List[i].Pool,
                                amount: res.List[i].Amounts[0] / 1000
                            });
                        }
                    });
                    await wait(1000);
                    remainPages -= jobSize;
                    baseIdx += jobSize;
                }

                for (let i = rows.length - 1; i >= 0; i--) {
                    let id = rows[i].betId;
                    if (!Array.isArray(sigOrders.pools[id])) { // new a bet
                        sigOrders.pools[id] = [];
                    }
                    sigOrders.pools[id].push(`${rows[i].pool}%3D${rows[i].amount}`);
                    sigOrders.total++;
                }
                // console.log(sigOrders);
                await GM.setValue("sigOrders", sigOrders);
                await wait(2000);
            }
        }
    }
    main()
        .then(() => console.log("Main Done"))
        .catch((err) => {
        console.error(err, "Restart in 6 secs ...");
        setTimeout(() => location.reload(), 6 * 1000);
    });
})();