|
|
本帖最后由 秋枫Mod 于 2026-5-1 13:15 编辑
本脚本基于 @青春向上 的作品进行二次修改与优化,仅用于个人学习、技术交流与研究。若原作者对本次修改存在异议,请联系删除,本人无任何侵权意图。
原帖:https://bbs.binmt.cc/thread-165883-1-1.html
新增评论过滤器功能
下面效果图:
未过滤:
过滤后:
脚本UI:
- //// ==UserScript==
- // [url=home.php?mod=space&uid=121064]@name[/url] [MT论坛]消息预览+
- // @namespace
- // @version 2026-05-01
- // @description 基于青春向上大佬的消息预览脚本进行修改,新增了评论过滤器功能
- // @author 青春向上&秋枫Mod
- // @match https://bbs.binmt.cc/home.php?mod=space&do=notice&view=mypost*
- // @icon https://bbs.binmt.cc/favicon.ico
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_deleteValue
- // @run-at document-idle
- // ==/UserScript==
- // 二次修改声明:
- // 本脚本基于原作者作品二次修改,仅用于个人学习、技术交流与研究,非商用
- // 原始版权归原作者所有,禁止未经授权转载、分发、商用,侵权请联系删除
- (function () {
- 'use strict';
- const noticeSelector = '.comiis_notice_list>ul';
- const itemSelector = `${noticeSelector}>li`;
- const selectContent = 'div.comiis_messages.comiis_aimg_show.cl';
- const MAX_CACHE_COUNT = 50;
- const MAX_LENGTH = 100;
- const PROCESSED_MARK = 'mt-preview-processed';
- const CACHE_STORAGE_KEY = 'mt_bbs_preview_cache';
- const FILTER_CONFIG_KEY = 'mt_bbs_comment_filter_config';
- let currentPageFiltered = [];
- const defaultFilterConfig = {
- minLength: 0,
- maxLength: 6,
- keywords: []
- };
- function disableBackground() {
- document.body.style.overflow = 'hidden';
- document.body.style.touchAction = 'none';
- }
- function enableBackground() {
- document.body.style.overflow = '';
- document.body.style.touchAction = '';
- }
- function getFilterConfig() {
- try {
- const saved = GM_getValue(FILTER_CONFIG_KEY);
- return saved ? JSON.parse(saved) : { ...defaultFilterConfig };
- } catch {
- return { ...defaultFilterConfig };
- }
- }
- function saveFilterConfig(config) {
- GM_setValue(FILTER_CONFIG_KEY, JSON.stringify(config));
- }
- function autoSaveConfig() {
- const min = parseInt(document.querySelector('#f-min')?.value) || 0;
- const max = parseInt(document.querySelector('#f-max')?.value) || 999;
- const kw = (document.querySelector('#f-kw')?.value || '')
- .split('\n').map(x => x.trim()).filter(Boolean);
- saveFilterConfig({ minLength: min, maxLength: max, keywords: kw });
- processAllNotices(true);
- }
- function shouldFilterComment(content) {
- if (!content) return false;
- const cfg = getFilterConfig();
- const len = content.length;
- if (len < cfg.minLength || len > cfg.maxLength) return false;
- return cfg.keywords.some(kw => kw && content.includes(kw));
- }
- function processAllNotices(force = false) {
- currentPageFiltered = [];
- document.querySelectorAll(itemSelector).forEach(li => {
- if (!force && li.hasAttribute(PROCESSED_MARK)) return;
- processNoticeItem(li, force);
- });
- updateFilterButton();
- }
- async function processNoticeItem(li, force) {
- if (!force) li.setAttribute(PROCESSED_MARK, 'true');
- const a = li.querySelector('a.lit[href*="goto=findpost"]');
- if (!a) return;
- const { tid, pid } = getTidPidFromUrl(a.href);
- if (!tid || !pid) return;
- const key = `${tid}_${pid}`;
- let content = getCache(key);
- if (!content) {
- content = await fetchReplyContent(tid, pid);
- if (content && !content.includes('失败')) setCache(key, content);
- }
- const needFilter = shouldFilterComment(content);
- if (needFilter) {
- li.style.display = 'none';
- currentPageFiltered.push({
- content: content,
- url: a.href
- });
- } else {
- li.style.display = '';
- }
- const show = truncateText(content, MAX_LENGTH);
- insertContentToLi(li, show);
- }
- function getTidPidFromUrl(url) {
- const p = new URLSearchParams(url);
- return { tid: p.get('ptid'), pid: p.get('pid') };
- }
- async function fetchReplyContent(tid, pid) {
- try {
- const r = await fetch(
- `https://bbs.binmt.cc/forum.php?mod=viewthread&tid=${tid}&viewpid=${pid}&mobile=2&inajax=1`,
- { signal: AbortSignal.timeout(5000) }
- );
- if (!r.ok) throw new Error('请求失败');
- const txt = await r.text();
- const xml = new DOMParser().parseFromString(txt, 'text/xml');
- if (xml.querySelector('parsererror')) throw new Error('XML错误');
- const html = xml.lastChild?.firstChild?.nodeValue || '';
- const div = document.createElement('div');
- div.innerHTML = html;
- const c = div.querySelector(selectContent)?.textContent.trim() || '';
- div.remove();
- return c || '[空内容]';
- } catch (e) {
- return `[获取失败:${e.message}]`;
- }
- }
- function truncateText(t, max) {
- if (!t) return '';
- return t.length <= max ? t : '...' + t.slice(-max);
- }
- function insertContentToLi(li, txt) {
- const old = li.querySelector('.mt-preview');
- if (old) old.remove();
- const s = document.createElement('span');
- s.className = 'mt-preview';
- s.textContent = `(预览:${txt})`;
- s.style.cssText = `
- color:#666;
- font-size:14px;
- line-height:1.5;
- margin-left:6px;
- display:inline-block;
- margin-top:2px;
- `;
- li.appendChild(s);
- }
- function getCacheData() {
- try {
- const d = localStorage.getItem(CACHE_STORAGE_KEY);
- return d ? JSON.parse(d) : { list: [] };
- } catch { return { list: [] } }
- }
- function saveCacheData(d) {
- localStorage.setItem(CACHE_STORAGE_KEY, JSON.stringify(d));
- }
- function getCache(k) {
- const c = getCacheData();
- const i = c.list.find(x => x.key === k);
- return i ? i.content : null;
- }
- function setCache(k, v) {
- const c = getCacheData();
- const idx = c.list.findIndex(x => x.key === k);
- if (idx > -1) c.list.splice(idx, 1);
- c.list.unshift({ key: k, content: v, time: Date.now() });
- if (c.list.length > MAX_CACHE_COUNT) c.list.pop();
- saveCacheData(c);
- }
- function animateIn(el) {
- el.style.transition = 'transform 0.28s cubic-bezier(0.25,0.8,0.25,1), opacity 0.28s ease';
- el.style.opacity = '0';
- el.style.transform = 'scale(0.94)';
- requestAnimationFrame(() => {
- el.style.opacity = '1';
- el.style.transform = 'scale(1)';
- });
- }
- function animateOut(el, cb) {
- el.style.transition = 'transform 0.22s ease, opacity 0.22s ease';
- el.style.opacity = '0';
- el.style.transform = 'scale(0.96)';
- setTimeout(() => { el.remove(); cb?.(); }, 230);
- }
- function createFilterUI() {
- disableBackground();
- const cfg = getFilterConfig();
- const overlay = document.createElement('div');
- overlay.style.cssText = `
- position:fixed;top:0;left:0;width:100vw;height:100vh;
- background:rgba(0,0,0,0.5);z-index:9999;display:flex;
- align-items:center;justify-content:center;
- `;
- const box = document.createElement('div');
- box.style.cssText = `
- background:#fff;border-radius:12px;width:90%;max-width:380px;
- padding:20px;box-sizing:border-box;box-shadow:0 10px 30px rgba(0,0,0,0.2);
- `;
- box.innerHTML = `
- <h3 style="margin:0 0 16px 0;text-align:center;font-size:17px;">评论过滤器</h3>
- <div style="margin-bottom:12px;">
- <label style="font-size:14px;font-weight:bold;display:block;">最小长度</label>
- <input type="number" id="f-min" value="${cfg.minLength}" style="width:100%;padding:8px;border-radius:6px;border:1px solid #ddd;box-sizing:border-box;">
- </div>
- <div style="margin-bottom:12px;">
- <label style="font-size:14px;font-weight:bold;display:block;">最大长度</label>
- <input type="number" id="f-max" value="${cfg.maxLength}" style="width:100%;padding:8px;border-radius:6px;border:1px solid #ddd;box-sizing:border-box;">
- </div>
- <div style="margin-bottom:16px;">
- <label style="font-size:14px;font-weight:bold;display:block;">关键字(换行分隔)</label>
- <textarea id="f-kw" rows="4" style="width:100%;padding:8px;border-radius:6px;border:1px solid #ddd;box-sizing:border-box;">${cfg.keywords.join('\n')}</textarea>
- </div>
- <button id="f-close" style="width:100%;padding:10px;border:none;border-radius:6px;background:#eee;">关闭</button>
- `;
- overlay.appendChild(box);
- document.body.appendChild(overlay);
- animateIn(box);
- box.querySelectorAll('input,textarea').forEach(i => {
- i.addEventListener('input', autoSaveConfig);
- });
- function close() {
- animateOut(box, () => { overlay.remove(); enableBackground(); });
- }
- box.querySelector('#f-close').onclick = close;
- overlay.onclick = e => e.target === overlay && close();
- }
- function showFilteredList() {
- disableBackground();
- const list = currentPageFiltered;
- const cnt = list.length;
- const overlay = document.createElement('div');
- overlay.style.cssText = `
- position:fixed;top:0;left:0;width:100vw;height:100vh;
- background:rgba(0,0,0,0.5);z-index:9999;display:flex;
- align-items:center;justify-content:center;
- `;
- const wrap = document.createElement('div');
- wrap.style.cssText = `
- background:#fff;border-radius:12px;width:90%;max-width:380px;
- max-height:80vh;display:flex;flex-direction:column;
- box-shadow:0 10px 30px rgba(0,0,0,0.2);overflow:hidden;
- `;
- const head = document.createElement('div');
- head.style.cssText = `padding:16px 20px;border-bottom:1px solid #eee;`;
- head.innerHTML = `<h3 style="margin:0;text-align:center;">当前页已过滤(${cnt}条)</h3>`;
- const content = document.createElement('div');
- content.style.cssText = `padding:10px 20px;flex:1;overflow-y:auto;max-height:50vh;`;
- if (cnt === 0) {
- content.innerHTML = `<div style="padding:40px 0;text-align:center;color:#999;">暂无过滤</div>`;
- } else {
- let html = '';
- list.forEach(it => {
- html += `
- <div style="padding:10px 0;border-bottom:1px solid #f5f5f5;">
- <div style="font-size:14px;line-height:1.5;word-break:break-all;margin-bottom:6px;">${it.content}</div>
- <a href="${it.url}" target="_blank" style="font-size:13px;color:#007bff;text-decoration:none;">查看原帖</a>
- </div>`;
- });
- content.innerHTML = html;
- }
- const foot = document.createElement('div');
- foot.style.cssText = `padding:12px 20px;border-top:1px solid #eee;background:#fff;`;
- foot.innerHTML = `<button id="c-close" style="width:100%;padding:10px;border:none;border-radius:6px;background:#eee;">关闭</button>`;
- wrap.appendChild(head);
- wrap.appendChild(content);
- wrap.appendChild(foot);
- overlay.appendChild(wrap);
- document.body.appendChild(overlay);
- animateIn(wrap);
- function close() {
- animateOut(wrap, () => { overlay.remove(); enableBackground(); });
- }
- foot.querySelector('#c-close').onclick = close;
- overlay.onclick = e => e.target === overlay && close();
- }
- function addButtons() {
- const bar = document.createElement('div');
- bar.style.cssText = `
- position:fixed;bottom:20px;right:20px;display:flex;gap:8px;z-index:9998;
- `;
- const btn1 = document.createElement('button');
- btn1.textContent = '评论过滤器';
- btn1.style.cssText = `padding:8px 12px;background:#007bff;color:#fff;border:none;border-radius:6px;cursor:pointer;`;
- btn1.onclick = createFilterUI;
- const btn2 = document.createElement('button');
- btn2.id = 'filtered-btn';
- btn2.style.cssText = `padding:8px 12px;background:#28a745;color:#fff;border:none;border-radius:6px;cursor:pointer;`;
- btn2.onclick = showFilteredList;
- bar.appendChild(btn1);
- bar.appendChild(btn2);
- document.body.appendChild(bar);
- updateFilterButton();
- }
- function updateFilterButton() {
- const btn = document.getElementById('filtered-btn');
- if (!btn) return;
- btn.textContent = `已过滤(${currentPageFiltered.length})`;
- }
- processAllNotices();
- addButtons();
- const ul = document.querySelector(noticeSelector);
- if (ul) {
- new MutationObserver(() => {
- processAllNotices();
- updateFilterButton();
- }).observe(ul, { childList: true });
- }
- })();
复制代码 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|