We have a Steam curator now. You should be following it. https://store.steampowered.com/curator/44994899-RPGHQ/
Chat client updated, if you have issues using chat press CTRL + SHIFT + R to force a hard refresh.

[Browser Script] Nexus Filters

Do you want to add big tiddies to a game but don't know how? Ask someone else to do it for you!

Moderator: Mod Janitor

Ignore Topic
User avatar
Oyster Sauce
Site Moderator
Posts: 11294
Joined: Jun 2, '23

Geolocation

Adventurer's Guild

[Browser Script] Nexus Filters

Post by Oyster Sauce »

Every single time I open Nexus I'm assailed by dogshit zero effort gacha voice packs or skins, and it's usually the only mod created by the author. I would really like a way to hide mods based on a custom filter list so I could add things like "genshin" "arknights" "hololive" "transgender" etc and never have to worry about it again.

Image
User avatar
rusty_shackleford
Site Admin
Posts: 45470
Joined: Feb 2, '23
Gender: Watermelon

Geolocation

Adventurer's Guild

Post by rusty_shackleford »

Slaved on this for 12 hours straight to help my friend :turtle:

Code: Select all

// ==UserScript==
// @name         Nexus Mods Hide Keywords
// @namespace    nexusmods-hide-keywords
// @version      1.0
// @description  Hide mods based on keywords in their names
// @author       Rusty
// @match        https://www.nexusmods.com/*
// @match        https://next.nexusmods.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let keywords = [];
    const STORAGE_KEY = 'nexusmods-hide-keywords';

    // Load keywords from localStorage
    function loadKeywords() {
        const stored = localStorage.getItem(STORAGE_KEY);
        keywords = stored ? JSON.parse(stored) : [];
    }

    // Save keywords to localStorage
    function saveKeywords() {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(keywords));
    }

    // Check if a title matches any keyword (case insensitive)
    function titleMatchesKeyword(title) {
        if (!title || keywords.length === 0) return false;
        return keywords.some(keyword =>
            title.toLowerCase().includes(keyword.toLowerCase())
        );
    }

    // Hide/show mods based on keywords
    function processMods() {
        // Look for mod tiles with the specific data attribute
        const modTiles = document.querySelectorAll('[data-e2eid="mod-tile"]');

        modTiles.forEach(tile => {
            const titleElement = tile.querySelector('[data-e2eid="mod-tile-title"]');
            if (titleElement) {
                const title = titleElement.textContent.trim();
                if (titleMatchesKeyword(title)) {
                    tile.style.display = 'none';
                    tile.setAttribute('data-hidden-by-keywords', 'true');
                } else {
                    tile.style.display = '';
                    tile.removeAttribute('data-hidden-by-keywords');
                }
            }
        });

        // Also check for alternative mod card structures
        const alternativeSelectors = [
            'a[href*="/mods/"]',
            '[class*="mod"]',
            '[class*="tile"]'
        ];

        alternativeSelectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(element => {
                // Skip if already processed
                if (element.hasAttribute('data-hidden-by-keywords') ||
                    element.querySelector('[data-e2eid="mod-tile-title"]')) {
                    return;
                }

                // Look for title text in various ways
                let title = '';
                const possibleTitleElements = element.querySelectorAll('h1, h2, h3, h4, h5, h6, [class*="title"], [class*="name"]');

                if (possibleTitleElements.length > 0) {
                    title = possibleTitleElements[0].textContent.trim();
                } else {
                    // Fallback to alt text or aria-label
                    const img = element.querySelector('img[alt]');
                    if (img) title = img.alt;
                }

                if (title && titleMatchesKeyword(title)) {
                    element.style.display = 'none';
                    element.setAttribute('data-hidden-by-keywords', 'true');
                } else if (title) {
                    element.style.display = '';
                    element.removeAttribute('data-hidden-by-keywords');
                }
            });
        });
    }

    // Create the overlay
    function createOverlay() {
        // Remove existing overlay if any
        const existingOverlay = document.getElementById('keyword-overlay');
        if (existingOverlay) {
            existingOverlay.remove();
        }

        const overlay = document.createElement('div');
        overlay.id = 'keyword-overlay';
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 10000;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        `;

        const modal = document.createElement('div');
        modal.style.cssText = `
            background: #1a1a1a;
            color: #ffffff;
            padding: 25px;
            border-radius: 12px;
            max-width: 600px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
            border: 1px solid #333;
        `;

        modal.innerHTML = `
            <h2 style="margin-top: 0; color: #ff6600; font-size: 24px;">🚫 Hide Keywords</h2>
            <p style="color: #ccc; margin-bottom: 20px;">Add keywords to hide mods with matching names (case insensitive):</p>
            <div style="margin-bottom: 20px; display: flex; gap: 10px;">
                <input type="text" id="keyword-input" placeholder="Enter keyword..." style="
                    flex: 1;
                    padding: 10px;
                    border: 1px solid #444;
                    border-radius: 6px;
                    background: #2a2a2a;
                    color: #fff;
                    font-size: 14px;
                ">
                <button id="add-keyword" style="
                    padding: 10px 20px;
                    background: #ff6600;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                    font-weight: bold;
                ">Add</button>
            </div>
            <div id="keyword-list" style="margin-bottom: 20px; max-height: 300px; overflow-y: auto;"></div>
            <div style="text-align: right; border-top: 1px solid #333; padding-top: 15px;">
                <button id="close-overlay" style="
                    padding: 10px 20px;
                    background: #666;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                ">Close</button>
            </div>
        `;

        overlay.appendChild(modal);
        document.body.appendChild(overlay);

        // Event listeners
        document.getElementById('add-keyword').addEventListener('click', addKeyword);
        document.getElementById('keyword-input').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') addKeyword();
        });
        document.getElementById('close-overlay').addEventListener('click', closeOverlay);
        overlay.addEventListener('click', function(e) {
            if (e.target === overlay) closeOverlay();
        });

        updateKeywordList();
        document.getElementById('keyword-input').focus();
    }

    function addKeyword() {
        const input = document.getElementById('keyword-input');
        const keyword = input.value.trim();

        if (keyword && !keywords.some(k => k.toLowerCase() === keyword.toLowerCase())) {
            keywords.push(keyword);
            saveKeywords();
            updateKeywordList();
            processMods();
            input.value = '';
            showMessage(`Added keyword: "${keyword}"`);
        } else if (keyword) {
            showMessage(`Keyword "${keyword}" already exists!`, 'error');
        }
    }

    function removeKeyword(keyword) {
        const index = keywords.findIndex(k => k === keyword);
        if (index > -1) {
            keywords.splice(index, 1);
            saveKeywords();
            updateKeywordList();
            processMods();
            showMessage(`Removed keyword: "${keyword}"`);
        }
    }

    function updateKeywordList() {
        const list = document.getElementById('keyword-list');
        if (!list) return;

        list.innerHTML = '';

        if (keywords.length === 0) {
            list.innerHTML = '<p style="color: #888; text-align: center; font-style: italic;">No keywords added yet</p>';
            return;
        }

        keywords.forEach(keyword => {
            const item = document.createElement('div');
            item.style.cssText = `
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 10px;
                border: 1px solid #444;
                margin: 5px 0;
                border-radius: 6px;
                background: #2a2a2a;
            `;

            item.innerHTML = `
                <span style="color: #fff;">${escapeHtml(keyword)}</span>
                <button data-keyword="${escapeHtml(keyword)}" style="
                    padding: 5px 10px;
                    background: #dc3545;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                ">Remove</button>
            `;

            const removeBtn = item.querySelector('button');
            removeBtn.addEventListener('click', () => removeKeyword(keyword));

            list.appendChild(item);
        });
    }

    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    function showMessage(message, type = 'success') {
        const messageEl = document.createElement('div');
        messageEl.textContent = message;
        messageEl.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 10px 15px;
            border-radius: 6px;
            z-index: 10001;
            font-weight: bold;
            max-width: 300px;
            background: ${type === 'error' ? '#dc3545' : '#28a745'};
            color: white;
        `;

        document.body.appendChild(messageEl);

        setTimeout(() => {
            messageEl.remove();
        }, 3000);
    }

    function closeOverlay() {
        const overlay = document.getElementById('keyword-overlay');
        if (overlay) {
            overlay.remove();
        }
    }

    // Create and add the button
    function createButton() {
        // Remove existing button if any
        const existingButton = document.getElementById('hide-keywords-btn');
        if (existingButton) {
            existingButton.remove();
        }

        // Find the logo element
        const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
        if (!logoElement) {
            console.log('Logo element not found, will retry...');
            return;
        }

        const button = document.createElement('button');
        button.id = 'hide-keywords-btn';
        button.innerHTML = '🚫 Keywords';
        button.style.cssText = `
            margin-left: 12px;
            padding: 8px 12px;
            background: #ff6600;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-size: 12px;
            box-shadow: 0 2px 8px rgba(255, 102, 0, 0.3);
            transition: all 0.2s ease;
            white-space: nowrap;
            flex-shrink: 0;
        `;

        button.addEventListener('mouseenter', () => {
            button.style.transform = 'translateY(-1px)';
            button.style.boxShadow = '0 4px 12px rgba(255, 102, 0, 0.4)';
        });

        button.addEventListener('mouseleave', () => {
            button.style.transform = 'translateY(0)';
            button.style.boxShadow = '0 2px 8px rgba(255, 102, 0, 0.3)';
        });

        button.addEventListener('click', createOverlay);

        // Insert the button next to the logo
        const logoParent = logoElement.parentElement;
        if (logoParent) {
            logoParent.insertAdjacentElement('afterend', button);
        } else {
            // Fallback to appending after the logo
            logoElement.insertAdjacentElement('afterend', button);
        }
    }

    // Initialize
    function init() {
        loadKeywords();

        // Try to create button, with retries if logo not found
        let buttonRetries = 0;
        const maxButtonRetries = 10;

        function tryCreateButton() {
            const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
            if (logoElement) {
                createButton();
            } else if (buttonRetries < maxButtonRetries) {
                buttonRetries++;
                setTimeout(tryCreateButton, 500);
            } else {
                console.log('Could not find logo element after retries, button not added');
            }
        }

        tryCreateButton();

        // Initial processing
        setTimeout(processMods, 1000);

        // Watch for new content with debouncing
        let processTimeout;
        const debouncedProcess = () => {
            clearTimeout(processTimeout);
            processTimeout = setTimeout(processMods, 500);
        };

        const observer = new MutationObserver((mutations) => {
            debouncedProcess();

            // Check if we need to recreate the button (in case header was re-rendered)
            if (!document.getElementById('hide-keywords-btn')) {
                const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
                if (logoElement) {
                    createButton();
                }
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // Also process on scroll (for infinite scroll)
        let scrollTimeout;
        window.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(processMods, 300);
        });

        console.log(`Nexus Mods Hide Keywords loaded! Current keywords: ${keywords.length}`);
    }

    // Wait for page to load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
Thank you for your attention to this matter!
Steam friend code: 40552640 https://steamcommunity.com/friends/add | email: [email protected]
Having trouble running an old Windows game?
Rusty's Stuff Collection
User avatar
Oyster Sauce
Site Moderator
Posts: 11294
Joined: Jun 2, '23

Geolocation

Adventurer's Guild

Post by Oyster Sauce »

rusty_shackleford wrote: June 29th, 2025, 03:37
Slaved on this for 12 hours straight to help my friend :turtle:

Code: Select all

// ==UserScript==
// @name         Nexus Mods Hide Keywords
// @namespace    nexusmods-hide-keywords
// @version      1.0
// @description  Hide mods based on keywords in their names
// @author       Rusty
// @match        https://www.nexusmods.com/*
// @match        https://next.nexusmods.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let keywords = [];
    const STORAGE_KEY = 'nexusmods-hide-keywords';

    // Load keywords from localStorage
    function loadKeywords() {
        const stored = localStorage.getItem(STORAGE_KEY);
        keywords = stored ? JSON.parse(stored) : [];
    }

    // Save keywords to localStorage
    function saveKeywords() {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(keywords));
    }

    // Check if a title matches any keyword (case insensitive)
    function titleMatchesKeyword(title) {
        if (!title || keywords.length === 0) return false;
        return keywords.some(keyword =>
            title.toLowerCase().includes(keyword.toLowerCase())
        );
    }

    // Hide/show mods based on keywords
    function processMods() {
        // Look for mod tiles with the specific data attribute
        const modTiles = document.querySelectorAll('[data-e2eid="mod-tile"]');

        modTiles.forEach(tile => {
            const titleElement = tile.querySelector('[data-e2eid="mod-tile-title"]');
            if (titleElement) {
                const title = titleElement.textContent.trim();
                if (titleMatchesKeyword(title)) {
                    tile.style.display = 'none';
                    tile.setAttribute('data-hidden-by-keywords', 'true');
                } else {
                    tile.style.display = '';
                    tile.removeAttribute('data-hidden-by-keywords');
                }
            }
        });

        // Also check for alternative mod card structures
        const alternativeSelectors = [
            'a[href*="/mods/"]',
            '[class*="mod"]',
            '[class*="tile"]'
        ];

        alternativeSelectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(element => {
                // Skip if already processed
                if (element.hasAttribute('data-hidden-by-keywords') ||
                    element.querySelector('[data-e2eid="mod-tile-title"]')) {
                    return;
                }

                // Look for title text in various ways
                let title = '';
                const possibleTitleElements = element.querySelectorAll('h1, h2, h3, h4, h5, h6, [class*="title"], [class*="name"]');

                if (possibleTitleElements.length > 0) {
                    title = possibleTitleElements[0].textContent.trim();
                } else {
                    // Fallback to alt text or aria-label
                    const img = element.querySelector('img[alt]');
                    if (img) title = img.alt;
                }

                if (title && titleMatchesKeyword(title)) {
                    element.style.display = 'none';
                    element.setAttribute('data-hidden-by-keywords', 'true');
                } else if (title) {
                    element.style.display = '';
                    element.removeAttribute('data-hidden-by-keywords');
                }
            });
        });
    }

    // Create the overlay
    function createOverlay() {
        // Remove existing overlay if any
        const existingOverlay = document.getElementById('keyword-overlay');
        if (existingOverlay) {
            existingOverlay.remove();
        }

        const overlay = document.createElement('div');
        overlay.id = 'keyword-overlay';
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 10000;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        `;

        const modal = document.createElement('div');
        modal.style.cssText = `
            background: #1a1a1a;
            color: #ffffff;
            padding: 25px;
            border-radius: 12px;
            max-width: 600px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
            border: 1px solid #333;
        `;

        modal.innerHTML = `
            <h2 style="margin-top: 0; color: #ff6600; font-size: 24px;">🚫 Hide Keywords</h2>
            <p style="color: #ccc; margin-bottom: 20px;">Add keywords to hide mods with matching names (case insensitive):</p>
            <div style="margin-bottom: 20px; display: flex; gap: 10px;">
                <input type="text" id="keyword-input" placeholder="Enter keyword..." style="
                    flex: 1;
                    padding: 10px;
                    border: 1px solid #444;
                    border-radius: 6px;
                    background: #2a2a2a;
                    color: #fff;
                    font-size: 14px;
                ">
                <button id="add-keyword" style="
                    padding: 10px 20px;
                    background: #ff6600;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                    font-weight: bold;
                ">Add</button>
            </div>
            <div id="keyword-list" style="margin-bottom: 20px; max-height: 300px; overflow-y: auto;"></div>
            <div style="text-align: right; border-top: 1px solid #333; padding-top: 15px;">
                <button id="close-overlay" style="
                    padding: 10px 20px;
                    background: #666;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                ">Close</button>
            </div>
        `;

        overlay.appendChild(modal);
        document.body.appendChild(overlay);

        // Event listeners
        document.getElementById('add-keyword').addEventListener('click', addKeyword);
        document.getElementById('keyword-input').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') addKeyword();
        });
        document.getElementById('close-overlay').addEventListener('click', closeOverlay);
        overlay.addEventListener('click', function(e) {
            if (e.target === overlay) closeOverlay();
        });

        updateKeywordList();
        document.getElementById('keyword-input').focus();
    }

    function addKeyword() {
        const input = document.getElementById('keyword-input');
        const keyword = input.value.trim();

        if (keyword && !keywords.some(k => k.toLowerCase() === keyword.toLowerCase())) {
            keywords.push(keyword);
            saveKeywords();
            updateKeywordList();
            processMods();
            input.value = '';
            showMessage(`Added keyword: "${keyword}"`);
        } else if (keyword) {
            showMessage(`Keyword "${keyword}" already exists!`, 'error');
        }
    }

    function removeKeyword(keyword) {
        const index = keywords.findIndex(k => k === keyword);
        if (index > -1) {
            keywords.splice(index, 1);
            saveKeywords();
            updateKeywordList();
            processMods();
            showMessage(`Removed keyword: "${keyword}"`);
        }
    }

    function updateKeywordList() {
        const list = document.getElementById('keyword-list');
        if (!list) return;

        list.innerHTML = '';

        if (keywords.length === 0) {
            list.innerHTML = '<p style="color: #888; text-align: center; font-style: italic;">No keywords added yet</p>';
            return;
        }

        keywords.forEach(keyword => {
            const item = document.createElement('div');
            item.style.cssText = `
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 10px;
                border: 1px solid #444;
                margin: 5px 0;
                border-radius: 6px;
                background: #2a2a2a;
            `;

            item.innerHTML = `
                <span style="color: #fff;">${escapeHtml(keyword)}</span>
                <button data-keyword="${escapeHtml(keyword)}" style="
                    padding: 5px 10px;
                    background: #dc3545;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                ">Remove</button>
            `;

            const removeBtn = item.querySelector('button');
            removeBtn.addEventListener('click', () => removeKeyword(keyword));

            list.appendChild(item);
        });
    }

    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    function showMessage(message, type = 'success') {
        const messageEl = document.createElement('div');
        messageEl.textContent = message;
        messageEl.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 10px 15px;
            border-radius: 6px;
            z-index: 10001;
            font-weight: bold;
            max-width: 300px;
            background: ${type === 'error' ? '#dc3545' : '#28a745'};
            color: white;
        `;

        document.body.appendChild(messageEl);

        setTimeout(() => {
            messageEl.remove();
        }, 3000);
    }

    function closeOverlay() {
        const overlay = document.getElementById('keyword-overlay');
        if (overlay) {
            overlay.remove();
        }
    }

    // Create and add the button
    function createButton() {
        // Remove existing button if any
        const existingButton = document.getElementById('hide-keywords-btn');
        if (existingButton) {
            existingButton.remove();
        }

        // Find the logo element
        const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
        if (!logoElement) {
            console.log('Logo element not found, will retry...');
            return;
        }

        const button = document.createElement('button');
        button.id = 'hide-keywords-btn';
        button.innerHTML = '🚫 Keywords';
        button.style.cssText = `
            margin-left: 12px;
            padding: 8px 12px;
            background: #ff6600;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-size: 12px;
            box-shadow: 0 2px 8px rgba(255, 102, 0, 0.3);
            transition: all 0.2s ease;
            white-space: nowrap;
            flex-shrink: 0;
        `;

        button.addEventListener('mouseenter', () => {
            button.style.transform = 'translateY(-1px)';
            button.style.boxShadow = '0 4px 12px rgba(255, 102, 0, 0.4)';
        });

        button.addEventListener('mouseleave', () => {
            button.style.transform = 'translateY(0)';
            button.style.boxShadow = '0 2px 8px rgba(255, 102, 0, 0.3)';
        });

        button.addEventListener('click', createOverlay);

        // Insert the button next to the logo
        const logoParent = logoElement.parentElement;
        if (logoParent) {
            logoParent.insertAdjacentElement('afterend', button);
        } else {
            // Fallback to appending after the logo
            logoElement.insertAdjacentElement('afterend', button);
        }
    }

    // Initialize
    function init() {
        loadKeywords();

        // Try to create button, with retries if logo not found
        let buttonRetries = 0;
        const maxButtonRetries = 10;

        function tryCreateButton() {
            const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
            if (logoElement) {
                createButton();
            } else if (buttonRetries < maxButtonRetries) {
                buttonRetries++;
                setTimeout(tryCreateButton, 500);
            } else {
                console.log('Could not find logo element after retries, button not added');
            }
        }

        tryCreateButton();

        // Initial processing
        setTimeout(processMods, 1000);

        // Watch for new content with debouncing
        let processTimeout;
        const debouncedProcess = () => {
            clearTimeout(processTimeout);
            processTimeout = setTimeout(processMods, 500);
        };

        const observer = new MutationObserver((mutations) => {
            debouncedProcess();

            // Check if we need to recreate the button (in case header was re-rendered)
            if (!document.getElementById('hide-keywords-btn')) {
                const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
                if (logoElement) {
                    createButton();
                }
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // Also process on scroll (for infinite scroll)
        let scrollTimeout;
        window.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(processMods, 300);
        });

        console.log(`Nexus Mods Hide Keywords loaded! Current keywords: ${keywords.length}`);
    }

    // Wait for page to load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
I put the keywords in this part and it doesn't seem to work :broken:

Image
User avatar
rusty_shackleford
Site Admin
Posts: 45470
Joined: Feb 2, '23
Gender: Watermelon

Geolocation

Adventurer's Guild

Post by rusty_shackleford »

Oyster Sauce wrote: June 29th, 2025, 04:03
rusty_shackleford wrote: June 29th, 2025, 03:37
Slaved on this for 12 hours straight to help my friend :turtle:

Code: Select all

// ==UserScript==
// @name         Nexus Mods Hide Keywords
// @namespace    nexusmods-hide-keywords
// @version      1.0
// @description  Hide mods based on keywords in their names
// @author       Rusty
// @match        https://www.nexusmods.com/*
// @match        https://next.nexusmods.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let keywords = [];
    const STORAGE_KEY = 'nexusmods-hide-keywords';

    // Load keywords from localStorage
    function loadKeywords() {
        const stored = localStorage.getItem(STORAGE_KEY);
        keywords = stored ? JSON.parse(stored) : [];
    }

    // Save keywords to localStorage
    function saveKeywords() {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(keywords));
    }

    // Check if a title matches any keyword (case insensitive)
    function titleMatchesKeyword(title) {
        if (!title || keywords.length === 0) return false;
        return keywords.some(keyword =>
            title.toLowerCase().includes(keyword.toLowerCase())
        );
    }

    // Hide/show mods based on keywords
    function processMods() {
        // Look for mod tiles with the specific data attribute
        const modTiles = document.querySelectorAll('[data-e2eid="mod-tile"]');

        modTiles.forEach(tile => {
            const titleElement = tile.querySelector('[data-e2eid="mod-tile-title"]');
            if (titleElement) {
                const title = titleElement.textContent.trim();
                if (titleMatchesKeyword(title)) {
                    tile.style.display = 'none';
                    tile.setAttribute('data-hidden-by-keywords', 'true');
                } else {
                    tile.style.display = '';
                    tile.removeAttribute('data-hidden-by-keywords');
                }
            }
        });

        // Also check for alternative mod card structures
        const alternativeSelectors = [
            'a[href*="/mods/"]',
            '[class*="mod"]',
            '[class*="tile"]'
        ];

        alternativeSelectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(element => {
                // Skip if already processed
                if (element.hasAttribute('data-hidden-by-keywords') ||
                    element.querySelector('[data-e2eid="mod-tile-title"]')) {
                    return;
                }

                // Look for title text in various ways
                let title = '';
                const possibleTitleElements = element.querySelectorAll('h1, h2, h3, h4, h5, h6, [class*="title"], [class*="name"]');

                if (possibleTitleElements.length > 0) {
                    title = possibleTitleElements[0].textContent.trim();
                } else {
                    // Fallback to alt text or aria-label
                    const img = element.querySelector('img[alt]');
                    if (img) title = img.alt;
                }

                if (title && titleMatchesKeyword(title)) {
                    element.style.display = 'none';
                    element.setAttribute('data-hidden-by-keywords', 'true');
                } else if (title) {
                    element.style.display = '';
                    element.removeAttribute('data-hidden-by-keywords');
                }
            });
        });
    }

    // Create the overlay
    function createOverlay() {
        // Remove existing overlay if any
        const existingOverlay = document.getElementById('keyword-overlay');
        if (existingOverlay) {
            existingOverlay.remove();
        }

        const overlay = document.createElement('div');
        overlay.id = 'keyword-overlay';
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 10000;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        `;

        const modal = document.createElement('div');
        modal.style.cssText = `
            background: #1a1a1a;
            color: #ffffff;
            padding: 25px;
            border-radius: 12px;
            max-width: 600px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
            border: 1px solid #333;
        `;

        modal.innerHTML = `
            <h2 style="margin-top: 0; color: #ff6600; font-size: 24px;">🚫 Hide Keywords</h2>
            <p style="color: #ccc; margin-bottom: 20px;">Add keywords to hide mods with matching names (case insensitive):</p>
            <div style="margin-bottom: 20px; display: flex; gap: 10px;">
                <input type="text" id="keyword-input" placeholder="Enter keyword..." style="
                    flex: 1;
                    padding: 10px;
                    border: 1px solid #444;
                    border-radius: 6px;
                    background: #2a2a2a;
                    color: #fff;
                    font-size: 14px;
                ">
                <button id="add-keyword" style="
                    padding: 10px 20px;
                    background: #ff6600;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                    font-weight: bold;
                ">Add</button>
            </div>
            <div id="keyword-list" style="margin-bottom: 20px; max-height: 300px; overflow-y: auto;"></div>
            <div style="text-align: right; border-top: 1px solid #333; padding-top: 15px;">
                <button id="close-overlay" style="
                    padding: 10px 20px;
                    background: #666;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                ">Close</button>
            </div>
        `;

        overlay.appendChild(modal);
        document.body.appendChild(overlay);

        // Event listeners
        document.getElementById('add-keyword').addEventListener('click', addKeyword);
        document.getElementById('keyword-input').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') addKeyword();
        });
        document.getElementById('close-overlay').addEventListener('click', closeOverlay);
        overlay.addEventListener('click', function(e) {
            if (e.target === overlay) closeOverlay();
        });

        updateKeywordList();
        document.getElementById('keyword-input').focus();
    }

    function addKeyword() {
        const input = document.getElementById('keyword-input');
        const keyword = input.value.trim();

        if (keyword && !keywords.some(k => k.toLowerCase() === keyword.toLowerCase())) {
            keywords.push(keyword);
            saveKeywords();
            updateKeywordList();
            processMods();
            input.value = '';
            showMessage(`Added keyword: "${keyword}"`);
        } else if (keyword) {
            showMessage(`Keyword "${keyword}" already exists!`, 'error');
        }
    }

    function removeKeyword(keyword) {
        const index = keywords.findIndex(k => k === keyword);
        if (index > -1) {
            keywords.splice(index, 1);
            saveKeywords();
            updateKeywordList();
            processMods();
            showMessage(`Removed keyword: "${keyword}"`);
        }
    }

    function updateKeywordList() {
        const list = document.getElementById('keyword-list');
        if (!list) return;

        list.innerHTML = '';

        if (keywords.length === 0) {
            list.innerHTML = '<p style="color: #888; text-align: center; font-style: italic;">No keywords added yet</p>';
            return;
        }

        keywords.forEach(keyword => {
            const item = document.createElement('div');
            item.style.cssText = `
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 10px;
                border: 1px solid #444;
                margin: 5px 0;
                border-radius: 6px;
                background: #2a2a2a;
            `;

            item.innerHTML = `
                <span style="color: #fff;">${escapeHtml(keyword)}</span>
                <button data-keyword="${escapeHtml(keyword)}" style="
                    padding: 5px 10px;
                    background: #dc3545;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                ">Remove</button>
            `;

            const removeBtn = item.querySelector('button');
            removeBtn.addEventListener('click', () => removeKeyword(keyword));

            list.appendChild(item);
        });
    }

    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    function showMessage(message, type = 'success') {
        const messageEl = document.createElement('div');
        messageEl.textContent = message;
        messageEl.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 10px 15px;
            border-radius: 6px;
            z-index: 10001;
            font-weight: bold;
            max-width: 300px;
            background: ${type === 'error' ? '#dc3545' : '#28a745'};
            color: white;
        `;

        document.body.appendChild(messageEl);

        setTimeout(() => {
            messageEl.remove();
        }, 3000);
    }

    function closeOverlay() {
        const overlay = document.getElementById('keyword-overlay');
        if (overlay) {
            overlay.remove();
        }
    }

    // Create and add the button
    function createButton() {
        // Remove existing button if any
        const existingButton = document.getElementById('hide-keywords-btn');
        if (existingButton) {
            existingButton.remove();
        }

        // Find the logo element
        const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
        if (!logoElement) {
            console.log('Logo element not found, will retry...');
            return;
        }

        const button = document.createElement('button');
        button.id = 'hide-keywords-btn';
        button.innerHTML = '🚫 Keywords';
        button.style.cssText = `
            margin-left: 12px;
            padding: 8px 12px;
            background: #ff6600;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-size: 12px;
            box-shadow: 0 2px 8px rgba(255, 102, 0, 0.3);
            transition: all 0.2s ease;
            white-space: nowrap;
            flex-shrink: 0;
        `;

        button.addEventListener('mouseenter', () => {
            button.style.transform = 'translateY(-1px)';
            button.style.boxShadow = '0 4px 12px rgba(255, 102, 0, 0.4)';
        });

        button.addEventListener('mouseleave', () => {
            button.style.transform = 'translateY(0)';
            button.style.boxShadow = '0 2px 8px rgba(255, 102, 0, 0.3)';
        });

        button.addEventListener('click', createOverlay);

        // Insert the button next to the logo
        const logoParent = logoElement.parentElement;
        if (logoParent) {
            logoParent.insertAdjacentElement('afterend', button);
        } else {
            // Fallback to appending after the logo
            logoElement.insertAdjacentElement('afterend', button);
        }
    }

    // Initialize
    function init() {
        loadKeywords();

        // Try to create button, with retries if logo not found
        let buttonRetries = 0;
        const maxButtonRetries = 10;

        function tryCreateButton() {
            const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
            if (logoElement) {
                createButton();
            } else if (buttonRetries < maxButtonRetries) {
                buttonRetries++;
                setTimeout(tryCreateButton, 500);
            } else {
                console.log('Could not find logo element after retries, button not added');
            }
        }

        tryCreateButton();

        // Initial processing
        setTimeout(processMods, 1000);

        // Watch for new content with debouncing
        let processTimeout;
        const debouncedProcess = () => {
            clearTimeout(processTimeout);
            processTimeout = setTimeout(processMods, 500);
        };

        const observer = new MutationObserver((mutations) => {
            debouncedProcess();

            // Check if we need to recreate the button (in case header was re-rendered)
            if (!document.getElementById('hide-keywords-btn')) {
                const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
                if (logoElement) {
                    createButton();
                }
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // Also process on scroll (for infinite scroll)
        let scrollTimeout;
        window.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(processMods, 300);
        });

        console.log(`Nexus Mods Hide Keywords loaded! Current keywords: ${keywords.length}`);
    }

    // Wait for page to load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
I put the keywords in this part and it doesn't seem to work :broken:

Image
it adds a new button
Selection_039.webp
You do not have the required permissions to view the files attached to this post.
Thank you for your attention to this matter!
Steam friend code: 40552640 https://steamcommunity.com/friends/add | email: [email protected]
Having trouble running an old Windows game?
Rusty's Stuff Collection
User avatar
Oyster Sauce
Site Moderator
Posts: 11294
Joined: Jun 2, '23

Geolocation

Adventurer's Guild

Post by Oyster Sauce »

rusty_shackleford wrote: June 29th, 2025, 05:12
Oyster Sauce wrote: June 29th, 2025, 04:03
rusty_shackleford wrote: June 29th, 2025, 03:37
Slaved on this for 12 hours straight to help my friend :turtle:

Code: Select all

// ==UserScript==
// @name         Nexus Mods Hide Keywords
// @namespace    nexusmods-hide-keywords
// @version      1.0
// @description  Hide mods based on keywords in their names
// @author       Rusty
// @match        https://www.nexusmods.com/*
// @match        https://next.nexusmods.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let keywords = [];
    const STORAGE_KEY = 'nexusmods-hide-keywords';

    // Load keywords from localStorage
    function loadKeywords() {
        const stored = localStorage.getItem(STORAGE_KEY);
        keywords = stored ? JSON.parse(stored) : [];
    }

    // Save keywords to localStorage
    function saveKeywords() {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(keywords));
    }

    // Check if a title matches any keyword (case insensitive)
    function titleMatchesKeyword(title) {
        if (!title || keywords.length === 0) return false;
        return keywords.some(keyword =>
            title.toLowerCase().includes(keyword.toLowerCase())
        );
    }

    // Hide/show mods based on keywords
    function processMods() {
        // Look for mod tiles with the specific data attribute
        const modTiles = document.querySelectorAll('[data-e2eid="mod-tile"]');

        modTiles.forEach(tile => {
            const titleElement = tile.querySelector('[data-e2eid="mod-tile-title"]');
            if (titleElement) {
                const title = titleElement.textContent.trim();
                if (titleMatchesKeyword(title)) {
                    tile.style.display = 'none';
                    tile.setAttribute('data-hidden-by-keywords', 'true');
                } else {
                    tile.style.display = '';
                    tile.removeAttribute('data-hidden-by-keywords');
                }
            }
        });

        // Also check for alternative mod card structures
        const alternativeSelectors = [
            'a[href*="/mods/"]',
            '[class*="mod"]',
            '[class*="tile"]'
        ];

        alternativeSelectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(element => {
                // Skip if already processed
                if (element.hasAttribute('data-hidden-by-keywords') ||
                    element.querySelector('[data-e2eid="mod-tile-title"]')) {
                    return;
                }

                // Look for title text in various ways
                let title = '';
                const possibleTitleElements = element.querySelectorAll('h1, h2, h3, h4, h5, h6, [class*="title"], [class*="name"]');

                if (possibleTitleElements.length > 0) {
                    title = possibleTitleElements[0].textContent.trim();
                } else {
                    // Fallback to alt text or aria-label
                    const img = element.querySelector('img[alt]');
                    if (img) title = img.alt;
                }

                if (title && titleMatchesKeyword(title)) {
                    element.style.display = 'none';
                    element.setAttribute('data-hidden-by-keywords', 'true');
                } else if (title) {
                    element.style.display = '';
                    element.removeAttribute('data-hidden-by-keywords');
                }
            });
        });
    }

    // Create the overlay
    function createOverlay() {
        // Remove existing overlay if any
        const existingOverlay = document.getElementById('keyword-overlay');
        if (existingOverlay) {
            existingOverlay.remove();
        }

        const overlay = document.createElement('div');
        overlay.id = 'keyword-overlay';
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 10000;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        `;

        const modal = document.createElement('div');
        modal.style.cssText = `
            background: #1a1a1a;
            color: #ffffff;
            padding: 25px;
            border-radius: 12px;
            max-width: 600px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
            border: 1px solid #333;
        `;

        modal.innerHTML = `
            <h2 style="margin-top: 0; color: #ff6600; font-size: 24px;">🚫 Hide Keywords</h2>
            <p style="color: #ccc; margin-bottom: 20px;">Add keywords to hide mods with matching names (case insensitive):</p>
            <div style="margin-bottom: 20px; display: flex; gap: 10px;">
                <input type="text" id="keyword-input" placeholder="Enter keyword..." style="
                    flex: 1;
                    padding: 10px;
                    border: 1px solid #444;
                    border-radius: 6px;
                    background: #2a2a2a;
                    color: #fff;
                    font-size: 14px;
                ">
                <button id="add-keyword" style="
                    padding: 10px 20px;
                    background: #ff6600;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                    font-weight: bold;
                ">Add</button>
            </div>
            <div id="keyword-list" style="margin-bottom: 20px; max-height: 300px; overflow-y: auto;"></div>
            <div style="text-align: right; border-top: 1px solid #333; padding-top: 15px;">
                <button id="close-overlay" style="
                    padding: 10px 20px;
                    background: #666;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                ">Close</button>
            </div>
        `;

        overlay.appendChild(modal);
        document.body.appendChild(overlay);

        // Event listeners
        document.getElementById('add-keyword').addEventListener('click', addKeyword);
        document.getElementById('keyword-input').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') addKeyword();
        });
        document.getElementById('close-overlay').addEventListener('click', closeOverlay);
        overlay.addEventListener('click', function(e) {
            if (e.target === overlay) closeOverlay();
        });

        updateKeywordList();
        document.getElementById('keyword-input').focus();
    }

    function addKeyword() {
        const input = document.getElementById('keyword-input');
        const keyword = input.value.trim();

        if (keyword && !keywords.some(k => k.toLowerCase() === keyword.toLowerCase())) {
            keywords.push(keyword);
            saveKeywords();
            updateKeywordList();
            processMods();
            input.value = '';
            showMessage(`Added keyword: "${keyword}"`);
        } else if (keyword) {
            showMessage(`Keyword "${keyword}" already exists!`, 'error');
        }
    }

    function removeKeyword(keyword) {
        const index = keywords.findIndex(k => k === keyword);
        if (index > -1) {
            keywords.splice(index, 1);
            saveKeywords();
            updateKeywordList();
            processMods();
            showMessage(`Removed keyword: "${keyword}"`);
        }
    }

    function updateKeywordList() {
        const list = document.getElementById('keyword-list');
        if (!list) return;

        list.innerHTML = '';

        if (keywords.length === 0) {
            list.innerHTML = '<p style="color: #888; text-align: center; font-style: italic;">No keywords added yet</p>';
            return;
        }

        keywords.forEach(keyword => {
            const item = document.createElement('div');
            item.style.cssText = `
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 10px;
                border: 1px solid #444;
                margin: 5px 0;
                border-radius: 6px;
                background: #2a2a2a;
            `;

            item.innerHTML = `
                <span style="color: #fff;">${escapeHtml(keyword)}</span>
                <button data-keyword="${escapeHtml(keyword)}" style="
                    padding: 5px 10px;
                    background: #dc3545;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                ">Remove</button>
            `;

            const removeBtn = item.querySelector('button');
            removeBtn.addEventListener('click', () => removeKeyword(keyword));

            list.appendChild(item);
        });
    }

    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    function showMessage(message, type = 'success') {
        const messageEl = document.createElement('div');
        messageEl.textContent = message;
        messageEl.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 10px 15px;
            border-radius: 6px;
            z-index: 10001;
            font-weight: bold;
            max-width: 300px;
            background: ${type === 'error' ? '#dc3545' : '#28a745'};
            color: white;
        `;

        document.body.appendChild(messageEl);

        setTimeout(() => {
            messageEl.remove();
        }, 3000);
    }

    function closeOverlay() {
        const overlay = document.getElementById('keyword-overlay');
        if (overlay) {
            overlay.remove();
        }
    }

    // Create and add the button
    function createButton() {
        // Remove existing button if any
        const existingButton = document.getElementById('hide-keywords-btn');
        if (existingButton) {
            existingButton.remove();
        }

        // Find the logo element
        const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
        if (!logoElement) {
            console.log('Logo element not found, will retry...');
            return;
        }

        const button = document.createElement('button');
        button.id = 'hide-keywords-btn';
        button.innerHTML = '🚫 Keywords';
        button.style.cssText = `
            margin-left: 12px;
            padding: 8px 12px;
            background: #ff6600;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-size: 12px;
            box-shadow: 0 2px 8px rgba(255, 102, 0, 0.3);
            transition: all 0.2s ease;
            white-space: nowrap;
            flex-shrink: 0;
        `;

        button.addEventListener('mouseenter', () => {
            button.style.transform = 'translateY(-1px)';
            button.style.boxShadow = '0 4px 12px rgba(255, 102, 0, 0.4)';
        });

        button.addEventListener('mouseleave', () => {
            button.style.transform = 'translateY(0)';
            button.style.boxShadow = '0 2px 8px rgba(255, 102, 0, 0.3)';
        });

        button.addEventListener('click', createOverlay);

        // Insert the button next to the logo
        const logoParent = logoElement.parentElement;
        if (logoParent) {
            logoParent.insertAdjacentElement('afterend', button);
        } else {
            // Fallback to appending after the logo
            logoElement.insertAdjacentElement('afterend', button);
        }
    }

    // Initialize
    function init() {
        loadKeywords();

        // Try to create button, with retries if logo not found
        let buttonRetries = 0;
        const maxButtonRetries = 10;

        function tryCreateButton() {
            const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
            if (logoElement) {
                createButton();
            } else if (buttonRetries < maxButtonRetries) {
                buttonRetries++;
                setTimeout(tryCreateButton, 500);
            } else {
                console.log('Could not find logo element after retries, button not added');
            }
        }

        tryCreateButton();

        // Initial processing
        setTimeout(processMods, 1000);

        // Watch for new content with debouncing
        let processTimeout;
        const debouncedProcess = () => {
            clearTimeout(processTimeout);
            processTimeout = setTimeout(processMods, 500);
        };

        const observer = new MutationObserver((mutations) => {
            debouncedProcess();

            // Check if we need to recreate the button (in case header was re-rendered)
            if (!document.getElementById('hide-keywords-btn')) {
                const logoElement = document.querySelector('[data-e2eid="nexus-logo"]');
                if (logoElement) {
                    createButton();
                }
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // Also process on scroll (for infinite scroll)
        let scrollTimeout;
        window.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(processMods, 300);
        });

        console.log(`Nexus Mods Hide Keywords loaded! Current keywords: ${keywords.length}`);
    }

    // Wait for page to load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
I put the keywords in this part and it doesn't seem to work :broken:

Image
it adds a new button
Image
Ohhh. It works! Thank you.
User avatar
Oyster Sauce
Site Moderator
Posts: 11294
Joined: Jun 2, '23

Geolocation

Adventurer's Guild

Post by Oyster Sauce »

Works gr8, I recommend making a proper thread for it so others can find and use it @rusty_shackleford
► few minutes of browsing
User avatar
Stack of Turtles
Posts: 7041
Joined: May 7, '24
Location: Soon-to-be Iran

Geolocation

Post by Stack of Turtles »

Oyster Sauce wrote: June 29th, 2025, 17:15
Works gr8, I recommend making a proper thread for it so others can find and use it @rusty_shackleford
► few minutes of browsing
Thanks for the tip, but you posted your list of favorited keywords by mistake.
VAE VICTIS
User avatar
Oyster Sauce
Site Moderator
Posts: 11294
Joined: Jun 2, '23

Geolocation

Adventurer's Guild

Post by Oyster Sauce »

This has wound up just being every female Japanese name ever made but I like it a lot :)
User avatar
Tweed
Turtle
Turtle
Posts: 6837
Joined: Feb 2, '23

Geolocation

Adventurer's Guild

Post by Tweed »

Oyster Sauce wrote: June 29th, 2025, 17:15
Works gr8, I recommend making a proper thread for it so others can find and use it @rusty_shackleford
► few minutes of browsing
Gaming sure is healing.