b-live-random-send-test

定时从设置的字幕中随机取出一条在B站直播间发送,需先登录(不可用)B站账号

当前为 2024-10-05 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/447936/1459643/b-live-random-send-test.js

  1. // ==UserScript==
  2. // @version 1.7.2.1
  3. // ==/UserScript==
  4.  
  5.  
  6.  
  7. (function () {
  8. const blobURL = URL.createObjectURL(
  9. new Blob(
  10. [
  11. '(',
  12. function () {
  13. const ids = {};
  14. // 监听message 开始执行定时器或者销毁
  15. self.onmessage = (e) => {
  16. switch (e.data.command) {
  17. case 'interval:start': // 开启定时器
  18. const intervalId = setInterval(() => postMessage({ message: 'interval:tick', id: e.data.id }), e.data.interval);
  19. // postMessage({ message: 'interval:started', id: e.data.id });
  20. ids[e.data.id] = intervalId;
  21. break;
  22. case 'interval:clear': // 销毁
  23. clearInterval(ids[e.data.id]);
  24. postMessage({ message: 'interval:cleared', id: e.data.id });
  25. delete ids[e.data.id];
  26. break;
  27. case 'timeout:start':
  28. const timeoutId = setTimeout(() => postMessage({ message: 'timeout:tick', id: e.data.id }), e.data.timeout);
  29. // postMessage({ message: 'timeout:started', id: e.data.id });
  30. ids[e.data.id] = timeoutId;
  31. break;
  32. case 'timeout:clear':
  33. clearTimeout(ids[e.data.id]);
  34. postMessage({ message: 'timeout:cleared', id: e.data.id });
  35. delete ids[e.data.id];
  36. break;
  37. }
  38. };
  39. }.toString(),
  40. ')()',
  41. ],
  42. { type: 'application/javascript' },
  43. ),
  44. );
  45. const worker = new Worker(blobURL);
  46. URL.revokeObjectURL(blobURL); //用完释放URL对象
  47. const workerTimer = {
  48. id: 0,
  49. callbacks: {},
  50. setInterval: (cb, interval, context) => {
  51. const id = ++workerTimer.id;
  52. workerTimer.callbacks[id] = { fn: cb, context: context };
  53. worker.postMessage({ command: 'interval:start', interval: interval, id: id });
  54. return id;
  55. },
  56. setTimeout: (cb, timeout, context) => {
  57. const id = ++workerTimer.id;
  58. workerTimer.callbacks[id] = { fn: cb, context: context };
  59. worker.postMessage({ command: 'timeout:start', timeout: timeout, id: id });
  60. return id;
  61. },
  62.  
  63. // 监听worker 里面的定时器发送的message 然后执行回调函数
  64. onMessage: (e) => {
  65. switch (e.data.message) {
  66. case 'interval:tick':
  67. case 'timeout:tick':
  68. const callbackItem = workerTimer.callbacks[e.data.id];
  69. if (callbackItem && callbackItem.fn) {
  70. callbackItem.fn.apply(callbackItem.context);
  71. }
  72.  
  73. break;
  74. case 'interval:cleared':
  75. case 'timeout:cleared':
  76. delete workerTimer.callbacks[e.data.id];
  77. break;
  78. }
  79. },
  80.  
  81. // 往worker里面发送销毁指令
  82. clearInterval: (id) => worker.postMessage({ command: 'interval:clear', id: id }),
  83. clearTimeout: (id) => worker.postMessage({ command: 'timeout:clear', id: id }),
  84. };
  85. worker.onmessage = workerTimer.onMessage.bind(workerTimer);
  86.  
  87. let source = {
  88. version: 4,
  89. like: false,
  90. random: true,
  91. usePublic: false,
  92. data1: { available: true, values: ['弹幕①', '弹幕②'] },
  93. data2: { available: true, values: [] },
  94. data3: { available: true, values: [] },
  95. data4: { available: true, values: ['弹幕⑦'] },
  96. data5: { available: true, values: [] }
  97. },
  98. signInCheckbox, hideLoginGuideCheckbox, hideHarunaCheckbox, hideShopCheckbox, noSleepCheckbox, hideGiftControlCheckbox,
  99. hideRoomFeedCheckbox, hideRoomInfoCheckbox, hideNoticeCheckbox, hideFooterCheckbox, lotteryCheckbox, closeLotteryCheckbox,
  100. hesitationCheckbox, hidePrivacyCheckbox, hideRoomStatusCheckbox, setPublicCheckbox, hideBusinessCheckbox,
  101. hideRankListCheckbox, showLiveAreaCheckbox,
  102. group1Checkbox, group2Checkbox, group3Checkbox, group4Checkbox, group5Checkbox,
  103. rdCheckbox, usePublicCheckbox, autoLikeCheckbox,
  104. dmInput, divSetting, dataText1, dataText2, dataText3, dataText4, dataText5, signInput, hesitateInput,
  105. dmButtonSend, beforeSpan, afterSpan, spanApplyMsg, divUpdateInfo, btnStartText,
  106. baseInfo = {}, config = {}, waiters = [], data = [], ws = null, wsHostList = [], wsCmds = [],
  107. sendTimer = null, signInTimer = null, miniCloseTimer, noSleepTimer, noSleepTimeouter, btnLotteryTimer,
  108. divSendBtnTimer, autoLikeTimer, clickLikeBtnTimer, wsHeartBeatTimer, wsReconnectWsTimer,
  109. count = 0, waitCount = 200, arrayIndex = 0, default_timeout = 600, wsHostIndex = 0, wsConnectCount = 30,
  110. lotteryChecked = 'lottery_checked', closeLotteryChecked = 'close_lottery_checked', hesitationChecked = 'hesitation_checked',
  111. hesitationExpiry = 'hesitation_expiry', wsToken = '',
  112. gmNotice = obj => { alert(''); },
  113. getGmValue = (key, defaultValue) => { return null; },
  114. setGmValue = (key, obj) => { console.warn('===> No implementation "setGmValue" method.'); },
  115. delGmValue = key => { console.warn('===> No implementation "delGmValue" method.'); };
  116.  
  117. const version = '1.7.2', upodateInfo = 'WebSocket', noticeTimeout = 10e3,
  118. icoUrl = 'https://www.bilibili.com/favicon.ico',
  119. roomId = window.location.pathname.replace(/^\/(\S+\/)*/g, ''),
  120. trueRoomId = window.__STORE__?.baseInfoRoom.roomID || 0,
  121. ruid = window.__STORE__?.baseInfoAnchor?.uid || 0,
  122. uid = window.__STORE__?.baseInfoUser?.uid || 0,
  123. textEncoder = new TextEncoder('utf-8'),
  124. textDecoder = new TextDecoder('utf-8'),
  125. eventTarget = new EventTarget(),
  126. eventTypeEnum = Object.freeze({authReply: 'AUTH_REPLY', heartBeatReply: 'HEARTBEAT_REPLY', danmuMsg: 'DANMU_MSG',
  127. reenterLiveRoom: 'REENTER_LIVE_ROOM', roomChange: 'ROOM_CHANGE', stopLiveRoomList: 'STOP_LIVE_ROOM_LIST',
  128. cutOff: 'CUT_OFF', anchorLotStart: 'ANCHOR_LOT_START', anchorLotEnd: 'ANCHOR_LOT_END', preparing: 'PREPARING',
  129. live: 'LIVE'}),
  130. onEvent = (eventTyppe, callback) => eventTarget.addEventListener(eventTyppe, e => callback(e.detail)),
  131. setGmGetValue = callback => getGmValue = callback,
  132. setGmSetValue = callback => setGmValue = callback,
  133. setGmDelValue = callback => delGmValue = callback,
  134. setGmNotice = callback => gmNotice = callback,
  135. setBaseInfo = obj => baseInfo = obj,
  136. arrayInfo = () => console.info(data),
  137. isNull = str => {
  138. if (!str || str == "") {
  139. return true;
  140. }
  141.  
  142. let regu = "^[ ]+$";
  143. let re = new RegExp(regu);
  144. return re.test(str);
  145. },
  146. compareVersion = (ver1, ver2) => {
  147. if (isNull(ver1)) return -1;
  148. if (isNull(ver2)) return 1;
  149.  
  150. const arr1 = ver1.split('.').map(x => x * 1),
  151. arr2 = ver2.split('.').map(x => x * 1),
  152. len = Math.max(arr1.length, arr2.length);
  153. for (let i = 0; i < len; i++) {
  154. if ((arr1[i] || 0) > (arr2[i] || 0)) return 1;
  155. if ((arr1[i] || 0) < (arr2[i] || 0)) return -1;
  156. }
  157.  
  158. return 0;
  159. },
  160. getCookie = name => {
  161. let key = name + '=';
  162. const cookies = document.cookie.split(';');
  163. for(var i = 0; i < cookies.length; i++) {
  164. var cookie = cookies[i].trim();
  165. if (cookie.indexOf(key) == 0)
  166. return cookie.substring(key.length, cookie.length);
  167. }
  168. return '';
  169. },
  170. decompressBrotli = async arrayBuff => {
  171. const stream = new DecompressionStream('brotli');
  172. const decompressedStream = new Response(arrayBuff.stream().pipeThrough(stream));
  173. const result = await decompressedStream.arrayBuffer();
  174. return new Uint8Array(result);
  175. },
  176. initCss = () => {
  177. let linkElement = document.createElement('link');
  178. linkElement.rel = 'stylesheet';
  179. linkElement.href = 'https://unpkg.zhimg.com/element-ui/lib/theme-chalk/index.css';
  180. document.head.appendChild(linkElement);
  181.  
  182. let customerStyle = document.createElement('style');
  183. customerStyle.setAttribute('type', 'text/css');
  184. customerStyle.innerHTML = '.danmu-group-title{font-size:14px;padding-left:2px;color:rgb(18,56,141);display:inline;margin-right:60%;vertical-align:middle;}.danmu-group-textarea{width:98%;min-height:100px;height:16%;margin:1px 0px 4px;border:0px;resize:none;}.el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#FFF;border:1px solid #DCDFE6;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;padding:12px 20px;font-size:14px;border-radius:4px}.el-button.is-circle{border-radius:50%;padding:12px}.el-button--mini.is-circle{padding:3px;}.el-button:focus,.el-button:hover{color:#409EFF;border-color:#c6e2ff;background-color:#ecf5ff}.el-icon-close.is-circle{padding:5px;color:#ff0000;border:1px solid #ff0000;margin-left:20px;}.el-icon-check.is-circle{padding:5px;color:#0000ff;border:1px solid #0000ff;margin-left:20px;}input[type="checkbox"]{display:none;}.switch-check{display:inline-block;margin:0 5px;vertical-align:middle;}.switch-check-label{display:inline-block;vertical-align:middle;border:1px solid #bdc3c7;border-radius:60px;width:40px;height:18px;position:relative;transition:all .3s;cursor:pointer;}.switch-check-label:before{width:14px;height:14px;content:"";display:inline-block;background-color:#bdc3c7;border-radius:100%;position:absolute;top:2px;left:4px;transition:all .3s;}.switch-check :checked ~ label{background-color:#26b22b;border-color:#26b22b;}.switch-check :checked ~ label:before{left:22px;background-color:#fff;}.switch-check-group{margin-top:5px;width:95%;}.danmu-random-setting-panel{background-color:#e7f1fb;border-radius:10px;width:100%;height:100%;overflow-y:auto;position:absolute;left:0px;top:0px;z-index:999;display:none;}.danmu-random-setting-panel::-webkit-scrollbar{width:4px;height:4px;}.danmu-random-setting-panel::-webkit-scrollbar-thumb{border-radius:5px;-webkit-box-shadow:inset 0 0 5px rgba(0,0,0,0.2);background:rgba(0,0,0,0.2);}.danmu-random-setting-panel::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 5px rgba(0,0,0,0.2);border-radius:0;background:rgba(0,0,0,0.1);}.danmu-random-setting-title{text-align:center;font-size:16px;font-weight:700;color:#1c5adc;line-height:30px;}.danmu-random-setting-title-sub{display:inline-block;color:#ee8b8b;height:24px;vertical-align:sub;-webkit-transform:scale(0.7);}.danmu-random-setting-tips{color:#0b81cc;text-align:center;font-style:italic;}.module-update-info{color:#0b81cc;text-indent:2em;font-size:13px;font-weight:700;margin:8px 0;padding:2px 0;background-color:#ffffff;}.update-info-text{color:#f00;vertical-align:-webkit-baseline-middle;}.danmu-random-setting-bottom{width:100%;line-height:35px;}.danmu-random-switch-button-title{font-size:14px;vertical-align:middle;margin-left:5px;color:#095ca2;cursor:help;}.danmu-random-setting-success-tips{text-align:center;display:inline-block;vertical-align:middle;width:40%;}.danmu-random-setting-success-text{font-size:16px;color:#128712;display:none;}.danmu-random-set-button-container{display:inline-block;vertical-align:middle;}.global-setting-tip{padding-left:10px;color:red;font-size:14px;font-weight:700;cursor:help;}.disabled{color:#ababab;cursor:not-allowed;}.clean-cache-btn{min-width:70px;font-size:14px;border-radius:4px;color:#fff;background:#d99d1b;border:0px;cursor:pointer;vertical-align:middle;line-height:30px;}.clean-cache-btn:hover{background:rgba(217,157,27,0.8);color:#000}.danmu-btn{min-width:65px;height:24px;font-size:12px;border-radius:4px;color:rgb(255,255,255);background:rgb(217,157,27);border:0px;cursor:pointer;line-height:1;display:inline-flex;justify-content:center;align-items:center;}.danmu-btn:hover{background:var(--color)!important;}.danmu-text-span{color:#095ca2;font-size:20px;vertical-align:middle;font-weight:700}.danmu-second-input{width:70px;height:20px;margin:0px 3px;border:0px;border-radius:3px;font-size:18px}.not-display{display:none !important;}';
  185. document.head.appendChild(customerStyle);
  186. },
  187. initScript = () => {
  188. let script = document.createElement('script');
  189. script.type = 'text/javascript';
  190. // script.src = 'https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.js';
  191. script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako_inflate.min.js';
  192. document.head.appendChild(script);
  193. },
  194. getCurrentTimestamp = () => new Date().getTime(),
  195. send = (msg, index) => {
  196. let dmTextArea = document.getElementById('chat-control-panel-vm').querySelector('textarea');
  197. if (!dmTextArea) {
  198. alert('找不到输入弹幕文本框,请尝试刷新页面');
  199. return;
  200. }
  201. if (!isNull(dmTextArea.value)) {
  202. console.log(`===> 有内容正在编辑,跳过该次发送任务`);
  203. return;
  204. }
  205.  
  206. let btnSend = document.getElementsByClassName('bl-button bl-button--primary')[0];
  207. if (!btnSend) {
  208. alert('找不到发送按钮,请尝试刷新页面');
  209. return;
  210. }
  211. dmTextArea.value = msg;
  212. dmTextArea.dispatchEvent(new Event('input', { "bubbles": true, "cancelable": true }));
  213. btnSend.click();
  214. // ++count;
  215. // console.log('===> ' + new Date().toLocaleString() + ' 弹幕发送成功 ' + count + ' 次,第【' + index + '】条数据 === ' + msg);
  216. },
  217. randomSort = arr => {
  218. for (let i = 0; i < arr.length; i++) {
  219. const rdIndex = Math.floor(Math.random() * arr.length);
  220. const temp = arr[i];
  221. arr[i] = arr[rdIndex];
  222. arr[rdIndex] = temp;
  223. }
  224.  
  225. return arr;
  226. },
  227. clearWaiters = () => {
  228. for (let i = 0; i < waiters.length; i++) {
  229. workerTimer.clearInterval(waiters[i]);
  230. waiters[i] = null;
  231. }
  232.  
  233. waiters = [];
  234. },
  235. signInFunc = () => {
  236. if (signInCheckbox.checked) {
  237. if (!signInTimer) {
  238. let defaultText = isNull(baseInfo.text) ? baseInfo.text : '打卡';
  239. let timestamp = new Date(new Date(new Date().setDate(new Date().getDate() + 1)).toDateString()).getTime() - getCurrentTimestamp();
  240. console.log('===> 设置凌晨打卡定时器【' + timestamp + '】');
  241. signInput.value = isNull(config.signInText) ? defaultText : config.signInText;
  242. signInTimer = workerTimer.setTimeout(() => {
  243. send(isNull(signInput.value) ? defaultText : signInput.value, 0);
  244. console.log('===> 设置下一次打卡');
  245. workerTimer.clearTimeout(signInTimer);
  246. signInTimer = null;
  247. signInFunc();
  248. }, timestamp);
  249. }
  250. } else if (signInTimer) {
  251. console.log('===> 关闭自动打卡功能');
  252. workerTimer.clearTimeout(signInTimer);
  253. signInTimer = null;
  254. }
  255. },
  256. setSource = obj => {
  257. dataText1.value = obj.data1.values.length ? obj.data1.values.join('|') : [];
  258. dataText2.value = obj.data2.values.length ? obj.data2.values.join('|') : [];
  259. dataText3.value = obj.data3.values.length ? obj.data3.values.join('|') : [];
  260. dataText4.value = obj.data4.values.length ? obj.data4.values.join('|') : [];
  261. dataText5.value = obj.data5.values.length ? obj.data5.values.join('|') : [];
  262. },
  263. runCheckbox = () => {
  264. usePublicFunc();
  265. hideFooterFunc();
  266. hideGiftControlFunc();
  267. hideLoginGuideFunc();
  268. hideHarunaFunc();
  269. hideShopFunc();
  270. hidePrivacyFunc();
  271. hideRoomStatusFunc();
  272. hideBusinessFunc();
  273. hideRankListFunc();
  274. noSleepFunc();
  275. signInFunc();
  276. lotteryFunc();
  277. hesitationFunc();
  278. closeLotteryFunc();
  279. autoLikeFunc();
  280. // autoPlay();
  281. hideRoomFeedFunc();
  282. hideRoomInfoFunc();
  283. hideNoticeFunc();
  284. showLiveAreaFunc();
  285. let hideTimeout = workerTimer.setTimeout(() => {
  286. workerTimer.clearTimeout(hideTimeout);
  287. hideRoomFeedFunc();
  288. hideRoomInfoFunc();
  289. hideNoticeFunc();
  290. showLiveAreaFunc();
  291. }, 1e3);
  292. },
  293. setOperationSetting = () => {
  294. setPublicCheckbox.checked = false;
  295. rdCheckbox.checked = source.random;
  296. autoLikeCheckbox.checked = source.like;
  297. usePublicCheckbox.checked = source.usePublic;
  298. dmInput.value = source.interval || default_timeout;
  299. group1Checkbox.checked = source.data1.available;
  300. group2Checkbox.checked = source.data2.available;
  301. group3Checkbox.checked = source.data3.available;
  302. group4Checkbox.checked = source.data4.available;
  303. group5Checkbox.checked = source.data5.available;
  304.  
  305. signInCheckbox.checked = config.autoSignIn;
  306. noSleepCheckbox.checked = config.noSleep;
  307. hideLoginGuideCheckbox.checked = config.hideLoginGuide;
  308. hideHarunaCheckbox.checked = config.hideHaruna;
  309. hideShopCheckbox.checked = config.hideShop;
  310. hideGiftControlCheckbox.checked = config.hideGift;
  311. hideRoomFeedCheckbox.checked = config.hideRoomFeed;
  312. hideRoomInfoCheckbox.checked = config.hideRoomInfo;
  313. hideNoticeCheckbox.checked = config.hideNotice;
  314. hideFooterCheckbox.checked = config.hideFooter;
  315. lotteryCheckbox.checked = config.lottery;
  316. hesitationCheckbox.checked = config.hesitation;
  317. closeLotteryCheckbox.checked = config.closeLottery;
  318. hidePrivacyCheckbox.checked = config.noPrivacy;
  319. hideRoomStatusCheckbox.checked = config.hideWatermark;
  320. hideBusinessCheckbox.checked = config.hideBusiness;
  321. hideRankListCheckbox.checked = config.hideRankList;
  322. showLiveAreaCheckbox.checked = config.showLiveArea;
  323. runCheckbox();
  324. },
  325. openSetting = () => divSetting.style.display = 'block',
  326. closeSetting = () => {
  327. setOperationSetting();
  328. divSetting.style.display = 'none';
  329. },
  330. initData = () => {
  331. if (source.data1.values.length <= 0
  332. && source.data2.values.length <= 0
  333. && source.data3.values.length <= 0
  334. && source.data4.values.length <= 0
  335. && source.data5.values.length <= 0) {
  336. return data ? data : [];
  337. }
  338.  
  339. let result = [];
  340. result = source.data1.available ? result.concat(source.data1.values) : result;
  341. result = source.data2.available ? result.concat(source.data2.values) : result;
  342. result = source.data3.available ? result.concat(source.data3.values) : result;
  343. result = source.data4.available ? result.concat(source.data4.values) : result;
  344. result = source.data5.available ? result.concat(source.data5.values) : result;
  345. data = result;
  346. rdCheckbox.checked ? data = randomSort(result) : arrayIndex = 0;
  347. },
  348. flashMsg = (txt, back, color) => {
  349. spanApplyMsg.textContent = txt;
  350. spanApplyMsg.style.display = 'block';
  351. if (color) {
  352. spanApplyMsg.style.color = color;
  353. }
  354. else {
  355. spanApplyMsg.style.color = '#128712';
  356. }
  357.  
  358. let hideTipTimer = workerTimer.setTimeout(() => {
  359. workerTimer.clearTimeout(hideTipTimer);
  360. spanApplyMsg.style.display = 'none';
  361. spanApplyMsg.textContent = '';
  362. if (back) {
  363. divSetting.style.display = 'none';
  364. }
  365. }, 1.5e3);
  366. },
  367. save = () => {
  368. if (!baseInfo || isNull(baseInfo.default) || isNull(baseInfo.config)) {
  369. flashMsg('保存失败', false, 'red');
  370. return;
  371. }
  372. if (baseInfo.config) {
  373. config.autoSignIn = signInCheckbox.checked;
  374. config.signInText = signInput.value;
  375. config.noSleep = noSleepCheckbox.checked;
  376. config.hideLoginGuide = hideLoginGuideCheckbox.checked;
  377. config.hideHaruna = hideHarunaCheckbox.checked;
  378. config.hideShop = hideShopCheckbox.checked;
  379. config.hideGift = hideGiftControlCheckbox.checked;
  380. config.hideRoomFeed = hideRoomFeedCheckbox.checked;
  381. config.hideRoomInfo = hideRoomInfoCheckbox.checked;
  382. config.hideNotice = hideNoticeCheckbox.checked;
  383. config.hideFooter = hideFooterCheckbox.checked;
  384. config.lottery = lotteryCheckbox.checked;
  385. window.localStorage.setItem(lotteryChecked, lotteryCheckbox.checked);
  386. config.hesitation = hesitationCheckbox.checked;
  387. window.localStorage.setItem(hesitationChecked, hesitationCheckbox.checked);
  388. config.hesitationExpiry = hesitateInput.value;
  389. window.localStorage.setItem(hesitationExpiry, hesitateInput.value);
  390. config.closeLottery = closeLotteryCheckbox.checked;
  391. window.localStorage.setItem(closeLotteryChecked, config.closeLottery);
  392. config.noPrivacy = hidePrivacyCheckbox.checked;
  393. config.hideWatermark = hideRoomStatusCheckbox.checked;
  394. config.hideBusiness = hideBusinessCheckbox.checked;
  395. config.hideRankList = hideRankListCheckbox.checked;
  396. config.showLiveArea = showLiveAreaCheckbox.checked;
  397. setGmValue(baseInfo.config, config);
  398. }
  399.  
  400. let v1 = isNull(dataText1.value) ? [] : dataText1.value.split('|'),
  401. v2 = isNull(dataText2.value) ? [] : dataText2.value.split('|'),
  402. v3 = isNull(dataText3.value) ? [] : dataText3.value.split('|'),
  403. v4 = isNull(dataText4.value) ? [] : dataText4.value.split('|'),
  404. v5 = isNull(dataText5.value) ? [] : dataText5.value.split('|');
  405. source.interval = dmInput.value;
  406. source.random = rdCheckbox.checked;
  407. source.like = autoLikeCheckbox.checked;
  408. source.usePublic = usePublicCheckbox.checked;
  409. source.data1.available = group1Checkbox.checked;
  410. source.data2.available = group2Checkbox.checked;
  411. source.data3.available = group3Checkbox.checked;
  412. source.data4.available = group4Checkbox.checked;
  413. source.data5.available = group5Checkbox.checked;
  414. if (!usePublicCheckbox.checked) {
  415. source.data1.values = v1;
  416. source.data2.values = v2;
  417. source.data3.values = v3;
  418. source.data4.values = v4;
  419. source.data5.values = v5;
  420. }
  421. setGmValue(roomId, source);
  422. if (setPublicCheckbox.checked) {
  423. let ps = {data1:{},data2:{},data3:{},data4:{},data5:{}};
  424. ps.data1.values = v1;
  425. ps.data2.values = v2;
  426. ps.data3.values = v3;
  427. ps.data4.values = v4;
  428. ps.data5.values = v5;
  429. setGmValue(baseInfo.default, ps);
  430. }
  431. if (usePublicCheckbox.checked) {
  432. source.data1.values = v1;
  433. source.data2.values = v2;
  434. source.data3.values = v3;
  435. source.data4.values = v4;
  436. source.data5.values = v5;
  437. }
  438. initData();
  439. flashMsg('设置成功', true);
  440. },
  441. cleanCache = () => {
  442. if (baseInfo.config && config) {
  443. config.script = '';
  444. config.moduleVersion = '0.0.0';
  445. config.lastUpdate = '清除脚本缓存';
  446. setGmValue(baseInfo.config, config);
  447. flashMsg('清除成功');
  448. } else {
  449. console.warn('元数据丢失');
  450. flashMsg('操作失败', false, 'red');
  451. }
  452. },
  453. danmu = () => {
  454. if (data.length < 1) {
  455. // gmNotice({
  456. // text: '请任意在一个分组里输入一条弹幕',
  457. // title: '没有弹幕数据,请先设置',
  458. // image: icoUrl,
  459. // highlight: true,
  460. // timeout: noticeTimeout
  461. // });
  462. alert('请先设置弹幕');
  463. return false;
  464. }
  465. if (rdCheckbox.checked) {
  466. arrayIndex = Math.floor((Math.random() * data.length));
  467. }
  468.  
  469. send(data[arrayIndex], arrayIndex);
  470. ++arrayIndex;
  471. if (arrayIndex >= data.length) {
  472. arrayIndex = 0;
  473. }
  474.  
  475. return true;
  476. },
  477. offOrOn = () => {
  478. let timeout = 0;
  479. if (sendTimer) {
  480. workerTimer.clearInterval(sendTimer);
  481. sendTimer = null;
  482. dmButtonSend.style.removeProperty('background');
  483. dmButtonSend.style.removeProperty('--color');
  484. dmInput.removeAttribute("disabled");
  485. btnStartText.textContent = '开始';
  486. } else {
  487. timeout = (dmInput.value || default_timeout) * 1e3;
  488. if (!danmu()) {
  489. return;
  490. }
  491.  
  492. sendTimer = workerTimer.setInterval(danmu, timeout);
  493. dmButtonSend.style.background = 'rgba(255,0,0,1)';
  494. dmButtonSend.style.setProperty('--color', 'rgba(255,0,0,0.8)');
  495. dmInput.setAttribute('disabled', 'disabled');
  496. btnStartText.textContent = '停止';
  497. }
  498. },
  499. createSwitch = (id, txt, title, func, container, indent, width, hidden, rear, disabled) => {
  500. let checkbox = document.createElement('input');
  501. checkbox.type = 'checkbox';
  502. checkbox.id = id;
  503. checkbox.checked = false;
  504. if (func && !disabled) {
  505. checkbox.addEventListener('click', func);
  506. }
  507.  
  508. let lblCheckbox = document.createElement('label');
  509. lblCheckbox.setAttribute('for', id);
  510. lblCheckbox.classList.add('switch-check-label');
  511.  
  512. let descSpan = document.createElement('span');
  513. descSpan.textContent = txt;
  514. descSpan.title = title;
  515. descSpan.classList.add('danmu-random-switch-button-title');
  516. if (disabled) {
  517. checkbox.disabled = true;
  518. checkbox.classList.add('disabled');
  519. lblCheckbox.classList.add('disabled');
  520. descSpan.classList.add('disabled');
  521. }
  522.  
  523. let divCheckbox = document.createElement('div');
  524. divCheckbox.id = id + 'Div';
  525. divCheckbox.classList.add('switch-check');
  526. divCheckbox.classList.add('switch-check-group');
  527. divCheckbox.appendChild(checkbox);
  528. divCheckbox.appendChild(lblCheckbox);
  529. divCheckbox.appendChild(descSpan);
  530. if (!isNull(indent)) {
  531. divCheckbox.style.marginLeft = indent;
  532. }
  533. if (!isNull(width)) {
  534. divCheckbox.style.width = width;
  535. }
  536. if (hidden) {
  537. divCheckbox.style.setProperty('display', 'none');
  538. }
  539. if (rear) {
  540. divCheckbox.appendChild(rear);
  541. }
  542.  
  543. container.appendChild(divCheckbox);
  544. return checkbox;
  545. },
  546. buildPanel = () => {
  547. /* ----------------------------------------- head ----------------------------------------- */
  548. let divSettingTitle = document.createElement('div');
  549. divSettingTitle.textContent = '弹幕设置';
  550. divSettingTitle.classList.add('danmu-random-setting-title');
  551.  
  552. let divSub = document.createElement('div');
  553. divSub.textContent = version;
  554. divSub.classList.add('danmu-random-setting-title-sub');
  555. divSettingTitle.appendChild(divSub);
  556.  
  557. let divTip = document.createElement('div');
  558. divTip.classList.add('danmu-random-setting-tips');
  559. divTip.innerHTML = '任一分组内输入弹幕即可,多条用<span style="color:#dc6b07;margin:0 2px 0 4px;font-weight:700;font-style:normal;">竖线</span>分隔';
  560.  
  561. divUpdateInfo = document.createElement('div');
  562. divUpdateInfo.classList.add('module-update-info');
  563. divUpdateInfo.innerHTML = `<span class="update-info-text">消息提示:</span><span class="update-info-text" style="color:#0f6ba6;">${baseInfo.msg || upodateInfo}</span>`;
  564. /* ----------------------------------------- head ----------------------------------------- */
  565.  
  566. /* ----------------------------------------- textarea 1 ----------------------------------------- */
  567. let divText1 = document.createElement('div');
  568. divText1.textContent = '分组 1 :';
  569. divText1.classList.add('danmu-group-title');
  570.  
  571. group1Checkbox = document.createElement('input');
  572. group1Checkbox.type = 'checkbox';
  573. group1Checkbox.id = 'group1Checkbox';
  574. group1Checkbox.checked = true;
  575.  
  576. let lblGroup1Checkbox = document.createElement('label');
  577. lblGroup1Checkbox.setAttribute('for', 'group1Checkbox');
  578. lblGroup1Checkbox.classList.add('switch-check-label');
  579.  
  580. let divGroup1Checkbox = document.createElement('div');
  581. divGroup1Checkbox.classList.add('switch-check');
  582. divGroup1Checkbox.appendChild(group1Checkbox);
  583. divGroup1Checkbox.appendChild(lblGroup1Checkbox);
  584.  
  585. dataText1 = document.createElement('textarea');
  586. dataText1.classList.add('danmu-group-textarea');
  587. dataText1.setAttribute('placeholder', '请输入弹幕,多条弹幕请用“|”分隔');
  588. /* ----------------------------------------- textarea 1 ----------------------------------------- */
  589.  
  590. /* ----------------------------------------- textarea 2 ----------------------------------------- */
  591. let divText2 = document.createElement('div');
  592. divText2.textContent = '分组 2 :';
  593. divText2.classList.add('danmu-group-title');
  594.  
  595. group2Checkbox = document.createElement('input');
  596. group2Checkbox.type = 'checkbox';
  597. group2Checkbox.id = 'group2Checkbox';
  598. group2Checkbox.checked = true;
  599.  
  600. let lblGroup2Checkbox = document.createElement('label');
  601. lblGroup2Checkbox.setAttribute('for', 'group2Checkbox');
  602. lblGroup2Checkbox.classList.add('switch-check-label');
  603.  
  604. let divGroup2Checkbox = document.createElement('div');
  605. divGroup2Checkbox.classList.add('switch-check');
  606. divGroup2Checkbox.appendChild(group2Checkbox);
  607. divGroup2Checkbox.appendChild(lblGroup2Checkbox);
  608.  
  609. dataText2 = document.createElement('textarea');
  610. dataText2.classList.add('danmu-group-textarea');
  611. dataText2.setAttribute('placeholder', '请输入弹幕,多条弹幕请用“|”分隔');
  612. /* ----------------------------------------- textarea 2 ----------------------------------------- */
  613.  
  614. /* ----------------------------------------- textarea 3 ----------------------------------------- */
  615. let divText3 = document.createElement('div');
  616. divText3.textContent = '分组 3 :';
  617. divText3.classList.add('danmu-group-title');
  618.  
  619. group3Checkbox = document.createElement('input');
  620. group3Checkbox.type = 'checkbox';
  621. group3Checkbox.id = 'group3Checkbox';
  622. group3Checkbox.checked = true;
  623.  
  624. let lblGroup3Checkbox = document.createElement('label');
  625. lblGroup3Checkbox.setAttribute('for', 'group3Checkbox');
  626. lblGroup3Checkbox.classList.add('switch-check-label');
  627.  
  628. let divGroup3Checkbox = document.createElement('div');
  629. divGroup3Checkbox.classList.add('switch-check');
  630. divGroup3Checkbox.appendChild(group3Checkbox);
  631. divGroup3Checkbox.appendChild(lblGroup3Checkbox);
  632.  
  633. dataText3 = document.createElement('textarea');
  634. dataText3.classList.add('danmu-group-textarea');
  635. dataText3.setAttribute('placeholder', '请输入弹幕,多条弹幕请用“|”分隔');
  636. /* ----------------------------------------- textarea 3 ----------------------------------------- */
  637.  
  638. /* ----------------------------------------- textarea 4 ----------------------------------------- */
  639. let divText4 = document.createElement('div');
  640. divText4.textContent = '分组 4 :';
  641. divText4.classList.add('danmu-group-title');
  642.  
  643. group4Checkbox = document.createElement('input');
  644. group4Checkbox.type = 'checkbox';
  645. group4Checkbox.id = 'group4Checkbox';
  646. group4Checkbox.checked = true;
  647.  
  648. let lblGroup4Checkbox = document.createElement('label');
  649. lblGroup4Checkbox.setAttribute('for', 'group4Checkbox');
  650. lblGroup4Checkbox.classList.add('switch-check-label');
  651.  
  652. let divGroup4Checkbox = document.createElement('div');
  653. divGroup4Checkbox.classList.add('switch-check');
  654. divGroup4Checkbox.appendChild(group4Checkbox);
  655. divGroup4Checkbox.appendChild(lblGroup4Checkbox);
  656.  
  657. dataText4 = document.createElement('textarea');
  658. dataText4.classList.add('danmu-group-textarea');
  659. dataText4.setAttribute('placeholder', '请输入弹幕,多条弹幕请用“|”分隔');
  660. /* ----------------------------------------- textarea 4 ----------------------------------------- */
  661.  
  662. /* ----------------------------------------- textarea 5 ----------------------------------------- */
  663. let divText5 = document.createElement('div');
  664. divText5.textContent = '分组 5 :';
  665. divText5.classList.add('danmu-group-title');
  666.  
  667. group5Checkbox = document.createElement('input');
  668. group5Checkbox.type = 'checkbox';
  669. group5Checkbox.id = 'group5Checkbox';
  670. group5Checkbox.checked = true;
  671.  
  672. let lblGroup5Checkbox = document.createElement('label');
  673. lblGroup5Checkbox.setAttribute('for', 'group5Checkbox');
  674. lblGroup5Checkbox.classList.add('switch-check-label');
  675.  
  676. let divGroup5Checkbox = document.createElement('div');
  677. divGroup5Checkbox.classList.add('switch-check');
  678. divGroup5Checkbox.appendChild(group5Checkbox);
  679. divGroup5Checkbox.appendChild(lblGroup5Checkbox);
  680.  
  681. dataText5 = document.createElement('textarea');
  682. dataText5.classList.add('danmu-group-textarea');
  683. dataText5.setAttribute('placeholder', '请输入弹幕,多条弹幕请用“|”分隔');
  684. /* ----------------------------------------- textarea 5 ----------------------------------------- */
  685.  
  686.  
  687. /* ----------------------------------------- send interval ----------------------------------------- */
  688. beforeSpan = document.createElement('span');
  689. beforeSpan.textContent = '设置弹幕每';
  690. beforeSpan.classList.add('danmu-text-span');
  691. beforeSpan.style.marginLeft = '4px';
  692.  
  693. dmInput = document.createElement('input');
  694. dmInput.value = default_timeout;
  695. dmInput.classList.add('danmu-second-input');
  696. dmInput.setAttribute('oninput', "this.value = this.value.replace(/[^0-9]/g, '')");
  697.  
  698. afterSpan = document.createElement('span');
  699. afterSpan.textContent = '秒发送一次';
  700. afterSpan.classList.add('danmu-text-span');
  701. afterSpan.style.marginRight = '4px';
  702.  
  703. let divSendInterval = document.createElement('div');
  704. divSendInterval.appendChild(beforeSpan);
  705. divSendInterval.appendChild(dmInput);
  706. divSendInterval.appendChild(afterSpan);
  707. /* ----------------------------------------- send interval ----------------------------------------- */
  708.  
  709.  
  710. /* ----------------------------------------- different room setting ----------------------------------------- */
  711. let divRoomSetting = document.createElement('div');
  712. divRoomSetting.appendChild(divText1);
  713. divRoomSetting.appendChild(divGroup1Checkbox);
  714. divRoomSetting.appendChild(dataText1);
  715. divRoomSetting.appendChild(divText2);
  716. divRoomSetting.appendChild(divGroup2Checkbox);
  717. divRoomSetting.appendChild(dataText2);
  718. divRoomSetting.appendChild(divText3);
  719. divRoomSetting.appendChild(divGroup3Checkbox);
  720. divRoomSetting.appendChild(dataText3);
  721. divRoomSetting.appendChild(divText4);
  722. divRoomSetting.appendChild(divGroup4Checkbox);
  723. divRoomSetting.appendChild(dataText4);
  724. divRoomSetting.appendChild(divText5);
  725. divRoomSetting.appendChild(divGroup5Checkbox);
  726. divRoomSetting.appendChild(dataText5);
  727. divRoomSetting.appendChild(divSendInterval);
  728. rdCheckbox = createSwitch('rdCheckbox', '随机从上面的弹幕中选出一条发送', '将合并所有分组数据,从中随机选出一条发送', null, divRoomSetting);
  729. usePublicCheckbox = createSwitch('usePublicCheckbox', '使用共用弹幕源', '使用设置为共用弹幕作为弹幕源', usePublicFunc, divRoomSetting);
  730. autoLikeCheckbox = createSwitch('autoLikeCheckbox', '自动点赞该直播间', '每15秒内随机一个时间点点赞一次直播间(点赞1000次大概需要4小时)', autoLikeFunc, divRoomSetting, null, null, null, null);
  731.  
  732. let operationDescription = document.createElement('div');
  733. operationDescription.textContent = '以上设置对应各个直播间独立保存,无需刷新';
  734. operationDescription.title = '请点击设置面板底下的“✓”进行保存';
  735. operationDescription.classList.add('global-setting-tip');
  736. operationDescription.classList.add('switch-check-group');
  737. divRoomSetting.appendChild(operationDescription);
  738. /* ----------------------------------------- different room setting ----------------------------------------- */
  739.  
  740. /* ----------------------------------------- global setting ----------------------------------------- */
  741. let divGlobalSetting = document.createElement('div');
  742. divGlobalSetting.style.margin = '20px 0 10px';
  743.  
  744. operationDescription = document.createElement('div');
  745. operationDescription.textContent = '以下设置,需刷新其它直播间才能适用';
  746. operationDescription.title = '请点击设置面板底下的“✓”进行保存';
  747. operationDescription.classList.add('global-setting-tip');
  748. operationDescription.classList.add('switch-check-group');
  749. divGlobalSetting.appendChild(operationDescription);
  750.  
  751. signInput = document.createElement('input');
  752. signInput.id = 'signInputText';
  753. signInput.style.border = '0';
  754. signInput.style.width = '90px';
  755. signInput.setAttribute('placeholder', '输入打卡的文字');
  756.  
  757. hesitateInput = document.createElement('input');
  758. hesitateInput.style.border = '0';
  759. hesitateInput.style.width = '55px';
  760. hesitateInput.setAttribute('placeholder', '单位:秒');
  761. hesitateInput.setAttribute('oninput', "this.value = this.value.replace(/[^0-9]/g, '')");
  762.  
  763. setPublicCheckbox = createSwitch('setPublicCheckbox', '设为共用弹幕源', '把这个直播间的弹幕共享给其它直播间使用,先后设置时,后面的会覆盖前面的设置', setPublicFunc, divGlobalSetting);
  764. signInCheckbox = createSwitch('signInCheckbox', '打卡弹幕(需登录(不可用)):', '每日零点发送一条打卡弹幕', signInFunc, divGlobalSetting, null, null, false, signInput);
  765. lotteryCheckbox = createSwitch('lotteryCheckbox', '自动参与天选时刻抽奖(需登录(不可用))', '自动点击参与按钮,请确保已经登录(不可用)了阿B账号', lotteryFunc, divGlobalSetting);
  766. hesitationCheckbox = createSwitch('hesitationCheckbox', '随机犹豫期:', '(尝试应对人机校验,不一定有效,随机的范围:(0, 填的数字])不立刻参与天选时刻,这期间可以手动处理,免得自动参加后后悔(时间单位:秒)', hesitationFunc, divGlobalSetting, '23px', '90%', true, hesitateInput);
  767. closeLotteryCheckbox = createSwitch('closeLotteryCheckbox', '关闭天选时刻', '关闭天选时刻弹窗', closeLotteryFunc, divGlobalSetting);
  768. hideGiftControlCheckbox = createSwitch('hideGiftControlCheckbox', '隐藏礼物栏', '隐藏播放器底部的礼物栏', hideGiftControlFunc, divGlobalSetting);
  769. noSleepCheckbox = createSwitch('noSleepCheckbox', '防止直播间休眠', '防止直播间页面一段时间没操作之后进入休眠', noSleepFunc, divGlobalSetting, null, null, false);
  770. hideLoginGuideCheckbox = createSwitch('hideLoginGuideCheckbox', '隐藏播放器底部登录(不可用)提示', '隐藏未登录(不可用)时播放器底部显示的登录(不可用)提示', hideLoginGuideFunc, divGlobalSetting);
  771. hideHarunaCheckbox = createSwitch('hideHarunaCheckbox', '隐藏看板娘立绘', '隐藏直播间Haruna立绘', hideHarunaFunc, divGlobalSetting);
  772. hideShopCheckbox = createSwitch('hideShopCheckbox', '隐藏购物提示', '隐藏播放器左上角的商店购物提示', hideShopFunc, divGlobalSetting);
  773. hideRoomFeedCheckbox = createSwitch('hideRoomFeedCheckbox', '隐藏主播动态', '隐藏播放器底下主播的动态栏', hideRoomFeedFunc, divGlobalSetting);
  774. hideRoomInfoCheckbox = createSwitch('hideRoomInfoCheckbox', '隐藏简介、荣誉,或直播间推荐列表', '隐藏播放器底下直播间推荐列表,不登录(不可用)账号显示为主播的荣耀和简介', hideRoomInfoFunc, divGlobalSetting);
  775. hideNoticeCheckbox = createSwitch('hideNoticeCheckbox', '隐藏主播公告', '隐藏弹幕列表底下主播的公告', hideNoticeFunc, divGlobalSetting);
  776. hideFooterCheckbox = createSwitch('hideFooterCheckbox', '隐藏直播间页脚', '隐藏直播间底部的网页页脚', hideFooterFunc, divGlobalSetting);
  777. hidePrivacyCheckbox = createSwitch('hidePrivacyCheckbox', '隐藏隐私提示对话框', '隐藏隐私提示登录(不可用)的对话框,被打码的昵称不保证变回正常', hidePrivacyFunc, divGlobalSetting);
  778. hideRoomStatusCheckbox = createSwitch('hideRoomStatusCheckbox', '隐藏直播水印', '隐藏播放器左上角的直播水印', hideRoomStatusFunc, divGlobalSetting);
  779. hideBusinessCheckbox = createSwitch('hideBusinessCheckbox', '隐藏商业性互动', '隐藏全站广播、PK、连MIC、连视频等', hideBusinessFunc, divGlobalSetting);
  780. hideRankListCheckbox = createSwitch('hideRankListCheckbox', '隐藏滚动排行榜', '隐藏顶部的人气榜、航海榜、礼物星球等', hideRankListFunc, divGlobalSetting);
  781. showLiveAreaCheckbox = createSwitch('showLiveArea', '显示直播分区', '显示直播间所属的直播分区', showLiveAreaFunc, divGlobalSetting);
  782. /* ----------------------------------------- global setting ----------------------------------------- */
  783.  
  784. /* ----------------------------------------- operation msg ----------------------------------------- */
  785. spanApplyMsg = document.createElement('span');
  786. spanApplyMsg.classList.add('danmu-random-setting-success-text');
  787.  
  788. let divApplyMsg = document.createElement('div');
  789. divApplyMsg.classList.add('danmu-random-setting-success-tips');
  790. divApplyMsg.appendChild(spanApplyMsg);
  791. /* ----------------------------------------- operation msg ----------------------------------------- */
  792. /* ----------------------------------------- clean cache ----------------------------------------- */
  793. let cleanCacheBtn = document.createElement('button');
  794. cleanCacheBtn.style.setProperty('display', 'none');
  795. cleanCacheBtn.textContent = '清除缓存';
  796. cleanCacheBtn.classList.add('clean-cache-btn');
  797. cleanCacheBtn.addEventListener('click', cleanCache);
  798. /* ----------------------------------------- clean cache ----------------------------------------- */
  799.  
  800. /* ----------------------------------------- save and close button ----------------------------------------- */
  801. let btnSave = document.createElement('i');
  802. btnSave.setAttribute('title', '保存');
  803. btnSave.style.padding = '5px';
  804. btnSave.classList.add('el-button');
  805. btnSave.classList.add('el-icon-check');
  806. btnSave.classList.add('is-circle');
  807. btnSave.addEventListener('click', save);
  808.  
  809. let btnClose = document.createElement('i');
  810. btnClose.setAttribute('title', '关闭');
  811. btnClose.style.padding = '5px';
  812. btnClose.classList.add('el-button');
  813. btnClose.classList.add('el-icon-close');
  814. btnClose.classList.add('is-circle');
  815. btnClose.addEventListener('click', closeSetting);
  816.  
  817. let divBtnSetting = document.createElement('div');
  818. divBtnSetting.classList.add('danmu-random-set-button-container');
  819. divBtnSetting.appendChild(cleanCacheBtn);
  820. divBtnSetting.appendChild(btnSave);
  821. divBtnSetting.appendChild(btnClose);
  822. /* ----------------------------------------- save and close button ----------------------------------------- */
  823.  
  824. /* ----------------------------------------- container ----------------------------------------- */
  825. let divBottomContainer = document.createElement('div');
  826. divBottomContainer.classList.add('danmu-random-setting-bottom');
  827. divBottomContainer.appendChild(divApplyMsg);
  828. divBottomContainer.appendChild(divBtnSetting);
  829.  
  830. let divOtherContainer = document.createElement('div');
  831. divOtherContainer.id = 'otherContainer';
  832.  
  833. let divContainer = document.createElement('div');
  834. divContainer.style.height = 'calc(98% - 30px - 25px)';
  835. divContainer.appendChild(divRoomSetting);
  836. divContainer.appendChild(divGlobalSetting);
  837. divContainer.appendChild(divOtherContainer);
  838. divContainer.appendChild(divBottomContainer);
  839. /* ----------------------------------------- container ----------------------------------------- */
  840.  
  841. divSetting = document.createElement('div');
  842. divSetting.id = 'danmu-setting-panel';
  843. divSetting.classList.add('danmu-random-setting-panel');
  844. divSetting.appendChild(divSettingTitle);
  845. divSetting.appendChild(divUpdateInfo);
  846. divSetting.appendChild(divTip);
  847. divSetting.appendChild(divContainer);
  848.  
  849. let asideAreaVm = document.getElementById('aside-area-vm');
  850. asideAreaVm.appendChild(divSetting);
  851.  
  852. },
  853. btnStart = btn => {
  854. const title = '开始定时发送';
  855. btnStartText = document.createElement('span');
  856. btnStartText.textContent = '开始';
  857. btnStartText.title = title;
  858. btnStartText.classList.add('txt');
  859.  
  860. dmButtonSend = document.createElement('button');
  861. dmButtonSend.title = title;
  862. //dmButtonSend.onclick = function() { alert('Hello world');}
  863. dmButtonSend.addEventListener('click', offOrOn);
  864. dmButtonSend.appendChild(btnStartText);
  865. if (btn) {
  866. copyAttributes(btn, dmButtonSend);
  867. const span = btn.querySelector('span');
  868. if (span) {
  869. copyAttributes(span, btnStartText);
  870. }
  871. }
  872. else {
  873. dmButtonSend.classList.add('danmu-btn');
  874. dmButtonSend.style.setProperty('--color', 'rgba(217,157,27,0.8)');
  875. }
  876. },
  877. btnSetting = () => {
  878. const title = '定时设置', divEmo = document.getElementsByClassName('emoticons-panel border-box')[0];
  879. let iElement = document.createElement('i');
  880. iElement.classList.add('el-icon-setting');
  881.  
  882. let btn = document.createElement('button');
  883. btn.title = title;
  884. btn.classList.add('el-button');
  885. btn.classList.add('el-button--mini');
  886. btn.classList.add('is-circle');
  887. btn.addEventListener('click', openSetting);
  888. btn.appendChild(iElement);
  889. if (divEmo) {
  890. let div = document.createElement('div');
  891. copyAttributes(divEmo, div);
  892. div.title = title;
  893. div.style.backgroundImage = 'none';
  894. div.style.position = 'absolute';
  895. div.style.right = '60px';
  896. div.style.bottom = '2px';
  897. div.appendChild(btn);
  898.  
  899. divEmo.style.position = 'absolute';
  900. divEmo.style.right = '60px';
  901. divEmo.style.top = '4px';
  902. return div;
  903. }
  904. else {
  905. btn.style.margin = '0 5px';
  906. return btn;
  907. }
  908. },
  909. copyAttributes = (oldNode, newNode) => {
  910. Array.prototype.forEach.call(oldNode.attributes, (item, index) => newNode.setAttribute(item.name, item.value));
  911. },
  912. setDisplay = (dom, property, important) => {
  913. if (important) {
  914. dom.style.setProperty('display', property, 'important');
  915. }
  916. else {
  917. dom.style.setProperty('display', property);
  918. }
  919. },
  920. blockDisplay = (dom, shwon) => {
  921. if (shwon) {
  922. setDisplay(dom, 'block', true);
  923. }
  924. else {
  925. dom.style.removeProperty('display');
  926. }
  927. },
  928. noneDisplay = (dom, hidden) => {
  929. if (hidden) {
  930. setDisplay(dom, 'none', true);
  931. }
  932. else {
  933. dom.style.removeProperty('display');
  934. }
  935. },
  936. removeAttributeFromChildren = (dom, attr) => {
  937. dom.removeAttribute(attr);
  938. if (0 < dom.children.length) {
  939. for (let i = 0; i < dom.children.length; i++) {
  940. removeAttributeFromChildren(dom.children[i], attr);
  941. }
  942. }
  943. },
  944. setAttributeToChildren = (dom, attr, val) => {
  945. dom.setAttribute(attr, val);
  946. if (0 < dom.children.length) {
  947. for (let i = 0; i < dom.children.length; i++) {
  948. setAttributeToChildren(dom.children[i], attr, val);
  949. }
  950. }
  951. },
  952. hideLoginGuideFunc = () => {
  953. let dom = document.getElementById('switch-login-guide-vm');
  954. if (dom) {
  955. noneDisplay(dom, hideLoginGuideCheckbox.checked);
  956. }
  957. },
  958. hideHarunaFunc = () => {
  959. let dom = document.getElementById('my-dear-haruna-vm');
  960. if (dom) {
  961. noneDisplay(dom, hideHarunaCheckbox.checked);
  962. }
  963. },
  964. hideShopFunc = () => {
  965. let dom = document.getElementById('shop-popover-vm');
  966. if (dom) {
  967. noneDisplay(dom, hideShopCheckbox.checked);
  968. }
  969. },
  970. hideGiftControlFunc = () => {
  971. let dom = document.getElementsByClassName('gift-control-section')[0];
  972. if (dom) {
  973. noneDisplay(dom, hideGiftControlCheckbox.checked);
  974. }
  975.  
  976. dom = document.getElementById('web-player__bottom-bar__container');
  977. if (dom) {
  978. noneDisplay(dom, hideGiftControlCheckbox.checked);
  979. }
  980.  
  981. dom = document.getElementsByTagName('video');
  982. for (let i = 0; i < dom.length; i++) {
  983. if (!dom[i]) {
  984. return;
  985. }
  986. if (hideGiftControlCheckbox.checked) {
  987. dom[i].style.setProperty('height', '100%');
  988. }
  989. else if (document.body.classList.contains('player-full-win') || document.body.classList.contains('fullscreen-fix')) {
  990. dom[i].style.setProperty('height', 'calc(100% - 114px)');
  991. }
  992. }
  993. },
  994. hideRoomFeedFunc = () => {
  995. let dom = document.getElementsByClassName('room-feed')[0];
  996. if (dom) {
  997. noneDisplay(dom, hideRoomFeedCheckbox.checked);
  998. }
  999. dom = document.getElementsByClassName('flip-view p-relative')[0];
  1000. if (dom) {
  1001. noneDisplay(dom, hideRoomFeedCheckbox.checked);
  1002. }
  1003. },
  1004. hideRoomInfoFunc = () => {
  1005. let dom = document.getElementsByClassName('room-info-ctnr')[0];
  1006. if (dom) {
  1007. noneDisplay(dom, hideRoomInfoCheckbox.checked);
  1008. }
  1009. },
  1010. hideNoticeFunc = () => {
  1011. let dom = document.getElementsByClassName('right-container')[0];
  1012. if (dom) {
  1013. dom.style.setProperty('min-height', 'auto');
  1014. noneDisplay(dom, hideNoticeCheckbox.checked);
  1015. }
  1016. },
  1017. hideFooterFunc = () => {
  1018. let dom = document.getElementById('link-footer-vm');
  1019. if (dom) {
  1020. noneDisplay(dom, hideFooterCheckbox.checked);
  1021. }
  1022. },
  1023. hidePrivacyFunc = () => {
  1024. if (hidePrivacyCheckbox.checked) {
  1025. let dom = document.createElement('style');
  1026. dom.id = 'hidePrivacyDialog';
  1027. dom.setAttribute('type', 'text/css');
  1028. dom.innerHTML = '.privacy-dialog{display:none !important;}';
  1029. document.head.appendChild(dom);
  1030. }
  1031. else {
  1032. let dom = document.getElementById('hidePrivacyDialog');
  1033. if (dom) {
  1034. dom.remove();
  1035. }
  1036. }
  1037. },
  1038. hideRoomStatusFunc = () => {
  1039. let dom = document.getElementsByClassName('web-player-icon-roomStatus')[0];
  1040. if (dom) {
  1041. noneDisplay(dom, hideRoomStatusCheckbox.checked);
  1042. }
  1043. },
  1044. hideBusinessFunc = () => {
  1045. let dom = document.getElementById('businessContainerElement');
  1046. if (dom) {
  1047. noneDisplay(dom, hideBusinessCheckbox.checked);
  1048. }
  1049. },
  1050. // 适配Bilibili直播自动追帧样式
  1051. adaptBililiveSeeker = () => {
  1052. let dom = document.getElementById('playback-rate-title');
  1053. if (dom) {
  1054. noneDisplay(dom, true);
  1055. dom.parentElement.style.removeProperty('padding-bottom');
  1056. }
  1057.  
  1058. dom = document.getElementById('playback-rate-username');
  1059. if (dom) {
  1060. dom.style.removeProperty('display');
  1061. }
  1062. },
  1063. hideRankListFunc = () => {
  1064. let lower = document.getElementsByClassName('lower-row')[0];
  1065. if (!lower) {
  1066. return;
  1067. }
  1068.  
  1069. let dom = lower.getElementsByClassName('right-ctnr')[0]
  1070. if (dom) {
  1071. noneDisplay(dom, hideRankListCheckbox.checked);
  1072. let t = workerTimer.setTimeout(() => {
  1073. workerTimer.clearTimeout(t);
  1074. t = null;
  1075. adaptBililiveSeeker();
  1076. }, 100);
  1077. }
  1078. },
  1079. showLiveAreaFunc = () => {
  1080. let dom = document.getElementsByClassName('live-area')[0];
  1081. if (dom) {
  1082. blockDisplay(dom, showLiveAreaCheckbox.checked);
  1083. adaptBililiveSeeker();
  1084. }
  1085. },
  1086. lotteryFunc = () => {
  1087. window.localStorage.setItem(lotteryChecked, lotteryCheckbox.checked);
  1088. let dom = document.getElementById('hesitationCheckboxDiv');
  1089. if (dom) {
  1090. noneDisplay(dom, !lotteryCheckbox.checked)
  1091. }
  1092. },
  1093. closeLotteryFunc = () => {
  1094. window.localStorage.setItem(closeLotteryChecked, closeLotteryCheckbox.checked);
  1095. },
  1096. hesitationFunc = () => {
  1097. window.localStorage.setItem(hesitationChecked, hesitationCheckbox.checked);
  1098. window.localStorage.setItem(hesitationExpiry, hesitateInput.value);
  1099. },
  1100. setPublicFunc = () => {},
  1101. usePublicFunc = () => {
  1102. let obj = null;
  1103. if (usePublicCheckbox.checked) {
  1104. obj = getGmValue(baseInfo.default, null);
  1105. }
  1106. else {
  1107. obj = source;
  1108. }
  1109. if (obj) {
  1110. setSource(obj);
  1111. }
  1112. },
  1113. clickLikeBtn = () => {
  1114. let dom = document.getElementsByClassName('like-btn')[0];
  1115. if (dom) {
  1116. dom.click();
  1117. }
  1118. },
  1119. autoLikeFunc = () => {
  1120. if (autoLikeCheckbox.checked) {
  1121. if (autoLikeTimer) {
  1122. return;
  1123. }
  1124.  
  1125. clickLikeBtn();
  1126. // console.log(`===> 开启自动点赞功能【${new Date().toLocaleString()}】`);
  1127. autoLikeTimer = workerTimer.setInterval(() => {
  1128. let rdTimeout = Math.floor(Math.random() * 15000);
  1129. if (1000 > rdTimeout) {
  1130. // console.log(`===> 【${new Date().toLocaleString()}】立刻点击点赞按钮`);
  1131. clickLikeBtn();
  1132. }
  1133. else {
  1134. // console.log(`===> 【${new Date().toLocaleString()}】设置【${rdTimeout}】毫秒后点击点赞按钮`);
  1135. clickLikeBtnTimer = workerTimer.setTimeout(() => {
  1136. workerTimer.clearTimeout(clickLikeBtnTimer);
  1137. clickLikeBtnTimer = null;
  1138. clickLikeBtn();
  1139. }, rdTimeout);
  1140. }
  1141. }, 15e3);
  1142. }
  1143. else {
  1144. if (autoLikeTimer) {
  1145. workerTimer.clearInterval(autoLikeTimer);
  1146. autoLikeTimer = null;
  1147. // console.log(`===> 关闭自动点赞功能`);
  1148. }
  1149. if (clickLikeBtnTimer) {
  1150. workerTimer.clearTimeout(clickLikeBtnTimer);
  1151. clickLikeBtnTimer = null;
  1152. // console.log(`===> 关闭超时点击点赞按钮功能`);
  1153. }
  1154. }
  1155. },
  1156. clickPlay = () => {},
  1157. autoPlay = () => {
  1158. let t = workerTimer.setTimeout(() => {
  1159. workerTimer.clearTimeout(t);
  1160. clickPlay();
  1161. t = workerTimer.setTimeout(() => {
  1162. workerTimer.clearTimeout(t);
  1163. t = null;
  1164. clickPlay();
  1165. }, 2e3);
  1166. }, 3e3);
  1167. },
  1168. noSleepFunc = () => {
  1169. if (noSleepCheckbox.checked) {
  1170. if (!noSleepTimer) {
  1171. console.log('===> 开启防休眠功能');
  1172. noSleepTimer = workerTimer.setInterval(() => {
  1173. noSleepTimeouter = workerTimer.setTimeout(() => {
  1174. workerTimer.clearTimeout(noSleepTimeouter);
  1175. document.body.dispatchEvent(new MouseEvent("mousemove", { bubbles: true }));
  1176. }, Math.random() * 3e3);
  1177. }, 17e3);
  1178. }
  1179. } else {
  1180. console.log('===> 关闭防休眠功能');
  1181. if (noSleepTimer) {
  1182. workerTimer.clearInterval(noSleepTimer);
  1183. noSleepTimer = null;
  1184. }
  1185. if (noSleepTimeouter) {
  1186. workerTimer.clearTimeout(noSleepTimeouter);
  1187. noSleepTimeouter = null;
  1188. }
  1189. }
  1190. },
  1191. biliMiniClose = () => {
  1192. if (!miniCloseTimer) {
  1193. let miniCloseCount = 3;
  1194. miniCloseTimer = workerTimer.setInterval(() => {
  1195. let mini_close = document.getElementsByClassName('bili-mini-close')[0];
  1196. if (!mini_close) {
  1197. if (0 >= --miniCloseCount) {
  1198. workerTimer.clearInterval(miniCloseTimer);
  1199. miniCloseTimer = null;
  1200. }
  1201.  
  1202. return;
  1203. }
  1204.  
  1205. mini_close.click();
  1206. workerTimer.clearInterval(miniCloseTimer);
  1207. miniCloseTimer = null;
  1208. }, 10e3);
  1209. }
  1210. },
  1211. closeLottery = delay => {
  1212. let btnClose = document.getElementsByClassName('close-btn bg-contain')[0];
  1213. if (btnClose) {
  1214. if (isNull(delay)) {
  1215. btnClose.click();
  1216. } else {
  1217. closeLotteryTimer = workerTimer.setTimeout(() => {
  1218. workerTimer.clearTimeout(closeLotteryTimer);
  1219. btnClose.click();
  1220. }, delay * 1000);
  1221. }
  1222. }
  1223. },
  1224. lottery = btn => {
  1225. if (!btn) {
  1226. console.warn('===> 没有抽奖按钮DOM');
  1227. return;
  1228. }
  1229. if ('false' === window.localStorage.getItem(lotteryChecked)) {
  1230. if ('true' === window.localStorage.getItem(closeLotteryChecked)) {
  1231. console.log('===> 不参与天选时刻抽奖,关闭弹窗');
  1232. closeLottery();
  1233. }
  1234.  
  1235. return;
  1236. }
  1237. if ('true' === window.localStorage.getItem(hesitationChecked)) {
  1238. let expiry = window.localStorage.getItem(hesitationExpiry);
  1239. expiry = isNull(expiry) || 0 >= expiry ? 10e4 : expiry * 1000;
  1240. expiry = Math.ceil(Math.random() * expiry);
  1241. // console.log(`===> 【${new Date().toLocaleString()}】随机时间【${expiry}】`);
  1242. let lotteryTimer = workerTimer.setTimeout(() => {
  1243. workerTimer.clearTimeout(lotteryTimer);
  1244. console.log(`===> ${new Date().toLocaleString()}】犹豫期过后自动参加抽奖`);
  1245. btn.click();
  1246. if ('true' === window.localStorage.getItem(closeLotteryChecked)) {
  1247. console.log('===> 参加成功,延迟关闭弹窗');
  1248. closeLottery(3);
  1249. }
  1250. }, expiry);
  1251. } else {
  1252. console.log('===> 立刻自动参加抽奖');
  1253. btn.click();
  1254. if ('true' === window.localStorage.getItem(closeLotteryChecked)) {
  1255. console.log('===> 参加成功,延迟关闭弹窗');
  1256. closeLottery(3);
  1257. }
  1258. }
  1259. },
  1260. listPlus = nodes => {
  1261. if (!nodes || 0 >= nodes.length) return;
  1262. Array.prototype.forEach.call(nodes, y => {
  1263. let aNode = y.firstChild;
  1264. if (!aNode) return;
  1265. let a_d2 = aNode.children[1];
  1266. if (!a_d2) return;
  1267. let a_d2_d2 = a_d2.children[1];
  1268. if (!a_d2_d2) return;
  1269. let a_d2_d2_d2 = a_d2_d2.children[1];
  1270. if (!a_d2_d2_d2) return;
  1271. let a_d2_d2_d2_d1 = a_d2_d2_d2.children[0];
  1272. if (!a_d2_d2_d2_d1) return;
  1273. aNode.title = a_d2_d2_d2_d1.textContent;
  1274. let a_d2_d1 = a_d2.children[0];
  1275. if (a_d2_d1) {
  1276. let a_d2_d1_last = a_d2_d1.lastChild;
  1277. if (a_d2_d1_last && a_d2_d1_last.style.display && 'none' == a_d2_d1_last.style.display) {
  1278. a_d2_d1_last.remove();
  1279. }
  1280. }
  1281. });
  1282. },
  1283. getConfig = () => {
  1284. let c = {};
  1285. if (baseInfo.config) {
  1286. c = getGmValue(baseInfo.config, {});
  1287. if (isNull(c.lottery)) {
  1288. c.lottery = false;
  1289. }
  1290. if (isNull(c.closeLottery)) {
  1291. c.closeLottery = false;
  1292. }
  1293. if (isNull(c.hesitation)) {
  1294. c.hesitation = false;
  1295. }
  1296. }
  1297.  
  1298. return c;
  1299. },
  1300. checkVersion = () => {
  1301. config = getConfig();
  1302. if (!config || !config.moduleVersion || !divUpdateInfo) {
  1303. return;
  1304. }
  1305. if (0 < compareVersion(config.moduleVersion, version)) {
  1306. divUpdateInfo.innerHTML = `<span class="update-info-text">有新版本,刷新网页进行更新</span>`;
  1307. let dom = document.getElementById('otherContainer');
  1308. if (dom) {
  1309. dom.style.fontSize = '18px';
  1310. dom.style.textAlign = 'center';
  1311. dom.appendChild(divUpdateInfo.cloneNode(true));
  1312. }
  1313. }
  1314. },
  1315. loadData = () => {
  1316. let obj = getGmValue(roomId, null),
  1317. key = roomId;
  1318. if (obj) {
  1319. if (obj.usePublic && baseInfo.default) {
  1320. let ps = getGmValue(baseInfo.default, null);
  1321. if (ps) {
  1322. obj.data1.values = ps.data1 ? ps.data1.values : obj.data1.values;
  1323. obj.data2.values = ps.data2 ? ps.data2.values : obj.data2.values;
  1324. obj.data3.values = ps.data3 ? ps.data3.values : obj.data3.values;
  1325. obj.data4.values = ps.data4 ? ps.data4.values : obj.data4.values;
  1326. obj.data5.values = ps.data5 ? ps.data5.values : obj.data5.values;
  1327. }
  1328. }
  1329. if (source.version === obj.version) {
  1330. source = {...source, ...obj};
  1331. }
  1332. else if (obj.version === 2) {
  1333. source.data1 = obj.data1;
  1334. source.data2 = obj.data2;
  1335. source.data3 = obj.data3;
  1336. source.data4 = obj.data4;
  1337. source.data5 = obj.data5;
  1338. setGmValue(key, source);
  1339. }
  1340. else {
  1341. source.data1.values = obj.data1 ? obj.data1.values : source.data1.values;
  1342. source.data2.values = obj.data2 ? obj.data2.values : source.data2.values;
  1343. source.data3.values = obj.data3 ? obj.data3.values : source.data3.values;
  1344. source.data4.values = obj.data4 ? obj.data4.values : source.data4.values;
  1345. source.data5.values = obj.data5 ? obj.data5.values : source.data5.values;
  1346. setGmValue(key, source);
  1347. }
  1348. }
  1349. config = getConfig();
  1350. signInput.value = isNull(config.signInText) ? '' : config.signInText;
  1351. hesitateInput.value = isNull(config.hesitationExpiry) ? '100' : config.hesitationExpiry;
  1352. setSource(source);
  1353. setOperationSetting();
  1354. initData();
  1355. },
  1356. closeWsHeartBeatTimer = () => workerTimer.clearInterval(wsHeartBeatTimer),
  1357. closeWsReconnectTimer = () => {
  1358. workerTimer.clearInterval(wsReconnectWsTimer);
  1359. wsReconnectWsTimer = null;
  1360. },
  1361. /* param
  1362. j 字符串
  1363. v 协议版本(
  1364. 0: 普通包 (正文不使用压缩)
  1365. 1: 心跳及认证包 (正文不使用压缩)
  1366. 2: 普通包 (正文使用 zlib 压缩)
  1367. 3: 普通包 (使用 brotli 压缩的多个带文件头的普通包)
  1368. t 操作码(封包类型)
  1369. 2: 心跳包
  1370. 3: 心跳包回复 (人气值)
  1371. 5: 普通包 (命令)
  1372. 7: 认证包
  1373. 8: 认证包回复
  1374. */
  1375. wsPacket = (j, v, t) => {
  1376. const en = textEncoder.encode(j),
  1377. headLen = 16,
  1378. packetLen = en.byteLength + headLen,
  1379. buff = new ArrayBuffer(packetLen),
  1380. view = new DataView(buff);
  1381. view.setUint32(0, packetLen);
  1382. view.setUint16(4, headLen);
  1383. view.setUint16(6, v);
  1384. view.setUint32(8, t);
  1385. view.setUint32(12, 1);
  1386. for(let i = 0; i < en.byteLength; i++) {
  1387. view.setUint8(16 + i, en[i]);
  1388. }
  1389.  
  1390. return buff;
  1391. },
  1392. wsUnpaket = (blob, callback) => {
  1393. const reader = new FileReader();
  1394. reader.onload = async e => {
  1395. const aBuff = e.target.result, //ArrayBuffer对象
  1396. abuffView = new DataView(aBuff); //视图
  1397. let offset = 0, result = [];
  1398. while(offset < aBuff.byteLength) { //数据提取
  1399. let decompressedData;
  1400. const packetLen = abuffView.getUint32(offset + 0),
  1401. headLen = abuffView.getUint16(offset + 4),
  1402. packetVer = abuffView.getUint16(offset + 6),
  1403. packetType = abuffView.getUint32(offset + 8),
  1404. seq = abuffView.getUint32(12);
  1405. switch(packetVer) { //解压数据
  1406. case 3: //brotli压缩
  1407. const brotli = new Uint8Array(aBuff, offset + headLen, packetLen - headLen);
  1408. try {
  1409. decompressedData = await decompressBrotli(brotli);
  1410. } catch(error) {
  1411. console.error(`===> decompressBrotli解压出错`, error);
  1412. decompressedData = brotli;
  1413. }
  1414. transferData(decompressedData, result);
  1415. break;
  1416. case 2: //zlib压缩
  1417. const zlib = new Uint8Array(aBuff, offset + headLen, packetLen - headLen);
  1418. try {
  1419. decompressedData = pako.inflate(zlib);
  1420. } catch(error) {
  1421. console.error(`===> pako.inflate解压出错`, error);
  1422. decompressedData = zlib;
  1423. }
  1424.  
  1425. transferData(decompressedData, result);
  1426. break;
  1427. default:
  1428. const packet = {};
  1429. packet.plen = packetLen;
  1430. packet.hlen = headLen;
  1431. packet.ver = packetVer;
  1432. packet.type = packetType;
  1433. packet.seq = seq;
  1434. if (packetType == 3){ //获取人气值
  1435. packet.body = (new DataView(aBuff, offset + headLen, packetLen - headLen)).getUint32(0); //若入参为dataArray.buffer,会返回整段buff的视图,而不是截取后的视图
  1436. }else{
  1437. const dataArray = new Uint8Array(aBuff, offset + headLen, packetLen - headLen);
  1438. packet.body = textDecoder.decode(dataArray); //utf-8格式数据解码,获得字符串
  1439. }
  1440.  
  1441. result.push(packet); //数据打包后传入数组
  1442. }
  1443.  
  1444. offset += packetLen;
  1445. }
  1446.  
  1447. callback(result); //数据后续处理
  1448. };
  1449.  
  1450. reader.readAsArrayBuffer(blob); //读取服务器传来的数据转换为ArrayBuffer
  1451. },
  1452. transferData = (arrayBuff, result) => {
  1453. if(!arrayBuff) {
  1454. return;
  1455. }
  1456. let offset = 0;
  1457. const buffView = new DataView(arrayBuff);
  1458. while(offset < arrayBuff.byteLength){ //解压后数据提取
  1459. const packetLen = buffView.getUint32(offset + 0),
  1460. headLen = buffView.getUint16(offset + 4),
  1461. packet = {};
  1462. packet.plen = packetLen;
  1463. packet.hlen = headLen;
  1464. packet.ver = buffView.getUint16(offset + 6);
  1465. packet.type = buffView.getUint32(offset + 8);
  1466. packet.seq = buffView.getUint32(12);
  1467. const dataArray = new Uint8Array(arrayBuff, offset + headLen, packetLen - headLen);
  1468. packet.body = textDecoder.decode(dataArray); //utf-8格式数据解码,获得字符串
  1469. result.push(packet); //数据打包后传入数组
  1470. offset += packetLen;
  1471. }
  1472. },
  1473. handleWsPacket = list => {
  1474. if (!list || 1 > list.length) {
  1475. return;
  1476. }
  1477.  
  1478. list.forEach(packet => {
  1479. switch (packet.type) {
  1480. // 认证包回复
  1481. case 8:
  1482. eventTarget.dispatchEvent(new CustomEvent(eventTypeEnum.authReply, {detail: JSON.parse(packet.body)}));
  1483. break;
  1484. // 心跳包回复(人气值)
  1485. case 3:
  1486. eventTarget.dispatchEvent(new CustomEvent(eventTypeEnum.heartBeatReply, {detail: packet.body}));
  1487. break;
  1488. // 普通包(命令)
  1489. case 5:
  1490. const msg = JSON.parse(packet.body);
  1491. eventTarget.dispatchEvent(new CustomEvent(msg.cmd, {detail: msg}));
  1492. if (!wsCmds.includes(msg.cmd)) {
  1493. wsCmds.push(msg.cmd);
  1494. }
  1495. break;
  1496. default:
  1497. console.log(`===> 没对应的操作码【${JSON.stringify(packet)}】`);
  1498. }
  1499. });
  1500. },
  1501. bingWsMsgEvent = () => {
  1502. // 认证包回复
  1503. onEvent(eventTypeEnum.authReply, d => {
  1504. if (0 == d.code) {
  1505. closeWsReconnectTimer();
  1506. console.log(`===> 成功加入房间`);
  1507. }
  1508. else {
  1509. console.warn(`===> 认证失败`);
  1510. }
  1511. });
  1512. // 心跳包回复(人气值)
  1513. onEvent(eventTypeEnum.heartBeatReply, d => {
  1514. closeWsReconnectTimer();
  1515. console.log(`===> 人气:${d}`);
  1516. });
  1517. // 弹幕消息
  1518. onEvent(eventTypeEnum.danmuMsg, d => {
  1519. console.log(`===> ${d.info[2][1]}: ${d.info[1]}`);
  1520. });
  1521. // 主播准备中(0 未轮播;1 正在轮播)
  1522. onEvent(eventTypeEnum.preparing, d => {
  1523. console.log(`===> 直播间【${d.roomid}】状态【${d.round}】`);
  1524. });
  1525. // 直播开始
  1526. onEvent(eventTypeEnum.live, d => {
  1527. console.log(`===> 直播间【${d.roomid}】在【${new Date(d.live_time).toLocaleString()}】开始直播`);
  1528. });
  1529. // 直播间信息更改
  1530. onEvent(eventTypeEnum.roomChange, e => {
  1531. console.log(`===> 直播信息变更,标题【${d.data.title}】,分区【${d.data.area_name}】,上级分区【${d.data.parent_area_name}】`);
  1532. });
  1533. // 下播的直播间
  1534. onEvent(eventTypeEnum.stopLiveRoomList, d => {
  1535. console.log(`===> 当前直播间是否下播【${d.data.room_id_list.includes(roomId)}】`);
  1536. });
  1537. // 切断
  1538. onEvent(eventTypeEnum.cutOff, d => {
  1539. console.log(`===> 直播间【${d.roomid}】已被切断【${d.msg}】`);
  1540. });
  1541. // 天选时刻开始
  1542. onEvent(eventTypeEnum.anchorLotStart, d => {
  1543. console.log(`===> 直播间【${d.data.room_id}】在【${new Date(d.data.current_time).toLocaleDateString()}】开始天选时刻抽奖,id${d.data.id};max_time${d.data.max_time};danmu${d.data.danmu};time${d.data.time}`);
  1544. });
  1545. // 天选时刻结束
  1546. onEvent(eventTypeEnum.anchorLotEnd, d => {
  1547. console.log(`===> 天选时刻结束【${JSON.stringify(d)}】`);
  1548. });
  1549. // 重连直播间
  1550. onEvent(eventTypeEnum.reenterLiveRoom, d => {
  1551. console.log(`===> 重进直播间【${JSON.stringify(d)}】`);
  1552. });
  1553. },
  1554. getWsInfo = async () => {
  1555. console.log(`===> 开始获取WS info`);
  1556. const resp = await fetch(baseInfo.ws.replace('#rid#', roomId), {method: 'GET', cache: 'no-store', credentials: 'include'});
  1557. if (!resp.ok) {
  1558. console.warn(`===> WS host信息请求不成功`);
  1559. return;
  1560. }
  1561.  
  1562. const obj = await resp.json();
  1563. if (0 !== obj?.code) {
  1564. console.warn(`===> 获取WS host失败【${obj?.message}】`);
  1565. return;
  1566. }
  1567. if (!obj?.data?.host_list || 1 > obj?.data?.host_list.length) {
  1568. console.warn(`===> WS host信息为空`);
  1569. return;
  1570. }
  1571.  
  1572. wsConnectCount = 30;
  1573. wsToken = obj.data.token || '';
  1574. wsHostList = obj.data.host_list;
  1575. console.log(`===> 获取WS info完成`);
  1576. },
  1577. connectWS = () => {
  1578. if (1 > wsHostList.length) {
  1579. console.warn(`===> WS host list为空`);
  1580. return;
  1581. }
  1582. if (wsHostIndex >= wsHostList.length) {
  1583. wsHostIndex = 0;
  1584. }
  1585. console.log(`===> 开始进行WS连接`);
  1586. ws = new WebSocket(`wss://${wsHostList[wsHostIndex].host}:${wsHostList[wsHostIndex].wss_port}/sub`);
  1587. ws.onopen = event => {
  1588. console.log(`===> WS连接成功,准备发认证包`);
  1589. const o = {
  1590. "uid": 0 < uid ? uid : ruid,
  1591. "roomid": 0 < trueRoomId ? trueRoomId : roomId,
  1592. "protover": 3,
  1593. "platform": "web",
  1594. "type": 2
  1595. },
  1596. buvid = getCookie('buvid3');
  1597. if (!isNull(buvid)) {
  1598. o.buvid = buvid;
  1599. }
  1600. if (!isNull(wsToken)) {
  1601. o.key = wsToken;
  1602. }
  1603. const json = JSON.stringify(o);
  1604. console.log(`===> 序列化认证包【${json}】`);
  1605. ws.send(wsPacket(json, 1, 7));
  1606. console.log(`===> 认证包已经发送`);
  1607. };
  1608. ws.onmessage = event => wsUnpaket(event.data, handleWsPacket);
  1609. ws.onclose = event => {
  1610. closeWsHeartBeatTimer();
  1611. ws = null;
  1612. console.warn(`===> WS连接已关闭`);
  1613. };
  1614. ws.onerror = error => console.error('===> WebSocket error: ', error);
  1615. },
  1616. heartBeatWS = () => {
  1617. wsHeartBeatTimer = workerTimer.setInterval(() => {
  1618. if (!ws) {
  1619. closeWsHeartBeatTimer();
  1620. return;
  1621. }
  1622.  
  1623. console.log(`===> 准备发送心跳包`);
  1624. ws.send(wsPacket('', 1, 2));
  1625. console.log(`===> 心跳包已经发送`);
  1626. reconnectWS();
  1627. }, 30e3);
  1628. },
  1629. initWS = async () => {
  1630. if (ws || isNull(baseInfo?.ws)) {
  1631. console.warn(`===> WS已初始化,或wsInfo为空`);
  1632. return;
  1633. }
  1634. if (1 > wsHostList.length || 0 >= wsConnectCount) {
  1635. await getWsInfo();
  1636. }
  1637. connectWS();
  1638. heartBeatWS();
  1639. },
  1640. reconnectWS = () => {
  1641. if (wsReconnectWsTimer) {
  1642. return;
  1643. }
  1644.  
  1645. console.log(`===> 启动重连定时器`);
  1646. wsReconnectWsTimer = workerTimer.setInterval(() => {
  1647. if (ws) {
  1648. console.warn(`===> 心跳包响应超时,进行WS关闭操作`);
  1649. ws.close();
  1650. }
  1651. console.log(`===> 尝试WS重连`);
  1652. ++wsHostIndex;
  1653. --wsConnectCount;
  1654. initWS();
  1655. }, 65e3);
  1656. },
  1657. initSettingPanel = (div, isLogin) => {
  1658. let settingPanel = document.getElementById('danmu-setting-panel');
  1659. if (div && !settingPanel) {
  1660. // console.log('===> 进行面板初始化');
  1661. let btnSend = document.getElementsByClassName('bl-button bl-button--primary')[0];
  1662. if (btnSend) {
  1663. buildPanel();
  1664. btnStart(btnSend);
  1665. if (isLogin) {
  1666. dmButtonSend.style.marginTop = '4px';
  1667. dmButtonSend.style.marginLeft = '30px';
  1668. btnSend.style.marginLeft = '30px';
  1669. div.appendChild(dmButtonSend);
  1670. div.parentNode.insertBefore(btnSetting(), div);
  1671. }
  1672. else {
  1673. let funcDiv = document.createElement('div');
  1674. funcDiv.style.position = 'absolute';
  1675. funcDiv.appendChild(dmButtonSend);
  1676. funcDiv.appendChild(btnSetting());
  1677. div.appendChild(funcDiv);
  1678. }
  1679. bingWsMsgEvent();
  1680. initWS();
  1681. loadData();
  1682. // console.log('===> 面板初始化完成');
  1683. } else {
  1684. console.warn('===> 发送按钮丢失');
  1685. return false;
  1686. }
  1687. }
  1688.  
  1689. return true;
  1690. },
  1691. main = (div, isLogin) => {
  1692. waiters[waiters.length] = workerTimer.setInterval(() => {
  1693. if (initSettingPanel(div, isLogin)) {
  1694. clearWaiters();
  1695. } else {
  1696. --waitCount;
  1697. if (0 >= waitCount) {
  1698. clearWaiters();
  1699. console.log('===> 创建面板失败,停止初始化');
  1700. }
  1701. }
  1702. }, 1.5e3);
  1703. },
  1704. PluginBtn = (clazz, isLogin) => {
  1705. let div = document.getElementsByClassName(clazz)[0];
  1706. if (div) {
  1707. main(div, isLogin);
  1708. } else {
  1709. let count = 0;
  1710. divSendBtnTimer = workerTimer.setInterval(() => {
  1711. div = document.getElementsByClassName(clazz)[0];
  1712. if (div) {
  1713. workerTimer.clearInterval(divSendBtnTimer);
  1714. main(div, isLogin);
  1715. } else if (count++ >= 10) {
  1716. workerTimer.clearInterval(divSendBtnTimer);
  1717. // console.log(`===> 页面【${window.location.href}】没有定位位置`);
  1718. }
  1719. }, 1e3);
  1720. }
  1721. },
  1722. PluginLogout = () => PluginBtn('bottom-actions p-relative', false),
  1723. PluginLogin = () => PluginBtn('right-actions border-box', true),
  1724. PluginLottery = () => {
  1725. let btn = document.getElementsByClassName('particitation-btn')[0];
  1726. if (btn) {
  1727. lottery(btn);
  1728. } else {
  1729. btnLotteryTimer = workerTimer.setTimeout(() => {
  1730. workerTimer.clearTimeout(btnLotteryTimer);
  1731. btn = document.getElementsByClassName('particitation-btn')[0];
  1732. if (btn) {
  1733. lottery(btn);
  1734. }
  1735. }, 2e3);
  1736. }
  1737. },
  1738. PluginListPlus = () => {
  1739. let listTimer = workerTimer.setTimeout(() => {
  1740. workerTimer.clearTimeout(listTimer);
  1741. let obsConfig = {childList: true},
  1742. tags = ['all__card-list-ctnr', 'all__special-area-recommend-list-ctnr'];
  1743. tags.forEach(x => {
  1744. let dom = document.getElementsByClassName(x)[0];
  1745. if (!dom) return;
  1746. Array.prototype.forEach.call(dom.children, y => {
  1747. if (/^index_/i.test(y.className)) {
  1748. listPlus(y.children);
  1749. let obs = new MutationObserver(mrs => {
  1750. if (!mrs || 0 >= mrs.length) return;
  1751. Array.prototype.forEach.call(mrs, z => listPlus(z.addedNodes));
  1752. });
  1753. obs.observe(y, obsConfig);
  1754. }
  1755. });
  1756. });
  1757. }, 0.5e3);
  1758. },
  1759. debug = () => {debugger;},
  1760. printWsInfos = () => {
  1761. console.log(`===> TrueRoomId: ${trueRoomId}; ShortRoomId: ${roomId}`);
  1762. console.log(`===> buvid: ${getCookie('buvid3')}`);
  1763. console.log(`==> wsConnectCount: ${wsConnectCount}`);
  1764. console.log(wsHostList);
  1765. console.info(wsCmds);
  1766. },
  1767. runWS = () => initWS(),
  1768. stopWS = () => {
  1769. closeWsReconnectTimer();
  1770. if (ws) {
  1771. ws.close();
  1772. }
  1773. },
  1774. runStart = () => {
  1775. PluginLottery();
  1776. PluginListPlus();
  1777. PluginLogout();
  1778. PluginLogin();
  1779. };
  1780.  
  1781. initCss();
  1782. initScript();
  1783.  
  1784. window.debug = debug;
  1785. window.runWS = runWS;
  1786. window.stopWS = stopWS;
  1787. window.printWsInfos = printWsInfos;
  1788.  
  1789. window.runStart = runStart;
  1790. window.arrayInfo = arrayInfo;
  1791. window.setGmNotice = setGmNotice;
  1792. window.setGmGetValue = setGmGetValue;
  1793. window.setGmSetValue = setGmSetValue;
  1794. window.setGmDelValue = setGmDelValue;
  1795. window.setBaseInfo = setBaseInfo;
  1796. window.checkVersion = checkVersion;
  1797. })();

QingJ © 2025

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