您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
B站直播间弹幕转发,需要用户已登录。若有滥用等问题概不负责,诶嘿。顺便关注一下小东人鱼和noworld吧~
当前为
// ==UserScript== // @name B站直播间弹幕广播 // @namespace http://tampermonkey.net/ // @version 0.2.1 // @description B站直播间弹幕转发,需要用户已登录。若有滥用等问题概不负责,诶嘿。顺便关注一下小东人鱼和noworld吧~ // @author 太陽闇の力 // @include /https?:\/\/live\.bilibili\.com\/(blanc\/)?\d+\??.*/ // @grant none // @require https://greasyfork.org/scripts/417560-bliveproxy/code/bliveproxy.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js // 命令分析参考自 https://segmentfault.com/a/1190000017328813 //界面参考自小东人鱼午安社五更耗纸 https://github.com/gokoururi-git/gachihelper/ // 弹幕api使用参考自 https://greasyfork.org/zh-CN/scripts/434726 // @license MIT // ==/UserScript== //to-do 修复两个直播间互相转弹幕的时候出错的问题 (function() { //-----------配置区---------- //0默认收起,1默认展开 let isunfold = 1; // 转发对象的UID,也即转发谁的弹幕 let uid = ` 353039514 `.replace(/ /g, '').trim().replace(/\n{2,}/g, '\n').split('\n'); // 要转发的直播间号,也即发到哪儿 let rooms = ` 9806022 2077803 431458 145657 `.replace(/ /g, '').trim().replace(/\n{2,}/g, '\n').split('\n'); // 设置弹幕的发送间隔(秒),默认1秒,指转发到直播间A,然后再转发到直播间B,中间的时间间隔 //如果设置太小,会因发送频率过快而被B站吞掉且有系统屏蔽风险。被吞掉会自动重发并且自动设置发送间隔为1秒(如果本来是1秒,将设置成1.5秒) let inter = 1; let logic = 0;//取值为0则为与,1则为或,3则为非。 //默认正则,带【】()[]()的弹幕都会被转发。 let regdef = "[【】()\\[\\]()]"; //默认自己为20字数限制,一般不改了(有需要的就找我反馈吧) // 当前直播间号 const proomid = /(?<=https?:\/\/live\.bilibili\.com\/(blanc\/)?)\d+/.exec(window.location.href)[0]; if(!(window.document.firstChild instanceof window.Comment)){//「あなたに逢えなくなって、錆びた時計と泣いたけど…」 return; } //-----------UI区---------- let unfold = ["展开","收起"]; // 总容器 const container = window.document.createElement('div'); container.style.cssText = 'width:218px;position:fixed;bottom:5px;right:60px;z-index:999;box-sizing:border-box;'; // 工具名称 const topTool = window.document.createElement('div'); topTool.innerText = '弹幕转发'; topTool.style.cssText = 'text-align:center;line-height:20px;width:100%;color:rgb(210,143,166);font-size:14px;'; // 最小化按钮 const collapseButton = window.document.createElement('button'); collapseButton.innerText = unfold[isunfold]; collapseButton.style.cssText = 'float:right;width:40px;height:20px;border:none;cursor:pointer;background-color:#1890ff;border-radius:1px;color:#ffffff;'; // 主窗口 const mainWindow = window.document.createElement('div'); mainWindow.style.cssText = 'display: flex;flex-wrap: wrap;justify-content:space-between;width:100%;background-color:rgba(220, 192, 221, .5);padding:10px;box-sizing:border-box;'; if(isunfold==0){ mainWindow.style.display = "none"; } // 直播间号输入框 const textArea = window.document.createElement('textarea'); textArea.placeholder = '转发的直播间号,换行分隔' textArea.style.cssText = 'width:45%;height:60px;resize:none;outline:none;background-color:rgba(255,255,255,.5);border-radius:2px'; // 直播间号输入框 const textArea2 = window.document.createElement('textarea'); textArea2.placeholder = '选择的uid,换行分隔' textArea2.style.cssText = 'width:45%;height:60px;resize:none;outline:none;background-color:rgba(255,255,255,.5);border-radius:2px'; // 按钮区容器 const buttonArea = window.document.createElement('div'); buttonArea.style.cssText = 'width:100%;height:30px;box-sizing:border-box;display:flex;justify-content: space-around;margin-top:10px;'; // 按钮区容器 const buttonArea2 = window.document.createElement('div'); buttonArea2.style.cssText = 'width:100%;height:28;box-sizing:border-box;display:flex;margin-top:10px;'; // 开始按钮 const goButton = window.document.createElement('button'); goButton.innerText = '开始'; goButton.style.cssText = 'width:max-content;height:28px;padding:0 5px;margin-left:5px;'; //发送间隔 const interBox = window.document.createElement('input'); interBox.value = 1; interBox.title = "转发间隔的秒数"; interBox.style.cssText = 'width:30px;height:20px;padding:0 5px;'; // 逻辑按钮 const logi = window.document.createElement('select'); logi.options.add(new Option("与",0)); logi.options.add(new Option("或",1)); logi.options.add(new Option("非",2));; logi.value = logic; logi.style.cssText = 'text-align:center;width:20px;height:20px;appearance:none;margin:auto;border-radius:2px'; // 正则提示文本 const regLabel = window.document.createElement('div'); regLabel.innerText = '正则:' regLabel.style.cssText = 'width:20%;height:28;line-height:30px;'; // 正则输入 const regBox = window.document.createElement('input'); regBox.title = ".*匹配任意字符"; regBox.value = regdef; regBox.style.cssText = 'width:60%;height:18px;padding:0 5px:5px;margin:auto'; // 组装 topTool.appendChild(collapseButton); container.appendChild(topTool); mainWindow.appendChild(textArea); mainWindow.appendChild(textArea2) buttonArea.appendChild(goButton); buttonArea.appendChild(interBox); buttonArea2.appendChild(logi); buttonArea2.appendChild(regLabel); buttonArea2.appendChild(regBox); mainWindow.appendChild(buttonArea2); mainWindow.appendChild(buttonArea); container.appendChild(mainWindow); window.document.body.appendChild(container); // 显示逻辑控制 collapseButton.addEventListener('click', () => { if (collapseButton.innerText === '收起') { mainWindow.style.display = 'none'; collapseButton.innerText = '展开'; return; } if (collapseButton.innerText === '展开') { mainWindow.style.display = 'flex'; collapseButton.innerText = '收起'; return; } }, false); function fadeOut(ele,time) { let count = 20; ele.style.opacity=1; return setInterval(function() { ele.style.opacity = ele.style.opacity - 1/count; }, time/count); } function showMessage(intext) { const div = window.document.createElement('div'); div.innerText = intext; div.style.cssText = 'box-sizing:border-box;width:200px;height:40px;position:fixed;bottom:40px;left:50px;z-index:999;background-color:rgba(255, 255, 0,.2);border-radius:5px;color:#FF0000;font-size:medium;line-height:40px;text-align:center;'; window.document.body.appendChild(div); let st = fadeOut(div, 2000); if (inter == 1) { inter = 1.5; } else { inter = 1; } setTimeout((ele) => { clearInterval(st); ele.remove(); }, 2000, div); } //-----------队列------------ function Queue() { let list = []; //向队列中添加数据 this.push = function(data,rlist) { for(let i = 0; i < rlist.length;i++){ let pre = this.getRear(); if(pre){ if(pre[0]==data){ data+="\u200b"; } if(data.length>20){ list.unshift([data.substring(0,20),rlist[i]]); list.unshift([data.substring(20,data.length-1),rlist[i]]); }else{ list.unshift([data,rlist[i]]); } }else{ if(data.length>20){ list.unshift([data.substring(0,20),rlist[i]]); list.unshift([data.substring(20,data.length-1),rlist[i]]); }else{ list.unshift([data,rlist[i]]); } } } return true; } this.getFront = function(){ return list[list.length-1]; } this.getRear = function(){ return list[0]; } this.pushHead = function(data,r){ let post = this.getFront; if(post){ if(post[0]==data){ data+="\u200b"; } if(data.length>20){ list[list.length] = [data.substring(0,20),r]; list[list.length] = [data.substring(20,data.length-1),r]; }else{ list[list.length] = [data,r]; } }else{ if(data.length>20){ list[list.length] = [data.substring(0,20),r]; list[list.length] = [data.substring(20,data.length-1),r]; }else{ list[list.length] = [data,r]; } } list[list.length] = [data,r]; return true; } //从队列中取出数据 this.pop = function() { return list.pop(); } //返回队列的大小 this.size = function() { return list.length; } } //-----------逻辑区---------- let msgQueue =new Queue(); let apiClient = axios.create({ baseURL: 'https://api.live.bilibili.com', withCredentials: true }) function objectCookies(cookie) { let cookies = cookie.split(';'); let result = {}; for (let i = 0; i < cookies.length; i++) { let keyvaluepair = cookies[i].split('='); result[keyvaluepair[0].trim()] = keyvaluepair[1]; } return result; } async function Request(msg, roomid) { let cookie = document.cookie; let rnd = parseInt(+new Date() / 1000); let ObjectCookie = objectCookies(cookie) let data = new FormData() data.append('bubble', 0) data.append('color', 16777215) data.append('fontsize', 25) data.append('mode', 1) data.append('rnd', rnd) data.append('msg', msg) data.append('roomid', roomid) data.append('csrf', ObjectCookie.bili_jct) data.append('csrf_token', ObjectCookie.bili_jct) let ajaxObj = (await apiClient.post('/msg/send', data, { cookie: cookie })).data return ajaxObj; } try{ let qt; let tempt; let flag= false;//qt是否在运行中 const originFetch = window.fetch; window.fetch = (...arg) => { if (arg[0].indexOf('send') > -1) { let ReturnPackage = Request(arg[1].data.msg,arg[1].data.roomid); ReturnPackage.then(res => { console.log(res); if(res.msg == "您发送弹幕的频率过快"){ showMessage(res.msg); if(flag){ showMessage("正在重发"); msgQueue.pushHead(arg[1].data.msg,arg[1].data.roomid); } }else if (res.msg == "f") { showMessage("全局屏蔽词"); }else if(res.msg == "k"){ showMessage("房间屏蔽词"); } }) return new Promise(() => { throw new Error(); }); } else { return originFetch(...arg); } } function allow(rms1,usrs1,rms2,usrs2){ } goButton.addEventListener('click', () => { if (goButton.innerText == '暂停') { bliveproxy.removeCommandHandler('DANMU_MSG', hdl) tempt = setInterval(()=>{ if(msgQueue.size()==0){ flag = false; clearInterval(qt); clearInterval(tempt); } },1000*inter); goButton.innerText = '开始'; return; } uid = textArea2.value.replace(/ /g, '').trim().replace(/\n{2,}/g, '\n').split('\n'); logic = logi.value; rooms=textArea.value; if(rooms==''){ showMessage("您未输入直播间号"); return; } rooms = textArea.value.replace(/ /g, '').trim().replace(/\n{2,}/g, '\n').split('\n'); if (rooms.indexOf(proomid) > -1) { showMessage("不能转发到所在直播间"); return; } bliveproxy.addCommandHandler('DANMU_MSG', hdl); goButton.innerText = '暂停'; if(!flag){ flag = true; qt = setInterval(()=>{ if(msgQueue.size()>0){ let [msg,roomid] = msgQueue.pop(); let ReturnPackage = Request(msg,roomid); ReturnPackage.then(res=>{ if(res.msg == "您发送弹幕的频率过快"){ showMessage(res.msg+"正在重发"); msgQueue.pushHead(msg,roomid); } }//数据参考 //code: -500 data: [] message: "超出限制长度" msg: "超出限制长度" [[Prototype]]: Object )//code: 10030 data: [] message: "您发送弹幕的频率过快" msg: "您发送弹幕的频率过快" } },1000*inter); } }, false); }catch (e) { alert('弹幕转发:发生未知错误\n' + e); } function hdl(command) { let info = command.info; console.log("a"); const uidlogi = uid.indexOf(info[2][0])>-1; console.log("g"); const reg = new RegExp(regBox.value); const reglogi = reg.test(info[1]); console.log("12"); switch(logic){ case 0:if(!(uidlogi&®logi)){return}; break; case 1:if(!(uidlogi||reglogi)){return};; break; case 2:if(!reglogi){return};; break; } msgQueue.push(info[1],rooms); } })(); //点击弹幕区的人显示uid window.onload = function(){ const div = window.document.createElement("div"); let chat = window.document.querySelector("#chat-items"); let username; if(chat){ chat.addEventListener("click",(e)=>{ if(!username){ username = window.document.querySelector(".danmaku-menu"); username.insertBefore(div,username.childNodes[1]); } if(e.target.className.split(" ").indexOf("pointer")>-1){ const userID=e.target.parentNode.getAttribute("data-uid"); div.innerText = userID; } }) }else{ console.log("无法获取弹幕栏"); } }