您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
B站直播心跳
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/447321/1071099/BiliveHeart.js
// ==UserScript== // @name BiliveHeart // @namespace https://github.com/lzghzr/TampermonkeyJS // @version 0.0.6 // @author lzghzr // @description B站直播心跳 // @include /^https?:\/\/live\.bilibili\.com\/(?:blanc\/)?\d/ // @require https://greasyfork.org/scripts/441505-crypto-js4-1-1/code/crypto-js411.js?version=1028182 // @license MIT // @grant none // ==/UserScript== class RoomHeart { constructor(t,g) { this.roomID = t, this.Dotime = g } areaID; parentID; seq = 0; roomID; Dotime; 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 t = []; for (const[e, i]of Object.entries(this._patchData)) t.push(i); return t } get isPatch() { return 0 === this.patchData.length ? 0 : 1 } W = "undefined" == typeof unsafeWindow ? window : unsafeWindow; ua = this.W && this.W.navigator ? this.W.navigator.userAgent : ""; csrf = this.getItem("bili_jct") || ""; nextInterval = Math.floor(5) + Math.floor(55 * Math.random()); heartBeatInterval; secretKey; secretRule; timestamp; lastHeartbeatTimestamp = Date.now(); get watchTimeFromLastReport() { const t = Math.ceil(((new Date).getTime() - this.lastHeartbeatTimestamp) / 1e3); return t < 0 ? 0 : t > this.heartBeatInterval ? this.heartBeatInterval : t } start() { return this.getInfoByRoom() } doneFunc = function () {}; async getInfoByRoom() { if (0 === this.roomID) return !1; const t = await fetch(`//api.live.bilibili.com/room/v1/Room/get_info?room_id=${this.roomID}&from=room`, { mode: "cors", credentials: "include" }).then((t => t.json())); return 0 === t.code && (({ area_id: this.areaID, parent_area_id: this.parentID, room_id: this.roomID } = t.data), 0 !== this.areaID && 0 !== this.parentID && (this.e(), !0)) } async webHeartBeat() { if (this.seq > this.Dotime) return; const t = `${this.nextInterval}|${this.roomID}|1|0`, e = CryptoJS.enc.Utf8.parse(t), i = CryptoJS.enc.Base64.stringify(e), s = await fetch(`//live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat?hb=${encodeURIComponent(i)}&pf=web`, { mode: "cors", credentials: "include" }).then((t => t.json())); 0 === s.code && (this.nextInterval = s.data.next_interval, setTimeout((() => this.webHeartBeat()), 1e3 * this.nextInterval)) } async savePatchData() { if (this.seq > this.Dotime) return; const t = { 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 }, e = this.sypder(JSON.stringify(t), this.secretRule), i = Object.assign({ s: e }, t); this._patchData[this.roomID] = i, setTimeout((() => this.savePatchData()), 15e3) } async e() { const t = { id: JSON.stringify(this.id), device: JSON.stringify(this.device), ts: this.ts, is_patch: 0, heart_beat: "[]", ua: this.ua }, 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(t)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`, mode: "cors", credentials: "include" }).then((t => t.json())); 0 === e.code && (this.seq += 1, ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = e.data), setTimeout((() => this.x()), 1e3 * this.heartBeatInterval)) } async x() { if (this.seq > this.Dotime) return this.doneFunc(); const t = { 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 }, e = this.sypder(JSON.stringify(t), this.secretRule), i = Object.assign({ s: e }, t); this._patchData[this.roomID] = i, this.lastHeartbeatTimestamp = Date.now(); const s = 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(i)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`, mode: "cors", credentials: "include" }).then((t => t.json())); 0 === s.code && (this.seq += 1, ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = s.data), setTimeout((() => this.x()), 1e3 * this.heartBeatInterval)) } sypder(t, e) { const i = JSON.parse(t), [s, a, r, n] = JSON.parse(i.id), [o, c] = JSON.parse(i.device), h = i.benchmark, m = { platform: "web", parent_id: s, area_id: a, seq_id: r, room_id: n, buvid: o, uuid: c, ets: i.ets, time: i.time, ts: i.ts }; let d = JSON.stringify(m); for (const t of e) switch (t) { case 0: d = CryptoJS.HmacMD5(d, h).toString(CryptoJS.enc.Hex); break; case 1: d = CryptoJS.HmacSHA1(d, h).toString(CryptoJS.enc.Hex); break; case 2: d = CryptoJS.HmacSHA256(d, h).toString(CryptoJS.enc.Hex); break; case 3: d = CryptoJS.HmacSHA224(d, h).toString(CryptoJS.enc.Hex); break; case 4: d = CryptoJS.HmacSHA512(d, h).toString(CryptoJS.enc.Hex); break; case 5: d = CryptoJS.HmacSHA384(d, h).toString(CryptoJS.enc.Hex); break; default: break } return d } getItem(t) { return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(t).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || "" } json2str(t) { let e = ""; for (const i in t) e += `${i}=${encodeURIComponent(t[i])}&`; return e.slice(0, -1) } }