您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add button to summarize and toggle content of the main post.
当前为
// ==UserScript== // @name Linux Do Summary // @namespace http://tampermonkey.net/ // @version 2.0 // @description Add button to summarize and toggle content of the main post. // @author Reno // @match https://linux.do/* // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function() { 'use strict'; let state = { originalContent: '', toggled: false }; async function fetchSummary(textContent) { const BASE_URL = window.localStorage.getItem('base_url'); const API_KEY = window.localStorage.getItem('apikey'); const MODEL = window.localStorage.getItem('model'); const PROMPT = "以下是linux.do论坛的一个主题,帮我用中文简明扼要地梳理总结:"; const headers = { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }; const body = JSON.stringify({ model: MODEL, messages: [{ role: "user", content: PROMPT + textContent }] }); try { const response = await fetch(BASE_URL, { method: "POST", headers, body }); if (!response.ok) throw new Error(`API请求失败,状态码: ${response.status}`); const jsonResponse = await response.json(); if (jsonResponse && jsonResponse.choices && jsonResponse.choices[0] && jsonResponse.choices[0].message) { return jsonResponse.choices[0].message.content; } else { throw new Error('API响应无效或格式不正确'); } } catch (error) { console.error(error.message); alert("无法加载摘要,请稍后再试。错误详情: " + error.message); return null; } } function formatContentFromAPI(text) { if (!text) return '无法加载摘要。'; text = text.replace(/\n/g, '<br>').replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>'); return text.replace(/^(#{1,6})\s(.*?)<br>/gm, (match, p1, p2) => `<h${p1.length}>${p2}</h${p1.length}><br>`) .replace(/- (.*?)<br>/g, '<li>$1</li><br>').replace(/<li>(.*?)<\/li><br><br>/g, '<ul><li>$1</li></ul><br>'); } function extractAndFormatContent() { let postStreamElement = document.querySelector('div.post-stream'); if (postStreamElement && postStreamElement.querySelector('#post_1')) { let articleElement = postStreamElement.querySelector('#post_1'); if (articleElement) { let cookedDiv = articleElement.querySelector('.cooked'); if (cookedDiv) { let elementsData = []; let index = 0; Array.from(cookedDiv.querySelectorAll('img, p, li')).forEach(node => { let tagName = node.tagName.toLowerCase() === 'li' ? 'p' : node.tagName.toLowerCase(); let textContent = node.textContent.trim(); let src = tagName === 'img' ? node.getAttribute('src')?.trim() : null; if (tagName === 'p' && textContent.includes('\n')) { let contents = textContent.split(/\n+/).map(line => line.trim()).filter(line => line.length > 0); elementsData.push({ index, tagName, textContent: contents[0], src }); index++; for (let i = 1; i < contents.length; i++) { elementsData.push({ index, tagName, textContent: contents[i], src }); index++; } } else { elementsData.push({ index, tagName, textContent, src }); index++; } }); let cleanedElementsData = elementsData.filter(({ tagName, textContent }) => tagName !== 'p' || textContent.length > 1); let uniqueElementsData = []; let uniqueTextContents = new Set(); cleanedElementsData.forEach(({ tagName, textContent, src }) => { let contentKey = `${tagName}_${textContent}_${src}`; if (!uniqueTextContents.has(contentKey)) { uniqueElementsData.push({ tagName, textContent, src }); uniqueTextContents.add(contentKey); } }); let htmlContent = ""; uniqueElementsData.forEach(({ tagName, textContent, src }) => { if (tagName === 'p') { htmlContent += `<p>${textContent}</p>`; } else if (tagName === 'img') { htmlContent += `<img src="${src}" alt="${textContent}">`; } }); return htmlContent; } } } return ''; } async function toggleSummary() { const summaryToggleButton = document.getElementById('summaryToggleButton'); const cookedContent = document.querySelector('div.cooked'); if (!state.toggled && cookedContent) { let countdown = 15; summaryToggleButton.disabled = true; summaryToggleButton.style.backgroundColor = '#808080'; summaryToggleButton.textContent = `${countdown} 秒`; const countdownInterval = setInterval(() => { countdown--; summaryToggleButton.textContent = `${countdown} 秒`; if (countdown <= 0) { clearInterval(countdownInterval); summaryToggleButton.textContent = '原文'; } }, 1000); const textContent = extractAndFormatContent(); const summary = await fetchSummary(textContent); clearInterval(countdownInterval); if (summary) { state.originalContent = cookedContent.innerHTML; cookedContent.innerHTML = formatContentFromAPI(summary); window.scrollTo(0, 0); summaryToggleButton.textContent = '原文'; summaryToggleButton.style.backgroundColor = '#007bff'; state.toggled = true; } else { summaryToggleButton.textContent = '重试'; } summaryToggleButton.disabled = false; } else if (state.toggled && cookedContent) { cookedContent.innerHTML = state.originalContent; summaryToggleButton.textContent = '总结'; window.scrollTo(0, 0); summaryToggleButton.style.backgroundColor = '#4CAF50'; state.toggled = false; } } function addButtonAndEventListener() { const controlsContainer = document.querySelector('nav.post-controls'); if (controlsContainer && !document.querySelector('#summaryToggleButton')) { state.originalContent = ''; state.toggled = false; const newButton = document.createElement('button'); newButton.textContent = '总结'; newButton.id = 'summaryToggleButton'; newButton.style.cssText = 'margin-left: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; padding: 5px 10px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; cursor: pointer; transition-duration: 0.4s;'; controlsContainer.appendChild(newButton); newButton.addEventListener('click', toggleSummary); } } function observeDOMChanges() { const targetNode = document.querySelector('body'); const config = { childList: true, subtree: true }; const callback = function(mutationsList, observer) { for(const mutation of mutationsList) { if (mutation.type === 'childList') { addButtonAndEventListener(); break; } } }; const observer = new MutationObserver(callback); if (targetNode) { observer.observe(targetNode, config); } } observeDOMChanges(); })();