BiliveHeartWithTimeParam

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

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/447940/1096591/BiliveHeartWithTimeParam.js

  1. // ==UserScript==
  2. // @name BiliveHeart
  3. // @namespace https://github.com/lzghzr/TampermonkeyJS
  4. // @version 0.0.5.4
  5. // @author lzghzr
  6. // @description 在0.0.5的基础上修改了一下用于挂机经验
  7. // @include /^https?:\/\/live\.bilibili\.com\/(?:blanc\/)?\d/
  8. // @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/crypto-js/4.1.1/crypto-js.min.js
  9. // @license MIT
  10. // @grant none
  11. // @run-at document-end
  12. // ==/UserScript==
  13. class RoomHeart {
  14. constructor(roomID, timeLimit = 70) {
  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. errorFunc = function () { }
  61. async getInfoByRoom() {
  62. if (this.roomID === 0)
  63. return false;
  64. const getInfoByRoom = await fetch(`//api.live.bilibili.com/room/v1/Room/get_info?room_id=${this.roomID}&from=room`, {
  65. mode: 'cors',
  66. credentials: 'include',
  67. }).then(res => res.json());
  68. if (getInfoByRoom.code === 0) {
  69. ;
  70. ({ area_id: this.areaID, parent_area_id: this.parentID, room_id: this.roomID } = getInfoByRoom.data);
  71. if (this.areaID === 0 || this.parentID === 0)
  72. return false;
  73. this.e();
  74. return true;
  75. }
  76. else {
  77. console.error(GM_info.script.name, `未获取到房间 ${this.roomID} 信息`);
  78. return false;
  79. }
  80. }
  81. async webHeartBeat() {
  82. if (this.seq > 30)
  83. return;
  84. const arg = `${this.nextInterval}|${this.roomID}|1|0`;
  85. const argUtf8 = CryptoJS.enc.Utf8.parse(arg);
  86. const argBase64 = CryptoJS.enc.Base64.stringify(argUtf8);
  87. const webHeartBeat = await fetch(`//live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat?hb=${encodeURIComponent(argBase64)}&pf=web`, {
  88. mode: 'cors',
  89. credentials: 'include',
  90. }).then(res => res.json());
  91. if (webHeartBeat.code === 0) {
  92. this.nextInterval = webHeartBeat.data.next_interval;
  93. setTimeout(() => this.webHeartBeat(), this.nextInterval * 1000);
  94. }
  95. else
  96. console.error(GM_info.script.name, `房间 ${this.roomID} 心跳失败`);
  97. }
  98. async savePatchData() {
  99. if (this.seq > 30)
  100. return;
  101. const sypderData = {
  102. id: JSON.stringify(this.id),
  103. device: JSON.stringify(this.device),
  104. ets: this.timestamp,
  105. benchmark: this.secretKey,
  106. time: this.watchTimeFromLastReport > this.heartBeatInterval ? this.heartBeatInterval : this.watchTimeFromLastReport,
  107. ts: this.ts,
  108. ua: this.ua,
  109. };
  110. const s = this.sypder(JSON.stringify(sypderData), this.secretRule);
  111. const arg = Object.assign({ s }, sypderData);
  112. this._patchData[this.roomID] = arg;
  113. setTimeout(() => this.savePatchData(), 15 * 1000);
  114. }
  115. async e() {
  116. const arg = {
  117. id: JSON.stringify(this.id),
  118. device: JSON.stringify(this.device),
  119. ts: this.ts,
  120. is_patch: 0,
  121. heart_beat: '[]',
  122. ua: this.ua,
  123. };
  124. const e = await fetch('//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E', {
  125. headers: {
  126. "content-type": "application/x-www-form-urlencoded",
  127. },
  128. method: 'POST',
  129. body: `${this.json2str(arg)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
  130. mode: 'cors',
  131. credentials: 'include',
  132. }).then(res => res.json());
  133. if (e.code === 0) {
  134. this.seq += 1;
  135. ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = e.data);
  136. setTimeout(() => {
  137. try {
  138. this.x();
  139. } catch (error) {
  140. this.errorFunc(error);
  141. throw error;
  142. }
  143. }, this.heartBeatInterval * 1000);
  144. }
  145. else
  146. console.error(GM_info.script.name, `房间 ${this.roomID} 获取小心心失败`);
  147. }
  148. async x() {
  149. if (this.seq > this.timeLimit)
  150. return this.doneFunc();
  151. const sypderData = {
  152. id: JSON.stringify(this.id),
  153. device: JSON.stringify(this.device),
  154. ets: this.timestamp,
  155. benchmark: this.secretKey,
  156. time: this.heartBeatInterval,
  157. ts: this.ts,
  158. ua: this.ua,
  159. };
  160. const s = this.sypder(JSON.stringify(sypderData), this.secretRule);
  161. const arg = Object.assign({ s }, sypderData);
  162. this._patchData[this.roomID] = arg;
  163. this.lastHeartbeatTimestamp = Date.now();
  164. const x = await fetch('//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X', {
  165. headers: {
  166. "content-type": "application/x-www-form-urlencoded",
  167. },
  168. method: 'POST',
  169. body: `${this.json2str(arg)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
  170. mode: 'cors',
  171. credentials: 'include',
  172. }).then(res => res.json());
  173. if (x.code === 0) {
  174. this.seq += 1;
  175. ({ heartbeat_interval: this.heartBeatInterval, secret_key: this.secretKey, secret_rule: this.secretRule, timestamp: this.timestamp } = x.data);
  176. setTimeout(() => this.x(), this.heartBeatInterval * 1000);
  177. } else {
  178. console.error(GM_info.script.name, `房间 ${this.roomID} 小心心 心跳失败`);
  179. this.errorFunc(x);
  180. }
  181. }
  182. sypder(str, rule) {
  183. const data = JSON.parse(str);
  184. const [parent_id, area_id, seq_id, room_id] = JSON.parse(data.id);
  185. const [buvid, uuid] = JSON.parse(data.device);
  186. const key = data.benchmark;
  187. const newData = {
  188. platform: 'web',
  189. parent_id,
  190. area_id,
  191. seq_id,
  192. room_id,
  193. buvid,
  194. uuid,
  195. ets: data.ets,
  196. time: data.time,
  197. ts: data.ts,
  198. };
  199. let s = JSON.stringify(newData);
  200. for (const r of rule) {
  201. switch (r) {
  202. case 0:
  203. s = CryptoJS.HmacMD5(s, key).toString(CryptoJS.enc.Hex);
  204. break;
  205. case 1:
  206. s = CryptoJS.HmacSHA1(s, key).toString(CryptoJS.enc.Hex);
  207. break;
  208. case 2:
  209. s = CryptoJS.HmacSHA256(s, key).toString(CryptoJS.enc.Hex);
  210. break;
  211. case 3:
  212. s = CryptoJS.HmacSHA224(s, key).toString(CryptoJS.enc.Hex);
  213. break;
  214. case 4:
  215. s = CryptoJS.HmacSHA512(s, key).toString(CryptoJS.enc.Hex);
  216. break;
  217. case 5:
  218. s = CryptoJS.HmacSHA384(s, key).toString(CryptoJS.enc.Hex);
  219. break;
  220. default:
  221. break;
  222. }
  223. }
  224. return s;
  225. }
  226. getItem(t) {
  227. return decodeURIComponent(document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(t).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || '';
  228. }
  229. json2str(arg) {
  230. let str = '';
  231. for (const name in arg)
  232. str += `${name}=${encodeURIComponent(arg[name])}&`;
  233. return str.slice(0, -1);
  234. }
  235. };

QingJ © 2025

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