您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically hides Bluesky posts immediately after confirming the block action using the native Block button.
当前为
// ==UserScript== // @name Bluesky Unified Block & Hide // @namespace https://greasyfork.org/en/users/567951-stuart-saddler // @version 1.0 // @description Automatically hides Bluesky posts immediately after confirming the block action using the native Block button. // @icon https://images.seeklogo.com/logo-png/52/2/bluesky-logo-png_seeklogo-520643.png // @match https://bsky.app/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // Global variable to store the currently selected username let currentUsername = null; /** * Utility function to log messages with a prefix for easier debugging. * @param {string} message - The message to log. */ function log(message) { console.log(`[Bluesky Auto Hide Blocked Posts] ${message}`); } /** * Determines if a given element is the "Three Dots" (Post Options) button. * @param {Element} element - The DOM element to check. * @returns {boolean} - True if it's the "Three Dots" button, else false. */ function isPostOptionsButton(element) { if (!element) return false; const ariaLabel = element.getAttribute('aria-label') || ''; return ariaLabel.includes('Open post options menu'); } /** * Determines if a given element is the "Block account" button based on its text content. * @param {Element} element - The DOM element to check. * @returns {boolean} - True if it's the "Block account" button, else false. */ function isBlockButton(element) { if (!element) return false; const blockButtonText = "Block account"; return element.textContent.trim() === blockButtonText; } /** * Extracts the username from a post container. * @param {Element} postContainer - The post container element. * @returns {string|null} - The username or null if not found. */ function getUsernameFromPost(postContainer) { if (!postContainer) return null; log('Attempting to extract username from post container.'); // Attempt to find an <a> tag with href containing "/profile/" const usernameLink = postContainer.querySelector('a[href*="/profile/"]'); if (usernameLink) { const href = usernameLink.getAttribute('href'); const parts = href.split('/'); const username = parts[parts.length - 1] || null; if (username) { log(`Extracted username: ${username}`); return username.toLowerCase(); // Ensure lowercase for consistency } } // Alternative method: Look for a span containing "@" symbol const possibleUsernameElements = postContainer.querySelectorAll('span, div'); for (let el of possibleUsernameElements) { const text = el.textContent.trim(); if (text.startsWith('@')) { // Look for text starting with "@" const username = text.substring(1); // Remove "@" symbol log(`Extracted username from span: ${username}`); return username.toLowerCase(); // Ensure lowercase for consistency } } log('Username could not be extracted from the post.'); return null; } /** * Hides all posts from the specified username. * @param {string} username - The username whose posts should be hidden. */ function hidePostsFromUser(username) { if (!username) return; log(`Hiding posts from user: @${username}`); // Define selectors based on post container identification const selector = `div[role="link"][tabindex="0"], div[role="article"], section[role="article"]`; const posts = document.querySelectorAll(selector); let hiddenCount = 0; posts.forEach(post => { const postUsername = getUsernameFromPost(post); if (postUsername && postUsername === username) { post.style.display = 'none'; log(`Post from @${username} has been hidden.`); hiddenCount++; } }); log(`Total posts hidden from @${username}: ${hiddenCount}`); } /** * Adds a username to the blocked list and hides their posts. * @param {string} username - The username to block. */ function addBlockedUser(username) { if (!username) return; hidePostsFromUser(username); } /** * Hides all posts from blocked users. * (This function is now redundant since we're not maintaining a blocked users list.) */ function hidePostsFromBlockedUsers() { // No longer needed as we're not maintaining a blocked users list. // Removed to streamline the script. } /** * Initializes the script by hiding posts from blocked users. * (This function is now redundant since we're not maintaining a blocked users list.) */ function initializeBlockedUsers() { // No longer needed as we're not maintaining a blocked users list. // Removed to streamline the script. // Optionally, if you still want to hide posts immediately after blocking without relying on storage, // you can keep any necessary logic here. } /** * Sets up the listener for the "Three Dots" (Post Options) button to capture the username. */ function setupPostOptionsListener() { document.addEventListener('click', function(event) { let target = event.target; // Traverse up the DOM tree to check if a "Three Dots" button was clicked while (target && target !== document.body) { if (isPostOptionsButton(target)) { log('"Three Dots" button clicked.'); // Find the post container associated with this button const postContainer = target.closest('div[role="link"][tabindex="0"], div[role="article"], section[role="article"]'); if (postContainer) { const username = getUsernameFromPost(postContainer); if (username) { currentUsername = username; log(`Current post's username set to: @${username}`); } else { log('Username could not be determined from the post.'); currentUsername = null; } } else { log('Post container not found.'); currentUsername = null; } break; // Exit once handled } target = target.parentElement; } }, true); // Use capture phase } /** * Sets up the listener for the "Block account" button within the menu to handle confirmation. */ function setupBlockButtonListener() { document.addEventListener('click', function(event) { let target = event.target; // Traverse up the DOM tree to check if a "Block account" button was clicked while (target && target !== document.body) { if (isBlockButton(target)) { log('"Block account" button clicked.'); // Do NOT hide posts here; wait for confirmation // The hiding will be handled in the confirmation dialog listener break; // Exit once handled } target = target.parentElement; } }, true); // Use capture phase } /** * Sets up a listener for the confirmation button to add the user to the blocked list and hide their posts. */ function setupConfirmationButtonListener() { document.addEventListener('click', function(event) { const target = event.target; // Check if the clicked element or its parent has data-testid="confirmBtn" const confirmBtn = target.closest('button[data-testid="confirmBtn"]'); if (confirmBtn) { log('Confirmation button clicked.'); if (currentUsername) { addBlockedUser(currentUsername); currentUsername = null; } else { log('No user recorded for blocking.'); } } }, true); // Use capture phase } /** * Utility function to debounce frequent function calls. * @param {Function} func - The function to debounce. * @param {number} delay - The delay in milliseconds. * @returns {Function} - The debounced function. */ function debounce(func, delay) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), delay); }; } /** * Initializes the script by setting up listeners. */ function init() { setupPostOptionsListener(); setupBlockButtonListener(); setupConfirmationButtonListener(); // initializeBlockedUsers(); // No longer needed log('Bluesky Auto Hide Blocked Posts script has been initialized.'); } /** * Waits for the DOM to be fully loaded before initializing the script. */ function waitForDOM() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { setTimeout(init, 500); // Slight delay to ensure all elements are loaded }); } else { setTimeout(init, 500); } } // Start the script waitForDOM(); })();