Slack NPS Chat Summarizer

Summarizes all NPS chats from the NPS channel using AI

Size

23.5 KB

Version

1.0.1

Created

Oct 30, 2025

Updated

15 days ago

1// ==UserScript==
2// @name		Slack NPS Chat Summarizer
3// @description		Summarizes all NPS chats from the NPS channel using AI
4// @version		1.0.1
5// @match		https://*.app.slack.com/*
6// @icon		data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAACxLAAAsSwGlPZapAAAFGmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0NDg4LCAyMDIwLzA3LzEwLTIyOjA2OjUzICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjIuMCAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMTItMTBUMTA6MzI6MzEtMDg6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTEyLTEwVDEwOjMzOjM2LTA4OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTEyLTEwVDEwOjMzOjM2LTA4OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmExODkyZGRmLTQ4ZDIt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
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('NPS Chat Summarizer extension loaded');
12
13    // Debounce function to prevent excessive calls
14    function debounce(func, wait) {
15        let timeout;
16        return function executedFunction(...args) {
17            const later = () => {
18                clearTimeout(timeout);
19                func(...args);
20            };
21            clearTimeout(timeout);
22            timeout = setTimeout(later, wait);
23        };
24    }
25
26    // Extract all messages from the current Slack channel
27    function extractMessages() {
28        console.log('Extracting messages from Slack channel...');
29        const messages = [];
30        
31        // Find all message elements in Slack
32        const messageElements = document.querySelectorAll('[data-qa="virtual-list-item"]');
33        
34        console.log(`Found ${messageElements.length} message elements`);
35        
36        messageElements.forEach((element, index) => {
37            try {
38                // Extract sender name
39                const senderElement = element.querySelector('[data-qa="message_sender_name"]') ||
40                                     element.querySelector('.c-message__sender_button') ||
41                                     element.querySelector('.c-message__sender');
42                const sender = senderElement ? senderElement.textContent.trim() : 'Unknown';
43                
44                // Extract message text
45                const messageTextElement = element.querySelector('[data-qa="message-text"]') ||
46                                          element.querySelector('.c-message_kit__text') ||
47                                          element.querySelector('.p-rich_text_section');
48                const text = messageTextElement ? messageTextElement.textContent.trim() : '';
49                
50                // Extract timestamp
51                const timestampElement = element.querySelector('[data-qa="message_timestamp"]') ||
52                                        element.querySelector('.c-timestamp');
53                const timestamp = timestampElement ? timestampElement.textContent.trim() : '';
54                
55                if (text && text.length > 0) {
56                    messages.push({
57                        sender: sender,
58                        text: text,
59                        timestamp: timestamp
60                    });
61                }
62            } catch (error) {
63                console.error(`Error extracting message ${index}:`, error);
64            }
65        });
66        
67        console.log(`Successfully extracted ${messages.length} messages`);
68        return messages;
69    }
70
71    // Create and show loading indicator
72    function showLoadingIndicator() {
73        const existingLoader = document.getElementById('nps-summarizer-loader');
74        if (existingLoader) return;
75        
76        const loader = document.createElement('div');
77        loader.id = 'nps-summarizer-loader';
78        loader.innerHTML = `
79            <div style="display: flex; align-items: center; gap: 10px;">
80                <div class="spinner"></div>
81                <span>AI is analyzing NPS chats...</span>
82            </div>
83        `;
84        loader.style.cssText = `
85            position: fixed;
86            top: 20px;
87            right: 20px;
88            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
89            color: white;
90            padding: 15px 20px;
91            border-radius: 8px;
92            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
93            z-index: 10000;
94            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
95            font-size: 14px;
96        `;
97        
98        document.body.appendChild(loader);
99        
100        // Add spinner animation
101        const style = document.createElement('style');
102        style.textContent = `
103            #nps-summarizer-loader .spinner {
104                width: 20px;
105                height: 20px;
106                border: 3px solid rgba(255,255,255,0.3);
107                border-top-color: white;
108                border-radius: 50%;
109                animation: spin 1s linear infinite;
110            }
111            @keyframes spin {
112                to { transform: rotate(360deg); }
113            }
114        `;
115        document.head.appendChild(style);
116    }
117
118    // Hide loading indicator
119    function hideLoadingIndicator() {
120        const loader = document.getElementById('nps-summarizer-loader');
121        if (loader) loader.remove();
122    }
123
124    // Display summary in a modal
125    function displaySummary(summary) {
126        console.log('Displaying summary modal');
127        
128        // Remove existing modal if any
129        const existingModal = document.getElementById('nps-summary-modal');
130        if (existingModal) existingModal.remove();
131        
132        const modal = document.createElement('div');
133        modal.id = 'nps-summary-modal';
134        modal.innerHTML = `
135            <div class="modal-overlay">
136                <div class="modal-content">
137                    <div class="modal-header">
138                        <h2>📊 NPS Chat Summary</h2>
139                        <button class="close-btn" id="close-summary-modal"></button>
140                    </div>
141                    <div class="modal-body">
142                        <div class="summary-section">
143                            <h3>🎯 Key Themes</h3>
144                            <ul>
145                                ${summary.keyThemes.map(theme => `<li>${theme}</li>`).join('')}
146                            </ul>
147                        </div>
148                        
149                        <div class="summary-section">
150                            <h3>😊 Positive Feedback</h3>
151                            <ul>
152                                ${summary.positiveFeedback.map(item => `<li>${item}</li>`).join('')}
153                            </ul>
154                        </div>
155                        
156                        <div class="summary-section">
157                            <h3>⚠️ Issues & Concerns</h3>
158                            <ul>
159                                ${summary.issues.map(item => `<li>${item}</li>`).join('')}
160                            </ul>
161                        </div>
162                        
163                        <div class="summary-section">
164                            <h3>💡 Recommendations</h3>
165                            <ul>
166                                ${summary.recommendations.map(item => `<li>${item}</li>`).join('')}
167                            </ul>
168                        </div>
169                        
170                        <div class="summary-section">
171                            <h3>📈 Overall Sentiment</h3>
172                            <p class="sentiment-text">${summary.overallSentiment}</p>
173                        </div>
174                    </div>
175                </div>
176            </div>
177        `;
178        
179        // Add styles
180        const style = document.createElement('style');
181        style.textContent = `
182            #nps-summary-modal .modal-overlay {
183                position: fixed;
184                top: 0;
185                left: 0;
186                right: 0;
187                bottom: 0;
188                background: rgba(0, 0, 0, 0.7);
189                display: flex;
190                align-items: center;
191                justify-content: center;
192                z-index: 10001;
193                animation: fadeIn 0.3s ease;
194            }
195            
196            #nps-summary-modal .modal-content {
197                background: white;
198                border-radius: 12px;
199                max-width: 800px;
200                max-height: 90vh;
201                width: 90%;
202                overflow: hidden;
203                box-shadow: 0 20px 60px rgba(0,0,0,0.3);
204                animation: slideUp 0.3s ease;
205            }
206            
207            #nps-summary-modal .modal-header {
208                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
209                color: white;
210                padding: 20px 30px;
211                display: flex;
212                justify-content: space-between;
213                align-items: center;
214            }
215            
216            #nps-summary-modal .modal-header h2 {
217                margin: 0;
218                font-size: 24px;
219                font-weight: 600;
220            }
221            
222            #nps-summary-modal .close-btn {
223                background: rgba(255,255,255,0.2);
224                border: none;
225                color: white;
226                font-size: 24px;
227                width: 36px;
228                height: 36px;
229                border-radius: 50%;
230                cursor: pointer;
231                display: flex;
232                align-items: center;
233                justify-content: center;
234                transition: background 0.2s;
235            }
236            
237            #nps-summary-modal .close-btn:hover {
238                background: rgba(255,255,255,0.3);
239            }
240            
241            #nps-summary-modal .modal-body {
242                padding: 30px;
243                overflow-y: auto;
244                max-height: calc(90vh - 80px);
245                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
246            }
247            
248            #nps-summary-modal .summary-section {
249                margin-bottom: 30px;
250            }
251            
252            #nps-summary-modal .summary-section h3 {
253                color: #333;
254                font-size: 18px;
255                margin-bottom: 12px;
256                font-weight: 600;
257            }
258            
259            #nps-summary-modal .summary-section ul {
260                list-style: none;
261                padding: 0;
262                margin: 0;
263            }
264            
265            #nps-summary-modal .summary-section li {
266                background: #f8f9fa;
267                padding: 12px 16px;
268                margin-bottom: 8px;
269                border-radius: 6px;
270                border-left: 3px solid #667eea;
271                color: #333;
272                line-height: 1.5;
273            }
274            
275            #nps-summary-modal .sentiment-text {
276                background: #f8f9fa;
277                padding: 16px;
278                border-radius: 6px;
279                color: #333;
280                line-height: 1.6;
281                margin: 0;
282            }
283            
284            @keyframes fadeIn {
285                from { opacity: 0; }
286                to { opacity: 1; }
287            }
288            
289            @keyframes slideUp {
290                from { transform: translateY(30px); opacity: 0; }
291                to { transform: translateY(0); opacity: 1; }
292            }
293        `;
294        document.head.appendChild(style);
295        
296        document.body.appendChild(modal);
297        
298        // Close modal handlers
299        document.getElementById('close-summary-modal').addEventListener('click', () => {
300            modal.remove();
301        });
302        
303        modal.querySelector('.modal-overlay').addEventListener('click', (e) => {
304            if (e.target.classList.contains('modal-overlay')) {
305                modal.remove();
306            }
307        });
308    }
309
310    // Main function to summarize NPS chats
311    async function summarizeNPSChats() {
312        console.log('Starting NPS chat summarization...');
313        
314        try {
315            showLoadingIndicator();
316            
317            // Extract messages
318            const messages = extractMessages();
319            
320            if (messages.length === 0) {
321                alert('No messages found in the current channel. Please make sure you are in the NPS channel.');
322                hideLoadingIndicator();
323                return;
324            }
325            
326            console.log(`Analyzing ${messages.length} messages with AI...`);
327            
328            // Prepare messages for AI analysis
329            const messagesText = messages.map((msg, idx) => 
330                `[${idx + 1}] ${msg.sender} (${msg.timestamp}): ${msg.text}`
331            ).join('\n\n');
332            
333            // Call AI to analyze and summarize
334            const prompt = `You are analyzing NPS (Net Promoter Score) feedback from a Slack channel. Below are all the messages from the NPS channel. Please analyze them and provide a comprehensive summary.
335
336Messages:
337${messagesText}
338
339Please analyze these NPS chats and provide:
3401. Key themes that emerge from the feedback
3412. Positive feedback and what customers appreciate
3423. Issues and concerns raised by customers
3434. Actionable recommendations for improvement
3445. Overall sentiment analysis
345
346Focus on extracting actionable insights that can help improve the product/service.`;
347
348            const summary = await RM.aiCall(prompt, {
349                type: "json_schema",
350                json_schema: {
351                    name: "nps_summary",
352                    schema: {
353                        type: "object",
354                        properties: {
355                            keyThemes: {
356                                type: "array",
357                                items: { type: "string" },
358                                description: "Main themes identified in the NPS feedback"
359                            },
360                            positiveFeedback: {
361                                type: "array",
362                                items: { type: "string" },
363                                description: "Positive aspects mentioned by customers"
364                            },
365                            issues: {
366                                type: "array",
367                                items: { type: "string" },
368                                description: "Problems and concerns raised"
369                            },
370                            recommendations: {
371                                type: "array",
372                                items: { type: "string" },
373                                description: "Actionable recommendations for improvement"
374                            },
375                            overallSentiment: {
376                                type: "string",
377                                description: "Overall sentiment analysis of the feedback"
378                            }
379                        },
380                        required: ["keyThemes", "positiveFeedback", "issues", "recommendations", "overallSentiment"]
381                    }
382                }
383            });
384            
385            console.log('AI analysis complete:', summary);
386            
387            hideLoadingIndicator();
388            
389            // Display the summary
390            displaySummary(summary);
391            
392            // Store the summary for future reference
393            await GM.setValue('last_nps_summary', JSON.stringify({
394                summary: summary,
395                timestamp: new Date().toISOString(),
396                messageCount: messages.length
397            }));
398            
399        } catch (error) {
400            console.error('Error summarizing NPS chats:', error);
401            hideLoadingIndicator();
402            alert('Failed to summarize NPS chats. Error: ' + error.message);
403        }
404    }
405
406    // Create the summarize button
407    function createSummarizeButton() {
408        console.log('Creating summarize button...');
409        
410        // Remove existing button if any
411        const existingButton = document.getElementById('nps-summarize-btn');
412        if (existingButton) {
413            existingButton.remove();
414        }
415        
416        // Find the channel header to place the button
417        const channelHeader = document.querySelector('[data-qa="channel_header"]') ||
418                             document.querySelector('.p-ia__view_header') ||
419                             document.querySelector('.p-view_header');
420        
421        if (!channelHeader) {
422            console.log('Channel header not found, will retry...');
423            return false;
424        }
425        
426        const button = document.createElement('button');
427        button.id = 'nps-summarize-btn';
428        button.innerHTML = '📊 Summarize NPS Chats';
429        button.style.cssText = `
430            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
431            color: white;
432            border: none;
433            padding: 8px 16px;
434            border-radius: 6px;
435            font-size: 14px;
436            font-weight: 600;
437            cursor: pointer;
438            margin-left: 12px;
439            transition: transform 0.2s, box-shadow 0.2s;
440            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
441            box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
442        `;
443        
444        button.addEventListener('mouseenter', () => {
445            button.style.transform = 'translateY(-2px)';
446            button.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.4)';
447        });
448        
449        button.addEventListener('mouseleave', () => {
450            button.style.transform = 'translateY(0)';
451            button.style.boxShadow = '0 2px 8px rgba(102, 126, 234, 0.3)';
452        });
453        
454        button.addEventListener('click', summarizeNPSChats);
455        
456        // Insert the button into the header
457        const headerActions = channelHeader.querySelector('.p-ia__view_header__actions') ||
458                             channelHeader.querySelector('[data-qa="channel_header_actions"]') ||
459                             channelHeader;
460        
461        headerActions.appendChild(button);
462        console.log('Summarize button created successfully');
463        return true;
464    }
465
466    // Initialize the extension
467    function init() {
468        console.log('Initializing NPS Chat Summarizer...');
469        
470        // Wait for the page to be fully loaded
471        if (document.readyState === 'loading') {
472            document.addEventListener('DOMContentLoaded', init);
473            return;
474        }
475        
476        // Try to create the button
477        const buttonCreated = createSummarizeButton();
478        
479        if (!buttonCreated) {
480            // If button creation failed, retry after a delay
481            setTimeout(init, 2000);
482            return;
483        }
484        
485        // Watch for navigation changes in Slack (SPA)
486        const observer = new MutationObserver(debounce(() => {
487            const existingButton = document.getElementById('nps-summarize-btn');
488            if (!existingButton) {
489                console.log('Button disappeared, recreating...');
490                createSummarizeButton();
491            }
492        }, 1000));
493        
494        observer.observe(document.body, {
495            childList: true,
496            subtree: true
497        });
498        
499        console.log('NPS Chat Summarizer initialized successfully');
500    }
501
502    // Start the extension
503    init();
504})();
Slack NPS Chat Summarizer | Robomonkey