B站直播间定时发随机弹幕

定时发送;凌晨打卡;设置随机弹幕;弹幕分组管理;各直播间弹幕互不干扰;无人值守参与、关闭天选时刻;直播间防休眠;隐藏模块简化直播间

  1. // ==UserScript==
  2. // @name B站直播间定时发随机弹幕
  3. // @namespace https://live.bilibili.com/暂无主页
  4. // @home https://github.com/Gamyou/bilibili-live-random-send
  5. // @update https://greasyfork.org/scripts/446725-b%E7%AB%99%E7%9B%B4%E6%92%AD%E9%97%B4%E5%AE%9A%E6%97%B6%E5%8F%91%E9%9A%8F%E6%9C%BA%E5%BC%B9%E5%B9%95/code/B%E7%AB%99%E7%9B%B4%E6%92%AD%E9%97%B4%E5%AE%9A%E6%97%B6%E5%8F%91%E9%9A%8F%E6%9C%BA%E5%BC%B9%E5%B9%95.user.js
  6. // @download https://greasyfork.org/scripts/446725-b%E7%AB%99%E7%9B%B4%E6%92%AD%E9%97%B4%E5%AE%9A%E6%97%B6%E5%8F%91%E9%9A%8F%E6%9C%BA%E5%BC%B9%E5%B9%95/code/B%E7%AB%99%E7%9B%B4%E6%92%AD%E9%97%B4%E5%AE%9A%E6%97%B6%E5%8F%91%E9%9A%8F%E6%9C%BA%E5%BC%B9%E5%B9%95.user.js
  7. // @version 2.5.0
  8. // @description 定时发送;凌晨打卡;设置随机弹幕;弹幕分组管理;各直播间弹幕互不干扰;无人值守参与、关闭天选时刻;直播间防休眠;隐藏模块简化直播间
  9. // @author Gamyou
  10. // @match *://live.bilibili.com/*
  11. // @icon https://www.bilibili.com/favicon.ico
  12. // @license Apache License, Version 2.0
  13. // @run-at document-idle
  14. // @grant GM_info
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @grant GM_deleteValue
  18. // @grant GM_notification
  19. // @grant GM_openInTab
  20. // @grant GM_xmlhttpRequest
  21. // @connect jsdelivr.net
  22. // @connect greasyfork.org
  23. // @note 24-09-10 2.5.0 处理缓存长时间不失效导致加载不了新版本的问题
  24. // ==/UserScript==
  25.  
  26.  
  27. window.onload = (function () {
  28. const local = {
  29. "id": "RandomSendHelper",
  30. "text": "打卡",
  31. "default": "danmu",
  32. "config": "config"
  33. },
  34. infoUrl = "https://update.greasyfork.org/scripts/448354/RandomSendBaseInfo.js",
  35. frameUrl = 'https://greasyfork.org/zh-CN/scripts/446725',
  36. isNull = str => {
  37. if (!str || str == "") {
  38. return true;
  39. }
  40.  
  41. let regu = "^[ ]+$";
  42. let re = new RegExp(regu);
  43. return re.test(str);
  44. },
  45. delValue = key => GM_deleteValue(key),
  46. getValue = (key, defaultVaue) => {
  47. return GM_getValue(key, defaultVaue);
  48. },
  49. setValue = (key, obj) => {
  50. if (isNull(key)) { console.warn('key为空,保存失败'); return; }
  51. if (obj) { GM_setValue(key, obj); }
  52. },
  53. notice = (msg) => {
  54. if (isNull(msg)) { GM_notification(msg); }
  55. else { console.warn('消息对象为空,无效通知'); }
  56. },
  57. compareVersion = (v1, v2) => {
  58. if (isNull(v1)) return -1;
  59. if (isNull(v2)) return 1;
  60.  
  61. const a1 = v1.split('.').map(x => x * 1),
  62. a2 = v2.split('.').map(x => x * 1),
  63. len = Math.max(a1.length, a2.length);
  64. for (let i = 0; i < len; i++) {
  65. if ((a1[i] || 0) > (a2[i] || 0)) return 1;
  66. if ((a1[i] || 0) < (a2[i] || 0)) return -1;
  67. }
  68.  
  69. return 0;
  70. },
  71. getVersion = txt => {
  72. let m = txt.match(/@version\s+\d+\.\d+\.\d+/i);
  73. if (!m || 1 > m.length || !m[0]) {
  74. return 0;
  75. }
  76.  
  77. let n = m[0].match(/\d+\.\d+\.\d+/);
  78. if (!n || 1 > n.length || !n[0]) {
  79. return 0;
  80. }
  81.  
  82. return n[0];
  83. },
  84. getInfo = count => {
  85. count = count || 0;
  86. if (1 < count) {
  87. create();
  88. }
  89. if (8 > count) {
  90. const d = {
  91. "url": infoUrl,
  92. "method": "GET",
  93. "nocache": true,
  94. "timeout": 15e3,
  95. "responseType": "json",
  96. "ontimeout": t => {
  97. console.log(`===> 获取信息超时【${t}】`);
  98. getInfo(++count);
  99. },
  100. "onerror": e => {
  101. console.log(`===> 获取信息出错【${e}】`);
  102. getInfo(++count);
  103. },
  104. "onload": r => {
  105. if (!r || !r.response) {
  106. console.log(`===> 获取信息为空,进行重试`);
  107. getInfo(++count);
  108. return;
  109. }
  110.  
  111. let info = {...r.response, ...local};
  112. info.url = (info.url || "").replace("@latest/", `@${info.version || "latest"}/`);
  113. if (0 < compareVersion(info.frame, GM_info?.script?.version)) {
  114. alert(`定时发随机弹幕版本过低,请及时升级`);
  115. GM_openInTab(frameUrl, {active: true});
  116. return;
  117. }
  118.  
  119. updateScript(info);
  120. }
  121. };
  122. GM_xmlhttpRequest(d);
  123. }
  124. },
  125. updateScript = (info, count) => {
  126. count = count || 0;
  127. let c = getValue((info.config || local.config), {});
  128. if (!isNull(c.moduleVersion) && 0 == compareVersion(c.moduleVersion, info.version)) {
  129. create(info, c.script);
  130. return;
  131. }
  132. if (1 < count) {
  133. create(info, c.script);
  134. }
  135. if (!isNull(info.url) && 6 > count) {
  136. const d = {
  137. "url": info.url,
  138. "method": "GET",
  139. "nocache": true,
  140. "timeout": 20e3,
  141. "ontimeout": t => {
  142. console.log(`===> 获取脚本超时【${t}】`);
  143. updateScript(info, ++count);
  144. },
  145. "onerror": e => {
  146. console.log(`===> 获取脚本出错【${e}】`);
  147. updateScript(info, ++count);
  148. },
  149. "onload": r => {
  150. if (!r || !r.responseText) {
  151. console.log(`===> 获取脚本为空,进行重试`);
  152. updateScript(info, ++count);
  153. return;
  154. }
  155.  
  156. create(info, r.responseText);
  157. c.script = r.responseText;
  158. c.moduleVersion = info.version;
  159. c.lastUpdate = new Date().toLocaleString();
  160. setValue(info.config, c);
  161. checkVersion();
  162. }
  163. };
  164. GM_xmlhttpRequest(d);
  165. }
  166. },
  167. create = (info, txt) => {
  168. if (!info) info = local;
  169. let script = document.getElementById(info.id);
  170. if (!script) {
  171. if (isNull(txt)) {
  172. let config = getValue((info.config), null);
  173. if (!config || !config.script) {
  174. return;
  175. }
  176.  
  177. txt = config.script;
  178. }
  179.  
  180. script = document.createElement('script');
  181. script.id = info.id;
  182. script.type = 'text/javascript';
  183. script.text = txt;
  184. document.head.appendChild(script);
  185. init(info);
  186. }
  187. },
  188. init = info => {
  189. setGmNotice(notice);
  190. setGmGetValue(getValue);
  191. setGmSetValue(setValue);
  192. setGmDelValue(delValue);
  193. setBaseInfo(info);
  194. runStart();
  195. };
  196.  
  197. getInfo();
  198. })();