您需要先安装一个扩展,例如 篡改猴、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.10.29 // @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: 18px; } #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> <h2>Summary</h2> <div>${summaryText.replace(/\n/g, '<br>')}</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) { // 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.' }, { role: 'user', content: `Please provide a concise summary of the following article, add a small introduction and conclusion, in the middle list topics but instead of bullet points use the most appropriate emoji to indicate the topic: \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.'); } }); } })();