Bumble Auto Swiper Pro

Advanced Bumble automation with smart filtering, auto-messaging, and activity tracking

Size

43.0 KB

Version

1.0.1

Created

Feb 5, 2026

Updated

about 1 month ago

1// ==UserScript==
2// @name		Bumble Auto Swiper Pro
3// @description		Advanced Bumble automation with smart filtering, auto-messaging, and activity tracking
4// @version		1.0.1
5// @match		https://*.bumble.com/*
6// @icon		https://robomonkey.io/favicon.ico
7// @grant		GM.getValue
8// @grant		GM.setValue
9// @grant		GM.deleteValue
10// @grant		GM.listValues
11// ==/UserScript==
12(function() {
13    'use strict';
14
15    // ============================================
16    // CONFIGURATION & STATE MANAGEMENT
17    // ============================================
18    
19    const CONFIG = {
20        MIN_DELAY: 2000,
21        MAX_DELAY: 5000,
22        PAUSE_AFTER_SWIPES: 20,
23        PAUSE_DURATION: 15000,
24        PROFILE_LOAD_TIMEOUT: 5000,
25        DEFAULT_FILTERED_WORDS: ['trans', 'transgender', 'trans woman', 'trans-woman', 'transwoman'],
26        STORAGE_KEYS: {
27            IS_ACTIVE: 'bumble_auto_swipe_active',
28            SETTINGS: 'bumble_settings',
29            SWIPE_HISTORY: 'bumble_swipe_history',
30            FILTERED_WORDS: 'bumble_filtered_words',
31            AUTO_MESSAGE_TEMPLATES: 'bumble_message_templates',
32            STATS: 'bumble_stats'
33        }
34    };
35
36    let state = {
37        isActive: false,
38        isPaused: false,
39        swipeCount: 0,
40        stats: {
41            totalSwipes: 0,
42            rightSwipes: 0,
43            leftSwipes: 0,
44            matches: 0,
45            messagesSent: 0
46        },
47        currentProfile: null,
48        observer: null
49    };
50
51    // ============================================
52    // UTILITY FUNCTIONS
53    // ============================================
54
55    function debounce(func, wait) {
56        let timeout;
57        return function executedFunction(...args) {
58            const later = () => {
59                clearTimeout(timeout);
60                func(...args);
61            };
62            clearTimeout(timeout);
63            timeout = setTimeout(later, wait);
64        };
65    }
66
67    function randomDelay(min = CONFIG.MIN_DELAY, max = CONFIG.MAX_DELAY) {
68        return Math.floor(Math.random() * (max - min + 1)) + min;
69    }
70
71    async function wait(ms) {
72        return new Promise(resolve => setTimeout(resolve, ms));
73    }
74
75    function log(message, data = null) {
76        const timestamp = new Date().toISOString();
77        console.log(`[Bumble Auto Swiper] ${timestamp} - ${message}`, data || '');
78    }
79
80    function logError(message, error = null) {
81        const timestamp = new Date().toISOString();
82        console.error(`[Bumble Auto Swiper ERROR] ${timestamp} - ${message}`, error || '');
83    }
84
85    // ============================================
86    // STORAGE FUNCTIONS
87    // ============================================
88
89    async function loadSettings() {
90        try {
91            const settings = await GM.getValue(CONFIG.STORAGE_KEYS.SETTINGS, {
92                minDelay: 2000,
93                maxDelay: 5000,
94                pauseAfterSwipes: 20,
95                autoMessageEnabled: false,
96                verificationPriority: true
97            });
98            return settings;
99        } catch (error) {
100            logError('Failed to load settings', error);
101            return {};
102        }
103    }
104
105    async function saveSettings(settings) {
106        try {
107            await GM.setValue(CONFIG.STORAGE_KEYS.SETTINGS, settings);
108            log('Settings saved successfully');
109        } catch (error) {
110            logError('Failed to save settings', error);
111        }
112    }
113
114    async function loadFilteredWords() {
115        try {
116            const words = await GM.getValue(CONFIG.STORAGE_KEYS.FILTERED_WORDS, CONFIG.DEFAULT_FILTERED_WORDS);
117            return words;
118        } catch (error) {
119            logError('Failed to load filtered words', error);
120            return CONFIG.DEFAULT_FILTERED_WORDS;
121        }
122    }
123
124    async function saveFilteredWords(words) {
125        try {
126            await GM.setValue(CONFIG.STORAGE_KEYS.FILTERED_WORDS, words);
127            log('Filtered words saved successfully');
128        } catch (error) {
129            logError('Failed to save filtered words', error);
130        }
131    }
132
133    async function loadSwipeHistory() {
134        try {
135            const history = await GM.getValue(CONFIG.STORAGE_KEYS.SWIPE_HISTORY, { right: [], left: [] });
136            return history;
137        } catch (error) {
138            logError('Failed to load swipe history', error);
139            return { right: [], left: [] };
140        }
141    }
142
143    async function saveSwipeHistory(history) {
144        try {
145            // Keep only last 100 entries for each
146            if (history.right.length > 100) history.right = history.right.slice(-100);
147            if (history.left.length > 100) history.left = history.left.slice(-100);
148            
149            await GM.setValue(CONFIG.STORAGE_KEYS.SWIPE_HISTORY, history);
150            log('Swipe history saved successfully');
151        } catch (error) {
152            logError('Failed to save swipe history', error);
153        }
154    }
155
156    async function loadStats() {
157        try {
158            const stats = await GM.getValue(CONFIG.STORAGE_KEYS.STATS, state.stats);
159            state.stats = stats;
160            return stats;
161        } catch (error) {
162            logError('Failed to load stats', error);
163            return state.stats;
164        }
165    }
166
167    async function saveStats() {
168        try {
169            await GM.setValue(CONFIG.STORAGE_KEYS.STATS, state.stats);
170        } catch (error) {
171            logError('Failed to save stats', error);
172        }
173    }
174
175    async function loadMessageTemplates() {
176        try {
177            const templates = await GM.getValue(CONFIG.STORAGE_KEYS.AUTO_MESSAGE_TEMPLATES, [
178                "Hey! How's your day going?",
179                "Hi there! I'd love to get to know you better 😊",
180                "Hey! Your profile caught my eye. What do you like to do for fun?"
181            ]);
182            return templates;
183        } catch (error) {
184            logError('Failed to load message templates', error);
185            return [];
186        }
187    }
188
189    // ============================================
190    // BUMBLE DOM INTERACTION
191    // ============================================
192
193    function findProfileCard() {
194        // Multiple selectors for Bumble's profile card structure
195        const selectors = [
196            'div[data-qa-role="profile-card"]',
197            'article[class*="encounters-story-profile"]',
198            'div[class*="encounters-album"]',
199            'div.encounters-story-profile',
200            '[class*="encounters-user"]'
201        ];
202
203        for (const selector of selectors) {
204            const element = document.querySelector(selector);
205            if (element) {
206                log('Profile card found with selector:', selector);
207                return element;
208            }
209        }
210
211        logError('Profile card not found with any selector');
212        return null;
213    }
214
215    function findSwipeButtons() {
216        // Find both like and pass buttons
217        const likeSelectors = [
218            'button[data-qa-icon="game-yes"]',
219            'button[aria-label*="Yes"]',
220            'button[class*="encounters-action--yes"]',
221            'div[class*="encounters-controls"] button:last-child',
222            'button[data-qa="pill-yes"]'
223        ];
224
225        const passSelectors = [
226            'button[data-qa-icon="game-no"]',
227            'button[aria-label*="No"]',
228            'button[class*="encounters-action--no"]',
229            'div[class*="encounters-controls"] button:first-child',
230            'button[data-qa="pill-no"]'
231        ];
232
233        let likeButton = null;
234        let passButton = null;
235
236        for (const selector of likeSelectors) {
237            likeButton = document.querySelector(selector);
238            if (likeButton) {
239                log('Like button found with selector:', selector);
240                break;
241            }
242        }
243
244        for (const selector of passSelectors) {
245            passButton = document.querySelector(selector);
246            if (passButton) {
247                log('Pass button found with selector:', selector);
248                break;
249            }
250        }
251
252        if (!likeButton || !passButton) {
253            logError('Swipe buttons not found', { likeButton: !!likeButton, passButton: !!passButton });
254        }
255
256        return { likeButton, passButton };
257    }
258
259    function extractProfileInfo() {
260        try {
261            const profileCard = findProfileCard();
262            if (!profileCard) {
263                return null;
264            }
265
266            // Extract name
267            const nameElement = profileCard.querySelector('[class*="encounters-story-profile__name"], h1, [class*="profile-name"]');
268            const name = nameElement ? nameElement.textContent.trim() : 'Unknown';
269
270            // Extract age
271            const ageElement = profileCard.querySelector('[class*="encounters-story-profile__age"], [class*="profile-age"]');
272            const age = ageElement ? ageElement.textContent.trim() : '';
273
274            // Extract bio
275            const bioElement = profileCard.querySelector('[class*="profile-bio"], [class*="encounters-story-profile__bio"], p[class*="bio"]');
276            const bio = bioElement ? bioElement.textContent.trim().toLowerCase() : '';
277
278            // Check for verification badge
279            const verificationBadge = profileCard.querySelector('[class*="verification"], [class*="verified"], svg[class*="badge"]');
280            const isVerified = !!verificationBadge;
281
282            // Extract profile image
283            const imageElement = profileCard.querySelector('img[src*="bumble"], img[class*="profile"], img[class*="photo"]');
284            const imageUrl = imageElement ? imageElement.src : '';
285
286            const profile = {
287                name,
288                age,
289                bio,
290                isVerified,
291                imageUrl,
292                timestamp: Date.now()
293            };
294
295            log('Profile extracted:', profile);
296            return profile;
297
298        } catch (error) {
299            logError('Error extracting profile info', error);
300            return null;
301        }
302    }
303
304    function shouldSwipeRight(profile, filteredWords) {
305        if (!profile) return false;
306
307        // Check for filtered words in bio
308        const bio = profile.bio.toLowerCase();
309        for (const word of filteredWords) {
310            if (bio.includes(word.toLowerCase())) {
311                log(`Filtered word detected: "${word}" in bio`);
312                return false;
313            }
314        }
315
316        // Prioritize verified profiles
317        if (profile.isVerified) {
318            log('Verified profile detected - swiping right');
319            return true;
320        }
321
322        // Default behavior - swipe right on most profiles
323        // You can add more sophisticated logic here
324        return Math.random() > 0.3; // 70% right swipe rate for non-verified
325    }
326
327    async function performSwipe(direction, profile) {
328        try {
329            const { likeButton, passButton } = findSwipeButtons();
330            
331            if (!likeButton || !passButton) {
332                logError('Cannot perform swipe - buttons not found');
333                return false;
334            }
335
336            const button = direction === 'right' ? likeButton : passButton;
337            
338            // Simulate human-like click
339            button.focus();
340            await wait(100);
341            button.click();
342            
343            log(`Swiped ${direction} on profile:`, profile.name);
344
345            // Update stats
346            state.stats.totalSwipes++;
347            if (direction === 'right') {
348                state.stats.rightSwipes++;
349            } else {
350                state.stats.leftSwipes++;
351            }
352            await saveStats();
353
354            // Save to history
355            const history = await loadSwipeHistory();
356            history[direction].push(profile);
357            await saveSwipeHistory(history);
358
359            // Update UI
360            updateStatsDisplay();
361            
362            return true;
363
364        } catch (error) {
365            logError('Error performing swipe', error);
366            return false;
367        }
368    }
369
370    // ============================================
371    // AUTO-SWIPE ENGINE
372    // ============================================
373
374    async function autoSwipeLoop() {
375        log('Auto-swipe loop started');
376
377        while (state.isActive) {
378            try {
379                // Check if paused
380                if (state.isPaused) {
381                    log('Auto-swipe paused, waiting...');
382                    await wait(1000);
383                    continue;
384                }
385
386                // Wait for profile to load
387                await wait(CONFIG.PROFILE_LOAD_TIMEOUT);
388
389                // Extract current profile
390                const profile = extractProfileInfo();
391                if (!profile) {
392                    log('No profile found, waiting for next profile...');
393                    await wait(2000);
394                    continue;
395                }
396
397                state.currentProfile = profile;
398
399                // Load settings and filtered words
400                const settings = await loadSettings();
401                const filteredWords = await loadFilteredWords();
402
403                // Decide swipe direction
404                const shouldRight = shouldSwipeRight(profile, filteredWords);
405                const direction = shouldRight ? 'right' : 'left';
406
407                // Perform swipe
408                const success = await performSwipe(direction, profile);
409                
410                if (success) {
411                    state.swipeCount++;
412
413                    // Check if we need to pause
414                    if (state.swipeCount >= settings.pauseAfterSwipes) {
415                        log(`Reached ${state.swipeCount} swipes, taking a break...`);
416                        state.isPaused = true;
417                        updateToggleButton();
418                        
419                        await wait(CONFIG.PAUSE_DURATION);
420                        
421                        state.isPaused = false;
422                        state.swipeCount = 0;
423                        updateToggleButton();
424                        log('Break finished, resuming auto-swipe');
425                    }
426
427                    // Random delay between swipes
428                    const delay = randomDelay(settings.minDelay, settings.maxDelay);
429                    log(`Waiting ${delay}ms before next swipe`);
430                    await wait(delay);
431                } else {
432                    log('Swipe failed, retrying...');
433                    await wait(2000);
434                }
435
436            } catch (error) {
437                logError('Error in auto-swipe loop', error);
438                await wait(3000);
439            }
440        }
441
442        log('Auto-swipe loop stopped');
443    }
444
445    async function toggleAutoSwipe() {
446        state.isActive = !state.isActive;
447        await GM.setValue(CONFIG.STORAGE_KEYS.IS_ACTIVE, state.isActive);
448
449        if (state.isActive) {
450            log('Auto-swipe activated');
451            updateToggleButton();
452            autoSwipeLoop();
453        } else {
454            log('Auto-swipe deactivated');
455            state.swipeCount = 0;
456            state.isPaused = false;
457            updateToggleButton();
458        }
459    }
460
461    // ============================================
462    // UI CREATION
463    // ============================================
464
465    function injectStyles() {
466        const styles = `
467            /* Main Container */
468            #bumble-auto-swiper-panel {
469                position: fixed;
470                top: 20px;
471                right: 20px;
472                width: 380px;
473                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
474                border-radius: 20px;
475                box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
476                z-index: 999999;
477                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
478                color: #ffffff;
479                overflow: hidden;
480                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
481            }
482
483            #bumble-auto-swiper-panel.minimized {
484                width: 60px;
485                height: 60px;
486                border-radius: 50%;
487            }
488
489            #bumble-auto-swiper-panel.minimized .panel-content {
490                display: none;
491            }
492
493            /* Header */
494            .swiper-header {
495                padding: 20px;
496                background: rgba(255, 255, 255, 0.1);
497                backdrop-filter: blur(10px);
498                display: flex;
499                justify-content: space-between;
500                align-items: center;
501                border-bottom: 1px solid rgba(255, 255, 255, 0.2);
502            }
503
504            .swiper-title {
505                font-size: 18px;
506                font-weight: 700;
507                letter-spacing: 0.5px;
508                display: flex;
509                align-items: center;
510                gap: 10px;
511            }
512
513            .swiper-title::before {
514                content: '🔥';
515                font-size: 24px;
516            }
517
518            .minimize-btn {
519                background: rgba(255, 255, 255, 0.2);
520                border: none;
521                color: white;
522                width: 30px;
523                height: 30px;
524                border-radius: 50%;
525                cursor: pointer;
526                font-size: 18px;
527                display: flex;
528                align-items: center;
529                justify-content: center;
530                transition: all 0.2s;
531            }
532
533            .minimize-btn:hover {
534                background: rgba(255, 255, 255, 0.3);
535                transform: scale(1.1);
536            }
537
538            /* Toggle Button */
539            .toggle-container {
540                padding: 20px;
541                display: flex;
542                flex-direction: column;
543                gap: 15px;
544            }
545
546            .toggle-button {
547                width: 100%;
548                padding: 18px;
549                border: none;
550                border-radius: 12px;
551                font-size: 16px;
552                font-weight: 600;
553                cursor: pointer;
554                transition: all 0.3s;
555                text-transform: uppercase;
556                letter-spacing: 1px;
557                position: relative;
558                overflow: hidden;
559            }
560
561            .toggle-button::before {
562                content: '';
563                position: absolute;
564                top: 50%;
565                left: 50%;
566                width: 0;
567                height: 0;
568                border-radius: 50%;
569                background: rgba(255, 255, 255, 0.3);
570                transform: translate(-50%, -50%);
571                transition: width 0.6s, height 0.6s;
572            }
573
574            .toggle-button:hover::before {
575                width: 300px;
576                height: 300px;
577            }
578
579            .toggle-button.active {
580                background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
581                color: white;
582                box-shadow: 0 8px 20px rgba(17, 153, 142, 0.4);
583            }
584
585            .toggle-button.inactive {
586                background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
587                color: white;
588                box-shadow: 0 8px 20px rgba(238, 9, 121, 0.4);
589            }
590
591            .toggle-button.paused {
592                background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
593                color: white;
594            }
595
596            /* Stats Display */
597            .stats-container {
598                padding: 0 20px 20px 20px;
599                display: grid;
600                grid-template-columns: repeat(2, 1fr);
601                gap: 12px;
602            }
603
604            .stat-box {
605                background: rgba(255, 255, 255, 0.15);
606                backdrop-filter: blur(10px);
607                padding: 15px;
608                border-radius: 12px;
609                text-align: center;
610                transition: all 0.3s;
611            }
612
613            .stat-box:hover {
614                background: rgba(255, 255, 255, 0.25);
615                transform: translateY(-2px);
616            }
617
618            .stat-value {
619                font-size: 28px;
620                font-weight: 700;
621                display: block;
622                margin-bottom: 5px;
623            }
624
625            .stat-label {
626                font-size: 12px;
627                opacity: 0.9;
628                text-transform: uppercase;
629                letter-spacing: 0.5px;
630            }
631
632            /* Tabs */
633            .tabs-container {
634                padding: 0 20px;
635            }
636
637            .tabs-header {
638                display: flex;
639                gap: 10px;
640                margin-bottom: 15px;
641            }
642
643            .tab-button {
644                flex: 1;
645                padding: 12px;
646                background: rgba(255, 255, 255, 0.1);
647                border: none;
648                border-radius: 10px;
649                color: white;
650                font-size: 14px;
651                font-weight: 600;
652                cursor: pointer;
653                transition: all 0.3s;
654            }
655
656            .tab-button:hover {
657                background: rgba(255, 255, 255, 0.2);
658            }
659
660            .tab-button.active {
661                background: rgba(255, 255, 255, 0.3);
662                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
663            }
664
665            .tab-content {
666                display: none;
667                max-height: 300px;
668                overflow-y: auto;
669                padding: 15px;
670                background: rgba(255, 255, 255, 0.1);
671                border-radius: 12px;
672                margin-bottom: 20px;
673            }
674
675            .tab-content.active {
676                display: block;
677            }
678
679            /* Settings */
680            .settings-group {
681                margin-bottom: 20px;
682            }
683
684            .settings-label {
685                display: block;
686                margin-bottom: 8px;
687                font-size: 13px;
688                font-weight: 600;
689                opacity: 0.9;
690            }
691
692            .settings-input {
693                width: 100%;
694                padding: 10px;
695                background: rgba(255, 255, 255, 0.2);
696                border: 1px solid rgba(255, 255, 255, 0.3);
697                border-radius: 8px;
698                color: white;
699                font-size: 14px;
700            }
701
702            .settings-input::placeholder {
703                color: rgba(255, 255, 255, 0.6);
704            }
705
706            .settings-input:focus {
707                outline: none;
708                background: rgba(255, 255, 255, 0.25);
709                border-color: rgba(255, 255, 255, 0.5);
710            }
711
712            .checkbox-container {
713                display: flex;
714                align-items: center;
715                gap: 10px;
716                margin-bottom: 15px;
717            }
718
719            .checkbox-container input[type="checkbox"] {
720                width: 20px;
721                height: 20px;
722                cursor: pointer;
723            }
724
725            /* History Items */
726            .history-item {
727                display: flex;
728                gap: 12px;
729                padding: 12px;
730                background: rgba(255, 255, 255, 0.1);
731                border-radius: 10px;
732                margin-bottom: 10px;
733                transition: all 0.3s;
734            }
735
736            .history-item:hover {
737                background: rgba(255, 255, 255, 0.15);
738                transform: translateX(5px);
739            }
740
741            .history-item.right {
742                border-left: 4px solid #38ef7d;
743            }
744
745            .history-item.left {
746                border-left: 4px solid #ff6a00;
747            }
748
749            .history-image {
750                width: 50px;
751                height: 50px;
752                border-radius: 8px;
753                object-fit: cover;
754            }
755
756            .history-info {
757                flex: 1;
758            }
759
760            .history-name {
761                font-weight: 600;
762                margin-bottom: 4px;
763            }
764
765            .history-bio {
766                font-size: 12px;
767                opacity: 0.8;
768                overflow: hidden;
769                text-overflow: ellipsis;
770                white-space: nowrap;
771            }
772
773            /* Scrollbar */
774            .tab-content::-webkit-scrollbar {
775                width: 6px;
776            }
777
778            .tab-content::-webkit-scrollbar-track {
779                background: rgba(255, 255, 255, 0.1);
780                border-radius: 10px;
781            }
782
783            .tab-content::-webkit-scrollbar-thumb {
784                background: rgba(255, 255, 255, 0.3);
785                border-radius: 10px;
786            }
787
788            .tab-content::-webkit-scrollbar-thumb:hover {
789                background: rgba(255, 255, 255, 0.5);
790            }
791
792            /* Filtered Words */
793            .filtered-words-list {
794                display: flex;
795                flex-wrap: wrap;
796                gap: 8px;
797                margin-top: 10px;
798            }
799
800            .filtered-word-tag {
801                background: rgba(255, 255, 255, 0.2);
802                padding: 6px 12px;
803                border-radius: 20px;
804                font-size: 12px;
805                display: flex;
806                align-items: center;
807                gap: 6px;
808            }
809
810            .remove-word-btn {
811                background: rgba(255, 255, 255, 0.3);
812                border: none;
813                color: white;
814                width: 18px;
815                height: 18px;
816                border-radius: 50%;
817                cursor: pointer;
818                font-size: 12px;
819                display: flex;
820                align-items: center;
821                justify-content: center;
822            }
823
824            .remove-word-btn:hover {
825                background: rgba(255, 255, 255, 0.5);
826            }
827
828            /* Save Button */
829            .save-button {
830                width: 100%;
831                padding: 12px;
832                background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
833                border: none;
834                border-radius: 10px;
835                color: white;
836                font-size: 14px;
837                font-weight: 600;
838                cursor: pointer;
839                transition: all 0.3s;
840                margin-top: 15px;
841            }
842
843            .save-button:hover {
844                transform: translateY(-2px);
845                box-shadow: 0 8px 20px rgba(17, 153, 142, 0.4);
846            }
847
848            /* Message Templates */
849            .template-item {
850                background: rgba(255, 255, 255, 0.1);
851                padding: 10px;
852                border-radius: 8px;
853                margin-bottom: 10px;
854                display: flex;
855                justify-content: space-between;
856                align-items: center;
857            }
858
859            .template-text {
860                flex: 1;
861                font-size: 13px;
862            }
863
864            .delete-template-btn {
865                background: rgba(255, 255, 255, 0.2);
866                border: none;
867                color: white;
868                padding: 6px 12px;
869                border-radius: 6px;
870                cursor: pointer;
871                font-size: 12px;
872            }
873
874            .delete-template-btn:hover {
875                background: rgba(255, 255, 255, 0.3);
876            }
877        `;
878
879        TM_addStyle(styles);
880    }
881
882    function createUI() {
883        const panel = document.createElement('div');
884        panel.id = 'bumble-auto-swiper-panel';
885        panel.innerHTML = `
886            <div class="panel-content">
887                <div class="swiper-header">
888                    <div class="swiper-title">Auto Swiper Pro</div>
889                    <button class="minimize-btn" id="minimize-panel"></button>
890                </div>
891
892                <div class="toggle-container">
893                    <button class="toggle-button inactive" id="toggle-auto-swipe">
894                        <span>Start Auto-Swipe</span>
895                    </button>
896                </div>
897
898                <div class="stats-container">
899                    <div class="stat-box">
900                        <span class="stat-value" id="stat-total">0</span>
901                        <span class="stat-label">Total Swipes</span>
902                    </div>
903                    <div class="stat-box">
904                        <span class="stat-value" id="stat-right">0</span>
905                        <span class="stat-label">Right Swipes</span>
906                    </div>
907                    <div class="stat-box">
908                        <span class="stat-value" id="stat-left">0</span>
909                        <span class="stat-label">Left Swipes</span>
910                    </div>
911                    <div class="stat-box">
912                        <span class="stat-value" id="stat-matches">0</span>
913                        <span class="stat-label">Matches</span>
914                    </div>
915                </div>
916
917                <div class="tabs-container">
918                    <div class="tabs-header">
919                        <button class="tab-button active" data-tab="history">History</button>
920                        <button class="tab-button" data-tab="settings">Settings</button>
921                        <button class="tab-button" data-tab="messages">Messages</button>
922                    </div>
923
924                    <div class="tab-content active" id="tab-history">
925                        <div class="tabs-header" style="margin-bottom: 10px;">
926                            <button class="tab-button active" data-subtab="right">Right Swipes</button>
927                            <button class="tab-button" data-subtab="left">Left Swipes</button>
928                        </div>
929                        <div id="history-right-content"></div>
930                        <div id="history-left-content" style="display: none;"></div>
931                    </div>
932
933                    <div class="tab-content" id="tab-settings">
934                        <div class="settings-group">
935                            <label class="settings-label">Min Delay (ms)</label>
936                            <input type="number" class="settings-input" id="setting-min-delay" value="2000" min="1000" max="10000">
937                        </div>
938                        <div class="settings-group">
939                            <label class="settings-label">Max Delay (ms)</label>
940                            <input type="number" class="settings-input" id="setting-max-delay" value="5000" min="2000" max="15000">
941                        </div>
942                        <div class="settings-group">
943                            <label class="settings-label">Pause After Swipes</label>
944                            <input type="number" class="settings-input" id="setting-pause-after" value="20" min="5" max="100">
945                        </div>
946                        <div class="checkbox-container">
947                            <input type="checkbox" id="setting-verification-priority" checked>
948                            <label class="settings-label" style="margin: 0;">Prioritize Verified Profiles</label>
949                        </div>
950                        <div class="settings-group">
951                            <label class="settings-label">Filtered Words (comma separated)</label>
952                            <input type="text" class="settings-input" id="filtered-words-input" placeholder="Add filtered word...">
953                            <div class="filtered-words-list" id="filtered-words-list"></div>
954                        </div>
955                        <button class="save-button" id="save-settings">Save Settings</button>
956                    </div>
957
958                    <div class="tab-content" id="tab-messages">
959                        <div class="checkbox-container">
960                            <input type="checkbox" id="setting-auto-message">
961                            <label class="settings-label" style="margin: 0;">Enable Auto-Messaging</label>
962                        </div>
963                        <div class="settings-group">
964                            <label class="settings-label">Message Templates</label>
965                            <input type="text" class="settings-input" id="new-template-input" placeholder="Add new template...">
966                            <button class="save-button" id="add-template" style="margin-top: 10px;">Add Template</button>
967                        </div>
968                        <div id="templates-list" style="margin-top: 15px;"></div>
969                    </div>
970                </div>
971            </div>
972        `;
973
974        document.body.appendChild(panel);
975        log('UI created successfully');
976
977        // Setup event listeners
978        setupEventListeners();
979        
980        // Load initial data
981        loadInitialData();
982    }
983
984    function setupEventListeners() {
985        // Toggle auto-swipe
986        document.getElementById('toggle-auto-swipe').addEventListener('click', toggleAutoSwipe);
987
988        // Minimize panel
989        document.getElementById('minimize-panel').addEventListener('click', () => {
990            const panel = document.getElementById('bumble-auto-swiper-panel');
991            panel.classList.toggle('minimized');
992        });
993
994        // Tab switching
995        document.querySelectorAll('.tab-button[data-tab]').forEach(button => {
996            button.addEventListener('click', (e) => {
997                const tab = e.target.dataset.tab;
998                
999                // Update tab buttons
1000                document.querySelectorAll('.tab-button[data-tab]').forEach(btn => btn.classList.remove('active'));
1001                e.target.classList.add('active');
1002
1003                // Update tab content
1004                document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
1005                document.getElementById(`tab-${tab}`).classList.add('active');
1006            });
1007        });
1008
1009        // History subtabs
1010        document.querySelectorAll('.tab-button[data-subtab]').forEach(button => {
1011            button.addEventListener('click', (e) => {
1012                const subtab = e.target.dataset.subtab;
1013                
1014                // Update subtab buttons
1015                document.querySelectorAll('.tab-button[data-subtab]').forEach(btn => btn.classList.remove('active'));
1016                e.target.classList.add('active');
1017
1018                // Update subtab content
1019                document.getElementById('history-right-content').style.display = subtab === 'right' ? 'block' : 'none';
1020                document.getElementById('history-left-content').style.display = subtab === 'left' ? 'block' : 'none';
1021            });
1022        });
1023
1024        // Save settings
1025        document.getElementById('save-settings').addEventListener('click', saveSettingsFromUI);
1026
1027        // Filtered words input
1028        document.getElementById('filtered-words-input').addEventListener('keypress', async (e) => {
1029            if (e.key === 'Enter') {
1030                const input = e.target;
1031                const word = input.value.trim();
1032                if (word) {
1033                    const words = await loadFilteredWords();
1034                    if (!words.includes(word)) {
1035                        words.push(word);
1036                        await saveFilteredWords(words);
1037                        renderFilteredWords();
1038                    }
1039                    input.value = '';
1040                }
1041            }
1042        });
1043
1044        // Add message template
1045        document.getElementById('add-template').addEventListener('click', async () => {
1046            const input = document.getElementById('new-template-input');
1047            const template = input.value.trim();
1048            if (template) {
1049                const templates = await loadMessageTemplates();
1050                templates.push(template);
1051                await GM.setValue(CONFIG.STORAGE_KEYS.AUTO_MESSAGE_TEMPLATES, templates);
1052                renderMessageTemplates();
1053                input.value = '';
1054            }
1055        });
1056    }
1057
1058    async function loadInitialData() {
1059        // Load stats
1060        await loadStats();
1061        updateStatsDisplay();
1062
1063        // Load settings
1064        const settings = await loadSettings();
1065        document.getElementById('setting-min-delay').value = settings.minDelay || 2000;
1066        document.getElementById('setting-max-delay').value = settings.maxDelay || 5000;
1067        document.getElementById('setting-pause-after').value = settings.pauseAfterSwipes || 20;
1068        document.getElementById('setting-verification-priority').checked = settings.verificationPriority !== false;
1069        document.getElementById('setting-auto-message').checked = settings.autoMessageEnabled || false;
1070
1071        // Load filtered words
1072        renderFilteredWords();
1073
1074        // Load message templates
1075        renderMessageTemplates();
1076
1077        // Load history
1078        renderHistory();
1079
1080        // Check if auto-swipe was active
1081        const wasActive = await GM.getValue(CONFIG.STORAGE_KEYS.IS_ACTIVE, false);
1082        if (wasActive) {
1083            state.isActive = true;
1084            updateToggleButton();
1085            autoSwipeLoop();
1086        }
1087    }
1088
1089    function updateToggleButton() {
1090        const button = document.getElementById('toggle-auto-swipe');
1091        if (!button) return;
1092
1093        if (state.isPaused) {
1094            button.className = 'toggle-button paused';
1095            button.innerHTML = '<span>⏸️ Paused (Taking Break)</span>';
1096        } else if (state.isActive) {
1097            button.className = 'toggle-button active';
1098            button.innerHTML = '<span>✓ Auto-Swipe Active</span>';
1099        } else {
1100            button.className = 'toggle-button inactive';
1101            button.innerHTML = '<span>Start Auto-Swipe</span>';
1102        }
1103    }
1104
1105    function updateStatsDisplay() {
1106        document.getElementById('stat-total').textContent = state.stats.totalSwipes;
1107        document.getElementById('stat-right').textContent = state.stats.rightSwipes;
1108        document.getElementById('stat-left').textContent = state.stats.leftSwipes;
1109        document.getElementById('stat-matches').textContent = state.stats.matches;
1110    }
1111
1112    async function renderFilteredWords() {
1113        const words = await loadFilteredWords();
1114        const container = document.getElementById('filtered-words-list');
1115        
1116        container.innerHTML = words.map(word => `
1117            <div class="filtered-word-tag">
1118                <span>${word}</span>
1119                <button class="remove-word-btn" data-word="${word}">×</button>
1120            </div>
1121        `).join('');
1122
1123        // Add remove listeners
1124        container.querySelectorAll('.remove-word-btn').forEach(btn => {
1125            btn.addEventListener('click', async (e) => {
1126                const word = e.target.dataset.word;
1127                const words = await loadFilteredWords();
1128                const filtered = words.filter(w => w !== word);
1129                await saveFilteredWords(filtered);
1130                renderFilteredWords();
1131            });
1132        });
1133    }
1134
1135    async function renderMessageTemplates() {
1136        const templates = await loadMessageTemplates();
1137        const container = document.getElementById('templates-list');
1138        
1139        container.innerHTML = templates.map((template, index) => `
1140            <div class="template-item">
1141                <div class="template-text">${template}</div>
1142                <button class="delete-template-btn" data-index="${index}">Delete</button>
1143            </div>
1144        `).join('');
1145
1146        // Add delete listeners
1147        container.querySelectorAll('.delete-template-btn').forEach(btn => {
1148            btn.addEventListener('click', async (e) => {
1149                const index = parseInt(e.target.dataset.index);
1150                const templates = await loadMessageTemplates();
1151                templates.splice(index, 1);
1152                await GM.setValue(CONFIG.STORAGE_KEYS.AUTO_MESSAGE_TEMPLATES, templates);
1153                renderMessageTemplates();
1154            });
1155        });
1156    }
1157
1158    async function renderHistory() {
1159        const history = await loadSwipeHistory();
1160        
1161        // Render right swipes
1162        const rightContainer = document.getElementById('history-right-content');
1163        rightContainer.innerHTML = history.right.slice(-20).reverse().map(profile => `
1164            <div class="history-item right">
1165                ${profile.imageUrl ? `<img src="${profile.imageUrl}" class="history-image" alt="${profile.name}">` : '<div class="history-image" style="background: rgba(255,255,255,0.2);"></div>'}
1166                <div class="history-info">
1167                    <div class="history-name">${profile.name} ${profile.age}</div>
1168                    <div class="history-bio">${profile.bio.substring(0, 50)}${profile.bio.length > 50 ? '...' : ''}</div>
1169                </div>
1170            </div>
1171        `).join('') || '<div style="text-align: center; opacity: 0.7;">No right swipes yet</div>';
1172
1173        // Render left swipes
1174        const leftContainer = document.getElementById('history-left-content');
1175        leftContainer.innerHTML = history.left.slice(-20).reverse().map(profile => `
1176            <div class="history-item left">
1177                ${profile.imageUrl ? `<img src="${profile.imageUrl}" class="history-image" alt="${profile.name}">` : '<div class="history-image" style="background: rgba(255,255,255,0.2);"></div>'}
1178                <div class="history-info">
1179                    <div class="history-name">${profile.name} ${profile.age}</div>
1180                    <div class="history-bio">${profile.bio.substring(0, 50)}${profile.bio.length > 50 ? '...' : ''}</div>
1181                </div>
1182            </div>
1183        `).join('') || '<div style="text-align: center; opacity: 0.7;">No left swipes yet</div>';
1184    }
1185
1186    async function saveSettingsFromUI() {
1187        const settings = {
1188            minDelay: parseInt(document.getElementById('setting-min-delay').value),
1189            maxDelay: parseInt(document.getElementById('setting-max-delay').value),
1190            pauseAfterSwipes: parseInt(document.getElementById('setting-pause-after').value),
1191            verificationPriority: document.getElementById('setting-verification-priority').checked,
1192            autoMessageEnabled: document.getElementById('setting-auto-message').checked
1193        };
1194
1195        await saveSettings(settings);
1196        
1197        // Show success feedback
1198        const button = document.getElementById('save-settings');
1199        const originalText = button.textContent;
1200        button.textContent = '✓ Saved!';
1201        button.style.background = 'linear-gradient(135deg, #38ef7d 0%, #11998e 100%)';
1202        
1203        setTimeout(() => {
1204            button.textContent = originalText;
1205            button.style.background = '';
1206        }, 2000);
1207    }
1208
1209    // ============================================
1210    // INITIALIZATION
1211    // ============================================
1212
1213    function init() {
1214        log('Bumble Auto Swiper Pro initializing...');
1215
1216        // Wait for page to be ready
1217        if (document.readyState === 'loading') {
1218            document.addEventListener('DOMContentLoaded', init);
1219            return;
1220        }
1221
1222        // Inject styles
1223        injectStyles();
1224
1225        // Create UI after a short delay to ensure page is loaded
1226        setTimeout(() => {
1227            createUI();
1228            log('Bumble Auto Swiper Pro initialized successfully');
1229        }, 2000);
1230
1231        // Setup MutationObserver to detect profile changes
1232        const observer = new MutationObserver(debounce(() => {
1233            if (state.isActive && !state.isPaused) {
1234                log('DOM mutation detected - profile may have changed');
1235            }
1236        }, 500));
1237
1238        observer.observe(document.body, {
1239            childList: true,
1240            subtree: true
1241        });
1242
1243        state.observer = observer;
1244    }
1245
1246    // Start the extension
1247    init();
1248
1249})();