Reddit AI Content Generator

Generate AI-powered content for Reddit posts with one click

Size

16.1 KB

Version

1.0.1

Created

Oct 28, 2025

Updated

17 days ago

1// ==UserScript==
2// @name		Reddit AI Content Generator
3// @description		Generate AI-powered content for Reddit posts with one click
4// @version		1.0.1
5// @match		https://*.reddit.com/*
6// @icon		https://www.redditstatic.com/shreddit/assets/favicon/64x64.png
7// @grant		GM.getValue
8// @grant		GM.setValue
9// ==/UserScript==
10(function() {
11    'use strict';
12
13    console.log('Reddit AI Content Generator initialized');
14
15    // Debounce function to prevent excessive calls
16    function debounce(func, wait) {
17        let timeout;
18        return function executedFunction(...args) {
19            const later = () => {
20                clearTimeout(timeout);
21                func(...args);
22            };
23            clearTimeout(timeout);
24            timeout = setTimeout(later, wait);
25        };
26    }
27
28    // Show loading indicator
29    function showLoadingIndicator(text = 'AI is generating content...') {
30        let loader = document.getElementById('reddit-ai-loader');
31        if (!loader) {
32            loader = document.createElement('div');
33            loader.id = 'reddit-ai-loader';
34            loader.style.cssText = `
35                position: fixed;
36                top: 20px;
37                right: 20px;
38                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
39                color: white;
40                padding: 16px 24px;
41                border-radius: 8px;
42                z-index: 999999;
43                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
44                font-size: 14px;
45                font-weight: 500;
46                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
47                display: flex;
48                align-items: center;
49                gap: 12px;
50            `;
51            document.body.appendChild(loader);
52        }
53        loader.innerHTML = `
54            <svg width="20" height="20" viewBox="0 0 20 20" style="animation: spin 1s linear infinite;">
55                <circle cx="10" cy="10" r="8" fill="none" stroke="white" stroke-width="2" stroke-dasharray="25 15"/>
56            </svg>
57            <span>${text}</span>
58        `;
59        loader.style.display = 'flex';
60
61        // Add spin animation
62        if (!document.getElementById('reddit-ai-spinner-style')) {
63            const style = document.createElement('style');
64            style.id = 'reddit-ai-spinner-style';
65            style.textContent = `
66                @keyframes spin {
67                    from { transform: rotate(0deg); }
68                    to { transform: rotate(360deg); }
69                }
70            `;
71            document.head.appendChild(style);
72        }
73    }
74
75    // Hide loading indicator
76    function hideLoadingIndicator() {
77        const loader = document.getElementById('reddit-ai-loader');
78        if (loader) {
79            loader.style.display = 'none';
80        }
81    }
82
83    // Show notification
84    function showNotification(message, type = 'success') {
85        const notification = document.createElement('div');
86        notification.style.cssText = `
87            position: fixed;
88            top: 20px;
89            right: 20px;
90            background: ${type === 'success' ? '#10b981' : '#ef4444'};
91            color: white;
92            padding: 16px 24px;
93            border-radius: 8px;
94            z-index: 999999;
95            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
96            font-size: 14px;
97            font-weight: 500;
98            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
99            animation: slideIn 0.3s ease-out;
100        `;
101        notification.textContent = message;
102        document.body.appendChild(notification);
103
104        setTimeout(() => {
105            notification.style.animation = 'slideOut 0.3s ease-out';
106            setTimeout(() => notification.remove(), 300);
107        }, 3000);
108    }
109
110    // Generate AI content
111    async function generateAIContent(prompt, contentType) {
112        try {
113            showLoadingIndicator();
114            console.log('Generating AI content for:', contentType, 'with prompt:', prompt);
115
116            let aiPrompt = '';
117            let responseFormat = null;
118
119            if (contentType === 'title') {
120                aiPrompt = `Generate a catchy, engaging Reddit post title based on this topic: "${prompt}". The title should be attention-grabbing, clear, and follow Reddit best practices. Keep it under 300 characters.`;
121            } else if (contentType === 'post') {
122                aiPrompt = `Write an engaging Reddit post about: "${prompt}". Make it conversational, authentic, and valuable to readers. Include relevant details and maintain a friendly tone. Format it with proper paragraphs.`;
123                responseFormat = {
124                    type: "json_schema",
125                    json_schema: {
126                        name: "reddit_post",
127                        schema: {
128                            type: "object",
129                            properties: {
130                                content: { type: "string" },
131                                suggestions: { 
132                                    type: "array", 
133                                    items: { type: "string" },
134                                    description: "3 tips for improving the post"
135                                }
136                            },
137                            required: ["content"]
138                        }
139                    }
140                };
141            } else if (contentType === 'comment') {
142                aiPrompt = `Write a thoughtful, engaging Reddit comment in response to: "${prompt}". Be helpful, conversational, and add value to the discussion. Keep it concise but meaningful.`;
143            }
144
145            const result = await RM.aiCall(aiPrompt, responseFormat);
146            console.log('AI response received:', result);
147
148            hideLoadingIndicator();
149
150            if (contentType === 'post' && typeof result === 'object') {
151                return result.content;
152            }
153
154            return result;
155        } catch (error) {
156            console.error('AI generation failed:', error);
157            hideLoadingIndicator();
158            showNotification('Failed to generate content. Please try again.', 'error');
159            return null;
160        }
161    }
162
163    // Add AI button to title field
164    function addAIButtonToTitle() {
165        const titleInput = document.querySelector('textarea[name="title"]');
166        if (!titleInput || document.getElementById('ai-title-btn')) {
167            return;
168        }
169
170        console.log('Adding AI button to title field');
171
172        const container = titleInput.closest('div');
173        if (!container) return;
174
175        const aiButton = document.createElement('button');
176        aiButton.id = 'ai-title-btn';
177        aiButton.type = 'button';
178        aiButton.innerHTML = `
179            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
180                <path d="M12 2L2 7l10 5 10-5-10-5z"/>
181                <path d="M2 17l10 5 10-5"/>
182                <path d="M2 12l10 5 10-5"/>
183            </svg>
184            <span>Generate Title with AI</span>
185        `;
186        aiButton.style.cssText = `
187            display: flex;
188            align-items: center;
189            gap: 8px;
190            margin-top: 8px;
191            padding: 8px 16px;
192            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
193            color: white;
194            border: none;
195            border-radius: 6px;
196            font-size: 13px;
197            font-weight: 500;
198            cursor: pointer;
199            transition: all 0.2s;
200            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
201        `;
202
203        aiButton.addEventListener('mouseenter', () => {
204            aiButton.style.transform = 'translateY(-1px)';
205            aiButton.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.4)';
206        });
207
208        aiButton.addEventListener('mouseleave', () => {
209            aiButton.style.transform = 'translateY(0)';
210            aiButton.style.boxShadow = 'none';
211        });
212
213        aiButton.addEventListener('click', async () => {
214            const currentTitle = titleInput.value.trim();
215            const prompt = currentTitle || 'Generate an interesting Reddit post title';
216            
217            const generatedTitle = await generateAIContent(prompt, 'title');
218            if (generatedTitle) {
219                titleInput.value = generatedTitle;
220                titleInput.dispatchEvent(new Event('input', { bubbles: true }));
221                titleInput.dispatchEvent(new Event('change', { bubbles: true }));
222                showNotification('Title generated successfully!');
223            }
224        });
225
226        container.appendChild(aiButton);
227    }
228
229    // Add AI button to post content field
230    function addAIButtonToContent() {
231        const contentEditor = document.querySelector('div[contenteditable="true"][data-lexical-editor="true"]');
232        if (!contentEditor || document.getElementById('ai-content-btn')) {
233            return;
234        }
235
236        console.log('Adding AI button to content field');
237
238        const container = contentEditor.closest('div')?.parentElement;
239        if (!container) return;
240
241        const aiButton = document.createElement('button');
242        aiButton.id = 'ai-content-btn';
243        aiButton.type = 'button';
244        aiButton.innerHTML = `
245            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
246                <path d="M12 2L2 7l10 5 10-5-10-5z"/>
247                <path d="M2 17l10 5 10-5"/>
248                <path d="M2 12l10 5 10-5"/>
249            </svg>
250            <span>Generate Post with AI</span>
251        `;
252        aiButton.style.cssText = `
253            display: flex;
254            align-items: center;
255            gap: 8px;
256            margin-top: 12px;
257            padding: 10px 20px;
258            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
259            color: white;
260            border: none;
261            border-radius: 6px;
262            font-size: 14px;
263            font-weight: 500;
264            cursor: pointer;
265            transition: all 0.2s;
266            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
267        `;
268
269        aiButton.addEventListener('mouseenter', () => {
270            aiButton.style.transform = 'translateY(-1px)';
271            aiButton.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.4)';
272        });
273
274        aiButton.addEventListener('mouseleave', () => {
275            aiButton.style.transform = 'translateY(0)';
276            aiButton.style.boxShadow = 'none';
277        });
278
279        aiButton.addEventListener('click', async () => {
280            const titleInput = document.querySelector('textarea[name="title"]');
281            const currentContent = contentEditor.textContent.trim();
282            const titleText = titleInput ? titleInput.value.trim() : '';
283            
284            const prompt = currentContent || titleText || 'Write an engaging Reddit post';
285            
286            const generatedContent = await generateAIContent(prompt, 'post');
287            if (generatedContent) {
288                // Clear existing content
289                contentEditor.innerHTML = '';
290                
291                // Insert generated content
292                const paragraphs = generatedContent.split('\n\n');
293                paragraphs.forEach((para, index) => {
294                    const p = document.createElement('p');
295                    p.textContent = para;
296                    contentEditor.appendChild(p);
297                    if (index < paragraphs.length - 1) {
298                        contentEditor.appendChild(document.createElement('br'));
299                    }
300                });
301
302                contentEditor.dispatchEvent(new Event('input', { bubbles: true }));
303                contentEditor.dispatchEvent(new Event('change', { bubbles: true }));
304                showNotification('Post content generated successfully!');
305            }
306        });
307
308        container.appendChild(aiButton);
309    }
310
311    // Add AI button to comment field
312    function addAIButtonToComment() {
313        const commentEditors = document.querySelectorAll('div[contenteditable="true"][data-lexical-editor="true"]');
314        
315        commentEditors.forEach((editor) => {
316            // Skip if already has button or is a post editor
317            if (editor.closest('[data-test-id="comment-submission-form-richtext"]') && 
318                !editor.parentElement?.querySelector('.ai-comment-btn')) {
319                
320                console.log('Adding AI button to comment field');
321
322                const container = editor.parentElement;
323                if (!container) return;
324
325                const aiButton = document.createElement('button');
326                aiButton.className = 'ai-comment-btn';
327                aiButton.type = 'button';
328                aiButton.innerHTML = `
329                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
330                        <path d="M12 2L2 7l10 5 10-5-10-5z"/>
331                        <path d="M2 17l10 5 10-5"/>
332                        <path d="M2 12l10 5 10-5"/>
333                    </svg>
334                    <span>AI Generate</span>
335                `;
336                aiButton.style.cssText = `
337                    display: flex;
338                    align-items: center;
339                    gap: 6px;
340                    margin-top: 8px;
341                    padding: 6px 12px;
342                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
343                    color: white;
344                    border: none;
345                    border-radius: 4px;
346                    font-size: 12px;
347                    font-weight: 500;
348                    cursor: pointer;
349                    transition: all 0.2s;
350                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
351                `;
352
353                aiButton.addEventListener('mouseenter', () => {
354                    aiButton.style.transform = 'translateY(-1px)';
355                    aiButton.style.boxShadow = '0 2px 8px rgba(102, 126, 234, 0.4)';
356                });
357
358                aiButton.addEventListener('mouseleave', () => {
359                    aiButton.style.transform = 'translateY(0)';
360                    aiButton.style.boxShadow = 'none';
361                });
362
363                aiButton.addEventListener('click', async () => {
364                    // Get post title and content for context
365                    const postTitle = document.querySelector('h1')?.textContent || '';
366                    const currentComment = editor.textContent.trim();
367                    
368                    const prompt = currentComment || `Write a thoughtful comment about: ${postTitle}`;
369                    
370                    const generatedComment = await generateAIContent(prompt, 'comment');
371                    if (generatedComment) {
372                        editor.innerHTML = '';
373                        const p = document.createElement('p');
374                        p.textContent = generatedComment;
375                        editor.appendChild(p);
376
377                        editor.dispatchEvent(new Event('input', { bubbles: true }));
378                        editor.dispatchEvent(new Event('change', { bubbles: true }));
379                        showNotification('Comment generated successfully!');
380                    }
381                });
382
383                container.appendChild(aiButton);
384            }
385        });
386    }
387
388    // Initialize the extension
389    function init() {
390        console.log('Initializing Reddit AI Content Generator');
391
392        // Use MutationObserver to detect when post/comment forms appear
393        const observer = new MutationObserver(debounce(() => {
394            addAIButtonToTitle();
395            addAIButtonToContent();
396            addAIButtonToComment();
397        }, 500));
398
399        observer.observe(document.body, {
400            childList: true,
401            subtree: true
402        });
403
404        // Initial check
405        setTimeout(() => {
406            addAIButtonToTitle();
407            addAIButtonToContent();
408            addAIButtonToComment();
409        }, 1000);
410    }
411
412    // Start when DOM is ready
413    if (document.readyState === 'loading') {
414        document.addEventListener('DOMContentLoaded', init);
415    } else {
416        init();
417    }
418})();
Reddit AI Content Generator | Robomonkey