您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Extracts content from the X (Twitter) feed and converts it to Markdown format, with an added direct auto-scroll feature.
// ==UserScript== // @name X (Twitter) Feed to Markdown with Auto-Scroll // @namespace http://tampermonkey.net/ // @version 1.5 // @description Extracts content from the X (Twitter) feed and converts it to Markdown format, with an added direct auto-scroll feature. // @match https://x.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // --- Markdown转换功能的状态变量 --- let isMonitoring = false; let collectedTweets = new Map(); let observer; // --- 自动滚动功能的状态变量 --- let isAutoScrolling = false; let scrollIntervalId = null; // --- 创建Markdown转换按钮 --- const markdownButton = document.createElement('button'); markdownButton.textContent = '开始转换Markdown'; Object.assign(markdownButton.style, { position: 'fixed', top: '10px', right: '10px', zIndex: '9999', padding: '8px 16px', backgroundColor: '#1DA1F2', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', fontSize: '14px' }); document.body.appendChild(markdownButton); markdownButton.addEventListener('click', toggleMonitoring); // --- 创建自动滚动按钮 --- const scrollButton = document.createElement('button'); scrollButton.textContent = '开始自动滚动'; Object.assign(scrollButton.style, { position: 'fixed', top: '55px', // 放在第一个按钮的下方 right: '10px', zIndex: '9999', padding: '8px 16px', backgroundColor: '#28a745', // 绿色 color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', fontSize: '14px' }); document.body.appendChild(scrollButton); scrollButton.addEventListener('click', toggleAutoScroll); // --- 自动滚动功能 --- /** * 【已修改】直接执行浏览器滚动,而不是模拟按键 */ function performScroll() { // window.scrollBy(x, y) 让窗口从当前位置滚动指定的像素值 // x为0表示水平不滚动,y为400表示向下滚动400像素 // 你可以调整 400 这个数值来改变滚动的速度/距离 window.scrollBy(0, 400); console.log('Auto-scroll: Scrolled down by 400px.'); } /** * 切换自动滚动状态 */ function toggleAutoScroll() { if (isAutoScrolling) { // 停止滚动 clearInterval(scrollIntervalId); scrollIntervalId = null; isAutoScrolling = false; scrollButton.textContent = '开始自动滚动'; scrollButton.style.backgroundColor = '#28a745'; // 恢复绿色 console.log('自动滚动已停止。'); } else { // 开始滚动 isAutoScrolling = true; // 【已修改】调用新的滚动函数 scrollIntervalId = setInterval(performScroll, 500); // 每500ms滚动一次 scrollButton.textContent = '停止自动滚动'; scrollButton.style.backgroundColor = '#dc3545'; // 变为红色 console.log('自动滚动已开始...'); } } // --- Markdown转换功能 (原脚本逻辑) --- // (以下代码保持不变) function toggleMonitoring() { if (isMonitoring) { stopMonitoring(); displayCollectedTweets(); } else { startMonitoring(); } } function startMonitoring() { isMonitoring = true; markdownButton.textContent = '停止并导出Markdown'; markdownButton.style.backgroundColor = '#FF4136'; collectedTweets.clear(); console.log("开始监控推文..."); document.querySelectorAll('article[data-testid="tweet"]').forEach(processTweet); const config = { childList: true, subtree: true }; observer = new MutationObserver(mutations => { for (const mutation of mutations) { if (mutation.addedNodes.length) { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.matches('article[data-testid="tweet"]')) { processTweet(node); } node.querySelectorAll('article[data-testid="tweet"]').forEach(processTweet); } }); } } }); observer.observe(document.body, config); } function stopMonitoring() { isMonitoring = false; markdownButton.textContent = '开始转换Markdown'; markdownButton.style.backgroundColor = '#1DA1F2'; if (observer) { observer.disconnect(); } console.log("停止监控。"); } function processTweet(tweet) { if (tweet.querySelector('[data-testid="promotedTweet"]')) return; const timeElement = tweet.querySelector('time[datetime]'); if (timeElement && timeElement.closest('div[data-testid="User-Name"]')?.nextElementSibling?.textContent?.includes('Ad')) { return; } const tweetData = formatTweet(tweet); if (tweetData && tweetData.url && !collectedTweets.has(tweetData.url)) { collectedTweets.set(tweetData.url, tweetData.markdown); } } function displayCollectedTweets() { if (collectedTweets.size === 0) { alert('没有收集到任何推文。'); return; } const sortedTweets = Array.from(collectedTweets.values()).sort((a, b) => { const timeMatchA = a.match(/\*\*发布时间\*\*: (.*)/); const timeMatchB = b.match(/\*\*发布时间\*\*: (.*)/); if (!timeMatchA || !timeMatchB) return 0; const timeA = new Date(timeMatchA[1]); const timeB = new Date(timeMatchB[1]); return timeB - timeA; }); const markdownOutput = sortedTweets.join('\n\n---\n\n'); const newWindow = window.open('', '_blank'); newWindow.document.write('<pre style="white-space: pre-wrap; word-wrap: break-word; padding: 10px;">' + markdownOutput.replace(/</g, "<").replace(/>/g, ">") + '</pre>'); newWindow.document.title = 'Twitter Feed as Markdown'; } function extractTextContent(element) { if (!element) return ''; let text = ''; element.childNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.tagName === 'IMG') { text += node.alt; } else if (node.tagName === 'A') { const url = node.href; if (!url.includes('/photo/') && !url.includes('/video/')) { text += `[${node.textContent}](${url})`; } } else { text += node.textContent; } } else { text += node.textContent; } }); return text.trim(); } function formatTweet(tweet) { const timeElement = tweet.querySelector('time'); if (!timeElement) return null; const linkElement = timeElement.closest('a'); if (!linkElement) return null; const tweetUrl = 'https://x.com' + linkElement.getAttribute('href'); const authorHandle = `@${tweetUrl.split('/')[3]}`; const postTime = timeElement.getAttribute('datetime'); const mainContentElement = tweet.querySelector('div[data-testid="tweetText"]'); const mainContent = extractTextContent(mainContentElement); let quoteContent = ''; const quoteHeader = Array.from(tweet.querySelectorAll('span')).find(s => s.textContent === 'Quote'); if (quoteHeader) { const quoteContainer = quoteHeader.parentElement.nextElementSibling; if (quoteContainer && quoteContainer.getAttribute('role') === 'link') { const quoteAuthorEl = quoteContainer.querySelector('[data-testid="User-Name"]'); const quoteAuthor = quoteAuthorEl ? quoteAuthorEl.textContent.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim() : '未知作者'; const quoteTextEl = quoteContainer.querySelector('div[lang]'); const quoteText = extractTextContent(quoteTextEl); const quoteLines = `**${quoteAuthor}**: ${quoteText}`.split('\n'); quoteContent = `\n\n${quoteLines.map(line => `> ${line}`).join('\n> ')}`; } } let sharedLink = ''; const cardWrapper = tweet.querySelector('[data-testid="card.wrapper"]'); if (cardWrapper) { const cardLinkEl = cardWrapper.querySelector('a'); if(cardLinkEl) { const cardUrl = cardLinkEl.href; const detailContainer = cardWrapper.querySelector('[data-testid$="detail"]'); let cardTitle = ''; if (detailContainer) { const spans = detailContainer.querySelectorAll('span'); cardTitle = spans.length > 1 ? spans[1].textContent : '链接'; } else { const largeMediaTitleEl = cardWrapper.querySelector('div[class*="r-fdjqy7"] span'); cardTitle = largeMediaTitleEl ? largeMediaTitleEl.textContent : '链接'; } sharedLink = `\n- **分享链接**: [${cardTitle.trim()}](${cardUrl})`; } } const socialContext = tweet.querySelector('[data-testid="socialContext"]'); let repostedBy = ''; if (socialContext && socialContext.textContent.toLowerCase().includes('reposted')) { repostedBy = `> *由 ${socialContext.textContent.replace(/reposted/i, '').trim()} 转推*\n\n`; } let threadIndicator = ''; const hasThreadLink = Array.from(tweet.querySelectorAll('a[role="link"] span')).some(span => span.textContent === 'Show this thread'); if (hasThreadLink) { threadIndicator = `- **串推**: 是\n`; } let markdown = `${repostedBy}- **原文链接**: ${tweetUrl}\n`; markdown += `- **作者**: ${authorHandle}\n`; markdown += `- **发布时间**: ${postTime}\n`; markdown += threadIndicator; markdown += `- **推文内容**:\n${mainContent}${quoteContent}`; markdown += sharedLink; return { url: tweetUrl, markdown: markdown }; } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址