Sniffies Negative Profile Filter

Add a popup with filters for profiles, including age range and dropdown menus, and remembers user choices.

// ==UserScript==
// @name         Sniffies Negative Profile Filter
// @version      1.5
// @description  Add a popup with filters for profiles, including age range and dropdown menus, and remembers user choices.
// @author       LiveCamShow
// @match        *://sniffies.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// @homepageURL  https://gitlab.com/livecamshow/UserScripts
// @namespace    LiveCamShow.scripts
// @license MIT
// ==/UserScript==
 
(function () {
    'use strict';
 
    // Wait for the settings button to load
    const waitForSettingsButton = () => {
        const settingsButton = document.querySelector('[data-testid="settingsButton"]');
        if (settingsButton) {
            initializeFilterButton(settingsButton);
        } else {
            setTimeout(waitForSettingsButton, 500);
        }
    };
 
    // Initialize the filter button and popup
    const initializeFilterButton = (settingsButton) => {
        const filterButtonDiv = document.createElement('div');
        filterButtonDiv.className = 'nav-inline-icon';
 
        const filterButton = document.createElement('button');
        filterButton.type = 'button';
        filterButton.title = 'Filter Profiles';
        filterButton.innerHTML = '<i class="fa fa-filter"></i>';
        filterButton.style.marginLeft = '10px';
        const navbar = settingsButton.parentElement.parentElement;
        filterButtonDiv.appendChild(filterButton);
        navbar.appendChild(filterButtonDiv);
        navbar.style.gridTemplateColumns = 'auto auto auto auto auto';
 
        const popup = createPopup();
        document.body.appendChild(popup);
 
        filterButton.addEventListener('click', () => {
            popup.style.display = popup.style.display === 'none' ? 'block' : 'none';
        });
 
        restoreSettings();
        applyFilters(); // Apply filters on load
    };
 
    const createPopup = () => {
        const popup = document.createElement('div');
        popup.id = 'filter-popup';
        popup.style = `
            position: fixed; top: 10%; right: 5%; padding: 15px; background-color: #ffffff;
            border: 1px solid #ddd; border-radius: 8px; z-index: 10000;
            box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.2); max-width: 320px;
            max-height: 75vh; overflow-y: auto; font-family: "Arial", sans-serif; color: #333;
            display: none;
        `;
 
        const title = document.createElement('h3');
        title.innerText = 'Remove Profiles That Contain';
        title.style.marginBottom = '10px';
        popup.appendChild(title);
 
        const categories = {
            Position: ["dom top (breeder)", "passive top", "top", "verse top", "verse", "verse bottom", "bottom", "power bottom", "submissive bottom", "side"],
            "Body Type": ["muscular", "fit", "slim", "average", "stocky", "chubby", "large"],
            Sexuality: ["straight", "straight-curious", "bicurious", "bi", "gay"],
            Expression: ["bear", "biker", "bro", "clean cut", "corporate", "daddy", "discreet", "femme", "gaymer", "geek", "goth", "guy next door", "jock", "leather", "nudist", "otter", "poz", "punk", "pup", "rugged", "skater", "son", "sporty", "surfer", "swinger", "trans", "trendy", "trucker", "twink", "u+"]
        };
 
        const checkboxes = {};
 
        for (const [category, options] of Object.entries(categories)) {
            const categoryContainer = document.createElement('div');
            categoryContainer.style.marginBottom = '10px';
 
            const categoryTitle = document.createElement('button');
            categoryTitle.innerText = category;
            categoryTitle.style = `
                display: block; width: 100%; text-align: left; margin-bottom: 5px;
                padding: 10px; background-color: #f5f5f5; border: 1px solid #ddd;
                border-radius: 6px; cursor: pointer; font-size: 1em; color: #333;
                transition: background-color 0.2s ease-in-out;
            `;
 
            const optionsContainer = document.createElement('div');
            optionsContainer.style = 'display: none; margin-left: 10px;';
 
            categoryTitle.addEventListener('click', () => {
                optionsContainer.style.display = optionsContainer.style.display === 'none' ? 'block' : 'none';
            });
 
            checkboxes[category] = {};
            for (const option of options) {
                const label = document.createElement('label');
                label.style.display = 'block';
 
                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.value = option;
 
                // Restore saved state
                checkbox.checked = GM_getValue(`${category}:${option}`, false);
 
                checkbox.addEventListener('change', () => {
                    if (checkbox.checked) {
                        GM_setValue(`${category}:${option}`, checkbox.checked);
                    } else {
                        GM_deleteValue(`${category}:${option}`)
                    }
                });
 
                checkboxes[category][option] = checkbox;
 
                label.appendChild(checkbox);
                label.appendChild(document.createTextNode(option));
                optionsContainer.appendChild(label);
            }
 
            categoryContainer.appendChild(categoryTitle);
            categoryContainer.appendChild(optionsContainer);
            popup.appendChild(categoryContainer);
        }
        // Has Messages Checkbox
        const hasMessagesContainer = document.createElement('div');
        hasMessagesContainer.style.marginBottom = '10px';
 
        const hasMessagesLabel = document.createElement('label');
        hasMessagesLabel.style.display = 'block';
 
        const hasMessagesCheckbox = document.createElement('input');
        hasMessagesCheckbox.type = 'checkbox';
        hasMessagesCheckbox.checked = GM_getValue('hasMessages', false);
 
        hasMessagesCheckbox.addEventListener('change', () => {
            if (hasMessagesCheckbox.checked) {
                GM_setValue('hasMessages', true);
            } else {
                GM_deleteValue('hasMessages');
            }
        });
 
        hasMessagesLabel.appendChild(hasMessagesCheckbox);
        hasMessagesLabel.appendChild(document.createTextNode('Has Messages'));
        hasMessagesContainer.appendChild(hasMessagesLabel);
 
        popup.appendChild(hasMessagesContainer);
 
        const ageRangeContainer = document.createElement('div');
        ageRangeContainer.style.marginBottom = '10px';
 
        const ageTitle = document.createElement('h4');
        ageTitle.innerText = 'Age Range';
        ageRangeContainer.appendChild(ageTitle);
 
        const minAgeInput = document.createElement('input');
        minAgeInput.type = 'number';
        minAgeInput.placeholder = 'Min Age';
        minAgeInput.style = 'width: 45%; padding: 5px; margin-right: 10px; border: 1px solid #ddd; border-radius: 4px;';
        minAgeInput.value = GM_getValue('minAge', '');
 
 
        const maxAgeInput = document.createElement('input');
        maxAgeInput.type = 'number';
        maxAgeInput.placeholder = 'Max Age';
        maxAgeInput.style = 'width: 45%; padding: 5px; border: 1px solid #ddd; border-radius: 4px;';
        maxAgeInput.value = GM_getValue('maxAge', '');
 
        minAgeInput.addEventListener('change', () => {
            GM_setValue('minAge', minAgeInput.value);
        });
 
        maxAgeInput.addEventListener('change', () => {
            GM_setValue('maxAge', maxAgeInput.value);
        });
 
        ageRangeContainer.appendChild(minAgeInput);
        ageRangeContainer.appendChild(maxAgeInput);
        popup.appendChild(ageRangeContainer);
 
        const buttonContainer = document.createElement('div');
        buttonContainer.style.marginTop = '10px';
 
        const resetButton = createButton('Reset', () => {
            Object.values(checkboxes).forEach(category => {
                Object.values(category).forEach(checkbox => {
                    checkbox.checked = false;
                    GM_deleteValue(`${checkbox.parentNode.parentNode.previousSibling.innerText}:${checkbox.value}`);
                });
            });
            minAgeInput.value = '0';
            maxAgeInput.value = '100';
            GM_setValue('minAge', '0');
            GM_setValue('maxAge', '100');
            applyFilters();
        });
 
        const applyButton = createButton('Apply', () => {
            applyFilters();
            popup.style.display = 'none';
        });
 
        buttonContainer.appendChild(resetButton);
        buttonContainer.appendChild(applyButton);
        popup.appendChild(buttonContainer);
 
        return popup;
    };
 
    const createButton = (text, onClick) => {
        const button = document.createElement('button');
        button.innerText = text;
        button.style = `
            padding: 8px 15px; background-color: #007bff; border: 1px solid #0056b3;
            border-radius: 6px; cursor: pointer; color: #fff; font-size: 0.9em;
        `;
        button.addEventListener('click', onClick);
        return button;
    };
 
    const applyFilters = () => {
        const profiles = document.querySelectorAll('div.marker-container.user > div > div.preview-tag > .title-tag');
        profiles.forEach(profile => {
            let hide = false;
 
            const profileText = profile.innerText.toLowerCase();
            const parentContainer = profile.closest('div.marker-container.user');
 
            // Check "Has Messages" filter
            const hasMessagesEnabled = GM_getValue('hasMessages', false);
            const innerContainer = parentContainer.querySelector('div.inner-container.messages');
            if (hasMessagesEnabled && innerContainer) {
                hide = true;
            }
 
            // Category and Age Range Filters
            const obj = Object.entries(GM_listValues());
            obj.filter(([key, value]) => key && value.includes(':'))
                .forEach(([key, value]) => {
                    const option = value.split(':')[1];
                    if (profileText.includes(option.toLowerCase())) {
                        hide = true;
                    }
                });
 
            const minAge = parseInt(GM_getValue('minAge', ''), 10);
            const maxAge = parseInt(GM_getValue('maxAge', ''), 10);
            const ageMatch = profileText.match(/\b(\d{2})\b/);
            const age = ageMatch ? parseInt(ageMatch[1], 10) : null;
 
            if ((minAge && age < minAge) || (maxAge && age > maxAge)) {
                hide = true;
            }
 
            // Apply visibility based on filters
            parentContainer.style.display = hide ? 'none' : '';
        });
    };
 
    const restoreSettings = () => {
        const allKeys = GM_listValues();
        allKeys.forEach(key => {
            if (key.includes(':')) {
                const [category, option] = key.split(':');
                const checkbox = document.querySelector(`input[value="${option}"]`);
                if (checkbox) checkbox.checked = GM_getValue(key, false);
            }
        });
    };
 
    waitForSettingsButton();
})();