Skip to content

Instantly share code, notes, and snippets.

@ALERTua
Last active August 12, 2025 11:24
Show Gist options
  • Save ALERTua/d9875cad4d457cc6782ad7537b4dfc2e to your computer and use it in GitHub Desktop.
Save ALERTua/d9875cad4d457cc6782ad7537b4dfc2e to your computer and use it in GitHub Desktop.
Steam Workshop Author Filter: Filter Steam Workshop items by author to hide inappropriate content
// ==UserScript==
// @name Steam Workshop Author Filter
// @namespace http://tampermonkey.net/
// @version 1.0.1
// @description Filter Steam Workshop items by author to hide inappropriate content
// @author You
// @match https://steamcommunity.com/workshop/browse/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Initialize blocked authors list from browser storage
// Now storing objects with both ID and nickname instead of just IDs
let blockedAuthors = JSON.parse(localStorage.getItem('steamWorkshopBlockedAuthors') || '[]');
// Function to save blocked authors to browser storage (prevents duplicates)
function saveBlockedAuthors() {
// Remove duplicates by creating a map with unique IDs
const uniqueMap = new Map();
blockedAuthors.forEach(author => {
if (author && author.id) {
uniqueMap.set(author.id, author);
}
});
// Convert back to array and save
const uniqueAuthors = Array.from(uniqueMap.values());
localStorage.setItem('steamWorkshopBlockedAuthors', JSON.stringify(uniqueAuthors));
blockedAuthors = uniqueAuthors;
}
// Extract author identifier from URL - handles both profile formats
function getAuthorIdentifier(url) {
// Clean the URL (remove query parameters and trim)
const cleanUrl = url.split('?')[0].trim();
// Check for numeric profile format: /profiles/1234567890
const numericMatch = cleanUrl.match(/\/profiles\/(\d+)/);
if (numericMatch) {
return {
type: 'profiles',
id: numericMatch[1],
fullId: `profiles/${numericMatch[1]}`
};
}
// Check for custom URL format: /id/customname/myworkshopfiles
const customMatch = cleanUrl.match(/\/id\/([^\/]+)\/myworkshopfiles/);
if (customMatch) {
return {
type: 'id',
id: customMatch[1],
fullId: `id/${customMatch[1]}`
};
}
return null;
}
// Main filtering function
function filterWorkshopItems() {
// Find all author elements on the page (this is the key element that contains author info)
document.querySelectorAll('.workshopItemAuthorName').forEach(authorElement => {
const authorLink = authorElement.querySelector('.workshop_author_link');
if (!authorLink) return;
const authorInfo = getAuthorIdentifier(authorLink.href);
if (!authorInfo) return;
// Find the parent container that represents the entire workshop item
let itemContainer = authorElement;
while (itemContainer && !itemContainer.classList.contains('browseFilter')) {
itemContainer = itemContainer.parentElement;
if (!itemContainer) break;
}
// If we couldn't find a proper container, use the closest element that looks like an item
if (!itemContainer) {
itemContainer = authorElement.closest('.workshopItem') ||
authorElement.closest('.collectionItem') ||
authorElement.closest('.ugc');
}
// Check if this author is in our blocked list
const isBlocked = blockedAuthors.some(author => author.id === authorInfo.fullId);
if (isBlocked && itemContainer) {
// Hide the entire item container
itemContainer.style.display = 'none';
// Add a visual indicator (optional)
if (!itemContainer.querySelector('.blocked-author-marker')) {
const marker = document.createElement('div');
marker.className = 'blocked-author-marker';
marker.style.position = 'absolute';
marker.style.top = '5px';
marker.style.right = '5px';
marker.style.backgroundColor = 'rgba(255, 0, 0, 0.7)';
marker.style.color = 'white';
marker.style.padding = '2px 5px';
marker.style.borderRadius = '3px';
marker.style.fontSize = '0.8em';
marker.style.zIndex = '100';
marker.textContent = 'Blocked Author';
itemContainer.appendChild(marker);
}
} else if (itemContainer) {
// Show the item (in case it was previously hidden)
itemContainer.style.display = '';
// Remove marker if exists
const marker = itemContainer.querySelector('.blocked-author-marker');
if (marker) marker.remove();
}
});
}
// Add block buttons next to author names
function addBlockButtons() {
document.querySelectorAll('.workshopItemAuthorName').forEach(authorElement => {
// Check if we've already added a button here
if (authorElement.querySelector('.block-btn')) return;
const authorLink = authorElement.querySelector('.workshop_author_link');
if (!authorLink) return;
// Extract author ID
const authorInfo = getAuthorIdentifier(authorLink.href);
if (!authorInfo) return;
// Get the author's display name (nickname)
const authorName = authorLink.textContent.trim();
// Check if this author is in our blocked list
const isBlocked = blockedAuthors.some(author => author.id === authorInfo.fullId);
// Create block button
const blockBtn = document.createElement('button');
blockBtn.className = 'block-btn';
blockBtn.style.marginLeft = '5px';
blockBtn.style.fontSize = '0.8em';
blockBtn.textContent = isBlocked ? 'Unblock' : 'Block';
blockBtn.style.backgroundColor = isBlocked ? '#d32f2f' : '#4caf50';
blockBtn.style.color = 'white';
blockBtn.style.border = 'none';
blockBtn.style.borderRadius = '3px';
blockBtn.style.padding = '1px 5px';
blockBtn.style.cursor = 'pointer';
// Add click handler
blockBtn.addEventListener('click', function() {
if (isBlocked) {
// Remove from blocked list
blockedAuthors = blockedAuthors.filter(author => author.id !== authorInfo.fullId);
} else {
// Add to blocked list with nickname
blockedAuthors.push({
id: authorInfo.fullId,
nickname: authorName
});
}
// Save to browser storage (prevents duplicates)
saveBlockedAuthors();
// Reapply filtering
filterWorkshopItems();
// Update button text
const newIsBlocked = blockedAuthors.some(author => author.id === authorInfo.fullId);
this.textContent = newIsBlocked ? 'Unblock' : 'Block';
this.style.backgroundColor = newIsBlocked ? '#d32f2f' : '#4caf50';
});
authorElement.appendChild(blockBtn);
});
}
// Add management UI for blocked authors
function addManagementUI() {
// Create a button to open the management panel
const managementBtn = document.createElement('button');
managementBtn.textContent = `Manage Blocked Authors (${blockedAuthors.length})`;
managementBtn.style.position = 'fixed';
managementBtn.style.bottom = '20px';
managementBtn.style.right = '20px';
managementBtn.style.backgroundColor = '#333';
managementBtn.style.color = 'white';
managementBtn.style.border = 'none';
managementBtn.style.borderRadius = '5px';
managementBtn.style.padding = '10px 15px';
managementBtn.style.cursor = 'pointer';
managementBtn.style.zIndex = '9999';
managementBtn.addEventListener('click', function() {
// Create management panel
const panel = document.createElement('div');
panel.style.position = 'fixed';
panel.style.top = '50%';
panel.style.left = '50%';
panel.style.transform = 'translate(-50%, -50%)';
panel.style.backgroundColor = '#2a2a2a';
panel.style.color = 'white';
panel.style.padding = '20px';
panel.style.borderRadius = '5px';
panel.style.zIndex = '10000';
panel.style.width = '300px';
panel.style.maxHeight = '80vh';
panel.style.overflowY = 'auto';
// Add title
const title = document.createElement('h3');
title.textContent = 'Blocked Authors';
title.style.marginTop = '0';
panel.appendChild(title);
// Add explanation note
const note = document.createElement('p');
note.style.fontSize = '0.8em';
note.style.color = '#aaa';
note.style.marginBottom = '15px';
note.textContent = 'Note: Blocking by custom URL (id/...) may not cover all appearances of the same author.';
panel.appendChild(note);
// Add list of blocked authors
if (blockedAuthors.length === 0) {
const emptyMsg = document.createElement('p');
emptyMsg.textContent = 'No authors blocked';
panel.appendChild(emptyMsg);
} else {
const list = document.createElement('ul');
list.style.listStyle = 'none';
list.style.padding = '0';
blockedAuthors.forEach(author => {
const li = document.createElement('li');
li.style.display = 'flex';
li.style.flexDirection = 'column';
li.style.marginBottom = '15px';
li.style.paddingBottom = '10px';
li.style.borderBottom = '1px solid #444';
// Author name
const nameSpan = document.createElement('span');
nameSpan.style.fontWeight = 'bold';
nameSpan.style.marginBottom = '5px';
nameSpan.textContent = author.nickname || author.id;
li.appendChild(nameSpan);
// Author ID
const idSpan = document.createElement('span');
idSpan.style.fontSize = '0.8em';
idSpan.style.color = '#aaa';
idSpan.style.overflow = 'hidden';
idSpan.style.textOverflow = 'ellipsis';
idSpan.style.whiteSpace = 'nowrap';
idSpan.textContent = author.id;
li.appendChild(idSpan);
const unblockBtn = document.createElement('button');
unblockBtn.textContent = 'Unblock';
unblockBtn.style.backgroundColor = '#d32f2f';
unblockBtn.style.color = 'white';
unblockBtn.style.border = 'none';
unblockBtn.style.borderRadius = '3px';
unblockBtn.style.padding = '2px 10px';
unblockBtn.style.marginTop = '8px';
unblockBtn.style.alignSelf = 'flex-end';
unblockBtn.addEventListener('click', function() {
blockedAuthors = blockedAuthors.filter(a => a.id !== author.id);
saveBlockedAuthors();
filterWorkshopItems();
document.body.removeChild(panel);
document.body.removeChild(managementBtn);
addManagementUI(); // Refresh the button count
});
li.appendChild(unblockBtn);
list.appendChild(li);
});
panel.appendChild(list);
}
// Add close button
const closeBtn = document.createElement('button');
closeBtn.textContent = 'Close';
closeBtn.style.backgroundColor = '#555';
closeBtn.style.color = 'white';
closeBtn.style.border = 'none';
closeBtn.style.borderRadius = '3px';
closeBtn.style.padding = '5px 10px';
closeBtn.style.width = '100%';
closeBtn.style.marginTop = '10px';
closeBtn.addEventListener('click', function() {
document.body.removeChild(panel);
});
panel.appendChild(closeBtn);
document.body.appendChild(panel);
});
// Remove existing button if it exists
const existingBtn = document.querySelector('#author-filter-management-btn');
if (existingBtn) existingBtn.remove();
managementBtn.id = 'author-filter-management-btn';
document.body.appendChild(managementBtn);
}
// Initial setup
function init() {
// Apply filtering initially
filterWorkshopItems();
// Add block buttons
addBlockButtons();
// Add management UI
addManagementUI();
// Set up mutation observer to handle dynamically loaded content
const observer = new MutationObserver(function() {
filterWorkshopItems();
addBlockButtons();
});
// Observe the main content area
const contentArea = document.querySelector('.workshopBrowseItems') || document.body;
observer.observe(contentArea, {
childList: true,
subtree: true
});
}
// Start the script after a short delay to ensure page is loaded
// Multiple attempts to handle slow loading
setTimeout(init, 1000);
setTimeout(init, 2000);
setTimeout(init, 3000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment