// ==UserScript== // @name X (Twitter) Feed Text Extractor // @namespace http://tampermonkey.net/ // @version 0.7 // @description Extract and monitor formatted text content from X (Twitter) feed // @match https://x.com/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/500409/X%20%28Twitter%29%20Feed%20Text%20Extractor.user.js // @updateURL https://update.greasyfork.icu/scripts/500409/X%20%28Twitter%29%20Feed%20Text%20Extractor.meta.js // ==/UserScript== (function() { 'use strict'; let isMonitoring = false; let collectedTweets = new Set(); let observer; const button = document.createElement('button'); button.textContent = '开始监控X内容'; button.style.position = 'fixed'; button.style.top = '10px'; button.style.right = '10px'; button.style.zIndex = '9999'; document.body.appendChild(button); button.addEventListener('click', toggleMonitoring); function formatRelativeTime(dateStr) { const now = new Date(); const past = new Date(dateStr); const diffMs = now - past; const diffSec = Math.floor(diffMs / 1000); const diffMin = Math.floor(diffSec / 60); const diffHour = Math.floor(diffMin / 60); const diffDay = Math.floor(diffHour / 24); const diffMonth = Math.floor(diffDay / 30); const diffYear = Math.floor(diffDay / 365); if (diffYear > 0) { return `${diffYear}年前`; } else if (diffMonth > 0) { return `${diffMonth}个月前`; } else if (diffDay > 0) { return `${diffDay}天前`; } else if (diffHour > 0) { return `${diffHour}小时前`; } else if (diffMin > 0) { return `${diffMin}分钟前`; } else { return '刚刚'; } } function toggleMonitoring() { if (isMonitoring) { stopMonitoring(); displayCollectedTweets(); } else { startMonitoring(); } } function startMonitoring() { isMonitoring = true; button.textContent = '停止监控并显示内容'; collectedTweets.clear(); const currentTweets = document.querySelectorAll('[data-testid="tweet"]'); currentTweets.forEach(processTweet); const config = { childList: true, subtree: true }; observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { const tweets = node.querySelectorAll('[data-testid="tweet"]'); tweets.forEach(processTweet); } }); } }); }); observer.observe(document.body, config); } function stopMonitoring() { isMonitoring = false; button.textContent = '开始监控X内容'; if (observer) { observer.disconnect(); } } function processTweet(tweet) { const tweetContent = formatTweet(tweet); if (tweetContent) { collectedTweets.add(tweetContent); } } function displayCollectedTweets() { let extractedText = Array.from(collectedTweets).join('\n\n---\n\n'); const newWindow = window.open('', '_blank'); newWindow.document.write('
' + extractedText + ''); } function formatTweet(tweet) { const authorElement = tweet.querySelector('div[data-testid="User-Name"]'); if (!authorElement) return null; const authorName = authorElement.querySelector('span')?.textContent || 'Unknown'; const authorId = authorElement.querySelector('a[href^="/"]')?.getAttribute('href')?.slice(1) || 'Unknown'; const timeElement = tweet.querySelector('time'); const tweetTime = timeElement ? formatRelativeTime(timeElement.getAttribute('datetime')) : 'Unknown time'; let formattedTweet = `作者: ${authorName} (@${authorId})\n发布时间: ${tweetTime}\n\n`; const tweetTextElement = tweet.querySelector('[data-testid="tweetText"]'); if (tweetTextElement) { formattedTweet += `内容: ${tweetTextElement.textContent}\n`; } const retweetElement = tweet.querySelector('[data-testid="socialContext"]'); if (retweetElement) { const retweetAuthor = retweetElement.textContent.replace('转推', '').trim(); formattedTweet = `转推自: ${retweetAuthor}\n\n${formattedTweet}`; } return formattedTweet; } })();