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})();