Yiffspot Chat Helper

Auto message, auto disconnect/reconnect, and blacklist for Yiffspot with enhanced UI and debugging

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Yiffspot Chat Helper
// @namespace    http://tampermonkey.net/
// @version      3.1
// @license      MIT
// @description  Auto message, auto disconnect/reconnect, and blacklist for Yiffspot with enhanced UI and debugging
// @match        https://www.Yiffspot.com*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=https://www.Yiffspot.com/
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // Debug mode (set to true to enable debug window)
    const DEBUG_MODE = false; // Change to true to enable debugging

    // Variables for the bot settings
    let settings = {
        autoMessage: GM_getValue('autoMessage', ''),
        disconnectTimer: GM_getValue('disconnectTimer', 0),
        autoReconnect: GM_getValue('autoReconnect', 0),
        blacklist: GM_getValue('blacklist', [])
    };

    let isChatConnected = false;   // Tracks if a chat is currently active
    let messageSent = false;       // Tracks if the bot has sent its auto-message
    let disconnectTimeout = null;  // Timer for auto-disconnect
    let reconnectTimeout = null;   // Timer for auto-reconnect
    let isPaused = false;          // Pauses all timers/actions
    let blacklistTriggered = false; // Tracks if a blacklist term was triggered
    let blacklistTerm = '';        // Stores the triggered blacklist term
    let disconnectTimeRemaining = 0;
    let reconnectTimeRemaining = 0;
    let blacklistTimeout = null;   // Timer for blacklist-triggered disconnect
    let blacklistTimeRemaining = 0;

    // Variable to track the active timer controlling the UI
    let currentActiveTimer = null;

    // Store the previous number of child elements in #messages
    let previousChildCount = 0;

    // Function to save settings using GM_setValue
    function saveSettings() {
        GM_setValue('autoMessage', settings.autoMessage);
        GM_setValue('disconnectTimer', settings.disconnectTimer);
        GM_setValue('autoReconnect', settings.autoReconnect);
        GM_setValue('blacklist', settings.blacklist);
    }

    // Function to process a node and its children
    function processNodeAndChildren(node) {
        if (node.nodeType === Node.ELEMENT_NODE) {
            checkBlacklistOnElement(node);

            // Recursively process child nodes
            node.querySelectorAll('*').forEach(childNode => {
                if (childNode.nodeType === Node.ELEMENT_NODE) {
                    checkBlacklistOnElement(childNode);
                }
            });
        } else if (node.nodeType === Node.TEXT_NODE) {
            checkBlacklistOnText(node.textContent);
        }
    }

    // Function to check for blacklist terms in an element's text content
    function checkBlacklistOnElement(element) {
        const textContent = element.textContent || '';
        checkBlacklistOnText(textContent);
    }

    // Function to check for blacklist terms in text
    function checkBlacklistOnText(text) {
        if (settings.blacklist.length === 0 || blacklistTriggered) return;

        const lowerCaseText = text.toLowerCase();
        let termFound = null;
        for (let term of settings.blacklist) {
            if (lowerCaseText.includes(term.toLowerCase())) {
                // Blacklist term found
                termFound = term;
                break;
            }
        }

        if (termFound) {
            if (DEBUG_MODE) console.log(`Blacklist term "${termFound}" found in text: "${text.trim()}"`);
            // Start the blacklist disconnect timer
            blacklistTriggered = true;
            blacklistTerm = termFound;
            startBlacklistDisconnectTimer();
            if (DEBUG_MODE) updateDebugInfo();
        } else {
            if (DEBUG_MODE) console.log(`No blacklist terms found in text: "${text.trim()}"`);
        }
    }

    // Function to start the blacklist disconnect timer
    function startBlacklistDisconnectTimer() {
        clearInterval(blacklistTimeout);
        if (!isPaused) {
            blacklistTimeRemaining = 5;  // Hardcoded 5-second timer
            currentActiveTimer = 'blacklist';  // Set blacklist as the active timer
            updateCountdownDisplay(blacklistTimeRemaining, 'Disconnecting in', '#dc3545');  // Red color
            blacklistTimeout = setInterval(() => {
                if (!isPaused) {
                    blacklistTimeRemaining--;
                    if (currentActiveTimer === 'blacklist') {
                        updateCountdownDisplay(blacklistTimeRemaining, 'Disconnecting in', '#dc3545');
                    }
                    if (blacklistTimeRemaining <= 0) {
                        clearInterval(blacklistTimeout);
                        handleDisconnect();
                    }
                }
            }, 1000);
        }
    }

    // Function to cancel the blacklist disconnect
    function cancelBlacklistDisconnect() {
        if (blacklistTriggered) {
            clearInterval(blacklistTimeout);
            blacklistTriggered = false;
            blacklistTerm = '';
            blacklistTimeRemaining = 0;
            hideCountdownDisplay();
            currentActiveTimer = null;  // Clear active timer
            if (isChatConnected) {
                startDisconnectTimer();  // Resume normal disconnect timer
            }
            if (DEBUG_MODE) updateDebugInfo();
        }
    }

    // Function to manually monitor changes in the #messages element
    function monitorMessagesManually() {
        const messagesElement = document.querySelector('#messages');  // Assuming the chat messages container ID is '#messages'
        if (!messagesElement) {
            // Retry after a short delay if the element is not found
            setTimeout(monitorMessagesManually, 1000);
            return;
        }

        // Check if the child count has changed
        const currentChildCount = messagesElement.childElementCount;

        // Subtract 1 for the "partner is typing..." element if it's present
        const adjustedChildCount = currentChildCount > 0 ? currentChildCount - 1 : 0;

        if (adjustedChildCount > previousChildCount) {
            // Process only the new elements (up to the second to last element, excluding the "partner is typing...")
            for (let i = previousChildCount; i < adjustedChildCount; i++) {
                const newChild = messagesElement.children[i];
                processNodeAndChildren(newChild);
            }

            // Reset the disconnect timer on new message
            if (isChatConnected) {
                startDisconnectTimer();  // Reset the disconnect timer
            }
        }

        // Update the previous child count
        previousChildCount = adjustedChildCount;

        // Re-check periodically (you can adjust the interval as needed)
        setTimeout(monitorMessagesManually, 1000);  // Check every second
    }

    // Function to handle disconnect
    function handleDisconnect() {
        clearInterval(disconnectTimeout);
        clearInterval(blacklistTimeout);
        hideCountdownDisplay();

        const disconnectButton = document.querySelector('#disconnect');
        if (disconnectButton) {
            disconnectButton.click();
            isChatConnected = false;
            messageSent = false;
            currentActiveTimer = null;  // Clear active timer
            if (DEBUG_MODE) console.log('Disconnected from chat');
            startAutoReconnectTimer();
        }
    }

    // Function to start the disconnect timer
    function startDisconnectTimer() {
        clearInterval(disconnectTimeout);
        if (currentActiveTimer !== 'blacklist') {  // Only start if blacklist is not active
            clearInterval(reconnectTimeout); // Pause reconnect timer
            clearInterval(blacklistTimeout);  // Pause blacklist timer
            if (settings.disconnectTimer > 0 && !isPaused && isChatConnected && !blacklistTriggered) {
                disconnectTimeRemaining = settings.disconnectTimer;
                currentActiveTimer = 'disconnect';  // Set disconnect as the active timer
                updateCountdownDisplay(disconnectTimeRemaining, 'Disconnect in', '#28a745');
                disconnectTimeout = setInterval(() => {
                    if (!isPaused && currentActiveTimer === 'disconnect') {
                        disconnectTimeRemaining--;
                        updateCountdownDisplay(disconnectTimeRemaining, 'Disconnect in', '#28a745');
                        if (disconnectTimeRemaining <= 0) {
                            clearInterval(disconnectTimeout);
                            handleDisconnect();
                        }
                    }
                }, 1000);
            }
        }
    }

    // Function to start auto reconnect timer
    function startAutoReconnectTimer() {
        clearInterval(reconnectTimeout);
        if (currentActiveTimer !== 'blacklist') {  // Only start if blacklist is not active
            if (settings.autoReconnect > 0 && !isPaused) {
                reconnectTimeRemaining = settings.autoReconnect;
                currentActiveTimer = 'reconnect';  // Set reconnect as the active timer
                updateCountdownDisplay(reconnectTimeRemaining, 'Reconnecting in', '#28a745');
                reconnectTimeout = setInterval(() => {
                    if (!isPaused && currentActiveTimer === 'reconnect') {
                        reconnectTimeRemaining--;
                        updateCountdownDisplay(reconnectTimeRemaining, 'Reconnecting in', '#28a745');
                        if (reconnectTimeRemaining <= 0) {
                            clearInterval(reconnectTimeout);
                            attemptReconnect();
                        }
                    }
                }, 1000);
            }
        }
    }

    // Function to attempt reconnect
    function attemptReconnect() {
        clearInterval(reconnectTimeout);
        hideCountdownDisplay();
        currentActiveTimer = null;  // Clear active timer

        const findPartnerButton = document.querySelector('#find-partner');
        if (findPartnerButton) {
            findPartnerButton.click();
            if (DEBUG_MODE) console.log('Attempting to reconnect...');
        }
    }

    // Function to observe chat status
    function observeChatStatus() {
        function setupObserver() {
            const disconnectRow = document.querySelector('#disconnect-row');
            if (disconnectRow) {
                // Set up a MutationObserver on the 'class' attribute
                const observer = new MutationObserver((mutationsList) => {
                    mutationsList.forEach((mutation) => {
                        if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
                            const isHidden = disconnectRow.classList.contains('hide-ele');

                            if (!isHidden && !isChatConnected) {
                                // Chat connected
                                isChatConnected = true;
                                messageSent = false;
                                blacklistTriggered = false;
                                blacklistTerm = '';
                                if (DEBUG_MODE) console.log('Chat connected');
                                sendAutoMessage();
                                startDisconnectTimer();
                                if (DEBUG_MODE) updateDebugInfo();
                            } else if (isHidden && isChatConnected) {
                                // Chat disconnected
                                isChatConnected = false;
                                messageSent = false;
                                clearInterval(disconnectTimeout);
                                clearInterval(reconnectTimeout);
                                hideCountdownDisplay();
                                if (DEBUG_MODE) console.log('Chat disconnected');
                                startAutoReconnectTimer();
                                if (DEBUG_MODE) updateDebugInfo();
                            }
                        }
                    });
                });

                // Start observing 'disconnectRow'
                observer.observe(disconnectRow, { attributes: true, attributeFilter: ['class'] });

                // Check the initial state
                const isHidden = disconnectRow.classList.contains('hide-ele');
                if (!isHidden && !isChatConnected) {
                    isChatConnected = true;
                    messageSent = false;
                    blacklistTriggered = false;
                    blacklistTerm = '';
                    if (DEBUG_MODE) console.log('Chat connected (initial)');
                    sendAutoMessage();
                    startDisconnectTimer();
                    if (DEBUG_MODE) updateDebugInfo();
                }
            } else {
                // Wait until 'disconnectRow' becomes available
                setTimeout(setupObserver, 1000);
            }
        }

        setupObserver();
    }

    // Function to send the auto message
    function sendAutoMessage() {
        if (settings.autoMessage.trim() === '' || messageSent || !isChatConnected) return;

        const messageBox = document.querySelector('#message');
        if (messageBox) {
            messageBox.value = settings.autoMessage;
            messageSent = true;

            simulateEnterKey(messageBox);
            if (DEBUG_MODE) console.log('Auto message sent:', settings.autoMessage);
        }
    }

    // Helper function to fully simulate the Enter keypress
    function simulateEnterKey(inputElement) {
        const keydownEvent = new KeyboardEvent('keydown', {
            bubbles: true,
            cancelable: true,
            key: 'Enter',
            code: 'Enter',
            keyCode: 13,
            which: 13
        });

        const keypressEvent = new KeyboardEvent('keypress', {
            bubbles: true,
            cancelable: true,
            key: 'Enter',
            code: 'Enter',
            keyCode: 13,
            which: 13
        });

        const keyupEvent = new KeyboardEvent('keyup', {
            bubbles: true,
            cancelable: true,
            key: 'Enter',
            code: 'Enter',
            keyCode: 13,
            which: 13
        });

        inputElement.dispatchEvent(keydownEvent);
        inputElement.dispatchEvent(keypressEvent);
        inputElement.dispatchEvent(keyupEvent);
    }

    // Function to create the control menu, countdown display, and debug window
    function createUI() {
        // Add custom styles
        GM_addStyle(`
            #chatHelperMenu {
                position: fixed;
                top: 60px;
                left: 10px;
                z-index: 10000;
                background-color: rgba(30, 30, 30, 0.95);
                border: 1px solid #555;
                border-radius: 8px;
                padding: 15px;
                box-shadow: 0 5px 15px rgba(0,0,0,0.5);
                display: none;
                max-width: 300px;
                font-family: Arial, sans-serif;
                font-size: 14px;
                color: #ddd;
            }
            #chatHelperMenu h2 {
                margin-top: 0;
                font-size: 18px;
                color: #fff;
            }
            #chatHelperMenu label {
                display: block;
                margin-top: 10px;
                color: #ccc;
            }
            #chatHelperMenu input, #chatHelperMenu textarea {
                width: 100%;
                margin-top: 5px;
                padding: 5px;
                box-sizing: border-box;
                font-size: 14px;
                background-color: #444;
                color: #eee;
                border: 1px solid #666;
            }
            #chatHelperMenu button {
                padding: 8px 12px;
                margin-top: 15px;
                font-size: 14px;
                cursor: pointer;
                background-color: #007BFF;
                color: white;
                border: none;
                border-radius: 5px;
            }
            #chatHelperMenuClose {
                position: absolute;
                top: 5px;
                right: 5px;
                cursor: pointer;
                font-size: 16px;
                color: #fff;
            }
            #chatHelperButton {
                position: fixed;
                top: 10px;
                left: 10px;
                z-index: 10000;
                padding: 10px;
                font-size: 20px;
                background-color: #007BFF;
                color: white;
                border: none;
                border-radius: 5px;
                cursor: pointer;
            }
            #chatHelperCountdown {
                position: fixed;
                top: 15px;
                left: 70px;
                z-index: 10000;
                padding: 8px 12px;
                font-size: 14px;
                background-color: #28a745;
                color: white;
                border-radius: 5px;
                cursor: pointer;
                display: none;
            }
            #chatHelperDebug {
                position: fixed;
                bottom: 10px;
                left: 10px;
                z-index: 10000;
                background-color: rgba(0, 0, 0, 0.8);
                color: #fff;
                padding: 10px;
                border-radius: 5px;
                font-family: monospace;
                font-size: 12px;
                display: none;
                max-width: 300px;
                word-wrap: break-word;
                white-space: pre-line;
            }
        `);

        // Create menu container
        const menuContainer = document.createElement('div');
        menuContainer.id = 'chatHelperMenu';

        // Create menu content
        menuContainer.innerHTML = `
            <span id="chatHelperMenuClose">&times;</span>
            <h2>Chat Helper Settings</h2>
            <label for="autoMessage">Auto Message:</label>
            <textarea id="autoMessage" rows="3"></textarea>
            <label for="disconnectTimer">Disconnect Timer (sec):</label>
            <input type="number" id="disconnectTimer" min="0">
            <label for="autoReconnect">Auto Reconnect Timer (sec):</label>
            <input type="number" id="autoReconnect" min="0">
            <label for="blacklist">Blacklist (comma-separated, quote-wrapped):</label>
            <textarea id="blacklist" rows="3" placeholder='"term1", "term2", "term3"'></textarea>
            <button id="saveSettings">Save</button>
        `;

        document.body.appendChild(menuContainer);

        // Create menu toggle button
        const menuButton = document.createElement('button');
        menuButton.id = 'chatHelperButton';
        menuButton.textContent = '⚙️';
        document.body.appendChild(menuButton);

        // Create countdown display
        const countdownDisplay = document.createElement('div');
        countdownDisplay.id = 'chatHelperCountdown';
        countdownDisplay.textContent = '';
        document.body.appendChild(countdownDisplay);

        // Create debug window if DEBUG_MODE is true
        if (DEBUG_MODE) {
            const debugWindow = document.createElement('div');
            debugWindow.id = 'chatHelperDebug';
            document.body.appendChild(debugWindow);
        }

        // Load settings into UI
        document.getElementById('autoMessage').value = settings.autoMessage;
        document.getElementById('disconnectTimer').value = settings.disconnectTimer;
        document.getElementById('autoReconnect').value = settings.autoReconnect;
        document.getElementById('blacklist').value = settings.blacklist.map(term => `"${term}"`).join(', ');

        // Menu button click event (toggle)
        menuButton.addEventListener('click', () => {
            if (menuContainer.style.display === 'none' || menuContainer.style.display === '') {
                menuContainer.style.display = 'block';
            } else {
                menuContainer.style.display = 'none';
            }
        });

        // Close menu button click event
        document.getElementById('chatHelperMenuClose').addEventListener('click', () => {
            menuContainer.style.display = 'none';
        });

        // Save settings button click event
        document.getElementById('saveSettings').addEventListener('click', () => {
            settings.autoMessage = document.getElementById('autoMessage').value.trim();
            settings.disconnectTimer = parseInt(document.getElementById('disconnectTimer').value, 10) || 0;
            settings.autoReconnect = parseInt(document.getElementById('autoReconnect').value, 10) || 0;

            const blacklistInput = document.getElementById('blacklist').value.trim();
            settings.blacklist = parseBlacklistInput(blacklistInput);

            saveSettings();
            menuContainer.style.display = 'none';

            // Apply settings immediately
            if (isChatConnected) {
                startDisconnectTimer();
                // Other settings will naturally take effect
            }
            if (DEBUG_MODE) updateDebugInfo();
        });

        // Countdown display click event (toggle pause or cancel blacklist disconnect)
        countdownDisplay.addEventListener('click', () => {
            if (currentActiveTimer === 'blacklist') {
                cancelBlacklistDisconnect();
            } else {
                // Toggle pause
                isPaused = !isPaused;
                if (isPaused) {
                    clearInterval(disconnectTimeout);
                    clearInterval(reconnectTimeout);
                    clearInterval(blacklistTimeout);
                    countdownDisplay.style.backgroundColor = '#ffc107'; // Yellow for paused
                    countdownDisplay.textContent = 'Paused';  // Show "Paused" text
                } else {
                    countdownDisplay.style.backgroundColor = '#28a745'; // Green for active
                    if (blacklistTriggered) {
                        startBlacklistDisconnectTimer();
                    } else if (isChatConnected) {
                        startDisconnectTimer();
                    } else {
                        startAutoReconnectTimer();
                    }
                }
                if (DEBUG_MODE) updateDebugInfo();
            }
        });

        // Store references for later use
        uiElements = {
            menuContainer,
            menuButton,
            countdownDisplay,
            debugWindow: DEBUG_MODE ? document.getElementById('chatHelperDebug') : null
        };
    }

    // Function to parse the blacklist input
    function parseBlacklistInput(input) {
        const regex = /"([^"]+)"/g;
        let terms = [];
        let match;
        while ((match = regex.exec(input)) !== null) {
            terms.push(match[1]);
        }
        return terms;
    }

    // UI elements reference
    let uiElements = {};

    // Function to update countdown display
    function updateCountdownDisplay(timeRemaining, text, color) {
        const display = uiElements.countdownDisplay;
        if (display) {
            display.textContent = `${text} ${timeRemaining}s`;
            display.style.display = 'block';
            display.style.backgroundColor = color;
        }
        if (DEBUG_MODE) updateDebugInfo();
    }

    // Function to hide countdown display
    function hideCountdownDisplay() {
        const display = uiElements.countdownDisplay;
        if (display) {
            display.style.display = 'none';
        }
    }

    // Function to update debug information
    function updateDebugInfo() {
        if (DEBUG_MODE && uiElements.debugWindow) {
            let debugText = `Chat Helper Debug Info:\n`;
            debugText += `Chat Connected: ${isChatConnected}\n`;
            debugText += `Paused: ${isPaused}\n`;
            debugText += `Blacklist Triggered: ${blacklistTriggered}\n`;
            if (blacklistTriggered) {
                debugText += `Blacklist Term: "${blacklistTerm}"\n`;
                debugText += `Blacklist Timer: ${blacklistTimeRemaining}s\n`;
            }
            if (!blacklistTriggered && isChatConnected) {
                debugText += `Disconnect Timer: ${disconnectTimeRemaining}s\n`;
            }
            if (!isChatConnected) {
                debugText += `Reconnect Timer: ${reconnectTimeRemaining}s\n`;
            }
            uiElements.debugWindow.textContent = debugText;
            uiElements.debugWindow.style.display = 'block';
        }
    }

    // Initialize the script
    function init() {
        createUI();

        // Start manually monitoring messages
        monitorMessagesManually();

        // Start observing chat status
        observeChatStatus();
    }

    // Run the initialization function
    init();
})();