// ==UserScript== // @name MyDealz | Kommentar-Export über alle Seiten // @namespace violentmonkey // @version 2.0 // @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 // @downloadURL 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.size--all-xl.size--fromW3-xxl' }; const INTERVAL = { CHECK: 500, CLICK: 200, PAGE: 2000 }; let collectedComments = []; let totalComments = 0; let exportBtn = null; const KI_LINKS = [ { id: 'chatgptBtn', label: 'chatgpt', url: 'https://chatgpt.com/' }, { id: 'perplexityBtn', label: 'perplexity', url: 'https://www.perplexity.ai/' }, { id: 'grokBtn', label: 'grok', url: 'https://grok.com/' }, { id: 'geminiBtn', label: 'gemini', url: 'https://gemini.google.com/' }, { id: 'mistralBtn', label: 'mistral', url: 'https://chat.mistral.ai/chat' }, { id: 'claudeBtn', label: 'claude', url: 'https://claude.ai/' } ]; 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); } } function collectCommentsOnPage() { const articles = document.querySelectorAll(SELECTORS.COMMENT_ARTICLE); for (const article of articles) { const bodyNode = article.querySelector('.comment-body .userHtml-content'); const text = bodyNode ? bodyNode.textContent.replace(/\s+/g, ' ').trim() : ''; 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; } collectedComments.push( `(Gefällt mir: ${like} | Hilfreich: ${helpful} | Lustig: ${funny} | ${text})` ); } return articles.length; } 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 getThreadTitleAndUrl() { let title = ''; const el = document.querySelector(SELECTORS.THREAD_TITLE); if (el) { title = el.textContent.trim(); } else { title = document.title.replace(/\|.*$/, '').trim(); } const url = window.location.href.replace(/#.*$/, ''); return { title, url }; } function buildIntroText() { const { title, url } = getThreadTitleAndUrl(); return ( `# Zusammenfassung der Kommentare zur MyDealz-Diskussion [${title}](${url}) ## Anweisung Fasse die bereitgestellten Benutzerkommentare zu der Diskussion zusammen, indem du die Hauptpunkte und gemeinsamen Themen identifizierst, die diskutiert werden. Füge relevante Zitate aus den Kommentaren hinzu, um die Schlüsselpunkte zu illustrieren, und formatiere diese Zitate als Markdown-Zitat-Blöcke (mit > ). Falls zutreffend, erwähne unterschiedliche Meinungen oder besonders aufschlussreiche Beiträge. Präsentiere die Zusammenfassung strikt im Markdown-Format, wobei du Überschriften (mit #, ## etc.) und Aufzählungspunkte (mit - oder * ) verwendest, um die Informationen klar zu organisieren. Stelle sicher, dass die Formatierung konsistent ist und den Markdown-Syntax-Regeln entspricht, sodass sie direkt in einen Markdown-Interpreter eingefügt werden kann. Erstelle die Zusammenfassung in derselben Sprache wie die Kommentare. Die zusammenzufassenden Daten liegen im Format (Gefällt mir: n | Hilfreich: n | Lustig: n | Kommentar) vor. Besonders häufig bewertete Kommentare kannst du auch mit ✅👍😅 kennzeichnen. ## Daten ---` ); } function openExportWindow(text) { // Fenstergröße: breiter und höher, kein Browser-Scrollbalken const w = window.open('', 'blank', 'width=900,height=700,resizable=yes,scrollbars=no'); if (!w) { alert('Popup blockiert! Bitte Popup-Blocker für diese Seite deaktivieren.'); return; } const { title } = getThreadTitleAndUrl(); const filename = title || 'mydealz-comments'; w.document.title = 'MyDealz Kommentar-Export'; w.document.head.innerHTML = ` `; w.document.body.innerHTML = `
 
${KI_LINKS.map(btn => ``).join('')}

            
`; 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.textContent = 'Copied!'; msg.style.opacity = 1; setTimeout(() => { msg.style.opacity = 0; msg.textContent = '\xa0'; // Platzhalter (White Space) }, 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); }; // KI-Buttons KI_LINKS.forEach(btn => { const el = w.document.getElementById(btn.id); if (el) el.onclick = () => window.open(btn.url, '_blank'); }); } async function runExport() { totalComments = getTotalComments(); collectedComments = []; 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; } const fullText = buildIntroText() + '\n' + collectedComments.join('\n'); openExportWindow(fullText); 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); } })();