您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在X用户资料页面添加直接屏蔽按钮,支持已屏蔽状态检测
// ==UserScript== // @name X一键屏蔽 // @namespace http://tampermonkey.net/ // @version 1.4 // @description 在X用户资料页面添加直接屏蔽按钮,支持已屏蔽状态检测 // @author DeepSeek // @match https://twitter.com/* // @match https://x.com/* // @icon https://abs.twimg.com/favicons/twitter.ico // @grant none // ==/UserScript== (function() { 'use strict'; const config = { checkInterval: 1000, maxRetryCount: 3, retryDelay: 500 }; // 全局变量跟踪当前状态 let isCurrentlyOnProfilePage = false; let observer = null; function init() { const isProfile = isProfilePage(); const isOwnProfile = isOwnProfilePage(); // 如果是自己的资料页面,不执行任何操作 if (isOwnProfile) { if (isCurrentlyOnProfilePage) { console.log('检测到自己的资料页面,移除屏蔽按钮'); removeBlockButton(); isCurrentlyOnProfilePage = false; } return; } // 如果页面状态没有变化,不需要重新初始化 if (isProfile === isCurrentlyOnProfilePage) { if (isProfile) { // 已经在资料页,确保按钮存在 ensureBlockButton(); } return; } // 更新状态 isCurrentlyOnProfilePage = isProfile; if (isProfile) { console.log('检测到其他用户资料页面,初始化屏蔽按钮'); addBlockButton(); } else { console.log('不在用户资料页面,停止脚本操作'); removeBlockButton(); } } function isProfilePage() { const path = window.location.pathname; // 更精确的用户资料页面检测 const isUserProfile = /^\/([^/]+)$/.test(path) && path !== '/' && !path.includes('home') && !path.includes('explore') && !path.includes('notifications') && !path.includes('messages') && !path.includes('compose'); return isUserProfile; } function isOwnProfilePage() { if (!isProfilePage()) return false; // 方法1: 检查是否有编辑资料按钮(自己的页面才有) const editProfileSelectors = [ '[data-testid="editProfileButton"]', '[aria-label="编辑资料"]', 'a[href*="/settings/profile"]', 'div[role="button"]:contains("编辑资料")' ]; for (const selector of editProfileSelectors) { try { const element = document.querySelector(selector); if (element) { console.log('检测到编辑资料按钮,确认是自己的页面'); return true; } } catch (e) { // 忽略选择器错误 } } // 方法2: 检查是否有关注按钮(自己的页面没有关注按钮) const followButton = document.querySelector('[data-testid*="follow"]'); if (!followButton) { console.log('未找到关注按钮,可能是自己的页面'); // 进一步确认:检查是否有更多操作按钮 const moreButton = document.querySelector('[data-testid="userActions"]'); if (!moreButton) { console.log('确认是自己的资料页面(无关注和更多按钮)'); return true; } } // 方法3: 检查URL路径是否包含已知的非用户页面 const path = window.location.pathname; if (path.includes('/settings') || path.includes('/account')) { return true; } return false; } function ensureBlockButton() { if (!document.getElementById('x-block-btn')) { addBlockButton(); } } function removeBlockButton() { const blockBtn = document.getElementById('x-block-btn'); if (blockBtn) { blockBtn.remove(); console.log('移除屏蔽按钮'); } } // 添加缺失的 findActionButtons 函数 function findActionButtons() { // 主要选择器:包含关注按钮的容器 const mainSelector = 'div[data-testid="placementTracking"]'; const actionContainer = document.querySelector(mainSelector); if (actionContainer) { console.log('找到操作按钮区域'); return actionContainer; } // 备用选择器 const backupSelectors = [ 'div[class*="profile"] > div:last-child > div:last-child', 'main section > div:last-child > div:last-child' ]; for (const selector of backupSelectors) { const element = document.querySelector(selector); if (element && element.querySelector('[data-testid*="follow"]')) { return element; } } console.log('未找到操作按钮区域'); return null; } function addBlockButton() { if (document.getElementById('x-block-btn')) return; // 如果不是资料页面或者是自己的页面,不添加按钮 if (!isProfilePage() || isOwnProfilePage()) { console.log('不在用户资料页面或是自己的页面,跳过添加按钮'); return; } const actionButtons = findActionButtons(); if (!actionButtons) { setTimeout(addBlockButton, config.checkInterval); return; } const blockBtn = createBlockButton(); actionButtons.appendChild(blockBtn); console.log('屏蔽按钮已成功添加到操作区域'); // 检查是否已屏蔽 checkBlockStatus(blockBtn); } function createBlockButton() { const btn = document.createElement('div'); btn.id = 'x-block-btn'; btn.innerHTML = ` <div role="button" tabindex="0" style=" margin-left: 12px; min-width: 80px; padding: 0 16px; height: 36px; border: 1px solid rgb(83, 100, 113); border-radius: 18px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px; color: rgb(239, 243, 244); background-color: rgba(0, 0, 0, 0.9); cursor: pointer; transition: background-color 0.2s; "> 屏蔽 </div> `; // 先添加点击事件,checkBlockStatus 会决定是否移除 btn.addEventListener('click', handleBlockClick); return btn; } function checkBlockStatus(blockBtn) { // 如果不是资料页面或者是自己的页面,不检查状态 if (!isProfilePage() || isOwnProfilePage()) { return false; } console.log('检查屏蔽状态...'); // 方法1: 检查关注按钮状态(已屏蔽的用户无法关注) const followButton = document.querySelector('[data-testid*="follow"]'); if (followButton) { const isDisabled = followButton.disabled || followButton.getAttribute('aria-disabled') === 'true'; const buttonText = (followButton.textContent || '').toLowerCase(); console.log('关注按钮状态:', { isDisabled, buttonText }); if (isDisabled || buttonText.includes('unblock') || buttonText.includes('取消屏蔽')) { setButtonBlocked(blockBtn); return true; } } // 方法2: 检查页面中的屏蔽状态提示 const blockIndicators = [ 'span', 'div', '[data-testid*="block"]' ]; for (const selector of blockIndicators) { try { const elements = document.querySelectorAll(selector); for (const element of elements) { const text = (element.textContent || element.innerText || '').toLowerCase(); if (text.includes('已屏蔽') || text.includes('blocked') || text.includes('unblock') || text.includes('取消屏蔽')) { console.log('找到屏蔽指示器:', text); setButtonBlocked(blockBtn); return true; } } } catch (e) { // 忽略选择器错误 } } // 方法3: 检查更多菜单中的选项 try { const moreMenuItems = document.querySelectorAll('[role="menuitem"]'); for (const item of moreMenuItems) { const text = (item.textContent || item.innerText || '').toLowerCase(); if (text.includes('取消屏蔽') || text.includes('unblock')) { console.log('找到取消屏蔽菜单项:', text); setButtonBlocked(blockBtn); return true; } } } catch (e) { console.log('检查菜单项时出错:', e); } console.log('用户未被屏蔽,按钮保持可点击状态'); return false; } function setButtonBlocked(blockBtn) { const innerDiv = blockBtn.querySelector('div'); innerDiv.textContent = '已屏蔽'; innerDiv.style.borderColor = 'rgb(103, 193, 103)'; innerDiv.style.color = 'rgb(103, 193, 103)'; innerDiv.style.cursor = 'default'; innerDiv.style.opacity = '0.7'; // 移除点击事件 blockBtn.onclick = null; blockBtn.removeEventListener('click', handleBlockClick); console.log('用户已被屏蔽,按钮状态已更新'); } async function handleBlockClick(event) { // 防止事件冒泡 event.stopPropagation(); // 如果不是资料页面或者是自己的页面,不执行操作 if (!isProfilePage() || isOwnProfilePage()) { console.log('不在用户资料页面或是自己的页面,取消屏蔽操作'); return; } const blockBtn = document.getElementById('x-block-btn'); if (blockBtn.getAttribute('data-processing') === 'true') return; blockBtn.setAttribute('data-processing', 'true'); updateButtonState(blockBtn, 'processing'); try { await performBlockAction(); // 屏蔽成功后更新按钮状态 setButtonBlocked(blockBtn); } catch (error) { console.error('屏蔽操作失败:', error); updateButtonState(blockBtn, 'error'); setTimeout(() => updateButtonState(blockBtn, 'normal'), 2000); } finally { blockBtn.removeAttribute('data-processing'); } } async function performBlockAction() { console.log('开始执行屏蔽操作...'); // 1. 点击更多按钮 const moreBtn = findMoreButton(); if (!moreBtn) throw new Error('找不到更多按钮'); console.log('找到更多按钮,点击中...'); moreBtn.click(); await wait(1500); // 2. 查找屏蔽选项 const blockOption = findBlockOption(); if (!blockOption) throw new Error('找不到屏蔽选项'); console.log('找到屏蔽选项,点击中...'); blockOption.click(); await wait(1500); // 3. 处理确认弹窗 const confirmBtn = findConfirmButton(); if (confirmBtn) { console.log('找到确认按钮,点击中...'); confirmBtn.click(); await wait(1000); } else { console.log('未找到确认按钮,可能不需要确认'); } console.log('屏蔽操作完成'); } function findMoreButton() { const moreButtonSelectors = [ '[data-testid="userActions"]', '[aria-label="更多"]', 'div[role="button"][aria-haspopup="menu"]', 'svg[viewBox="0 0 24 24"]' ]; for (const selector of moreButtonSelectors) { const element = document.querySelector(selector); if (element) { const button = element.closest('[role="button"]') || element; console.log('找到更多按钮:', button); return button; } } console.log('未找到更多按钮'); return null; } function findBlockOption() { const blockOptionSelectors = [ '[role="menuitem"][data-testid="block"]', 'div[role="menuitem"]', 'span', 'button' ]; for (const selector of blockOptionSelectors) { const elements = document.querySelectorAll(selector); for (const element of elements) { const text = (element.textContent || element.innerText || '').toLowerCase(); if (text.includes('屏蔽') || text.includes('block')) { console.log('找到屏蔽选项:', element, text); return element.closest('[role="menuitem"]') || element; } } } console.log('未找到屏蔽选项'); return null; } function findConfirmButton() { const confirmSelectors = [ '[data-testid="confirmationSheetConfirm"]', 'div[role="button"][data-testid*="confirm"]', 'span', 'button' ]; for (const selector of confirmSelectors) { const elements = document.querySelectorAll(selector); for (const element of elements) { const text = (element.textContent || element.innerText || '').toLowerCase(); if (text.includes('确认') || text.includes('confirm')) { console.log('找到确认按钮:', element, text); return element.closest('[role="button"]') || element; } } } console.log('未找到确认按钮'); return null; } function updateButtonState(button, state) { const innerDiv = button.querySelector('div'); switch (state) { case 'processing': innerDiv.textContent = '屏蔽中...'; innerDiv.style.opacity = '0.7'; break; case 'error': innerDiv.textContent = '失败'; innerDiv.style.borderColor = 'rgb(193, 103, 103)'; innerDiv.style.color = 'rgb(193, 103, 103)'; break; default: innerDiv.textContent = '屏蔽'; innerDiv.style.opacity = '1'; innerDiv.style.borderColor = 'rgb(83, 100, 113)'; innerDiv.style.color = 'rgb(239, 243, 244)'; } } function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 初始化 let initialized = false; function startObserver() { if (initialized) return; initialized = true; init(); // 监听URL变化 let lastUrl = location.href; setInterval(() => { if (location.href !== lastUrl) { lastUrl = location.href; init(); } }, config.checkInterval); // 监听DOM变化,但只在资料页面时进行深度监听 observer = new MutationObserver((mutations) => { if (isProfilePage() && !isOwnProfilePage()) { init(); } }); observer.observe(document.body, { childList: true, subtree: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', startObserver); } else { startObserver(); } })();