Created
June 5, 2025 17:04
-
-
Save Wikinaut/234e139d8f953806647b683e03b46764 to your computer and use it in GitHub Desktop.
Tagesspiegel-Kommentare Download und Markdownformatierer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Coral Talk Auto Load All Comments (Markdown Export mit Einrückung und Thread-Erkennung) | |
// @namespace http://tampermonkey.net/ | |
// @version 3.0 | |
// @description Lädt automatisch alle Kommentare und exportiert sie als strukturierte Markdown-Datei mit Einrückung und Thread-Erkennung. | |
// @author ChatGPT | |
// @match https://coral.tagesspiegel.de/embed/stream* | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
function wait(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
const statusBtn = document.createElement('button'); | |
statusBtn.style.position = 'fixed'; | |
statusBtn.style.top = '10px'; | |
statusBtn.style.right = '10px'; | |
statusBtn.style.padding = '10px 20px'; | |
statusBtn.style.borderRadius = '5px'; | |
statusBtn.style.fontSize = '14px'; | |
statusBtn.style.fontWeight = 'bold'; | |
statusBtn.style.boxShadow = '0 2px 6px rgba(0,0,0,0.3)'; | |
statusBtn.style.zIndex = 9999; | |
statusBtn.style.color = 'white'; | |
statusBtn.style.border = 'none'; | |
statusBtn.style.cursor = 'default'; | |
statusBtn.disabled = true; | |
document.body.appendChild(statusBtn); | |
let blinkInterval; | |
function startLoadingStatus() { | |
statusBtn.textContent = 'Lade Kommentare...'; | |
statusBtn.style.backgroundColor = '#d32f2f'; | |
statusBtn.disabled = true; | |
statusBtn.style.cursor = 'default'; | |
blinkInterval = setInterval(() => { | |
statusBtn.style.opacity = statusBtn.style.opacity === '0.5' ? '1' : '0.5'; | |
}, 600); | |
} | |
function showFinishedStatus(count) { | |
clearInterval(blinkInterval); | |
statusBtn.style.opacity = '1'; | |
statusBtn.textContent = `✅ ${count} Kommentare geladen – Klick für Markdown-Download`; | |
statusBtn.style.backgroundColor = '#4caf50'; | |
statusBtn.disabled = false; | |
statusBtn.style.cursor = 'pointer'; | |
} | |
function gatherAllCommentsMarkdown() { | |
const commentContainers = document.querySelectorAll('.talk-stream-comment'); | |
let allText = ''; | |
let count = 0; | |
commentContainers.forEach(container => { | |
let author = 'Unbekannter Autor'; | |
const header = container.querySelector('.talk-stream-comment-header'); | |
if (header) { | |
const authorSpan = header.querySelector('span'); | |
if (authorSpan && authorSpan.innerText.trim() !== '') { | |
author = authorSpan.innerText.trim(); | |
} | |
} | |
let date = 'Unbekannter Zeitpunkt'; | |
const timeEl = container.querySelector('time'); | |
if (timeEl && timeEl.innerText.trim() !== '') { | |
date = timeEl.innerText.trim(); | |
} else if (header) { | |
const headerText = header.innerText.trim(); | |
const afterAuthor = headerText.replace(author, '').trim(); | |
if (afterAuthor.length > 0) date = afterAuthor; | |
} | |
const textEl = container.querySelector('.talk-stream-comment-content'); | |
const text = textEl ? textEl.innerText.trim() : ''; | |
let votes = '0 Zustimmungen'; | |
const voteEls = Array.from(container.querySelectorAll('span, button')).filter(el => | |
el.textContent.toLowerCase().includes('zustimmen') | |
); | |
for (const el of voteEls) { | |
const match = el.textContent.match(/(\d+)/); | |
if (match && match[1]) { | |
votes = match[1] + ' Zustimmungen'; | |
break; | |
} | |
} | |
if (text) { | |
count++; | |
const isReply = /^@\S+/.test(text.split('\n')[0].trim()); | |
const prefix = isReply ? '> ' : ''; | |
const lines = text.split('\n').map(line => prefix + line).join('\n'); | |
if (isReply) { | |
allText += `> ### Kommentar ${count} von ${author} am ${date} (${votes})\n>\n${lines}\n\n---\n\n`; | |
} else { | |
allText += `### Kommentar ${count} von ${author} am ${date} (${votes})\n\n${lines}\n\n---\n\n`; | |
} | |
} | |
}); | |
return { text: allText.trim(), count }; | |
} | |
function downloadTextFile(filename, text) { | |
const blob = new Blob([text], {type: 'text/markdown;charset=utf-8'}); | |
const url = URL.createObjectURL(blob); | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = filename; | |
document.body.appendChild(a); | |
a.click(); | |
setTimeout(() => { | |
document.body.removeChild(a); | |
URL.revokeObjectURL(url); | |
}, 0); | |
} | |
statusBtn.addEventListener('click', () => { | |
if (statusBtn.disabled) return; | |
const { text, count } = gatherAllCommentsMarkdown(); | |
if (!text) { | |
alert('Keine Kommentare gefunden zum Download.'); | |
return; | |
} | |
const filename = 'Kommentare_Tagesspiegel.md'; | |
downloadTextFile(filename, text); | |
}); | |
async function clickLoadMoreLoop() { | |
console.log('Starte automatisches Laden aller Kommentare...'); | |
startLoadingStatus(); | |
let attemptsWithoutButton = 0; | |
let noNewCountAttempts = 0; | |
while (true) { | |
const buttons = Array.from(document.querySelectorAll('button')); | |
const button = buttons.find(b => { | |
const text = b.textContent.trim().toLowerCase(); | |
return text.startsWith('weitere kommentare anzeigen') || text.startsWith('alle antworten anzeigen') | |
}); | |
if (button && !button.disabled) { | |
console.log('Button gefunden, klicke...'); | |
const lastCount = document.querySelectorAll('.talk-stream-comment').length; | |
button.click(); | |
const maxWait = 20000; | |
const interval = 5000; | |
let waited = 0; | |
while (waited < maxWait) { | |
await wait(interval); | |
waited += interval; | |
const currentCount = document.querySelectorAll('.talk-stream-comment').length; | |
if (currentCount > lastCount) { | |
break; | |
} | |
} | |
noNewCountAttempts = 0; | |
attemptsWithoutButton = 0; | |
} else { | |
attemptsWithoutButton++; | |
noNewCountAttempts++; | |
if (attemptsWithoutButton >= 10 || noNewCountAttempts >= 10) { | |
console.log('Fertig mit Laden.'); | |
const { count } = gatherAllCommentsMarkdown(); | |
showFinishedStatus(count); | |
break; | |
} | |
await wait(2000); | |
} | |
} | |
} | |
window.addEventListener('load', () => { | |
setTimeout(() => { | |
clickLoadMoreLoop(); | |
}, 2000); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment