B站直播心心助手

我的我的 都是我的

As of 2020-08-06. See the latest version.

// ==UserScript==
// @name         B站直播心心助手
// @namespace    http://tampermonkey.net/
// @version      7.2.2
// @description  我的我的 都是我的
// @author       逆回十六夜、SeaLoong
// @license      MIT License
// @include      /https?:\/\/live\.bilibili\.com\/\d+\??.*/
// @require      https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js
// @require      https://cdn.bootcss.com/layer/2.4/layer.js
// @require      https://greasyfork.org/scripts/406141-bilibiliapi-1-4-6/code/BilibiliAPI_146.js
// @require      https://greasyfork.org/scripts/406185-ocrad2-3-2/code/OCRAD232.js
// ==/UserScript==
/*
[github源]
// @require      https://raw.githubusercontent.com/sakamaki-izayoi-LYN/API/master/BilibiliAPI.js
// @require      https://raw.githubusercontent.com/sakamaki-izayoi-LYN/API/master/OCRAD.min.js
[腾讯云源]
// @require      https://js-1258131272.file.myqcloud.com/BilibiliAPI.js
// @require      https://js-1258131272.file.myqcloud.com/OCRAD.min.js
[jsDelivr源]
// @require      https://cdn.jsdelivr.net/gh/sakamaki-izayoi-LYN/API/BilibiliAPI.js
// @require      https://cdn.jsdelivr.net/gh/sakamaki-izayoi-LYN/API/OCRAD.min.js
[GitCDN源]
// @require      https://gitcdn.link/repo/sakamaki-izayoi-LYN/API/master/BilibiliAPI.js
// @require      https://gitcdn.link/repo/sakamaki-izayoi-LYN/API/master/OCRAD.min.js
*/
let logSwitch = false; //控制开关
let NAME = 'IZAYOI';
let BAPI, MY_API, TreasureBox, SmallHeart;
if (!logSwitch) {
    console.log = () => {
    };//关闭控制台输出
}
let Info = {
    roomId: undefined,
    uid: undefined,
    silver: undefined,
    gold: undefined,
    mobile_verify: undefined,
    identification: undefined,
    awardBlocked: undefined,
    token: undefined,
};
$(function () {//DOM完毕,等待弹幕加载完成
    let loadInfo = (delay) => {
        setTimeout(function () {
            if (BilibiliLive === undefined || parseInt(BilibiliLive.UID) === 0 || isNaN(parseInt(BilibiliLive.UID))) {
                loadInfo(1000);
                console.log('无配置信息');
            } else {
                Info.roomId = BilibiliLive.ROOMID;
                Info.uid = BilibiliLive.UID;
                console.log(Info);
                init();
            }
        }, delay);
    };
    loadInfo(1000);
    addStyle();//加载style
    $('head').append('<link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/layer/2.4/skin/layer.css">');//加载layer样式
});

Array.prototype.remove = function (val) {
    let index = this.indexOf(val);
    if (index > -1) {
        this.splice(index, 1);
    }
};

function addStyle() {
    $('head').append(`
<style>
    .izayoi_input{
        outline: none;
        border: 1px solid #e9eaec;
        background-color: #fff;
        border-radius: 4px;
        padding: 1px 0 0;
        overflow: hidden;
        font-size: 12px;
        line-height: 19px;
        width: 30px;
    }
    .izayoi_btn{
        background-color: #23ade5;
        color: #fff;
        border-radius: 4px;
        border: none;
        padding: 5px;
        cursor: pointer;
        box-shadow: 0 0 2px #00000075;
    }
    .izayoi_fs{
        border: 2px solid #d4d4d4;
    }
</style>
    `)
}

function init() {//API初始化
    try {
        BAPI = BilibiliAPI;
    } catch (err) {
        alert(`[${NAME}]BilibiliAPI初始化失败,请手动更改源`);
        return;
    }
    let tk = getCookie('bili_jct');
    BAPI.setCommonArgs(tk, '');// 设置token
    Info.token = tk;

    MY_API = {
        CONFIG_DEFAULT: {
            TIME_RELOAD: 60,
            RANDOM_DELAY: true,
            TIME_AREA_DISABLE: false,
            TIME_AREA_START: 2,
            TIME_AREA_END: 8,
            RANDOM_SKIP: 0,
            MAX_GIFT: 99999,
            AUTO_TREASUREBOX: true,
            AUTO_EXCHANGE: true,
            AUTO_SIGNIN: true,
            AUTO_HEART: true,
            SHARE: true,
            AUTO_GROUP_SIGN: true,
        },
        CONFIG: {},
        GIFT_COUNT: {
            COUNT: 0,
            LOVE_COUNT: 0,
            CLEAR_TS: 0,
            EXCHANGE_TS: 0,
            SIGN_TS: 0,
            SHARE_TS: 0,
            AUTO_GROUP_SIGH_TS: 0,
        },
        init: function () {
            let p = $.Deferred();
            try {
                MY_API.loadConfig().then(function () {
                    MY_API.chatLog('脚本载入配置成功', 'success');
                    p.resolve()
                });
            } catch (e) {
                console.log('API初始化出错', e);
                MY_API.chatLog('脚本初始化出错', 'warning');
                p.reject()
            }
            return p
        },
        loadConfig: function () {
            let p = $.Deferred();
            try {
                let config = JSON.parse(localStorage.getItem(`${NAME}_CONFIG`));
                $.extend(true, MY_API.CONFIG, MY_API.CONFIG_DEFAULT);
                for (let item in MY_API.CONFIG) {
                    if (!MY_API.CONFIG.hasOwnProperty(item)) continue;
                    if (config[item] !== undefined && config[item] !== null) MY_API.CONFIG[item] = config[item];
                }
                MY_API.loadGiftCount();//载入礼物统计
                p.resolve()
            } catch (e) {
                console.log('API载入配置失败,加载默认配置', e);
                MY_API.setDefaults();
                p.reject()
            }
            return p
        },
        saveConfig: function () {
            try {
                localStorage.setItem(`${NAME}_CONFIG`, JSON.stringify(MY_API.CONFIG));
                MY_API.chatLog('配置已保存');
                console.log(MY_API.CONFIG);
                return true
            } catch (e) {
                console.log('API保存出错', e);
                return false
            }
        },
        setDefaults: function () {
            MY_API.CONFIG = MY_API.CONFIG_DEFAULT;
            MY_API.saveConfig();
            MY_API.chatLog('配置已重置为默认3秒后刷新页面');
            setTimeout(() => {
                window.location.reload()
            }, 3000);
        },
        loadGiftCount: function () {
            try {
                let config = JSON.parse(localStorage.getItem(`${NAME}_GIFT_COUNT`));
                for (let item in MY_API.GIFT_COUNT) {
                    if (!MY_API.GIFT_COUNT.hasOwnProperty(item)) continue;
                    if (config[item] !== undefined && config[item] !== null) MY_API.GIFT_COUNT[item] = config[item];
                }
                console.log(MY_API.GIFT_COUNT);
            } catch (e) {
                console.log('读取统计失败', e);
            }
        },
        saveGiftCount: function () {
            try {
                localStorage.setItem(`${NAME}_GIFT_COUNT`, JSON.stringify(MY_API.GIFT_COUNT));
                console.log('统计保存成功', MY_API.GIFT_COUNT);
                return true
            } catch (e) {
                console.log('统计保存出错', e);
                return false
            }
        },
        addGift: function (count) {
            MY_API.GIFT_COUNT.COUNT += count;
            $('#giftCount span:eq(0)').text(MY_API.GIFT_COUNT.COUNT);
            MY_API.saveGiftCount();
        },
        addLove: function (count) {
            MY_API.GIFT_COUNT.LOVE_COUNT = parseInt(MY_API.GIFT_COUNT.LOVE_COUNT) + parseInt(count);
            $('#giftCount span:eq(1)').text(MY_API.GIFT_COUNT.LOVE_COUNT);
            MY_API.saveGiftCount();
        },
        creatSetBox: function () {//创建设置框
            //添加按钮
            let btn = $('<button style="opacity: .5;position: absolute; top: 128px; left: 0;z-index: 10;background-color: #23ade5;color: #fff;border-radius: 4px;border: none;padding: 5px;cursor: pointer;box-shadow: 1px 1px 2px #00000075;">' +
                '隐藏信息</button>');
            btn.click(function () {
                $('.izayoiMsg').hide();
            });
            $('.chat-history-panel').append(btn);
            let div = $('<div>');
            div.css({
                'width': '400px',
                'height': '54px',
                'position': 'absolute',
                'top': '110px',
                'right': '10px',
                'background': '#F0F0F0',
                'padding': '10px',
                'z-index': '10',
                'border-radius': '4px',
                'overflow': 'hidden',
                'box-shadow': '1px 1px 2px #00000075',
            });

            div.on('mouseover mouseout', '', function (e) {
                if (e.type === 'mouseover') {
                    $(this).css('height', 'auto');
                } else {
                    $(this).css('height', '54px');
                }
            });

            div.append(`
<fieldset class="izayoi_fs">
     <legend>今日统计</legend>
            <div id="giftCount" style="font-size: large; text-shadow: 1px 1px #00000066; color: blueviolet;">
                ❤·<span>${MY_API.GIFT_COUNT.COUNT}</span>
                 银瓜子·<span>${MY_API.GIFT_COUNT.LOVE_COUNT}</span>
                 <button class="izayoi_btn" style="font-size: small" data-action="countReset">重置统计(会刷新页面)</button>
            </div>
</fieldset>
<fieldset class="izayoi_fs">
     <legend>瓜子及签到</legend>
        <div data-toggle="AUTO_SIGNIN">
        <label style="cursor: pointer; margin: 5px auto;">
        <input style="vertical-align: text-top;" type="checkbox">自动签到
        </label>
        </div>
        <div data-toggle="AUTO_EXCHANGE">
        <label style="cursor: pointer; margin: 5px auto;">
        <input style="vertical-align: text-top;" type="checkbox">自动银瓜子换硬币(一天一个)
        </label>
        </div>
        <div data-toggle="AUTO_TREASUREBOX">
        <label style="cursor: pointer; margin: 5px auto;">
        <input style="vertical-align: text-top;" type="checkbox">自动领瓜子
        </label>
        </div>
        <div data-toggle="AUTO_HEART">
        <label style="cursor: pointer; margin: 5px auto;">
        <input style="vertical-align: text-top;" type="checkbox">自动挂心心
        </label>
        </div>
        <div data-toggle="SHARE">
        <label style="cursor: pointer; margin: 5px auto;">
        <input style="vertical-align: text-top;" type="checkbox">自动分享视频(5点经验,不会出现在动态)
        </label>
        </div>
        <div data-toggle="AUTO_GROUP_SIGN">
        <label style="cursor: pointer; margin: 5px auto;">
        <input style="vertical-align: text-top;" type="checkbox">自动应援团签到(相应勋章加10点亲密度)
        </label>
        </div>
</fieldset>
<fieldset class="izayoi_fs">
     <legend>功能</legend>
     <div data-toggle="BUY_BADGE">
        <label style="cursor: pointer; margin: 5px auto; color: blue">
        20硬币购买指定直播间勋章 直播间号:<input class="ID izayoi_input" style="width: 70px;" value="你自己填好伐" type="text">
        </label>
        <button data-action="buy" class="izayoi_btn">购买</button>
        <br>↑和视频投币不一样 购买后的勋章无需领取 官方接口放心使用↑
    </div>
</fieldset>
<fieldset class="izayoi_fs">
    <legend>其他设置</legend>
    <div data-toggle="TIME_RELOAD">
    本直播间重载时间(整数 刷新后生效 别设置成0):
    <input class="delay-seconds izayoi_input" type="text" style="width: 30px;">分
    <button data-action="save" class="izayoi_btn">保存</button>
    </div>
    <div><button data-action="reset" style="color: red;" class="izayoi_btn">重置所有为默认</button></div>
</fieldset>
<fieldset class="izayoi_fs">
    <legend>说明</legend>
    <span style="color: #cf00ff;">小心心最多同时挂四个,也就是四倍速,30分钟挂满24个</span><br>
    <span style="color: #cf00ff;">小心心统计会有一点延迟</span><br>
    <span style="color: #cf00ff;">脚本启用后直播间默认静音状态</span><br>
    <span style="color: #cf00ff;">请禁用浏览器Flash以节省资源(默认都是禁用的)</span><br>
</fieldset>
<fieldset class="izayoi_fs">
    <legend>更新</legend>
    <ul>
        <li><span style="color: rgb(79,178,255);">8-6:修改心心统计模式,添加应援团签到、每日分享功能</span></li>
        <li><span style="color: rgb(79,178,255);">8-3:删除部分辣条代码,添加挂小心心功能</span></li>
        <li><span style="color: rgb(79,178,255);">7-20:添加硬币购买勋章功能</span></li>
    </ul>
</fieldset>
`);
            $('.player-ctnr').append(div);

            let checkList = [
                'AUTO_SIGNIN',
                'AUTO_EXCHANGE',
                'AUTO_TREASUREBOX',
                'AUTO_HEART',
                'SHARE',
                'AUTO_GROUP_SIGN',
            ];
            for (let i of checkList) {//所有checkbox事件绑定
                let input = div.find(`div[data-toggle="${i}"] input:checkbox`);
                if (MY_API.CONFIG[i]) input.attr('checked', '');
                input.change(function () {
                    MY_API.CONFIG[i] = $(this).prop('checked');
                    MY_API.saveConfig()
                });
            }

            //对应配置状态
            div.find('div[data-toggle="TIME_RELOAD"] .delay-seconds').val(MY_API.CONFIG.TIME_RELOAD.toString());

            //事件绑定
            div.find('button[data-action="reset"]').click(function () {//重置按钮
                MY_API.setDefaults();
            });

            div.find('#giftCount [data-action="countReset"]').click(function () {//
                MY_API.GIFT_COUNT = {
                    COUNT: 0,
                    LOVE_COUNT: 0,
                    CLEAR_TS: 0,
                };
                MY_API.saveGiftCount();
                MY_API.chatLog('已清空3秒后刷新页面');
                setTimeout(() => {
                    window.location.reload()
                }, 3000);
            });

            div.find('div[data-toggle="TIME_RELOAD"] [data-action="save"]').click(function () {//TIME_RELOAD save按钮
                let val = parseInt(div.find('div[data-toggle="TIME_RELOAD"] .delay-seconds').val());
                if (MY_API.CONFIG.TIME_RELOAD === val) {
                    MY_API.chatLog('改都没改保存尼玛呢');
                    return
                }
                if (val <= 0 || val > 10000) {
                    MY_API.chatLog('你咋不上天呢');
                    return
                }
                MY_API.CONFIG.TIME_RELOAD = val;
                MY_API.saveConfig()
            });

            div.find('div[data-toggle="BUY_BADGE"] [data-action="buy"]').click(function () {
                let id = parseInt(div.find('div[data-toggle="BUY_BADGE"] .ID').val());
                MY_API.buyBadge(id);
            });
        },
        chatLog: function (text, type = 'info') {//自定义提示
            let div = $("<div class='izayoiMsg'>");
            let msg = $("<div>");
            let ct = $('#chat-history-list');
            let myDate = new Date();
            msg.html(text);
            div.text(myDate.toLocaleString());
            div.append(msg);
            div.css({
                'text-align': 'center',
                'border-radius': '4px',
                'min-height': '30px',
                'width': '256px',
                'color': '#9585FF',
                'line-height': '30px',
                'padding': '0 10px',
                'margin': '10px auto',
            });
            msg.css({
                'word-wrap': 'break-word',
                'width': '100%',
                'line-height': '1em',
                'margin-bottom': '10px',
            });
            switch (type) {
                case 'warning':
                    div.css({
                        'border': '1px solid rgb(236, 221, 192)',
                        'color': 'rgb(218, 142, 36)',
                        'background': 'rgb(245, 235, 221) none repeat scroll 0% 0%',
                    });
                    break;
                case 'success':
                    div.css({
                        'border': '1px solid rgba(22, 140, 0, 0.28)',
                        'color': 'rgb(69, 171, 69)',
                        'background': 'none 0% 0% repeat scroll rgba(16, 255, 0, 0.18)',
                    });
                    break;
                case 'error':
                    div.css({
                        'border': '1px solid rgba(255, 0, 39, 0.28)',
                        'color': 'rgb(116,0,15)',
                        'background': 'none 0% 0% repeat scroll rgba(255, 0, 39, 0.18)',
                    });
                    break;
                default:
                    div.css({
                        'border': '1px solid rgb(203, 195, 255)',
                        'background': 'rgb(233, 230, 255) none repeat scroll 0% 0%',
                    });
            }
            ct.find('#chat-items').append(div);//向聊天框加入信息
            ct.scrollTop(ct.prop("scrollHeight"));//滚动到底部
        },
        blocked: false,
        max_blocked: false,
        listen: (roomId, uid, area = '本直播间') => {
            BAPI.room.getConf(roomId).then((response) => {
                console.log('服务器地址', response);
                let wst = new BAPI.DanmuWebSocket(uid, roomId, response.data.host_server_list, response.data.token);
                wst.bind((newWst) => {
                    wst = newWst;
                    MY_API.chatLog(`${area}弹幕服务器连接断开,尝试重连`, 'warning');
                }, () => {
                    MY_API.chatLog(`连接弹幕服务器成功<br>房间号: ${roomId} 分区: ${area}`
                        , 'success');
                }, () => {
                    if (MY_API.blocked) {
                        wst.close();
                        MY_API.chatLog(`进了小黑屋主动与弹幕服务器断开连接-${area}`, 'warning')
                    }
                    if (MY_API.max_blocked) {
                        wst.close();
                        MY_API.chatLog(`辣条最大值主动与弹幕服务器断开连接-${area}`, 'warning')
                    }
                }, (obj) => {
                    if (inTimeArea(MY_API.CONFIG.TIME_AREA_START, MY_API.CONFIG.TIME_AREA_END) && MY_API.CONFIG.TIME_AREA_DISABLE) return;//当前是否在两点到八点 如果在则返回

                    console.log('弹幕公告' + area, obj);
                    switch (obj.cmd) {
                        case 'GUARD_MSG':
                            if (obj.roomid === obj.real_roomid) {
                                MY_API.checkRoom(obj.roomid, area);
                            } else {
                                MY_API.checkRoom(obj.roomid, area);
                                MY_API.checkRoom(obj.real_roomid, area);
                            }
                            break;
                        case 'PK_BATTLE_SETTLE_USER':
                            if (!!obj.data.winner) {
                                MY_API.checkRoom(obj.data.winner.room_id, area);
                            } else {
                                MY_API.checkRoom(obj.data.my_info.room_id, area);
                            }
                            break;
                        case 'NOTICE_MSG':
                            if (obj.roomid === obj.real_roomid) {
                                MY_API.checkRoom(obj.roomid, area);
                            } else {
                                MY_API.checkRoom(obj.roomid, area);
                                MY_API.checkRoom(obj.real_roomid, area);
                            }
                            break;
                        default:
                            return;
                    }
                });
            }, () => {
                MY_API.chatLog('获取弹幕服务器地址错误', 'warning')
            });
        },
        RoomId_list: [],
        err_roomId: [],
        checkRoom: function (roomId, area = '本直播间') {
            if (roomId === undefined) return;
            if (MY_API.blocked || MY_API.max_blocked) {
                return
            }
            if (MY_API.RoomId_list.indexOf(roomId) >= 0) {//防止重复检查直播间
                return
            } else {
                MY_API.RoomId_list.push(roomId);
            }
            BAPI.room.room_entry_action(roomId);//直播间进入记录
            $.get('https://api.live.bilibili.com/xlive/lottery-interface/v1/lottery/Check?roomid=' + roomId,
                function (re) {
                    setTimeout(() => {
                        MY_API.RoomId_list.remove(roomId);//移除房间号
                        // console.log('防重复检查房间号列表', MY_API.RoomId_list);
                    }, 5e3);
                    console.log('检查房间返回信息', re);
                    let data = re.data;
                    if (re.code === 0) {
                        let list;
                        if (data.gift) {
                            list = data.gift;
                            for (let i in list) {
                                if (!list.hasOwnProperty(i)) continue;
                                MY_API.creat_join(roomId, list[i], 'gift', area)
                            }
                        }
                        if (data.guard) {
                            list = data.guard;
                            for (let i in list) {
                                if (!list.hasOwnProperty(i)) continue;
                                MY_API.creat_join(roomId, list[i], 'guard', area)
                            }
                        }
                        if (data.pk) {
                            list = data.pk;
                            for (let i in list) {
                                if (!list.hasOwnProperty(i)) continue;
                                MY_API.creat_join(roomId, list[i], 'pk', area)
                            }
                        }
                    } else {
                        if (MY_API.err_roomId.indexOf(roomId) > -1) {
                            console.log(`[检查此房间出错多次]${roomId}${re.message}`);
                        } else {
                            MY_API.err_roomId.push(roomId);
                            MY_API.checkRoom(roomId, area);
                            console.log(`[检查房间出错_重试一次]${roomId}${re.message}`);
                        }

                    }
                });
        },
        Id_list_history: {
            add: function (id, type) {
                let id_list = [];
                try {
                    let config = JSON.parse(localStorage.getItem(`${NAME}_${type}Id_list`));
                    id_list = [].concat(config.list);
                    id_list.push(id);
                    if (id_list.length > 1000) {
                        id_list.splice(0, 200);//删除前200条数据
                    }
                    localStorage.setItem(`${NAME}_${type}Id_list`, JSON.stringify({list: id_list}));
                    console.log(`${NAME}_${type}Id_list_add`, id_list);
                } catch (e) {
                    id_list.push(id);
                    localStorage.setItem(`${NAME}_${type}Id_list`, JSON.stringify({list: id_list}));
                }
            },
            isIn: function (id, type) {
                let id_list = [];
                try {
                    let config = JSON.parse(localStorage.getItem(`${NAME}_${type}Id_list`));
                    if (config === null) {
                        id_list = [];
                    } else {
                        id_list = [].concat(config.list);
                    }
                    console.log(`${NAME}_${type}Id_list_read`, config);
                    return id_list.indexOf(id) > -1
                } catch (e) {
                    localStorage.setItem(`${NAME}_${type}Id_list`, JSON.stringify({list: id_list}));
                    console.log('读取' + `${NAME}_${type}Id_list` + '缓存错误已重置');
                    return id_list.indexOf(id) > -1
                }
            }
        },
        raffleId_list: [],
        guardId_list: [],
        pkId_list: [],
        creat_join: function (roomId, data, type, area = '本直播间') {
            console.log('礼物信息', data);
            if (MY_API.GIFT_COUNT.COUNT >= MY_API.CONFIG.MAX_GIFT) {//判断是否超过辣条限制
                console.log('超过今日辣条限制,不参与抽奖');
                MY_API.max_blocked = true;
                return
            }
            switch (type) {//防止重复抽奖上船PK
                case 'gift':
                    if (MY_API.Id_list_history.isIn(data.raffleId, 'raffle')) {
                        console.log('礼物重复');
                        return
                    } else {
                        MY_API.raffleId_list.push(data.raffleId);
                        MY_API.Id_list_history.add(data.raffleId, 'raffle');
                    }
                    break;
                case 'guard':
                    if (MY_API.Id_list_history.isIn(data.id, 'guard')) {
                        console.log('舰长重复');
                        return
                    } else {
                        MY_API.guardId_list.push(data.id);
                        MY_API.Id_list_history.add(data.id, 'guard');
                    }
                    break;
                case 'pk':
                    if (MY_API.Id_list_history.isIn(data.id, 'pk')) {
                        console.log('pk重复');
                        return
                    } else {
                        MY_API.pkId_list.push(data.id);
                        MY_API.Id_list_history.add(data.id, 'pk');
                    }
                    break;
            }

            let delay = data.time_wait || 0;
            if (MY_API.CONFIG.RANDOM_DELAY) delay += 2 + Math.ceil(Math.random() * 8);//随机延迟
            let div = $("<div class='izayoiMsg'>");
            let msg = $("<div>");
            let aa = $("<div>");
            let ct = $('#chat-history-list');
            let myDate = new Date();
            msg.text(`[${area}]` + data.thank_text.split('<%')[1].split('%>')[0] + data.thank_text.split('%>')[1]);
            div.text(myDate.toLocaleString());
            div.append(msg);
            aa.css('color', 'red');
            msg.append(aa);
            div.css({
                'text-align': 'center',
                'border-radius': '4px',
                'min-height': '30px',
                'width': '256px',
                'color': '#9585FF',
                'line-height': '30px',
                'padding': '0 10px',
                'margin': '10px auto',
            });
            msg.css({
                'word-wrap': 'break-word',
                'width': '100%',
                'line-height': '1em',
                'margin-bottom': '10px',
            });

            div.css({
                'border': '1px solid rgb(203, 195, 255)',
                'background': 'rgb(233, 230, 255) none repeat scroll 0% 0%',
            });

            ct.find('#chat-items').append(div);//向聊天框加入信息
            ct.scrollTop(ct.prop("scrollHeight"));//滚动到底部
            let run = () => {
                aa.text(`等待抽奖倒计时${delay}秒`);
                if (delay <= 0) {
                    if (probability(MY_API.CONFIG.RANDOM_SKIP)) {
                        aa.text(`跳过此礼物抽奖`);
                    } else {
                        switch (type) {
                            case 'gift':
                                MY_API.lineUpCall(aa, MY_API.gift_join, roomId, data.raffleId, data.type).then(function (msg, num) {
                                    aa.css('color', 'green');
                                    aa.text('获得' + msg);
                                    if (num) {
                                        if (msg.indexOf('辣条') > -1) {
                                            MY_API.addGift(num);
                                        } else if (msg.indexOf('亲密度') > -1) {
                                            MY_API.addLove(num);
                                        }
                                    }
                                    MY_API.raffleId_list.remove(data.raffleId);//移除礼物id列表
                                });
                                break;
                            case 'guard':
                                MY_API.lineUpCall(aa, MY_API.guard_join, roomId, data.id).then(function (msg, num) {
                                    aa.css('color', 'green');
                                    aa.text('获得' + msg);
                                    if (num) {
                                        if (msg.indexOf('辣条') > -1) {
                                            MY_API.addGift(num);
                                        } else if (msg.indexOf('亲密度') > -1) {
                                            MY_API.addLove(num);
                                        }
                                    }
                                    MY_API.guardId_list.remove(data.id);//移除礼物id列表
                                });
                                break;
                            case 'pk':
                                MY_API.lineUpCall(aa, MY_API.pk_join, roomId, data.id).then(function (msg, num) {
                                    aa.css('color', 'green');
                                    aa.text('获得' + msg);
                                    if (num) {
                                        if (msg.indexOf('辣条') > -1) {
                                            MY_API.addGift(num);
                                        } else if (msg.indexOf('亲密度') > -1) {
                                            MY_API.addLove(num);
                                        }
                                    }
                                    MY_API.pkId_list.remove(data.id);//移除礼物id列表
                                });
                                break;
                        }
                    }
                    clearInterval(timer)
                }
                delay--;
            };
            let timer = setInterval(run, 1000);
            run();
        },
        gift_join: function (roomid, raffleId, type) {
            let p = $.Deferred();
            BAPI.Lottery.Gift.join(roomid, raffleId, type).then((response) => {
                console.log('抽奖返回信息', response);
                switch (response.code) {
                    case 0:
                        if (response.data.award_text) {
                            p.resolve(response.data.award_text, response.data.award_num);
                        } else {
                            p.resolve(response.data.award_name + 'X' + response.data.award_num.toString()
                                , response.data.award_num);
                        }
                        break;
                    default:
                        if (response.msg.indexOf('拒绝') > -1) {
                            MY_API.blocked = true;//停止抽奖
                            p.resolve('访问被拒绝,您的帐号可能已经被关小黑屋,已停止');
                        } else {
                            p.resolve(`[礼物抽奖](roomid=${roomid},id=${raffleId},type=${type})${response.msg}`);
                        }
                }
            });
            return p
        },
        guard_join: function (roomid, Id) {
            let p = $.Deferred();
            BAPI.Lottery.Guard.join(roomid, Id).then((response) => {
                console.log('上船抽奖返回信息', response);
                switch (response.code) {
                    case 0:
                        if (response.data.award_text) {
                            p.resolve(response.data.award_text, response.data.award_num);
                        } else {
                            p.resolve(response.data.award_name + 'X' + response.data.award_num.toString()
                                , response.data.award_num);
                        }
                        break;
                    default:
                        if (response.msg.indexOf('拒绝') > -1) {
                            MY_API.blocked = true;//停止抽奖
                            p.resolve('访问被拒绝,您的帐号可能已经被关小黑屋,已停止');
                        } else {
                            p.resolve(`[上船](roomid=${roomid},id=${Id})${response.msg}`);
                        }
                        break;
                }
            });
            return p
        },
        pk_join: function (roomid, Id) {
            let p = $.Deferred();
            BAPI.Lottery.Pk.join(roomid, Id).then((response) => {
                console.log('PK抽奖返回信息', response);
                switch (response.code) {
                    case 0:
                        if (response.data.award_text) {
                            p.resolve(response.data.award_text, response.data.award_num);
                        } else {
                            p.resolve(response.data.award_name + 'X' + response.data.award_num.toString()
                                , response.data.award_num);
                        }
                        break;
                    default:
                        if (response.msg.indexOf('拒绝') > -1) {
                            MY_API.blocked = true;//停止抽奖
                            p.resolve('访问被拒绝,您的帐号可能已经被关小黑屋,已停止');
                        } else {
                            p.resolve(`[PK](roomid=${roomid},id=${Id})${response.msg}`);
                        }
                        break;
                }
            });
            return p
        },
        Exchange: {
            run: () => {
                try {
                    return MY_API.Exchange.silver2coin().then(() => {
                    }, () => delayCall(() => MY_API.Exchange.run()));
                } catch (err) {
                    MY_API.chatLog('[银瓜子换硬币]运行时出现异常,已停止', 'error');
                    console.error(`[${NAME}]`, err);
                    return $.Deferred().reject();
                }
            },
            silver2coin: () => {
                return BAPI.Exchange.silver2coin().then((response) => {
                    console.log('Exchange.silver2coin: API.SilverCoinExchange.silver2coin', response);
                    if (response.code === 0) {
                        // 兑换成功
                        MY_API.chatLog(`[银瓜子换硬币]${response.msg}`, 'success');
                    } else if (response.code === 403) {
                        // 每天最多能兑换 1 个
                        // 银瓜子余额不足
                        MY_API.chatLog(`[银瓜子换硬币]${response.msg}`, 'info');
                    } else {
                        MY_API.chatLog(`[银瓜子换硬币]${response.msg}`, 'caution');
                    }
                }, () => {
                    MY_API.chatLog('[银瓜子换硬币]兑换失败,请检查网络', 'error');
                    return delayCall(() => MY_API.Exchange.silver2coin());
                });
            }
        },
        lineList: [],
        lineDelay: 0,
        reSetDelay: function () {
            this.lineDelay = this.CONFIG.LINE_DELAY;
            let timer = setInterval(() => {
                if (this.lineDelay > 0.15) {
                    this.lineDelay -= 0.1;
                } else {
                    this.lineDelay = 0;
                    clearInterval(timer);
                }
            }, 100)
        },
        lineUpCall: function (div, fun, arg1, arg2, arg3, arg4, arg5, arg6) {
            let p = $.Deferred();
            let delayRunTimer;
            let delayRun = () => {
                if (this.lineDelay === 0) {
                    run();
                } else {
                    delayRunTimer = setInterval(() => {
                        if (this.lineDelay === 0) {
                            run();
                            clearInterval(delayRunTimer);
                        } else {
                            div.css('color', '#b700ff');
                            div.text(`抽奖等待中...${this.lineDelay.toFixed(1)}S`);
                        }
                    }, 100)
                }
            };
            let run = () => {
                this.lineList.shift();//删除第一个
                this.reSetDelay();//重置冷却
                div.text(`进行抽奖...`);
                let funRt = fun(arg1, arg2, arg3, arg4, arg5, arg6);
                if (funRt && funRt.then) funRt.then((arg1, arg2, arg3, arg4, arg5, arg6) => p.resolve(arg1, arg2, arg3, arg4, arg5, arg6));
                else p.resolve();
                if (this.lineList.length !== 0) this.lineList[0]();
            };

            if (this.CONFIG.LINE_DELAY === 0) {//如果为延迟0则直接运行
                run();
            } else if (this.lineList.length === 0) {
                this.lineList.push(delayRun);
                delayRun();
            } else {
                this.lineList.push(delayRun);
                div.css('color', '#00b5e5');
                div.text(`排队中...`);
            }
            return p
        },
        sendBagGift: function () {
            BAPI.gift.bag_list().then(function (bagResult) {
                if (bagResult.data.list[0].corner_mark === '1天' && bagResult.data.list[0].gift_name === '辣条') {
                    BAPI.live_user.get_anchor_in_room(MY_API.CONFIG.GIFT_ROOM).then(function (roomResult) {
                        bagSend(roomResult.data.info.uid,
                            bagResult.data.list[0].gift_id,
                            bagResult.data.list[0].bag_id,
                            bagResult.data.list[0].gift_num)
                    });
                }
            });
            let bagSend = (rUid, gift_id, bag_id, num) => {
                let ts = Math.round(new Date() / 1000);//时间戳
                BAPI.gift.bag_send(Info.uid, gift_id, rUid, num, bag_id, MY_API.CONFIG.GIFT_ROOM, ts).then(function (result) {
                    if (result.code === 0 && result.msg === 'success') {
                        MY_API.chatLog('[只剩1天辣条]' + result.data.send_tips, 'success');
                    } else {
                        MY_API.chatLog('[礼物]赠送失败', 'warning');
                    }
                });
            }
        },
        buyBadge: function (roomId) {
            BAPI.live_user.get_anchor_in_room(roomId).then(function (roomResult) {
                if (roomResult.code === 0) {
                    if (confirm(`提示:该房间主播是${roomResult.data.info.uname} 确定购买勋章吗?`)) {
                        MY_API.buyRequest(roomResult.data.info.uid).then(function (data) {
                            MY_API.chatLog(`购买勋章${data.code === 0 ? '成功' : `失败 code ${data.code} ${data.message}`}`)
                        }, function () {
                            MY_API.chatLog(`购买勋章出错`)
                        });
                    }
                } else {
                    MY_API.chatLog('检测房间出错,你确定是正确房间ID?');
                }
            });
        },
        buyRequest: function (uid) {
            let p = $.Deferred();
            $.ajax({
                url: '//api.vc.bilibili.com/link_group/v1/member/buy_medal',
                method: 'POST',
                data: {
                    coin_type: 'metal',
                    master_uid: uid,
                    platform: 'android',
                    csrf_token: Info.token,
                    csrf: Info.token
                },
                success: function (result) {
                    p.resolve(result);
                },
                error: function () {
                    p.reject();
                },
                crossDomain: true,
                dataType: 'json',
                xhrFields: {
                    withCredentials: true,
                },
            });
            return p
        },
        share: async () => {
            if (!MY_API.CONFIG.SHARE) return $.Deferred().resolve();
            if (!checkNewDay(MY_API.GIFT_COUNT.SHARE_TS)) {
                console.log(`[${NAME}]无需分享`);
                return
            }
            MY_API.GIFT_COUNT.SHARE_TS = ts_ms();
            MY_API.saveGiftCount();
            let response = await BAPI.dynamic_svr.dynamic_new(Info.uid, 8).catch(() => {
                console.log('获取"动态-投稿视频"失败,请检查网络', 'error');
            });
            let aid = 0;
            if (response.code === 0) {
                if (!!response.data.cards) {
                    let obj = JSON.parse(response.data.cards[0].card);
                    aid = obj.aid;
                    BAPI.DailyReward.share(aid).then((response) => {
                        if (response.code === 0) {
                            MY_API.chatLog(`[每日分享]分享成功(av=${aid})`, 'success');
                        } else if (response.code === 71000) {
                            // 重复分享
                            MY_API.chatLog('[每日分享]今日分享已完成', 'info');
                        } else {
                            MY_API.chatLog(`[每日分享]'${response.msg}`, 'warning');
                        }
                    }, () => {
                        MY_API.chatLog('[每日分享]分享失败,请检查网络', 'error');
                        return delayCall(() => MY_API.share(aid));
                    });
                } else {
                    console.log('"动态-投稿视频"中暂无动态', 'info');
                }
            } else {
                console.log(`获取"动态-投稿视频"'${response.msg}`, 'caution');
            }
        },
        GroupSign: {//修改自SeaLoong大神的代码
            getGroups: () => {//获取应援团列表
                return BAPI.Group.my_groups().then((response) => {
                    if (response.code === 0) return $.Deferred().resolve(response.data.list);
                    MY_API.chatLog(`[自动应援团签到]'${response.msg}`, 'warning');
                    return $.Deferred().reject();
                }, () => {
                    MY_API.chatLog('[自动应援团签到]获取应援团列表失败,请检查网络', 'error');
                    return delayCall(() => MY_API.GroupSign.getGroups());
                });
            },
            signInList: (list, i = 0) => {//应援团签到
                if (i >= list.length) return $.Deferred().resolve();
                const obj = list[i];
                //自己不能给自己的应援团应援
                if (obj.owner_uid === Info.uid) return MY_API.GroupSign.signInList(list, i + 1);
                return BAPI.Group.sign_in(obj.group_id, obj.owner_uid).then((response) => {
                    let p = $.Deferred();
                    if (response.code === 0) {
                        if (response.data.add_num > 0) {// || response.data.status === 1
                            MY_API.chatLog(`[自动应援团签到]应援团${obj.group_name}签到成功,当前勋章亲密度+${response.data.add_num}`, 'success');
                            p.resolve();
                        } else if (response.data.add_num === 0) {
                            MY_API.chatLog(`[自动应援团签到]应援团${obj.group_name}已签到`, 'success');
                            p.resolve();
                        } else {
                            MY_API.chatLog(`[自动应援团签到]应援团${obj.group_name}错误亲密度${response.data.add_num}`, 'error');
                            p.reject();
                        }
                    } else {
                        MY_API.chatLog(`[自动应援团签到]'${response.msg}`, 'warning');
                        return p.reject();
                    }
                    return $.when(MY_API.GroupSign.signInList(list, i + 1), p);
                }, () => {
                    MY_API.chatLog(`[自动应援团签到]应援团${obj.group_name}签到失败,请检查网络`, 'error');
                    return delayCall(() => MY_API.GroupSign.signInList(list, i));
                });
            },
            run: () => {//执行应援团任务
                try {
                    if (!MY_API.CONFIG.AUTO_GROUP_SIGN) return $.Deferred().resolve();
                    if (!checkNewDay(MY_API.GIFT_COUNT.AUTO_GROUP_SIGH_TS)) {
                        console.log(`[${NAME}]应援团无需签到`);
                        return $.Deferred().resolve();
                    }
                    return MY_API.GroupSign.getGroups().then((list) => {
                        console.log(`[${NAME}]应援团列表获取成功${list}`);
                        return MY_API.GroupSign.signInList(list).then(() => {
                            MY_API.GIFT_COUNT.AUTO_GROUP_SIGH_TS = ts_ms();
                            MY_API.saveGiftCount();
                            console.log(`[${NAME}]应援团签到完成`);
                            return $.Deferred().resolve();
                        }, () => delayCall(() => MY_API.GroupSign.run()));
                    }, () => delayCall(() => MY_API.GroupSign.run()));
                } catch (err) {
                    MY_API.chatLog('[自动应援团签到]运行时出现异常,已停止', 'error');
                    console.error(`[${NAME}]`, err);
                    return $.Deferred().reject();
                }
            }
        },
    };

    SmallHeart = {
        openRoom: [],//记录以及打开的直播间
        timer: undefined,
        run() {
            if (SmallHeart.timer) {
                clearInterval(SmallHeart.timer);
            }
            SmallHeart.timer = setInterval(() => {//每隔60s检查开播勋章直播间
                if (MY_API.GIFT_COUNT.COUNT >= 24) {
                    console.log(`[${NAME}]今日小心心已满`);
                    SmallHeart.stop();
                    return
                }
                SmallHeart.checkMedalRoom();
                SmallHeart.checkSmallHeartCount();
            }, 60e3);
            SmallHeart.checkMedalRoom();
        },
        stop() {
            layer.closeAll();
            clearInterval(SmallHeart.timer);
        },
        checkSmallHeartCount: async () => {
            let bagResult = await BAPI.gift.bag_list();
            if (bagResult.code === 0) {
                let count = 0;
                for (let i of bagResult.data.list) {
                    if (i.corner_mark === '7天' && i.gift_name === '小心心') {
                        count += parseInt(i.gift_num);
                    }
                }
                console.log(`[${NAME}]今日小心心数量:${count}`);
                MY_API.GIFT_COUNT.COUNT = count;
                MY_API.saveGiftCount();
                $('#giftCount span:eq(0)').text(count);
                if (count === 24) {
                    MY_API.chatLog(`今日小心心已满,停止挂小心心`);
                    SmallHeart.stop();
                }
            } else {
                console.log(`[${NAME}]检查心心数量,获取背包礼物失败`)
            }
        },
        checkMedalRoom: async () => {
            if (MY_API.GIFT_COUNT.COUNT >= 24) return;
            let result = await SmallHeart.getMedalList();
            if (result.code === 0) {
                for (let i of result.data) {
                    if (i.live_stream_status === 1) {
                        let roomId = i.room_id;
                        if (!SmallHeart.openRoom.includes(roomId)) {
                            SmallHeart.openWin(roomId);
                        } else {
                            console.log(`[${NAME}]直播间${roomId}已打开`)
                        }
                    }
                }
            } else {
                console.log(`[${NAME}]获取勋章列表失败`)
            }
        },
        openWin(roomId) {
            if (MY_API.GIFT_COUNT.COUNT >= 24) return;
            let rid = roomId;
            let index;
            if (SmallHeart.openRoom.length > 3) {
                console.log(`[${NAME}]最大打开4个直播间`);
                return
            }
            if (roomId === 6498960) return;
            SmallHeart.openRoom.push(roomId);
            MY_API.chatLog(`开始在直播间${roomId}挂小心心`);
            let checkTimer = setInterval(async () => {
                let result = await SmallHeart.getRoomInfo(roomId);
                if (result.code === 0) {
                    if (result.data.room_info.live_status === 0) {
                        layer.close(index);
                        console.log(`[${NAME}]直播间${rid}没有开播 关闭窗口`);
                        MY_API.chatLog(`直播间${roomId}未开播,停止挂小心心`);
                    } else {
                        console.log(`[${NAME}]直播间${rid}为开播状态`)
                    }
                } else {
                    console.log(`[${NAME}]直播间${rid}状态获取出错`)
                }
            }, 60e3);

            index = layer.open({
                title: `${rid}`,
                type: 2,
                shade: 0,
                content: `https://live.bilibili.com/${rid}?flashMod`,
                end: () => {
                    clearInterval(checkTimer);
                    SmallHeart.openRoom.remove(roomId);
                }
            });
            layer.style(index, { // 隐藏弹窗
                display: 'none',
            });
        },
        getMedalList() {
            return SmallHeart.xhrRequest(
                'https://api.live.bilibili.com/fans_medal/v1/FansMedal/get_list_in_room',
                'GET');
        },
        getRoomInfo(roomId) {
            return SmallHeart.xhrRequest(
                `https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=${roomId}`,
                'GET'
            )
        },
        xhrRequest(url, method, data) {
            let p = $.Deferred();
            $.ajax({
                url: url,
                method: method,
                data: data,
                success: function (result) {
                    p.resolve(result);
                },
                error: function (result) {
                    p.reject(result);
                },
                crossDomain: true,
                dataType: 'json',
                xhrFields: {
                    withCredentials: true,
                },
            });
            return p
        },
    };

    const runUntilSucceed = (callback, delay = 0, period = 100) => {
        setTimeout(() => {
            if (!callback()) runUntilSucceed(callback, period, period);
        }, delay);
    };
    const ts_s = () => Math.round(ts_ms() / 1000);
    const ts_ms = () => Date.now();
    const delayCall = (callback, delay = 10e3) => {
        const p = $.Deferred();
        setTimeout(() => {
            const t = callback();
            if (t && t.then) t.then((arg1, arg2, arg3, arg4, arg5, arg6) => p.resolve(arg1, arg2, arg3, arg4, arg5, arg6));
            else p.resolve();
        }, delay);
        return p;
    };

    /**
     * 引用SeaLoong的Bilibili-LRHH
     */
    TreasureBox = {
        timer: undefined,
        time_end: undefined,
        time_start: undefined,
        promise: {
            calc: undefined,
            timer: undefined
        },
        DOM: {
            image: undefined,
            canvas: undefined,
            div_tip: undefined,
            div_timer: undefined,
            tp: undefined,
        },
        init: () => {
            if (!MY_API.CONFIG.AUTO_TREASUREBOX) return $.Deferred().resolve();
            const p = $.Deferred();
            runUntilSucceed(() => {
                try {
                    let treasure_box = $('#gift-control-vm div.treasure-box.p-relative');
                    if (!treasure_box.length) return false;
                    treasure_box = treasure_box.first();
                    treasure_box.attr('id', 'old_treasure_box');
                    treasure_box.hide();

                    const div = $(`<div id="${NAME}_treasure_div" class="treasure-box p-relative"  style="min-width: 46px;display: inline-block;float: left;padding: 0px 0 0 15px;"></div>`);
                    TreasureBox.DOM.div_tip = $(`<div id="${NAME}_treasure_div_tip" class="t-center b-box none-select">领取中</div>`);
                    TreasureBox.DOM.tp = $(`<img src="https://i0.hdslb.com/bfs/article/d40ff17d533047cbb9b2bed4feb927cb0e71901c.gif" height="60" width="60" >`);
                    TreasureBox.DOM.div_timer = $(`<div id="${NAME}_treasure_div_timer" class="t-center b-box none-select">0</div>`);
                    TreasureBox.DOM.image = $(`<img id="${NAME}_treasure_image" style="display:none">`);
                    TreasureBox.DOM.canvas = $(`<canvas id="${NAME}_treasure_canvas" style="display:none" height="40" width="120"></canvas>`);
                    const css_text = 'min-width: 40px;padding: 2px 3px;margin-top: 3px;font-size: 12px;color: #23ade6;background-color: rgba(0, 180, 255, .3);border-radius: 10px;';
                    TreasureBox.DOM.div_tip[0].style = css_text;
                    TreasureBox.DOM.div_timer[0].style = css_text;
                    div.append(TreasureBox.DOM.tp);
                    div.append(TreasureBox.DOM.div_tip);
                    div.append(TreasureBox.DOM.image);
                    div.append(TreasureBox.DOM.canvas);
                    TreasureBox.DOM.div_tip.after(TreasureBox.DOM.div_timer);
                    treasure_box.after(div);
                    if (!Info.mobile_verify) {
                        TreasureBox.setMsg('未绑定<br>手机');
                        MY_API.chatLog('[自动领取瓜子]未绑定手机,已停止');
                        p.resolve();
                        return true;
                    }
                    try {
                        if (OCRAD) ;
                    } catch (err) {
                        TreasureBox.setMsg('初始化<br>失败');
                        MY_API.chatLog('[自动领取瓜子]OCRAD初始化失败,请检查网络更换OCRAD源', 'error');
                        console.error(`[${NAME}]`, err);
                        p.resolve();
                        return true;
                    }
                    TreasureBox.timer = setInterval(() => {
                        let t = parseInt(TreasureBox.DOM.div_timer.text(), 10);
                        if (isNaN(t)) t = 0;
                        if (t > 0) TreasureBox.DOM.div_timer.text(`${t - 1}s`);
                        else TreasureBox.DOM.div_timer.hide();
                    }, 1e3);
                    TreasureBox.DOM.image[0].onload = () => {
                        // 实现功能类似 https://github.com/zacyu/bilibili-helper/blob/master/src/bilibili_live.js 中Live.treasure.init()的验证码处理部分
                        const ctx = TreasureBox.DOM.canvas[0].getContext('2d');
                        ctx.font = '40px agencyfbbold';
                        ctx.textBaseline = 'top';
                        ctx.clearRect(0, 0, TreasureBox.DOM.canvas[0].width, TreasureBox.DOM.canvas[0].height);
                        ctx.drawImage(TreasureBox.DOM.image[0], 0, 0);
                        const grayscaleMap = TreasureBox.captcha.OCR.getGrayscaleMap(ctx);
                        const filterMap = TreasureBox.captcha.OCR.orderFilter2In3x3(grayscaleMap);
                        ctx.clearRect(0, 0, 120, 40);
                        for (let i = 0; i < filterMap.length; ++i) {
                            const gray = filterMap[i];
                            ctx.fillStyle = `rgb(${gray}, ${gray}, ${gray})`;
                            ctx.fillRect(i % 120, Math.round(i / 120), 1, 1);
                        }
                        try {
                            const question = TreasureBox.captcha.correctQuestion(OCRAD(ctx.getImageData(0, 0, 120, 40)));
                            console.log('TreasureBox.DOM.image.load', 'question =', question);
                            const answer = TreasureBox.captcha.eval(question);
                            console.log('TreasureBox.DOM.image.load', 'answer =', answer);
                            if (answer !== undefined) {
                                // chatLog(`[自动领取瓜子]验证码识别结果: ${question} = ${answer}`, 'info');
                                console.info(`[${NAME}][自动领取瓜子]验证码识别结果: ${question} = ${answer}`);
                                TreasureBox.promise.calc.resolve(answer);
                            }
                        } catch (err) {
                            TreasureBox.promise.calc.reject();
                        }
                    };
                    p.resolve();
                    return true;
                } catch (err) {
                    MY_API.chatLog('[自动领取瓜子]初始化时出现异常,已停止', 'error');
                    console.error(`[${NAME}]`, err);
                    p.reject();
                    return true;
                }
            });
            return p;
        },
        run: () => {
            try {
                if (!MY_API.CONFIG.AUTO_TREASUREBOX || !TreasureBox.timer) return;
                if (Info.awardBlocked) {
                    TreasureBox.setMsg('瓜子小黑屋');
                    MY_API.chatLog('[自动领取瓜子]可能被关瓜子小黑屋,停止领取瓜子');
                    return;
                }
                //     if (CACHE.treasure_box_ts && !checkNewDay(CACHE.treasure_box_ts)) {
                //         TreasureBox.setMsg('今日<br>已领完');
                //         runTomorrow(TreasureBox.run);
                //         return;
                //     }
                TreasureBox.getCurrentTask().then((response) => {
                    console.log('TreasureBox.run: TreasureBox.getCurrentTask().then', response);
                    if (response.code === 0) {
                        // 获取任务成功
                        TreasureBox.promise.timer = $.Deferred();
                        TreasureBox.promise.timer.then(() => {
                            TreasureBox.captcha.calc().then((captcha) => {
                                // 验证码识别完成
                                TreasureBox.getAward(captcha).then(() => TreasureBox.run(), () => TreasureBox.run());
                            }, () => TreasureBox.run());
                        });
                        TreasureBox.time_end = response.data.time_end;
                        TreasureBox.time_start = response.data.time_start;
                        let t = TreasureBox.time_end - ts_s() + 1;
                        if (t < 0) t = 0;
                        setTimeout(() => {
                            if (TreasureBox.promise.timer) TreasureBox.promise.timer.resolve();
                        }, t * 1e3);
                        TreasureBox.DOM.div_timer.text(`${t}s`);
                        TreasureBox.DOM.div_timer.show();
                        TreasureBox.DOM.div_tip.html(`嗑瓜子`);
                    } else if (response.code === -10017) {
                        // 今天所有的宝箱已经领完!
                        TreasureBox.setMsg('今日<br>已领完');
                        MY_API.chatLog(`[自动领取瓜子]${response.msg}`, 'info');
                        // CACHE.treasure_box_ts = ts_ms();
                        // Essential.Cache.save();
                        // runTomorrow(TreasureBox.run);
                    } else if (response.code === -500) {
                        // 请先登录!
                        MY_API.chatLog(`[自动领取瓜子]请先登录!`);
                    } else {
                        MY_API.chatLog(`[自动领取瓜子]${response.msg}`);
                        return TreasureBox.run();
                    }
                });
            } catch (err) {
                TreasureBox.setMsg('运行<br>异常');
                MY_API.chatLog('[自动领取瓜子]运行时出现异常,已停止', 'error');
                console.error(`[${NAME}]`, err);
            }
        },
        setMsg: (htmltext) => {
            if (!MY_API.CONFIG.AUTO_TREASUREBOX) return;
            if (TreasureBox.promise.timer) {
                TreasureBox.promise.timer.reject();
                TreasureBox.promise.timer = undefined;
            }
            if (TreasureBox.DOM.div_timer) TreasureBox.DOM.div_timer.hide();
            if (TreasureBox.DOM.div_tip) TreasureBox.DOM.div_tip.html(htmltext);
        },
        getAward: (captcha, cnt = 0) => {
            if (!MY_API.CONFIG.AUTO_TREASUREBOX) return $.Deferred().reject();
            if (cnt > 3) return $.Deferred().resolve(); // 3次时间未到,重新运行任务
            return BAPI.TreasureBox.getAward(TreasureBox.time_start, TreasureBox.time_end, captcha).then((response) => {
                console.log('TreasureBox.getAward: getAward', response);
                switch (response.code) {
                    case 0:
                        MY_API.chatLog(`[自动领取瓜子]领取了 ${response.data.awardSilver} 银瓜子`, 'success');
                        MY_API.addLove(response.data.awardSilver);
                        return $.Deferred().resolve();
                    case -903: // -903: 已经领取过这个宝箱
                        // MY_API.chatLog('[自动领取瓜子]已经领取过这个宝箱');
                        return $.Deferred().resolve();
                    case -902: // -902: 验证码错误
                    case -901: // -901: 验证码过期
                        return TreasureBox.captcha.calc().then((captcha) => {
                            return TreasureBox.getAward(captcha, cnt);
                        });
                    case -800: // -800:未绑定手机
                        TreasureBox.setMsg('未绑定<br>手机');
                        MY_API.chatLog('[自动领取瓜子]未绑定手机,已停止');
                        return $.Deferred().reject();
                    case -500: // -500:领取时间未到, 请稍后再试
                        const p = $.Deferred();
                        setTimeout(() => {
                            TreasureBox.captcha.calc().then((captcha) => {
                                TreasureBox.getAward(captcha, cnt + 1).then(() => p.resolve(), () => p.reject());
                            }, () => p.reject());
                        }, 3e3);
                        return p;
                    case 400: // 400: 访问被拒绝
                        if (response.msg.indexOf('拒绝') > -1) {
                            Info.awardBlocked = true;
                            // Essential.DataSync.down();
                            TreasureBox.setMsg('拒绝<br>访问');
                            MY_API.chatLog('[自动领取瓜子]访问被拒绝,可能已经被关瓜子小黑屋,已停止', 'error');
                            return $.Deferred().reject();
                        }
                        MY_API.chatLog(`[自动领取瓜子]${response.msg}`);
                        return $.Deferred().resolve();
                    default: // 其他错误
                        MY_API.chatLog(`[自动领取瓜子]${response.msg}`);
                }
            }, () => {
                MY_API.chatLog('[自动领取瓜子]获取任务失败,请检查网络', 'error');
                return delayCall(() => TreasureBox.getAward(captcha, cnt));
            });
        },
        getCurrentTask: () => {
            if (!MY_API.CONFIG.AUTO_TREASUREBOX) return $.Deferred().reject();
            return BAPI.TreasureBox.getCurrentTask().then((response) => {
                console.log('TreasureBox.getCurrentTask: API.TreasureBox.getCurrentTask', response);
                return $.Deferred().resolve(response);
            }, () => {
                MY_API.chatLog('[自动领取瓜子]获取当前任务失败,请检查网络', 'error');
                return delayCall(() => TreasureBox.getCurrentTask());
            });
        },
        captcha: {
            cnt: 0,
            calc: () => {
                if (!MY_API.CONFIG.AUTO_TREASUREBOX) {
                    TreasureBox.captcha.cnt = 0;
                    return $.Deferred().reject();
                }
                if (TreasureBox.captcha.cnt > 100) { // 允许验证码无法识别的次数
                    // 验证码识别失败
                    TreasureBox.setMsg('验证码<br>识别<br>失败');
                    MY_API.chatLog('[自动领取瓜子]验证码识别失败,已停止', 'error');
                    return $.Deferred().reject();
                }
                return BAPI.TreasureBox.getCaptcha(ts_ms()).then((response) => {
                    console.log('TreasureBox.captcha.calc: getCaptcha', response);
                    if (response.code === 0) {
                        TreasureBox.captcha.cnt++;
                        const p = $.Deferred();
                        TreasureBox.promise.calc = $.Deferred();
                        TreasureBox.promise.calc.then((captcha) => {
                            TreasureBox.captcha.cnt = 0;
                            p.resolve(captcha);
                        }, () => {
                            TreasureBox.captcha.calc().then((captcha) => {
                                p.resolve(captcha);
                            }, () => {
                                p.reject();
                            });
                        });
                        TreasureBox.DOM.image.attr('src', response.data.img);
                        return p;
                    } else {
                        MY_API.chatLog(`[自动领取瓜子]${response.msg}`);
                        return delayCall(() => TreasureBox.captcha.calc());
                    }
                }, () => {
                    MY_API.chatLog('[自动领取瓜子]加载验证码失败,请检查网络', 'error');
                    return delayCall(() => TreasureBox.captcha.calc());
                });
            },
            // 对B站验证码进行处理
            // 代码来源:https://github.com/zacyu/bilibili-helper/blob/master/src/bilibili_live.js
            // 删除了未使用的变量
            OCR: {
                getGrayscaleMap: (context, rate = 235, width = 120, height = 40) => {
                    function getGrayscale(x, y) {
                        const pixel = context.getImageData(x, y, 1, 1).data;
                        return pixel ? (77 * pixel[0] + 150 * pixel[1] + 29 * pixel[2] + 128) >> 8 : 0;
                    }

                    const map = [];
                    for (let y = 0; y < height; y++) { // line y
                        for (let x = 0; x < width; x++) { // column x
                            const gray = getGrayscale(x, y);
                            map.push(gray > rate ? gray : 0);
                        }
                    }
                    return map;
                },
                orderFilter2In3x3: (grayscaleMap, n = 9, width = 120) => {
                    const gray = (x, y) => (x + y * width >= 0) ? grayscaleMap[x + y * width] : 255;
                    const map = [];
                    const length = grayscaleMap.length;
                    const catchNumber = n - 1;
                    for (let i = 0; i < length; ++i) {
                        const [x, y] = [i % width, Math.floor(i / width)];
                        const matrix = new Array(9);
                        matrix[0] = gray(x - 1, y - 1);
                        matrix[1] = gray(x + 0, y - 1);
                        matrix[2] = gray(x + 1, y - 1);
                        matrix[3] = gray(x - 1, y + 0);
                        matrix[4] = gray(x + 0, y + 0);
                        matrix[5] = gray(x + 1, y + 0);
                        matrix[6] = gray(x - 1, y + 1);
                        matrix[7] = gray(x + 0, y + 1);
                        matrix[8] = gray(x + 1, y + 1);
                        matrix.sort((a, b) => a - b);
                        map.push(matrix[catchNumber]);
                    }
                    return map;
                },
                execMap: (connectMap, rate = 4) => {
                    const map = [];
                    const connectMapLength = connectMap.length;
                    for (let i = 0; i < connectMapLength; ++i) {
                        let blackPoint = 0;
                        // const [x, y] = [i % 120, Math.round(i / 120)];
                        const top = connectMap[i - 120];
                        const topLeft = connectMap[i - 120 - 1];
                        const topRight = connectMap[i - 120 + 1];
                        const left = connectMap[i - 1];
                        const right = connectMap[i + 1];
                        const bottom = connectMap[i + 120];
                        const bottomLeft = connectMap[i + 120 - 1];
                        const bottomRight = connectMap[i + 120 + 1];
                        if (top) blackPoint += 1;
                        if (topLeft) blackPoint += 1;
                        if (topRight) blackPoint += 1;
                        if (left) blackPoint += 1;
                        if (right) blackPoint += 1;
                        if (bottom) blackPoint += 1;
                        if (bottomLeft) blackPoint += 1;
                        if (bottomRight) blackPoint += 1;
                        if (blackPoint > rate) map.push(1);
                        else map.push(0);
                    }
                    return map;
                }
            },
            eval: (fn) => {
                let Fn = Function;
                return new Fn(`return ${fn}`)();
            },
            // 修正OCRAD识别结果
            // 代码来源:https://github.com/zacyu/bilibili-helper/blob/master/src/bilibili_live.js
            // 修改部分:
            // 1.将correctStr声明在correctQuestion函数内部,并修改相关引用
            // 2.在correctStr中增加'>': 3
            correctStr: {
                'g': 9,
                'z': 2,
                'Z': 2,
                'o': 0,
                'l': 1,
                'B': 8,
                'O': 0,
                'S': 6,
                's': 6,
                'i': 1,
                'I': 1,
                '.': '-',
                '_': 4,
                'b': 6,
                'R': 8,
                '|': 1,
                'D': 0,
                '>': 3
            },
            correctQuestion: (question) => {
                let q = '';
                question = question.trim();
                for (let i in question) {
                    let a = TreasureBox.captcha.correctStr[question[i]];
                    q += (a !== undefined ? a : question[i]);
                }
                if (q[2] === '4') q[2] = '+';
                return q;
            }
        }
    }; // Constantly Run, Need Init


    MY_API.init().then(function () {
        if (Info.uid === 0 || isNaN(Info.uid)) {
            MY_API.chatLog('未登录,请先登录再使用脚本', 'warning');
            return
        }
        console.log(MY_API.CONFIG);
        StartPlunder(MY_API);
    });
}

function StartPlunder() {
    'use strict';
    let flashMod = window.location.href.indexOf('flashMod') > -1;
    if (flashMod) {
        setTimeout(() => {
            $('[data-title="Flash播放器"]').click();
            setTimeout(() => {
                window.localStorage["LIVE_PLAYER_STATUS"] = window.localStorage["LIVE_PLAYER_STATUS"].replace("flash", 'html5');
                window.localStorage["videoVolume"] = 0;
            }, 2e3);
        }, 4e3);
    }
    window.localStorage["LIVE_PLAYER_STATUS"] = window.localStorage["LIVE_PLAYER_STATUS"].replace("flash", 'html5');
    window.localStorage["videoVolume"] = 0;
    if (!flashMod) {
        if (MY_API.CONFIG.AUTO_TREASUREBOX) {
            const run = async () => {
                const get_room_info = BAPI.live_user.get_info_in_room(Info.roomId).then((response) => {
                    console.log('[直播间信息]', response);
                    Info.silver = response.data.wallet.silver;
                    Info.gold = response.data.wallet.gold;
                    Info.mobile_verify = response.data.info.mobile_verify;
                    Info.identification = response.data.info.identification;
                });
                await get_room_info;
                await TreasureBox.init(); //领瓜子初始化
                await TreasureBox.run();
            };
            run().then(() => {
                MY_API.chatLog('开始自动磕瓜子', 'success')
            });
        }

        let YB = () => {//判断是否换硬币
            if (MY_API.CONFIG.AUTO_EXCHANGE && checkNewDay(MY_API.GIFT_COUNT.EXCHANGE_TS)) {
                MY_API.GIFT_COUNT.EXCHANGE_TS = dateNow();
                MY_API.saveGiftCount();
                MY_API.Exchange.run();
                console.log('银瓜子换硬币')
            } else {
                console.log('无需银瓜子换硬币')
            }
        };
        YB();

        let SS = () => {//判断是否签到
            if (MY_API.CONFIG.AUTO_SIGNIN && checkNewDay(MY_API.GIFT_COUNT.SIGN_TS)) {
                MY_API.GIFT_COUNT.SIGN_TS = dateNow();
                MY_API.saveGiftCount();
                BAPI.sign.doSign().then(function (result) {
                    MY_API.chatLog(result.message);
                });
            } else {
                console.log('无需签到')
            }
        };
        SS();

        MY_API.creatSetBox();//创建设置框

        let G_Timer = () => {//判断是否清空礼物数量
            if (checkNewDay(MY_API.GIFT_COUNT.CLEAR_TS)) {
                MY_API.GIFT_COUNT.COUNT = 0;
                MY_API.GIFT_COUNT.LOVE_COUNT = 0;
                MY_API.GIFT_COUNT.CLEAR_TS = dateNow();
                MY_API.saveGiftCount();
                $('#giftCount span').text(0);
                console.log('清空礼物数量')
            } else {
                console.log('无需清空礼物数量')
            }
        };
        setInterval(G_Timer, 60e3);
        G_Timer();

        if (MY_API.CONFIG.AUTO_HEART) {
            if (MY_API.GIFT_COUNT.COUNT < 24) {
                setTimeout(SmallHeart.run, 10e3);
            } else {
                MY_API.chatLog('今日小心心已满');
            }
        }
        MY_API.share();
        MY_API.GroupSign.run();

        let reset = (delay) => {
            setTimeout(function () {//重置直播间
                window.location.reload();
            }, delay);
        };
        reset(MY_API.CONFIG.TIME_RELOAD * 60000);
    } else {
        MY_API.chatLog('脚本未在此直播间启用<br><a href="https://live.bilibili.com/6498960">点击前往</a>', 'warning');
    }
}

/**
 * (2,10) 当前是否在两点到十点之间(10,2) 当前是否在十点到次日两点之间
 * @param a 整数 起始时间
 * @param b 整数 终止时间
 * @returns {boolean}
 */
function inTimeArea(a, b) {
    a %= 24;
    b %= 24;
    if (a < 0 || b < 0 || a === b) {
        console.log('错误时间段');
        return false
    }
    let myDate = new Date();
    let h = myDate.getHours();
    if (a < b) {
        return h >= a && h < b
    } else {
        return h >= a || h < b
    }
}

/**
 * 概率
 * @param val
 * @returns {boolean}
 */
function probability(val) {
    if (val <= 0) return false;
    let rad = Math.ceil(Math.random() * 100);
    return val >= rad
}

const dateNow = () => Date.now();
/**
 * 检查是否为新一天
 * @param ts
 * @returns {boolean}
 */
const checkNewDay = (ts) => {
    if (ts === 0) return true;
    let t = new Date(ts);
    let d = new Date();
    let td = t.getDate();
    let dd = d.getDate();
    return (dd !== td);
};

/**
 * 获取cookie
 * @param name
 * @returns {string|boolean}
 */
function getCookie(name) {
    let arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
    if (arr != null) return unescape(arr[2]);
    return false;
}