您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
用于b站评论区屏蔽at信息的脚本
// ==UserScript== // @name 评论推土机 // @namespace tuituji // @version 2.3 // @description 用于b站评论区屏蔽at信息的脚本 // @author leostou // @match https://www.bilibili.com/video/* // @match https://www.bilibili.com/read/* // @grant GM_xmlhttpRequest // @connect * // @icon https://static.hdslb.com/images/favicon.ico // @license MIT // ==/UserScript== (function () { 'use strict'; let commentDom = null let commentOb = null let officialUrl = 'https://api.bilibili.com/x/space/acc/info?mid=' let fansUrl = 'https://api.bilibili.com/x/relation/stat?vmid=' let t = 0 let maxT = 20 let ready = false let timer = null /*可配置项*/ let showTips = true //进入页面是否提示开启脚本,false为直接启动脚本 let officialCheck = true // 是否开启认证校验,关闭则除白名单外的全部删除 let bigUserFans = 200000 //认证校验开启时,非认证号粉丝量在200000以上的标记标记=>☆粉丝认证 let smallUserFans = 2000//认证校验开启时,非认证号粉丝量在2000以上的标记标记=>☆未来可期 let rightTagList = ['business', 'personal'] //头像右下角认证信息 /*白名单,不过滤 id: 白名单的id prev: 前缀 reName: 重命名 suffix: 后缀 color: 匹配后的颜色 */ let whiteList = [{ id: '', prev: '', reName: '', suffix: '', color: '' }, { id: '', prev: '', reName: '', suffix: '', color: '' }] /* 认证信息,认证校验开启时,非认证号粉丝量在200000以上的标记标记=>☆机构认证/☆知名认证 type: 0 知名个人认证,1机构认证,10粉丝数大于2000,99粉丝数大于200000 prev: 前缀 suffix: 后缀 color: 匹配后的颜色 */ let officialList = [{ type: 0, prev: '', suffix: '[☆知名认证]\n', color: '#ffc62e', }, { type: 1, prev: '', suffix: '[☆机构认证]\n', color: '#3ec6f3', }, { type: 10, prev: '', suffix: '[☆未来可期]\n', color: '#95ddb2' }, { type: 99, prev: '', suffix: '[☆粉丝认证]\n', color: '#ff0000' }] const initOb = (dom) => { commentOb = new MutationObserver((mutations, ob) => { handlerComment(mutations[0].addedNodes) }) commentOb.observe(dom, { childList: true }) } const initTimer = () => { timer = setInterval(() => { // 10秒内没绑定成功关闭脚本 if (ready || t >= maxT) { clearInterval(timer) timer = null } else { t++ commentDom = document.querySelector('.comment-list') let list = commentDom ? commentDom.querySelectorAll('.list-item') : null handlerComment(list) if (list) { ready = true initOb(commentDom) } } }, 500) } // 评论列表绑定dom监听 const handlerComment = (list) => { if (!list || list.length === 0) return Array.from(list).forEach(async item => { // 判断是否发言人是否有认证信息 if (rightTagMatch(item)) return // 是否存在白名单 if (whiteListMatch(item, 'dom')) return // 处理at逻辑 atFn(item) }) } const rightTagMatch = (dom) => { const rightTagClassName = dom.querySelector('span.bili-avatar-icon').className return rightTagList.some(item => { return rightTagClassName.includes(item) }) } const whiteListMatch = (dom, type) => { let mid = type === 'dom' ? dom.querySelector('.user-face>a').dataset.usercardMid : dom.pathname.substr(1) if (whiteList.some(item => item.id === mid)) { type === 'dom' ? reWrite(dom.querySelector('.con .user>a'), '', mid) : reWrite(dom, '', mid) return true } return false } const atFn = async (dom) => { // 判断发言是否存在at let atReg = /@.*?\s/g let target = dom.querySelector('.con .text') if (atReg.test(target.innerText)) { // 是否校验认证用户 const data = await textMatch(target) if (!data.some(item => item === true)) { dom.style.display = 'none' } } } const reWrite = (dom, type, id) => { let data if (type === 'official') { data = officialList.find(item => item.type === id) } else { data = whiteList.find(item => item.id === id) } let name = data.reName ? data.reName : dom.innerText dom.innerText = data.prev + name + data.suffix dom.style.color = data.color ? data.color : dom.style.color } const getInfo = (id, type) => { return new Promise((resolve, reject) => { let fullUrl = (type === 'official' ? officialUrl : fansUrl) + id GM_xmlhttpRequest({ url: fullUrl, method: "GET", headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36' }, onload: function (xhr) { resolve(xhr.responseText) } }); }) } const officialTest = (data) => { let res = JSON.parse(data) if (res && res.data) { return res.data.official.type } return -1 } const fansTest = (data) => { let res = JSON.parse(data) if (res && res.data) { if (res.data.follower < smallUserFans) { return -1 } else if (res.data.follower >= bigUserFans) { return 99 } else { return 10 } } } const textMatch = async (commentDom) => { let aTagArr = Array.from(commentDom.querySelectorAll('a')) return await Promise.all(aTagArr.map(aItem => new Promise(async (res, rej) => { if (whiteListMatch(aItem, 'atUser')) { res(true) } if (!officialCheck) { res(false) } let mid = aItem.pathname.substr(1) // 发送请求获取认证状态 let type = -1 const data = await getInfo(mid, 'official') type = officialTest(data) if (type === -1) { const data = await getInfo(mid, 'fans') type = fansTest(data) } if (textReplace(type, aItem)) { res(true) } res(false) }))) } const textReplace = (type, aItem) => { // 认证条件判断成功 if (type !== -1) { reWrite(aItem, 'official', type) return true } return false } const initTips = () => { if (!showTips) { initTimer() return } const box = document.createElement('div') box.style = 'position:fixed;width:200px;height:100px;left:-200px;top:500px;border-radius:10px;border:1px solid #37c8f7;background-color:#fff; text-align: center;transition :all linear 0.25s;opacity:.5;z-index:9999' const boxShow = () => { box.style.left = '0px' box.style.opacity = 1 } const boxHide = () => { box.style.left = '-200px' box.style.opacity = '0' } const p = document.createElement('p') p.style = 'padding:10px' p.innerText = '是否开启坟头推土机脚本' const btnBox = document.createElement('div') btnBox.style.marginTop = '30px' let btnStyle = 'padding:5px 10px;border:1px solid #ccc;border-radius:5px;margin:5px;user-select:none;cursor:pointer' const btnCancel = document.createElement('span') btnCancel.innerText = '取消' btnCancel.style = btnStyle btnCancel.addEventListener('click', () => { boxHide() setTimeout(textBoxShow, 250) }) const btnConfirm = document.createElement('span') btnConfirm.innerText = '确定' btnConfirm.style = btnStyle btnConfirm.style.backgroundColor = '#00a1d6' btnConfirm.style.color = '#fff' btnConfirm.addEventListener('click', () => { initTimer() boxHide() }) const textBox = document.createElement('div') textBox.style = 'position:fixed;left:-60px;top:500px;background-color:#00a1d6;width:30px;padding:10px 10px 10px 0px;font-size:16px;color:#fff;font-weight:700;writing-mode: vertical-rl;transition :all linear 0.25s;opacity:0;cursor: pointer;border-radius: 0 10px 10px 0;' textBox.innerText = '评论推土机' textBox.addEventListener('click', () => { textBoxHide() setTimeout(boxShow, 250) }) const textBoxHide = () => { textBox.style.left = '-50px' textBox.style.opacity = '0' } const textBoxShow = () => { textBox.style.left = '-10px' textBox.style.opacity = 1 } const textBoxMouseOver = () => { textBox.style.transition = 'all .25s linear' textBox.style.left = '0px' } const textBoxMouseLeave = () => { textBox.style.transition = 'all .25s linear' textBox.style.left = '-10px' } textBox.addEventListener('mouseover', textBoxMouseOver) textBox.addEventListener('mouseleave', textBoxMouseLeave) document.body.appendChild(textBox) document.body.appendChild(box) box.appendChild(p) box.appendChild(btnBox) btnBox.appendChild(btnCancel) btnBox.appendChild(btnConfirm) document.querySelector('#app').addEventListener('click', (e) => { if (commentOb) return boxHide() setTimeout(textBoxShow, 250) }, false) setTimeout(() => { textBoxShow() }) } initTips() })()