// ==UserScript==
// @name 百度贴吧净化器
// @version 0.2.1
// @description 在访问百度贴吧时,对包含关键字的帖子或用户的帖子进行屏蔽,也可以高亮显示。控制面板可以添加或删除关键字、用户名,净化方式。
// @author yu4n
// @license MIT
// @match *://tieba.baidu.com/f?*
// @match *://tieba.baidu.com/p/*
// @grant window.onurlchange
// @grant GM_addStyle
// @namespace https://greasyfork.org/users/1323535
// ==/UserScript==
(function() {
'use strict';
// #region 本地数据处理
let keywordList, usernameList, cleanseMethod;
function handleOldVersionData() {
let t = localStorage.getItem('titleKeywords');
let u = localStorage.getItem('usernameKeywords');
let c = localStorage.getItem('cleanseType');
if(t != null) {
localStorage.setItem('keywordList', t);
localStorage.removeItem('titleKeywords');
}
if(u != null) {
localStorage.setItem('usernameList', u);
localStorage.removeItem('usernameKeywords');
}
if(c != null) {
localStorage.setItem('cleanseMethod', c);
localStorage.removeItem('cleanseType');
}
}
handleOldVersionData();
function loadLocalStorage() {
keywordList = mySplit(localStorage.getItem('keywordList'), ',');
usernameList = mySplit(localStorage.getItem('usernameList'), ',');
cleanseMethod = localStorage.getItem('cleanseMethod') == null ? 'shield' : localStorage.getItem('cleanseMethod');
function mySplit(str1, str2) {
if (str1 == null || '' == str1) return [];
return str1.split(str2);
}
}
function saveLocalStorage() {
localStorage.setItem('keywordList', keywordList.join());
localStorage.setItem('usernameList', usernameList.join());
localStorage.setItem('cleanseMethod', cleanseMethod);
}
// #endregion
// #region 页面数据处理
const pageData = {
f: {
name: 'frs',
description: '某个吧的主页',
getAllPost() { // 获取所有帖子
return document.getElementById('thread_list').childNodes;
},
// getTopPostList() { // 获取置顶帖
// return document.getElementById('thread_top_list').childNodes;
// },
getGeneralPostList() { // 获取正常帖
return document.getElementById('thread_list').querySelectorAll(':scope > li.j_thread_list.clearfix.thread_item_box');
},
getAdPostList() { // 获取广告帖
return document.getElementById('thread_list').querySelectorAll(':scope > div');
},
getPostId(post) { // 获取正常帖的id
return post.dataset.tid;
},
getPostTitle(post) { // 获取正常帖的标题
return post.querySelector('a.j_th_tit').innerText;
},
getPostAuthor(post) { // 获取正常帖的作者
return post.querySelector('a.frs-author-name.j_user_card').innerText;
},
getPostAuthorId(post) { // 获取正常帖的作者id
return parseInt(post.querySelector('span.tb_icon_author').dataset.field.substring(11));
},
getPostAuthorLv(post) { // 获取用户等级,未找到方法
return 0;
},
getPostContent(post) { // 获取正常帖的内容
return post.querySelector('div.threadlist_abs.threadlist_abs_onlyline').innerText;
},
shieldPost(post) { // 屏蔽帖子
post.style.display = 'none';
},
highLightPost(post) { // 高亮显示帖子
post.style.backgroundColor = 'rgba(255, 0, 0, 0.15)';
},
recoverPost(post) { // 复原帖子
post.style.display = 'list-item';
post.style.backgroundColor = '#fff';
}
},
p: {
name: 'post',
description: '帖子详情页',
getAllPost() { // 获取所有帖子
return document.getElementById('j_p_postlist').childNodes;
},
getGeneralPostList() { // 获取正常帖
let l = document.getElementById('j_p_postlist');
if(l.firstElementChild.id == 'j_p_postlist') {
return l.firstElementChild.querySelectorAll(':scope > div.l_post.l_post_bright.j_l_post.clearfix');
}
return l.querySelectorAll(':scope > div.l_post.l_post_bright.j_l_post.clearfix');
},
getAdPostList() { // 获取广告帖
let l = document.getElementById('j_p_postlist');
if(l.firstElementChild.id == 'j_p_postlist') {
return l.firstElementChild.querySelectorAll(':scope > div:not([data-pid])');
}
return l.querySelectorAll(':scope > div:not([data-pid])');
},
getPostId(post) { // 获取帖的id
return post.dataset.pid;
},
getPostTitle(post) { // 获取帖的标题
return '';
},
getPostAuthor(post) { // 获取帖的作者
return post.querySelector('a.p_author_name.j_user_card').innerText;
},
getPostAuthorId(post) { // 获取帖的作者id
return parseInt(post.querySelector('d_name').dataset.field.substring(11));
},
getPostAuthorLv(post) { // 获取用户等级
return parseInt(post.querySelector('div.d_badge_lv').innerText);
},
getPostContent(post) { // 获取帖的内容
return post.querySelector('div.d_post_content.j_d_post_content').innerText;
},
shieldPost(post) { // 屏蔽帖子
post.style.display = 'none';
},
highLightPost(post) { // 高亮显示帖子
post.style.background = 'rgba(255, 0, 0, 0.15) url()';
post.lastElementChild.style.backgroundColor = 'transparent';
},
recoverPost(post) { // 复原帖子样式
post.style.display = 'block';
post.style.background = '#fff url(//tb2.bdstatic.com/tb/static-pb/widget/post_list/img/bg_ba2195f.jpg)';
}
}
};
let page, generalPostList, adPostList;
// 加载页面信息
function loadPageInfo() {
page = pageData[window.location.href.split('/')[3].substring(0, 1)];
generalPostList = page.getGeneralPostList() == null ? [] : page.getGeneralPostList();
adPostList = page.getAdPostList() == null ? [] : page.getAdPostList();
}
// #endregion
// #region 净化
let cleansePostList;
// 移除广告
function removeAds() {
adPostList.forEach( (item) => {
item.remove();
});
}
// 净化帖子
function cleansing() {
cleansePostList = [];
// 通过帖子信息(标题、作者、内容)判定是否需要屏蔽
generalPostList.forEach( (item) => {
try {
let title = page.getPostTitle(item);
let author = page.getPostAuthor(item);
let content = page.getPostContent(item);
let postText = `${title}${content}`;
if(usernameList.includes(author) || _includes(postText, keywordList)) {
cleansePostList.push(item);
}
} catch (error) {
item.remove();
}
});
switch (cleanseMethod) {
case 'shield':
cleansePostList.forEach( (item) => { page.shieldPost(item); });
break;
case 'highLight':
cleansePostList.forEach( (item) => { page.highLightPost(item); });
break;
default:
break;
}
function _includes(str, arr) {
for (let i = 0; i < arr.length; i++) {
if(str.includes(arr[i])) return true;
}
return false;
}
}
// 复原帖子
function recoverPageUI() {
if(cleansePostList != null && cleansePostList.length >= 1) {
cleansePostList.forEach( (item) => { page.recoverPost(item); });
}
}
// #endregion
// #region 控制面板
function createCleanseControlPanel() {
// 面板按钮
let ul = document.querySelector('ul.tbui_aside_float_bar');
let li = document.createElement('li');
li.style = 'height: 52px';
li.className = 'tbui_aside_fbar_button';
let div = document.createElement('div');
div.id = 'ce_cleanseEntrance';
div.style = 'background-color: #fff; padding: 5px; height: 35px; cursor: pointer';
div.innerHTML = `<svg t="1719153331127" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11652" width="35" height="35"><path d="M1012.297519 561.529003s-36.97919 12.388029-51.70153 94.389383-83.203178 202.114386-287.23586 119.673904c0 0-67.46391-76.708708-3.073895-177.107209 55.491897-86.485081 131.045005-105.205796 342.011285-36.956078z" fill="#4cd362" p-id="11653"></path><path d="M717.134246 1024h-140.197355c25.839209-314.323117-205.257617-669.76247-205.257618-669.76247s328.167201 218.408342 345.454973 669.76247z" fill="#4cd362" p-id="11654"></path><path d="M187.176226 146.137137c90.321672 3.212567 171.028755 14.005868 240.364737 32.148784 66.701214 17.472667 122.655351 41.740261 166.406355 72.086308 35.962262 25.007177 63.627319 54.336298 82.278699 87.201553a244.409335 244.409335 0 0 1-33.48928 283.052589 254.717284 254.717284 0 0 1-186.51379 81.284883c-8.089198 0-16.317068-0.392904-24.429378-1.1556a279.655126 279.655126 0 0 1-60.992551-12.850269c-99.381574-32.772807-163.471133-85.814833-195.596804-162.223085-34.228863-81.37733-26.856137-173.98709-14.653005-237.337065 11.047533-57.271521 19.783867-104.350652 26.625017-142.208098M71.385137 0S60.152708 76.570036 24.35223 262.113122c-39.082382 202.85397-0.670248 457.617478 303.044463 557.507516a417.079041 417.079041 0 0 0 91.407936 19.206067q18.836275 1.756512 37.372094 1.756512c220.580869 0 405.8235-183.763463 387.957929-413.265563-17.264659-221.482237-209.81068-413.70469-688.991649-420.638288C128.055746 4.137047 100.182682 1.918295 71.338913 0.046224z" fill="#4cd362" p-id="11655"></path><path d="M617.174872 644.616621a254.832844 254.832844 0 0 1-88.126032 46.616891 1122.06419 1122.06419 0 0 1 34.922222 134.326909 393.2968 393.2968 0 0 0 108.811267-49.921907 870.097233 870.097233 0 0 0-55.607457-131.021893z" fill="#4cd362" p-id="11656"></path></svg>`;
li.appendChild(div);
ul.insertBefore(li, ul.children[0]);
// 控制面板CSS
GM_addStyle('#ce_panel { display: none; z-index: 10001; position: fixed; left: 50%; top: 15px; margin-left: -300px; width: 550px; padding: 10px 25px; background-color: #333; border-radius: 25px; color: #ccc; box-shadow: 0 0 15px black;}');
GM_addStyle('#ce_panel ul { padding-left: 0; }');
GM_addStyle('.ce_ranks>li { margin-top: 10px; }');
GM_addStyle('.ce_vertical_scroll { height:100px; overflow-y: scroll; background-color: #ccc; border-radius: 10px;}');
GM_addStyle('.ce_vertical_scroll::-webkit-scrollbar { width: 15px;}');
GM_addStyle('.ce_vertical_scroll::-webkit-scrollbar-button { display: none; }');
GM_addStyle('.ce_vertical_scroll::-webkit-scrollbar-track { display: none; }');
GM_addStyle('.ce_vertical_scroll::-webkit-scrollbar-thumb { background: #333; border: 3px solid #ccc; border-radius: 15px; }');
GM_addStyle('.ce_vertical_scroll>li { height: 24px; display: inline-block; margin: 5px; padding: 0 10px; background-color: #666; color: #ccc; border-radius: 15px;}');
GM_addStyle('.ce_btn { display: inline-block; text-align: center; vertical-align: middle; cursor: pointer; }');
GM_addStyle('.ce_btn_a { width: 15px; height: 15px; margin-left: 5px; border-radius: 15px; background-color: red; color: white; line-height: 15px; font-size: 18px; }');
GM_addStyle('.ce_btn_b { width: 50px; background-color: green; color: white;}');
GM_addStyle('.ce_btn_c { padding: 0 5px; background-color: rgb(200, 130, 0); color: white; border-radius: 5px; line-height: 38px; font-size: 22px; letter-spacing: 0.5rem;}');
GM_addStyle('.ce_input_text { color: #333; -webkit-text-fill-color: #333;');
// 控制面板
let panel = document.createElement('div');
panel.id = 'ce_panel';
panel.innerHTML = `
<ul class="ce_ranks">
<li><h1>百度贴吧净化器控制面板</h1></li>
<li><hr></li>
<li><h3>关键字列表</h3></li>
<li><ul id="ce_keywordsUl" class="ce_vertical_scroll"></ul></li>
<li><input id="ce_keyword" class="ce_input_text" type="text"><span id="ce_keyword_append" class="ce_btn ce_btn_b">添加</span></li>
<li><h3>用户名列表</h3></li>
<li><ul id="ce_usernameUl" class="ce_vertical_scroll"></ul></li>
<li><input id="ce_username" class="ce_input_text" type="text"><span id="ce_username_append" class="ce_btn ce_btn_b">添加</span></li>
<li>
<span>将包含关键字的帖子和这些用户的帖子:</span>
<span>
<input type="radio" id="ce_cleanseMethodChoice1" name="cleanseMethod" value="shield">
<label for="ce_cleanseMethodChoice1">屏蔽</label>
<input type="radio" id="ce_cleanseMethodChoice2" name="cleanseMethod" value="highLight">
<label for="ce_cleanseMethodChoice2">高亮显示</label>
</span>
</li>
<li><hr></li>
<li>
<span id="ce_save" class="ce_btn ce_btn_c">保存</span>
<span id="ce_undo" class="ce_btn ce_btn_c">撤销</span>
<span id="ce_close" class="ce_btn ce_btn_c">关闭</span>
</li>
</ul>`;
document.body.appendChild(panel);
return panel;
}
window.addEventListener('load', function() {
let panel = createCleanseControlPanel();
// 开关
document.getElementById('ce_cleanseEntrance').addEventListener('click', function() {
if (panel.style.display != 'block') {
updateKeywordsUI();
updateUsernameUI();
updateCleanseMethodUI();
panel.style.display = 'block';
} else {
panel.style.display = 'none';
}
});
// 保存应用
document.getElementById('ce_save').addEventListener('click', function() {
if (document.getElementById('ce_cleanseMethodChoice1').checked) {
cleanseMethod = 'shield';
} else if (document.getElementById('ce_cleanseMethodChoice2').checked) {
cleanseMethod = 'highLight';
}
saveLocalStorage();
recoverPageUI();
cleansing();
panel.style.display = 'none';
});
// 撤销修改
document.getElementById('ce_undo').addEventListener('click', function() {
loadLocalStorage();
updateKeywordsUI();
updateUsernameUI();
updateCleanseMethodUI();
});
// 关闭页面
document.getElementById('ce_close').addEventListener('click', function() {
panel.style.display = 'none';
});
// 添加关键字
document.getElementById('ce_keyword_append').addEventListener('click', function() {
keywordList.push(document.getElementById('ce_keyword').value);
document.getElementById('ce_keyword').value = '';
updateKeywordsUI();
});
// 添加用户名
document.getElementById('ce_username_append').addEventListener('click', function() {
usernameList.push(document.getElementById('ce_username').value);
document.getElementById('ce_username').value = '';
updateUsernameUI();
});
function updateKeywordsUI() {
let titleUl = document.getElementById('ce_keywordsUl');
let str = '';
if (keywordList != null) {
keywordList.forEach( (item, index) => {
str += `<li><span>${item}</span><span class="ce_btn ce_btn_a" data-index="${index}">×</span></li>`;
});
}
titleUl.innerHTML = str;
setTimeout(null, 250);
titleUl.querySelectorAll('.ce_btn_a').forEach( (item) => {
item.addEventListener('click', function() {
keywordList.splice(Number(item.dataset.index), 1);
updateKeywordsUI();
});
});
}
function updateUsernameUI() {
let usernameUl = document.getElementById('ce_usernameUl');
let str = '';
if (usernameList != null) {
usernameList.forEach( (item, index) => {
str += `<li><span>${item}</span><span class="ce_btn ce_btn_a" data-index="${index}">×</span></li>`;
});
}
usernameUl.innerHTML = str;
setTimeout(null, 250);
usernameUl.querySelectorAll('.ce_btn_a').forEach( (item) => {
item.addEventListener('click', function() {
usernameList.splice(Number(item.dataset.index), 1);
updateUsernameUI();
});
});
}
function updateCleanseMethodUI() {
if(cleanseMethod == 'shield') {
document.getElementById('ce_cleanseMethodChoice1').checked = true;
} else if(cleanseMethod == 'highLight') {
document.getElementById('ce_cleanseMethodChoice2').checked = true;
}
}
});
// #endregion
// #region 入口
// 页面加载完毕
window.addEventListener('load', function() {
loadLocalStorage();
loadPageInfo();
removeAds();
cleansing();
});
// 翻页时等到广告出现,清除广告净化帖子
let setIntervalId;
window.addEventListener('urlchange', function() {
setIntervalId = setInterval(() => {
if (page.getAdPostList() != null && page.getAdPostList().length >= 1) {
loadPageInfo();
setTimeout(null, 250);
loadPageInfo();
removeAds();
cleansing();
clearInterval(setIntervalId);
}
}, 1000);
});
// 关闭页面时清除循环计时器
window.addEventListener('unload', function() {
if (setIntervalId != null) clearInterval(setIntervalId);
});
// #endregion
})();