- // ==UserScript==
- // @name 斗鱼屏蔽ID消息(blockspeech)
- // @namespace http://tampermonkey.net/
- // @version 1.0.1
- // @description 根据ID屏蔽直播间的发言(持久化存储、傻瓜式使用/管理)
- // @author Ginyang
- // @match *://www.douyu.com/*
- // @icon https://www.douyu.com/favicon.ico
- // @run-at document-start
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_registerMenuCommand
- // @license MIT
- // ==/UserScript==
-
- /************************* v1.0.1 更新内容*************************
- *
- 1、【新增/优化】新增消息提示,现在不用进入控制台查看信息了,直接会在页面上方给出提示消息
- 2、【修复】进入直播间可能会因为页面未加载完全导致插件加载失败(现在添加了window.onload判断)
- 3、【修复】Firefox中input输入框的宽度超出设置页面
- 4、【优化】Firefox中的滚动条样式不起作用(这是火狐的内核导致的,提供的样式美化太少了,我又懒得自己覆盖)
- 5、【优化】代码结构优化
-
- *************************************************************** */
-
- (function () {
- 'use strict';
- window.onload = function () {
- console.log("bs load success");
- // 全局变量
- var bs_observer = null;
- // 定义消息为全局对象
- var message = null;
-
- function bsStyleInit() {
- let bs_style_css = `
- /***************************遮罩层overlay、对话框dialog***************************/
- #bs_overlay {
- display: block;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.5);
- z-index: 9999;
- }
- @keyframes show {
- 0% {
- transform: rotateX(30deg);
- }
- 58% {
- opacity: 1;
- transform: rotateX(-12deg);
- }
- 100% {
- opacity: 1;
- }
- }
- #bs_dialog {
- display: block;
- position: fixed;
- top: 50%;
- left: 50%;
- margin-top: -160px;
- margin-left: -160px;
- width: 320px;
- height: 330px;
- background-color: #fff;
- color: #333;
- font-size: 16px;
- border-radius: 5px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
- padding: 20px;
- transition: all 0.3s ease-in-out;
- /* transform-style: preserve-3d; */
- transform-origin: center center;
- animation: show 0.3s ease-in-out;
- }
- #bs_dialog h2{
- font-size: 24px;
- display: block;
- margin-bottom: 12px;
- font-weight: bold;
- }
- /***************************关闭close***************************/
- #bs_btn_close {
- position: absolute;
- top: 20px;
- right: 20px;
- margin: 3px;
- width: 24px;
- height: 24px;
- background: white;
- cursor: pointer;
- box-sizing: border-box;
- transition: all 0.3s ease-in-out;
- }
- #bs_btn_close:hover::before,
- #bs_btn_close:hover::after {
- background: red;
- }
- #bs_btn_close:before {
- position: absolute;
- content: '';
- width: 1px;
- height: 25px;
- background: #999;
- transform: rotate(45deg);
- top: -3px;
- left: 11px;
- }
- #bs_btn_close:after {
- content: '';
- position: absolute;
- width: 1px;
- height: 25px;
- background: #999;
- transform: rotate(-45deg);
- top: -3px;
- left: 11px;
- }
- /*************** 表格样式 *************/
- #bs_table {
- display: block;
- width: 300px;
- height: 220px;
- margin: auto;
- border-collapse: collapse;
- overflow-y: scroll;
- scrollbar-width: thin;
- }
- #bs_table td {
- border-bottom: 1px solid #ddd;
- font-size: 16px;
- padding: 5px 10px;
- }
- #bs_table td:first-child {
- width: 250px;
- text-align: left;
- max-width: 250px;
- word-break: break-all;
- }
- #bs_table td:last-child {
- width: 50px;
- text-align: right;
- }
- #bs_table tr:last-child td {
- border-bottom: none;
- }
- /* ****************插入行table ******************/
- #row_insert {
- margin: auto;
- margin-bottom: 10px;
- }
- /*****************滚动条******************/
- #bs_dialog ::-webkit-scrollbar {
- width: 5px;
- height: 10px;
- }
- #bs_dialog ::-webkit-scrollbar-track {
- width: 6px;
- background: rgba(#101F1C, 0.1);
- -webkit-border-radius: 2em;
- -moz-border-radius: 2em;
- border-radius: 2em;
- }
- #bs_dialog ::-webkit-scrollbar-thumb {
- background-color: rgba(144, 147, 153, .3);
- background-clip: padding-box;
- min-height: 28px;
- -webkit-border-radius: 2em;
- -moz-border-radius: 2em;
- border-radius: 2em;
- transition: background-color .3s;
- cursor: pointer;
- }
- #bs_dialog ::-webkit-scrollbar-thumb:hover {
- background-color: rgba(144, 147, 153, .5);
- }
- /**************底部按钮*****************/
- #bs_tool_row {
- width: 300px;
- display:flex;
- justify-content: center;
- align-items: center;
- margin: auto;
- margin-top: 10px;
- }
- #bs_tool_row button {
- width: 50px;
- padding: auto;
- margin: 0 5px;
- }
- #bs_overlay button:focus {
- outline: none;
- }
- #bs_overlay button {
- border: none;
- background-color: rgba(23, 171, 227, .8);
- color: #eee;
- border-radius: 5px;
- cursor: pointer;
- line-height: 28px;
- height: 28px;
- padding: 0 5px;
- }
- #bs_overlay button:hover {
- background-color: rgba(23, 171, 227, 1);
- }
- /******************输入框input*******************/
- #bs_input {
- width: 200px;
- border-radius: 5px;
- border: 1px solid;
- padding-left: 5px;
- }
- #bs_input:focus {
- box-shadow: 0 0 6px #2993e9;
- }
- /**********************************message_css********************************/
- #message-container {
- position: fixed;
- left: 0;
- top: 0;
- right: 0;
- display: flex;
- flex-direction: column;
- align-items: center;
- z-index: 10000;
- pointer-events: none; /* 允许鼠标事件穿透 */
- }
- #message-container .message {
- background: #fff;
- margin: 10px 0;
- padding: 0 10px;
- height: 40px;
- box-shadow: 0 0 10px 0 #eee;
- font-size: 14px;
- border-radius: 3px;
- display: flex;
- align-items: center;
- transition: height 0.2s ease-in-out, margin 0.2s ease-in-out
- }
- #message-container .message .text {
- color: #333;
- padding: 0 20px 0 5px
- }
- #message-container .message .close {
- cursor: pointer;
- color: #999
- }
- #message-container .message .icon-info {
- color: #0482f8
- }
- #message-container .message .icon-error {
- color: #f83504
- }
- #message-container .message .icon-success {
- color: #06a35a
- }
- #message-container .message .icon-warning {
- color: #ceca07
- }
- #message-container .message .icon-loading {
- color: #0482f8
- }
- @keyframes message-move-in {
- 0% {
- opacity: 0;
- transform: translateY(-100%)
- }
-
- 100% {
- opacity: 1;
- transform: translateY(0)
- }
- }
- #message-container .message.move-in {
- animation: message-move-in 0.3s ease-in-out
- }
- @keyframes message-move-out {
- 0% {
- opacity: 1;
- transform: translateY(0)
- }
-
- 100% {
- opacity: 0;
- transform: translateY(-100%)
- }
- }
- #message-container .message.move-out {
- animation: message-move-out 0.3s ease-in-out;
- animation-fill-mode: forwards
- }
- /************************外部样式***************************/
- @font-face {font-family: "icon";
- src: url('//at.alicdn.com/t/font_1117508_wxidm5ry7od.eot?t=1554098097778'); /* IE9 */
- src: url('//at.alicdn.com/t/font_1117508_wxidm5ry7od.eot?t=1554098097778#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAASwAAsAAAAACXAAAARhAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDXgqFdIUpATYCJAMcCxAABCAFhD0HXBtNCFGUL0qC7GuMwfUModQ5d89yCS2IguQlYTzox9H5PfHlpWFpEC0ubQKL/QJcYxBBtFZWz/RCiEgotIBChV2EASSXCBX5xofBiPtz7s0nnCJ450fACtP2peU0NJSTZudnZywngIyp2Q4m293vbjhIKItjihNIZS3s4mx2bTEk1jI8gciSLJCwesNvAaBfaMOve98XmEY5wnfz3a39QU5QZFVbdF5i+gcD/3G/4l/jK/C2ASA6KCowynZ8xxew8qJK+MQORO8xnOLqb1h0n09guNkGZLZsHVyQsoTLAnHcdykpB7QygxR6RZuwN8VNUNCXS/jGDev347+CKZoKj7Pj4KZDVn79eUjAP1zXN0C85syQD6JiJSKJ84nWk3JOYKWc4Z+/mbcHMVqvaH/98v5qf3X63Pk8/P8Eq3EwjKZ4qhS9/c/TEi0uHQnEAUm7kDyNSeXXO5Pkt51J8NuJSeH/DpOG/4cyi4p1i0djFD4DsZ8RG81VIUlYY9YzIfBOsZLGd25VXu8jbXf6O5mXeVhHN3GEsYVtgIyXm9AHradeL9uATxQOXxaLRydnjMJbK6L0zDFsY6Jt8bFHRO6RB/FTztgwMNYuFWkR8SNHnjiJz5JGGDLNn6gcgzdXhWUVo8vr46LysuGJDUxSWT6CbcHi0jX9JpvoOrSRi+KG10H6vQ0GY25JMkc36XmPmdEouyf9E33O07RhdBOWZPYHx3yQMC19ZCzZWkLAR35/5xmxDLYu9AqejrN5PTy2lgc0d06bS6k1uX9wWvJPVHBJZp7qnXWdMCJ3gR5WVL//cZmeBgxOanWDbCXDjsu4WzpKRUciLwh3CYkhh/Y3byXAmC7LvYr4Hs70do3p3dBqyD5xPqoJ9l+te2FzLY0T/svpCG1o9hqPZHJ33jEGNEMzFch0HfRQKdIox2/yaAiNEun5XePlysEZfBnivwZ9vTqLA8PGZXIbZLU687F4Dc46J/785DmxDLYu9IYAhKBE8yY+gOzpaGSIjSCNxyPLQad7CAZzBLGO9++/I0k9Awh1e/aHQUxK4TEZBN9kz/LCloUvZiR4c3V9wKqrB3uFwrz7bFSSASAVAeq6fPO1voHgXIHOGjnOkNpw6uHuco731Zx8UuFUQprkQdGZBlWaiySxEBojLIVW2gDDrTB98AgTWHIih2E5wyD0XkAx2iuoeq+RJIagMdkntHrvYbh9/o85wuJo9C6RanTAegbf5glTNbpqx5+hayIqFY7mvUKpfPDA6NCIP7yNCcoqG9SDO6Y1AyZ5DFulL4ZRxCGVPEBbD3lap3PDwyzpjYZsHpOqzSREacgBLM+Az8YlmOHUqpHVZ5DLiFAyQWCZryCIlcKBTDBqyEgGsi1NMut2Fas8cI0JJI0tYiQuBrY6N6OIAhxIk2cFkE0b4lXwpeYM80asqHSoW3m8P4dgODxBFyVqZDTRRhe9PDL/tykpY9uoVCni1PETt2BHXGHpkcokEI9SckkIAAAA') format('woff2'),
- url('//at.alicdn.com/t/font_1117508_wxidm5ry7od.woff?t=1554098097778') format('woff'),
- url('//at.alicdn.com/t/font_1117508_wxidm5ry7od.ttf?t=1554098097778') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
- url('//at.alicdn.com/t/font_1117508_wxidm5ry7od.svg?t=1554098097778#icon') format('svg'); /* iOS 4.1- */
- }
- .icon {
- font-family: "icon" !important;
- font-size: 16px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
- .icon-info:before {
- content: "\\e72a";
- }
- .icon-success:before {
- content: "\\e730";
- }
- .icon-loading:before {
- content: "\\e6a6";
- }
- .icon-close:before {
- content: "\\e6e9";
- }
- .icon-warning:before {
- content: "\\ebad";
- }
- .icon-error:before {
- content: "\\ebb2";
- }
- `;
- let bs_style_sheet = document.createElement('style');
- bs_style_sheet.innerHTML = bs_style_css;
- document.body.appendChild(bs_style_sheet);
- };
-
-
-
- /*****************************配置对话框函数*************************/
-
- var isButtonClicked = false;
- function openConfigDialog() {
- if (isButtonClicked) {
- return; // 如果按钮已被点击,则忽略后续点击事件
- }
- isButtonClicked = true;
-
- // 创建一个遮罩层及其内部的对话框
- const bs_overlay_html = `
- <div id="bs_dialog">
- <h2 style="margin-top: -4px;">BlockSpeech
- <a title="前往插件详情页面查看更多插件信息"style="font-size: 8px; color: #17abe3; text-decoration: underline;"target="_blank"href="https://greasyfork.org/zh-CN/scripts/472846">插件信息</a>
- </h2>
- <table id="row_insert">
- <tr>
- <td>ID:</td>
- <td><input id="bs_input" type="text" placeholder="请输入需要屏蔽的ID"></td>
- <td><button id="bs_btn_insert">添加</button></td>
- </tr>
- </table>
- <table id="bs_table"></table>
- <div id="bs_tool_row"><button id="bs_btn_save">保存</button><button id="bs_btn_clear">清空</button></div>
- <div id="bs_btn_close"></div>
- </div>
- `;
- let bs_overlay = document.createElement('div');
- bs_overlay.id = 'bs_overlay';
- bs_overlay.innerHTML = bs_overlay_html;
- document.body.appendChild(bs_overlay);
-
- /****************************************各个function*****************************************/
-
- // 获取全局变量bs_table
- let bs_table = document.getElementById('bs_table');
-
- // 清空表格
- function bs_tableClear() {
- bs_table.innerHTML = '';
- }
-
- // 保存表格到GM_value
- function bs_tableSave() {
- let bs_list = [];
- document.querySelectorAll('.bs_user_id').forEach(function (item) {
- bs_list.unshift(item.textContent);
- })
- GM_setValue("bs_list", bs_list);
- console.log("【blockspeech】list:" + bs_list);
- console.log("【blockspeech】save ok");
- stopObserver();
- startObserver(bs_list);
- }
-
- // 删除行
- function bs_deleteRow(btn_del) {
- // 获取按钮的父元素<tr>
- let row_del = btn_del.parentNode.parentNode;
- // 从表格中删除该行
- row_del.parentNode.removeChild(row_del);
- }
-
-
- // 插入行
- function bs_insertRow(userid) {
- let new_row = bs_table.insertRow(0);
- let cell1 = new_row.insertCell(0);
- cell1.className = 'bs_user_id';
- cell1.innerHTML = userid;
- let cell2 = new_row.insertCell(1);
- let bs_btn_del = document.createElement("button");
- bs_btn_del.innerText = "删除";
- // 添加点击事件
- bs_btn_del.addEventListener("click", function () {
- bs_deleteRow(this);
- });
- cell2.appendChild(bs_btn_del);
- }
-
- // 判断input内容
- function judgeIfok(new_id) {
- if (new_id == '') {
- console.log("【blockspeech】user id can not be null");
- message.show({
- type: 'warning',
- text: 'ID内容不能为空',
- });
- return false;
- }
- let user_id_objlist = document.querySelectorAll('.bs_user_id');
- let user_id_list = [];
- user_id_objlist.forEach(function (obj) {
- user_id_list.push(obj.innerHTML);
- })
- if (user_id_list.includes(new_id)) {
- console.log("【blockspeech】user id had been added");
- message.show({
- type: 'info',
- text: '此ID已被添加,请勿重复添加',
- });
- return false;
- }
- return true;
- }
-
- bs_Init();
-
- // 初始化表格
- function bs_Init() {
- bs_tableClear();
- let bs_list = GM_getValue("bs_list");
- if (bs_list) {
- bs_list.forEach(function (item) {
- bs_insertRow(item);
- });
- }
- }
-
-
- /****************************************添加点击事件*****************************************/
-
- // 给插入按钮添加点击事件
- document.getElementById('bs_btn_insert').addEventListener("click", function () {
- let bs_input = document.getElementById('bs_input');
- if (judgeIfok(bs_input.value.trim())) {
- bs_insertRow(bs_input.value.trim());
- message.show({
- type: 'success',
- text: '添加"' + bs_input.value.trim() + '"成功',
- });
- }
- bs_input.value = '';
- });
-
- // 给保存按钮添加点击事件
- document.getElementById('bs_btn_save').addEventListener("click", () => {
- bs_tableSave();
- message.show({
- type: 'success',
- text: '保存成功',
- });
- });
-
- // 给清空按钮添加点击事件
- document.getElementById('bs_btn_clear').addEventListener("click", () => {
- bs_tableClear();
- message.show({
- type: 'success',
- text: '清空列表成功',
- });
- });
-
- // 给关闭按钮添加点击事件
- let bs_btn_close = document.getElementById('bs_btn_close');
- bs_btn_close.addEventListener('click', function () {
- bs_overlay.remove();
- isButtonClicked = false;
- });
- }
-
- function bs_startMonitor() {
- // 直播间内执行
- if (document.querySelector('.layout-Player-aside')) {// 如果页面有侧边栏则该页面为直播间
- console.log("【blockspeech】Is live room.");
-
- let bs_list = GM_getValue('bs_list');
- if (bs_list) {
- setTimeout(() => startObserver(bs_list), 8000);
- }
- }
- }
-
- function startObserver(bs_list = GM_getValue('bs_list')) {
- let chat_list = document.querySelector("#js-barrage-list");
- if (chat_list) {
- // 创建 MutationObserver 对象
- bs_observer = new MutationObserver(function (mutations) {
- // 遍历每个变化
- mutations.forEach(function (mutation) {
- // 遍历每个被添加的元素
- mutation.addedNodes.forEach(function (node) {
- // 判断节点元素
- if (node.nodeType === 1 && node.nodeName === 'LI' && node.classList.contains('Barrage-listItem')) {
- // 获取用户昵称对象
- let userchat = node.querySelector(".Barrage-nickName");
- // 获取第用户昵称 文本内容
- let userid = userchat.title;
- if (bs_list.includes(userid)) {
- node.remove();
- console.log("【blockspeech】remove chat from " + userid + " ok");
- }
- }
- });
- });
- });
- // 配置 MutationObserver 对象
- let config = { childList: true, subtree: true };
- // 在聊天信息列表上开始监视变化
- bs_observer.observe(chat_list, config);
- console.log("【blockspeech】strat observer success...");
- message.show({
- type: 'success',
- text: '屏蔽ID消息开启成功',
- });
- } else {
- console.log("【blockspeech】因网络原因屏蔽失败,请刷新页面或者点击设置页面的保存按钮重新加载屏蔽");
- }
- } // startObserver -- funciton
-
- function stopObserver() {
- if (bs_observer) {
- bs_observer.disconnect();
- bs_observer = null;
- console.log('【blockspeech】Stop observer ok');
- } else {
- console.log('【blockspeech】there is no observer task');
- }
- }
-
- /********************************** message_js****************************/
- class Message {
- constructor() {
- const containerId = 'message-container';
- this.containerEl = document.getElementById(containerId);
- if (!this.containerEl) {
- this.containerEl = document.createElement('div');
- this.containerEl.id = containerId;
- document.body.appendChild(this.containerEl)
- }
- }
- show({
- type = 'info',
- text = '',
- duration = 2000,
- closeable = false
- }) {
- let messageEl = document.createElement('div');
- messageEl.className = 'message move-in';
- messageEl.innerHTML = `<span class="icon icon-${type}"></span><div class="text">${text}</div>`;
- if (closeable) {
- let closeEl = document.createElement('div');
- closeEl.className = 'close icon icon-close';
- messageEl.appendChild(closeEl);
- closeEl.addEventListener('click', () => {
- this.close(messageEl)
- })
- }
- this.containerEl.appendChild(messageEl);
- if (duration > 0) {
- setTimeout(() => {
- this.close(messageEl)
- }, duration)
- }
- }
- close(messageEl) {
- messageEl.className = messageEl.className.replace('move-in', '');
- messageEl.className += 'move-out';
- messageEl.addEventListener('animationend', () => {
- messageEl.setAttribute('style', 'height: 0; margin: 0')
- });
- messageEl.addEventListener('transitionend', () => {
- messageEl.remove()
- })
- }
- }
-
- // 主函数main
- function main() {
- // 初始化css
- bsStyleInit();
-
- // 消息对象实例化
- message = new Message();
-
- // 添加配置选项
- GM_registerMenuCommand('配置屏蔽ID', openConfigDialog);
-
- // 启动监听
- window.onload = function () {
- console.log("【blockspeech】window.onload...");
- bs_startMonitor();
- }
- }
-
- // 进入main
- main();
- } // window.onload
- })(); // 主function -- function