Generate engaging LinkedIn posts with AI assistance
Size
11.9 KB
Version
1.0.1
Created
Oct 25, 2025
Updated
20 days ago
1// ==UserScript==
2// @name AI-powered LinkedIn Content Generator
3// @description Generate engaging LinkedIn posts with AI assistance
4// @version 1.0.1
5// @match https://*.linkedin.com/*
6// @icon https://static.licdn.com/aero-v1/sc/h/akt4ae504epesldzj74dzred8
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('AI-powered LinkedIn Content Generator initialized');
12
13 // Add custom styles for the AI button and modal
14 TM_addStyle(`
15 .ai-generate-btn {
16 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17 color: white;
18 border: none;
19 padding: 8px 16px;
20 border-radius: 16px;
21 font-size: 14px;
22 font-weight: 600;
23 cursor: pointer;
24 margin: 8px;
25 display: inline-flex;
26 align-items: center;
27 gap: 6px;
28 transition: all 0.3s ease;
29 box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
30 }
31
32 .ai-generate-btn:hover {
33 transform: translateY(-2px);
34 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
35 }
36
37 .ai-generate-btn:disabled {
38 opacity: 0.6;
39 cursor: not-allowed;
40 transform: none;
41 }
42
43 .ai-modal-overlay {
44 position: fixed;
45 top: 0;
46 left: 0;
47 right: 0;
48 bottom: 0;
49 background: rgba(0, 0, 0, 0.6);
50 display: flex;
51 align-items: center;
52 justify-content: center;
53 z-index: 9999;
54 backdrop-filter: blur(4px);
55 }
56
57 .ai-modal {
58 background: white;
59 border-radius: 12px;
60 padding: 24px;
61 max-width: 500px;
62 width: 90%;
63 box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
64 }
65
66 .ai-modal h2 {
67 margin: 0 0 16px 0;
68 color: #333;
69 font-size: 20px;
70 }
71
72 .ai-modal textarea {
73 width: 100%;
74 min-height: 100px;
75 padding: 12px;
76 border: 2px solid #e0e0e0;
77 border-radius: 8px;
78 font-size: 14px;
79 font-family: inherit;
80 resize: vertical;
81 margin-bottom: 16px;
82 box-sizing: border-box;
83 }
84
85 .ai-modal textarea:focus {
86 outline: none;
87 border-color: #667eea;
88 }
89
90 .ai-modal-buttons {
91 display: flex;
92 gap: 12px;
93 justify-content: flex-end;
94 }
95
96 .ai-modal-btn {
97 padding: 10px 20px;
98 border-radius: 8px;
99 font-size: 14px;
100 font-weight: 600;
101 cursor: pointer;
102 border: none;
103 transition: all 0.2s ease;
104 }
105
106 .ai-modal-btn-primary {
107 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
108 color: white;
109 }
110
111 .ai-modal-btn-primary:hover {
112 transform: translateY(-1px);
113 box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
114 }
115
116 .ai-modal-btn-primary:disabled {
117 opacity: 0.6;
118 cursor: not-allowed;
119 transform: none;
120 }
121
122 .ai-modal-btn-secondary {
123 background: #f0f0f0;
124 color: #333;
125 }
126
127 .ai-modal-btn-secondary:hover {
128 background: #e0e0e0;
129 }
130
131 .ai-loading {
132 display: inline-block;
133 width: 14px;
134 height: 14px;
135 border: 2px solid rgba(255, 255, 255, 0.3);
136 border-top-color: white;
137 border-radius: 50%;
138 animation: spin 0.8s linear infinite;
139 }
140
141 @keyframes spin {
142 to { transform: rotate(360deg); }
143 }
144 `);
145
146 // Debounce function to avoid multiple rapid calls
147 function debounce(func, wait) {
148 let timeout;
149 return function executedFunction(...args) {
150 const later = () => {
151 clearTimeout(timeout);
152 func(...args);
153 };
154 clearTimeout(timeout);
155 timeout = setTimeout(later, wait);
156 };
157 }
158
159 // Function to create and show the AI prompt modal
160 function showAIPromptModal(targetEditor) {
161 const overlay = document.createElement('div');
162 overlay.className = 'ai-modal-overlay';
163
164 const modal = document.createElement('div');
165 modal.className = 'ai-modal';
166
167 modal.innerHTML = `
168 <h2>✨ AI Content Generator</h2>
169 <textarea id="ai-prompt-input" placeholder="Describe what you want to post about...
170
171Examples:
172• Write a post about the importance of work-life balance
173• Share insights on AI in software development
174• Announce a new job position
175• Celebrate a team achievement"></textarea>
176 <div class="ai-modal-buttons">
177 <button class="ai-modal-btn ai-modal-btn-secondary" id="ai-cancel-btn">Cancel</button>
178 <button class="ai-modal-btn ai-modal-btn-primary" id="ai-generate-btn">Generate Post</button>
179 </div>
180 `;
181
182 overlay.appendChild(modal);
183 document.body.appendChild(overlay);
184
185 const promptInput = modal.querySelector('#ai-prompt-input');
186 const cancelBtn = modal.querySelector('#ai-cancel-btn');
187 const generateBtn = modal.querySelector('#ai-generate-btn');
188
189 // Focus on input
190 promptInput.focus();
191
192 // Close modal on overlay click
193 overlay.addEventListener('click', (e) => {
194 if (e.target === overlay) {
195 overlay.remove();
196 }
197 });
198
199 // Cancel button
200 cancelBtn.addEventListener('click', () => {
201 overlay.remove();
202 });
203
204 // Generate button
205 generateBtn.addEventListener('click', async () => {
206 const prompt = promptInput.value.trim();
207
208 if (!prompt) {
209 alert('Please enter a description for your post');
210 return;
211 }
212
213 // Disable button and show loading
214 generateBtn.disabled = true;
215 generateBtn.innerHTML = '<span class="ai-loading"></span> Generating...';
216
217 try {
218 console.log('Generating LinkedIn post with AI...');
219
220 // Call AI to generate the post
221 const aiPrompt = `Create an engaging LinkedIn post based on this topic: "${prompt}"
222
223Requirements:
224- Professional yet conversational tone
225- 150-250 words
226- Include relevant emojis (but not too many)
227- Add 3-5 relevant hashtags at the end
228- Make it engaging and authentic
229- Use line breaks for readability
230
231Generate only the post content, nothing else.`;
232
233 const generatedPost = await RM.aiCall(aiPrompt);
234
235 console.log('AI post generated successfully');
236
237 // Insert the generated content into the LinkedIn editor
238 insertContentIntoEditor(targetEditor, generatedPost);
239
240 // Close modal
241 overlay.remove();
242
243 } catch (error) {
244 console.error('Error generating post:', error);
245 alert('Failed to generate post. Please try again.');
246 generateBtn.disabled = false;
247 generateBtn.textContent = 'Generate Post';
248 }
249 });
250
251 // Allow Enter key to submit (with Shift+Enter for new line)
252 promptInput.addEventListener('keydown', (e) => {
253 if (e.key === 'Enter' && !e.shiftKey) {
254 e.preventDefault();
255 generateBtn.click();
256 }
257 });
258 }
259
260 // Function to insert content into LinkedIn's editor
261 function insertContentIntoEditor(editor, content) {
262 try {
263 // Try to find the contenteditable div
264 const editableDiv = editor.querySelector('[contenteditable="true"]') ||
265 editor.querySelector('.ql-editor') ||
266 editor;
267
268 if (editableDiv) {
269 // Set focus first
270 editableDiv.focus();
271
272 // Clear existing content
273 editableDiv.innerHTML = '';
274
275 // Insert new content with proper formatting
276 const lines = content.split('\n');
277 lines.forEach((line, index) => {
278 if (line.trim()) {
279 const p = document.createElement('p');
280 p.textContent = line;
281 editableDiv.appendChild(p);
282 } else if (index < lines.length - 1) {
283 // Add empty paragraph for line breaks
284 const br = document.createElement('p');
285 br.innerHTML = '<br>';
286 editableDiv.appendChild(br);
287 }
288 });
289
290 // Trigger input event to notify LinkedIn
291 editableDiv.dispatchEvent(new Event('input', { bubbles: true }));
292 editableDiv.dispatchEvent(new Event('change', { bubbles: true }));
293
294 console.log('Content inserted successfully');
295 } else {
296 console.error('Could not find editable element');
297 }
298 } catch (error) {
299 console.error('Error inserting content:', error);
300 }
301 }
302
303 // Function to add AI button to the post editor
304 function addAIButtonToEditor(editorContainer) {
305 // Check if button already exists
306 if (editorContainer.querySelector('.ai-generate-btn')) {
307 return;
308 }
309
310 // Find the toolbar or button container
311 const toolbar = editorContainer.querySelector('[class*="share-actions"]') ||
312 editorContainer.querySelector('[class*="share-box-footer"]') ||
313 editorContainer.querySelector('[class*="editor-content"]');
314
315 if (toolbar) {
316 const aiButton = document.createElement('button');
317 aiButton.className = 'ai-generate-btn';
318 aiButton.innerHTML = '✨ Generate with AI';
319 aiButton.type = 'button';
320
321 aiButton.addEventListener('click', (e) => {
322 e.preventDefault();
323 e.stopPropagation();
324 showAIPromptModal(editorContainer);
325 });
326
327 // Insert button at the beginning of toolbar
328 toolbar.insertBefore(aiButton, toolbar.firstChild);
329 console.log('AI button added to editor');
330 }
331 }
332
333 // Observer to detect when post editor opens
334 const observePostEditor = debounce(() => {
335 // Look for the post creation modal/editor
336 const editors = document.querySelectorAll('[class*="share-creation-state"]');
337
338 editors.forEach(editor => {
339 if (editor.offsetParent !== null) { // Check if visible
340 addAIButtonToEditor(editor);
341 }
342 });
343
344 // Also check for the expanded editor modal
345 const modal = document.querySelector('[role="dialog"][class*="share"]');
346 if (modal) {
347 addAIButtonToEditor(modal);
348 }
349 }, 500);
350
351 // Set up mutation observer to watch for editor appearance
352 const observer = new MutationObserver(observePostEditor);
353
354 // Start observing when body is ready
355 TM_runBody(() => {
356 observer.observe(document.body, {
357 childList: true,
358 subtree: true
359 });
360
361 // Initial check
362 observePostEditor();
363
364 console.log('Watching for LinkedIn post editor...');
365 });
366
367})();