Last active
June 4, 2024 18:45
-
-
Save wardboumans/b523647ac016579d8e6b52a67f73c377 to your computer and use it in GitHub Desktop.
OpenAI summarization button for Inoreader.com rssreader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name AI Summary for Inoreader | |
// @namespace http://tampermonkey.net/ | |
// @version 1.4 | |
// @description Adds a button on Inoreader and summarizes the article with OpenAI | |
// @author Ward Boumans | |
// @match https://www.inoreader.com/article/* | |
// @grant none | |
// @require https://cdn.jsdelivr.net/npm/marked/marked.min.js | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const apiKey = "<APIKEY>"; | |
// Function to create the new button | |
function createButton() { | |
const newButton = document.createElement('div'); | |
newButton.className = 'article_footer_buttons star_img icon16 icon-bubble'; | |
newButton.title = 'Summarize with AI (Hotkey: C)'; | |
newButton.id = 'custom_button'; | |
newButton.addEventListener('click', async function() { | |
const article = document.getElementsByClassName("article_content"); | |
if (article.length === 1) { | |
const text = article[0].innerText; | |
const summaryDiv = document.querySelector('.article_sub_title'); | |
if (summaryDiv) { | |
summaryDiv.classList.add("article_content"); | |
summaryDiv.classList.remove("flex"); | |
summaryDiv.classList.remove("article_sub_title"); | |
summaryDiv.textContent = '✨ working...'; | |
await summarizeArticle(text, summaryDiv); | |
} else { | |
alert('Could not find element with class "article_sub_title"'); | |
} | |
} | |
}); | |
return newButton; | |
} | |
async function summarizeArticle(text, summaryDiv) { | |
const controller = new AbortController(); | |
const signal = controller.signal; | |
try { | |
const response = await fetch("https://api.openai.com/v1/chat/completions", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
Authorization: `Bearer ${apiKey}`, | |
}, | |
body: JSON.stringify({ | |
model: "gpt-3.5-turbo", // Choose your desired model | |
messages: [ | |
{ role: "user", content: "Give short summary of the following text in 10 or less bullet points. Only output the summary without additional text: " + JSON.stringify(text) } | |
], | |
max_tokens: 150, // Adjust max summary length | |
stream: true, // Enable streaming | |
}), | |
signal, | |
}); | |
if (!response.ok) { | |
throw new Error(`API Error: ${response.status}`); | |
} | |
const decoder = new TextDecoder(); | |
var buffer = '✨ Summary:\n\n'; | |
// Iterate through the chunks in the response body using for-await...of | |
for await (const chunk of response.body) { | |
const decodedChunk = decoder.decode(chunk); | |
// Clean up the data | |
const lines = decodedChunk | |
.split("\n") | |
.map((line) => line.replace("data: ", "")) | |
.filter((line) => line.length > 0) | |
.filter((line) => line !== "[DONE]") | |
.map((line) => JSON.parse(line)); | |
for (const line of lines) { | |
const { | |
choices: [ | |
{ | |
delta: { content }, | |
}, | |
], | |
} = line; | |
if (content) { | |
buffer += content; | |
summaryDiv.innerHTML = marked.parse(buffer); | |
} | |
} | |
} | |
} | |
catch (error) { | |
console.error("Error:", error); | |
summaryDiv.textContent = "Error: Something went wrong with the summarization."; | |
} finally { | |
controller.abort(); | |
} | |
} | |
// Function to append the button to the toolbar | |
function appendButtonToToolbar() { | |
const toolbar = document.querySelector('.article_footer_main_buttons'); | |
if (toolbar && !document.querySelector('#custom_button')) { | |
const newButton = createButton(); | |
toolbar.appendChild(newButton); | |
} | |
} | |
// Function to check if the toolbar is available and append the button | |
function checkAndAddButton() { | |
const toolbar = document.querySelector('.article_footer_main_buttons'); | |
if (toolbar) { | |
appendButtonToToolbar(); | |
} | |
} | |
// Initial check | |
checkAndAddButton(); | |
// Observe changes to the DOM for SPA navigation | |
const observer = new MutationObserver((mutations) => { | |
mutations.forEach((mutation) => { | |
if (mutation.addedNodes.length || mutation.removedNodes.length) { | |
checkAndAddButton(); | |
} | |
}); | |
}); | |
// Start observing the body for added nodes | |
observer.observe(document.body, { childList: true, subtree: true }); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment