B站直播间弹幕广播

B站直播间弹幕转发,需要用户已登录(不可用)。若有滥用等问题概不负责,诶嘿。顺便关注一下小东人鱼和noworld吧~

目前為 2022-01-05 提交的版本,檢視 最新版本

// ==UserScript==
// @name         B站直播间弹幕广播
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  B站直播间弹幕转发,需要用户已登录(不可用)。若有滥用等问题概不负责,诶嘿。顺便关注一下小东人鱼和noworld吧~
// @author       太陽闇の力
// @include      /https?:\/\/live\.bilibili\.com\/(blanc\/)?\d+\??.*/
// @grant        none
// @require https://gf.qytechs.cn/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://gf.qytechs.cn/zh-CN/scripts/434726
// @license MIT
// ==/UserScript==

(function() {
    //-----------配置区----------
    //0默认收起,1默认展开
    let isunfold = 0;
    // 转发对象的UID,也即转发谁的弹幕
    let uid = 353039514;
    // 要转发的直播间号,也即发到哪儿
    let rooms = `
9806022
2077803
431458
145657
`.replace(/ /g, '').trim().split('\n');
    // 设置弹幕的发送间隔(秒),默认1秒,指转发到直播间A,然后再转发到直播间B,中间的时间间隔
    //如果设置太小,会因发送频率过快而被B站吞掉弹幕,将自动重发并且自动设置发送间隔为1秒(如果本来是1秒,将设置成1.5秒)
    let inter = 1;
    //默认正则,带【】()[]()的弹幕都会被转发。
    let regdef = "[【】()\\[\\]()]";
    //默认自己为20字数限制,一般不改了(有需要的就找我反馈吧)
    // 当前直播间号
    const proomid = /(?!https:\/\/live.bilibili.com\/)\d+/.exec(window.location.href)[0];
    //-----------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 = '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:100%;height:60px;resize:none;outline:none;background-color:rgba(255,255,255,.5);';

    // 按钮区容器
    const buttonArea = window.document.createElement('div');
    buttonArea.style.cssText = 'width:100%;height:30px;box-sizing:border-box;display:flex;';

    // 按钮区容器
    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;';

    //用户UID
    const uidBox = window.document.createElement('input');
    uidBox.placeholder = "输入用户uid";
    uidBox.style.cssText = 'width:70px;height:28px;padding:0 5px;margin-left:5px;';

    //发送间隔
    const interBox = window.document.createElement('input');
    interBox.value = 1;
    interBox.placeholder = "间隔(秒";
    interBox.style.cssText = 'width:40px;height:28px;padding:0 5px;margin-left:5px;';
    // 正则提示文本
    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.placeholder = ".*匹配任意字符";
    regBox.value = regdef;
    regBox.style.cssText = 'width:60%;height:18px;padding:0 5px;margin-left:5px;';
    // 组装
    topTool.appendChild(collapseButton);
    container.appendChild(topTool);
    mainWindow.appendChild(textArea);
    buttonArea.appendChild(uidBox);
    buttonArea.appendChild(interBox);
    buttonArea.appendChild(goButton);
    buttonArea2.appendChild(regLabel);
    buttonArea2.appendChild(regBox);
    mainWindow.appendChild(buttonArea);
    mainWindow.appendChild(buttonArea2);
    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 = 'block';
            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,rlist[i]];
                }
            }
            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('api.live.bilibili.com/msg/send') > -1) {
                let ReturnPackage = Request(arg[1].data.msg,arg[1].data.roomid);
                ReturnPackage.then(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);
            }
        }
        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 = uidBox.value;
            if(uid==''){
                showMessage("您未输入uid");
                return;
            }
            rooms=textArea.value;
            if(rooms==''){
                showMessage("您未输入直播间号");
                return;
            }
            rooms = textArea.value.replace(/ /g, '').trim().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;
        if (info[2][0] != uid) {
            return;
        }
        const reg = new RegExp(regBox.value);
        if(!(reg.test(info[1]))){
            return;
        };
        msgQueue.push(info[1],rooms);

    }
})();

QingJ © 2025

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