Linux Do Summary

Add button to summarize and toggle content of the main post

От 04.04.2024. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Linux Do Summary
// @namespace    http://tampermonkey.net/
// @version      1.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';

    // 定义请求格式
    const BASE_URL = "https://api.openai.com/v1/chat/completions";
    const API_KEY = "Your_API_Key";
    const MODEL = "gpt-4-all";
    const PROMPT = "以下是linu.do论坛的一个主题,帮我用中文梳理总结:";

    let originalContent = ''; // 存储原始内容
    let toggled = false; // 切换状态

    // 提取并格式化主帖内容
    function extractAndFormat() {
        console.log('提取并格式化内容...');
        var postStreamElement = document.querySelector('div.post-stream');
        if (postStreamElement && postStreamElement.querySelector('#post_1')) {
            var articleElement = postStreamElement.querySelector('#post_1');
            if (articleElement) {
                var cookedDiv = articleElement.querySelector('.cooked');
                if (cookedDiv) {
                    var elementsData = [];
                    var index = 0;

                    // 提取并存储所有img、p和li元素,将li转换为p
                    Array.from(cookedDiv.querySelectorAll('img, p, li')).forEach(node => {
                        var tagName = node.tagName.toLowerCase() === 'li' ? 'p' : node.tagName.toLowerCase(); // 将li转换为p
                        var textContent = node.textContent.trim();
                        var src = tagName === 'img' ? node.getAttribute('src')?.trim() : null;

                        if (tagName === 'p' && textContent.includes('\n')) {
                            var contents = textContent.split(/\n+/).map(line => line.trim()).filter(line => line.length > 0);
                            elementsData.push({ index, tagName, textContent: contents[0], src });
                            index++;
                            for (var i = 1; i < contents.length; i++) {
                                elementsData.push({ index, tagName, textContent: contents[i], src });
                                index++;
                            }
                        } else {
                            elementsData.push({ index, tagName, textContent, src });
                            index++;
                        }
                    });

                    // 过滤掉不必要的元素和重复项
                    var cleanedElementsData = elementsData.filter(({ tagName, textContent }) => tagName !== 'p' || textContent.length > 1);
                    var uniqueElementsData = [];
                    var uniqueTextContents = new Set();
                    cleanedElementsData.forEach(({ tagName, textContent, src }) => {
                        var contentKey = `${tagName}_${textContent}_${src}`;
                        if (tagName === 'img' || !uniqueTextContents.has(contentKey)) {
                            uniqueElementsData.push({ tagName, textContent, src });
                            uniqueTextContents.add(contentKey);
                        }
                    });

                    // 转换为HTML
                    var 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; // 返回最终的HTML字符串
                }
            }
        }
        return '';
    }

    // 发送内容到API
    function sendToAPI(textContent, callback) {
        console.log('向API发送内容...');
        var xhr = new XMLHttpRequest();
        xhr.open("POST", BASE_URL);
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.setRequestHeader("Authorization", `Bearer ${API_KEY}`);
        xhr.onload = function() {
            if (xhr.status === 200) {
                var jsonResponse = JSON.parse(xhr.responseText);
                if(jsonResponse && jsonResponse.choices && jsonResponse.choices[0] && jsonResponse.choices[0].message) {
                    callback(jsonResponse.choices[0].message.content);
                }
            }
        };
        xhr.onerror = function() {
            console.error('错误:', xhr.statusText);
            callback('');
        };
        xhr.send(JSON.stringify({ model: MODEL, messages: [{ role: "user", content: PROMPT + textContent }] }));
    }

    // 格式化从API接收到的内容
    function formatContent(text) {
        console.log('格式化内容...');
        // 处理换行
        text = text.replace(/\n/g, '<br>');

        // 处理加粗
        text = text.replace(/\*\*\*\*(.*?)\*\*\*\*/g, '<strong>$1</strong>');

        // 处理标题
        text = text.replace(/^(#{1,6})\s(.*?)<br>/gm, function(match, p1, p2) {
            const level = p1.length;
            return `<h${level}>${p2}</h${level}><br>`;
        });

        // 处理列表
        text = text.replace(/- (.*?)<br>/g, '<li>$1</li><br>');
        text = text.replace(/<li>(.*?)<\/li><br><br>/g, '<ul><li>$1</li></ul><br>');

        return text;
    }

    // 检查是否存在post_1元素和按钮
    function checkAndAddButton() {
        console.log('检查帖子元素和按钮...');
        const postElement = document.querySelector('#post_1');
        const buttonExists = document.querySelector('#summaryToggleButton');
        if (postElement && !buttonExists) {
            addButtonAndProcessData();
        }
    }

    // 添加总结按钮并附加事件处理程序
    function addButtonAndProcessData() {
        console.log('添加按钮并处理数据...');
        const controlsContainer = document.querySelector('nav.post-controls');
        if (controlsContainer) {
            const newButton = document.createElement('button');
            newButton.textContent = '总结';
            newButton.id = 'summaryToggleButton'; // 给按钮定义id
            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);

            // 初始化状态
            originalContent = '';
            toggled = false;

            newButton.addEventListener('click', function() {
                console.log('按钮点击...');
                const cookedContent = document.querySelector('div.cooked');
                if (cookedContent) {
                    if (!toggled) {
                        originalContent = cookedContent.innerHTML;
                        const textContent = extractAndFormat();
                        sendToAPI(textContent, function(summary) {
                            console.log('从API接收到摘要...');
                            cookedContent.innerHTML = formatContent(summary) || '内容加载中...';
                            window.scrollTo(0, 0);
                        });
                    } else {
                        cookedContent.innerHTML = originalContent;
                    }
                    toggled = !toggled;
                }
            });
        }
    }

    // 持续检查帖子元素和按钮的存在性
    setInterval(checkAndAddButton, 5000); // 每5秒检查一次是否存在post_1和按钮
})();