Size
15.9 KB
Version
1.0.1
Created
Oct 22, 2025
Updated
about 13 hours ago
1// ==UserScript==
2// @name AI-Powered Reddit Summarizer
3// @description Summarize Reddit posts and comments using AI
4// @version 1.0.1
5// @match https://*.reddit.com/*
6// @icon https://www.redditstatic.com/shreddit/assets/favicon/64x64.png
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('AI-Powered Reddit Summarizer initialized');
12
13 // Add custom styles for the summarizer
14 TM_addStyle(`
15 .reddit-summarizer-btn {
16 display: inline-flex;
17 align-items: center;
18 padding: 6px 12px;
19 margin-left: 8px;
20 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
21 color: white;
22 border: none;
23 border-radius: 20px;
24 font-size: 12px;
25 font-weight: 600;
26 cursor: pointer;
27 transition: all 0.3s ease;
28 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
29 }
30
31 .reddit-summarizer-btn:hover {
32 transform: translateY(-1px);
33 box-shadow: 0 4px 8px rgba(0,0,0,0.2);
34 background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
35 }
36
37 .reddit-summarizer-btn:disabled {
38 opacity: 0.6;
39 cursor: not-allowed;
40 transform: none;
41 }
42
43 .reddit-summarizer-icon {
44 margin-right: 6px;
45 font-size: 14px;
46 }
47
48 .reddit-summary-modal {
49 position: fixed;
50 top: 0;
51 left: 0;
52 right: 0;
53 bottom: 0;
54 background: rgba(0, 0, 0, 0.7);
55 display: flex;
56 align-items: center;
57 justify-content: center;
58 z-index: 10000;
59 padding: 20px;
60 animation: fadeIn 0.3s ease;
61 }
62
63 @keyframes fadeIn {
64 from { opacity: 0; }
65 to { opacity: 1; }
66 }
67
68 .reddit-summary-content {
69 background: white;
70 border-radius: 12px;
71 max-width: 800px;
72 max-height: 90vh;
73 overflow-y: auto;
74 padding: 30px;
75 box-shadow: 0 10px 40px rgba(0,0,0,0.3);
76 animation: slideUp 0.3s ease;
77 position: relative;
78 }
79
80 @keyframes slideUp {
81 from { transform: translateY(20px); opacity: 0; }
82 to { transform: translateY(0); opacity: 1; }
83 }
84
85 .reddit-summary-header {
86 display: flex;
87 justify-content: space-between;
88 align-items: center;
89 margin-bottom: 20px;
90 padding-bottom: 15px;
91 border-bottom: 2px solid #f0f0f0;
92 }
93
94 .reddit-summary-title {
95 font-size: 24px;
96 font-weight: bold;
97 color: #1a1a1b;
98 margin: 0;
99 }
100
101 .reddit-summary-close {
102 background: none;
103 border: none;
104 font-size: 28px;
105 cursor: pointer;
106 color: #878a8c;
107 padding: 0;
108 width: 32px;
109 height: 32px;
110 display: flex;
111 align-items: center;
112 justify-content: center;
113 border-radius: 50%;
114 transition: all 0.2s ease;
115 }
116
117 .reddit-summary-close:hover {
118 background: #f6f7f8;
119 color: #1a1a1b;
120 }
121
122 .reddit-summary-body {
123 color: #1a1a1b;
124 line-height: 1.6;
125 font-size: 15px;
126 }
127
128 .reddit-summary-section {
129 margin-bottom: 20px;
130 }
131
132 .reddit-summary-section-title {
133 font-size: 18px;
134 font-weight: 600;
135 color: #1a1a1b;
136 margin-bottom: 10px;
137 display: flex;
138 align-items: center;
139 }
140
141 .reddit-summary-section-icon {
142 margin-right: 8px;
143 }
144
145 .reddit-summary-text {
146 color: #1c1c1c;
147 margin-bottom: 10px;
148 }
149
150 .reddit-summary-list {
151 list-style: none;
152 padding: 0;
153 margin: 0;
154 }
155
156 .reddit-summary-list li {
157 padding: 8px 0;
158 padding-left: 24px;
159 position: relative;
160 color: #1c1c1c;
161 }
162
163 .reddit-summary-list li:before {
164 content: "•";
165 position: absolute;
166 left: 8px;
167 color: #667eea;
168 font-weight: bold;
169 }
170
171 .reddit-summary-loading {
172 text-align: center;
173 padding: 40px;
174 }
175
176 .reddit-summary-spinner {
177 border: 3px solid #f3f3f3;
178 border-top: 3px solid #667eea;
179 border-radius: 50%;
180 width: 40px;
181 height: 40px;
182 animation: spin 1s linear infinite;
183 margin: 0 auto 20px;
184 }
185
186 @keyframes spin {
187 0% { transform: rotate(0deg); }
188 100% { transform: rotate(360deg); }
189 }
190
191 .reddit-summary-loading-text {
192 color: #1a1a1b;
193 font-size: 16px;
194 }
195 `);
196
197 // Function to extract post content
198 function extractPostContent() {
199 console.log('Extracting post content...');
200
201 const postTitle = document.querySelector('h1[id^="post-title-"]')?.textContent?.trim() || '';
202 console.log('Post title:', postTitle);
203
204 // Try to find post body text
205 let postBody = '';
206 const postTextBody = document.querySelector('shreddit-post-text-body div[slot="text-body"]');
207 if (postTextBody) {
208 postBody = postTextBody.textContent?.trim() || '';
209 }
210 console.log('Post body length:', postBody.length);
211
212 return { postTitle, postBody };
213 }
214
215 // Function to extract comments
216 function extractComments(maxComments = 10) {
217 console.log('Extracting comments...');
218
219 const comments = [];
220 const commentElements = document.querySelectorAll('shreddit-comment[depth="0"]');
221
222 console.log('Found comment elements:', commentElements.length);
223
224 for (let i = 0; i < Math.min(commentElements.length, maxComments); i++) {
225 const commentEl = commentElements[i];
226 const commentText = commentEl.querySelector('div[slot="comment"]')?.textContent?.trim();
227
228 if (commentText && commentText.length > 20) {
229 comments.push(commentText);
230 }
231 }
232
233 console.log('Extracted comments:', comments.length);
234 return comments;
235 }
236
237 // Function to generate summary using AI
238 async function generateSummary(postTitle, postBody, comments) {
239 console.log('Generating AI summary...');
240
241 const prompt = `Please provide a comprehensive summary of this Reddit post and its discussion:
242
243POST TITLE: ${postTitle}
244
245POST CONTENT: ${postBody || 'No additional content'}
246
247TOP COMMENTS:
248${comments.map((c, i) => `${i + 1}. ${c}`).join('\n\n')}
249
250Please analyze and summarize this in a structured format.`;
251
252 try {
253 const summary = await RM.aiCall(prompt, {
254 type: "json_schema",
255 json_schema: {
256 name: "reddit_summary",
257 schema: {
258 type: "object",
259 properties: {
260 mainTopic: {
261 type: "string",
262 description: "A brief one-sentence summary of what the post is about"
263 },
264 keyPoints: {
265 type: "array",
266 items: { type: "string" },
267 description: "3-5 key points or main ideas from the post and discussion"
268 },
269 topPerspectives: {
270 type: "array",
271 items: { type: "string" },
272 description: "2-4 different perspectives or opinions from the comments"
273 },
274 consensus: {
275 type: "string",
276 description: "What seems to be the general consensus or common theme in the discussion"
277 }
278 },
279 required: ["mainTopic", "keyPoints", "topPerspectives", "consensus"]
280 }
281 }
282 });
283
284 console.log('AI summary generated successfully');
285 return summary;
286 } catch (error) {
287 console.error('Error generating summary:', error);
288 throw error;
289 }
290 }
291
292 // Function to display summary modal
293 function displaySummary(summary) {
294 console.log('Displaying summary modal');
295
296 const modal = document.createElement('div');
297 modal.className = 'reddit-summary-modal';
298
299 modal.innerHTML = `
300 <div class="reddit-summary-content">
301 <div class="reddit-summary-header">
302 <h2 class="reddit-summary-title">📝 AI Summary</h2>
303 <button class="reddit-summary-close" aria-label="Close">×</button>
304 </div>
305 <div class="reddit-summary-body">
306 <div class="reddit-summary-section">
307 <h3 class="reddit-summary-section-title">
308 <span class="reddit-summary-section-icon">🎯</span>
309 Main Topic
310 </h3>
311 <p class="reddit-summary-text">${summary.mainTopic}</p>
312 </div>
313
314 <div class="reddit-summary-section">
315 <h3 class="reddit-summary-section-title">
316 <span class="reddit-summary-section-icon">💡</span>
317 Key Points
318 </h3>
319 <ul class="reddit-summary-list">
320 ${summary.keyPoints.map(point => `<li>${point}</li>`).join('')}
321 </ul>
322 </div>
323
324 <div class="reddit-summary-section">
325 <h3 class="reddit-summary-section-title">
326 <span class="reddit-summary-section-icon">💬</span>
327 Top Perspectives
328 </h3>
329 <ul class="reddit-summary-list">
330 ${summary.topPerspectives.map(perspective => `<li>${perspective}</li>`).join('')}
331 </ul>
332 </div>
333
334 <div class="reddit-summary-section">
335 <h3 class="reddit-summary-section-title">
336 <span class="reddit-summary-section-icon">🤝</span>
337 General Consensus
338 </h3>
339 <p class="reddit-summary-text">${summary.consensus}</p>
340 </div>
341 </div>
342 </div>
343 `;
344
345 document.body.appendChild(modal);
346
347 // Close modal handlers
348 const closeBtn = modal.querySelector('.reddit-summary-close');
349 closeBtn.addEventListener('click', () => modal.remove());
350
351 modal.addEventListener('click', (e) => {
352 if (e.target === modal) {
353 modal.remove();
354 }
355 });
356
357 // Close on Escape key
358 const escapeHandler = (e) => {
359 if (e.key === 'Escape') {
360 modal.remove();
361 document.removeEventListener('keydown', escapeHandler);
362 }
363 };
364 document.addEventListener('keydown', escapeHandler);
365 }
366
367 // Function to show loading modal
368 function showLoadingModal() {
369 const modal = document.createElement('div');
370 modal.className = 'reddit-summary-modal';
371 modal.id = 'reddit-summary-loading';
372
373 modal.innerHTML = `
374 <div class="reddit-summary-content">
375 <div class="reddit-summary-loading">
376 <div class="reddit-summary-spinner"></div>
377 <div class="reddit-summary-loading-text">🤖 AI is analyzing the post and comments...</div>
378 </div>
379 </div>
380 `;
381
382 document.body.appendChild(modal);
383 return modal;
384 }
385
386 // Main summarize function
387 async function summarizePost(button) {
388 console.log('Summarize button clicked');
389
390 button.disabled = true;
391 button.textContent = '⏳ Summarizing...';
392
393 const loadingModal = showLoadingModal();
394
395 try {
396 // Wait a bit for comments to load
397 await new Promise(resolve => setTimeout(resolve, 2000));
398
399 const { postTitle, postBody } = extractPostContent();
400 const comments = extractComments(10);
401
402 if (!postTitle) {
403 throw new Error('Could not extract post content');
404 }
405
406 if (comments.length === 0) {
407 console.log('No comments found, summarizing post only');
408 }
409
410 const summary = await generateSummary(postTitle, postBody, comments);
411
412 loadingModal.remove();
413 displaySummary(summary);
414
415 } catch (error) {
416 console.error('Error summarizing post:', error);
417 loadingModal.remove();
418 alert('Failed to generate summary. Please try again.');
419 } finally {
420 button.disabled = false;
421 button.innerHTML = '<span class="reddit-summarizer-icon">✨</span>Summarize';
422 }
423 }
424
425 // Function to add summarize button
426 function addSummarizeButton() {
427 console.log('Adding summarize button...');
428
429 // Check if we're on a post page
430 if (!window.location.pathname.includes('/comments/')) {
431 console.log('Not on a post page, skipping button');
432 return;
433 }
434
435 // Check if button already exists
436 if (document.querySelector('.reddit-summarizer-btn')) {
437 console.log('Button already exists');
438 return;
439 }
440
441 // Find the share button
442 const shareButton = document.querySelector('faceplate-dropdown-menu.share-dropdown-menu button');
443
444 if (shareButton) {
445 console.log('Found share button, adding summarize button');
446
447 const summarizeBtn = document.createElement('button');
448 summarizeBtn.className = 'reddit-summarizer-btn';
449 summarizeBtn.innerHTML = '<span class="reddit-summarizer-icon">✨</span>Summarize';
450
451 summarizeBtn.addEventListener('click', (e) => {
452 e.preventDefault();
453 e.stopPropagation();
454 summarizePost(summarizeBtn);
455 });
456
457 shareButton.parentElement.insertAdjacentElement('afterend', summarizeBtn);
458 console.log('Summarize button added successfully');
459 } else {
460 console.log('Share button not found, will retry...');
461 }
462 }
463
464 // Initialize the extension
465 function init() {
466 console.log('Initializing Reddit Summarizer...');
467
468 // Add button on page load
469 TM_runBody(() => {
470 setTimeout(addSummarizeButton, 2000);
471 });
472
473 // Watch for navigation changes (Reddit is a SPA)
474 let lastUrl = location.href;
475 new MutationObserver(() => {
476 const url = location.href;
477 if (url !== lastUrl) {
478 lastUrl = url;
479 console.log('URL changed, re-initializing...');
480 setTimeout(addSummarizeButton, 2000);
481 }
482 }).observe(document.body, { subtree: true, childList: true });
483 }
484
485 // Start the extension
486 init();
487})();