// ==UserScript== // @name Twitter Search Filter // @namespace https://x.com/pollowinworld // @version 1.4 // @description Filter tweets in Twitter search. Support hide auto-post bot tweets, visual management panel, mask hint, full hide, dark mode, draggable, collapsible, fully dynamic page adaptation! 🚀 // @author pollowinworld // @match https://x.com/* // @license MIT // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; let blockedWords = JSON.parse(localStorage.getItem('blockedWords') || '["AI Alert &"]'); let filterEnabled = JSON.parse(localStorage.getItem('filterEnabled') || 'true'); let fullHideEnabled = JSON.parse(localStorage.getItem('fullHideEnabled') || 'true'); let darkModeEnabled = JSON.parse(localStorage.getItem('darkModeEnabled') || 'false'); let hideAutoPostEnabled = JSON.parse(localStorage.getItem('hideAutoPostEnabled') || 'false'); const autoPostKeywords = ["自动发推", "Automated"]; let panelCreated = false; let tweetObserver = null; function saveBlockedWords() { localStorage.setItem('blockedWords', JSON.stringify(blockedWords)); } function saveFilterStatus() { localStorage.setItem('filterEnabled', JSON.stringify(filterEnabled)); } function saveFullHideStatus() { localStorage.setItem('fullHideEnabled', JSON.stringify(fullHideEnabled)); } function saveDarkModeStatus() { localStorage.setItem('darkModeEnabled', JSON.stringify(darkModeEnabled)); } function saveHideAutoPostStatus() { localStorage.setItem('hideAutoPostEnabled', JSON.stringify(hideAutoPostEnabled)); } function shouldBlock(tweetText) { return blockedWords.find(word => tweetText.toLowerCase().includes(word.toLowerCase())); } function isAutoPost(tweetText) { return autoPostKeywords.find(word => tweetText.toLowerCase().includes(word.toLowerCase())); } function createMask(article, matchedWord) { const mask = document.createElement('div'); mask.style.position = 'absolute'; mask.style.top = '0'; mask.style.left = '0'; mask.style.width = '100%'; mask.style.height = '100%'; mask.style.background = 'rgba(255, 255, 255, 0.9)'; mask.style.display = 'flex'; mask.style.flexDirection = 'column'; mask.style.justifyContent = 'center'; mask.style.alignItems = 'center'; mask.style.zIndex = '100'; mask.innerHTML = `
已屏蔽推文
匹配关键词:${matchedWord}
`; const button = mask.querySelector('button'); button.addEventListener('click', () => { mask.remove(); }); article.style.position = 'relative'; article.appendChild(mask); } function filterTweets() { if (!filterEnabled) return; const articles = document.querySelectorAll('article[data-testid="tweet"]'); articles.forEach(article => { const textContent = article.innerText; const matchedWord = shouldBlock(textContent); const autoPostMatch = hideAutoPostEnabled ? autoPostKeywords.find(word => textContent.includes(word)) : null; if (matchedWord || autoPostMatch) { if (!article.dataset.filtered) { article.dataset.filtered = 'true'; if (fullHideEnabled) { article.style.display = 'none'; } else { createMask(article, matchedWord || (autoPostMatch + '(自动发推)')); } } } }); } function createControlPanel() { if (panelCreated) return; panelCreated = true; const panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.top = '100px'; panel.style.right = '20px'; panel.style.width = '280px'; panel.style.background = 'white'; panel.style.border = '1px solid #ccc'; panel.style.borderRadius = '8px'; panel.style.padding = '10px'; panel.style.zIndex = '9999'; panel.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)'; panel.style.fontSize = '14px'; panel.style.fontFamily = 'Arial, sans-serif'; panel.style.cursor = 'move'; panel.id = 'filter-panel'; panel.innerHTML = `

🛡️ 屏蔽词管理

`; document.body.appendChild(panel); const collapseBtn = panel.querySelector('#collapse-btn'); const panelBody = panel.querySelector('#panel-body'); let collapsed = true; panelBody.style.display = 'none'; collapseBtn.textContent = '➕'; panel.style.width = '60px'; collapseBtn.addEventListener('click', () => { collapsed = !collapsed; if (collapsed) { panelBody.style.display = 'none'; collapseBtn.textContent = '➕'; panel.style.width = '60px'; } else { panelBody.style.display = ''; collapseBtn.textContent = '➖'; panel.style.width = '280px'; } }); const wordListDiv = panel.querySelector('#word-list'); const input = panel.querySelector('#new-word'); const addButton = panel.querySelector('#add-word'); const toggleButton = panel.querySelector('#toggle-filter'); const toggleFullHideButton = panel.querySelector('#toggle-fullhide'); const toggleAutoPostButton = panel.querySelector('#toggle-autopost'); const toggleDarkModeButton = panel.querySelector('#toggle-darkmode'); const exportButton = panel.querySelector('#export-words'); const importButton = panel.querySelector('#import-words'); const clearButton = panel.querySelector('#clear-words'); function refreshWordList() { wordListDiv.innerHTML = ''; blockedWords.forEach((word, index) => { const wordItem = document.createElement('div'); wordItem.style.display = 'flex'; wordItem.style.justifyContent = 'space-between'; wordItem.style.marginBottom = '5px'; wordItem.innerHTML = `${word}`; wordListDiv.appendChild(wordItem); }); wordListDiv.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', (e) => { const idx = parseInt(e.target.getAttribute('data-index')); blockedWords.splice(idx, 1); saveBlockedWords(); refreshWordList(); }); }); } function applyTheme() { if (darkModeEnabled) { panel.style.background = '#1e1e1e'; panel.style.color = '#eee'; panel.querySelectorAll('button').forEach(btn => { btn.style.backgroundColor = '#333'; btn.style.color = '#eee'; }); } else { panel.style.background = 'white'; panel.style.color = 'black'; panel.querySelectorAll('button').forEach(btn => { btn.style.backgroundColor = ''; btn.style.color = ''; }); } } addButton.addEventListener('click', () => { const word = input.value.trim(); if (word && !blockedWords.includes(word)) { blockedWords.push(word); saveBlockedWords(); refreshWordList(); } input.value = ''; }); toggleButton.addEventListener('click', () => { filterEnabled = !filterEnabled; saveFilterStatus(); toggleButton.innerText = filterEnabled ? '✅ 过滤开启' : '❌ 过滤关闭'; }); toggleFullHideButton.addEventListener('click', () => { fullHideEnabled = !fullHideEnabled; saveFullHideStatus(); toggleFullHideButton.innerText = fullHideEnabled ? '✅ 完全隐藏开启' : '❌ 完全隐藏关闭'; }); toggleAutoPostButton.addEventListener('click', () => { hideAutoPostEnabled = !hideAutoPostEnabled; saveHideAutoPostStatus(); toggleAutoPostButton.innerText = hideAutoPostEnabled ? '🚀 隐藏自动发推 ✅' : '🚀 隐藏自动发推 ❌'; }); toggleDarkModeButton.addEventListener('click', () => { darkModeEnabled = !darkModeEnabled; saveDarkModeStatus(); applyTheme(); }); exportButton.addEventListener('click', () => { const blob = new Blob([JSON.stringify(blockedWords, null, 2)], {type: "application/json"}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'blocked_words.json'; a.click(); URL.revokeObjectURL(url); }); importButton.addEventListener('click', () => { const inputFile = document.createElement('input'); inputFile.type = 'file'; inputFile.accept = 'application/json'; inputFile.onchange = e => { const file = e.target.files[0]; const reader = new FileReader(); reader.onload = event => { try { const imported = JSON.parse(event.target.result); if (Array.isArray(imported)) { blockedWords = imported; saveBlockedWords(); refreshWordList(); alert('✅ 成功导入屏蔽词'); } else { alert('❌ 文件格式错误'); } } catch (err) { alert('❌ 解析失败'); } }; reader.readAsText(file); }; inputFile.click(); }); clearButton.addEventListener('click', () => { if (confirm('确定要清空所有屏蔽词吗?')) { blockedWords = []; saveBlockedWords(); refreshWordList(); } }); refreshWordList(); applyTheme(); // 拖拽+吸附 let isDragging = false, offsetX, offsetY; panel.addEventListener('mousedown', e => { isDragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; panel.style.transition = 'none'; }); document.addEventListener('mousemove', e => { if (isDragging) { panel.style.left = (e.clientX - offsetX) + 'px'; panel.style.top = (e.clientY - offsetY) + 'px'; panel.style.right = 'auto'; } }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; const windowWidth = window.innerWidth; const panelRect = panel.getBoundingClientRect(); const middle = windowWidth / 2; if (panelRect.left + panelRect.width / 2 < middle) { panel.style.left = '10px'; panel.style.right = 'auto'; } else { panel.style.right = '10px'; panel.style.left = 'auto'; } panel.style.transition = 'left 0.2s, right 0.2s, top 0.2s'; } }); } function observeTweets() { if (tweetObserver) tweetObserver.disconnect(); tweetObserver = new MutationObserver(() => { if (location.pathname.startsWith('/search')) { createControlPanel(); filterTweets(); } }); tweetObserver.observe(document.body, { childList: true, subtree: true }); } observeTweets(); })();