Created
November 16, 2025 08:53
-
-
Save Amitro123/e0b0bf1af59deae7af1f632d2e6f6142 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| // Paste your API key here | |
| const API_KEY = ""; | |
| const DOCUMENT_URL = ""; | |
| const DAILY_RUN_HOUR = 8; | |
| const GMAIL_SEARCH_QUERY = "(AI OR machine learning OR agents OR model) is:unread"; | |
| function setupDailyTrigger() { | |
| var triggers = ScriptApp.getProjectTriggers(); | |
| triggers.forEach(function(trigger) { | |
| if (trigger.getHandlerFunction() === 'analyzeAndSummarizeNewsletters') { | |
| ScriptApp.deleteTrigger(trigger); | |
| } | |
| }); | |
| ScriptApp.newTrigger('analyzeAndSummarizeNewsletters') | |
| .timeBased() | |
| .atHour(DAILY_RUN_HOUR) | |
| .everyDays(1) | |
| .create(); | |
| Logger.log("✓ Daily trigger set up successfully!"); | |
| } | |
| function removeDailyTrigger() { | |
| var triggers = ScriptApp.getProjectTriggers(); | |
| triggers.forEach(function(trigger) { | |
| if (trigger.getHandlerFunction() === 'analyzeAndSummarizeNewsletters') { | |
| ScriptApp.deleteTrigger(trigger); | |
| } | |
| }); | |
| Logger.log("✓ Daily trigger removed successfully!"); | |
| } | |
| function analyzeAndSummarizeNewsletters() { | |
| var doc = DocumentApp.openByUrl(DOCUMENT_URL); | |
| var body = doc.getBody(); | |
| if (body.getText() === "") { | |
| body.appendParagraph("AI Newsletter Summaries").setHeading(DocumentApp.ParagraphHeading.TITLE); | |
| } | |
| var threads = GmailApp.search(GMAIL_SEARCH_QUERY); | |
| var summaries = []; | |
| threads.forEach(function(thread) { | |
| try { | |
| var message = thread.getMessages()[thread.getMessageCount() - 1]; | |
| var htmlBody = message.getBody(); | |
| var plainBody = stripHtmlTags(htmlBody); | |
| if (!plainBody || plainBody.trim().length < 100) { | |
| plainBody = message.getPlainBody(); | |
| } | |
| var summary = getGeminiSummary(plainBody, API_KEY); | |
| var links = extractLinks(htmlBody); | |
| var sender = message.getFrom(); | |
| var subject = message.getSubject(); | |
| var date = message.getDate(); | |
| summaries.push({ | |
| subject: subject, | |
| sender: sender, | |
| date: date, | |
| summary: summary, | |
| links: links | |
| }); | |
| thread.markRead(); | |
| } catch (e) { | |
| Logger.log("Error processing email: " + e); | |
| } | |
| }); | |
| summaries.forEach(function(item) { | |
| body.appendHorizontalRule(); | |
| body.appendParagraph(item.subject).setHeading(DocumentApp.ParagraphHeading.HEADING1); | |
| body.appendParagraph("From: " + item.sender + " | Date: " + item.date.toLocaleString()).setHeading(DocumentApp.ParagraphHeading.HEADING3); | |
| body.appendParagraph("AI Summary:").setHeading(DocumentApp.ParagraphHeading.HEADING2); | |
| body.appendParagraph(item.summary); | |
| if (item.links && item.links !== "No links found") { | |
| body.appendParagraph("Links:").setHeading(DocumentApp.ParagraphHeading.HEADING2); | |
| body.appendParagraph(item.links); | |
| } | |
| }); | |
| } | |
| function getGeminiSummary(textBody, apiKey) { | |
| var truncatedBody = textBody.substring(0, 30000); | |
| var prompt = "Summarize this newsletter. Focus on the main topics and key takeaways. Present the summary as 3-5 concise bullet points:\n\n" + truncatedBody; | |
| var models = ["gemini-2.0-flash", "gemini-1.5-flash-latest", "gemini-1.5-flash", "gemini-1.5-pro", "gemini-pro"]; | |
| var url = "https://generativelanguage.googleapis.com/v1beta/models/" + models[0] + ":generateContent?key=" + apiKey; | |
| var payload = { | |
| "contents": [{ | |
| "parts": [{"text": prompt}] | |
| }] | |
| }; | |
| var options = { | |
| 'method': 'post', | |
| 'contentType': 'application/json', | |
| 'payload': JSON.stringify(payload), | |
| 'muteHttpExceptions': true | |
| }; | |
| try { | |
| var response = UrlFetchApp.fetch(url, options); | |
| if (response.getResponseCode() === 200) { | |
| var jsonResponse = JSON.parse(response.getContentText()); | |
| if (jsonResponse.candidates && jsonResponse.candidates[0].content && jsonResponse.candidates[0].content.parts[0]) { | |
| return jsonResponse.candidates[0].content.parts[0].text; | |
| } | |
| } else if (response.getResponseCode() === 429) { | |
| Utilities.sleep(12000); | |
| return getGeminiSummary(textBody, apiKey); | |
| } | |
| return "Error creating summary."; | |
| } catch (e) { | |
| return "Error creating summary."; | |
| } | |
| } | |
| function stripHtmlTags(html) { | |
| var text = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ''); | |
| text = text.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, ''); | |
| text = text.replace(/<br\s*\/?>/gi, '\n'); | |
| text = text.replace(/<\/(p|div|h[1-6]|li)>/gi, '\n'); | |
| text = text.replace(/<[^>]+>/g, ''); | |
| text = text.replace(/ /g, ' '); | |
| text = text.replace(/</g, '<'); | |
| text = text.replace(/>/g, '>'); | |
| text = text.replace(/&/g, '&'); | |
| text = text.replace(/"/g, '"'); | |
| text = text.replace(/'/g, "'"); | |
| text = text.replace(/\n\s*\n/g, '\n\n'); | |
| return text.trim(); | |
| } | |
| function extractLinks(htmlBody) { | |
| var allLinks = []; | |
| var seenUrls = new Set(); | |
| var cleanHtml = decodeHtmlEntities(htmlBody); | |
| const linkRegex = /<a\s+href="([^"]+)"[^>]*>([^<]{2,})<\/a>/gi; | |
| var match; | |
| while ((match = linkRegex.exec(cleanHtml)) !== null) { | |
| var url = match[1]; | |
| var text = match[2]; | |
| var cleanText = text.replace(/(\r\n|\n|\r)/gm, " ").replace(/\s+/g, ' ').trim(); | |
| if (cleanText.length > 2 && !cleanText.match(/&#\d+;/)) { | |
| var decodedUrl = tryDecodeBase64Url(url); | |
| if (isRelevantLink(decodedUrl, cleanText)) { | |
| if (!seenUrls.has(decodedUrl)) { | |
| allLinks.push(cleanText + ": " + decodedUrl); | |
| seenUrls.add(decodedUrl); | |
| } | |
| } | |
| } | |
| } | |
| const urlRegex = /(https?:\/\/[^\s<>"{}|\\^`\[\]]{10,})/g; | |
| while ((match = urlRegex.exec(cleanHtml)) !== null) { | |
| var url = match[1]; | |
| if (url.match(/&#\d+;/)) continue; | |
| var decodedUrl = tryDecodeBase64Url(url); | |
| if (isRelevantLink(decodedUrl, "")) { | |
| if (!seenUrls.has(decodedUrl)) { | |
| allLinks.push(decodedUrl); | |
| seenUrls.add(decodedUrl); | |
| } | |
| } | |
| } | |
| return allLinks.length > 0 ? allLinks.join('\n') : "No links found"; | |
| } | |
| function isRelevantLink(url, text) { | |
| var irrelevantDomains = [ | |
| 'substack.com/app-link', 'substack.com/redirect', 'substack.com/@', 'substack.com/i/', | |
| 'substack-post-media', 'chat.whatsapp.com', 'substack.com/signup', 'substack.com/subscribe', | |
| 'substack.com/action/disable_email', 'substack.com/redirect/2/eyJl', | |
| 'click.redditmail.com', 'redditstatic', 'redditmail', 'email', 'tracking', 'analytics', 'beacon', | |
| 'click.convertkit-mail2.com', 'embed.filekitcdn.com', 'api.filekitcdn.com', 'functions-js.kit.com', | |
| 'eotrx.substackcdn.com/open', 'substackcdn.com/image/fetch' | |
| ]; | |
| var irrelevantTexts = ['Join', 'Subscribe', 'Register', 'Sign up', 'Disable email', 'Unsubscribe']; | |
| var imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.bmp']; | |
| var isIrrelevantDomain = irrelevantDomains.some(domain => url.includes(domain)); | |
| var isIrrelevantText = irrelevantTexts.some(t => text.includes(t)); | |
| var isImage = imageExtensions.some(ext => url.toLowerCase().endsWith(ext)); | |
| return !isIrrelevantDomain && !isIrrelevantText && !isImage; | |
| } | |
| function decodeHtmlEntities(text) { | |
| text = text.replace(/&#(\d+);/g, function(match, dec) { | |
| return String.fromCharCode(parseInt(dec, 10)); | |
| }); | |
| text = text.replace(/&#x([a-fA-F0-9]+);/g, function(match, hex) { | |
| return String.fromCharCode(parseInt(hex, 16)); | |
| }); | |
| var entities = { | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| ''': "'", | |
| ' ': ' ' | |
| }; | |
| for (var entity in entities) { | |
| text = text.replace(new RegExp(entity, 'g'), entities[entity]); | |
| } | |
| return text; | |
| } | |
| function tryDecodeBase64Url(url) { | |
| try { | |
| var base64Matches = url.match(/[A-Za-z0-9+/]{40,}={0,3}/g); | |
| if (base64Matches) { | |
| for (var i = 0; i < base64Matches.length; i++) { | |
| var base64String = base64Matches[i]; | |
| try { | |
| var decodedString = Utilities.newBlob(Utilities.base64Decode(base64String)).getDataAsString(); | |
| if (decodedString.startsWith('http')) { | |
| return decodedString; | |
| } | |
| } catch (e) {} | |
| } | |
| } | |
| return url; | |
| } catch (e) { | |
| return url; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment