- // ==UserScript==
- // @name BiliveHeart
- // @namespace https://github.com/lzghzr/TampermonkeyJS
- // @version 0.0.7
- // @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,r) {
- this.roomID = t,
- this.Dotime = g,
- this.ruid = r
- }
- 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),
- ruid:this.ruid,
- 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),
- ruid:this.ruid,
- 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),
- ruid:this.ruid,
- 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)
- }
- }