Discord Mass User Inviter

Invite multiple Discord users to servers with checkboxes and automation controls

Size

56.4 KB

Version

1.2.4

Created

Nov 9, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		Discord Mass User Inviter
3// @description		Invite multiple Discord users to servers with checkboxes and automation controls
4// @version		1.2.4
5// @match		https://*.discord.com/*
6// @icon		data:image/png;base64,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
7// @grant		GM.getValue
8// @grant		GM.setValue
9// ==/UserScript==
10(function() {
11    'use strict';
12
13    console.log('Discord Mass User Inviter - Extension loaded');
14
15    // State management
16    let isInviting = false;
17    let invitedUsers = new Set();
18    let selectedUsers = new Set();
19    let targetServerId = null;
20    let isSelecting = false; // Flag to track if selection/deselection is in progress
21
22    // Debounce function for MutationObserver
23    function debounce(func, wait) {
24        let timeout;
25        return function executedFunction(...args) {
26            const later = () => {
27                clearTimeout(timeout);
28                func(...args);
29            };
30            clearTimeout(timeout);
31            timeout = setTimeout(later, wait);
32        };
33    }
34
35    // Load saved state
36    async function loadState() {
37        try {
38            const savedInvited = await GM.getValue('invitedUsers', '[]');
39            const savedServerId = await GM.getValue('targetServerId', null);
40            invitedUsers = new Set(JSON.parse(savedInvited));
41            targetServerId = savedServerId;
42            console.log('Loaded state:', { invitedUsers: invitedUsers.size, targetServerId });
43        } catch (error) {
44            console.error('Error loading state:', error);
45        }
46    }
47
48    // Save state
49    async function saveState() {
50        try {
51            await GM.setValue('invitedUsers', JSON.stringify([...invitedUsers]));
52            await GM.setValue('targetServerId', targetServerId);
53            console.log('State saved');
54        } catch (error) {
55            console.error('Error saving state:', error);
56        }
57    }
58
59    // Add styles
60    TM_addStyle(`
61        .mass-inviter-checkbox {
62            width: 16px;
63            height: 16px;
64            margin-right: 8px;
65            cursor: pointer;
66            flex-shrink: 0;
67        }
68
69        .mass-inviter-control-panel {
70            position: fixed;
71            top: 60px;
72            right: 20px;
73            background: #2b2d31;
74            border: 1px solid #1e1f22;
75            border-radius: 8px;
76            padding: 0;
77            z-index: 10000;
78            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
79            min-width: 280px;
80            color: #dbdee1;
81            font-family: 'gg sans', 'Noto Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
82        }
83
84        .mass-inviter-header {
85            background: #1e1f22;
86            padding: 12px 16px;
87            border-radius: 8px 8px 0 0;
88            cursor: move;
89            display: flex;
90            justify-content: space-between;
91            align-items: center;
92            user-select: none;
93        }
94
95        .mass-inviter-header:active {
96            cursor: grabbing;
97        }
98
99        .mass-inviter-control-panel h3 {
100            margin: 0;
101            font-size: 16px;
102            font-weight: 600;
103            color: #f2f3f5;
104        }
105
106        .mass-inviter-minimize-btn {
107            background: transparent;
108            border: none;
109            color: #b5bac1;
110            font-size: 20px;
111            cursor: pointer;
112            padding: 0;
113            width: 24px;
114            height: 24px;
115            display: flex;
116            align-items: center;
117            justify-content: center;
118            border-radius: 4px;
119            transition: background-color 0.2s, color 0.2s;
120        }
121
122        .mass-inviter-minimize-btn:hover {
123            background: #4e5058;
124            color: #ffffff;
125        }
126
127        .mass-inviter-content {
128            padding: 16px;
129            max-height: 600px;
130            overflow-y: auto;
131        }
132
133        .mass-inviter-content.minimized {
134            display: none;
135        }
136
137        .mass-inviter-control-panel h3 {
138            margin: 0 0 12px 0;
139            font-size: 16px;
140            font-weight: 600;
141            color: #f2f3f5;
142        }
143
144        .mass-inviter-button {
145            width: 100%;
146            padding: 10px 16px;
147            margin: 6px 0;
148            border: none;
149            border-radius: 4px;
150            font-size: 14px;
151            font-weight: 500;
152            cursor: pointer;
153            transition: background-color 0.2s;
154            color: white;
155        }
156
157        .mass-inviter-button.primary {
158            background: #5865f2;
159        }
160
161        .mass-inviter-button.primary:hover {
162            background: #4752c4;
163        }
164
165        .mass-inviter-button.secondary {
166            background: #4e5058;
167        }
168
169        .mass-inviter-button.secondary:hover {
170            background: #6d6f78;
171        }
172
173        .mass-inviter-button.danger {
174            background: #da373c;
175        }
176
177        .mass-inviter-button.danger:hover {
178            background: #a12d30;
179        }
180
181        .mass-inviter-button.success {
182            background: #248046;
183        }
184
185        .mass-inviter-button.success:hover {
186            background: #1a6334;
187        }
188
189        .mass-inviter-button:disabled {
190            opacity: 0.5;
191            cursor: not-allowed;
192        }
193
194        .mass-inviter-input {
195            width: 100%;
196            padding: 10px;
197            margin: 6px 0;
198            background: #1e1f22;
199            border: 1px solid #4e5058;
200            border-radius: 4px;
201            color: #dbdee1;
202            font-size: 14px;
203            box-sizing: border-box;
204        }
205
206        .mass-inviter-input:focus {
207            outline: none;
208            border-color: #5865f2;
209        }
210
211        .mass-inviter-status {
212            margin: 12px 0;
213            padding: 8px;
214            background: #1e1f22;
215            border-radius: 4px;
216            font-size: 12px;
217            color: #b5bac1;
218        }
219
220        .mass-inviter-invited {
221            opacity: 0.5;
222            background: #2d7d46 !important;
223        }
224
225        .mass-inviter-label {
226            font-size: 12px;
227            font-weight: 600;
228            color: #b5bac1;
229            margin-bottom: 4px;
230            display: block;
231        }
232
233        .mass-inviter-divider {
234            height: 1px;
235            background: #4e5058;
236            margin: 12px 0;
237        }
238    `);
239
240    // Get username from member element
241    function getUsernameFromMember(memberElement) {
242        const usernameElement = memberElement.querySelector('.username__703b9, .name__703b9');
243        return usernameElement ? usernameElement.textContent.trim() : null;
244    }
245
246    // Get user ID from member element
247    function getUserIdFromMember(memberElement) {
248        const listItemId = memberElement.getAttribute('data-list-item-id');
249        return listItemId || getUsernameFromMember(memberElement);
250    }
251
252    // Check if user is already invited
253    function isUserInvited(userId) {
254        return invitedUsers.has(userId);
255    }
256
257    // Add checkbox to member
258    function addCheckboxToMember(memberElement) {
259        // Check if checkbox already exists
260        if (memberElement.querySelector('.mass-inviter-checkbox')) {
261            return;
262        }
263
264        const userId = getUserIdFromMember(memberElement);
265        if (!userId) return;
266
267        const checkbox = document.createElement('input');
268        checkbox.type = 'checkbox';
269        checkbox.className = 'mass-inviter-checkbox';
270        checkbox.dataset.userId = userId;
271
272        // Check if user was already invited
273        if (isUserInvited(userId)) {
274            memberElement.classList.add('mass-inviter-invited');
275            checkbox.checked = false;
276            checkbox.disabled = true;
277        } else {
278            checkbox.checked = selectedUsers.has(userId);
279        }
280
281        checkbox.addEventListener('change', (e) => {
282            if (e.target.checked) {
283                selectedUsers.add(userId);
284            } else {
285                selectedUsers.delete(userId);
286            }
287            updateControlPanel();
288        });
289
290        // Insert checkbox at the beginning of the member element
291        const memberInner = memberElement.querySelector('.memberInner__5d473, .layout__91a9d');
292        if (memberInner) {
293            memberInner.insertBefore(checkbox, memberInner.firstChild);
294        }
295    }
296
297    // Add checkboxes to all members
298    function addCheckboxesToAllMembers() {
299        const memberElements = document.querySelectorAll('.member__5d473[role="listitem"]');
300        console.log('Found members:', memberElements.length);
301        memberElements.forEach(addCheckboxToMember);
302    }
303
304    // Create control panel
305    function createControlPanel() {
306        // Remove existing panel if any
307        const existingPanel = document.getElementById('mass-inviter-panel');
308        if (existingPanel) {
309            existingPanel.remove();
310        }
311
312        // Get list of servers
313        const guilds = [];
314        const guildElements = document.querySelectorAll('[data-list-item-id^="guildsnav___"]');
315        guildElements.forEach(el => {
316            const guildId = el.getAttribute('data-list-item-id').replace('guildsnav___', '');
317            // Get server name from multiple possible locations
318            const hiddenVisuallySpan = el.querySelector('.hiddenVisually_b18fe2');
319            const dndContainer = el.querySelector('[data-dnd-name]');
320            const guildName = (hiddenVisuallySpan ? hiddenVisuallySpan.textContent.trim() : null) ||
321                             (dndContainer ? dndContainer.getAttribute('data-dnd-name') : null) ||
322                             el.getAttribute('aria-label') || 
323                             'Server ' + guildId;
324            if (guildId !== 'home' && guildId !== 'create-join-button' && guildId !== 'guild-discover-button' && guildId !== 'app-download-button') {
325                guilds.push({ id: guildId, name: guildName });
326            }
327        });
328
329        const panel = document.createElement('div');
330        panel.id = 'mass-inviter-panel';
331        panel.className = 'mass-inviter-control-panel';
332
333        // Build server options HTML
334        let serverOptionsHTML = '<option value="">Select a server...</option>';
335        guilds.forEach(guild => {
336            const selected = guild.id === targetServerId ? 'selected' : '';
337            serverOptionsHTML += `<option value="${guild.id}" ${selected}>${guild.name}</option>`;
338        });
339
340        panel.innerHTML = `
341            <div class="mass-inviter-header">
342                <h3>Mass User Inviter</h3>
343                <button class="mass-inviter-minimize-btn" id="mass-inviter-minimize-btn">-</button>
344            </div>
345            
346            <div class="mass-inviter-content">
347                <label class="mass-inviter-label">Target Server</label>
348                <select id="mass-inviter-server-select" class="mass-inviter-input">
349                    ${serverOptionsHTML}
350                </select>
351                
352                <div class="mass-inviter-divider"></div>
353                
354                <button id="mass-inviter-select-all" class="mass-inviter-button secondary">
355                    Select All
356                </button>
357                
358                <button id="mass-inviter-deselect-all" class="mass-inviter-button secondary">
359                    Deselect All
360                </button>
361                
362                <button id="mass-inviter-stop-selection" class="mass-inviter-button danger" style="display: none;">
363                    Stop Selection
364                </button>
365                
366                <div class="mass-inviter-divider"></div>
367                
368                <button id="mass-inviter-start" class="mass-inviter-button success">
369                    Start Inviting
370                </button>
371                
372                <button id="mass-inviter-pause" class="mass-inviter-button danger" style="display: none;">
373                    Pause
374                </button>
375                
376                <div class="mass-inviter-status" id="mass-inviter-status">
377                    Selected: <span id="mass-inviter-selected-count">0</span> users<br>
378                    Invited: <span id="mass-inviter-invited-count">${invitedUsers.size}</span> users
379                </div>
380                
381                <button id="mass-inviter-reset" class="mass-inviter-button secondary">
382                    Reset Invited List
383                </button>
384            </div>
385        `;
386
387        document.body.appendChild(panel);
388
389        // Add event listeners
390        document.getElementById('mass-inviter-server-select').addEventListener('change', async (e) => {
391            targetServerId = e.target.value;
392            await saveState();
393            console.log('Target server changed to:', targetServerId);
394        });
395
396        document.getElementById('mass-inviter-select-all').addEventListener('click', selectAllUsers);
397        document.getElementById('mass-inviter-deselect-all').addEventListener('click', deselectAllUsers);
398        document.getElementById('mass-inviter-stop-selection').addEventListener('click', stopSelection);
399        document.getElementById('mass-inviter-start').addEventListener('click', startInviting);
400        document.getElementById('mass-inviter-pause').addEventListener('click', pauseInviting);
401        document.getElementById('mass-inviter-reset').addEventListener('click', resetInvitedList);
402
403        // Add minimize/maximize functionality
404        const minimizeBtn = document.getElementById('mass-inviter-minimize-btn');
405        const content = panel.querySelector('.mass-inviter-content');
406        let isMinimized = false;
407
408        minimizeBtn.addEventListener('click', () => {
409            isMinimized = !isMinimized;
410            if (isMinimized) {
411                content.classList.add('minimized');
412                minimizeBtn.textContent = '+';
413            } else {
414                content.classList.remove('minimized');
415                minimizeBtn.textContent = '-';
416            }
417        });
418
419        // Add drag functionality
420        const header = panel.querySelector('.mass-inviter-header');
421        let isDragging = false;
422        let currentX;
423        let currentY;
424        let initialX;
425        let initialY;
426        let xOffset = 0;
427        let yOffset = 0;
428
429        header.addEventListener('mousedown', dragStart);
430        document.addEventListener('mousemove', drag);
431        document.addEventListener('mouseup', dragEnd);
432
433        function dragStart(e) {
434            initialX = e.clientX - xOffset;
435            initialY = e.clientY - yOffset;
436
437            if (e.target === header || e.target.tagName === 'H3') {
438                isDragging = true;
439            }
440        }
441
442        function drag(e) {
443            if (isDragging) {
444                e.preventDefault();
445                
446                currentX = e.clientX - initialX;
447                currentY = e.clientY - initialY;
448
449                xOffset = currentX;
450                yOffset = currentY;
451
452                setTranslate(currentX, currentY, panel);
453            }
454        }
455
456        function dragEnd() {
457            initialX = currentX;
458            initialY = currentY;
459            isDragging = false;
460        }
461
462        function setTranslate(xPos, yPos, el) {
463            el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
464        }
465
466        updateControlPanel();
467    }
468
469    // Update control panel
470    function updateControlPanel() {
471        const selectedCountElement = document.getElementById('mass-inviter-selected-count');
472        const invitedCountElement = document.getElementById('mass-inviter-invited-count');
473        
474        if (selectedCountElement) {
475            selectedCountElement.textContent = selectedUsers.size;
476        }
477        if (invitedCountElement) {
478            invitedCountElement.textContent = invitedUsers.size;
479        }
480    }
481
482    // Select all users
483    async function selectAllUsers() {
484        if (isSelecting) {
485            console.log('Selection already in progress');
486            return;
487        }
488
489        console.log('Select All clicked - loading and selecting all users...');
490        
491        const membersContainer = document.querySelector('.members_c8ffbb');
492        if (!membersContainer) {
493            console.log('Members container not found');
494            return;
495        }
496
497        isSelecting = true;
498        
499        // Show stop button, hide select/deselect buttons
500        document.getElementById('mass-inviter-select-all').style.display = 'none';
501        document.getElementById('mass-inviter-deselect-all').style.display = 'none';
502        document.getElementById('mass-inviter-stop-selection').style.display = 'block';
503
504        // The members_c8ffbb div itself is the scrollable container
505        const scrollableContainer = membersContainer;
506        let currentMemberCount = 0;
507        let noChangeCount = 0;
508        let totalSelected = 0;
509
510        console.log('Starting continuous scroll and select...');
511        console.log('Scrollable container found:', scrollableContainer.className);
512
513        while (noChangeCount < 3 && isSelecting) {
514            // Get current member count before scrolling
515            const beforeScrollCount = document.querySelectorAll('.member__5d473[role="listitem"]').length;
516            
517            // Add checkboxes to currently visible members
518            addCheckboxesToAllMembers();
519            
520            // Wait for checkboxes to be added
521            await new Promise(resolve => setTimeout(resolve, 200));
522            
523            // Select all currently available checkboxes
524            const checkboxes = document.querySelectorAll('.mass-inviter-checkbox:not(:disabled)');
525            let newlySelected = 0;
526            checkboxes.forEach(checkbox => {
527                if (!checkbox.checked) {
528                    checkbox.checked = true;
529                    selectedUsers.add(checkbox.dataset.userId);
530                    newlySelected++;
531                }
532            });
533            
534            if (newlySelected > 0) {
535                totalSelected += newlySelected;
536                console.log('Selected', newlySelected, 'new users. Total selected:', totalSelected);
537                updateControlPanel();
538            }
539            
540            // Check if we should stop
541            if (!isSelecting) {
542                console.log('Selection stopped by user');
543                break;
544            }
545            
546            // Scroll down in chunks (500px at a time)
547            const currentScrollTop = scrollableContainer.scrollTop;
548            const scrollHeight = scrollableContainer.scrollHeight;
549            const clientHeight = scrollableContainer.clientHeight;
550            const maxScroll = scrollHeight - clientHeight;
551            
552            // Scroll down by 500px or to the bottom, whichever is less
553            const scrollAmount = 500;
554            const newScrollPosition = Math.min(currentScrollTop + scrollAmount, maxScroll);
555            scrollableContainer.scrollTop = newScrollPosition;
556            
557            const actualScrollTop = scrollableContainer.scrollTop;
558            console.log('Scroll chunk - from:', currentScrollTop, 'to:', actualScrollTop, 'max:', maxScroll);
559            
560            // Wait for new members to load
561            await new Promise(resolve => setTimeout(resolve, 500));
562            
563            // Get member count after scrolling
564            const afterScrollCount = document.querySelectorAll('.member__5d473[role="listitem"]').length;
565            
566            console.log('Members before scroll:', beforeScrollCount, 'after scroll:', afterScrollCount);
567            
568            if (afterScrollCount === beforeScrollCount) {
569                noChangeCount++;
570                console.log('No new members loaded, attempt', noChangeCount, 'of 3');
571            } else {
572                noChangeCount = 0;
573                console.log('New members loaded! Total members:', afterScrollCount);
574            }
575            
576            currentMemberCount = afterScrollCount;
577        }
578
579        // Final pass - add checkboxes and select any remaining users
580        if (isSelecting) {
581            console.log('Doing final pass...');
582            addCheckboxesToAllMembers();
583            await new Promise(resolve => setTimeout(resolve, 300));
584            
585            const finalCheckboxes = document.querySelectorAll('.mass-inviter-checkbox:not(:disabled)');
586            let finalSelected = 0;
587            finalCheckboxes.forEach(checkbox => {
588                if (!checkbox.checked) {
589                    checkbox.checked = true;
590                    selectedUsers.add(checkbox.dataset.userId);
591                    finalSelected++;
592                }
593            });
594            
595            if (finalSelected > 0) {
596                totalSelected += finalSelected;
597                console.log('Final pass selected', finalSelected, 'users');
598            }
599        }
600        
601        updateControlPanel();
602        console.log('Select All completed. Total users selected:', totalSelected);
603        console.log('Total members in list:', currentMemberCount);
604        
605        // Scroll back to top
606        scrollableContainer.scrollTop = 0;
607        
608        // Reset UI
609        isSelecting = false;
610        document.getElementById('mass-inviter-select-all').style.display = 'block';
611        document.getElementById('mass-inviter-deselect-all').style.display = 'block';
612        document.getElementById('mass-inviter-stop-selection').style.display = 'none';
613    }
614
615    // Deselect all users
616    async function deselectAllUsers() {
617        if (isSelecting) {
618            console.log('Selection already in progress');
619            return;
620        }
621
622        console.log('Deselect All clicked - loading and deselecting all users...');
623        
624        const membersContainer = document.querySelector('.members_c8ffbb');
625        if (!membersContainer) {
626            console.log('Members container not found');
627            return;
628        }
629
630        isSelecting = true;
631        
632        // Show stop button, hide select/deselect buttons
633        document.getElementById('mass-inviter-select-all').style.display = 'none';
634        document.getElementById('mass-inviter-deselect-all').style.display = 'none';
635        document.getElementById('mass-inviter-stop-selection').style.display = 'block';
636
637        // The members_c8ffbb div itself is the scrollable container
638        const scrollableContainer = membersContainer;
639        let currentMemberCount = 0;
640        let noChangeCount = 0;
641        let totalDeselected = 0;
642
643        console.log('Starting continuous scroll and deselect...');
644        console.log('Scrollable container found:', scrollableContainer.className);
645
646        while (noChangeCount < 3 && isSelecting) {
647            // Get current member count before scrolling
648            const beforeScrollCount = document.querySelectorAll('.member__5d473[role="listitem"]').length;
649            
650            // Add checkboxes to currently visible members
651            addCheckboxesToAllMembers();
652            
653            // Wait for checkboxes to be added
654            await new Promise(resolve => setTimeout(resolve, 200));
655            
656            // Deselect all currently available checkboxes
657            const checkboxes = document.querySelectorAll('.mass-inviter-checkbox');
658            let newlyDeselected = 0;
659            checkboxes.forEach(checkbox => {
660                if (checkbox.checked) {
661                    checkbox.checked = false;
662                    selectedUsers.delete(checkbox.dataset.userId);
663                    newlyDeselected++;
664                }
665            });
666            
667            if (newlyDeselected > 0) {
668                totalDeselected += newlyDeselected;
669                console.log('Deselected', newlyDeselected, 'users. Total deselected:', totalDeselected);
670                updateControlPanel();
671            }
672            
673            // Check if we should stop
674            if (!isSelecting) {
675                console.log('Deselection stopped by user');
676                break;
677            }
678            
679            // Scroll down in chunks (500px at a time)
680            const currentScrollTop = scrollableContainer.scrollTop;
681            const scrollHeight = scrollableContainer.scrollHeight;
682            const clientHeight = scrollableContainer.clientHeight;
683            const maxScroll = scrollHeight - clientHeight;
684            
685            // Scroll down by 500px or to the bottom, whichever is less
686            const scrollAmount = 500;
687            const newScrollPosition = Math.min(currentScrollTop + scrollAmount, maxScroll);
688            scrollableContainer.scrollTop = newScrollPosition;
689            
690            const actualScrollTop = scrollableContainer.scrollTop;
691            console.log('Scroll chunk - from:', currentScrollTop, 'to:', actualScrollTop, 'max:', maxScroll);
692            
693            // Wait for new members to load
694            await new Promise(resolve => setTimeout(resolve, 500));
695            
696            // Get member count after scrolling
697            const afterScrollCount = document.querySelectorAll('.member__5d473[role="listitem"]').length;
698            
699            console.log('Members before scroll:', beforeScrollCount, 'after scroll:', afterScrollCount);
700            
701            if (afterScrollCount === beforeScrollCount) {
702                noChangeCount++;
703                console.log('No new members loaded, attempt', noChangeCount, 'of 3');
704            } else {
705                noChangeCount = 0;
706                console.log('New members loaded! Total members:', afterScrollCount);
707            }
708            
709            currentMemberCount = afterScrollCount;
710        }
711
712        // Final pass - add checkboxes and deselect any remaining users
713        if (isSelecting) {
714            console.log('Doing final pass...');
715            addCheckboxesToAllMembers();
716            await new Promise(resolve => setTimeout(resolve, 300));
717            
718            const finalCheckboxes = document.querySelectorAll('.mass-inviter-checkbox');
719            let finalDeselected = 0;
720            finalCheckboxes.forEach(checkbox => {
721                if (checkbox.checked) {
722                    checkbox.checked = false;
723                    selectedUsers.delete(checkbox.dataset.userId);
724                    finalDeselected++;
725                }
726            });
727            
728            if (finalDeselected > 0) {
729                totalDeselected += finalDeselected;
730                console.log('Final pass deselected', finalDeselected, 'users');
731            }
732        }
733        
734        updateControlPanel();
735        console.log('Deselect All completed. Total users deselected:', totalDeselected);
736        console.log('Total members in list:', currentMemberCount);
737        
738        // Scroll back to top
739        scrollableContainer.scrollTop = 0;
740        
741        // Reset UI
742        isSelecting = false;
743        document.getElementById('mass-inviter-select-all').style.display = 'block';
744        document.getElementById('mass-inviter-deselect-all').style.display = 'block';
745        document.getElementById('mass-inviter-stop-selection').style.display = 'none';
746    }
747
748    // Stop selection/deselection process
749    function stopSelection() {
750        isSelecting = false;
751        console.log('Selection/deselection stopped by user');
752        
753        // Reset UI
754        document.getElementById('mass-inviter-select-all').style.display = 'block';
755        document.getElementById('mass-inviter-deselect-all').style.display = 'block';
756        document.getElementById('mass-inviter-stop-selection').style.display = 'none';
757    }
758
759    // Scroll to load all users in the member list
760    async function scrollToLoadAllUsers() {
761        const membersContainer = document.querySelector('.members_c8ffbb');
762        if (!membersContainer) {
763            console.log('Members container not found');
764            return;
765        }
766
767        // The members_c8ffbb div itself is the scrollable container
768        const scrollableContainer = membersContainer;
769        let previousMemberCount = 0;
770        let currentMemberCount = document.querySelectorAll('.member__5d473[role="listitem"]').length;
771        let noChangeCount = 0;
772
773        console.log('Starting to load all users...');
774
775        while (noChangeCount < 3) {
776            // Scroll to bottom
777            scrollableContainer.scrollTop = scrollableContainer.scrollHeight;
778            
779            // Wait for new members to load
780            await new Promise(resolve => setTimeout(resolve, 300));
781            
782            previousMemberCount = currentMemberCount;
783            currentMemberCount = document.querySelectorAll('.member__5d473[role="listitem"]').length;
784            
785            if (currentMemberCount === previousMemberCount) {
786                noChangeCount++;
787            } else {
788                noChangeCount = 0;
789                console.log('Loaded members:', currentMemberCount);
790            }
791        }
792
793        console.log('Finished loading all users. Total:', currentMemberCount);
794        
795        // Scroll back to top
796        scrollableContainer.scrollTop = 0;
797    }
798
799    // Reset invited list
800    async function resetInvitedList() {
801        if (confirm('Are you sure you want to reset the invited users list?')) {
802            invitedUsers.clear();
803            await saveState();
804            
805            // Re-enable all checkboxes
806            const checkboxes = document.querySelectorAll('.mass-inviter-checkbox');
807            checkboxes.forEach(checkbox => {
808                checkbox.disabled = false;
809                const memberElement = checkbox.closest('.member__5d473');
810                if (memberElement) {
811                    memberElement.classList.remove('mass-inviter-invited');
812                }
813            });
814            
815            updateControlPanel();
816            console.log('Invited list reset');
817        }
818    }
819
820    // Simulate right-click on member
821    async function rightClickMember(memberElement) {
822        return new Promise((resolve) => {
823            const rect = memberElement.getBoundingClientRect();
824            const x = rect.left + rect.width / 2;
825            const y = rect.top + rect.height / 2;
826
827            const contextMenuEvent = new MouseEvent('contextmenu', {
828                bubbles: true,
829                cancelable: true,
830                view: window,
831                clientX: x,
832                clientY: y,
833                button: 2
834            });
835
836            memberElement.dispatchEvent(contextMenuEvent);
837            console.log('Right-clicked member');
838            
839            // Wait for context menu to appear
840            setTimeout(resolve, 1000);
841        });
842    }
843
844    // Click invite option in context menu
845    async function clickInviteOption() {
846        return new Promise((resolve) => {
847            setTimeout(() => {
848                // Look for "Invite to Server" option in context menu
849                const inviteMenuItem = document.getElementById('user-context-invite-to-server');
850
851                if (inviteMenuItem) {
852                    // Hover over it to open submenu (don't click!)
853                    inviteMenuItem.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
854                    inviteMenuItem.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
855                    console.log('Hovered over invite option, waiting for submenu...');
856                    setTimeout(resolve, 800);
857                } else {
858                    const menuItems = document.querySelectorAll('[role="menuitem"]');
859                    console.error('Invite option not found in context menu. Available options:', 
860                        Array.from(menuItems).map(item => item.textContent.trim()).join(', '));
861                    resolve();
862                }
863            }, 500);
864        });
865    }
866
867    // Select server from invite modal
868    async function selectServerFromModal() {
869        return new Promise((resolve) => {
870            setTimeout(() => {
871                console.log('Looking for server in submenu...');
872                
873                // The server appears in submenu with ID format: user-context-invite-to-server--[SERVER_ID]
874                const serverMenuItem = document.getElementById('user-context-invite-to-server--' + targetServerId);
875                
876                if (serverMenuItem) {
877                    serverMenuItem.click();
878                    console.log('Clicked target server:', targetServerId);
879                    setTimeout(resolve, 500);
880                    return;
881                }
882
883                // If not found by exact ID, try to find by partial match or name
884                const allServerItems = document.querySelectorAll('[id^="user-context-invite-to-server--"]');
885                console.log('Found', allServerItems.length, 'server options in submenu');
886                
887                for (const serverItem of allServerItems) {
888                    const itemId = serverItem.id.replace('user-context-invite-to-server--', '');
889                    const serverName = serverItem.textContent.trim();
890                    
891                    console.log('Checking server:', serverName, 'ID:', itemId);
892                    
893                    // Try matching by ID or name
894                    if (itemId === targetServerId || serverName.toLowerCase().includes(targetServerId.toLowerCase())) {
895                        serverItem.click();
896                        console.log('Selected target server:', serverName);
897                        setTimeout(resolve, 500);
898                        return;
899                    }
900                }
901
902                console.error('Target server not found in submenu. Looking for:', targetServerId);
903                console.error('Available servers:', 
904                    Array.from(allServerItems).map(el => el.textContent.trim() + ' (ID: ' + el.id.replace('user-context-invite-to-server--', '') + ')').join(', '));
905                resolve();
906            }, 300);
907        });
908    }
909
910    // Close any open modals
911    function closeModals() {
912        const closeButtons = document.querySelectorAll('[aria-label="Close"], [class*="closeButton"]');
913        closeButtons.forEach(button => button.click());
914        
915        // Press Escape key
916        document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27, bubbles: true }));
917    }
918
919    // Invite single user
920    async function inviteUser(userId) {
921        const memberElement = document.querySelector(`.mass-inviter-checkbox[data-user-id="${userId}"]`)?.closest('.member__5d473');
922        
923        if (!memberElement) {
924            console.error('Member element not found for user:', userId);
925            return false;
926        }
927
928        if (!targetServerId) {
929            alert('Please set a target server ID first!');
930            return false;
931        }
932
933        try {
934            console.log('Inviting user:', userId);
935            
936            // Right-click the member
937            await rightClickMember(memberElement);
938            
939            // Click invite option
940            await clickInviteOption();
941            
942            // Select server from modal
943            await selectServerFromModal();
944            
945            // Mark as invited
946            invitedUsers.add(userId);
947            selectedUsers.delete(userId);
948            await saveState();
949            
950            // Update UI
951            const checkbox = memberElement.querySelector('.mass-inviter-checkbox');
952            if (checkbox) {
953                checkbox.checked = false;
954                checkbox.disabled = true;
955            }
956            memberElement.classList.add('mass-inviter-invited');
957            
958            // Close any modals
959            closeModals();
960            
961            console.log('User invited successfully:', userId);
962            return true;
963        } catch (error) {
964            console.error('Error inviting user:', error);
965            closeModals();
966            return false;
967        }
968    }
969
970    // Start inviting process
971    async function startInviting() {
972        if (isInviting) return;
973        
974        if (!targetServerId) {
975            alert('Please set a target server ID first!');
976            return;
977        }
978
979        if (selectedUsers.size === 0) {
980            alert('Please select at least one user to invite!');
981            return;
982        }
983
984        isInviting = true;
985        document.getElementById('mass-inviter-start').style.display = 'none';
986        document.getElementById('mass-inviter-pause').style.display = 'block';
987
988        const usersToInvite = [...selectedUsers];
989        console.log('Starting invitation process for', usersToInvite.length, 'users');
990
991        // Get members container for scrolling
992        const membersContainer = document.querySelector('.members_c8ffbb');
993        const scrollableContainer = membersContainer;
994        let currentScrollPosition = 0;
995
996        for (let i = 0; i < usersToInvite.length; i++) {
997            const userId = usersToInvite[i];
998            
999            if (!isInviting) {
1000                console.log('Invitation process paused');
1001                break;
1002            }
1003
1004            // Scroll down periodically to keep loading members
1005            if (i > 0 && i % 5 === 0 && scrollableContainer) {
1006                const scrollHeight = scrollableContainer.scrollHeight;
1007                const clientHeight = scrollableContainer.clientHeight;
1008                const maxScroll = scrollHeight - clientHeight;
1009                
1010                // Scroll down by 500px
1011                currentScrollPosition = Math.min(currentScrollPosition + 500, maxScroll);
1012                scrollableContainer.scrollTop = currentScrollPosition;
1013                console.log('Scrolled to position:', currentScrollPosition);
1014                
1015                // Wait for members to load
1016                await new Promise(resolve => setTimeout(resolve, 500));
1017                
1018                // Re-add checkboxes to newly loaded members
1019                addCheckboxesToAllMembers();
1020            }
1021
1022            await inviteUser(userId);
1023            updateControlPanel();
1024            
1025            // Wait between invitations to avoid rate limiting
1026            await new Promise(resolve => setTimeout(resolve, 30000));
1027        }
1028
1029        // Scroll back to top when done
1030        if (scrollableContainer) {
1031            scrollableContainer.scrollTop = 0;
1032        }
1033
1034        isInviting = false;
1035        document.getElementById('mass-inviter-start').style.display = 'block';
1036        document.getElementById('mass-inviter-pause').style.display = 'none';
1037        
1038        console.log('Invitation process completed');
1039    }
1040
1041    // Pause inviting process
1042    function pauseInviting() {
1043        isInviting = false;
1044        document.getElementById('mass-inviter-start').style.display = 'block';
1045        document.getElementById('mass-inviter-pause').style.display = 'none';
1046        console.log('Invitation process paused by user');
1047    }
1048
1049    // Initialize extension
1050    async function init() {
1051        console.log('Initializing Discord Mass User Inviter');
1052        
1053        await loadState();
1054        
1055        // Function to setup observers
1056        function setupObservers() {
1057            const membersContainer = document.querySelector('.members_c8ffbb');
1058            if (membersContainer) {
1059                console.log('Members list found, adding checkboxes');
1060                
1061                addCheckboxesToAllMembers();
1062                
1063                // Create control panel if it doesn't exist
1064                if (!document.getElementById('mass-inviter-panel')) {
1065                    createControlPanel();
1066                }
1067                
1068                // Observe for new members being added
1069                const observer = new MutationObserver(debounce(() => {
1070                    console.log('Members list changed, updating checkboxes');
1071                    addCheckboxesToAllMembers();
1072                }, 200));
1073                
1074                observer.observe(membersContainer, {
1075                    childList: true,
1076                    subtree: true
1077                });
1078                
1079                console.log('Observer attached to members container');
1080                return true;
1081            }
1082            return false;
1083        }
1084        
1085        // Initial setup
1086        const initialWait = setInterval(() => {
1087            if (setupObservers()) {
1088                clearInterval(initialWait);
1089                console.log('Extension initialized successfully');
1090            }
1091        }, 1000);
1092        
1093        // Watch for the entire members container being replaced (when switching servers)
1094        const bodyObserver = new MutationObserver(debounce(() => {
1095            const membersContainer = document.querySelector('.members_c8ffbb');
1096            if (membersContainer) {
1097                // Check if we need to re-add checkboxes
1098                const hasCheckboxes = membersContainer.querySelector('.mass-inviter-checkbox');
1099                if (!hasCheckboxes) {
1100                    console.log('Members container replaced, re-initializing...');
1101                    setupObservers();
1102                }
1103            }
1104        }, 100));
1105        
1106        bodyObserver.observe(document.body, {
1107            childList: true,
1108            subtree: true
1109        });
1110        
1111        console.log('Body observer attached for server switches');
1112    }
1113
1114    // Start when DOM is ready
1115    if (document.readyState === 'loading') {
1116        document.addEventListener('DOMContentLoaded', init);
1117    } else {
1118        init();
1119    }
1120})();
Discord Mass User Inviter | Robomonkey