雨天的自动回复机器人

获取bilibili的直播弹幕内容并自动发送回复弹幕

  1. // ==UserScript==
  2. // @name 雨天的自动回复机器人
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.1
  5. // @description 获取bilibili的直播弹幕内容并自动发送回复弹幕
  6. // @author 雨天lul
  7. // @include /https?:\/\/live\.bilibili\.com\/?\??.*/
  8. // @include /https?:\/\/live\.bilibili\.com\/\d+\??.*/
  9. // @include /https?:\/\/live\.bilibili\.com\/(blanc\/)?\d+\??.*/
  10. // @run-at document-start
  11. // @require https://cdn.jsdelivr.net/gh/google/brotli@5692e422da6af1e991f9182345d58df87866bc5e/js/decode.js
  12. // @require https://cdn.jsdelivr.net/npm/pako@2.0.3/dist/pako_inflate.min.js
  13.  
  14. // @grant none
  15.  
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18. // @grant GM_deleteValue
  19. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
  20. // @license MIT
  21. // ==/UserScript==
  22.  
  23. //填入自己的id,检测弹幕的时候过滤掉自己发送的弹幕
  24. var selfId = '4955333';
  25. var autoReply = false;
  26.  
  27. //cgd
  28. (function () {
  29. console.log('autoreply st')
  30. const HEADER_SIZE = 16
  31.  
  32. const WS_BODY_PROTOCOL_VERSION_NORMAL = 0
  33. const WS_BODY_PROTOCOL_VERSION_HEARTBEAT = 1
  34. const WS_BODY_PROTOCOL_VERSION_DEFLATE = 2
  35. const WS_BODY_PROTOCOL_VERSION_BROTLI = 3
  36.  
  37. const OP_HEARTBEAT_REPLY = 3
  38. const OP_SEND_MSG_REPLY = 5
  39.  
  40. let textEncoder = new TextEncoder()
  41. let textDecoder = new TextDecoder()
  42.  
  43. function main() {
  44. if (window.bliveproxy) {
  45. // 防止多次加载
  46. return
  47. }
  48. initApi()
  49. hook()
  50. }
  51.  
  52. function initApi() {
  53. window.bliveproxy = api
  54. }
  55.  
  56. let api = {
  57. addCommandHandler(cmd, handler) {
  58. let handlers = this._commandHandlers[cmd]
  59. if (!handlers) {
  60. handlers = this._commandHandlers[cmd] = []
  61. }
  62. handlers.push(handler)
  63. },
  64. removeCommandHandler(cmd, handler) {
  65. let handlers = this._commandHandlers[cmd]
  66. if (!handlers) {
  67. return
  68. }
  69. this._commandHandlers[cmd] = handlers.filter(item => item !== handler)
  70. },
  71.  
  72. // 私有API
  73. _commandHandlers: {},
  74. _getCommandHandlers(cmd) {
  75. // if (this._commandHandlers[cmd] != null) {
  76. // console.log('HAND EXT ' + cmd);
  77. // } else {
  78. // console.log('HAND NE! ' + cmd);
  79. // }
  80. return this._commandHandlers[cmd] || null
  81. }
  82. }
  83.  
  84. function hook() {
  85. window.WebSocket = new Proxy(window.WebSocket, {
  86. construct(target, args) {
  87. let obj = new target(...args)
  88. return new Proxy(obj, proxyHandler)
  89. }
  90. })
  91. }
  92.  
  93. let proxyHandler = {
  94. get(target, property) {
  95. let value = target[property]
  96. if ((typeof value) === 'function') {
  97. value = value.bind(target)
  98. }
  99. return value
  100. },
  101. set(target, property, value) {
  102. if (property === 'onmessage') {
  103. let realOnMessage = value
  104. value = function (event) {
  105. myOnMessage(event, realOnMessage)
  106. }
  107. }
  108. target[property] = value
  109. return value
  110. }
  111. }
  112.  
  113. function myOnMessage(event, realOnMessage) {
  114. if (!(event.data instanceof ArrayBuffer)) {
  115. realOnMessage(event)
  116. return
  117. }
  118.  
  119. let data = new Uint8Array(event.data)
  120. function callRealOnMessageByPacket(packet) {
  121. realOnMessage({ ...event, data: packet })
  122. }
  123. try {
  124. handleMessage(data, callRealOnMessageByPacket)
  125. } catch (e) {
  126.  
  127. console.log('handlemessage Error: ' + e.message)
  128. // console.log(data)
  129. }
  130. }
  131.  
  132. function makePacketFromCommand(command) {
  133. let body = textEncoder.encode(JSON.stringify(command))
  134. return makePacketFromUint8Array(body, OP_SEND_MSG_REPLY)
  135. }
  136.  
  137. function makePacketFromUint8Array(body, operation) {
  138. let packLen = HEADER_SIZE + body.byteLength
  139. let packet = new ArrayBuffer(packLen)
  140.  
  141. // 不需要压缩
  142. let ver = operation === OP_HEARTBEAT_REPLY ? WS_BODY_PROTOCOL_VERSION_HEARTBEAT : WS_BODY_PROTOCOL_VERSION_NORMAL
  143. let packetView = new DataView(packet)
  144. packetView.setUint32(0, packLen) // pack_len
  145. packetView.setUint16(4, HEADER_SIZE) // raw_header_size
  146. packetView.setUint16(6, ver) // ver
  147. packetView.setUint32(8, operation) // operation
  148. packetView.setUint32(12, 1) // seq_id
  149.  
  150. let packetBody = new Uint8Array(packet, HEADER_SIZE, body.byteLength)
  151. for (let i = 0; i < body.byteLength; i++) {
  152. packetBody[i] = body[i]
  153. }
  154. return packet
  155. }
  156.  
  157. function handleMessage(data, callRealOnMessageByPacket) {
  158. let offset = 0
  159. while (offset < data.byteLength) {
  160. let dataView = new DataView(data.buffer, offset)
  161. let packLen = dataView.getUint32(0)
  162. // let rawHeaderSize = dataView.getUint16(4)
  163. let ver = dataView.getUint16(6)
  164. let operation = dataView.getUint32(8)
  165. // let seqId = dataView.getUint32(12)
  166.  
  167. let body = new Uint8Array(data.buffer, offset + HEADER_SIZE, packLen - HEADER_SIZE)
  168. if (operation === OP_SEND_MSG_REPLY) {
  169. switch (ver) {
  170. case WS_BODY_PROTOCOL_VERSION_NORMAL:
  171. body = textDecoder.decode(body)
  172. body = JSON.parse(body)
  173. handleCommand(body, callRealOnMessageByPacket)
  174. break
  175. case WS_BODY_PROTOCOL_VERSION_DEFLATE:
  176. body = pako.inflate(body)
  177. handleMessage(body, callRealOnMessageByPacket)
  178. break
  179. case WS_BODY_PROTOCOL_VERSION_BROTLI:
  180. body = BrotliDecode(body)
  181. handleMessage(body, callRealOnMessageByPacket)
  182. break
  183. default: {
  184. let packet = makePacketFromUint8Array(body, operation)
  185. callRealOnMessageByPacket(packet)
  186. break
  187. }
  188. }
  189. } else {
  190. let packet = makePacketFromUint8Array(body, operation)
  191. callRealOnMessageByPacket(packet)
  192. }
  193.  
  194. offset += packLen
  195. }
  196. }
  197.  
  198. function handleCommand(command, callRealOnMessageByPacket) {
  199. if (command instanceof Array) {
  200. for (let oneCommand of command) {
  201. this.handleCommand(oneCommand)
  202. }
  203. return
  204. }
  205.  
  206. let cmd = command.cmd || ''
  207. let pos = cmd.indexOf(':')
  208. if (pos != -1) {
  209. cmd = cmd.substr(0, pos)
  210. }
  211. // console.log('!! ' + cmd);
  212. let handlers = api._getCommandHandlers(cmd)
  213. if (handlers) {
  214. for (let handler of handlers) {
  215. handler(command)
  216. }
  217. }
  218. // console.log(command)
  219.  
  220. let packet = makePacketFromCommand(command)
  221. callRealOnMessageByPacket(packet)
  222. }
  223.  
  224. main()
  225.  
  226. setTimeout(() => {
  227.  
  228. //添加面板
  229. var div = document.getElementById('control-panel-ctnr-box');
  230. var btn = document.createElement('button');
  231. btn.innerHTML = '自动回复:' + (autoReply ? '开' : '关');
  232. btn.style.background = '#23ade5';
  233. btn.style.color = 'white';
  234. btn.style.cursor = 'pointer';;
  235. btn.style.padding = '6px 12px';
  236. btn.style.border = '0';
  237. btn.style.borderRadius = '4px';
  238. btn.id = 'autoReplyBtn';
  239. div.appendChild(btn);
  240. btn.addEventListener('click', () => {
  241. autoReply = !autoReply;
  242. btn.innerHTML = '自动回复:' + (autoReply ? '开' : '关');
  243. });
  244. }, 1000);
  245.  
  246.  
  247.  
  248. })();
  249.  
  250.  
  251. async function sendDM(sendMsg) {
  252. let shortUid = window.location.href.split('live.bilibili.com/')[1];
  253. let room = await fetch(`http://api.live.bilibili.com/room/v1/Room/room_init?id=${shortUid}`, {
  254. method: 'GET',
  255. credentials: 'include'
  256. })
  257. let roomid = await room.json();
  258. roomid = roomid['data']['room_id'];
  259. let scrf = document.cookie.split(';').map(c => c.trim()).filter(c => c.startsWith('bili_jct='))[0].split('bili_jct=')[1];
  260.  
  261. let send = await fetch('https://api.live.bilibili.com/msg/send', {
  262. method: 'POST',
  263. credentials: 'include',
  264. headers: {
  265. 'accept': 'application/json, text/javascript, */*; q=0.01',
  266. 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
  267. },
  268. body: `color=16777215&fontsize=25&mode=1&msg=${sendMsg}&rnd=16082868&roomid=${roomid}&csrf_token=${scrf}&csrf=${scrf}`
  269. });
  270. let sendApiRes = await send.json();
  271. if (sendApiRes['message'] !== "") {
  272. console.log(`发送弹幕:【${sendMsg}】失败,原因:${sendApiRes['message']}。\n`);
  273. } else {
  274. console.log(`发送弹幕:【${sendMsg}】成功\n`);
  275. };
  276.  
  277. }
  278. (async function () {
  279. bliveproxy.addCommandHandler('DANMU_MSG', command => {
  280. console.log('get ' + command.info[1]);
  281. if (autoReply) {
  282. let info = command.info
  283. console.log('info--DANMU_MSG')
  284. console.log('user: ' + command.info[2][1] + ' : ' + command.info[2][0]);
  285. console.log('cont: ' + command.info[1]);
  286.  
  287. var _dm = command.info[1];
  288. var _userid = command.info[2][0];
  289. var _username = command.info[2][1];
  290. if (_userid == selfId.toString()) {
  291. console.log('idok');
  292. if (_dm.match(/确实/)) {
  293. sendDM(_username + ', 确实');
  294. }
  295. // } else if (_username.match(/^[a-zA-Z\_]*$/)) {//纯英文id
  296. // console.log('纯英文id: ' + _userid);
  297. else if (_dm.match(/晚安/)) {
  298. sendDM(_username + '晚安');
  299. }
  300. }
  301. }
  302. }
  303.  
  304.  
  305. // if (command.info[1].toString().substr(0, 5) != 'reply')
  306. // sendDM('reply-- ' + command.info[2][1])
  307. );
  308.  
  309. bliveproxy.addCommandHandler('ROOM_REAL_TIME_MESSAGE_UPDATE', command => {
  310. if (autoReply) {
  311. let info = command
  312. console.log('info--ROOM_REAL_TIME_MESSAGE_UPDATE ' + Object.keys(info.data))
  313. console.log(Object.values(info.data));
  314. console.log('');
  315. }
  316. });
  317. // bliveproxy.addCommandHandler('NOTICE_MSG', command => {
  318. // let info = command
  319. // console.log('info--NOTICE_MSG ' + Object.keys(info.data))
  320. // console.log(Object.values(info.data));
  321. // console.log('');
  322. // });
  323. // bliveproxy.addCommandHandler('INTERACT_WORD', command => {
  324. // let info = command
  325. // console.log('info--INTERACT_WORD ' + Object.keys(info.data))
  326. // console.log(Object.values(info.data));
  327. // console.log('');
  328. // });
  329. // bliveproxy.addCommandHandler('STOP_LIVE_ROOM_LIST', command => {
  330. // let info = command
  331. // console.log('info--STOP_LIVE_ROOM_LIST ' + Object.keys(info.data))
  332. // console.log(Object.values(info.data));
  333. // console.log('');
  334. // });
  335. // bliveproxy.addCommandHandler('NOTICE_MSG', command => {
  336. // let info = command
  337. // console.log('info--NOTICE_MSG ' + Object.keys(info.data))
  338. // console.log(Object.values(info.data));
  339. // console.log('');
  340. // });
  341. })();

QingJ © 2025

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