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();
})();