// ==UserScript== // @name X 黄推+币圈机器人终极隐藏器 // @namespace https://grok.x.ai // @version 1.8 // @description 强力过滤引流/币圈/擦边/博彩等机器人内容,支持快捷键添加、管理面板与用户名白名单。 // @author Grok、codex、Claude // @match https://x.com/* // @match https://twitter.com/* // @grant none // @run-at document-start // @license MIT // @icon https://greasyfork.org/images/blacklogo16.png // @homepageURL https://greasyfork.org/zh-CN/scripts/570683 // @downloadURL https://update.greasyfork.icu/scripts/570683/X%20%E9%BB%84%E6%8E%A8%2B%E5%B8%81%E5%9C%88%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%BB%88%E6%9E%81%E9%9A%90%E8%97%8F%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/570683/X%20%E9%BB%84%E6%8E%A8%2B%E5%B8%81%E5%9C%88%E6%9C%BA%E5%99%A8%E4%BA%BA%E7%BB%88%E6%9E%81%E9%9A%90%E8%97%8F%E5%99%A8.meta.js // ==/UserScript== (function () { 'use strict'; const STORAGE_KEY = 'x_block_words_v1'; const WHITELIST_KEY = 'x_whitelist_names_v1'; const HIDDEN_ATTR = 'data-x-ultra-hidden'; const KEEP_SPACE_WHEN_HIDE = false; const CACHE_HASH_ATTR = 'data-x-last-hash'; const CACHE_SHOULD_ATTR = 'data-x-last-should'; const BUILTIN_PATTERNS = [ /快领我回家|扣1白给|陪我聊聊天|有弟弟来一起|推特第一骚|我约过她|姐姐在等你哦|你喜欢我的我都有哦/i, /懂[得的].{0,3}(来|私|入|dd|联系|撩|进|加|懂)/i, /免费破|私处|想被人炒|万达广场|约个有经验的大叔|找我那视频啊|找个哥哥调教|小m在线等调/i, /男大|足控|蜜桃臀|推特刷粉丝|刷粉/i, /主人|舔狗|小狗|线下|上门|白虎|急找|私约|野战|在线蹲|蹲一个/i, /有没有单男|有没有游戏搭子|有没有单女|单男/i, /急需一位|chu男|Chu男|处男|处女找个哥哥|无偿|免费破|调教|附近的有没有|满足我|看做爱|不如做爱/i, /母狗|欲望少女|纯欲|反差/i, /我是真人/i, /那亲亲吧|你的娱乐群/i, /空投|USDT.*(奖池|交易|转账|推广)|拉盘|打新项目|上币路线|Pre-IPO|合约.*(喊单|爆仓)/i, /引流|私信|DM|加V|VX|微信|电报|TG|群号|群聊|福利群|进群|进组/i, /开户|理财|搬砖|量化|带单|套利|返利|高收益|稳赚|保本|日赚/i, /博彩|赌场|下注|百家乐|时时彩|快三|网投|棋牌游戏/i, /约炮|一夜情|同城|包夜|全套|外围|小姐|嫩模|学生妹/i, /裸聊|视频聊天|视频交友|语音交友|聊骚|骚扰|撩骚/i, /兼职|日结|招募|代理|推广员|刷单|返现|冲量/i, /空降|互关|互粉|互赞|点赞关注|关注我|回关|求关/i, /[\u2190-\u21FF\u2B00-\u2BFF\u27A1]/, /搭子|固炮/i, /陪聊|陪玩|陪睡|宠物系/i, /在线等(你|哥哥|弟弟)/i, /淫/i, /温柔主(人)?/i, /(^|[^A-Za-z0-9])[sSmM]([^A-Za-z0-9]|$)/, /萌猫|小烧货|千万次心跳|护士姐姐|学妹|学姐|反差婊/i, /(萌|甜|软|奶|乖|小|可爱).*(猫|狗|兔|鹿|狐|虎|猫咪)/i, /(护士|学妹|学姐|老师|姐姐|妹妹).*(小烧货|反差|纯欲|欲望)/i, /\b[A-Z][a-z]{4,}[A-Z][a-z]{1,3}\d{5,}\b/, /有没有离得近的|被.*(小烧货|骚货|sao货)|扣1白给啊?|温柔s|爹系男友/i, /(射射|色色|涩涩|瑟瑟).{0,6}视频|瓦学弟|破处/i, /催情|春药|迷药|催春|迷幻|迷奸|买春|迷.?药/i ]; const NAME_PATTERNS = [ /^Date-/i, /反差|母狗|chu男|Chu男|处男|处女|臀|破处|私约|野战|处男|白虎|无偿|处女|找主人|免费|温柔主/i, /\b[A-Z][a-z]{4,}[A-Z][a-z]{1,3}\d{5,}\b/, /懂[得的].{0,3}(来|私|入|dd|联系|撩|进|加|懂)/i, /男大|足控|线下|曰|日|免费线下|被人炒|后入|同城|可飞|蜜桃臀|推特刷粉丝|刷粉/i ]; function escapeRegExp(text) { return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } // 同型字/形近字映射:黄推常用替换手段,normalize 前先还原 const CONFUSABLES = { '\u66f0': '\u65e5', // 曰→日 '\u2f72': '\u65e5', // ⽇→日 (康熙部首) '\u7384': '\u4e3b', // 玊→主 (少见但有) '\u6bcb': '\u6bcd', // 毋→母 '\u72ac': '\u5927', // 犬→大 '\u5165': '\u4eba', // 入→人 '\u56d7': '\u53e3', // 囗→口 '\u7dab': '\u7ebf', // 線→线 '\u7e10': '\u7ebf', // 縐→线 (繁体) '\u7dda': '\u7ebf', // 綫→线 '\u51e6': '\u5904', // 処→处 (日语汉字) '\u5df3': '\u5df2', // 巳→已 '\u5df1': '\u5df2', // 己→已 }; // 全角字母/数字 → 半角 (U+FF21-FF3A → A-Z, U+FF41-FF5A → a-z, U+FF10-FF19 → 0-9) for (let i = 0; i < 26; i++) { CONFUSABLES[String.fromCharCode(0xFF21 + i)] = String.fromCharCode(0x41 + i); CONFUSABLES[String.fromCharCode(0xFF41 + i)] = String.fromCharCode(0x61 + i); } for (let i = 0; i < 10; i++) { CONFUSABLES[String.fromCharCode(0xFF10 + i)] = String.fromCharCode(0x30 + i); } const CONFUSABLE_RE = new RegExp('[' + Object.keys(CONFUSABLES).map(c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0') ).join('') + ']', 'g'); function deconfuse(text) { return (text || '').replace(CONFUSABLE_RE, ch => CONFUSABLES[ch] || ch); } // 剥除零宽空格、零宽(非)连接符、方向标记、变体选择符、 // 软连字符、BOM、字节序标记等所有不可见干扰字符 const INVISIBLE_RE = /[\u200B-\u200F\u2028-\u202F\u2060-\u206F\uFEFF\u00AD\u034F\u061C\u180E\uFFF9-\uFFFC\u{E0001}\u{E0020}-\u{E007F}\u{13430}-\u{1343F}]/gu; function stripInvisible(text) { return (text || '').replace(INVISIBLE_RE, ''); } function normalize(text) { return deconfuse(stripInvisible(text)) .toLowerCase() .replace(/[\s\.\,\!\?\-_/\\()\[\]{}"'`~@#$%^&*+=<>|:;、,。!?【】()《》“”‘’·]+/g, '') .replace(/[\p{Extended_Pictographic}\uFE0F\u{1F3FB}-\u{1F3FF}]/gu, ''); } function isMostlyEmoji(text) { const stripped = (text || '').replace(/[\p{Extended_Pictographic}\uFE0F\u{1F3FB}-\u{1F3FF}\s\.,!?;:、,。!?【】()《》“”‘’·~`'"-]/gu, ''); const emojiCount = ((text || '').match(/[\p{Extended_Pictographic}]/gu) || []).length; return stripped.length === 0 && emojiCount >= 3; } function emojiOnlyWithHearts(text) { const t = text || ''; const nonEmoji = t.replace(/[\p{Extended_Pictographic}\uFE0F\u{1F3FB}-\u{1F3FF}\s\.,!?;:、,。!?【】()《》“”‘’·~`'"-]/gu, ''); if (nonEmoji.length > 2) return false; const emojis = (t.match(/[\p{Extended_Pictographic}]/gu) || []).length; if (emojis < 1) return false; return /[\u2764\uFE0F\u{1F49B}\u{1F49A}\u{1F499}\u{1F49C}\u{1F90E}\u{1F90D}\u{1F5A4}\u2763]/u.test(t); } function loadUserWords() { try { const raw = localStorage.getItem(STORAGE_KEY); const list = raw ? JSON.parse(raw) : []; return Array.isArray(list) ? list : []; } catch { return []; } } function loadWhitelist() { try { const raw = localStorage.getItem(WHITELIST_KEY); const list = raw ? JSON.parse(raw) : []; return Array.isArray(list) ? list : []; } catch { return []; } } let userWords = loadUserWords(); let userRegexes = buildUserRegexes(userWords); let whitelistNames = loadWhitelist(); let rulesVersion = 0; function buildUserRegexes(words) { return words .map(w => String(w || '').trim()) .filter(Boolean) .map(w => new RegExp(`.*${escapeRegExp(w)}.*`, 'i')); } function saveUserWords() { localStorage.setItem(STORAGE_KEY, JSON.stringify(userWords)); userRegexes = buildUserRegexes(userWords); rulesVersion++; } function saveWhitelist() { localStorage.setItem(WHITELIST_KEY, JSON.stringify(whitelistNames)); rulesVersion++; } function addUserWord(word) { const w = String(word || '').trim(); if (!w) return; if (userWords.includes(w)) return; userWords.push(w); saveUserWords(); toast(`已加入屏蔽词:${w}`); scheduleScan(); } function addWhitelistName(word) { const w = String(word || '').trim(); if (!w) return; if (whitelistNames.includes(w)) return; whitelistNames.push(w); saveWhitelist(); toast(`已加入白名单:${w}`); scheduleScan(); } function isWhitelisted(nameText) { if (!nameText) return false; const raw = nameText.toLowerCase(); const compact = normalize(nameText); return whitelistNames.some(w => { const ww = String(w || '').trim().toLowerCase(); if (!ww) return false; const wCompact = normalize(ww); return raw.includes(ww) || compact.includes(wCompact); }); } function hashString(value) { let hash = 5381; for (let i = 0; i < value.length; i++) { hash = ((hash << 5) + hash) ^ value.charCodeAt(i); } return hash >>> 0; } function shouldHide(text, nameText) { const original = deconfuse(stripInvisible(text || '')); const compact = normalize(original); const name = deconfuse(stripInvisible(nameText || '')); const nameCompact = normalize(name); const whitelisted = isWhitelisted(name); if (emojiOnlyWithHearts(original) || isMostlyEmoji(original)) return true; // 用户黑名单(内容)优先命中,避免被 quickMatch 短路 if (userRegexes.some(r => r.test(original) || r.test(compact))) return true; if (!whitelisted && NAME_PATTERNS.some(r => r.test(name) || r.test(nameCompact))) return true; if (!whitelisted && BUILTIN_PATTERNS.some(r => r.test(name) || r.test(nameCompact))) return true; if (BUILTIN_PATTERNS.some(r => r.test(original) || r.test(compact))) return true; if (!whitelisted && userRegexes.some(r => r.test(name) || r.test(nameCompact))) return true; return false; } function extractTextWithEmojis(node) { if (!node) return ''; let text = ''; for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; if (child.nodeType === 3) { text += child.nodeValue; } else if (child.nodeType === 1) { if (child.tagName === 'IMG' && child.alt) { text += child.alt; } else { text += extractTextWithEmojis(child); } } } return text; } function collectArticleText(article) { const parts = []; article.querySelectorAll('[data-testid="tweetText"]').forEach(node => { const text = extractTextWithEmojis(node); if (text) parts.push(text); }); return parts.join(' '); } function collectNameText(article) { const nameNode = article.querySelector('[data-testid="User-Name"]'); return nameNode ? (nameNode.textContent || '') : ''; } function unhideArticle(article) { if (!article.getAttribute(HIDDEN_ATTR)) return; article.removeAttribute(HIDDEN_ATTR); article.style.removeProperty('visibility'); article.style.removeProperty('display'); article.style.removeProperty('pointer-events'); const container = article.closest('div[data-testid="cellInnerDiv"]'); if (container) { container.style.removeProperty('display'); container.style.removeProperty('visibility'); } } function hideArticle(article) { const text = collectArticleText(article).trim(); const nameText = collectNameText(article).trim(); const hash = hashString(`${rulesVersion}|${text}|${nameText}`); const cachedHash = article.getAttribute(CACHE_HASH_ATTR); const cachedShould = article.getAttribute(CACHE_SHOULD_ATTR); let should; if (cachedHash && Number(cachedHash) === hash && cachedShould !== null) { should = cachedShould === '1'; } else { should = shouldHide(text, nameText); article.setAttribute(CACHE_HASH_ATTR, String(hash)); article.setAttribute(CACHE_SHOULD_ATTR, should ? '1' : '0'); } if (!should) { unhideArticle(article); return; } if (article.getAttribute(HIDDEN_ATTR)) return; article.setAttribute(HIDDEN_ATTR, '1'); if (KEEP_SPACE_WHEN_HIDE) { article.style.setProperty('visibility', 'hidden', 'important'); article.style.setProperty('pointer-events', 'none', 'important'); } else { article.style.setProperty('display', 'none', 'important'); article.style.setProperty('visibility', 'hidden', 'important'); const container = article.closest('div[data-testid="cellInnerDiv"]'); if (container) container.style.setProperty('display', 'none', 'important'); } } function scanAll() { document.querySelectorAll('article[data-testid="tweet"]').forEach(hideArticle); } let scanInProgress = false; function scheduleScan() { if (scanInProgress) return; scanInProgress = true; scanAll(); scanInProgress = false; } function handleMutations(mutations) { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType !== 1) continue; if (node.matches && node.matches('article[data-testid="tweet"]')) { hideArticle(node); } else if (node.querySelectorAll) { node.querySelectorAll('article[data-testid="tweet"]').forEach(hideArticle); } } } } function getSelectionText() { return String(window.getSelection && window.getSelection().toString() || '').trim(); } function createManagePanel() { const panel = document.createElement('div'); panel.id = 'x-ultra-block-panel'; panel.style.cssText = [ 'position:fixed', 'right:16px', 'bottom:64px', 'width:280px', 'max-height:360px', 'overflow:auto', 'background:#111', 'color:#fff', 'border:1px solid #444', 'border-radius:10px', 'padding:10px', 'font-size:12px', 'z-index:2147483647', 'display:none' ].join(';'); const header = document.createElement('div'); header.textContent = '屏蔽词管理'; header.style.cssText = 'font-weight:600; margin-bottom:8px;'; const list = document.createElement('div'); const inputRow = document.createElement('div'); inputRow.style.cssText = 'display:flex; gap:6px; margin-bottom:8px;'; const input = document.createElement('input'); input.placeholder = '手动添加屏蔽词'; input.style.cssText = 'flex:1; padding:4px 6px; border:1px solid #333; border-radius:6px; background:#0b0b0b; color:#fff;'; const addBtn = document.createElement('button'); addBtn.textContent = '添加'; addBtn.style.cssText = 'padding:4px 8px; cursor:pointer;'; inputRow.appendChild(input); inputRow.appendChild(addBtn); const quickRow = document.createElement('div'); quickRow.style.cssText = 'display:flex; gap:6px; margin-bottom:8px;'; const addSelBtn = document.createElement('button'); addSelBtn.textContent = '添加选中'; addSelBtn.style.cssText = 'flex:1; padding:4px 6px; cursor:pointer;'; const hint = document.createElement('div'); hint.textContent = '快捷键:Alt+B 添加选中文本'; hint.style.cssText = 'flex:2; color:#aaa; align-self:center;'; quickRow.appendChild(addSelBtn); quickRow.appendChild(hint); const actions = document.createElement('div'); actions.style.cssText = 'margin-top:8px; display:flex; gap:6px;'; const clearBtn = document.createElement('button'); clearBtn.textContent = '清空'; clearBtn.style.cssText = 'flex:1; padding:4px 6px; cursor:pointer;'; const closeBtn = document.createElement('button'); closeBtn.textContent = '关闭'; closeBtn.style.cssText = 'flex:1; padding:4px 6px; cursor:pointer;'; actions.appendChild(clearBtn); actions.appendChild(closeBtn); panel.appendChild(header); panel.appendChild(inputRow); panel.appendChild(quickRow); panel.appendChild(list); panel.appendChild(actions); const wlHeader = document.createElement('div'); wlHeader.textContent = '用户名白名单'; wlHeader.style.cssText = 'font-weight:600; margin:12px 0 6px;'; const wlInputRow = document.createElement('div'); wlInputRow.style.cssText = 'display:flex; gap:6px; margin-bottom:8px;'; const wlInput = document.createElement('input'); wlInput.placeholder = '添加白名单用户名/关键词'; wlInput.style.cssText = 'flex:1; padding:4px 6px; border:1px solid #333; border-radius:6px; background:#0b0b0b; color:#fff;'; const wlAddBtn = document.createElement('button'); wlAddBtn.textContent = '添加'; wlAddBtn.style.cssText = 'padding:4px 8px; cursor:pointer;'; wlInputRow.appendChild(wlInput); wlInputRow.appendChild(wlAddBtn); const wlHint = document.createElement('div'); wlHint.textContent = '快捷键:Alt+W 添加选中到白名单'; wlHint.style.cssText = 'color:#aaa; margin:0 0 8px;'; const wlList = document.createElement('div'); const wlActions = document.createElement('div'); wlActions.style.cssText = 'margin-top:8px; display:flex; gap:6px;'; const wlClearBtn = document.createElement('button'); wlClearBtn.textContent = '清空白名单'; wlClearBtn.style.cssText = 'flex:1; padding:4px 6px; cursor:pointer;'; wlActions.appendChild(wlClearBtn); panel.appendChild(wlHeader); panel.appendChild(wlInputRow); panel.appendChild(wlHint); panel.appendChild(wlList); panel.appendChild(wlActions); document.documentElement.appendChild(panel); function renderList(scrollToEnd = false) { list.innerHTML = ''; if (!userWords.length) { const empty = document.createElement('div'); empty.textContent = '暂无自定义屏蔽词'; empty.style.cssText = 'color:#888; padding:4px 0;'; list.appendChild(empty); return; } userWords.forEach((w, idx) => { const row = document.createElement('div'); row.style.cssText = 'display:flex; justify-content:space-between; align-items:center; padding:4px 0; border-bottom:1px dashed #333;'; const text = document.createElement('span'); text.textContent = w; const del = document.createElement('button'); del.textContent = '删除'; del.style.cssText = 'margin-left:8px; cursor:pointer;'; del.addEventListener('click', () => { userWords.splice(idx, 1); saveUserWords(); renderList(); scheduleScan(); }); row.appendChild(text); row.appendChild(del); list.appendChild(row); }); if (scrollToEnd) { list.lastElementChild?.scrollIntoView({ behavior: 'smooth', block: 'end' }); } } function renderWhitelist(scrollToEnd = false) { wlList.innerHTML = ''; if (!whitelistNames.length) { const empty = document.createElement('div'); empty.textContent = '暂无白名单'; empty.style.cssText = 'color:#888; padding:4px 0;'; wlList.appendChild(empty); return; } whitelistNames.forEach((w, idx) => { const row = document.createElement('div'); row.style.cssText = 'display:flex; justify-content:space-between; align-items:center; padding:4px 0; border-bottom:1px dashed #333;'; const text = document.createElement('span'); text.textContent = w; const del = document.createElement('button'); del.textContent = '删除'; del.style.cssText = 'margin-left:8px; cursor:pointer;'; del.addEventListener('click', () => { whitelistNames.splice(idx, 1); saveWhitelist(); renderWhitelist(); scheduleScan(); }); row.appendChild(text); row.appendChild(del); wlList.appendChild(row); }); if (scrollToEnd) { wlList.lastElementChild?.scrollIntoView({ behavior: 'smooth', block: 'end' }); } } addBtn.addEventListener('click', () => { addUserWord(input.value); input.value = ''; renderList(true); }); input.addEventListener('keydown', (event) => { if (event.key === 'Enter') { addUserWord(input.value); input.value = ''; renderList(true); } }); addSelBtn.addEventListener('click', () => { const sel = getSelectionText(); if (sel) addUserWord(sel); renderList(true); }); wlAddBtn.addEventListener('click', () => { addWhitelistName(wlInput.value); wlInput.value = ''; renderWhitelist(true); }); wlInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { addWhitelistName(wlInput.value); wlInput.value = ''; renderWhitelist(true); } }); clearBtn.addEventListener('click', () => { userWords = []; saveUserWords(); renderList(); scheduleScan(); }); closeBtn.addEventListener('click', () => { panel.style.display = 'none'; }); wlClearBtn.addEventListener('click', () => { whitelistNames = []; saveWhitelist(); renderWhitelist(); scheduleScan(); }); function togglePanel() { renderList(); renderWhitelist(); panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; } return { panel, togglePanel, renderWhitelist }; } function toast(message) { const node = document.createElement('div'); node.textContent = message; node.style.cssText = [ 'position:fixed', 'right:16px', 'bottom:16px', 'background:#222', 'color:#fff', 'border:1px solid #444', 'padding:8px 10px', 'border-radius:8px', 'font-size:12px', 'z-index:2147483647' ].join(';'); document.documentElement.appendChild(node); setTimeout(() => node.remove(), 1600); } function init() { scanAll(); const observer = new MutationObserver(handleMutations); observer.observe(document.documentElement, { childList: true, subtree: true }); window.addEventListener('load', scheduleScan); window.addEventListener('popstate', scheduleScan); const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function () { const result = originalPushState.apply(this, arguments); scheduleScan(); return result; }; history.replaceState = function () { const result = originalReplaceState.apply(this, arguments); scheduleScan(); return result; }; window.addEventListener('scroll', () => { if (Math.random() < 0.08) scheduleScan(); }, { passive: true }); setInterval(scheduleScan, 1200); const manage = createManagePanel(); document.addEventListener('keydown', (event) => { if (event.altKey && !event.ctrlKey && !event.metaKey && event.key.toLowerCase() === 'm') { manage.togglePanel(); } }, { capture: true }); document.addEventListener('keydown', (event) => { if (event.altKey && !event.ctrlKey && !event.metaKey && event.key.toLowerCase() === 'b') { const sel = getSelectionText(); if (sel) addUserWord(sel); } }, { capture: true }); document.addEventListener('keydown', (event) => { if (event.altKey && !event.ctrlKey && !event.metaKey && event.key.toLowerCase() === 'w') { const sel = getSelectionText(); if (sel) { addWhitelistName(sel); manage.renderWhitelist?.(true); } } }, { capture: true }); } if (document.documentElement) { init(); } else { document.addEventListener('readystatechange', () => { if (document.documentElement) init(); }, { once: true }); } })();