// ==UserScript==
// @name         Auto-Rate Posts with Close Handling
// @namespace    http://tampermonkey.net/
// @version      1.23
// @description  Automatically rate posts in a forum thread and handle already rated posts
// @author       Your Name
// @match        https://141love.net/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let currentIndex = 0; // To keep track of the current index of the rate button
    const maxRetries = 5; // Max retries for score input fields
    const retryDelay = 500; // Delay between retries in milliseconds

    // Set user-specific ratings and logic based on content
    const ratingLogic = {
        "Juven": { score1: 5, score2: 50, score4: 5 },
        "力王ER": { score1: 5, score2: 50, score4: 5 },
        // Add more usernames and their corresponding ratings as needed
    };

    // Default rating if username is not found in the defined logic
    const defaultRating = { score1: 2, score2: 20, score4: 2 };

    // Adjusted updateInputsAndSubmit function to simulate submission without visible clicks
    function updateInputsAndSubmit(button, retries = 0) {
        const postElement = button.closest('.plhin');
        if (!postElement) {
            console.error('Could not find the post element associated with this button.');
            return;
        }

        const score1 = document.querySelector('input#score1.px.z');
        const score2 = document.querySelector('input#score2.px.z');
        const score4 = document.querySelector('input#score4.px.z');

        const usernameElement = postElement.querySelector('div.authi a.xw1');
        const username = usernameElement ? usernameElement.innerText.trim() : '';
        let rating = ratingLogic[username] || defaultRating;

        if (score1 && score2 && score4) {
            score1.value = rating.score1;
            score2.value = rating.score2;
            score4.value = rating.score4;

            console.log(`Setting scores for user: ${username}`);

            // Simulate form submission without actually triggering a click event
            const form = document.querySelector('form#rateform');
            if (form) {
                form.dispatchEvent(new Event('submit', { cancelable: true }));
            } else {
                console.error('Form not found.');
            }
        } else {
            console.error('Score input fields are missing.');
            if (retries < maxRetries) {
                console.log(`Retrying to find score inputs... (${retries + 1}/${maxRetries})`);
                setTimeout(() => updateInputsAndSubmit(button, retries + 1), retryDelay);
            } else {
                console.warn('Max retries exceeded for finding score input fields. Moving to next rate button.');
                currentIndex++;
                setTimeout(clickNextRateButton, 1000);
            }
        }
    }

    // Adjusted clickNextRateButton function to skip UI interactions
    function clickNextRateButton() {
        const rateButtons = Array.from(document.querySelectorAll('a.rateb'));
        if (currentIndex >= rateButtons.length) {
            console.log('All replies have been rated.');
            return;
        }

        const button = rateButtons[currentIndex];
        const postElement = button.closest('.plhin');

        if (shouldIgnoreMessage(postElement)) {
            console.log(`Ignoring post ${currentIndex + 1}.`);
            currentIndex++;
            setTimeout(clickNextRateButton, 1000);
            return;
        }

        // Skip button click and directly proceed with rating logic
        updateInputsAndSubmit(button);
        currentIndex++;

        // Schedule the next iteration after a delay
        setTimeout(clickNextRateButton, 1000);
    }

    function closeFwinWindow() {
        const closeButton = document.querySelector('button#closebtn.pn.pnc');
        if (closeButton) {
            console.log('Already rated window detected. Closing it.');
            closeButton.click();
        }
    }

    function shouldIgnoreMessage(postElement) {
        const messageElement = postElement.querySelector('.t_f');
        const messageHTML = messageElement ? messageElement.innerHTML.trim() : '';

        // Define ignore patterns for common phrases
        const ignorePatterns = [
            /^.{0,7}感[谢|謝].*?分享.{0,7}$/i, // Ignore if "感謝" and "分享" together make less than 8 chars
            /^.{0,7}多[谢|謝].*?分享.{0,7}$/i,
            /^[多|谢|謝][谢|謝].{0,7}$/i,
            /非常精彩/i,
            /thx.*?for.*?sharing/i,   // "thx for sharing"
            /thanks.*?for.*?sharing/i, // "thanks for sharing"
			/(T.*?Q|thx|thanks)/i,
            /nice.*?[.!?]?/i,         // Matches "nice" statements
            /good.*?[.!?]?/i,         // Matches "good" statements
            /good\s+good/i,           // Matches "good good"
            /nice\s+nice/i            // Matches "nice nice"
        ];

        // Clean up message for analysis, removing GIFs first
        const cleanedMessage = messageHTML
            .replace(/<br\s*\/?>/g, ' ')           // Normalize break tags
            .replace(/<img[^>]*src="[^"]*\.gif"[^>]*>/g, ' ') // Remove GIF images
            .replace(/<img[^>]*>/g, ' ')            // Replace other images with space (if needed)
            .replace(/&nbsp;/g, ' ')                // Normalize non-breaking spaces
            .replace(/\s+/g, ' ')                   // Collapse multiple spaces into a single space
            .trim();                                // Trim leading/trailing whitespace

        const hasMeaningfulContent = cleanedMessage.length > 0; // Check for meaningful content

        // Check if the cleaned message doesn't have any meaningful content
        if (!hasMeaningfulContent) {
            console.log(`Ignoring message as it contains only GIFs.`);
            return true; // Ignore if all GIFs or no content remains
        }

        // Normalize the cleaned message for common phrases checking
        const normalizedMessage = cleanedMessage.toLowerCase().replace(/[!.,?;:()]/g, '');

        // Check for matches against ignore patterns
        for (const pattern of ignorePatterns) {
            if (pattern.test(normalizedMessage)) {
                console.log(`Ignoring message due to pattern match: ${normalizedMessage}`);
                return true; // Ignore this message
            }
        }

        console.log(`Valid message passed: ${normalizedMessage}`);
        return false; // Not ignored
    }
  
    function clickNextRateButton() {
        const rateButtons = Array.from(document.querySelectorAll('a.rateb'));
        if (currentIndex >= rateButtons.length) {
            console.log('All replies have been rated.');
            return;
        }

        const button = rateButtons[currentIndex];
        const postElement = button.closest('.plhin');

        if (shouldIgnoreMessage(postElement)) {
            console.log(`Ignoring post ${currentIndex + 1}.`);
            currentIndex++;
            setTimeout(clickNextRateButton, 1000);
            return;
        }

        if (button) {
            console.log(`Clicking rate button for post index ${currentIndex}`);
            button.click();
            currentIndex++;
        }

        setTimeout(() => {
            const rateWindow = document.getElementById('fwin_rate');
            if (rateWindow) {
                updateInputsAndSubmit(button);
            }

            const alreadyRatedWindow = document.querySelector('table.fwin');
            if (alreadyRatedWindow) {
                closeFwinWindow();
                setTimeout(clickNextRateButton, 500);
            } else {
                setTimeout(clickNextRateButton, 1000);
            }
        }, 1000);
    }

    function automateRating() {
        currentIndex = 0; // Reset the index
        clickNextRateButton(); // Start clicking buttons
    }

    function addAutomationButton(container, buttonText, styles, buttonClass) {
        if (container) {
            const autoButton = document.createElement('button');
            autoButton.innerText = buttonText;
            autoButton.onclick = automateRating;
            Object.assign(autoButton.style, styles);
            autoButton.className = buttonClass;
            container.appendChild(autoButton);
        }
    }

    const style = document.createElement('style');
    style.textContent = `
        .main-button {
            background-color: #FE9510;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 7px 10px;
            font-size: 14.5px;
            cursor: pointer;
            margin: 0px;
            transition: background-color 0.3s, color 0.3s;
        }

        .main-button:hover {
            background-color: #E78F1B;
            color: #FFF;
        }

        .scroll-top-button {
            background-color: #F4F4F4;
            color: #BBBBBB;
            border-top: 1px solid #BBBBBB;
            border-left: none;
            border-bottom: none;
            border-right: none;
            border-radius: 0px;
            padding: 7px 7px;
            font-size: 11px;
            font-weight: bold;
            cursor: pointer;
            margin: 0px;
            writing-mode: horizontal-lr;
            transition: background-color 0.3s, color 0.3s;
        }

        .scroll-top-button:hover {
            background-color: #F4F4F4;
            color: #7DA0CC;
        }
    `;
    document.head.appendChild(style);

    window.addEventListener('load', () => {
        if (window.location.href.includes("thread")) {
            addAutomationButton(document.querySelector('div#pgt.pgs.cl'), '自動評分', {}, 'main-button');
            addAutomationButton(document.querySelector('div#scrolltop'), '自動評分', {}, 'scroll-top-button');
        } else {
            console.log('Not a thread page. Automation buttons will not be added.');
        }
    });
})();

It seems like when user-specific rating is used, the following user will get the same rating score.  It should first check before rating.