您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a little button to summarize articles, news, and similar content using the OpenAI API (gpt-4o-mini model). The button only appears on pages detected as articles or news. The summary is displayed in a responsive overlay with a loading effect and error handling.
当前为
// ==UserScript== // @name Summarize with AI // @namespace https://github.com/insign/summarize-with-ai // @version 2024.09.19.11.18 // @description Adds a little button to summarize articles, news, and similar content using the OpenAI API (gpt-4o-mini model). The button only appears on pages detected as articles or news. The summary is displayed in a responsive overlay with a loading effect and error handling. // @author Hélio <[email protected]> // @license WTFPL // @match *://*/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect api.openai.com // ==/UserScript== (function() { 'use strict'; // Check if the current page is an article or news content if (!isArticlePage()) { return; } // Add the "S" button to the page addSummarizeButton(); /*** Function Definitions ***/ // Function to determine if the page is an article function isArticlePage() { // Check for <article> element if (document.querySelector('article')) { return true; } // Check for Open Graph meta tag const ogType = document.querySelector('meta[property="og:type"]'); if (ogType && ogType.content === 'article') { return true; } // Check for news content in the URL const url = window.location.href; if (/news|article|story|post/i.test(url)) { return true; } // Check for significant text content (e.g., more than 500 words) const bodyText = document.body.innerText || ""; const wordCount = bodyText.split(/\s+/).length; if (wordCount > 500) { return true; } return false; } // Function to add the summarize button function addSummarizeButton() { // Create the button element const button = document.createElement('div'); button.id = 'summarize-button'; button.innerText = 'S'; document.body.appendChild(button); // Add event listeners button.addEventListener('click', onSummarizeClick); button.addEventListener('dblclick', onApiKeyReset); // Add styles GM_addStyle(` #summarize-button { position: fixed; bottom: 20px; right: 20px; width: 50px; height: 50px; background-color: #007bff; color: white; font-size: 24px; font-weight: bold; text-align: center; line-height: 50px; border-radius: 50%; cursor: pointer; z-index: 10000; box-shadow: 0 2px 5px rgba(0,0,0,0.3); } #summarize-overlay { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; z-index: 10001; padding: 20px; box-shadow: 0 0 10px rgba(0,0,0,0.5); overflow: auto; } #summarize-overlay h2 { margin-top: 0; } #summarize-close { position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 22px; } #summarize-loading { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(45deg, #007bff, #00ff6a, #007bff); background-size: 600% 600%; animation: GradientAnimation 3s ease infinite; z-index: 10000; display: flex; align-items: center; justify-content: center; flex-direction: column; color: white; font-size: 24px; } @keyframes GradientAnimation { 0%{background-position:0% 50%} 50%{background-position:100% 50%} 100%{background-position:0% 50%} } #summarize-cancel { margin-top: 20px; padding: 10px 20px; background-color: rgba(0,0,0,0.3); border: none; color: white; font-size: 18px; cursor: pointer; } #summarize-error { position: fixed; bottom: 20px; left: 20px; background-color: rgba(255,0,0,0.8); color: white; padding: 10px 20px; border-radius: 5px; z-index: 10002; } @media (max-width: 768px) { #summarize-overlay { width: 90%; height: 90%; } } @media (min-width: 769px) { #summarize-overlay { width: 60%; height: 85%; } } `); } // Handler for clicking the "S" button function onSummarizeClick() { const apiKey = getApiKey(); if (!apiKey) { return; } // Capture page source const pageContent = document.documentElement.outerHTML; // Show loading overlay showLoadingOverlay(); // Send content to OpenAI API summarizeContent(apiKey, pageContent); } // Handler for resetting the API key function onApiKeyReset() { const newKey = prompt('Please enter your OpenAI API key:', ''); if (newKey) { localStorage.setItem('openai_api_key', newKey.trim()); alert('API key updated successfully.'); } } // Function to get the API key function getApiKey() { let apiKey = localStorage.getItem('openai_api_key'); if (!apiKey) { apiKey = prompt('Please enter your OpenAI API key:', ''); if (apiKey) { localStorage.setItem('openai_api_key', apiKey.trim()); } else { alert('API key is required to generate a summary.'); return null; } } return apiKey.trim(); } // Function to show the loading overlay with animation function showLoadingOverlay() { // Create the loading overlay const loadingDiv = document.createElement('div'); loadingDiv.id = 'summarize-loading'; loadingDiv.innerHTML = ` <div>Generating summary...</div> <button id="summarize-cancel">Cancel</button> `; document.body.appendChild(loadingDiv); // Add event listener for cancel button document.getElementById('summarize-cancel').addEventListener('click', onCancelRequest); } // Handler to cancel the API request function onCancelRequest() { if (xhrRequest) { xhrRequest.abort(); removeLoadingOverlay(); } } // Function to remove the loading overlay function removeLoadingOverlay() { const loadingDiv = document.getElementById('summarize-loading'); if (loadingDiv) { loadingDiv.remove(); } } // Function to display the summary in an overlay function showSummaryOverlay(summaryText) { // Create the overlay const overlay = document.createElement('div'); overlay.id = 'summarize-overlay'; overlay.innerHTML = ` <div id="summarize-close">×</div> <div>${summaryText}</div> `; document.body.appendChild(overlay); // Add event listener for close button document.getElementById('summarize-close').addEventListener('click', () => { overlay.remove(); }); } // Function to display an error notification function showErrorNotification(message) { const errorDiv = document.createElement('div'); errorDiv.id = 'summarize-error'; errorDiv.innerText = message; document.body.appendChild(errorDiv); // Remove the notification after 2 seconds setTimeout(() => { errorDiv.remove(); }, 2000); } // Variable to hold the XMLHttpRequest for cancellation let xhrRequest = null; // Function to summarize the content using OpenAI API function summarizeContent(apiKey, content) { const userLanguage = navigator.language; // Prepare the API request const apiUrl = 'https://api.openai.com/v1/chat/completions'; const requestData = { model: 'gpt-4o-mini', messages: [ { role: 'system', content: `You are a helpful assistant that summarizes articles based on the HTML content provided. And gives a concise summary of the article, add a short introduction and a short conclusion, in the middle list topics but instead of bullet points use the most appropriate emoji to indicate the topic. Always use HTML tags to structure the summary. Besides the article language, always use the user language which is ${userLanguage}. Never use markdown, always send send the raw html ready to be injected in the target. Use h2 for the summary title. Do not add texts like "Intruduction", "Conclusion", "Summary", "Topics", etc before the text content. Instead be direct: Title, Short Introduction, Topics, Short Conclusion` }, { role: 'user', content: `Page content: \n\n${content}` } ], max_tokens: 500, temperature: 0.5, n: 1, stream: false }; // Send the request using GM_xmlhttpRequest xhrRequest = GM_xmlhttpRequest({ method: 'POST', url: apiUrl, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, data: JSON.stringify(requestData), onload: function(response) { removeLoadingOverlay(); if (response.status === 200) { const resData = JSON.parse(response.responseText); const summary = resData.choices[0].message.content; showSummaryOverlay(summary); } else { showErrorNotification('Error: Failed to retrieve summary.'); } }, onerror: function() { removeLoadingOverlay(); showErrorNotification('Error: Network error.'); }, onabort: function() { removeLoadingOverlay(); showErrorNotification('Request canceled.'); } }); } })();