Greasy Fork

Greasy Fork is available in English.

bilibili直播净化

屏蔽聊天室礼物以及关键字, 净化聊天室环境

当前为 2016-08-01 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        bilibili直播净化
// @namespace   https://github.com/lzghzr/GreasemonkeyJS
// @version     2.0.3
// @author      lzghzr
// @description 屏蔽聊天室礼物以及关键字, 净化聊天室环境
// @supportURL  https://github.com/lzghzr/GreasemonkeyJS/issues
// @include     /^http:\/\/live\.bilibili\.com\/\d.*$/
// @license     MIT
// @grant       none
// @run-at      document-end
// ==/UserScript==
/**
 * 屏蔽B站直播间聊天室内容
 *
 * @class BiLiveNoVIP
 */
class BiLiveNoVIP {
    constructor() {
        this.W = window;
        this.D = document;
        this.defaultConfig = {
            noVIPIcon: {
                name: '隐藏VIP',
                enable: false
            },
            noMedalIcon: {
                name: '隐藏勋章',
                enable: false
            },
            noUserLevelIcon: {
                name: '隐藏等级',
                enable: false
            },
            noLiveTitleIcon: {
                name: '隐藏头衔',
                enable: false
            },
            noSystemMsg: {
                name: '隐藏系统消息',
                enable: false
            },
            noGiftMsg: {
                name: '隐藏礼物',
                enable: false
            },
            noSuperGift: {
                name: '隐藏礼物连击',
                enable: false
            },
            noSmallGift: {
                name: '隐藏下方礼物',
                enable: false
            },
            fixTreasure: {
                name: '下移宝箱',
                enable: false
            },
            popularWords: {
                name: '屏蔽热词',
                enable: false
            }
        };
        this.roomID = this.W.ROOMID;
        // 加载设置
        let config = localStorage.getItem('blnvConfig');
        this.config = (config === null) ? this.defaultConfig : JSON.parse(config);
    }
    /**
     * 开始
     */
    Start() {
        this.AddUI();
        this.ChangeCSS();
        if (this.config.popularWords.enable)
            this.PopularWords(true);
    }
    /**
     * 模拟实时屏蔽
     *
     * @private
     */
    ChangeCSS() {
        // 获取或者插入style
        let elmStyle = this.D.querySelector('#gunCSS');
        if (elmStyle === null) {
            elmStyle = this.D.createElement('style');
            elmStyle.id = 'gunCSS';
            this.D.body.appendChild(elmStyle);
        }
        //css内容
        let cssText = '';
        if (this.config.noVIPIcon.enable)
            cssText += '#chat-msg-list .vip-icon {display: none !important;}';
        if (this.config.noMedalIcon.enable)
            cssText += '#chat-msg-list .medal-icon {display: none !important;}';
        if (this.config.noUserLevelIcon.enable)
            cssText += '#chat-msg-list .user-level-icon {display: none !important;}';
        if (this.config.noLiveTitleIcon.enable)
            cssText += '#chat-msg-list .live-title-icon {display: none !important;}';
        if (this.config.noSystemMsg.enable)
            cssText += '#chat-msg-list .system-msg {display: none !important;} #chat-msg-list .announcement-container {display: none !important;}';
        if (this.config.noGiftMsg.enable)
            cssText += '#chat-msg-list .gift-msg {display: none !important;}';
        if (this.config.noSuperGift.enable)
            cssText += '#chat-list-ctnr > .super-gift-ctnr {display: none !important;}';
        if (this.config.noSmallGift.enable)
            cssText += '#chat-list-ctnr > #gift-msg-1000 {display: none !important;} #chat-list-ctnr > #chat-msg-list {height: 100% !important;}';
        if (this.config.fixTreasure.enable)
            cssText += '#player-container > .treasure-box-ctnr {margin: -160px 0 !important;}';
        elmStyle.innerHTML = cssText;
    }
    /**
     * 添加按钮
     *
     * @private
     */
    AddUI() {
        // 添加按钮相关的css
        this.AddCSS();
        // 获取按钮插入的位置
        let elmDivBtns = this.D.querySelector('.btns');
        // 传说中的UI, 真的很丑
        let elmDivGun = this.D.createElement('div');
        elmDivGun.id = 'gunBut';
        let html = '滚<div id="gunMeun" class="gunHide">';
        // 循环插入内容
        for (let x in this.config) {
            html += `
<div>
  <input type="checkbox" id="${x}" class="gunHide" />
	<label for="${x}"></label>
  <span>${this.config[x].name}</span>
</div>`;
        }
        html += '</div>';
        elmDivGun.innerHTML = html;
        // 插入菜单按钮
        elmDivBtns.appendChild(elmDivGun);
        // 获取刚刚插入的DOM
        let elmDivMeun = this.D.querySelector('#gunMeun');
        // 为了和b站更搭, 所以监听body的click
        this.D.body.addEventListener('click', (ev) => {
            let evt = ev.target;
            if (elmDivGun.contains(evt)) {
                if (evt === elmDivGun) {
                    elmDivMeun.classList.toggle('gunHide');
                }
            }
            else {
                elmDivMeun.classList.add('gunHide');
            }
        });
        // 循环设置监听插入的DOM
        for (let x in this.config) {
            let checkbox = this.D.getElementById(x);
            checkbox.checked = this.config[x].enable;
            checkbox.addEventListener('change', (ev) => {
                let evt = ev.target;
                this.config[evt.id].enable = checkbox.checked;
                localStorage.setItem('blnvConfig', JSON.stringify(this.config));
                if (evt.id === 'popularWords') {
                    this.PopularWords(checkbox.checked);
                }
                else {
                    this.ChangeCSS();
                }
            });
        }
    }
    AddCSS() {
        let cssText = `
.gunHide {
  display: none;
}
#gunBut {
  border: 1px solid #999;
  border-radius: 50%;
  cursor: pointer;
  display: inline-block;
  font-size: 13px;
  height: 18px;
  margin: -3px 5px;
  text-align: center;
  width: 18px;
  vertical-align: text-top;
}
#gunBut > #gunMeun {
  animation:move-in-right cubic-bezier(.22,.58,.12,.98) .4s;
  background-color: #fff;
  border-radius: 5px;
  box-shadow: 0 0 2em .1em rgba(0,0,0,0.15);
  cursor: default;
  font-size: 12px;
  height: 210px;
  margin: -250px -125px;
  padding: 10px;
  position: absolute;
  width: 100px;
}
#gunBut > #gunMeun > div {
	background: darkgray;
	border-radius: 5px;
	height: 10px;
	margin: 0 0 12px 0;
	position: relative;
	width: 20px;
}
#gunBut > #gunMeun > div > label {
	background: dimgray;
	border-radius: 50%;
	cursor: pointer;
	display: block;
	height: 16px;
	left: -3px;
	position: absolute;
	top: -3px;
	transition: all .5s ease;
	width: 16px;
}
#gunBut > #gunMeun > div > input[type=checkbox]:checked + label {
  background: #4fc1e9;
	left: 7px;
}
#gunBut > #gunMeun > div > span {
  left: 0;
  margin: -3px 0 0 20px;
  position: absolute;
  width: 80px;
}
}`;
        // 插入css
        let elmStyle = this.D.createElement('style');
        elmStyle.innerHTML = cssText;
        this.D.body.appendChild(elmStyle);
    }
    /**
     * 屏蔽热词
     *
     * @private
     */
    PopularWords(disable) {
        let player = `/api/player?id=cid:${this.roomID}&ts=${Date.now().toString(16)}`;
        let popularWords = this.W.flash_popularWords();
        this.XHR(player, 'document')
            .then((resolve) => {
            return resolve.querySelector('user_sheid_keyword').innerHTML;
        })
            .then((resolve) => {
            let userKeyword = new Set(resolve.split(','));
            let hotWords = [...popularWords].filter((y) => (disable) ? !userKeyword.has(y) : userKeyword.has(y));
            for (let y of hotWords) {
                let shield = `/liveact/shield_keyword?keyword=${encodeURIComponent(y)}&roomid=${this.roomID}&type=${(disable) ? 1 : 0}`;
                this.XHR(shield, '', 'POST');
            }
        });
    }
    /**
     * 使用Promise封装xhr
     *
     * @private
     * @template T
     * @param {string} url
     * @param {string} [type='']
     * @param {string} [method='GET']
     * @param {boolean} [cookie=false]
     * @returns {Promise<T>}
     */
    XHR(url, type = '', method = 'GET', cookie = false) {
        return new Promise((resolve, reject) => {
            // 并不需要处理错误
            let timeout = setTimeout(reject, 3e4); //30秒
            let path = url;
            if (type === 'jsonp') {
                // 感觉引入jquery还是太大材小用
                let elmScript = this.D.createElement('script');
                this.D.body.appendChild(elmScript);
                this.W['cb'] = (json) => {
                    clearTimeout(timeout);
                    this.D.body.removeChild(elmScript);
                    this.W['cb'] = undefined;
                    resolve(json);
                };
                elmScript.src = `${path}&callback=cb & _=${Date.now()} `;
            }
            else {
                let postData = '';
                let xhr = new XMLHttpRequest();
                if (method === 'POST') {
                    path = url.split('?')[0];
                    postData = url.split('?')[1];
                }
                xhr.open(method, path, true);
                if (method === 'POST')
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
                if (cookie)
                    xhr.withCredentials = true;
                xhr.responseType = type;
                xhr.onload = (ev) => {
                    clearTimeout(timeout);
                    resolve(ev.target.response);
                };
                xhr.send(postData);
            }
        });
    }
}
const gun = new BiLiveNoVIP();
gun.Start();