Greasy Fork

MyDealz | Kommentar-Export über alle Seiten

Exportiert alle Kommentare eines MyDealz-Threads über alle Seiten als TXT (inkl. Fortschritt, Copy-All, Save-TXT, Popup-Blocker-Erkennung)

当前为 2025-05-16 提交的版本,查看 最新版本

// ==UserScript==
// @name         MyDealz | Kommentar-Export über alle Seiten
// @namespace    violentmonkey
// @version      1.6
// @description  Exportiert alle Kommentare eines MyDealz-Threads über alle Seiten als TXT (inkl. Fortschritt, Copy-All, Save-TXT, Popup-Blocker-Erkennung)
// @match        https://www.mydealz.de/diskussion/*
// @match        https://www.mydealz.de/deals/*
// @match        https://www.mydealz.de/gutscheine/*
// @icon         https://www.mydealz.de/assets/img/emojis/cool_b7b27.svg
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // Selektoren
    const SELECTORS = {
        REPLY_BTN: 'button[data-t="moreReplies"]:not([disabled])',
        NEXT_PAGE: 'button[aria-label="Nächste Seite"]:not([disabled])',
        FIRST_PAGE: 'button[aria-label="Erste Seite"]:not([disabled])',
        LAST_PAGE: 'button[aria-label="Letzte Seite"]',
        CURRENT_PAGE: 'button[aria-label="Aktuelle Seite"]',
        COMMENT_LINK: 'a[href*="#comments"].button--type-text',
        COMMENT_ARTICLE: 'article.comment',
        THREAD_TITLE: '.thread-title .text--b, title'
    };
    // Timings
    const INTERVAL = { CHECK: 500, CLICK: 200, PAGE: 2000 };

    let collectedComments = [];
    let totalComments = 0;
    let exportBtn = null;

    // Hilfsfunktionen
    const sleep = ms => new Promise(r => setTimeout(r, ms));

    function pageNum(sel) {
        const el = document.querySelector(sel);
        if (!el) return null;
        const m = el.textContent.match(/\d+/);
        return m ? parseInt(m[0], 10) : null;
    }

    function getTotalComments() {
        const a = document.querySelector(SELECTORS.COMMENT_LINK);
        if (!a) return 0;
        const m = a.textContent.match(/\d+/);
        return m ? parseInt(m[0], 10) : 0;
    }

    async function clickAllReplies() {
        while (true) {
            const btn = document.querySelector(SELECTORS.REPLY_BTN);
            if (!btn || btn.disabled || btn.offsetParent === null) break;
            btn.click();
            await sleep(INTERVAL.CLICK);
        }
    }

    // NEU: Kommentare + Reaktionen sammeln
    function collectCommentsOnPage() {
        const articles = document.querySelectorAll(SELECTORS.COMMENT_ARTICLE);
        let count = 0;
        for (const article of articles) {
            // Kommentartext
            const bodyNode = article.querySelector('.comment-body .userHtml-content');
            const text = bodyNode ? bodyNode.textContent.trim() : '';

            // Reaktionen
            const reactionsBtn = article.querySelector('button.comment-reactions');
            let like = 0, helpful = 0, funny = 0;
            if (reactionsBtn) {
                const likeSpan = reactionsBtn.querySelector('.comment-like');
                const helpfulSpan = reactionsBtn.querySelector('.comment-helpful');
                const funnySpan = reactionsBtn.querySelector('.comment-funny');
                like = likeSpan ? parseInt(likeSpan.textContent.trim(), 10) || 0 : 0;
                helpful = helpfulSpan ? parseInt(helpfulSpan.textContent.trim(), 10) || 0 : 0;
                funny = funnySpan ? parseInt(funnySpan.textContent.trim(), 10) || 0 : 0;
            }

            // Format wie gewünscht
            collectedComments.push(
                `Gefällt mir: ${like}; Hilfreich: ${helpful}; Lustig: ${funny};\n${text}`
            );
            count++;
        }
        return count;
    }

    function updateProgress(pageCollected, page, last) {
        const pct = totalComments ? Math.round(collectedComments.length / totalComments * 100) : 0;
        exportBtn.textContent = `Kommentare exportieren (${collectedComments.length} von ${totalComments} | Seite ${page}/${last} | ${pct}%)`;
    }

    async function goNextPage() {
        const btn = document.querySelector(SELECTORS.NEXT_PAGE);
        if (btn) {
            btn.click();
            await sleep(INTERVAL.PAGE);
            return true;
        }
        return false;
    }

    function getThreadTitle() {
        let title = '';
        const el = document.querySelector(SELECTORS.THREAD_TITLE);
        if (el) {
            title = el.textContent.trim();
        } else {
            title = document.title.replace(/\|.*$/, '').trim();
        }
        // Nur erlaubte Zeichen für Dateinamen
        return title.replace(/[\\\/:*?"<>|]/g, '').replace(/\s+/g, ' ').trim();
    }

    function openExportWindow(text) {
        const w = window.open('', 'blank', 'width=800,height=600');
        if (!w) {
            alert('Popup blockiert! Bitte Popup-Blocker für diese Seite deaktivieren.');
            return;
        }
        const filename = getThreadTitle() || 'mydealz-comments';
        w.document.title = 'MyDealz Kommentar-Export';
        w.document.head.innerHTML = `
            <style>
                body { font-family: sans-serif; margin: 20px; }
                button { display: inline-flex; align-items: center; padding: 10px 16px; margin-right: 8px; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }
                #copyBtn { background: #d32f2f; color: #fff; }
                #saveBtn { background: #007bff; color: #fff; }
                pre { white-space: pre-wrap; word-wrap: break-word; border: 1px solid #ccc; padding: 12px; border-radius: 4px; max-height: 80vh; overflow: auto; }
                #copiedMsg { margin-left: 8px; color: #4caf50; font-weight: bold; opacity: 0; transition: opacity .3s; }
            </style>
        `;
        w.document.body.innerHTML = `
            <button id="copyBtn">Copy All</button>
            <button id="saveBtn">Save .txt</button>
            <span id="copiedMsg">Copied!</span>
            <pre id="exportText"></pre>
        `;
        const pre = w.document.getElementById('exportText');
        pre.textContent = text;
        const btnC = w.document.getElementById('copyBtn');
        const btnS = w.document.getElementById('saveBtn');
        const msg = w.document.getElementById('copiedMsg');
        btnC.onclick = () => {
            w.navigator.clipboard.writeText(text).then(() => {
                msg.style.opacity = 1;
                setTimeout(() => msg.style.opacity = 0, 2000);
            });
        };
        btnS.onclick = () => {
            const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
            const url = w.URL.createObjectURL(blob);
            const a = w.document.createElement('a');
            a.href = url;
            a.download = filename + '.txt';
            w.document.body.appendChild(a);
            a.click();
            w.URL.revokeObjectURL(url);
            w.document.body.removeChild(a);
        };
    }

    async function runExport() {
        totalComments = getTotalComments();
        collectedComments = [];
        // Immer erst auf Seite 1 starten
        const first = document.querySelector(SELECTORS.FIRST_PAGE);
        if (first) {
            first.click();
            await sleep(INTERVAL.PAGE);
        }
        while (true) {
            await clickAllReplies();
            const pageCollected = collectCommentsOnPage();
            const page = pageNum(SELECTORS.CURRENT_PAGE) || 1;
            const last = pageNum(SELECTORS.LAST_PAGE) || 1;
            updateProgress(pageCollected, page, last);
            if (page >= last) break;
            const next = await goNextPage();
            if (!next) break;
        }
        openExportWindow(collectedComments.join('\n\n---\n\n'));
        exportBtn.textContent = 'Fertig!';
        exportBtn.disabled = false;
    }

    function injectExportBtn() {
        exportBtn = document.createElement('button');
        exportBtn.textContent = 'Kommentare exportieren';
        Object.assign(exportBtn.style, {
            position: 'fixed',
            top: '20px',
            right: '20px',
            padding: '10px 16px',
            background: '#d32f2f',
            color: '#fff',
            border: 'none',
            borderRadius: '4px',
            fontSize: '14px',
            cursor: 'pointer',
            zIndex: 9999
        });
        exportBtn.onclick = () => {
            exportBtn.disabled = true;
            exportBtn.textContent = 'Lade...';
            runExport();
        };
        document.body.appendChild(exportBtn);
    }

    function ensureStartOnPageOne() {
        const url = new URL(window.location.href);
        const isCommentPage = url.hash.includes('comments');
        const pageParam = url.searchParams.get('page');
        if (pageParam && pageParam !== '1' && isCommentPage) {
            url.searchParams.set('page', '1');
            url.hash = 'comments';
            window.location.href = url.toString();
        } else {
            injectExportBtn();
        }
    }

    if (document.readyState === 'complete') {
        ensureStartOnPageOne();
    } else {
        window.addEventListener('load', ensureStartOnPageOne);
    }
})();