Greasy Fork is available in English.
新网页百度贴吧增强,支持隐藏无用组件/屏蔽视频帖/屏蔽软广吧/签到/夜间模式适配
当前为
// ==UserScript==
// @name 新版百度贴吧增强-夜间/精简/签到
// @namespace http://greasyfork.icu/zh-CN/users/1069880-l-l
// @version 3.02
// @description 新网页百度贴吧增强,支持隐藏无用组件/屏蔽视频帖/屏蔽软广吧/签到/夜间模式适配
// @author Li
// @license MIT
// @match https://tieba.baidu.com/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @connect tieba.baidu.com
// @connect c.tieba.baidu.com
// @require https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js
// @icon https://tb3.bdstatic.com/tb/pc/pc-main-core/static/img/home_icon_min.f440eb33.png
// ==/UserScript==
(function () {
'use strict';
const isFirefox = /Firefox/i.test(navigator.userAgent);
const CONFIG_KEY = 'tieba_dark_mode_config';
const SIGN_RECORD_KEY = 'tieba_sign_record';
const defaultConfig = {
darkMode: true,
hideRightNav: false,
hideMyForums: false,
hidePlayedGames: false,
hideVideoPosts: false,
hideHelpPosts: false,
hideMenuItems: false,
blockForums: false,
blockedForumNames: [],
autoSign: false,
autoSignOnLoad: false,
signOncePerDay: false,
openInCurrentPage: true,
imgWheelMode: 0,
hideRelatedRec: false,
hideFeedAds: true,
openThreadInNewTab: false,
textBold: false
};
// 读取配置,兼容旧版
function getConfig() {
try {
const saved = GM_getValue(CONFIG_KEY);
if (saved) {
const config = JSON.parse(saved);
return {
...defaultConfig,
...config,
blockedForumNames: config.blockedForumNames || []
};
}
return defaultConfig;
} catch {
return defaultConfig;
}
}
function saveConfig(config) {
GM_setValue(CONFIG_KEY, JSON.stringify(config));
}
// 检查今天是否已经签到成功
function hasSignedToday() {
try {
const record = GM_getValue(SIGN_RECORD_KEY);
if (record) {
const data = JSON.parse(record);
const today = new Date().toDateString();
return data.date === today && data.success === true;
}
} catch {
}
return false;
}
// 记录今天的签到状态
function saveSignRecord(success) {
const record = {
date: new Date().toDateString(),
success: success
};
GM_setValue(SIGN_RECORD_KEY, JSON.stringify(record));
}
/*
* The script references some code from youxiaohou.com.
* MIT License.
*/
let currentConfig = getConfig();
/**
* 深色模式滤镜配置
* Chrome 用 SVG 引用,Firefox 用内联 data URI
* 反转滤镜用于图片视频,避免二次反色
*/
const FILTERS = {
chrome: '-webkit-filter: url(#dark-mode-filter) !important; filter: url(#dark-mode-filter) !important;',
chromeReverse: '-webkit-filter: url(#dark-mode-reverse-filter) !important; filter: url(#dark-mode-reverse-filter) !important;',
firefox: `filter: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><filter id="dark-mode-filter" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0.283 -0.567 -0.567 0 0.925 -0.567 0.283 -0.567 0 0.925 -0.567 -0.567 0.283 0 0.925 0 0 0 1 0"/></filter></svg>#dark-mode-filter') !important;`,
firefoxReverse: `filter: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><filter id="dark-mode-reverse-filter" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0.333 -0.667 -0.667 0 1 -0.667 0.333 -0.667 0 1 -0.667 -0.667 0.333 0 1 0 0 0 1 0"/></filter></svg>#dark-mode-reverse-filter') !important;`,
none: '-webkit-filter: none !important; filter: none !important;'
};
// Chrome 需要在 DOM 中插入 SVG 滤镜定义
function createSVGFilter() {
if (document.getElementById('dark-mode-svg')) return;
const svg = '<svg id="dark-mode-svg" style="height: 0; width: 0;">' +
'<filter id="dark-mode-filter" x="0" y="0" width="99999" height="99999">' +
'<feColorMatrix type="matrix" values="0.283 -0.567 -0.567 0 0.925 -0.567 0.283 -0.567 0 0.925 -0.567 -0.567 0.283 0 0.925 0 0 0 1 0"></feColorMatrix>' +
'</filter>' +
'<filter id="dark-mode-reverse-filter" x="0" y="0" width="99999" height="99999">' +
'<feColorMatrix type="matrix" values="0.333 -0.667 -0.667 0 1 -0.667 0.333 -0.667 0 1 -0.667 -0.667 0.333 0 1 0 0 0 1 0"></feColorMatrix>' +
'</filter>' +
'</svg>';
const container = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
container.innerHTML = svg;
const fragment = document.createDocumentFragment();
while (container.firstChild) {
fragment.appendChild(container.firstChild);
}
document.head.appendChild(fragment);
}
// 额外样式修正,主要处理按钮和弹窗的显示问题
function getExtraStyles() {
const customRules = `
/* 按钮文字 */
.add-post .add-icon,
.add-post .add-icon *,
.add-post .ml-4, /* 发帖按钮 */
.center, /* 搜索按钮 */
.issue-btn.button-wrapper--primary .center,
.upload-btn,
.dialog-btn.button-wrapper--primary .center {
${FILTERS.none}
color: #000000 !important;
fill: #000000 !important;
}
/* 图标 */
.card-header img[src*="topic-header"],
.card-header img[src*="forum-header"] {
${FILTERS.none}
}
/* 弹窗遮罩 */
div.publisher-warp,
div.dialog-mask,
div.el-image-viewer__mask,
div.message-warp {
${FILTERS.none}
background-color: rgba(255, 255, 255, 0.4) !important;
}
/* 文本内容不反转(包括emoji) */
.text, .pb-content-item, .comment-content, .post-content, .reply-content {
${FILTERS.none}
}
${currentConfig.textBold ? `
/* 文本过细加粗 */
html {
text-shadow: 0 0 0 !important;
}
` : ''}
`;
return customRules;
}
function createDarkStyles() {
if (document.getElementById('dark-mode-style')) return;
const mainFilter = isFirefox ? FILTERS.firefox : FILTERS.chrome;
const reverseFilter = isFirefox ? FILTERS.firefoxReverse : FILTERS.chromeReverse;
const css = `
@media screen {
html {
${mainFilter}
scrollbar-color: #454a4d #202324;
}
/* 反转黑名单 */
img,
video,
iframe,
canvas,
:not(object):not(body) > embed,
object,
svg image,
[style*="background:url"],
[style*="background-image:url"],
[style*="background: url"],
[style*="background-image: url"],
[background],
twitterwidget,
.sr-reader,
.no-dark-mode,
.sr-backdrop {
${reverseFilter}
}
/* 取消子元素滤镜 */
[style*="background:url"] *,
[style*="background-image:url"] *,
[style*="background: url"] *,
[style*="background-image: url"] *,
input,
[background] *,
img[src^="https://s0.wp.com/latex.php"],
twitterwidget .NaturalImage-image,
.art-bottom,
.art-bottom * {
${FILTERS.none}
}
/* 文本对比度 */
/* html {
text-shadow: 0 0 0 !important;
} */
/* 全屏模式 */
.no-filter,
:-webkit-full-screen,
:-webkit-full-screen *,
:-moz-full-screen,
:-moz-full-screen *,
:fullscreen,
:fullscreen * {
${FILTERS.none}
}
.art-video-player.art-fullscreen,
.art-video-player.art-fullscreen * {
${FILTERS.none}
}
/* 滚动条样式 */
::-webkit-scrollbar {
background-color: #202324;
color: #aba499;
}
::-webkit-scrollbar-thumb {
background-color: #454a4d;
}
::-webkit-scrollbar-thumb:hover {
background-color: #575e62;
}
::-webkit-scrollbar-thumb:active {
background-color: #484e51;
}
::-webkit-scrollbar-corner {
background-color: #181a1b;
}
/* 页面背景 */
html {
background: #fff !important;
}
${getExtraStyles()}
}
@media print {
.no-print {
display: none !important;
}
}
`;
const style = document.createElement('style');
style.id = 'dark-mode-style';
style.textContent = css;
document.head.appendChild(style);
}
function setThemeColor() {
let meta = document.getElementsByName('theme-color')[0];
if (meta) {
meta.setAttribute('content', '#131313');
} else {
meta = document.createElement('meta');
meta.name = 'theme-color';
meta.content = '#131313';
document.head.appendChild(meta);
}
}
let iconObserver = null;
// 左上角图标的深色版本,深色模式下替换
function replaceHomeIcon() {
const base64Image = 'data:image/png;base64,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';
if (!base64Image) return;
const replaceIcon = () => {
const homeIcon = document.querySelector('img.home-icon[src*="home_icon"]');
if (homeIcon && homeIcon.src !== base64Image) {
homeIcon.src = base64Image;
}
};
replaceIcon();
if (!iconObserver) {
iconObserver = new MutationObserver(replaceIcon);
iconObserver.observe(document.body, { childList: true, subtree: true });
}
}
function stopReplaceHomeIcon() {
if (iconObserver) {
iconObserver.disconnect();
iconObserver = null;
}
}
let hideHomeCardsObserver = null;
// 主页卡片隐藏配置
const hideHomeCardsConfig = {
hideMyForums: { selector: '.list-container-wrapper.card-border', titleText: '我常逛的吧', dataKey: 'myForumsChecked' },
hidePlayedGames: { selector: '.list-container-wrapper.card-border', titleText: '我玩过的游戏', dataKey: 'playedGamesChecked' },
hideRightNav: { selector: '.right-nav-bar', titleText: null, dataKey: 'rightNavChecked', homeOnly: true }
};
function isHomePage() {
const path = location.pathname;
// 主页判断 及 202604更新新增搜索页
return path === '/' || path === '/index.html' || path === '/f/search/res';
}
function processHomeCards() {
Object.keys(hideHomeCardsConfig).forEach(configKey => {
if (!currentConfig[configKey]) return;
const config = hideHomeCardsConfig[configKey];
if (config.homeOnly && !isHomePage()) return;
const elements = document.querySelectorAll(config.selector);
elements.forEach(element => {
if (element.dataset[config.dataKey]) return;
element.dataset[config.dataKey] = 'true';
if (config.titleText) {
const titleDiv = element.querySelector('.list-title');
if (titleDiv && titleDiv.textContent.trim() === config.titleText) {
element.style.display = 'none';
}
} else {
element.style.display = 'none';
}
});
});
}
function startHomeCardsObserver() {
if (hideHomeCardsObserver) {
hideHomeCardsObserver.disconnect();
}
hideHomeCardsObserver = new MutationObserver(() => {
processHomeCards();
});
hideHomeCardsObserver.observe(document.body, {
childList: true,
subtree: true
});
processHomeCards();
}
function stopHomeCardsObserver() {
if (hideHomeCardsObserver) {
hideHomeCardsObserver.disconnect();
hideHomeCardsObserver = null;
}
}
function toggleHomeCard(configKey, hide) {
currentConfig[configKey] = hide;
saveConfig(currentConfig);
const config = hideHomeCardsConfig[configKey];
if (config.homeOnly && !isHomePage()) return;
const elements = document.querySelectorAll(config.selector);
if (hide) {
const hasAnyEnabled = Object.keys(hideHomeCardsConfig).some(key => currentConfig[key]);
if (hasAnyEnabled) {
startHomeCardsObserver();
}
} else {
elements.forEach(element => {
if (element.dataset[config.dataKey]) {
element.style.display = '';
delete element.dataset[config.dataKey];
}
});
const hasAnyEnabled = Object.keys(hideHomeCardsConfig).some(key => currentConfig[key]);
if (!hasAnyEnabled) {
stopHomeCardsObserver();
}
}
}
let hideVideoObserver = null;
// 隐藏视频帖 .video-wrapper
function hideVideoPosts() {
const processVideoPosts = () => {
const posts = document.querySelectorAll('.virtual-list-item');
posts.forEach(post => {
if (post.dataset.videoChecked) return;
post.dataset.videoChecked = 'true';
// 视频贴标签检测
const hasVideo = post.querySelector('.video-wrapper.thread-image') ||
post.querySelector('.art-video-player') ||
post.querySelector('video.art-video');
if (hasVideo) {
post.style.display = 'none';
}
});
};
if (hideVideoObserver) {
hideVideoObserver.disconnect();
}
hideVideoObserver = new MutationObserver(() => {
processVideoPosts();
});
hideVideoObserver.observe(document.body, {
childList: true,
subtree: true
});
processVideoPosts();
}
function showVideoPosts() {
if (hideVideoObserver) {
hideVideoObserver.disconnect();
hideVideoObserver = null;
}
const posts = document.querySelectorAll('.virtual-list-item');
posts.forEach(post => {
if (post.dataset.videoChecked) {
post.style.display = '';
delete post.dataset.videoChecked;
}
});
}
let hideHelpPostsObserver = null;
let hideMenuItemsObserver = null;
// 隐藏求助帖 .title-prefix
function hideHelpPosts() {
const processHelpPosts = () => {
const posts = document.querySelectorAll('.virtual-list-item');
posts.forEach(post => {
if (post.dataset.helpChecked) return;
post.dataset.helpChecked = 'true';
const titlePrefix = post.querySelector('.title-prefix');
if (titlePrefix && titlePrefix.textContent.trim() === '求助') {
post.style.display = 'none';
}
});
};
if (hideHelpPostsObserver) {
hideHelpPostsObserver.disconnect();
}
hideHelpPostsObserver = new MutationObserver(() => {
processHelpPosts();
});
hideHelpPostsObserver.observe(document.body, {
childList: true,
subtree: true
});
processHelpPosts();
}
function showHelpPosts() {
if (hideHelpPostsObserver) {
hideHelpPostsObserver.disconnect();
hideHelpPostsObserver = null;
}
const posts = document.querySelectorAll('.virtual-list-item');
posts.forEach(post => {
if (post.dataset.helpChecked) {
post.style.display = '';
delete post.dataset.helpChecked;
}
});
}
function toggleHelpPosts(hide) {
currentConfig.hideHelpPosts = hide;
saveConfig(currentConfig);
if (hide) {
hideHelpPosts();
} else {
showHelpPosts();
}
}
function hideMenuItems() {
const processMenuItems = () => {
document.querySelectorAll('.menu-item').forEach(item => {
if (item.dataset.menuItemChecked) return;
const useElement = item.querySelector('svg use');
if (useElement) {
const href = useElement.getAttribute('xlink:href');
if (href === '#down_client' || href === '#game_center') {
item.dataset.menuItemChecked = 'true';
const parent = item.parentElement;
if (parent.tagName === 'SPAN' && parent.classList.contains('popover__reference-wrapper')) {
parent.parentElement.style.display = 'none';
} else {
item.style.display = 'none';
}
}
}
});
};
if (!hideMenuItemsObserver) {
hideMenuItemsObserver = new MutationObserver(() => {
processMenuItems();
});
hideMenuItemsObserver.observe(document.body, {
childList: true,
subtree: true
});
}
processMenuItems();
}
function showMenuItems() {
if (hideMenuItemsObserver) {
hideMenuItemsObserver.disconnect();
hideMenuItemsObserver = null;
}
document.querySelectorAll('.menu-item').forEach(item => {
if (item.dataset.menuItemChecked) {
const parent = item.parentElement;
if (parent.tagName === 'SPAN' && parent.classList.contains('popover__reference-wrapper')) {
parent.parentElement.style.display = '';
} else {
item.style.display = '';
}
delete item.dataset.menuItemChecked;
}
});
}
function toggleMenuItems(hide) {
currentConfig.hideMenuItems = hide;
saveConfig(currentConfig);
if (hide) {
hideMenuItems();
} else {
showMenuItems();
}
}
let forumLinkHandler = null;
let threadLinkHandler = null;
function setupForumLinkHandler() {
if (!currentConfig.openInCurrentPage) return;
if (forumLinkHandler) return;
forumLinkHandler = function(e) {
if (e.ctrlKey || e.metaKey || e.shiftKey || e.button === 1) return;
const forumCard = e.target.closest('.forum-card-wrapper');
if (!forumCard) return;
// 带有 my-card 类或在 bar-wrap 容器内 当前页跳转黑名单
if (forumCard.classList.contains('my-card')) return;
if (forumCard.closest('.bar-wrap')) return;
const nameElement = forumCard.querySelector('.forum-name');
if (!nameElement) return;
const forumName = nameElement.textContent.trim();
if (!forumName) return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const targetUrl = `https://tieba.baidu.com/f?kw=${encodeURIComponent(forumName)}`;
const currentPath = window.location.pathname;
if (currentPath === '/') {
history.pushState(null, '', targetUrl);
window.dispatchEvent(new PopStateEvent('popstate'));
} else {
window.location.href = targetUrl;
}
};
document.addEventListener('click', forumLinkHandler, true);
}
function removeForumLinkHandler() {
if (forumLinkHandler) {
document.removeEventListener('click', forumLinkHandler, true);
forumLinkHandler = null;
}
}
function setupThreadLinkHandler() {
if (!currentConfig.openThreadInNewTab) return;
if (threadLinkHandler) return;
threadLinkHandler = function(e) {
const threadLink = e.target.closest('.thread-content-link');
if (!threadLink) return;
const href = threadLink.getAttribute('href');
if (!href || href === '#') return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
window.open(href, '_blank');
};
document.addEventListener('click', threadLinkHandler, true);
}
function removeThreadLinkHandler() {
if (threadLinkHandler) {
document.removeEventListener('click', threadLinkHandler, true);
threadLinkHandler = null;
}
}
function toggleOpenThreadInNewTab(enabled) {
currentConfig.openThreadInNewTab = enabled;
saveConfig(currentConfig);
if (enabled) {
setupThreadLinkHandler();
} else {
removeThreadLinkHandler();
}
}
function toggleOpenInCurrentPage(enabled) {
currentConfig.openInCurrentPage = enabled;
saveConfig(currentConfig);
if (enabled) {
setupForumLinkHandler();
} else {
removeForumLinkHandler();
}
}
function toggleVideoPosts(hide) {
currentConfig.hideVideoPosts = hide;
saveConfig(currentConfig);
if (hide) {
hideVideoPosts();
} else {
showVideoPosts();
}
}
let blockForumObserver = null;
/**
* 吧名屏蔽指定吧帖
* 从 .forum-name 元素中解析吧名,匹配黑名单
*/
function blockForumPosts() {
if (!currentConfig.blockForums || !currentConfig.blockedForumNames || !currentConfig.blockedForumNames.length) {
if (blockForumObserver) {
blockForumObserver.disconnect();
blockForumObserver = null;
}
return;
}
const processPosts = () => {
const posts = document.querySelectorAll('.virtual-list-item');
posts.forEach(post => {
if (post.dataset.forumChecked) return;
const forumNameEl = post.querySelector('.forum-name');
if (!forumNameEl) return;
post.dataset.forumChecked = 'true';
let fname = forumNameEl.textContent.trim().replace(/\s+/g, '');
if (fname.endsWith('吧')) {
fname = fname.slice(0, -1);
}
if (fname && currentConfig.blockedForumNames.includes(fname)) {
post.style.display = 'none';
}
});
};
if (blockForumObserver) {
blockForumObserver.disconnect();
}
blockForumObserver = new MutationObserver(() => {
processPosts();
});
blockForumObserver.observe(document.body, {
childList: true,
subtree: true
});
processPosts();
}
function toggleBlockForums(enabled) {
currentConfig.blockForums = enabled;
saveConfig(currentConfig);
if (enabled) {
blockForumPosts();
} else {
if (blockForumObserver) {
blockForumObserver.disconnect();
blockForumObserver = null;
}
const posts = document.querySelectorAll('.virtual-list-item');
posts.forEach(post => {
post.style.display = '';
delete post.dataset.forumChecked;
});
}
}
function addBlockedForum(forumName) {
if (!forumName || forumName.trim() === '') return false;
if (!currentConfig.blockedForumNames) {
currentConfig.blockedForumNames = [];
}
const trimmedName = forumName.trim();
if (!currentConfig.blockedForumNames.includes(trimmedName)) {
currentConfig.blockedForumNames.push(trimmedName);
saveConfig(currentConfig);
if (currentConfig.blockForums) {
blockForumPosts();
}
return true;
}
return false;
}
function removeBlockedForum(forumName) {
if (!currentConfig.blockedForumNames) {
currentConfig.blockedForumNames = [];
return false;
}
const index = currentConfig.blockedForumNames.indexOf(forumName);
if (index > -1) {
currentConfig.blockedForumNames.splice(index, 1);
saveConfig(currentConfig);
if (currentConfig.blockForums) {
const posts = document.querySelectorAll('.virtual-list-item[data-track]');
posts.forEach(post => {
post.style.display = '';
delete post.dataset.forumChecked;
});
blockForumPosts();
}
return true;
}
return false;
}
function enableDarkMode() {
if (!currentConfig.darkMode) return;
if (!isFirefox) {
createSVGFilter();
}
createDarkStyles();
setThemeColor();
replaceHomeIcon();
}
function applyHomeCardSettings() {
const hasAnyEnabled = Object.keys(hideHomeCardsConfig).some(key => currentConfig[key]);
if (hasAnyEnabled) {
startHomeCardsObserver();
} else {
stopHomeCardsObserver();
}
}
function applyVideoPostsSetting() {
if (currentConfig.hideVideoPosts) {
hideVideoPosts();
} else if (hideVideoObserver) {
hideVideoObserver.disconnect();
hideVideoObserver = null;
}
}
function applyBlockForumsSetting() {
if (currentConfig.blockForums) {
blockForumPosts();
} else if (blockForumObserver) {
blockForumObserver.disconnect();
blockForumObserver = null;
}
}
function applyHelpPostsSetting() {
if (currentConfig.hideHelpPosts) {
hideHelpPosts();
} else if (hideHelpPostsObserver) {
hideHelpPostsObserver.disconnect();
hideHelpPostsObserver = null;
}
}
function applyMenuItemsSetting() {
if (currentConfig.hideMenuItems) {
hideMenuItems();
} else if (hideMenuItemsObserver) {
hideMenuItemsObserver.disconnect();
hideMenuItemsObserver = null;
}
}
function disableDarkMode() {
const style = document.getElementById('dark-mode-style');
if (style) style.remove();
const svg = document.getElementById('dark-mode-svg');
if (svg) svg.remove();
const meta = document.getElementsByName('theme-color')[0];
if (meta) meta.remove();
stopReplaceHomeIcon();
stopEmojiWrapper();
}
function toggleDarkMode(enabled) {
currentConfig.darkMode = enabled;
saveConfig(currentConfig);
if (enabled) {
enableDarkMode();
initEmojiWrapper();
} else {
disableDarkMode();
}
}
function createSettingsDialog() {
const existing = document.getElementById('tieba-settings-dialog');
if (existing) existing.remove();
const dialog = document.createElement('div');
dialog.id = 'tieba-settings-dialog';
dialog.className = 'no-dark-mode';
const blockedForumNames = currentConfig.blockedForumNames || [];
const blockedListHTML = blockedForumNames.map(name => `
<div class="blocked-item">
<span>${name}</span>
<button class="remove-btn" data-name="${name}">删除</button>
</div>
`).join('');
dialog.innerHTML = `
<style>
#tieba-settings-dialog{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:999999}
.settings-box{background:#2b2b2b;color:#e0e0e0;padding:20px;border-radius:8px;width:350px;max-height:80vh;overflow-y:auto}
.settings-box h3{margin:0 0 15px;font-size:16px;color:#fff}
.settings-box h4{margin:10px 0;font-size:14px;color:#fff}
.settings-box label{display:flex;align-items:center;gap:10px;cursor:pointer;margin-bottom:10px}
.forum-block-section{margin-top:15px;padding-top:15px;border-top:1px solid #444}
.img-wheel-section{margin-top:15px;padding-top:15px;border-top:1px solid #444}
.img-wheel-options{margin-top:10px;padding-left:20px;display:flex;flex-direction:column;gap:8px}
.img-wheel-options label{margin-bottom:0}
.img-wheel-options input[type="radio"]{margin-right:8px}
.add-forum-input{display:flex;gap:8px;margin-bottom:10px}
.add-forum-input input{flex:1;padding:6px 10px;background:#1a1a1a;border:1px solid #444;border-radius:4px;color:#e0e0e0}
.add-forum-input button,.remove-btn{padding:6px 12px;border:none;border-radius:4px;color:#fff;cursor:pointer;font-size:12px}
.add-forum-input button{background:#4070FF}
.blocked-list{max-height:200px;overflow-y:auto;background:#1a1a1a;border-radius:4px;padding:8px}
.blocked-item{display:flex;justify-content:space-between;align-items:center;padding:6px 8px;margin-bottom:4px;background:#2b2b2b;border-radius:4px}
.remove-btn{background:#f44}
.empty-message{text-align:center;color:#888;padding:20px}
.feedback-link{display:block;text-align:center;margin-top:15px;padding-top:15px;border-top:1px solid #444}
.feedback-link a{color:#4070FF;text-decoration:none}
</style>
<div class="settings-box">
<h3>设置</h3>
<label title="启用后会将页面转换为夜间模式(深色主题)">
<input type="checkbox" id="dark-mode-toggle" ${currentConfig.darkMode ? 'checked' : ''}>
<span>深色模式</span>
</label>
<label title="启用后会隐藏主页及搜索结果页右侧的热点推荐栏">
<input type="checkbox" id="hide-right-nav-toggle" ${currentConfig.hideRightNav ? 'checked' : ''}>
<span>隐藏 | 主页右侧热点</span>
</label>
<label title="启用后会隐藏主页左侧“我常逛的吧”模块(与下面选项存在兼容问题,多开关重试几次即可)">
<input type="checkbox" id="hide-my-forums-toggle" ${currentConfig.hideMyForums ? 'checked' : ''}>
<span>隐藏 | 主页常逛的吧</span>
</label>
<label title="启用后会隐藏主页左侧“我玩过的游戏”模块(与上面选项存在兼容问题,多开关重试几次即可)">
<input type="checkbox" id="hide-played-games-toggle" ${currentConfig.hidePlayedGames ? 'checked' : ''}>
<span>隐藏 | 主页玩过游戏</span>
</label>
<label title="启用后会隐藏信息流中的视频帖子,添加后开关一次即可生效">
<input type="checkbox" id="hide-video-posts-toggle" ${currentConfig.hideVideoPosts ? 'checked' : ''}>
<span>隐藏 | 信息流视频贴</span>
</label>
<label title="启用后会隐藏信息流中带求助标签的帖子">
<input type="checkbox" id="hide-help-posts-toggle" ${currentConfig.hideHelpPosts ? 'checked' : ''}>
<span>隐藏 | 信息流求助帖</span>
</label>
<label title="启用后会隐藏顶部菜单右侧的下载客户端和游戏中心入口">
<input type="checkbox" id="hide-menu-items-toggle" ${currentConfig.hideMenuItems ? 'checked' : ''}>
<span>隐藏 | 菜单无用组件</span>
</label>
<label title="启用后会在帖子详情页右侧屏蔽相关推荐">
<input type="checkbox" id="hide-related-rec-toggle" ${currentConfig.hideRelatedRec ? 'checked' : ''}>
<span>隐藏 | 帖子相关推荐</span>
</label>
<label title="启用后会屏蔽楼中广告">
<input type="checkbox" id="hide-feed-ads-toggle" ${currentConfig.hideFeedAds ? 'checked' : ''}>
<span>隐藏 | 屏蔽楼中广告</span>
</label>
<label title="启用后 侧栏的吧 会在当前页打开">
<input type="checkbox" id="open-in-current-page-toggle" ${currentConfig.openInCurrentPage ? 'checked' : ''}>
<span>增强 | 侧边吧当前页打开</span>
</label>
<label title="启用后 帖子 会在新标签页打开">
<input type="checkbox" id="open-thread-in-new-tab-toggle" ${currentConfig.openThreadInNewTab ? 'checked' : ''}>
<span>增强 | 帖子新标签页打开</span>
</label>
<label title="启用后会在菜单栏右上角添加一键签到按钮">
<input type="checkbox" id="auto-sign-toggle" ${currentConfig.autoSign ? 'checked' : ''}>
<span>增强 | 一键签到按钮</span>
</label>
<label title="启用后每次进入页面都会自动执行一次签到任务">
<input type="checkbox" id="auto-sign-on-load-toggle" ${currentConfig.autoSignOnLoad ? 'checked' : ''}>
<span>增强 | 启用自动签到</span>
</label>
<label title="启用后,如果签到全部成功,今天不会再自动执行签到">
<input type="checkbox" id="sign-once-per-day-toggle" ${currentConfig.signOncePerDay ? 'checked' : ''}>
<span>增强 | 每日只签一次</span>
</label>
<label title="启用后,解决部分字体过细的问题,可能导致少部分文字重影">
<input type="checkbox" id="text-bold-toggle" ${currentConfig.textBold ? 'checked' : ''}>
<span>增强 | 文本过细加粗</span>
</label>
<div class="img-wheel-section">
<label title="在图片查看器中启用滚轮功能,可选择切换图片或缩放">
<span>增强 | 图片滚轮控制</span>
</label>
<div class="img-wheel-options">
<label>
<input type="radio" name="img-wheel-mode" value="0" ${currentConfig.imgWheelMode === 0 ? 'checked' : ''}>
<span>禁用</span>
</label>
<label>
<input type="radio" name="img-wheel-mode" value="1" ${currentConfig.imgWheelMode === 1 ? 'checked' : ''}>
<span>滚轮切图</span>
</label>
<label>
<input type="radio" name="img-wheel-mode" value="2" ${currentConfig.imgWheelMode === 2 ? 'checked' : ''}>
<span>滚轮缩放</span>
</label>
</div>
</div>
<div class="forum-block-section">
<label title="启用后可以根据吧名屏蔽信息流中的帖子">
<input type="checkbox" id="block-forums-toggle" ${currentConfig.blockForums ? 'checked' : ''}>
<span>吧名匹配屏蔽</span>
</label>
<div id="forum-block-controls" style="display:${currentConfig.blockForums ? 'block' : 'none'}">
<h4>添加屏蔽的贴吧</h4>
<div class="add-forum-input">
<input type="text" id="forum-name-input" placeholder="输入吧名,如:贴吧游戏">
<button id="add-forum-btn">添加</button>
</div>
<h4>已屏蔽 (${blockedForumNames.length})</h4>
<div class="blocked-list" id="blocked-list">${blockedListHTML || '<div class="empty-message">暂无屏蔽的贴吧</div>'}</div>
</div>
</div>
<div class="feedback-link">
<a href="http://greasyfork.icu/zh-CN/scripts/561749/feedback" target="_blank">脚本问题反馈</a>
</div>
</div>
`;
document.body.appendChild(dialog);
const darkModeToggle = dialog.querySelector('#dark-mode-toggle');
const hideRightNavToggle = dialog.querySelector('#hide-right-nav-toggle');
const hideMyForumsToggle = dialog.querySelector('#hide-my-forums-toggle');
const hidePlayedGamesToggle = dialog.querySelector('#hide-played-games-toggle');
const hideVideoPostsToggle = dialog.querySelector('#hide-video-posts-toggle');
const hideHelpPostsToggle = dialog.querySelector('#hide-help-posts-toggle');
const hideMenuItemsToggle = dialog.querySelector('#hide-menu-items-toggle');
const openInCurrentPageToggle = dialog.querySelector('#open-in-current-page-toggle');
const openThreadInNewTabToggle = dialog.querySelector('#open-thread-in-new-tab-toggle');
const autoSignToggle = dialog.querySelector('#auto-sign-toggle');
const autoSignOnLoadToggle = dialog.querySelector('#auto-sign-on-load-toggle');
const signOncePerDayToggle = dialog.querySelector('#sign-once-per-day-toggle');
const textBoldToggle = dialog.querySelector('#text-bold-toggle');
const hideRelatedRecToggle = dialog.querySelector('#hide-related-rec-toggle');
const hideFeedAdsToggle = dialog.querySelector('#hide-feed-ads-toggle');
const blockForumsToggle = dialog.querySelector('#block-forums-toggle');
const forumBlockControls = dialog.querySelector('#forum-block-controls');
const forumNameInput = dialog.querySelector('#forum-name-input');
const addForumBtn = dialog.querySelector('#add-forum-btn');
const blockedList = dialog.querySelector('#blocked-list');
const imgWheelRadios = dialog.querySelectorAll('input[name="img-wheel-mode"]');
dialog.addEventListener('click', (e) => {
if (e.target === dialog) dialog.remove();
});
darkModeToggle.addEventListener('change', (e) => {
toggleDarkMode(e.target.checked);
});
hideRightNavToggle.addEventListener('change', (e) => {
toggleHomeCard('hideRightNav', e.target.checked);
});
hideMyForumsToggle.addEventListener('change', (e) => {
toggleHomeCard('hideMyForums', e.target.checked);
});
hidePlayedGamesToggle.addEventListener('change', (e) => {
toggleHomeCard('hidePlayedGames', e.target.checked);
});
hideVideoPostsToggle.addEventListener('change', (e) => {
toggleVideoPosts(e.target.checked);
});
hideHelpPostsToggle.addEventListener('change', (e) => {
toggleHelpPosts(e.target.checked);
});
hideMenuItemsToggle.addEventListener('change', (e) => {
toggleMenuItems(e.target.checked);
});
openInCurrentPageToggle.addEventListener('change', (e) => {
toggleOpenInCurrentPage(e.target.checked);
});
openThreadInNewTabToggle.addEventListener('change', (e) => {
toggleOpenThreadInNewTab(e.target.checked);
});
autoSignToggle.addEventListener('change', (e) => {
toggleAutoSign(e.target.checked);
});
autoSignOnLoadToggle.addEventListener('change', (e) => {
currentConfig.autoSignOnLoad = e.target.checked;
saveConfig(currentConfig);
});
signOncePerDayToggle.addEventListener('change', (e) => {
currentConfig.signOncePerDay = e.target.checked;
saveConfig(currentConfig);
});
textBoldToggle.addEventListener('change', (e) => {
toggleTextBold(e.target.checked);
});
hideRelatedRecToggle.addEventListener('change', (e) => {
toggleRelatedRec(e.target.checked);
});
hideFeedAdsToggle.addEventListener('change', (e) => {
toggleFeedAds(e.target.checked);
});
blockForumsToggle.addEventListener('change', (e) => {
const enabled = e.target.checked;
forumBlockControls.style.display = enabled ? 'block' : 'none';
toggleBlockForums(enabled);
});
imgWheelRadios.forEach(radio => {
radio.addEventListener('change', (e) => {
if (e.target.checked) {
currentConfig.imgWheelMode = parseInt(e.target.value);
saveConfig(currentConfig);
}
});
});
const addForum = () => {
const forumName = forumNameInput.value.trim();
if (forumName) {
if (addBlockedForum(forumName)) {
forumNameInput.value = '';
createSettingsDialog();
} else {
alert('该贴吧已在屏蔽列表中');
}
}
};
addForumBtn.addEventListener('click', addForum);
forumNameInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addForum();
});
blockedList.addEventListener('click', (e) => {
if (e.target.classList.contains('remove-btn')) {
const forumName = e.target.getAttribute('data-name');
if (removeBlockedForum(forumName)) {
createSettingsDialog();
}
}
});
}
/**
* 签到状态管理
* 记录成功、失败、已签到和重试成功的贴吧列表
*/
const signState = {
isRunning: false,
successList: [],
failList: [],
signedList: [],
retryList: [],
totalCount: 0,
currentIndex: 0
};
function showNotification(message, type = 'info', options = {}) {
const existing = document.getElementById('tieba-sign-notification');
if (existing) existing.remove();
const notification = document.createElement('div');
notification.id = 'tieba-sign-notification';
notification.className = 'no-dark-mode sign-notification';
const showClose = options.showClose || false;
const showButton = options.showButton || false;
const buttonText = options.buttonText || '';
const onButtonClick = options.onButtonClick || null;
notification.innerHTML = `
<style>
.sign-notification{position:fixed;top:60px;right:20px;background:#2b2b2b;color:#e0e0e0;border-radius:8px;padding:15px 20px;box-shadow:0 4px 12px rgba(0,0,0,.3);z-index:999998;min-width:280px}
.sign-notification-content{display:flex;flex-direction:column;gap:10px}
.sign-notification-header{display:flex;justify-content:space-between;align-items:flex-start}
.sign-notification-message{font-size:14px;line-height:1.5;flex:1}
.sign-notification-close{background:none;border:none;color:#888;cursor:pointer;padding:0;font-size:18px;line-height:1;margin-left:10px}
.sign-notification-close:hover{color:#e0e0e0}
.sign-notification-button{background:#4070FF;color:#fff;border:none;padding:6px 12px;border-radius:4px;cursor:pointer;font-size:13px;width:100%}
.sign-notification-button:hover{background:#5080FF}
</style>
<div class="sign-notification-content">
<div class="sign-notification-header">
<div class="sign-notification-message">${message}</div>
${showClose ? '<button class="sign-notification-close">×</button>' : ''}
</div>
${showButton ? `<button class="sign-notification-button">${buttonText}</button>` : ''}
</div>
`;
document.body.appendChild(notification);
if (showClose) {
const closeBtn = notification.querySelector('.sign-notification-close');
closeBtn.addEventListener('click', () => notification.remove());
}
if (showButton && onButtonClick) {
const btn = notification.querySelector('.sign-notification-button');
btn.addEventListener('click', () => {
onButtonClick();
notification.remove();
});
}
if (type === 'success' && !showButton) {
setTimeout(() => {
if (document.getElementById('tieba-sign-notification')) {
notification.remove();
}
}, 10000);
}
}
function updateNotification(message) {
const notification = document.getElementById('tieba-sign-notification');
if (notification) {
const messageEl = notification.querySelector('.sign-notification-message');
if (messageEl) messageEl.textContent = message;
}
}
function showSignList() {
const existing = document.getElementById('tieba-sign-list-dialog');
if (existing) existing.remove();
const dialog = document.createElement('div');
dialog.id = 'tieba-sign-list-dialog';
dialog.className = 'no-dark-mode';
const renderList = (list, title) => {
if (!list.length) return '';
return `
<div class="sign-section">
<h4>${title} (${list.length})</h4>
${list.map(item => `
<div class="sign-item">
<a href="https://tieba.baidu.com/f?kw=${encodeURIComponent(item.name)}" target="_blank">${item.name}</a>
${item.rank ? `<span>${item.rank}名</span>` : ''}
</div>
`).join('')}
</div>
`;
};
dialog.innerHTML = `
<style>
#tieba-sign-list-dialog{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:999999}
.sign-list-box{background:#2b2b2b;color:#e0e0e0;padding:20px;border-radius:8px;width:400px;max-height:80vh;overflow-y:auto}
.sign-list-box h3{margin:0 0 15px;font-size:16px;color:#fff}
.sign-section{margin-bottom:15px}
.sign-section h4{margin:0 0 10px;font-size:14px;color:#fff}
.sign-item{display:flex;justify-content:space-between;padding:8px 10px;background:#1a1a1a;border-radius:4px;margin-bottom:4px}
.sign-item a{color:#e0e0e0;text-decoration:none;flex:1}
.sign-item a:hover{color:#4070FF}
.sign-item span{color:#888;font-size:12px}
</style>
<div class="sign-list-box">
<h3>签到结果</h3>
${renderList(signState.successList, '签到成功')}
${renderList(signState.signedList, '已签到')}
${renderList(signState.failList, '签到失败')}
</div>
`;
document.body.appendChild(dialog);
dialog.addEventListener('click', (e) => {
if (e.target === dialog) dialog.remove();
});
}
// 获取 tbs
async function fetchTbs() {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: 'https://tieba.baidu.com/dc/common/tbs',
onload: (response) => {
try {
const data = JSON.parse(response.responseText);
if (data.is_login === 1) {
resolve(data.tbs);
} else {
reject('未登录');
}
} catch (e) {
reject('获取tbs失败');
}
}
});
});
}
// 获取吧列表
async function fetchForums() {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: 'https://tieba.baidu.com/mo/q/newmoindex',
onload: (response) => {
try {
const data = JSON.parse(response.responseText);
if (data.no === 0) {
const forums = { signed: [], unsigned: [] };
data.data.like_forum.forEach(forum => {
if (forum.is_sign === 1) {
forums.signed.push(forum.forum_name);
} else {
forums.unsigned.push(forum.forum_name);
}
});
resolve(forums);
} else {
reject('获取关注贴吧失败');
}
} catch (e) {
reject('解析数据失败');
}
}
});
});
}
/**
* 签到
* 160002表示已签到
*/
async function signForum(forumName, tbs) {
return new Promise((resolve) => {
const sign = md5(`kw=${forumName}tbs=${tbs}tiebaclient!!!`);
const formData = `kw=${encodeURIComponent(forumName)}&tbs=${tbs}&sign=${sign}`;
GM_xmlhttpRequest({
method: 'POST',
url: 'https://c.tieba.baidu.com/c/c/forum/sign',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: formData,
onload: (response) => {
try {
const data = JSON.parse(response.responseText);
if (data.error_code === '0') {
resolve({ success: true, rank: data.user_info?.user_sign_rank });
} else if (data.error_code === '160002') { //已签到过的吧
resolve({ success: true, signed: true });
} else {
resolve({ success: false });
}
} catch (e) {
resolve({ success: false });
}
}
});
});
}
/**
* 批量签到流程
* 先签未签到的吧,失败的会自动重试一次
* 这延迟目前我是没问题的,关注的吧太多可能得延长
*/
async function startSign() {
if (signState.isRunning) {
showNotification('请勿重复点击', 'info');
return;
}
signState.isRunning = true;
signState.successList = [];
signState.failList = [];
signState.signedList = [];
signState.retryList = [];
try {
showNotification('正在获取贴吧列表...', 'progress');
const tbs = await fetchTbs();
const forums = await fetchForums();
signState.totalCount = forums.signed.length + forums.unsigned.length;
signState.currentIndex = 0;
forums.signed.forEach(name => {
signState.signedList.push({ name });
});
updateNotification(`开始签到 (0/${forums.unsigned.length})`);
for (const forumName of forums.unsigned) {
signState.currentIndex++;
updateNotification(`正在签到: ${forumName} (${signState.currentIndex}/${forums.unsigned.length})`);
const result = await signForum(forumName, tbs);
if (result.success) {
if (result.signed) {
signState.signedList.push({ name: forumName });
} else {
signState.successList.push({ name: forumName, rank: result.rank });
}
} else {
signState.failList.push({ name: forumName });
}
const delay = Math.floor(Math.random() * 700) + 800;
await new Promise(resolve => setTimeout(resolve, delay));
}
if (signState.failList.length > 0) {
updateNotification(`重试失败的吧 (0/${signState.failList.length})`);
const retryList = [...signState.failList];
signState.failList = [];
for (let i = 0; i < retryList.length; i++) {
const forum = retryList[i];
updateNotification(`重试: ${forum.name} (${i + 1}/${retryList.length})`);
const result = await signForum(forum.name, tbs);
if (result.success) {
signState.successList.push({ name: forum.name, rank: result.rank });
signState.retryList.push({ name: forum.name });
} else {
signState.failList.push(forum);
}
const delay = Math.floor(Math.random() * 700) + 800;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
const notification = document.getElementById('tieba-sign-notification');
if (notification) notification.remove();
const retryMsg = signState.retryList.length > 0 ? `,重试成功${signState.retryList.length}个` : '';
const message = `签到完成!成功${signState.successList.length}个,失败${signState.failList.length}个,已签${signState.signedList.length}个${retryMsg}`;
// 如果启用了每日只签一次,且签到全部成功,记录今天已签到
if (currentConfig.signOncePerDay && signState.failList.length === 0) {
saveSignRecord(true);
}
if (signState.failList.length > 0) {
showNotification(message, 'error', {
showClose: true,
showButton: true,
buttonText: '签到列表',
onButtonClick: showSignList
});
} else {
showNotification(message, 'success', {
showClose: true
});
}
} catch (error) {
showNotification(`签到失败: ${error}`, 'error', { showClose: true });
} finally {
signState.isRunning = false;
}
}
/**
* 添加签到按钮到菜单栏
* 如果菜单还没加载完成,每500ms重试一次
*/
function createSignButton() {
if (!currentConfig.autoSign) return;
const addButton = () => {
if (document.querySelector('.tieba-sign-btn')) return;
const menuList = document.querySelector('.right-menu .menu-list');
if (!menuList) {
setTimeout(addButton, 500);
return;
}
const signBtn = document.createElement('div');
signBtn.className = 'tieba-sign-btn menu-item';
signBtn.style.marginLeft = '10px';
signBtn.innerHTML = `
<svg class="menu-icon" viewBox="0 0 24 20" style="width: 28px; height: 28px;">
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" fill="currentColor"/>
</svg>
`;
signBtn.title = '一键签到';
signBtn.onclick = () => startSign();
menuList.appendChild(signBtn);
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(addButton, 1000));
} else {
setTimeout(addButton, 1000);
}
}
function removeSignButton() {
const btn = document.querySelector('.tieba-sign-btn');
if (btn) btn.remove();
}
function toggleAutoSign(enabled) {
currentConfig.autoSign = enabled;
saveConfig(currentConfig);
if (enabled) {
createSignButton();
} else {
removeSignButton();
}
}
/**
* 文本过细加粗
* 开启后会应用text-shadow
*/
function toggleTextBold(enabled) {
currentConfig.textBold = enabled;
saveConfig(currentConfig);
const darkStyle = document.getElementById('dark-mode-style');
if (darkStyle) {
darkStyle.remove();
createDarkStyles();
}
}
/**
* 图片查看器滚轮控制
* 在图片查看器中滚动鼠标滚轮可切换图片或缩放
*/
function initPhotoViewerScroll() {
document.addEventListener('wheel', (ev) => {
const cfg = currentConfig.imgWheelMode;
if (!cfg) return;
const box = document.querySelector('.el-image-viewer__wrapper');
if (!box || box.style.display === 'none') return;
if (!ev.target.closest('.el-image-viewer__wrapper')) return;
ev.preventDefault();
ev.stopPropagation();
if (cfg === 1) {
const el = ev.deltaY > 0
? box.querySelector('.el-image-viewer__next')
: box.querySelector('.el-image-viewer__prev');
el?.click();
} else if (cfg === 2) {
const icons = box.querySelector('.el-image-viewer__actions__inner')?.querySelectorAll('.icon');
if (!icons || icons.length < 2) return;
(ev.deltaY > 0 ? icons[0] : icons[1])?.click();
}
}, { capture: true, passive: false });
}
let relatedRecWatcher = null;
function isPostPage() {
return location.pathname.startsWith('/p/');
}
function hideRelatedRecommend() {
if (!isPostPage()) return;
const headers = document.querySelectorAll('.card-header .header-title');
for (const h of headers) {
if (h.textContent?.trim() !== '相关推荐') continue;
const block = h.closest('.mt-20');
if (!block || block.dataset.recHidden) continue;
const sidebar = block.closest('.right-area-wrapper');
if (!sidebar) continue;
block.style.display = 'none';
block.dataset.recHidden = '1';
break;
}
}
function showRelatedRecommend() {
const blocks = document.querySelectorAll('[data-rec-hidden]');
for (const block of blocks) {
block.style.display = '';
delete block.dataset.recHidden;
}
}
function startRelatedRecObserver() {
if (relatedRecWatcher) relatedRecWatcher.disconnect();
relatedRecWatcher = new MutationObserver(hideRelatedRecommend);
relatedRecWatcher.observe(document.body, { childList: true, subtree: true });
hideRelatedRecommend();
}
function stopRelatedRecObserver() {
if (relatedRecWatcher) {
relatedRecWatcher.disconnect();
relatedRecWatcher = null;
}
showRelatedRecommend();
}
function toggleRelatedRec(enable) {
currentConfig.hideRelatedRec = enable;
saveConfig(currentConfig);
if (enable) {
startRelatedRecObserver();
} else {
stopRelatedRecObserver();
}
}
function applyRelatedRecSetting() {
if (currentConfig.hideRelatedRec) {
startRelatedRecObserver();
}
}
// 广告屏蔽
let feedAdsWatcher = null;
function hideFeedAds() {
// 查找广告元素
const selectors = [
'[data-key^="ad__"]',
'[dsp-data-track]',
'.feed-ad-container',
'.pb-ad-wrapper',
'.virtual-list-item[data-key*="ad"]'
];
for (const selector of selectors) {
const adElements = document.querySelectorAll(selector);
for (const el of adElements) {
if (el.dataset.adHidden) continue;
const container = el.closest('.virtual-list-item') || el;
container.style.display = 'none';
container.dataset.adHidden = '1';
}
}
}
function showFeedAds() {
const ads = document.querySelectorAll('[data-ad-hidden]');
for (const ad of ads) {
ad.style.display = '';
delete ad.dataset.adHidden;
}
}
function startFeedAdsObserver() {
if (feedAdsWatcher) feedAdsWatcher.disconnect();
hideFeedAds();
requestAnimationFrame(() => {
hideFeedAds();
});
feedAdsWatcher = new MutationObserver((mutations) => {
const hasNewNodes = mutations.some(m => m.addedNodes.length > 0);
if (hasNewNodes) {
hideFeedAds();
}
});
feedAdsWatcher.observe(document.body, { childList: true, subtree: true });
}
function stopFeedAdsObserver() {
if (feedAdsWatcher) {
feedAdsWatcher.disconnect();
feedAdsWatcher = null;
}
showFeedAds();
}
function toggleFeedAds(enable) {
currentConfig.hideFeedAds = enable;
saveConfig(currentConfig);
if (enable) {
startFeedAdsObserver();
} else {
stopFeedAdsObserver();
}
}
function applyFeedAdsSetting() {
if (currentConfig.hideFeedAds) {
startFeedAdsObserver();
} else {
stopFeedAdsObserver();
}
}
/**
* 目前反色方法下的临时方案
* emoji字符用span包装,应用反向滤镜
* 目前仅限正文及类似地方,输入框仍然会反色
*/
const emojiRegex = /[\p{Emoji_Presentation}\p{Extended_Pictographic}]/gu;
function wrapEmojis(node) {
if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent;
if (!emojiRegex.test(text)) return;
emojiRegex.lastIndex = 0;
const fragment = document.createDocumentFragment();
let lastIndex = 0;
let match;
while ((match = emojiRegex.exec(text)) !== null) {
if (match.index > lastIndex) {
fragment.appendChild(document.createTextNode(text.slice(lastIndex, match.index)));
}
const emojiSpan = document.createElement('span');
emojiSpan.className = 'emoji-wrapper';
emojiSpan.textContent = match[0];
emojiSpan.style.cssText = isFirefox ? FILTERS.firefoxReverse : FILTERS.chromeReverse;
fragment.appendChild(emojiSpan);
lastIndex = emojiRegex.lastIndex;
}
if (lastIndex < text.length) {
fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
}
node.parentNode.replaceChild(fragment, node);
} else if (node.nodeType === Node.ELEMENT_NODE && !node.classList.contains('emoji-wrapper')) {
const children = Array.from(node.childNodes);
children.forEach(child => wrapEmojis(child));
}
}
let emojiObserver = null;
function initEmojiWrapper() {
if (!currentConfig.darkMode) return;
const processEmojis = () => {
const textContainers = document.querySelectorAll('.text, .pb-content-item, .comment-content, .post-content, .reply-content');
textContainers.forEach(container => {
if (container.dataset.emojiProcessed) return;
container.dataset.emojiProcessed = 'true';
wrapEmojis(container);
});
};
if (emojiObserver) emojiObserver.disconnect();
emojiObserver = new MutationObserver(() => {
processEmojis();
});
emojiObserver.observe(document.body, {
childList: true,
subtree: true
});
processEmojis();
}
function stopEmojiWrapper() {
if (emojiObserver) {
emojiObserver.disconnect();
emojiObserver = null;
}
}
GM_registerMenuCommand('设置', createSettingsDialog);
GM_registerMenuCommand('查看签到结果', showSignList);
let headObserver = null;
let isInitialized = false;
function reapplyFeatures() {
applyHomeCardSettings();
applyVideoPostsSetting();
applyHelpPostsSetting();
applyMenuItemsSetting();
applyBlockForumsSetting();
applyRelatedRecSetting();
applyFeedAdsSetting();
if (currentConfig.autoSign) {
createSignButton();
}
if (currentConfig.openThreadInNewTab) {
setupThreadLinkHandler();
}
}
function setupSPAListener() {
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function(...args) {
originalPushState.apply(this, args);
setTimeout(reapplyFeatures, 300);
};
history.replaceState = function(...args) {
originalReplaceState.apply(this, args);
setTimeout(reapplyFeatures, 300);
};
window.addEventListener('popstate', () => {
setTimeout(reapplyFeatures, 300);
});
}
/**
* 初始化脚本
* 应用所有已启用的功能,监听 head 变化防止样式丢失
* 如果启用了自动签到,延迟 2 秒后执行一次(检查每日只签一次的限制)
*/
function init() {
if (isInitialized) return;
isInitialized = true;
if (document.head) {
enableDarkMode();
applyHomeCardSettings();
applyVideoPostsSetting();
applyHelpPostsSetting();
applyMenuItemsSetting();
applyBlockForumsSetting();
}
setupForumLinkHandler();
setupSPAListener();
initPhotoViewerScroll();
applyRelatedRecSetting();
applyFeedAdsSetting();
if (currentConfig.darkMode) {
initEmojiWrapper();
}
if (currentConfig.autoSign) {
createSignButton();
}
if (currentConfig.openThreadInNewTab) {
setupThreadLinkHandler();
}
if (currentConfig.autoSignOnLoad) {
setTimeout(() => {
if (currentConfig.signOncePerDay && hasSignedToday()) {
return;
}
startSign();
}, 2000);
}
if (!headObserver) {
headObserver = new MutationObserver(() => {
if (currentConfig.darkMode && !document.getElementById('dark-mode-style')) {
enableDarkMode();
}
});
headObserver.observe(document.head, { childList: true, subtree: true });
}
}
init();
})();