BiliveHeart

在0.0.5的基础上修改了一下用于挂机经验

目前为 2022-07-16 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/447940/1070791/BiliveHeart.js

  1. // ==UserScript==
  2. // @name BiliveHeart
  3. // @namespace https://github.com/lzghzr/TampermonkeyJS
  4. // @version 0.0.5.1
  5. // @author lzghzr
  6. // @description 在0.0.5的基础上修改了一下用于挂机经验
  7. // @include /^https?:\/\/live\.bilibili\.com\/(?:blanc\/)?\d/
  8. // @require https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.js
  9. // @license MIT
  10. // @grant none
  11. // @run-at document-end
  12. // ==/UserScript==
  13. class RoomHeart {
  14. constructor(roomID, timeLimit) {
  15. this.roomID = roomID;
  16. this.timeLimit = timeLimit;
  17. }
  18. areaID;
  19. parentID;
  20. seq = 0;
  21. roomID;
  22. timeLimit;
  23. get id() {
  24. return [this.parentID, this.areaID, this.seq, this.roomID];
  25. }
  26. buvid = this.getItem('LIVE_BUVID');
  27. uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, t => {
  28. const e = 16 * Math.random() | 0;
  29. return ('x' === t ? e : 3 & e | 8).toString(16);
  30. });
  31. device = [this.buvid, this.uuid];
  32. get ts() {
  33. return Date.now();
  34. }
  35. _patchData = {}
  36. get patchData() {
  37. const list = [];
  38. for (const [_, data] of Object.entries(this._patchData))
  39. list.push(data);
  40. return list;
  41. }
  42. get isPatch() { return this.patchData.length === 0 ? 0 : 1; }
  43. W = typeof unsafeWindow === 'undefined' ? window : unsafeWindow;
  44. ua = this.W && this.W.navigator ? this.W.navigator.userAgent : '';
  45. csrf = this.getItem("bili_jct") || '';
  46. nextInterval = Math.floor(5) + Math.floor(Math.random() * (60 - 5));
  47. heartBeatInterval;
  48. secretKey;
  49. secretRule;
  50. timestamp;
  51. lastHeartbeatTimestamp = Date.now();
  52. get watchTimeFromLastReport() {
  53. const t = Math.ceil(((new Date).getTime() - this.lastHeartbeatTimestamp) / 1000);
  54. return t < 0 ? 0 : t > this.heartBeatInterval ? this.heartBeatInterval : t;
  55. }
  56. start() {
  57. return this.getInfoByRoom();
  58. }
  59. doneFunc = function () { }
  60. async getInfoByRoom() {
  61. if (this.roomID === 0)
  62. return false;
  63. const getInfoByRoom = await fetch(`//api.live.bilibili.com/room/v1/Room/get_info?room_id=${this.roomID}&from=room`, {
  64. mode: 'cors',
  65. credentials: 'include',
  66. }).then(res => res.json());
  67. if (getInfoByRoom.code === 0) {
  68. ;
  69. ({ area_id: this.areaID, parent_area_id: this.parentID, room_id: this.roomID } = getInfoByRoom.data);
  70. if (this.areaID === 0 || this.parentID === 0)
  71. return false;
  72. this.e();
  73. return true;
  74. }
  75. else {
  76. console.error(GM_info.script.name, `未获取到房间 ${this.roomID} 信息`);
  77. return false;
  78. }
  79. }
  80. async webHeartBeat() {
  81. if (this.seq > 30)
  82. return;
  83. const arg = `${this.nextInterval}|${this.roomID}|1|0`;
  84. const argUtf8 = CryptoJS.enc.Utf8.parse(arg);
  85. const argBase64 = CryptoJS.enc.Base64.stringify(argUtf8);
  86. const webHeartBeat = await fetch(`//live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat?hb=${encodeURIComponent(argBase64)}&pf=web`, {
  87. mode: 'cors',
  88. credentials: 'include',
  89. }).then(res => res.json());
  90. if (webHeartBeat.code === 0) {
  91. if (webHeartBeat.data.area_id == 0) {
  92. console.log(GM_info.script.name, `直播间 ${this.roomID} 没有填写分区,取消观看`);
  93. return true;
  94. }
  95. this.nextInterval = webHeartBeat.data.next_interval;
  96. setTimeout(() => this.webHeartBeat(), this.nextInterval * 1000);
  97. }
  98. else
  99. console.error(GM_info.script.name, `房间 ${this.roomID} 心跳失败`);
  100. }
  101. async savePatchData() {
  102. if (this.seq > 30)
  103. return;
  104. const sypderData = {
  105. id: JSON.stringify(this.id),
  106. device: JSON.stringify(this.device),
  107. ets: this.timestamp,
  108. benchmark: this.secretKey,
  109. time: this.watchTimeFromLastReport > this.heartBeatInterval ? this.heartBeatInterval : this.watchTimeFromLastReport,
  110. ts: this.ts,
  111. ua: this.ua,
  112. };
  113. const s = this.sypder(JSON.stringify(sypderData), this.secretRule);
  114. const arg = Object.assign({ s }, sypderData);
  115. this._patchData[this.roomID] = arg;
  116. setTimeout(() => this.savePatchData(), 15 * 1000);
  117. }
  118. async e() {
  119. const arg = {
  120. id: JSON.stringify(this.id),
  121. device: JSON.stringify(this.device),
  122. ts: this.ts,
  123. is_patch: 0,
  124. heart_beat: '[]',
  125. ua: this.ua,
  126. };
  127. const e = await fetch('//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E', {
  128. headers: {
  129. "content-type": "application/x-www-form-urlencoded",
  130. },
  131. method: 'POST',
  132. body: `${this.json2str(arg)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
  133. mode: 'cors',
  134. credentials: 'include',
  135. }).then(res => res.json());
  136. if (e.code === 0) {
  137. this.seq += 1;
  138. ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = e.data);
  139. setTimeout(() => this.x(), this.heartBeatInterval * 1000);
  140. }
  141. else
  142. console.error(GM_info.script.name, `房间 ${this.roomID} 获取小心心失败`);
  143. }
  144. async x() {
  145. if (this.seq > this.timeLimit)
  146. return this.doneFunc();
  147. const sypderData = {
  148. id: JSON.stringify(this.id),
  149. device: JSON.stringify(this.device),
  150. ets: this.timestamp,
  151. benchmark: this.secretKey,
  152. time: this.heartBeatInterval,
  153. ts: this.ts,
  154. ua: this.ua,
  155. };
  156. const s = this.sypder(JSON.stringify(sypderData), this.secretRule);
  157. const arg = Object.assign({ s }, sypderData);
  158. this._patchData[this.roomID] = arg;
  159. this.lastHeartbeatTimestamp = Date.now();
  160. const x = await fetch('//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X', {
  161. headers: {
  162. "content-type": "application/x-www-form-urlencoded",
  163. },
  164. method: 'POST',
  165. body: `${this.json2str(arg)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
  166. mode: 'cors',
  167. credentials: 'include',
  168. }).then(res => res.json());
  169. if (x.code === 0) {
  170. this.seq += 1;
  171. ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = x.data);
  172. setTimeout(() => this.x(), this.heartBeatInterval * 1000);
  173. }
  174. else
  175. console.error(GM_info.script.name, `房间 ${this.roomID} 小心心 心跳失败`);
  176. }
  177. sypder(str, rule) {
  178. const data = JSON.parse(str);
  179. const [parent_id, area_id, seq_id, room_id] = JSON.parse(data.id);
  180. const [buvid, uuid] = JSON.parse(data.device);
  181. const key = data.benchmark;
  182. const newData = {
  183. platform: 'web',
  184. parent_id,
  185. area_id,
  186. seq_id,
  187. room_id,
  188. buvid,
  189. uuid,
  190. ets: data.ets,
  191. time: data.time,
  192. ts: data.ts,
  193. };
  194. let s = JSON.stringify(newData);
  195. for (const r of rule) {
  196. switch (r) {
  197. case 0:
  198. s = CryptoJS.HmacMD5(s, key).toString(CryptoJS.enc.Hex);
  199. break;
  200. case 1:
  201. s = CryptoJS.HmacSHA1(s, key).toString(CryptoJS.enc.Hex);
  202. break;
  203. case 2:
  204. s = CryptoJS.HmacSHA256(s, key).toString(CryptoJS.enc.Hex);
  205. break;
  206. case 3:
  207. s = CryptoJS.HmacSHA224(s, key).toString(CryptoJS.enc.Hex);
  208. break;
  209. case 4:
  210. s = CryptoJS.HmacSHA512(s, key).toString(CryptoJS.enc.Hex);
  211. break;
  212. case 5:
  213. s = CryptoJS.HmacSHA384(s, key).toString(CryptoJS.enc.Hex);
  214. break;
  215. default:
  216. break;
  217. }
  218. }
  219. return s;
  220. }
  221. getItem(t) {
  222. return decodeURIComponent(document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(t).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || '';
  223. }
  224. json2str(arg) {
  225. let str = '';
  226. for (const name in arg)
  227. str += `${name}=${encodeURIComponent(arg[name])}&`;
  228. return str.slice(0, -1);
  229. }
  230. };

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址