您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Утилита, которая сокращает текст темы, экономя ваше время на прочтение.
当前为
// ==UserScript== // @name LZT Article Summarizer // @namespace http://tampermonkey.net/ // @version 0.1 // @description Утилита, которая сокращает текст темы, экономя ваше время на прочтение. // @author @planetus (lolz) // @match https://lolz.live/threads/* // @match https://zelenka.guru/threads/* // @match https://lolz.guru/threads/* // @icon https://www.google.com/s2/favicons?sz=64&domain=zelenka.guru // @license MIT // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; const defaultSettings = { minTextLength: 200, maxSummaryLength: 500, maxKeyPoints: 5, buttonColor: '#228e5d', summaryBgColor: '#091e15', summaryTextColor: '#ecf0f1', animationSpeed: 300 }; let settings = Object.assign({}, defaultSettings, GM_getValue('lztSummarizerSettings', {})); function saveSettings() { GM_setValue('lztSummarizerSettings', settings); updateStyles(); } function clearSettings() { GM_setValue('lztSummarizerSettings', {}); settings = Object.assign({}, defaultSettings); updateStyles(); } function createSettingsMenu() { const settingsHTML = ` <div id="lzt-settings-overlay" class="lzt-settings-overlay"> <div id="lzt-settings" class="lzt-settings"> <h2 class="lzt-settings-title">Настройки LZT Summarizer</h2> <div class="lzt-settings-group"> <label for="minTextLength">Минимальная длина текста для резюме:</label> <input type="number" id="minTextLength" value="${settings.minTextLength}"> </div> <div class="lzt-settings-group"> <label for="maxSummaryLength">Максимальная длина резюме:</label> <input type="number" id="maxSummaryLength" value="${settings.maxSummaryLength}"> </div> <div class="lzt-settings-group"> <label for="maxKeyPoints">Максимальное количество ключевых моментов:</label> <input type="number" id="maxKeyPoints" value="${settings.maxKeyPoints}"> </div> <div class="lzt-settings-group"> <label for="buttonColor">Цвет кнопки:</label> <input type="color" id="buttonColor" value="${settings.buttonColor}"> </div> <div class="lzt-settings-group"> <label for="summaryBgColor">Цвет фона резюме:</label> <input type="color" id="summaryBgColor" value="${settings.summaryBgColor}"> </div> <div class="lzt-settings-group"> <label for="summaryTextColor">Цвет текста резюме:</label> <input type="color" id="summaryTextColor" value="${settings.summaryTextColor}"> </div> <div class="lzt-settings-group"> <label for="animationSpeed">Скорость анимации (мс):</label> <input type="number" id="animationSpeed" value="${settings.animationSpeed}"> </div> <div class="lzt-settings-buttons"> <button id="saveSettings" class="lzt-settings-button lzt-settings-save">Сохранить</button> <button id="clearSettings" class="lzt-settings-button lzt-settings-clear">Очистить всё</button> <button id="closeSettings" class="lzt-settings-button lzt-settings-close">Закрыть</button> </div> </div> </div> `; const settingsDiv = document.createElement('div'); settingsDiv.innerHTML = settingsHTML; document.body.appendChild(settingsDiv); const overlay = document.getElementById('lzt-settings-overlay'); const settingsPanel = document.getElementById('lzt-settings'); setTimeout(() => { overlay.style.opacity = '1'; settingsPanel.style.transform = 'translate(-50%, -50%) scale(1)'; }, 50); document.getElementById('saveSettings').addEventListener('click', function() { settings.minTextLength = parseInt(document.getElementById('minTextLength').value); settings.maxSummaryLength = parseInt(document.getElementById('maxSummaryLength').value); settings.maxKeyPoints = parseInt(document.getElementById('maxKeyPoints').value); settings.buttonColor = document.getElementById('buttonColor').value; settings.summaryBgColor = document.getElementById('summaryBgColor').value; settings.summaryTextColor = document.getElementById('summaryTextColor').value; settings.animationSpeed = parseInt(document.getElementById('animationSpeed').value); saveSettings(); closeSettingsMenu(); }); document.getElementById('clearSettings').addEventListener('click', function() { if (confirm('Вы уверены, что хотите сбросить все настройки?')) { clearSettings(); closeSettingsMenu(); alert('Настройки сброшены до значений по умолчанию.'); } }); document.getElementById('closeSettings').addEventListener('click', closeSettingsMenu); function closeSettingsMenu() { overlay.style.opacity = '0'; settingsPanel.style.transform = 'translate(-50%, -50%) scale(0.9)'; setTimeout(() => { settingsDiv.remove(); }, 300); } } function updateStyles() { GM_addStyle(` .lzt-summary-button { background: linear-gradient(45deg, ${settings.buttonColor}, ${lightenDarkenColor(settings.buttonColor, -20)}); border: none; color: white; padding: 10px 15px; text-align: center; text-decoration: none; display: inline-block; font-size: 14px; user-select: none; margin: 10px 0; cursor: pointer; border-radius: 4px; transition: all ${settings.animationSpeed}ms ease; box-shadow: 0 2px 4px rgba(0,0,0,0.1); animation: fadeInButton ${settings.animationSpeed}ms ease-out; } .lzt-summary-button:hover { background: linear-gradient(45deg, ${lightenDarkenColor(settings.buttonColor, -20)}, ${lightenDarkenColor(settings.buttonColor, -40)}); transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); } .lzt-summary-button:active { transform: translateY(0); } .lzt-summary { position: relative; z-index: 99; background-color: ${settings.summaryBgColor}; border-left: 4px solid ${settings.buttonColor}; color: ${settings.summaryTextColor}; padding: 20px; margin: 15px 0; border-radius: 8px; font-size: 14px; line-height: 1.6; animation: fadeIn ${settings.animationSpeed}ms ease-out; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } @keyframes fadeInButton { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .lzt-summary-title { color: ${settings.buttonColor}; font-weight: bold; margin-bottom: 15px; font-size: 18px; } .lzt-summary-content { margin-top: 10px; } .lzt-key-points { margin-top: 20px; padding-top: 15px; border-top: 1px solid rgba(255,255,255,0.2); } .lzt-key-points-title { color: ${settings.buttonColor}; font-weight: bold; margin-bottom: 10px; } .lzt-key-point { margin: 8px 0; padding-left: 20px; position: relative; } .lzt-key-point:before { content: "•"; color: ${settings.buttonColor}; position: absolute; left: 0; font-size: 18px; } .lzt-popup { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.9); background-color: ${settings.summaryBgColor}; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 999999; width: 90%; max-width: 800px; max-height: 90vh; overflow-y: auto; opacity: 0; transition: all ${settings.animationSpeed}ms ease; } .lzt-popup.show { opacity: 1; transform: translate(-50%, -50%) scale(1); } .lzt-popup-close { position: absolute; top: 15px; right: 20px; font-size: 28px; color: ${settings.summaryTextColor}; cursor: pointer; transition: color ${settings.animationSpeed}ms ease; } .lzt-popup-close:hover { color: ${settings.buttonColor}; } .lzt-original-content { transition: opacity ${settings.animationSpeed}ms ease, height ${settings.animationSpeed}ms ease; overflow: hidden; } .lzt-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.7); backdrop-filter: blur(8px); z-index: 99998; opacity: 0; transition: opacity ${settings.animationSpeed}ms ease; } .lzt-overlay.show { opacity: 1; } .lzt-settings-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.8); z-index: 10000; display: flex; justify-content: center; align-items: center; opacity: 0; backdrop-filter: blur(8px); transition: opacity ${settings.animationSpeed}ms ease; } .lzt-settings { background-color: #303030b0; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); width: 90%; max-width: 500px; transform: translate(-50%, -50%) scale(0.9); transition: transform ${settings.animationSpeed}ms ease; position: fixed; top: 50%; left: 50%; } .lzt-settings-title { color: #ecf0f1; font-size: 24px; margin-bottom: 20px; text-align: center; } .lzt-settings-group { margin-bottom: 15px; } .lzt-settings-group label { display: block; color: #bdc3c7; margin-bottom: 5px; } .lzt-settings-group input { width: 100%; padding: 8px; border: none; border-radius: 4px; background-color: #303030; color: #ecf0f1; font-size: 14px; } .lzt-settings-group input[type="color"] { height: 40px; cursor: pointer; } .lzt-settings-buttons { display: flex; gap: 5px; justify-content: space-between; margin-top: 20px; } .lzt-settings-button { padding: 10px 20px; border: none; border-radius: 4px; font-size: 15px; cursor: pointer; transition: background-color ${settings.animationSpeed}ms ease; } .lzt-settings-save { display: block; width: -webkit-fill-available; background-color: #2ecc7161; color: white; } .lzt-settings-save:hover { background-color: #27ae60; } .lzt-settings-clear { display: block; width: -webkit-fill-available; background-color: #f39c1291; color: white; } .lzt-settings-clear:hover { background-color: #d35400; } .lzt-settings-close { display: block; width: -webkit-fill-available; background-color: #e74c3cde; color: white; } .lzt-settings-close:hover { background-color: #c0392b; } `); } function lightenDarkenColor(col, amt) { let usePound = false; if (col[0] == "#") { col = col.slice(1); usePound = true; } let num = parseInt(col,16); let r = (num >> 16) + amt; if (r > 255) r = 255; else if (r < 0) r = 0; let b = ((num >> 8) & 0x00FF) + amt; if (b > 255) b = 255; else if (b < 0) b = 0; let g = (num & 0x0000FF) + amt; if (g > 255) g = 255; else if (g < 0) g = 0; return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16).padStart(6, '0'); } function extractKeyPoints(text) { const sentences = text.match(/[^\.!\?]+[\.!\?]+/g) || []; const keyPoints = []; const indicators = [ 'важно', 'главное', 'необходимо', 'следует', 'нужно', 'основной', 'ключевой', 'рекомендуется', 'обратите внимание', 'в первую очередь', 'самое важное', 'ключевая идея', 'существенно', 'критически', 'принципиально', 'фундаментально', 'приоритетно' ]; sentences.forEach(sentence => { const lowercaseSentence = sentence.toLowerCase(); if (indicators.some(indicator => lowercaseSentence.includes(indicator)) || (sentence.length > 30 && sentence.length < 200 && /[0-9]/.test(sentence)) || /^[•\-]/.test(sentence.trim())) { keyPoints.push(sentence.trim()); } }); const startIndicators = ['ключевые моменты', 'основные пункты', 'главные идеи']; let foundKeyPointSection = false; sentences.forEach(sentence => { const lowercaseSentence = sentence.toLowerCase().trim(); if (startIndicators.some(indicator => lowercaseSentence.startsWith(indicator))) { foundKeyPointSection = true; } if (foundKeyPointSection && /^[•\-]/.test(sentence.trim())) { keyPoints.push(sentence.trim()); } }); return [...new Set(keyPoints)].slice(0, settings.maxKeyPoints); } function summarizeText(text) { text = text.replace(/\s+/g, ' ').trim(); const sentences = text.match(/[^\.!\?]+[\.!\?]+/g) || []; if (sentences.length === 0) { return ''; } const sentenceScores = sentences.map((sentence, index) => { const words = sentence.toLowerCase().split(' '); let score = 0; const importantWords = ['важно', 'главное', 'необходимо', 'ключевой', 'существенно', 'критически', 'спонсор', 'проект']; if (words.some(word => importantWords.includes(word))) { score += 5; } if (sentence.length > 30 && sentence.length < 200) { score += 2; } if (/[0-9]/.test(sentence)) { score += 2; } if (index < 5) { score += 3; } if (index > 0) { const prevSentence = sentences[index - 1].toLowerCase(); const commonWords = words.filter(word => prevSentence.includes(word) && word.length > 3); score += commonWords.length; } return { sentence, score, index }; }); sentenceScores.sort((a, b) => b.score - a.score); let summary = ''; let currentLength = 0; let usedIndexes = new Set(); for (const { sentence, index } of sentenceScores) { if (currentLength + sentence.length <= settings.maxSummaryLength && !usedIndexes.has(index)) { if (summary && Math.abs(index - Array.from(usedIndexes).pop()) > 3) { continue; } summary += sentence + ' '; currentLength += sentence.length; usedIndexes.add(index); } if (currentLength >= settings.maxSummaryLength) break; } return summary.trim(); } function createPopup(content) { const overlay = document.createElement('div'); overlay.className = 'lzt-overlay'; document.body.appendChild(overlay); const popup = document.createElement('div'); popup.className = 'lzt-popup'; popup.innerHTML = ` <div class="lzt-popup-close">×</div> ${content} `; document.body.appendChild(popup); setTimeout(() => { overlay.classList.add('show'); popup.classList.add('show'); }, 50); const closePopup = () => { overlay.classList.remove('show'); popup.classList.remove('show'); setTimeout(() => { overlay.remove(); popup.remove(); }, settings.animationSpeed); }; popup.querySelector('.lzt-popup-close').addEventListener('click', closePopup); overlay.addEventListener('click', closePopup); } function getTextContent(element) { let text = ''; for (let node of element.childNodes) { if (node.nodeType === Node.TEXT_NODE) { text += node.textContent; } else if (node.nodeType === Node.ELEMENT_NODE && node.nodeName !== 'IMG' && !node.classList.contains('bbCodeBlock')) { text += getTextContent(node); } } return text; } function addSummaryButton(postElement) { if (!postElement.classList.contains('firstPost')) return; if (postElement.querySelector('.lzt-summary-button')) return; const contentElement = postElement.querySelector('.messageContent'); if (!contentElement) return; const fullText = getTextContent(contentElement); if (fullText.length < settings.minTextLength) return; const summaryButton = document.createElement('button'); summaryButton.textContent = '📝 Показать резюме'; summaryButton.className = 'lzt-summary-button'; summaryButton.style.opacity = '0'; let summaryElement = null; let isProcessing = false; summaryButton.addEventListener('click', function(e) { e.preventDefault(); if (isProcessing) return; isProcessing = true; if (summaryElement) { summaryElement.style.display = summaryElement.style.display === 'none' ? 'block' : 'none'; summaryButton.textContent = summaryElement.style.display === 'none' ? '📝 Показать резюме' : '❌ Скрыть резюме'; contentElement.style.height = summaryElement.style.display === 'none' ? 'auto' : '0'; contentElement.style.opacity = summaryElement.style.display === 'none' ? '1' : '0'; isProcessing = false; } else { const summary = summarizeText(fullText); const keyPoints = extractKeyPoints(fullText); summaryElement = document.createElement('div'); summaryElement.className = 'lzt-summary'; let summaryHTML = ` <div class="lzt-summary-title">📌 Краткое содержание</div> <div class="lzt-summary-content">${summary || 'Не удалось создать краткое содержание.'}</div> `; if (keyPoints.length > 0) { summaryHTML += ` <div class="lzt-key-points"> <div class="lzt-key-points-title">🔑 Ключевые моменты:</div> ${keyPoints.map(point => `<div class="lzt-key-point">${point}</div>`).join('')} </div> `; } else { summaryHTML += ` <div class="lzt-key-points"> <div class="lzt-key-points-title">🔑 Ключевые моменты:</div> <div class="lzt-key-point">Не удалось выделить ключевые моменты.</div> </div> `; } summaryElement.innerHTML = summaryHTML; contentElement.parentNode.insertBefore(summaryElement, contentElement.nextSibling); summaryButton.textContent = '❌ Скрыть резюме'; contentElement.style.height = '0'; contentElement.style.opacity = '0'; setTimeout(() => { isProcessing = false; }, settings.animationSpeed); } }); contentElement.parentNode.insertBefore(summaryButton, contentElement); setTimeout(() => { summaryButton.style.opacity = '1'; }, 100); } function summarizeMainArticle() { const firstPost = document.querySelector('li.message.firstPost'); if (!firstPost) { alert('Не удалось найти основную статью на странице.'); return; } const contentElement = firstPost.querySelector('.messageContent'); if (!contentElement) { alert('Не удалось найти содержимое статьи.'); return; } const fullText = getTextContent(contentElement); const summary = summarizeText(fullText); const keyPoints = extractKeyPoints(fullText); let popupContent = ` <div class="lzt-summary-title">📝 Краткое содержание статьи</div> <div class="lzt-summary-content">${summary || 'Не удалось создать краткое содержание.'}</div> `; if (keyPoints.length > 0) { popupContent += ` <div class="lzt-key-points"> <div class="lzt-key-points-title">🔑 Ключевые моменты:</div> ${keyPoints.map(point => `<div class="lzt-key-point">${point}</div>`).join('')} </div> `; } else { popupContent += ` <div class="lzt-key-points"> <div class="lzt-key-points-title">🔑 Ключевые моменты:</div> <div class="lzt-key-point">Не удалось выделить ключевые моменты.</div> </div> `; } createPopup(popupContent); } function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } GM_registerMenuCommand("LZT Резюме статьи", debounce(summarizeMainArticle, 300)); GM_registerMenuCommand("Настройки LZT Summarizer", debounce(createSettingsMenu, 300)); updateStyles(); window.addEventListener('load', function() { const firstPost = document.querySelector('li.message.firstPost'); if (firstPost) { addSummaryButton(firstPost); } }); const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList') { mutation.addedNodes.forEach(function(node) { if (node.nodeType === 1 && node.matches('li.message.firstPost')) { addSummaryButton(node); } }); } }); }); observer.observe(document.body, { childList: true, subtree: true }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址