// ==UserScript==
// @name bilibili直播净化
// @namespace https://github.com/lzghzr/GreasemonkeyJS
// @version 3.7.0
// @author lzghzr
// @description 屏蔽聊天室礼物以及关键字, 净化聊天室环境
// @supportURL https://github.com/lzghzr/GreasemonkeyJS/issues
// @include /^https:\/\/live\.bilibili\.com\/(?:blanc\/)?\d/
// @match https://live.bilibili.com/blackboard/activity-*
// @match https://www.bilibili.com/blackboard/live/*
// @require https://cdn.jsdelivr.net/gh/lzghzr/TampermonkeyJS@25127e6f47da91603645f9ec3a7da65ecb1180cf/Ajax-hook/Ajax-hook.js
// @license MIT
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-start
// ==/UserScript==
class NoVIP {
noBBChat = false;
noBBDanmaku = false;
noSleep = false;
sleepTimer = 0;
elmStyleCSS;
chatObserver;
danmakuObserver;
Start() {
this.elmStyleCSS = GM_addStyle('');
this.AddCSS();
const chatMessage = new Map();
this.chatObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(addedNode => {
if (addedNode instanceof HTMLDivElement && addedNode.classList.contains('danmaku-item')) {
const chatNode = addedNode.querySelector('.danmaku-content');
if (chatNode !== null) {
const chatText = chatNode.innerText;
const dateNow = Date.now();
if (chatMessage.has(chatText) && dateNow - chatMessage.get(chatText) < 5000)
addedNode.remove();
chatMessage.set(chatText, dateNow);
}
}
});
});
});
const danmakuMessage = new Map();
this.danmakuObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(addedNode => {
const danmakuNode = addedNode instanceof Text ? addedNode.parentElement : addedNode;
if (danmakuNode?.className === 'bilibili-danmaku') {
const danmakuText = danmakuNode.innerText;
const dateNow = Date.now();
if (danmakuMessage.has(danmakuText) && dateNow - danmakuMessage.get(danmakuText) < 5000)
danmakuNode.innerText = '';
danmakuMessage.set(danmakuText, dateNow);
}
});
});
});
setInterval(() => {
const dateNow = Date.now();
chatMessage.forEach((value, key) => {
if (dateNow - value > 60 * 1000)
chatMessage.delete(key);
});
danmakuMessage.forEach((value, key) => {
if (dateNow - value > 60 * 1000)
danmakuMessage.delete(key);
});
}, 60 * 1000);
this.ChangeCSS();
const bodyObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(addedNode => {
if (addedNode instanceof HTMLDivElement && addedNode.classList.contains('dialog-ctnr')) {
const blockEffectCtnr = addedNode.querySelector('.block-effect-ctnr');
if (blockEffectCtnr !== null)
this.AddUI(blockEffectCtnr);
}
});
});
});
bodyObserver.observe(document.body, { childList: true, subtree: true });
}
enableNOBBChat() {
if (this.noBBChat)
return;
const elmDivChatList = document.querySelector('#chat-items');
if (elmDivChatList !== null) {
this.noBBChat = true;
this.chatObserver.observe(elmDivChatList, { childList: true });
}
}
disableNOBBChat() {
if (!this.noBBChat)
return;
this.noBBChat = false;
this.chatObserver.disconnect();
}
enableNOBBDanmaku() {
if (this.noBBDanmaku)
return;
const elmDivDanmaku = document.querySelector('#live-player');
if (elmDivDanmaku !== null) {
this.noBBDanmaku = true;
this.danmakuObserver.observe(elmDivDanmaku, { childList: true, subtree: true });
}
}
disableNOBBDanmaku() {
if (!this.noBBDanmaku)
return;
this.noBBDanmaku = false;
this.danmakuObserver.disconnect();
}
enableNOSleep() {
if (this.noSleep)
return;
this.noSleep = true;
this.sleepTimer = setInterval(() => document.dispatchEvent(new MouseEvent('mousemove')), 10 * 60 * 1000);
}
disableNOSleep() {
if (!this.noSleep)
return;
this.noSleep = false;
clearInterval(this.sleepTimer);
}
ChangeCSS() {
let height = 62;
let cssText = `
.chat-item .user-name {
color: #23ade5 !important;
}`;
if (config.menu.noGiftMsg.enable) {
height -= 32;
cssText += `
.chat-history-list.with-penury-gift {
height: 100% !important;
}
/* 热门流量推荐 */
.chat-item.hot-rank-msg,
/* VIP标识 */
#activity-welcome-area-vm,
.chat-item .vip-icon,
.chat-item.welcome-msg,
/* 高能标识 */
.chat-item.top3-notice,
.chat-item .rank-icon,
/* 分享直播间 */
.chat-item.important-prompt-item,
#chat-gift-bubble-vm,
#penury-gift-msg,
#gift-screen-animation-vm,
#my-dear-haruna-vm .super-gift-bubbles,
.chat-item.gift-item,
.chat-item.system-msg,
.web-player-inject-wrap>div:first-child>div:last-child,
.bilibili-live-player-video-operable-container>div:first-child>div:last-child,
.bilibili-live-player-video-gift,
.bilibili-live-player-danmaku-gift {
display: none !important;
}`;
}
if (config.menu.noSystemMsg.enable) {
height -= 30;
cssText += `
.chat-history-list.with-brush-prompt {
height: 100% !important;
}
#brush-prompt,
.chat-item.misc-msg {
display: none !important;
}`;
}
if (config.menu.noSuperChat.enable)
cssText += `
.chat-item.superChat-card-detail {
margin-left: unset !important;
margin-right: unset !important;
min-height: unset !important;
}
.chat-item .card-item-middle-top {
background-color: unset !important;
background-image: unset !important;
border: unset !important;
display: inline !important;
padding: unset !important;
}
.chat-item .card-item-middle-top-right {
display: unset !important;
}
.chat-item .superChat-base {
display: unset !important;
height: unset !important;
line-height: unset !important;
vertical-align: unset !important;
width: unset !important;
}
.chat-item .superChat-base .fans-medal-item-ctnr {
margin-right: 4px !important;
}
.chat-item .name {
color: #23ade5 !important;
display: unset !important;
font-size: unset !important;
font-weight: unset !important;
height: unset !important;
line-height: 20px !important;
margin-left: unset !important;
opacity: unset !important;
overflow: unset !important;
text-overflow: unset !important;
vertical-align: unset !important;
white-space: unset !important;
width: unset !important;
}
.chat-item .card-item-middle-bottom {
background-color: unset !important;
display: unset !important;
padding: unset !important;
}
.chat-item .input-contain {
display: unset !important;
}
.chat-item .text {
color: #646c7a !important;
}
/* SuperChat 提示条 */
#chat-msg-bubble-vm,
/* SuperChat 保留条 */
#pay-note-panel-vm,
.chat-item .bottom-background,
.chat-item .card-item-top-right,
#chat-control-panel-vm .super-chat {
display: none !important;
}`;
if (config.menu.noEmoticons.enable)
cssText += `
.bilibili-danmaku > img,
#chat-control-panel-vm .emoticons-panel,
.chat-item[data-type="1"] {
display: none !important;
}`;
if (config.menu.noKanBanMusume.enable)
cssText += `
#my-dear-haruna-vm {
display: none !important;
}`;
if (config.menu.noGuardIcon.enable)
cssText += `
.chat-item.guard-danmaku .vip-icon {
margin-right: 4px !important;
}
.chat-item.guard-danmaku .admin-icon,
.chat-item.guard-danmaku .anchor-icon,
.chat-item.guard-danmaku .fans-medal-item-ctnr,
.chat-item.guard-danmaku .guard-icon,
.chat-item.guard-danmaku .title-label,
.chat-item.guard-danmaku .user-level-icon,
.chat-item.guard-danmaku .user-lpl-logo {
margin-right: 5px !important;
}
.chat-item.guard-level-1,
.chat-item.guard-level-2 {
margin: 0 !important;
padding: 4px 5px !important;
}
.chat-item.chat-colorful-bubble {
background-color: rgba(248, 248, 248, 0) !important;
border-radius: 0px !important;
display: block !important;
margin: 0 !important;
}
#welcome-area-bottom-vm,
.chat-item.common-danmuku-msg,
.chat-item.guard-buy,
.chat-item.welcome-guard,
.chat-item .guard-icon,
.chat-item.guard-level-1:after,
.chat-item.guard-level-2:after,
.chat-item.guard-level-1:before,
.chat-item.guard-level-2:before {
display: none !important;
}`;
if (config.menu.noMedalIcon.enable)
cssText += `
.chat-item .fans-medal-item-ctnr {
display: none !important;
}`;
if (config.menu.noLiveTitleIcon.enable)
cssText += `
.chat-item .title-label {
display: none !important;
}`;
if (config.menu.noRaffle.enable)
cssText += `
body[style*="overflow: hidden;"] {
overflow-y: auto !important;
}
#anchor-guest-box-id,
.anchor-lottery-entry,
#player-effect-vm,
#chat-draw-area-vm {
display: none !important;
}`;
cssText += `
.chat-history-list.with-penury-gift.with-brush-prompt {
height: calc(100% - ${height}px) !important;
}`;
if (config.menu.noBBChat.enable)
this.enableNOBBChat();
else
this.disableNOBBChat();
if (config.menu.noBBDanmaku.enable)
this.enableNOBBDanmaku();
else
this.disableNOBBDanmaku();
if (config.menu.noSleep.enable)
this.enableNOSleep();
else
this.disableNOSleep();
this.elmStyleCSS.innerHTML = cssText;
}
AddUI(addedNode) {
const elmUList = addedNode.firstElementChild;
const listLength = elmUList.childElementCount;
if (listLength > 10)
return;
const changeListener = (itemHTML, x) => {
const itemSpan = itemHTML.querySelector('span');
const itemInput = itemHTML.querySelector('input');
itemInput.checked = config.menu[x].enable;
itemInput.checked ? selectedCheckBox(itemSpan) : defaultCheckBox(itemSpan);
itemInput.addEventListener('change', ev => {
const evt = ev.target;
evt.checked ? selectedCheckBox(itemSpan) : defaultCheckBox(itemSpan);
config.menu[x].enable = evt.checked;
GM_setValue('blnvConfig', JSON.stringify(config));
this.ChangeCSS();
});
};
const selectedCheckBox = (spanClone) => {
spanClone.classList.remove('checkbox-default');
spanClone.classList.add('checkbox-selected');
};
const defaultCheckBox = (spanClone) => {
spanClone.classList.remove('checkbox-selected');
spanClone.classList.add('checkbox-default');
};
const theSameName = (listNodes, x) => {
for (const clild of listNodes)
if (clild.innerText === config.menu[x].name)
return clild;
};
const itemHTML = elmUList.firstElementChild.cloneNode(true);
const itemInput = itemHTML.querySelector('input');
const itemLabel = itemHTML.querySelector('label');
itemInput.id = itemInput.id.replace(/\d/, '');
itemLabel.htmlFor = itemLabel.htmlFor.replace(/\d/, '');
let i = listLength;
const listNodes = elmUList.childNodes;
for (const x in config.menu) {
const clild = theSameName(listNodes, x);
if (clild === undefined) {
const itemHTMLClone = itemHTML.cloneNode(true);
const itemInputClone = itemHTMLClone.querySelector('input');
const itemLabelClone = itemHTMLClone.querySelector('label');
itemInputClone.id += i;
itemLabelClone.htmlFor += i;
i++;
itemLabelClone.innerText = config.menu[x].name;
changeListener(itemHTMLClone, x);
elmUList.appendChild(itemHTMLClone);
}
else {
const itemHTMLClone = clild.cloneNode(true);
changeListener(itemHTMLClone, x);
clild.remove();
elmUList.appendChild(itemHTMLClone);
}
}
}
AddCSS() {
GM_addStyle(`
.gift-block {
border: 2px solid;
border-radius: 50%;
display: inline-block;
height: 17px;
text-align: center;
width: 17px;
}
.gift-block:before {
content: '滚' !important;
font-size: 13px;
vertical-align: top;
}
/*隐藏网页全屏榜单*/
.player-full-win .rank-list-section {
display: none !important;
}
.player-full-win .chat-history-panel:not([style]) {
height: calc(100% - 135px) !important;
}`);
}
}
const defaultConfig = {
version: 1629375942428,
menu: {
noGiftMsg: {
name: '屏蔽全部礼物及广播',
enable: false
},
noSystemMsg: {
name: '屏蔽进场信息',
enable: false
},
noSuperChat: {
name: '屏蔽醒目留言',
enable: false
},
noEmoticons: {
name: '屏蔽表情特效',
enable: false
},
noKanBanMusume: {
name: '屏蔽看板娘',
enable: false
},
noGuardIcon: {
name: '屏蔽舰队标识',
enable: false
},
noMedalIcon: {
name: '屏蔽粉丝勋章',
enable: false
},
noLiveTitleIcon: {
name: '屏蔽成就头衔',
enable: false
},
noRaffle: {
name: '屏蔽抽奖弹窗',
enable: false
},
noBBChat: {
name: '屏蔽刷屏聊天',
enable: false
},
noBBDanmaku: {
name: '屏蔽刷屏弹幕',
enable: false
},
noActivityPlat: {
name: '屏蔽活动皮肤',
enable: false
},
noRoomSkin: {
name: '屏蔽房间皮肤',
enable: false
},
noRoundPlay: {
name: '屏蔽视频轮播',
enable: false
},
noSleep: {
name: '屏蔽挂机检测',
enable: false
},
invisible: {
name: '隐身入场',
enable: false
}
}
};
const userConfig = JSON.parse(GM_getValue('blnvConfig', JSON.stringify(defaultConfig)));
let config;
if (userConfig.version === undefined || userConfig.version < defaultConfig.version) {
for (const x in defaultConfig.menu) {
try {
defaultConfig.menu[x].enable = userConfig.menu[x].enable;
}
catch (error) {
console.error(error);
}
}
config = defaultConfig;
}
else
config = userConfig;
if (location.href.match(/^https:\/\/live\.bilibili\.com\/(?:blanc\/)?\d/)) {
if (config.menu.invisible.enable || config.menu.noRoundPlay.enable || config.menu.noRoomSkin.enable) {
if (config.menu.noRoundPlay.enable)
Reflect.defineProperty(unsafeWindow, '__NEPTUNE_IS_MY_WAIFU__', {});
ah.proxy({
onRequest: (XHRconfig, handler) => {
if (config.menu.invisible.enable && XHRconfig.url.includes('//api.live.bilibili.com/xlive/web-room/v1/index/getInfoByUser'))
XHRconfig.url = '//api.live.bilibili.com/xlive/web-room/v1/index/getInfoByUser?room_id=273022';
handler.next(XHRconfig);
},
onResponse: (response, handler) => {
if (config.menu.invisible.enable && response.config.url.includes('//api.live.bilibili.com/xlive/web-room/v1/index/getInfoByUser'))
response.response = response.response.replace('"is_room_admin":false', '"is_room_admin":true');
if (config.menu.noRoundPlay.enable) {
if (response.config.url.includes('//api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo'))
response.response = response.response.replace('"live_status":2', '"live_status":0');
if (response.config.url.includes('//api.live.bilibili.com/live/getRoundPlayVideo'))
response.status = 403;
}
if (config.menu.noRoomSkin.enable && response.config.url.includes('//api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom')) {
const json = JSON.parse(response.response);
json.data.skin_info = undefined;
response.response = JSON.stringify(json);
}
handler.next(response);
}
});
if (config.menu.noRoundPlay.enable) {
const _fetch = unsafeWindow.fetch;
unsafeWindow.fetch = new Proxy(_fetch, {
apply: async function (target, _this, args) {
if (typeof args[0] === 'string' && args[0].includes('//api.live.bilibili.com/live/getRoundPlayVideo')) {
const response = await target(...args);
return new Response(response.body, {
status: 403,
statusText: 'Forbidden',
headers: response.headers
});
}
return Reflect.apply(target, _this, args);
}
});
}
}
if (config.menu.noActivityPlat.enable) {
if (self === top) {
if (location.pathname.startsWith('/blanc'))
history.replaceState(null, '', location.href.replace(`${location.origin}/blanc`, location.origin));
else
location.href = location.href.replace(location.origin, `${location.origin}/blanc`);
}
else {
top.postMessage(location.origin + location.pathname, 'https://live.bilibili.com');
top.postMessage(location.origin + location.pathname, 'https://www.bilibili.com');
}
}
document.addEventListener('readystatechange', () => {
if (document.readyState === 'complete')
new NoVIP().Start();
});
}
else if (location.href.startsWith('https://live.bilibili.com/blackboard/activity-') || location.href.startsWith('https://www.bilibili.com/blackboard/live/')) {
if (config.menu.noActivityPlat.enable)
unsafeWindow.addEventListener("message", msg => {
if (msg.origin === 'https://live.bilibili.com' && msg.data.startsWith('https://live.bilibili.com/blanc/'))
location.href = msg.data;
});
}