Greasy Fork

Isoliertes Load All Comments & Expand Replies kann als snippet dienen

Load all comments from all pages on MyDealz with optimized performance

// ==UserScript==
// @name         Isoliertes Load All Comments & Expand Replies kann als snippet dienen
// @namespace    violentmonkey
// @version      4.0.1
// @description  Load all comments from all pages on MyDealz with optimized performance
// @author       optimized version
 // @license MIT
// @match        https://www.mydealz.de/deals/*
// @match        https://www.mydealz.de/diskussion/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=mydealz.de
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Konfiguration basierend auf dem funktionierenden Original
    const CONFIG = {
        REPLY_CHECK_INTERVAL: 500,
        MAX_CHECKS_WITHOUT_ACTION: 5,
        CLICK_COOLDOWN_MS: 200,
        PAGE_TRANSITION_DELAY: 2000,
        DOM_CHECK_INTERVAL: 100,
        DOM_TIMEOUT: 10000
    };

    const REPLY_BUTTON_SELECTOR = 'button[data-t="moreReplies"]:not([disabled])';
    
    // Button-Styles
    const BUTTON_STYLES = {
        default: `
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 8px 16px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            z-index: 9999;
            font-size: 12px;
        `,
        processing: `
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 8px 16px;
            background-color: #ff0000;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: not-allowed;
            z-index: 9999;
            font-size: 12px;
        `,
        success: `
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 8px 16px;
            background-color: #00ff00;
            color: black;
            border: none;
            border-radius: 5px;
            cursor: default;
            z-index: 9999;
            font-size: 12px;
        `
    };

    // Button erstellen oder aktualisieren
    function ensureButton(className, text, style, clickHandler) {
        let button = document.querySelector(`button.${className}`);
        if (!button) {
            button = document.createElement('button');
            button.className = className;
            button.textContent = text;
            button.style.cssText = style;
            button.addEventListener('click', clickHandler);
            document.body.appendChild(button);
            console.log(`${text} button hinzugefügt.`);
        }
        return button;
    }

    // Button-Status aktualisieren
    function updateButtonState(button, state, text, clickCount = 0) {
        switch (state) {
            case 'processing':
                button.textContent = text || 'Verarbeitung...';
                button.style.cssText = BUTTON_STYLES.processing;
                button.disabled = true;
                break;
            case 'success':
                button.textContent = clickCount ? `Fertig (${clickCount} Klicks)` : 'Erfolgreich!';
                button.style.cssText = BUTTON_STYLES.success;
                button.disabled = true;
                break;
            default:
                button.textContent = text;
                button.style.cssText = BUTTON_STYLES.default;
                button.disabled = false;
        }
    }

    // Warten auf DOM-Element
    function waitForElement(selector, minChildren = 1, timeout = CONFIG.DOM_TIMEOUT) {
        return new Promise((resolve, reject) => {
            const startTime = Date.now();
            const check = () => {
                const element = document.querySelector(selector);
                if (element && element.children.length >= minChildren) {
                    resolve(element);
                } else if (Date.now() - startTime > timeout) {
                    reject(new Error(`Timeout beim Warten auf ${selector} mit mindestens ${minChildren} Kindern`));
                } else {
                    setTimeout(check, CONFIG.DOM_CHECK_INTERVAL);
                }
            };
            check();
        });
    }

    // Antworten auf aktueller Seite erweitern
    async function expandAllReplies() {
        return new Promise((resolve) => {
            let totalClicks = 0;
            let consecutiveChecksWithoutAction = 0;

            const checkAndClick = async () => {
                // Nur sichtbare Buttons berücksichtigen
                const buttons = Array.from(document.querySelectorAll(REPLY_BUTTON_SELECTOR))
                    .filter(btn => btn.offsetParent !== null);

                if (buttons.length > 0) {
                    consecutiveChecksWithoutAction = 0;
                    const buttonToClick = buttons[0];

                    console.log(`Klicke Button: "${buttonToClick.textContent.trim()}"`);
                    try {
                        buttonToClick.click();
                        totalClicks++;
                        await new Promise(res => setTimeout(res, CONFIG.CLICK_COOLDOWN_MS));
                        setTimeout(checkAndClick, 0);
                    } catch (e) {
                        console.error("Fehler beim Klicken:", e, buttonToClick);
                        await new Promise(res => setTimeout(res, CONFIG.REPLY_CHECK_INTERVAL));
                        setTimeout(checkAndClick, 0);
                    }
                } else {
                    consecutiveChecksWithoutAction++;
                    console.log(`Keine klickbaren Buttons gefunden. Check ${consecutiveChecksWithoutAction}/${CONFIG.MAX_CHECKS_WITHOUT_ACTION}. Warte ${CONFIG.REPLY_CHECK_INTERVAL}ms...`);
                    if (consecutiveChecksWithoutAction < CONFIG.MAX_CHECKS_WITHOUT_ACTION) {
                        setTimeout(checkAndClick, CONFIG.REPLY_CHECK_INTERVAL);
                    } else {
                        console.log(`Erweiterung abgeschlossen. Gesamt Klicks: ${totalClicks}.`);
                        resolve(totalClicks);
                    }
                }
            };

            checkAndClick();
        });
    }

    // Hauptfunktion: Alle Kommentare laden und Antworten erweitern
    async function loadAllCommentsAndExpandReplies() {
        const loadButton = ensureButton(
            'mydealz-load-all-button',
            'Alle Kommentare laden & Antworten erweitern',
            BUTTON_STYLES.default,
            loadAllCommentsAndExpandReplies
        );

        // URL normalisieren
        const baseUrl = window.location.pathname + '?#comments';
        if (window.location.href !== window.location.origin + baseUrl) {
            console.log(`Lade normalisierte URL: ${baseUrl}`);
            localStorage.setItem('mydealz_script_triggered', 'true');
            updateButtonState(loadButton, 'processing', 'Verarbeitung...');
            window.location.href = baseUrl;
            return;
        }

        updateButtonState(loadButton, 'processing', 'Verarbeitung...');

        let masterCommentList;
        try {
            masterCommentList = await waitForElement('ol.commentList.commentList--anchored', 1);
        } catch (error) {
            console.log('Keine Kommentarliste gefunden. Skript kann nicht fortfahren.');
            updateButtonState(loadButton, 'default', 'Alle Kommentare laden & Antworten erweitern');
            return;
        }

        const pagination = document.querySelector('nav[role="navigation"][aria-label="Nummerierung"]');
        let totalPages = 1;
        if (pagination) {
            const pageButtons = pagination.querySelectorAll('.comments-pagi-page');
            if (pageButtons.length > 0) {
                const lastPageButton = pageButtons[pageButtons.length - 1];
                totalPages = parseInt(lastPageButton.textContent.trim(), 10) || 1;
                console.log(`Gesamtseiten erkannt: ${totalPages}`);
            }
        }

        const allComments = new Map();

        // Kommentare von jeder Seite mit erweiterten Antworten sammeln
        for (let page = 1; page <= totalPages; page++) {
            console.log(`Verarbeite Seite ${page}...`);
            try {
                masterCommentList = await waitForElement('ol.commentList.commentList--anchored', 1);
                console.log(`Erweitere Antworten auf Seite ${page}...`);
                await expandAllReplies();
                
                const comments = masterCommentList.querySelectorAll('.commentList-item');
                console.log(`${comments.length} Kommentare auf Seite ${page} gefunden (mit Antworten)`);
                
                comments.forEach(comment => {
                    const commentId = comment.getAttribute('data-id');
                    if (commentId && !allComments.has(commentId)) {
                        allComments.set(commentId, comment.cloneNode(true));
                    }
                });

                if (page < totalPages) {
                    const nextButton = pagination.querySelector('button[aria-label="Nächste Seite"]');
                    if (!nextButton || nextButton.hasAttribute('disabled')) {
                        console.log(`Kein "Nächste Seite" Button verfügbar oder deaktiviert auf Seite ${page}. Stoppe.`);
                        break;
                    }
                    console.log(`Klicke "Nächste Seite" für Seite ${page + 1}...`);
                    nextButton.click();
                    await new Promise(res => setTimeout(res, CONFIG.PAGE_TRANSITION_DELAY));
                }
            } catch (error) {
                console.error(`Fehler beim Verarbeiten von Seite ${page}:`, error);
            }
        }

        // Alle gesammelten Kommentare anhängen
        masterCommentList = document.querySelector('ol.commentList.commentList--anchored');
        if (masterCommentList) {
            console.log(`Vor dem Hinzufügen hat die Master-Liste ${masterCommentList.children.length} Elemente`);
            masterCommentList.innerHTML = '';
            allComments.forEach(comment => {
                masterCommentList.appendChild(comment);
            });
            console.log(`Nach dem Hinzufügen hat die Master-Liste ${masterCommentList.children.length} Elemente`);
            console.log(`${allComments.size} eindeutige Kommentare mit Antworten hinzugefügt.`);
            updateButtonState(loadButton, 'success', 'Erfolgreich!', allComments.size);
        } else {
            console.log('Master-Kommentarliste nach Verarbeitung nicht gefunden. Kann Kommentare nicht anhängen.');
            updateButtonState(loadButton, 'default', 'Alle Kommentare laden & Antworten erweitern');
        }

        localStorage.removeItem('mydealz_script_triggered');
    }

    // Buttons initialisieren
    function initializeButtons() {
        ensureButton(
            'mydealz-load-all-button',
            'Alle Kommentare laden & Antworten erweitern',
            BUTTON_STYLES.default,
            loadAllCommentsAndExpandReplies
        );
    }

    // Auto-Run nur wenn von vorheriger Seite ausgelöst
    if (window.location.hash === '#comments' && localStorage.getItem('mydealz_script_triggered') === 'true') {
        console.log('Button wurde zuvor geklickt. Auto-Run des Skripts auf #comments...');
        loadAllCommentsAndExpandReplies();
    } else if (document.readyState === 'complete') {
        initializeButtons();
    } else {
        window.addEventListener('load', () => {
            initializeButtons();
        });
    }

    console.log('MyDealz Comment Enhancer Skript (Optimiert) initialisiert.');
})();