skypesky / (skypesky.cn) 淘宝天猫购物优惠券 搜券助手 省钱必备工具

// ==UserScript==
// @namespace    https://openuserjs.org/user/skypesky
// @name         (skypesky.cn) 淘宝天猫购物优惠券 搜券助手 省钱必备工具
// @author       skypesky
// @collaborator liang
// @copyright    2018, skypesky (https://openuserjs.org/users/skypesky)
// @version      18.09.14
// @description  [skypesky 出品]支持手机扫描二维码领取优惠券 淘宝天猫购物优惠券 优惠券 省钱必备工具 最新版(2018-09-14) 

// @license Apache-2.0

// @connect      *
// @grant        GM_getResourceText
// @grant        GM_addStyle


// ==========================详情页===========================
// @include      http*://s.taobao.com/search*
// @include      http*://list.tmall.com*
// @include      http*://list.tmall.hk/search_product.htm*
// @include      http*://www.tmall.com/*

// ==========================搜索页============================
// @include      http*://item.taobao.com/*
// @include      http*://detail.tmall.com/*
// @include      http*://chaoshi.detail.tmall.com/*
// @include      http*://detail.tmall.hk/*
// @include      http*://detail.yao.95095.com/item.htm*

// ==========================资源============================
// @resource     toastr https://cdn.bootcss.com/izitoast/1.3.0/css/iziToast.min.css
// @resource     contexMenu https://cdn.bootcss.com/jquery-contextmenu/2.6.1/jquery.contextMenu.min.css


// ==========================js============================
// @require      https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js
// @require      https://cdn.bootcss.com/jquery.qrcode/1.0/jquery.qrcode.min.js
// @require      https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js
// @require      https://cdn.bootcss.com/jquery-contextmenu/2.6.1/jquery.contextMenu.min.js
// @require      https://cdn.bootcss.com/izitoast/1.3.0/js/iziToast.min.js


// ==/UserScript== 


const config = {
    successClassTag: `skypesky-search-success`,
    errorClassTag: `skypesky-search-error`,
    requestApi: {
        url: "https://skypesky.cn/api/search",
        method: "POST"
    }
};



(function () {

    'use strict';

    //导入的样式
    const cssList = [
        "contexMenu",
        "toastr"
    ];

    const SingleProductList = [{
            'webUrl': 'http*://item.taobao.com/*',
            'testUrl': [
                'https://detail.tmall.com/item.htm?spm=a230r.1.14.49.540ebfacqvEYTO&id=567321234000&ns=1&abbucket=19&sku_properties=5919063:6536025'
            ],
            'description': '淘宝商品详情页',
            'rule': /\/\/item.taobao.com\//,
            'title': {
                'selector': '.tb-main-title',
            }
        },
        {
            'webUrl': 'http*://detail.tmall.com/*',
            'testUrl': [
                'https://detail.tmall.com/item.htm?spm=a220m.1000858.1000725.44.3485471b9MXFa6&id=548041586009&skuId=3487271543110&standard=1&user_id=1956576107&cat_id=2&is_b=1&rn=3845a7b75c21c0c215f9387e6da29c12'
            ],
            'description': '天猫商品详情页',
            'rule': /\/\/detail.tmall.com\//,
            'title': {
                'selector': '.tb-detail-hd > h1',
            }
        },
        {
            'webUrl': 'http*://chaoshi.detail.tmall.com/*',
            'testUrl': [
                'https://chaoshi.detail.tmall.com/item.htm?spm=a3204.7933263.0.0.30755689u71e3R&id=40553443841&rewcatid=50514008'
            ],
            'description': '天猫超市商品详情页',
            'rule': /\/\/chaoshi.detail.tmall.com\//,
            'title': {
                'selector': '.tb-detail-hd > h1',
            }
        },
        {
            'webUrl': 'http*://detail.tmall.hk/*',
            'testUrl': [
                'https://detail.tmall.hk/item.htm?spm=a220m.1000858.1000725.1.245adef3e8C0PD&id=537322241064&skuId=3208297513344&user_id=2248370076&cat_id=52792006&is_b=1&rn=08fddf654f15cf5f597126d24d58036f'
            ],
            'description': '天猫国际商品详情页',
            'rule': /\/\/detail.tmall.hk\//,
            'title': {
                'selector': '.tb-detail-hd > h1',
            }
        },
        {
            'webUrl': 'http*://detail.yao.95095.com/item.htm*',
            'testUrl': [
                'https://detail.tmall.com/item.htm?spm=a220m.1000858.1000725.31.32b94fc54dnadz&id=566728087172&user_id=1756499995&cat_id=2&is_b=1&rn=452988c5bbaabcd343620724279e4566'
            ],
            'description': '天猫医药商品详情页',
            'rule': /\/\/detail.yao.95095.com\//,
            'title': {
                'selector': '.tb-detail-hd > h1',
            }
        },
    ];

    const SearchProductList = [{
            'webUrl': 'https://s.taobao.com/search/*',
            'testUrl': [
                'https://s.taobao.com/search?q=%E8%83%8C%E5%8C%85&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20180530&ie=utf8'
            ],
            'description': '淘宝网直接搜索',
            'rule': /\/\/s.taobao.com\/search/,
            'id': {
                'selector': '.title a[data-nid]',
                'attr': 'data-nid'
            },
            'title': {
                'selector': 'div.title a',
            },
            'image': {
                'selector': '.pic-box-inner'
            },
            'product': {
                'selector': '.J_MouserOnverReq',
            }
        },
        {
            'webUrl': 'http*://list.tmall.hk/search_product.htm*from=yao..pc_1_searchbutton',
            'description': '天猫医药馆直接搜索',
            'testUrl': [
                'https://list.tmall.com/search_product.htm?spm=a222y.22577.6850814529.3.60042883GjWeq6&abbucket=&cat=53350009&acm=lb-zebra-22578-290927.1003.8.426589&aldid=176543&q=%B2%B9%C9%F6&abtest=&scm=1003.8.lb-zebra-22578-290927.ITEM_14466834623622_426589'
            ],
            'rule': /\/\/list.tmall.com\/search_product.htm[\s\S]*from=yao..pc_1_searchbutton/,
            'id': {
                'selector': 'div.product[data-id]',
                'attr': 'data-id'
            },
            'title': {
                'selector': '.productTitle a',
            },
            'image': {
                'selector': '.productImg-wrap'
            },
            'product': {
                'selector': 'div.product',
            }
        },
        {
            'webUrl': 'http*://list.tmall.com/search_product.htm*from=chaoshi..pc_1_searchbutton',
            'testUrl': [
                'https://list.tmall.com/search_product.htm?q=%D3%CD&user_id=725677994&type=p&cat=50514008&spm=a3204.7084717.a2227oh.d100&from=chaoshi.index.pc_1_searchbutton',
                'https://list.tmall.com//search_product.htm?q=%D3%CD%D1%CC%BB%FA&user_id=725677994&type=p&cat=50514008&spm=a3204.7933263.a2227oh.d100&xl=%D3%CD_1&from=chaoshi..pc_1_suggest'
            ],
            'description': '天猫超市直接搜索',
            'rule': /\/\/list.tmall.com\/+search_product.htm[\s\S]*from=chaoshi/,
            'id': {
                'selector': 'li.product',
                'attr': 'data-itemid'
            },
            'title': {
                'selector': '.product-title a',
            },
            'image': {
                'selector': '.product-img'
            },
            'product': {
                'selector': 'li.product',
            }
        },
        {
            'webUrl': 'http*://list.tmall.hk/search_product.htm',
            'testUrl': [
                'https://list.tmall.hk/search_product.htm?q=%C3%E6%C4%A4&spm=a2231.7718719.a2227oh.d100&type=p&from=tmallhk.list.pc_1_searchbutton'
            ],
            'description': '天猫国际直接搜索',
            'rule': /\/\/list.tmall.hk\/search_product.htm/,
            'id': {
                'selector': 'div.product[data-id]',
                'attr': 'data-id'
            },
            'title': {
                'selector': '.productTitle',
            },
            'image': {
                'selector': '.productImg-wrap'
            },
            'product': {
                'selector': 'div.product',
            }
        },
        {
            'webUrl': 'https://list.tmall.com/search_product.htm*from=mallfp..pc_1_searchbutton',
            'testUrl': [
                'https://list.tmall.com/search_product.htm?q=%C4%DA%D2%C2&type=p&spm=a220m.1000858.a2227oh.d100&from=.list.pc_1_searchbutton',
                'https://list.tmall.com/search_product.htm?q=%B1%CA%D0%BE&type=p&vmarket=&spm=875.7931836%2FB.a2227oh.d100&from=mallfp..pc_1_searchbutton'
            ],
            'description': '天猫直接搜索',
            'rule': /\/\/list.tmall.com\/search_product.htm*/,
            'id': {
                'selector': 'div.product[data-id]',
                'attr': 'data-id'
            },
            'title': {
                'selector': '.productTitle',
            },
            'image': {
                'selector': '.productImg-wrap'
            },
            'product': {
                'selector': 'div.product[data-id]'
            }
        }
    ];

    $(function () {
        $('body').css('background', '#F4F4F4');
        // 添加样式
        importUtil.setCssList(cssList);
        importUtil.importCss();
        // 商品详情页搜券程序
        Util.run(SingleProductList, new SingleProduct());

    });

    $(window).on('load', function () {
        // 商品搜索页搜券程序
        Util.run(SearchProductList, new SearchProduct());
    })

})();

// 商品详情
function SingleProduct() {
    // 二维码
    this._qrocde = null;
    // 优惠券图标
    this._discountIcon = null;
}

// 运行函数
SingleProduct.prototype.run = function (website) {

    // 创建discountIcon
    this.createDiscountIcon("点我搜券");
    $('body').append(this._discountIcon);

    // 创建qrcode
    this.createQrcode();
    $(this._discountIcon).before(this._qrcode);

    // 获取title
    let url = window.location.href;
    let id = (url.match(/[&?]id=\d+[&]?/g) + "").match(/\d+/g) + "";
    // 获取id
    let title = $.trim($(website.title.selector).text());


    // 验证id,title的合法性
    if (!Validate.checkIdAndTitle(id, title)) {
        console.error(`${id} or ${title} error!`);
        return;
    }

    // 第一次请求api   
    $.ajax({
        url: config.requestApi.url,
        method: config.requestApi.method,
        data: {
            title: title,
            id: id
        },
        success: (res) => {
            if (res && res.flag) { // 优惠券存在                
                // 更新discountIcon
                this.updateDiscountIcon("查找成功! " + res.data.resultInfo.coupon_info);
                this._discountIcon.attr('onclick', 'window.open("' + res.data.spreadUrl + '")');
                // 更新qrcode
                this.updateQrcode(res.data.spreadUrl);
                $(this._discountIcon).before(this._qrcode);
            } else { // 优惠券不存在
                // 更新discountIcon
                this.updateDiscountIcon('优惠券逃跑了*_*');
                this._discountIcon.removeAttr('onclick');
            }
        }
    });

    // click discountIcon
    $('#skypesky-discountIcon').bind('click', (event) => {
        console.log(`再次发起请求`);
        // 验证id,title的合法性
        if (!Validate.checkIdAndTitle(id, title)) {
            console.error(`${id} or ${title} error!`);
            return;
        }
        // 请求api
        $.ajax({
            url: config.requestApi.url,
            method: config.requestApi.method,
            data: {
                title: title,
                id: id
            },
            success: (res) => {
                if (res != null && res.flag) { // 优惠券存在

                    // 更新discountIcon
                    this.updateDiscountIcon("查找成功! " + res.data.resultInfo.coupon_info);
                    // 设置跳转的链接
                    this._discountIcon.attr('onclick', 'window.open("' + res.data.spreadUrl + '")');
                    // 更新qrcode
                    this.updateQrcode(res.data.spreadUrl);
                    $(this._discountIcon).before(this._qrcode);
                } else { // 优惠券不存在
                    // 更新discountIcon
                    this.updateDiscountIcon('优惠券逃跑了*_*');
                    this._discountIcon.removeAttr('onclick');
                }
            }
        });
    });

    $(window).scroll(function () {
        if ($(window).scrollTop() > 1000) {
            // 隐藏优惠券和qrcode
            $('#skypesky-discountIcon').hide();
            $('#skypesky-qrcode').hide();
        } else {
            $('#skypesky-discountIcon').show();
            $('#skypesky-qrcode').show();
        }
    });

};

/*
 ** @desc: 创建qrcode
 ** @param: text => 扫描链接
 ** @return: 返回qrcode
 */
SingleProduct.prototype.createQrcode = function (text = 'http://weixin.qq.com/r/8DqNlTXEc2gMrXH292-i') {

    text = Util.toUtf8(text);
    let id = 'skypesky-qrcode';
    // create qrcode element
    this._qrcode = $('<div></div>');
    // set id property
    this._qrcode.attr('id', id);
    // set css qrcode
    this._qrcode.css({
        'position': 'fixed',
        'left': '5px',
        'bottom': '10rem',
        'z-index': '666'
    });
    // create qrcode image
    this._qrcode.qrcode({
        width: 90,
        height: 90,
        render: 'canvas',
        typeNumber: -1,
        correctLevel: 0,
        background: '#ffffff',
        foreground: '#000000',
        text: text
    });

    return this._qrcode;
};
/*
 ** @desc: 更新qrcode
 ** @param: text => 扫描链接
 ** @return: 返回qrcode
 */
SingleProduct.prototype.updateQrcode = function (text) {
    this.removeQrcode();
    this.createQrcode(text);
    return this._qrcode;
};
/*
 ** @desc: 移除qrcode
 ** @param:
 ** @return:
 */
SingleProduct.prototype.removeQrcode = function () {
    this._qrcode.remove();
};

/*
 ** @desc: 创建discountIcon
 ** @param:  text => 显示的文本信息
 ** @return: 返回discountIcon
 */
SingleProduct.prototype.createDiscountIcon = function (text = '点我搜券') {

    // 设置id
    let id = 'skypesky-discountIcon';
    // create tag
    this._discountIcon = $('<button></button>').text(text);
    // set id property
    this._discountIcon.attr('id', id);
    this._discountIcon.attr('type', 'button');
    //set css property
    this._discountIcon.css({
        'position': 'fixed',
        'left': '0px',
        'bottom': '3rem',
        'margin-left': '5px',
        'padding': ".5em 1em",
        'width': '90px',
        'height': 'auto',
        'line-height': '24px',
        'font-family': 'Hiragino Sans GB,microsoft yahei,sans-serif',
        'font-size': '12px',
        'white-space': 'normal',
        'color': '#fff',
        'background-color': '#0e90d2',
        'border-color': '#0e90d2',
        'display': 'inline-block',
        'border-radius': '0',
        'border': '1px solid transparent',
        'text-align': 'center',
        'z-index': '666'
    });

    return this._discountIcon;
};
/*
 ** @desc: 更新discountIcon
 ** @param: text => 显示的文本信息
 ** @return: 返回discountIcon
 */
SingleProduct.prototype.updateDiscountIcon = function (text) {
    this._discountIcon.text(text);
    return this._discountIcon;
};

// 商品搜索处理类
function SearchProduct() {
    this._discountTag = null;
    // 统计数据
    this._statistic = new Statistic();
    // 搜索的商品数
    this._number = 0;
};

/*
 ** @desc: 创建discountTag
 ** @param: text => 显示的文本信息
 ** @return: 返回discountTag
 */
SearchProduct.prototype.createDiscountTag = function (text) {
    let id = 'skypesky-discountTag';
    // create tag
    this._discountTag = $('<span></span>');
    // 设置文本信息
    this._discountTag.text(text);
    // 设置id属性
    this._discountTag.attr('id', id);
    // 设置样式
    this._discountTag.css({
        'position': 'absolute',
        'right': '0px',
        'top': '0px',
        'padding': '0.5em 0.2em',
        'width': '80px',
        'height': 'auto',
        'line-height': '14px',
        'font-family': 'Hiragino Sans GB,microsoft yahei,sans-serif',
        'font-size': '14px',
        'white-space': 'normal',
        'color': '#fff',
        'background-color': '#DC143C',
        'display': 'inline-block',
        'text-align': 'center',
        'z-index': '8'
    });

    return this._discountTag;
};

// 运行函数
SearchProduct.prototype.run = function (website) {

    // 添加右键菜单    
    $.contextMenu({
        selector: 'body',
        callback: (key, options) => {
            if (key == "showSome") {
                // 仅显示优惠券信息
                iziToast.success({
                    message: "仅显示优惠券信息!",
                    position: "bottomLeft"
                });
                this.onlyShowDiscount();
            } else if (key == "showAll") {
                iziToast.success({
                    message: "显示所有信息!",
                    position: "bottomLeft"
                });
                this.showAll();
            } else if (key == "feedback") {
                // this.feedback();
                iziToast.error({
                    message: "此功能暂未开放!",
                    position: "bottomLeft"
                });
            } else {
                console.log("选择无效!")
            }
        },
        items: {
            "showSome": {
                name: "仅显示优惠券信息",
            },
            "line1": "--------------------",
            "showAll": {
                name: "显示所有信息",
            },
            "line2": "--------------------",
            "feedback": {
                name: "反馈",
            }
        }
    });

    // 当用户翻页时,也能正常搜券
    this.changePageRun(website);

    // 获取信息
    let idList = $(website.id.selector);
    let titleList = $(website.title.selector);
    let imageList = $(website.image.selector);

    // 获取商品总数
    this._number = idList.length;

    // console.log(idList.length, titleList.length, imageList.length);

    for (let i = 0; i < idList.length; ++i) {
        // 获取id
        let id = !website.id.rule ? $.trim($(idList[i]).attr(website.id.attr)) : $.trim($(idList[i]).attr(website.id.attr).match(website.id.rule).toString());
        // 获取标题(去除左右的空格)
        let title = $.trim($(titleList[i]).text());

        // 验证id和title
        if (!Validate.checkIdAndTitle(id, title)) {
            console.error(`${id} or ${title} error!`);
            continue;
        }
        // 请求api
        $.ajax({
            url: config.requestApi.url,
            method: config.requestApi.method,
            data: {
                title: title,
                id: id
            },
            success: (res) => {

                if (res && res.flag) { // 优惠券存在
                    // 成功的请求数
                    this._statistic.successCount++;

                    // 创建标签显示优惠信息
                    this.createDiscountTag(res.data.resultInfo.coupon_info);
                    // 设置需要跳转的链接
                    this._discountTag.attr('onclick', 'window.open("' + res.data.spreadUrl + '")');
                    $(imageList[i]).append(this._discountTag);


                    $($(website.product.selector)[i]).addClass(config.successClassTag);
                } else { //优惠券不存在

                    // 失败的请求数
                    this._statistic.errorCount++;

                    $($(website.product.selector)[i]).addClass(config.errorClassTag);
                }
            }
        });


    }

    // 找到优惠券给出提示
    let task = setInterval(() => {
        if (this._statistic.getAllCount() == idList.length) {
            clearInterval(task);
            iziToast.info({
                message: `已找到${this._statistic.successCount}张商品优惠券`,
                position: "bottomLeft"
            });
        }
    }, 100);

};

/**
 ** @desc: 仅显示优惠券信息
 ** @param: 
 ** @return:
 */
SearchProduct.prototype.onlyShowDiscount = function () {

    console.log("onlyShowDiscount");
    let task = setInterval(() => {
        if (this._statistic.getAllCount() == this._number) {
            clearInterval(task);
            const errorProductList = $(`.${config.errorClassTag}`);
            console.log("errorProductList: " + errorProductList.length);
            errorProductList.hide();
        }
    }, 100);
}

/**
 ** @desc: 显示所有信息(不管有没有优惠券)
 ** @param: 
 ** @return:
 */
SearchProduct.prototype.showAll = function () {
    console.log("showAll");
    let task = setInterval(() => {
        if (this._statistic.getAllCount() == this._number) {
            clearInterval(task);
            const errorProductList = $(`.${config.errorClassTag}`);
            console.log("errorProductList: " + errorProductList.length);
            errorProductList.show();
        }
    }, 100);
}

/*
 ** @desc: 定时检测网站路径的变化,当网站的路径变化时,再次运行搜券程序
 ** @param: website => 网站配置项
 */
SearchProduct.prototype.changePageRun = function (website) {

    // 获取初始的url
    let originUrl = window.location.href;
    // 注册一个定时器
    let task = setInterval(() => {
        // 用户切换商品页,导致url发送变化
        if (originUrl != window.location.href) {
            clearInterval(task);
            originUrl = window.location.href;
            // 清空数据
            this.clear();
            // 再次执行搜券函数
            this.run(website);
        }
    }, 100);

};

/*
 ** @desc: 清除一些参数
 ** @param:
 ** @return:
 */
SearchProduct.prototype.clear = function () {
    // 清除统计数据
    this._statistic.clear();
    // 搜索的商品数置为0
    this._number = 0;
}

// 工具类
const Util = {

    // 选择配置
    select: function (configList) {
        let url = window.location.href;
        for (let i = 0; i < configList.length; ++i) {
            if (configList[i].rule.test(url)) {
                return i;
            }
        }
        return -1;
    },

    // 执行配置
    run: function (configList, object) {
        let index = this.select(configList);
        if (index != -1) { //匹配成功
            console.log("匹配成功! " + configList[index].description);
            object.run(configList[index]);
        } else { //匹配失败
            console.log("匹配失败!");
        }
    },

    // 解决乱码问题
    toUtf8: function (str) {
        let out, i, len, c;
        out = "";
        len = str.length;
        for (i = 0; i < len; i++) {
            c = str.charCodeAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                out += str.charAt(i);
            } else if (c > 0x07FF) {
                out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
                out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
                out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
            } else {
                out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
                out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
            }
        }
        return out;
    }
};

// 验证工具类
const Validate = {

    // 验证id
    checkId: function (id) {
        // id不合法或者不是纯数字,返回false
        if (id == undefined || id == null || id == "" || !/\d*/.test(id)) {
            return false;
        }
        return true;
    },

    // 验证title
    checkTitle: function (title) {
        // title不合法返回false
        if (title == undefined || title == null || title == "") {
            return false;
        }
        return true;
    },

    checkIdAndTitle: function (id, title) {
        // id, title需要同时满足条件
        return (this.checkId(id) && this.checkTitle(title));
    },
};

// 统计数据
function Statistic() {
    this.successCount = 0;
    this.errorCount = 0;
}
/*
 ** @desc: 清空统计数据
 ** @param:
 ** @return:
 */
Statistic.prototype.clear = function () {
    this.successCount = 0;
    this.errorCount = 0;
}
/*
 ** @desc: 获取总的统计数
 ** @param:
 ** @return: 返回总的统计数
 */
Statistic.prototype.getAllCount = function () {
    return this.successCount + this.errorCount;
}

/*
 ** @desc: 获取成功率
 ** @param:
 ** @return: 返回统计的成功率,保留两位小数
 */
Statistic.prototype.getSuccessRate = function () {
    return (this.successCount / this.getAllCount()).toFixed(2);
}

// 导入样式库
const importUtil = {

    cssList: [],

    importCss: function () {
        let styles = "";
        for (let i = 0; i < this.cssList.length; ++i) {
            styles += GM_getResourceText(this.cssList[i]);
        }
        GM_addStyle(styles);
    },

    setCssList: function (cssList) {
        this.cssList = cssList;
    },

    clear: function () {
        this.cssList = null;
    }

}