// ==UserScript== // @name bilibili直播净化 // @namespace https://github.com/lzghzr/GreasemonkeyJS // @version 2.0.1 // @author lzghzr // @description 屏蔽聊天室礼物以及关键字, 净化聊天室环境 // @supportURL https://github.com/lzghzr/GreasemonkeyJS/issues // @include /^http:\/\/live\.bilibili\.com\/\d.*$/ // @license MIT // @grant none // @run-at document-end // @downloadURL none // ==/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 += '.vip-icon {display: none !important;}'; if (this.config.noMedalIcon.enable) cssText += '.medal-icon {display: none !important;}'; if (this.config.noUserLevelIcon.enable) cssText += '.user-level-icon {display: none !important;}'; if (this.config.noLiveTitleIcon.enable) cssText += '.live-title-icon {display: none !important;}'; if (this.config.noSystemMsg.enable) cssText += '.system-msg {display: none !important;} .announcement-container {display: none !important;}'; if (this.config.noGiftMsg.enable) cssText += '.gift-msg {display: none !important;}'; if (this.config.noSuperGift.enable) cssText += '.super-gift-ctnr {display: none !important;}'; if (this.config.noSmallGift.enable) cssText += '.gift-msg-1000 {display: none !important;} .chat-msg-list {height: 100% !important;}'; if (this.config.fixTreasure.enable) cssText += '.treasure {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 = '滚
'; // 循环插入内容 for (let x in this.config) { html += `
${this.config[x].name}
`; } html += '
'; 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: 9px; 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:gunMeun 300ms; background-color: #fff; border-radius: 5px; box-shadow: 0 0 20px; 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; } @keyframes gunMeun { from { margin: -250px -200px; opacity: 0; } to { margin: -250px -125px; opacity: 1; } }`; // 插入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'] * @returns {Promise} */ XHR(url, type = '', method = 'GET') { 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'); xhr.responseType = type; xhr.onload = (ev) => { clearTimeout(timeout); resolve(ev.target.response); }; xhr.send(postData); } }); } } const gun = new BiLiveNoVIP(); gun.Start();