BiliveHeartWithTimeParam

无分区的直播间不进行挂机

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/447940/1096591/BiliveHeartWithTimeParam.js

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        BiliveHeart
// @namespace   https://github.com/lzghzr/TampermonkeyJS
// @version     0.0.5.4
// @author      lzghzr
// @description 在0.0.5的基础上修改了一下用于挂机经验
// @include     /^https?:\/\/live\.bilibili\.com\/(?:blanc\/)?\d/
// @require     https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/crypto-js/4.1.1/crypto-js.min.js
// @license     MIT
// @grant       none
// @run-at      document-end
// ==/UserScript==
class RoomHeart {
    constructor(roomID, timeLimit = 70) {
        this.roomID = roomID;
        this.timeLimit = timeLimit;
    }
    areaID;
    parentID;
    seq = 0;
    roomID;
    timeLimit;
    get id() {
        return [this.parentID, this.areaID, this.seq, this.roomID];
    }
    buvid = this.getItem('LIVE_BUVID');
    uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, t => {
        const e = 16 * Math.random() | 0;
        return ('x' === t ? e : 3 & e | 8).toString(16);
    });
    device = [this.buvid, this.uuid];
    get ts() {
        return Date.now();
    }
    _patchData = {}
    get patchData() {
        const list = [];
        for (const [_, data] of Object.entries(this._patchData))
            list.push(data);
        return list;
    }
    get isPatch() { return this.patchData.length === 0 ? 0 : 1; }
    W = typeof unsafeWindow === 'undefined' ? window : unsafeWindow;
    ua = this.W && this.W.navigator ? this.W.navigator.userAgent : '';
    csrf = this.getItem("bili_jct") || '';
    nextInterval = Math.floor(5) + Math.floor(Math.random() * (60 - 5));
    heartBeatInterval;
    secretKey;
    secretRule;
    timestamp;
    lastHeartbeatTimestamp = Date.now();
    get watchTimeFromLastReport() {
        const t = Math.ceil(((new Date).getTime() - this.lastHeartbeatTimestamp) / 1000);
        return t < 0 ? 0 : t > this.heartBeatInterval ? this.heartBeatInterval : t;
    }
    start() {
        return this.getInfoByRoom();
    }
    doneFunc = function () { }
    errorFunc = function () { }
    async getInfoByRoom() {
        if (this.roomID === 0)
            return false;
        const getInfoByRoom = await fetch(`//api.live.bilibili.com/room/v1/Room/get_info?room_id=${this.roomID}&from=room`, {
            mode: 'cors',
            credentials: 'include',
        }).then(res => res.json());
        if (getInfoByRoom.code === 0) {
            ;
            ({ area_id: this.areaID, parent_area_id: this.parentID, room_id: this.roomID } = getInfoByRoom.data);
            if (this.areaID === 0 || this.parentID === 0)
                return false;
            this.e();
            return true;
        }
        else {
            console.error(GM_info.script.name, `未获取到房间 ${this.roomID} 信息`);
            return false;
        }
    }
    async webHeartBeat() {
        if (this.seq > 30)
            return;
        const arg = `${this.nextInterval}|${this.roomID}|1|0`;
        const argUtf8 = CryptoJS.enc.Utf8.parse(arg);
        const argBase64 = CryptoJS.enc.Base64.stringify(argUtf8);
        const webHeartBeat = await fetch(`//live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat?hb=${encodeURIComponent(argBase64)}&pf=web`, {
            mode: 'cors',
            credentials: 'include',
        }).then(res => res.json());
        if (webHeartBeat.code === 0) {
            this.nextInterval = webHeartBeat.data.next_interval;
            setTimeout(() => this.webHeartBeat(), this.nextInterval * 1000);
        }
        else
            console.error(GM_info.script.name, `房间 ${this.roomID} 心跳失败`);
    }
    async savePatchData() {
        if (this.seq > 30)
            return;
        const sypderData = {
            id: JSON.stringify(this.id),
            device: JSON.stringify(this.device),
            ets: this.timestamp,
            benchmark: this.secretKey,
            time: this.watchTimeFromLastReport > this.heartBeatInterval ? this.heartBeatInterval : this.watchTimeFromLastReport,
            ts: this.ts,
            ua: this.ua,
        };
        const s = this.sypder(JSON.stringify(sypderData), this.secretRule);
        const arg = Object.assign({ s }, sypderData);
        this._patchData[this.roomID] = arg;
        setTimeout(() => this.savePatchData(), 15 * 1000);
    }
    async e() {
        const arg = {
            id: JSON.stringify(this.id),
            device: JSON.stringify(this.device),
            ts: this.ts,
            is_patch: 0,
            heart_beat: '[]',
            ua: this.ua,
        };
        const e = await fetch('//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E', {
            headers: {
                "content-type": "application/x-www-form-urlencoded",
            },
            method: 'POST',
            body: `${this.json2str(arg)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
            mode: 'cors',
            credentials: 'include',
        }).then(res => res.json());
        if (e.code === 0) {
            this.seq += 1;
            ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = e.data);
            setTimeout(() => {
                try {
                    this.x();
                } catch (error) {
                    this.errorFunc(error);
                    throw error;
                }
            }, this.heartBeatInterval * 1000);
        }
        else
            console.error(GM_info.script.name, `房间 ${this.roomID} 获取小心心失败`);
    }
    async x() {
        if (this.seq > this.timeLimit)
            return this.doneFunc();
        const sypderData = {
            id: JSON.stringify(this.id),
            device: JSON.stringify(this.device),
            ets: this.timestamp,
            benchmark: this.secretKey,
            time: this.heartBeatInterval,
            ts: this.ts,
            ua: this.ua,
        };
        const s = this.sypder(JSON.stringify(sypderData), this.secretRule);
        const arg = Object.assign({ s }, sypderData);
        this._patchData[this.roomID] = arg;
        this.lastHeartbeatTimestamp = Date.now();
        const x = await fetch('//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X', {
            headers: {
                "content-type": "application/x-www-form-urlencoded",
            },
            method: 'POST',
            body: `${this.json2str(arg)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
            mode: 'cors',
            credentials: 'include',
        }).then(res => res.json());
        if (x.code === 0) {
            this.seq += 1;
            ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = x.data);
            setTimeout(() => this.x(), this.heartBeatInterval * 1000);
        } else {
            console.error(GM_info.script.name, `房间 ${this.roomID} 小心心 心跳失败`);
            this.errorFunc(x);
        }
    }
    sypder(str, rule) {
        const data = JSON.parse(str);
        const [parent_id, area_id, seq_id, room_id] = JSON.parse(data.id);
        const [buvid, uuid] = JSON.parse(data.device);
        const key = data.benchmark;
        const newData = {
            platform: 'web',
            parent_id,
            area_id,
            seq_id,
            room_id,
            buvid,
            uuid,
            ets: data.ets,
            time: data.time,
            ts: data.ts,
        };
        let s = JSON.stringify(newData);
        for (const r of rule) {
            switch (r) {
                case 0:
                    s = CryptoJS.HmacMD5(s, key).toString(CryptoJS.enc.Hex);
                    break;
                case 1:
                    s = CryptoJS.HmacSHA1(s, key).toString(CryptoJS.enc.Hex);
                    break;
                case 2:
                    s = CryptoJS.HmacSHA256(s, key).toString(CryptoJS.enc.Hex);
                    break;
                case 3:
                    s = CryptoJS.HmacSHA224(s, key).toString(CryptoJS.enc.Hex);
                    break;
                case 4:
                    s = CryptoJS.HmacSHA512(s, key).toString(CryptoJS.enc.Hex);
                    break;
                case 5:
                    s = CryptoJS.HmacSHA384(s, key).toString(CryptoJS.enc.Hex);
                    break;
                default:
                    break;
            }
        }
        return s;
    }
    getItem(t) {
        return decodeURIComponent(document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(t).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || '';
    }
    json2str(arg) {
        let str = '';
        for (const name in arg)
            str += `${name}=${encodeURIComponent(arg[name])}&`;
        return str.slice(0, -1);
    }
};