Smart AI Message Assistant

AI-powered assistant that automates messages, reviews conversations, and generates contextual responses for Google Messages

Size

23.6 KB

Version

1.0.1

Created

Nov 28, 2025

Updated

15 days ago

1// ==UserScript==
2// @name		Smart AI Message Assistant
3// @description		AI-powered assistant that automates messages, reviews conversations, and generates contextual responses for Google Messages
4// @version		1.0.1
5// @match		https://*.messages.google.com/*
6// @icon		https://ssl.gstatic.com/android-messages-web/images/2022.3/1x/messages_2022_96dp.png
7// @grant		GM.getValue
8// @grant		GM.setValue
9// @grant		GM.xmlhttpRequest
10// ==/UserScript==
11(function() {
12    'use strict';
13
14    console.log('Smart AI Message Assistant initialized');
15
16    // Utility function to debounce
17    function debounce(func, wait) {
18        let timeout;
19        return function executedFunction(...args) {
20            const later = () => {
21                clearTimeout(timeout);
22                func(...args);
23            };
24            clearTimeout(timeout);
25            timeout = setTimeout(later, wait);
26        };
27    }
28
29    // Add custom styles for AI assistant UI
30    function addStyles() {
31        const styles = `
32            .ai-assistant-container {
33                position: fixed;
34                bottom: 20px;
35                right: 20px;
36                z-index: 10000;
37                font-family: 'Google Sans', Roboto, Arial, sans-serif;
38            }
39
40            .ai-assistant-button {
41                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
42                color: white;
43                border: none;
44                border-radius: 50%;
45                width: 60px;
46                height: 60px;
47                font-size: 24px;
48                cursor: pointer;
49                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
50                transition: all 0.3s ease;
51                display: flex;
52                align-items: center;
53                justify-content: center;
54            }
55
56            .ai-assistant-button:hover {
57                transform: scale(1.1);
58                box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
59            }
60
61            .ai-assistant-panel {
62                position: fixed;
63                bottom: 90px;
64                right: 20px;
65                width: 380px;
66                max-height: 600px;
67                background: white;
68                border-radius: 16px;
69                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
70                display: none;
71                flex-direction: column;
72                overflow: hidden;
73                z-index: 10001;
74            }
75
76            .ai-assistant-panel.active {
77                display: flex;
78            }
79
80            .ai-panel-header {
81                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
82                color: white;
83                padding: 16px 20px;
84                font-size: 18px;
85                font-weight: 500;
86                display: flex;
87                justify-content: space-between;
88                align-items: center;
89            }
90
91            .ai-panel-close {
92                background: none;
93                border: none;
94                color: white;
95                font-size: 24px;
96                cursor: pointer;
97                padding: 0;
98                width: 30px;
99                height: 30px;
100                display: flex;
101                align-items: center;
102                justify-content: center;
103                border-radius: 50%;
104                transition: background 0.2s;
105            }
106
107            .ai-panel-close:hover {
108                background: rgba(255, 255, 255, 0.2);
109            }
110
111            .ai-panel-content {
112                padding: 20px;
113                overflow-y: auto;
114                flex: 1;
115            }
116
117            .ai-feature-section {
118                margin-bottom: 20px;
119            }
120
121            .ai-feature-title {
122                font-size: 14px;
123                font-weight: 500;
124                color: #5f6368;
125                margin-bottom: 10px;
126                text-transform: uppercase;
127                letter-spacing: 0.5px;
128            }
129
130            .ai-button {
131                width: 100%;
132                padding: 12px 16px;
133                margin-bottom: 10px;
134                background: #f1f3f4;
135                border: none;
136                border-radius: 8px;
137                cursor: pointer;
138                font-size: 14px;
139                font-weight: 500;
140                color: #202124;
141                transition: all 0.2s;
142                text-align: left;
143                display: flex;
144                align-items: center;
145                gap: 10px;
146            }
147
148            .ai-button:hover {
149                background: #e8eaed;
150                transform: translateY(-1px);
151            }
152
153            .ai-button:active {
154                transform: translateY(0);
155            }
156
157            .ai-button-icon {
158                font-size: 18px;
159            }
160
161            .ai-suggestions-container {
162                margin-top: 15px;
163                padding: 15px;
164                background: #f8f9fa;
165                border-radius: 8px;
166                border-left: 4px solid #667eea;
167            }
168
169            .ai-suggestion-item {
170                padding: 10px;
171                margin-bottom: 8px;
172                background: white;
173                border-radius: 6px;
174                cursor: pointer;
175                transition: all 0.2s;
176                font-size: 14px;
177                line-height: 1.5;
178            }
179
180            .ai-suggestion-item:hover {
181                background: #e8f0fe;
182                transform: translateX(4px);
183            }
184
185            .ai-suggestion-item:last-child {
186                margin-bottom: 0;
187            }
188
189            .ai-loading {
190                display: flex;
191                align-items: center;
192                justify-content: center;
193                padding: 20px;
194                color: #5f6368;
195                font-size: 14px;
196            }
197
198            .ai-loading-spinner {
199                border: 3px solid #f3f3f3;
200                border-top: 3px solid #667eea;
201                border-radius: 50%;
202                width: 24px;
203                height: 24px;
204                animation: spin 1s linear infinite;
205                margin-right: 10px;
206            }
207
208            @keyframes spin {
209                0% { transform: rotate(0deg); }
210                100% { transform: rotate(360deg); }
211            }
212
213            .ai-context-display {
214                padding: 12px;
215                background: #e8f0fe;
216                border-radius: 8px;
217                font-size: 13px;
218                line-height: 1.6;
219                color: #202124;
220                margin-top: 10px;
221                max-height: 200px;
222                overflow-y: auto;
223            }
224
225            .ai-status-badge {
226                display: inline-block;
227                padding: 4px 8px;
228                border-radius: 12px;
229                font-size: 11px;
230                font-weight: 500;
231                margin-left: 8px;
232            }
233
234            .ai-status-active {
235                background: #e6f4ea;
236                color: #137333;
237            }
238
239            .ai-status-inactive {
240                background: #fce8e6;
241                color: #c5221f;
242            }
243
244            .compose-ai-toolbar {
245                display: flex;
246                gap: 8px;
247                padding: 8px;
248                background: #f8f9fa;
249                border-radius: 8px;
250                margin-bottom: 8px;
251            }
252
253            .compose-ai-button {
254                padding: 8px 12px;
255                background: white;
256                border: 1px solid #dadce0;
257                border-radius: 6px;
258                cursor: pointer;
259                font-size: 13px;
260                font-weight: 500;
261                color: #5f6368;
262                transition: all 0.2s;
263                display: flex;
264                align-items: center;
265                gap: 6px;
266            }
267
268            .compose-ai-button:hover {
269                background: #f1f3f4;
270                border-color: #667eea;
271                color: #667eea;
272            }
273        `;
274
275        const styleSheet = document.createElement('style');
276        styleSheet.textContent = styles;
277        document.head.appendChild(styleSheet);
278    }
279
280    // Extract conversation context
281    function extractConversationContext() {
282        const messages = [];
283        const messageElements = document.querySelectorAll('mws-message-wrapper[data-e2e-message-wrapper]');
284        
285        messageElements.forEach(msgEl => {
286            const coreEl = msgEl.querySelector('[data-e2e-message-wrapper-core]');
287            if (!coreEl) return;
288            
289            const isOutgoing = coreEl.getAttribute('data-e2e-message-outgoing') === 'true';
290            const textContent = msgEl.querySelector('.text-msg-content, [data-e2e-message-content]')?.textContent?.trim();
291            
292            if (textContent) {
293                messages.push({
294                    sender: isOutgoing ? 'You' : 'Contact',
295                    text: textContent,
296                    isOutgoing: isOutgoing
297                });
298            }
299        });
300
301        return messages;
302    }
303
304    // Get conversation summary
305    async function getConversationSummary() {
306        const messages = extractConversationContext();
307        
308        if (messages.length === 0) {
309            return 'No messages found in this conversation.';
310        }
311
312        const conversationText = messages.map(m => `${m.sender}: ${m.text}`).join('\n');
313        
314        try {
315            const summary = await RM.aiCall(
316                `Analyze this conversation and provide a brief summary with key points:\n\n${conversationText}`,
317                {
318                    type: "json_schema",
319                    json_schema: {
320                        name: "conversation_summary",
321                        schema: {
322                            type: "object",
323                            properties: {
324                                summary: { type: "string" },
325                                keyTopics: { type: "array", items: { type: "string" } },
326                                sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"] },
327                                suggestedActions: { type: "array", items: { type: "string" } }
328                            },
329                            required: ["summary", "keyTopics", "sentiment"]
330                        }
331                    }
332                }
333            );
334            
335            return summary;
336        } catch (error) {
337            console.error('Error getting conversation summary:', error);
338            return { summary: 'Error analyzing conversation', keyTopics: [], sentiment: 'neutral' };
339        }
340    }
341
342    // Generate smart replies
343    async function generateSmartReplies() {
344        const messages = extractConversationContext();
345        
346        if (messages.length === 0) {
347            return ['Hello!', 'How can I help?', 'Thanks for reaching out!'];
348        }
349
350        const lastMessages = messages.slice(-5);
351        const conversationText = lastMessages.map(m => `${m.sender}: ${m.text}`).join('\n');
352        
353        try {
354            const replies = await RM.aiCall(
355                `Based on this conversation, generate 3 contextually appropriate reply suggestions:\n\n${conversationText}`,
356                {
357                    type: "json_schema",
358                    json_schema: {
359                        name: "smart_replies",
360                        schema: {
361                            type: "object",
362                            properties: {
363                                replies: {
364                                    type: "array",
365                                    items: { type: "string" },
366                                    minItems: 3,
367                                    maxItems: 3
368                                }
369                            },
370                            required: ["replies"]
371                        }
372                    }
373                }
374            );
375            
376            return replies.replies;
377        } catch (error) {
378            console.error('Error generating smart replies:', error);
379            return ['Thanks for your message!', 'I appreciate that.', 'Sounds good!'];
380        }
381    }
382
383    // Generate custom response
384    async function generateCustomResponse(instruction) {
385        const messages = extractConversationContext();
386        const conversationText = messages.slice(-5).map(m => `${m.sender}: ${m.text}`).join('\n');
387        
388        try {
389            const response = await RM.aiCall(
390                `Conversation context:\n${conversationText}\n\nUser instruction: ${instruction}\n\nGenerate an appropriate response:`
391            );
392            
393            return response;
394        } catch (error) {
395            console.error('Error generating custom response:', error);
396            return 'Sorry, I could not generate a response.';
397        }
398    }
399
400    // Insert text into message input
401    function insertTextIntoCompose(text) {
402        // Try different selectors for the compose area
403        const composeSelectors = [
404            'textarea[aria-label*="message" i]',
405            'textarea[placeholder*="message" i]',
406            '[contenteditable="true"][aria-label*="message" i]',
407            'mws-message-compose textarea',
408            'mws-message-compose [contenteditable="true"]'
409        ];
410
411        let composeElement = null;
412        for (const selector of composeSelectors) {
413            composeElement = document.querySelector(selector);
414            if (composeElement) break;
415        }
416
417        if (!composeElement) {
418            console.error('Could not find compose element');
419            alert('Please click on the message input field first, then try again.');
420            return false;
421        }
422
423        // Set the value
424        if (composeElement.tagName === 'TEXTAREA' || composeElement.tagName === 'INPUT') {
425            composeElement.value = text;
426            composeElement.dispatchEvent(new Event('input', { bubbles: true }));
427            composeElement.dispatchEvent(new Event('change', { bubbles: true }));
428        } else if (composeElement.isContentEditable) {
429            composeElement.textContent = text;
430            composeElement.dispatchEvent(new Event('input', { bubbles: true }));
431        }
432
433        composeElement.focus();
434        return true;
435    }
436
437    // Create AI Assistant UI
438    function createAssistantUI() {
439        const container = document.createElement('div');
440        container.className = 'ai-assistant-container';
441        
442        // Main button
443        const button = document.createElement('button');
444        button.className = 'ai-assistant-button';
445        button.innerHTML = '🤖';
446        button.title = 'AI Message Assistant';
447        
448        // Panel
449        const panel = document.createElement('div');
450        panel.className = 'ai-assistant-panel';
451        
452        panel.innerHTML = `
453            <div class="ai-panel-header">
454                <span>AI Assistant</span>
455                <button class="ai-panel-close">×</button>
456            </div>
457            <div class="ai-panel-content">
458                <div class="ai-feature-section">
459                    <div class="ai-feature-title">📊 Conversation Analysis</div>
460                    <button class="ai-button" data-action="analyze">
461                        <span class="ai-button-icon">🔍</span>
462                        <span>Analyze Conversation</span>
463                    </button>
464                    <button class="ai-button" data-action="summary">
465                        <span class="ai-button-icon">📝</span>
466                        <span>Get Summary</span>
467                    </button>
468                </div>
469                
470                <div class="ai-feature-section">
471                    <div class="ai-feature-title">💬 Smart Replies</div>
472                    <button class="ai-button" data-action="smart-replies">
473                        <span class="ai-button-icon"></span>
474                        <span>Generate Reply Suggestions</span>
475                    </button>
476                    <button class="ai-button" data-action="friendly">
477                        <span class="ai-button-icon">😊</span>
478                        <span>Friendly Response</span>
479                    </button>
480                    <button class="ai-button" data-action="professional">
481                        <span class="ai-button-icon">💼</span>
482                        <span>Professional Response</span>
483                    </button>
484                    <button class="ai-button" data-action="brief">
485                        <span class="ai-button-icon"></span>
486                        <span>Brief Response</span>
487                    </button>
488                </div>
489                
490                <div class="ai-feature-section">
491                    <div class="ai-feature-title">🎯 Quick Actions</div>
492                    <button class="ai-button" data-action="thank">
493                        <span class="ai-button-icon">🙏</span>
494                        <span>Thank You Message</span>
495                    </button>
496                    <button class="ai-button" data-action="followup">
497                        <span class="ai-button-icon">📞</span>
498                        <span>Follow-up Message</span>
499                    </button>
500                    <button class="ai-button" data-action="decline">
501                        <span class="ai-button-icon">🚫</span>
502                        <span>Polite Decline</span>
503                    </button>
504                </div>
505                
506                <div id="ai-results-container"></div>
507            </div>
508        `;
509        
510        container.appendChild(button);
511        container.appendChild(panel);
512        document.body.appendChild(container);
513        
514        // Event listeners
515        button.addEventListener('click', () => {
516            panel.classList.toggle('active');
517        });
518        
519        panel.querySelector('.ai-panel-close').addEventListener('click', () => {
520            panel.classList.remove('active');
521        });
522        
523        // Action buttons
524        panel.querySelectorAll('.ai-button[data-action]').forEach(btn => {
525            btn.addEventListener('click', async () => {
526                const action = btn.getAttribute('data-action');
527                await handleAction(action);
528            });
529        });
530        
531        console.log('AI Assistant UI created');
532    }
533
534    // Show loading state
535    function showLoading() {
536        const resultsContainer = document.getElementById('ai-results-container');
537        resultsContainer.innerHTML = `
538            <div class="ai-loading">
539                <div class="ai-loading-spinner"></div>
540                <span>AI is thinking...</span>
541            </div>
542        `;
543    }
544
545    // Display results
546    function displayResults(content) {
547        const resultsContainer = document.getElementById('ai-results-container');
548        resultsContainer.innerHTML = content;
549    }
550
551    // Handle actions
552    async function handleAction(action) {
553        showLoading();
554        
555        try {
556            switch (action) {
557                case 'analyze':
558                    await handleAnalyze();
559                    break;
560                case 'summary':
561                    await handleSummary();
562                    break;
563                case 'smart-replies':
564                    await handleSmartReplies();
565                    break;
566                case 'friendly':
567                    await handleCustomResponse('Generate a friendly, warm response');
568                    break;
569                case 'professional':
570                    await handleCustomResponse('Generate a professional, formal response');
571                    break;
572                case 'brief':
573                    await handleCustomResponse('Generate a brief, concise response');
574                    break;
575                case 'thank':
576                    await handleCustomResponse('Generate a thank you message');
577                    break;
578                case 'followup':
579                    await handleCustomResponse('Generate a follow-up message');
580                    break;
581                case 'decline':
582                    await handleCustomResponse('Generate a polite decline message');
583                    break;
584            }
585        } catch (error) {
586            console.error('Error handling action:', error);
587            displayResults(`
588                <div class="ai-context-display" style="background: #fce8e6; color: #c5221f;">
589                    Error: ${error.message || 'Something went wrong'}
590                </div>
591            `);
592        }
593    }
594
595    async function handleAnalyze() {
596        const messages = extractConversationContext();
597        const summary = await getConversationSummary();
598        
599        displayResults(`
600            <div class="ai-suggestions-container">
601                <div class="ai-feature-title">Conversation Analysis</div>
602                <div class="ai-context-display">
603                    <strong>Summary:</strong><br>${summary.summary}<br><br>
604                    <strong>Key Topics:</strong><br>${summary.keyTopics?.join(', ') || 'None'}<br><br>
605                    <strong>Sentiment:</strong> ${summary.sentiment}<br><br>
606                    ${summary.suggestedActions ? `<strong>Suggested Actions:</strong><br>${summary.suggestedActions.join('<br>')}` : ''}
607                </div>
608                <div style="margin-top: 10px; font-size: 12px; color: #5f6368;">
609                    Total messages: ${messages.length}
610                </div>
611            </div>
612        `);
613    }
614
615    async function handleSummary() {
616        const summary = await getConversationSummary();
617        
618        displayResults(`
619            <div class="ai-suggestions-container">
620                <div class="ai-feature-title">Conversation Summary</div>
621                <div class="ai-context-display">
622                    ${summary.summary}
623                </div>
624            </div>
625        `);
626    }
627
628    async function handleSmartReplies() {
629        const replies = await generateSmartReplies();
630        
631        const repliesHTML = replies.map((reply, index) => `
632            <div class="ai-suggestion-item" data-reply="${reply.replace(/"/g, '&quot;')}">
633                ${reply}
634            </div>
635        `).join('');
636        
637        displayResults(`
638            <div class="ai-suggestions-container">
639                <div class="ai-feature-title">Smart Reply Suggestions</div>
640                ${repliesHTML}
641            </div>
642        `);
643        
644        // Add click handlers to suggestions
645        document.querySelectorAll('.ai-suggestion-item').forEach(item => {
646            item.addEventListener('click', () => {
647                const reply = item.getAttribute('data-reply');
648                insertTextIntoCompose(reply);
649            });
650        });
651    }
652
653    async function handleCustomResponse(instruction) {
654        const response = await generateCustomResponse(instruction);
655        
656        displayResults(`
657            <div class="ai-suggestions-container">
658                <div class="ai-feature-title">Generated Response</div>
659                <div class="ai-suggestion-item" data-reply="${response.replace(/"/g, '&quot;')}">
660                    ${response}
661                </div>
662            </div>
663        `);
664        
665        // Add click handler
666        document.querySelector('.ai-suggestion-item').addEventListener('click', () => {
667            insertTextIntoCompose(response);
668        });
669    }
670
671    // Initialize
672    function init() {
673        console.log('Initializing Smart AI Message Assistant...');
674        
675        // Wait for page to be ready
676        if (document.readyState === 'loading') {
677            document.addEventListener('DOMContentLoaded', init);
678            return;
679        }
680        
681        // Add styles
682        addStyles();
683        
684        // Wait a bit for the page to fully load
685        setTimeout(() => {
686            createAssistantUI();
687            console.log('Smart AI Message Assistant ready!');
688        }, 2000);
689    }
690
691    // Start the extension
692    init();
693})();
Smart AI Message Assistant | Robomonkey