Size
16.4 KB
Version
1.0.3
Created
Mar 26, 2026
Updated
about 1 month ago
Intelligent comment responses for Facebook, X.com, YouTube, and TikTok using AI (OpenAI/Claude/Gemini)
1// ==UserScript==
2// @name Social Media AI Comment Responder
3// @description Intelligent comment responses for Facebook, X.com, YouTube, and TikTok using AI (OpenAI/Claude/Gemini)
4// @version 1.0.3
5// @match https://*.facebook.com/*
6// @match https://*.x.com/*
7// @match https://*.youtube.com/*
8// @match https://*.tiktok.com/*
9// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC3ElEQVR4AcRWO2sUURg99+ZFFlmy67NSUlmIEjJBCx9YWKQStfZBSGwENWzEP2BrQiJoQBJS2IuoiNgoCoKQkWAaG42l5DEJiUSMyVy/c2cSMibOzq67O5fvm72P851z9s7d2dFI2I7dm8u2D85edAZnRpzB2XHJOcm1MNmXuZkRYohNSIuiBpyB74cpuuqbeQU8AlQ3AEcyL8l6Jvsyp7qJIZY1rBVMbLD4nwAhuQtd/wmBaCwW0SZY1c1ayxFdi4wEGBnbgTPgybeeHQdUH/67qT65TePk3I5qi4H2gemT0P4bAcuWyrUy4ZDTcv/FFzFAl0rrJ4LhPZWPikae3NTYzKo3D8TlmIyrIS60NvKhhh3wsmEgPCyV3Hbyb5dOqGXXrAFHfmoo48BlGhWudDRj+EIWJ1obkbzJwbSagDUAXXczeTHQebAJD85n8bInh+vHMzi0tx7ZJlUKBdY1dfDUUl1Jqi87zXh1NYc7nTtwdH8DMg0K5TfVRW392zdnhSTYCenExYFcHfKZALrw02D6hx8HL7ZmtbWCOV0Mub7uLft4+3UFt58v4cxDD9/m19aXyvqktnwd1Za0+v77ZRSeLeH1l5WkJUVwqk0MoBXptVYaaElPHy00kKI+7HNgIUUHC9yBqRQNTIkBM5GeATOhDRT/+1PxQG3doNVTUfclax0+tfWHGzsXATNWa3VqUlvOgEj7a0NyrW2EmtaAW9g3KY76S3Vw7fEiOobmcGrYw4vPv0ooN/2BJuxzwBa6vbtvSceVrHa4oZbVsTtge7z4mu8FHrtVSg+BxgZ9xIBbyE8a3z8nq9Uw4ZGbGsK/EREDnP1Y2PNOXPIdoZK3wyWn5abIptxigGt06fbu6ijnYLI+mnLghIuc0flgtK2BYAlweTD91SNiZFTmfMmkIVgzCqm1HDFVsQZYx5+LkPTUa5UzwKXQDG8Pz4kIgcm+zJlRYohlDWvJEZd/AAAA//+lQe39AAAABklEQVQDAN8M/fdDhMOKAAAAAElFTkSuQmCC
10// @require https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
11// ==/UserScript==
12(function() {
13 'use strict';
14
15 // Configuration
16 const CONFIG = {
17 facebook: {
18 commentSelector: '[data-sigil="mfeed_pivots_message feed-story-highlight-candidate"] [data-sigil="mfeed_premessage"]',
19 replyButtonSelector: '[data-sigil="messageable_reply_button"]',
20 replyBoxSelector: '[data-sigil="mfeed_pivots_message feed-story-highlight-candidate"] textarea',
21 postButtonSelector: '[data-sigil="send"]'
22 },
23 twitter: {
24 commentSelector: '[data-testid="tweet"]',
25 replyButtonSelector: '[data-testid="reply"]',
26 replyBoxSelector: '[data-testid="tweetTextarea_0"]',
27 postButtonSelector: '[data-testid="tweetButton"]'
28 },
29 youtube: {
30 commentSelector: '#comment',
31 replyButtonSelector: '#reply-button-end',
32 replyBoxSelector: '#textarea',
33 postButtonSelector: '#submit-button'
34 },
35 tiktok: {
36 commentSelector: '[data-e2e="comment-container"]',
37 replyButtonSelector: '[data-e2e="reply-button"]',
38 replyBoxSelector: '[data-e2e="user-comment-input"]',
39 postButtonSelector: '[data-e2e="add-comment-btn"]'
40 }
41 };
42
43 // AI Providers configuration
44 const AI_PROVIDERS = {
45 openai: {
46 name: 'OpenAI (GPT)',
47 apiUrl: 'https://api.openai.com/v1/chat/completions',
48 model: 'gpt-3.5-turbo'
49 },
50 claude: {
51 name: 'Claude (Anthropic)',
52 apiUrl: 'https://api.anthropic.com/v1/messages',
53 model: 'claude-3-haiku-20240307'
54 },
55 gemini: {
56 name: 'Gemini (Google)',
57 apiUrl: 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent',
58 model: 'gemini-pro'
59 }
60 };
61
62 // Default reply message
63 const DEFAULT_REPLY = "Thank you for your comment! I'll get back to you soon.";
64
65 // Initialize the extension
66 function init() {
67 console.log('Social Media AI Comment Responder initialized');
68
69 // Check which platform we're on
70 const platform = detectPlatform();
71 if (platform) {
72 console.log(`Detected platform: ${platform}`);
73 setupAutoReply(platform);
74 } else {
75 console.log('Unsupported platform');
76 }
77 }
78
79 // Detect which social media platform we're on
80 function detectPlatform() {
81 const hostname = window.location.hostname;
82
83 if (hostname.includes('facebook.com')) {
84 return 'facebook';
85 } else if (hostname.includes('x.com') || hostname.includes('twitter.com')) {
86 return 'twitter';
87 } else if (hostname.includes('youtube.com')) {
88 return 'youtube';
89 } else if (hostname.includes('tiktok.com')) {
90 return 'tiktok';
91 }
92
93 return null;
94 }
95
96 // Set up auto-reply functionality for the detected platform
97 function setupAutoReply(platform) {
98 // Add UI controls to manage auto-replies
99 addControlPanel(platform);
100
101 // Start observing for new comments
102 observeComments(platform);
103 }
104
105 // Add control panel for managing auto-replies
106 function addControlPanel(platform) {
107 // Create control panel element
108 const panel = document.createElement('div');
109 panel.id = 'auto-reply-control-panel';
110 panel.style.cssText = `
111 position: fixed;
112 top: 20px;
113 right: 20px;
114 width: 320px;
115 background: white;
116 border: 1px solid #ccc;
117 border-radius: 8px;
118 padding: 15px;
119 box-shadow: 0 4px 12px rgba(0,0,0,0.15);
120 z-index: 10000;
121 font-family: Arial, sans-serif;
122 font-size: 14px;
123 `;
124
125 // Generate AI provider options
126 let aiProviderOptions = '';
127 for (const [key, provider] of Object.entries(AI_PROVIDERS)) {
128 aiProviderOptions += `<option value="${key}">${provider.name}</option>`;
129 }
130
131 panel.innerHTML = `
132 <h3 style="margin-top: 0; color: #333;">AI Comment Responder</h3>
133 <div style="margin-bottom: 10px;">
134 <label for="auto-reply-toggle" style="display: flex; align-items: center; cursor: pointer;">
135 <input type="checkbox" id="auto-reply-toggle" style="margin-right: 8px;">
136 Enable AI Replies
137 </label>
138 </div>
139 <div style="margin-bottom: 10px;">
140 <label for="ai-provider" style="display: block; margin-bottom: 5px;">AI Provider:</label>
141 <select id="ai-provider" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;">
142 ${aiProviderOptions}
143 </select>
144 </div>
145 <div style="margin-bottom: 10px;">
146 <label for="api-key" style="display: block; margin-bottom: 5px;">API Key:</label>
147 <input type="password" id="api-key" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;" placeholder="Enter your API key">
148 </div>
149 <div style="margin-bottom: 10px;">
150 <label for="custom-prompt" style="display: block; margin-bottom: 5px;">Custom Prompt:</label>
151 <textarea id="custom-prompt" style="width: 100%; height: 80px; padding: 8px; border: 1px solid #ccc; border-radius: 4px;" placeholder="Enter custom prompt for AI response generation">You are responding to a social media comment. Be friendly, engaging, and authentic. Keep responses concise but personalized to the comment content.</textarea>
152 </div>
153 <div>
154 <button id="save-settings" style="background: #1877f2; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-right: 5px;">Save Settings</button>
155 <button id="test-ai" style="background: #4CAF50; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">Test AI</button>
156 </div>
157 `;
158
159 // Add panel to page
160 document.body.appendChild(panel);
161
162 // Load saved settings
163 loadSettings(platform);
164
165 // Add event listeners
166 document.getElementById('save-settings').addEventListener('click', () => {
167 saveSettings(platform);
168 alert('Settings saved!');
169 });
170
171 document.getElementById('test-ai').addEventListener('click', async () => {
172 testAIConnection(platform);
173 });
174 }
175
176 // Load saved settings from storage
177 async function loadSettings(platform) {
178 try {
179 const enabled = await GM.getValue(`${platform}_auto_reply_enabled`, false);
180 const aiProvider = await GM.getValue(`${platform}_ai_provider`, 'openai');
181 const apiKey = await GM.getValue(`${platform}_api_key`, '');
182 const customPrompt = await GM.getValue(`${platform}_custom_prompt`, 'You are responding to a social media comment. Be friendly, engaging, and authentic. Keep responses concise but personalized to the comment content.');
183
184 document.getElementById('auto-reply-toggle').checked = enabled;
185 document.getElementById('ai-provider').value = aiProvider;
186 document.getElementById('api-key').value = apiKey;
187 document.getElementById('custom-prompt').value = customPrompt;
188 } catch (error) {
189 console.error('Error loading settings:', error);
190 }
191 }
192
193 // Save settings to storage
194 async function saveSettings(platform) {
195 try {
196 const enabled = document.getElementById('auto-reply-toggle').checked;
197 const aiProvider = document.getElementById('ai-provider').value;
198 const apiKey = document.getElementById('api-key').value;
199 const customPrompt = document.getElementById('custom-prompt').value;
200
201 await GM.setValue(`${platform}_auto_reply_enabled`, enabled);
202 await GM.setValue(`${platform}_ai_provider`, aiProvider);
203 await GM.setValue(`${platform}_api_key`, apiKey);
204 await GM.setValue(`${platform}_custom_prompt`, customPrompt);
205 } catch (error) {
206 console.error('Error saving settings:', error);
207 }
208 }
209
210 // Test AI connection
211 async function testAIConnection(platform) {
212 try {
213 const aiProvider = document.getElementById('ai-provider').value;
214 const apiKey = document.getElementById('api-key').value;
215 const customPrompt = document.getElementById('custom-prompt').value;
216
217 if (!apiKey) {
218 alert('Please enter your API key');
219 return;
220 }
221
222 const testResponse = await generateAIResponse(
223 "This is a test comment",
224 aiProvider,
225 apiKey,
226 customPrompt
227 );
228
229 alert(`AI Connection Successful!\nSample Response: ${testResponse}`);
230 } catch (error) {
231 console.error('AI Test Error:', error);
232 alert(`AI Test Failed: ${error.message}`);
233 }
234 }
235
236 // Generate AI response based on comment content
237 async function generateAIResponse(commentText, aiProvider, apiKey, customPrompt) {
238 try {
239 const provider = AI_PROVIDERS[aiProvider];
240 if (!provider) {
241 throw new Error('Invalid AI provider selected');
242 }
243
244 const fullPrompt = `${customPrompt}\n\nComment: "${commentText}"\n\nResponse:`;
245
246 let response;
247
248 switch (aiProvider) {
249 case 'openai':
250 response = await axios.post(provider.apiUrl, {
251 model: provider.model,
252 messages: [{ role: 'user', content: fullPrompt }],
253 max_tokens: 150,
254 temperature: 0.7
255 }, {
256 headers: {
257 'Authorization': `Bearer ${apiKey}`,
258 'Content-Type': 'application/json'
259 }
260 });
261 return response.data.choices[0].message.content.trim();
262
263 case 'claude':
264 response = await axios.post(provider.apiUrl, {
265 model: provider.model,
266 messages: [{ role: 'user', content: fullPrompt }],
267 max_tokens: 150,
268 temperature: 0.7
269 }, {
270 headers: {
271 'x-api-key': apiKey,
272 'Content-Type': 'application/json',
273 'anthropic-version': '2023-06-01'
274 }
275 });
276 return response.data.content[0].text.trim();
277
278 case 'gemini':
279 response = await axios.post(`${provider.apiUrl}?key=${apiKey}`, {
280 contents: [{
281 parts: [{
282 text: fullPrompt
283 }]
284 }]
285 }, {
286 headers: {
287 'Content-Type': 'application/json'
288 }
289 });
290 return response.data.candidates[0].content.parts[0].text.trim();
291
292 default:
293 throw new Error('Unsupported AI provider');
294 }
295 } catch (error) {
296 console.error('AI Generation Error:', error);
297 throw new Error(`Failed to generate AI response: ${error.message}`);
298 }
299 }
300
301 // Observe comments and auto-reply when new ones appear
302 function observeComments(platform) {
303 const config = CONFIG[platform];
304 if (!config) return;
305
306 // Create observer
307 const observer = new MutationObserver(async (mutations) => {
308 // Check if auto-reply is enabled
309 const enabled = await GM.getValue(`${platform}_auto_reply_enabled`, false);
310 if (!enabled) return;
311
312 // Process mutations
313 for (const mutation of mutations) {
314 if (mutation.type === 'childList') {
315 mutation.addedNodes.forEach(node => {
316 if (node.nodeType === Node.ELEMENT_NODE) {
317 // Check if this is a new comment
318 if (node.matches && node.matches(config.commentSelector)) {
319 processComment(node, platform);
320 }
321
322 // Also check child elements
323 const comments = node.querySelectorAll ? node.querySelectorAll(config.commentSelector) : [];
324 comments.forEach(comment => processComment(comment, platform));
325 }
326 });
327 }
328 }
329 });
330
331 // Start observing
332 observer.observe(document.body, {
333 childList: true,
334 subtree: true
335 });
336 }
337
338 // Process a comment and potentially reply to it
339 async function processComment(commentElement, platform) {
340 try {
341 // Get settings
342 const enabled = await GM.getValue(`${platform}_auto_reply_enabled`, false);
343 if (!enabled) return;
344
345 const aiProvider = await GM.getValue(`${platform}_ai_provider`, 'openai');
346 const apiKey = await GM.getValue(`${platform}_api_key`, '');
347 const customPrompt = await GM.getValue(`${platform}_custom_prompt`, 'You are responding to a social media comment. Be friendly, engaging, and authentic. Keep responses concise but personalized to the comment content.');
348
349 if (!apiKey) {
350 console.warn('API key not set, skipping AI response generation');
351 return;
352 }
353
354 // Extract comment text (this would need to be adapted for each platform)
355 let commentText = '';
356 if (platform === 'facebook') {
357 const commentTextElement = commentElement.querySelector('[data-sigil="mfeed_pivots_message feed-story-highlight-candidate"] [data-sigil="mfeed_premessage"]');
358 commentText = commentTextElement ? commentTextElement.textContent : '';
359 } else if (platform === 'twitter') {
360 const commentTextElement = commentElement.querySelector('[data-testid="tweetText"]');
361 commentText = commentTextElement ? commentTextElement.textContent : '';
362 } else if (platform === 'youtube') {
363 const commentTextElement = commentElement.querySelector('#comment-content');
364 commentText = commentTextElement ? commentTextElement.textContent : '';
365 } else if (platform === 'tiktok') {
366 const commentTextElement = commentElement.querySelector('[data-e2e="comment-text"]');
367 commentText = commentTextElement ? commentTextElement.textContent : '';
368 }
369
370 if (!commentText.trim()) {
371 console.log('No comment text found, skipping');
372 return;
373 }
374
375 // Generate AI response
376 const aiResponse = await generateAIResponse(commentText, aiProvider, apiKey, customPrompt);
377
378 // Find reply button
379 const replyButton = commentElement.querySelector(CONFIG[platform].replyButtonSelector) ||
380 commentElement.closest(CONFIG[platform].commentSelector)?.querySelector(CONFIG[platform].replyButtonSelector);
381
382 if (replyButton) {
383 // Click reply button
384 replyButton.click();
385
386 // Wait for reply box to appear
387 setTimeout(() => {
388 // Find reply box
389 const replyBox = document.querySelector(CONFIG[platform].replyBoxSelector);
390
391 if (replyBox) {
392 // Fill reply box with AI-generated message
393 replyBox.value = aiResponse;
394
395 // Trigger input event
396 const inputEvent = new Event('input', { bubbles: true });
397 replyBox.dispatchEvent(inputEvent);
398
399 // Wait a bit then click post button
400 setTimeout(() => {
401 const postButton = document.querySelector(CONFIG[platform].postButtonSelector);
402 if (postButton) {
403 postButton.click();
404 console.log(`AI-replied to comment on ${platform}: ${commentText} -> ${aiResponse}`);
405 }
406 }, 1000);
407 }
408 }, 1000);
409 }
410 } catch (error) {
411 console.error(`Error processing comment on ${platform}:`, error);
412 }
413 }
414
415 // Run initialization when page loads
416 if (document.readyState === 'loading') {
417 document.addEventListener('DOMContentLoaded', init);
418 } else {
419 init();
420 }
421})();