Supabets Aviator Rain Auto-Claimer

Automatically claims Aviator rain rewards with toggle control and auto-scroll

Size

15.5 KB

Version

1.0.1

Created

Mar 16, 2026

Updated

6 days ago

1// ==UserScript==
2// @name		Supabets Aviator Rain Auto-Claimer
3// @description		Automatically claims Aviator rain rewards with toggle control and auto-scroll
4// @version		1.0.1
5// @match		https://*.gaming.supabets.co.za/*
6// @match		https://games-interface-supabets.bitville-api.com/*
7// @icon		https://gaming.supabets.co.za/images/gaming.supabets.co.za/favicon/favicon.ico
8// @grant		GM.getValue
9// @grant		GM.setValue
10// @grant		GM.addStyle
11// @run-at		document-start
12// ==/UserScript==
13(function() {
14    'use strict';
15
16    // Debounce function to prevent excessive calls
17    function debounce(func, wait) {
18        let timeout;
19        return function executedFunction(...args) {
20            const later = () => {
21                clearTimeout(timeout);
22                func(...args);
23            };
24            clearTimeout(timeout);
25            timeout = setTimeout(later, wait);
26        };
27    }
28
29    // Add styles for the toggle button
30    GM.addStyle(`
31        #rain-auto-claim-toggle {
32            position: fixed;
33            top: 10px;
34            right: 10px;
35            z-index: 999999;
36            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
37            color: white;
38            border: none;
39            padding: 12px 24px;
40            border-radius: 25px;
41            cursor: pointer;
42            font-weight: bold;
43            font-size: 14px;
44            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
45            transition: all 0.3s ease;
46            font-family: Arial, sans-serif;
47            display: flex;
48            align-items: center;
49            gap: 8px;
50        }
51        
52        #rain-auto-claim-toggle:hover {
53            transform: translateY(-2px);
54            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
55        }
56        
57        #rain-auto-claim-toggle.active {
58            background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
59        }
60        
61        #rain-auto-claim-toggle.inactive {
62            background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
63        }
64        
65        #rain-auto-claim-toggle .status-indicator {
66            width: 10px;
67            height: 10px;
68            border-radius: 50%;
69            background: white;
70            animation: pulse 2s infinite;
71        }
72        
73        @keyframes pulse {
74            0%, 100% { opacity: 1; }
75            50% { opacity: 0.5; }
76        }
77        
78        #rain-claim-log {
79            position: fixed;
80            bottom: 10px;
81            right: 10px;
82            z-index: 999998;
83            background: rgba(0, 0, 0, 0.85);
84            color: #00ff00;
85            padding: 10px 15px;
86            border-radius: 8px;
87            font-family: 'Courier New', monospace;
88            font-size: 12px;
89            max-width: 300px;
90            max-height: 150px;
91            overflow-y: auto;
92            display: none;
93            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
94        }
95        
96        #rain-claim-log.show {
97            display: block;
98        }
99        
100        #rain-claim-log .log-entry {
101            margin: 3px 0;
102            padding: 3px 0;
103            border-bottom: 1px solid rgba(0, 255, 0, 0.2);
104        }
105        
106        #rain-claim-log .log-entry:last-child {
107            border-bottom: none;
108        }
109    `);
110
111    // State management
112    let isEnabled = false;
113    let claimCount = 0;
114    let observer = null;
115    let processedElements = new Set();
116
117    // Initialize the extension
118    async function init() {
119        console.log('[Rain Auto-Claimer] Initializing...');
120        
121        // Load saved state
122        isEnabled = await GM.getValue('rainAutoClaimEnabled', false);
123        claimCount = await GM.getValue('rainClaimCount', 0);
124        
125        // Wait for body to be ready
126        if (document.body) {
127            setupUI();
128            if (isEnabled) {
129                startMonitoring();
130            }
131        } else {
132            const bodyObserver = new MutationObserver((mutations, obs) => {
133                if (document.body) {
134                    obs.disconnect();
135                    setupUI();
136                    if (isEnabled) {
137                        startMonitoring();
138                    }
139                }
140            });
141            bodyObserver.observe(document.documentElement, { childList: true, subtree: true });
142        }
143    }
144
145    // Setup UI elements
146    function setupUI() {
147        // Create toggle button
148        const toggleButton = document.createElement('button');
149        toggleButton.id = 'rain-auto-claim-toggle';
150        toggleButton.className = isEnabled ? 'active' : 'inactive';
151        toggleButton.innerHTML = `
152            <span class="status-indicator"></span>
153            <span class="status-text">${isEnabled ? 'Auto-Claim ON' : 'Auto-Claim OFF'}</span>
154        `;
155        
156        toggleButton.addEventListener('click', toggleAutoClaim);
157        document.body.appendChild(toggleButton);
158        
159        // Create log display
160        const logDisplay = document.createElement('div');
161        logDisplay.id = 'rain-claim-log';
162        document.body.appendChild(logDisplay);
163        
164        console.log('[Rain Auto-Claimer] UI setup complete');
165    }
166
167    // Toggle auto-claim functionality
168    async function toggleAutoClaim() {
169        isEnabled = !isEnabled;
170        await GM.setValue('rainAutoClaimEnabled', isEnabled);
171        
172        const button = document.getElementById('rain-auto-claim-toggle');
173        button.className = isEnabled ? 'active' : 'inactive';
174        button.querySelector('.status-text').textContent = isEnabled ? 'Auto-Claim ON' : 'Auto-Claim OFF';
175        
176        if (isEnabled) {
177            startMonitoring();
178            addLog('Auto-claim activated! 🎯');
179        } else {
180            stopMonitoring();
181            addLog('Auto-claim deactivated');
182        }
183        
184        console.log(`[Rain Auto-Claimer] ${isEnabled ? 'Enabled' : 'Disabled'}`);
185    }
186
187    // Start monitoring for rain notifications
188    function startMonitoring() {
189        if (observer) {
190            observer.disconnect();
191        }
192        
193        console.log('[Rain Auto-Claimer] Starting monitoring...');
194        
195        // Monitor the entire document for rain-related elements
196        observer = new MutationObserver(debounce((mutations) => {
197            if (!isEnabled) return;
198            
199            // Look for rain notifications and claim buttons
200            scanForRainElements();
201        }, 100));
202        
203        observer.observe(document.body, {
204            childList: true,
205            subtree: true,
206            attributes: true,
207            attributeFilter: ['class', 'style']
208        });
209        
210        // Initial scan
211        scanForRainElements();
212        
213        // Also monitor iframes
214        monitorIframes();
215    }
216
217    // Monitor iframes for rain elements
218    function monitorIframes() {
219        const iframes = document.querySelectorAll('iframe');
220        iframes.forEach(iframe => {
221            try {
222                if (iframe.contentDocument && iframe.contentDocument.body) {
223                    const iframeObserver = new MutationObserver(debounce(() => {
224                        if (!isEnabled) return;
225                        scanForRainElementsInContext(iframe.contentDocument);
226                    }, 100));
227                    
228                    iframeObserver.observe(iframe.contentDocument.body, {
229                        childList: true,
230                        subtree: true,
231                        attributes: true
232                    });
233                }
234            } catch (e) {
235                // Cross-origin iframe, can't access
236                console.log('[Rain Auto-Claimer] Cannot access iframe:', e.message);
237            }
238        });
239    }
240
241    // Scan for rain elements in a specific context
242    function scanForRainElementsInContext(context = document) {
243        // Look for various rain-related selectors
244        const selectors = [
245            '[class*="rain" i][class*="claim" i]',
246            '[class*="rain" i] button',
247            'button[class*="claim" i]',
248            '[id*="rain" i][id*="claim" i]',
249            '[data-testid*="rain" i]',
250            '[aria-label*="claim" i]',
251            'button:not([disabled])[class*="button" i]',
252            '.claim-button',
253            '.rain-claim',
254            '[onclick*="claim" i]'
255        ];
256        
257        selectors.forEach(selector => {
258            try {
259                const elements = context.querySelectorAll(selector);
260                elements.forEach(element => {
261                    const elementId = generateElementId(element);
262                    if (!processedElements.has(elementId) && isRainClaimButton(element)) {
263                        processedElements.add(elementId);
264                        claimRain(element);
265                    }
266                });
267            } catch (e) {
268                // Invalid selector or other error
269            }
270        });
271        
272        // Also check for text content containing "rain" and "claim"
273        const allButtons = context.querySelectorAll('button:not([disabled]), [role="button"]:not([disabled])');
274        allButtons.forEach(button => {
275            const text = button.textContent.toLowerCase();
276            const elementId = generateElementId(button);
277            
278            if (!processedElements.has(elementId) && 
279                (text.includes('claim') || text.includes('rain')) &&
280                isVisible(button)) {
281                processedElements.add(elementId);
282                claimRain(button);
283            }
284        });
285        
286        // Auto-scroll chat if new messages appear
287        autoScrollChat(context);
288    }
289
290    // Scan for rain elements (wrapper for default context)
291    function scanForRainElements() {
292        scanForRainElementsInContext(document);
293    }
294
295    // Check if element is a rain claim button
296    function isRainClaimButton(element) {
297        const text = element.textContent.toLowerCase();
298        const className = element.className.toLowerCase();
299        const id = element.id.toLowerCase();
300        
301        const hasRainKeyword = text.includes('rain') || className.includes('rain') || id.includes('rain');
302        const hasClaimKeyword = text.includes('claim') || className.includes('claim') || id.includes('claim');
303        
304        return (hasRainKeyword || hasClaimKeyword) && isVisible(element);
305    }
306
307    // Check if element is visible
308    function isVisible(element) {
309        if (!element) return false;
310        const style = window.getComputedStyle(element);
311        return style.display !== 'none' && 
312               style.visibility !== 'hidden' && 
313               style.opacity !== '0' &&
314               element.offsetWidth > 0 &&
315               element.offsetHeight > 0;
316    }
317
318    // Generate unique ID for element
319    function generateElementId(element) {
320        return `${element.tagName}_${element.className}_${element.textContent.substring(0, 50)}_${element.getBoundingClientRect().top}`;
321    }
322
323    // Claim rain reward with human-like behavior
324    async function claimRain(button) {
325        if (!isEnabled) return;
326        
327        try {
328            console.log('[Rain Auto-Claimer] Attempting to claim rain...');
329            
330            // Add random delay to appear more human-like (50-200ms)
331            const delay = Math.floor(Math.random() * 150) + 50;
332            await new Promise(resolve => setTimeout(resolve, delay));
333            
334            // Simulate mouse movement and click
335            const rect = button.getBoundingClientRect();
336            const x = rect.left + rect.width / 2;
337            const y = rect.top + rect.height / 2;
338            
339            // Dispatch mouse events for more realistic interaction
340            const mouseOverEvent = new MouseEvent('mouseover', {
341                bubbles: true,
342                cancelable: true,
343                view: window,
344                clientX: x,
345                clientY: y
346            });
347            button.dispatchEvent(mouseOverEvent);
348            
349            await new Promise(resolve => setTimeout(resolve, 20));
350            
351            const mouseDownEvent = new MouseEvent('mousedown', {
352                bubbles: true,
353                cancelable: true,
354                view: window,
355                clientX: x,
356                clientY: y
357            });
358            button.dispatchEvent(mouseDownEvent);
359            
360            await new Promise(resolve => setTimeout(resolve, 30));
361            
362            const mouseUpEvent = new MouseEvent('mouseup', {
363                bubbles: true,
364                cancelable: true,
365                view: window,
366                clientX: x,
367                clientY: y
368            });
369            button.dispatchEvent(mouseUpEvent);
370            
371            // Click the button
372            button.click();
373            
374            // Update claim count
375            claimCount++;
376            await GM.setValue('rainClaimCount', claimCount);
377            
378            addLog(`✅ Claimed rain #${claimCount}!`);
379            console.log(`[Rain Auto-Claimer] Successfully claimed rain #${claimCount}`);
380            
381            // Remove from processed after some time to allow re-claiming if needed
382            setTimeout(() => {
383                const elementId = generateElementId(button);
384                processedElements.delete(elementId);
385            }, 30000); // 30 seconds
386            
387        } catch (error) {
388            console.error('[Rain Auto-Claimer] Error claiming rain:', error);
389            addLog('❌ Claim failed');
390        }
391    }
392
393    // Auto-scroll chat when new messages appear
394    function autoScrollChat(context = document) {
395        const chatSelectors = [
396            '[class*="chat" i][class*="container" i]',
397            '[class*="chat" i][class*="messages" i]',
398            '[class*="message" i][class*="list" i]',
399            '[id*="chat" i]',
400            '.chat-container',
401            '.messages-container',
402            '.chat-messages'
403        ];
404        
405        chatSelectors.forEach(selector => {
406            try {
407                const chatContainers = context.querySelectorAll(selector);
408                chatContainers.forEach(container => {
409                    if (container.scrollHeight > container.clientHeight) {
410                        // Smooth scroll to bottom
411                        container.scrollTo({
412                            top: container.scrollHeight,
413                            behavior: 'smooth'
414                        });
415                    }
416                });
417            } catch (e) {
418                // Selector error
419            }
420        });
421    }
422
423    // Stop monitoring
424    function stopMonitoring() {
425        if (observer) {
426            observer.disconnect();
427            observer = null;
428        }
429        console.log('[Rain Auto-Claimer] Monitoring stopped');
430    }
431
432    // Add log entry
433    function addLog(message) {
434        const logDisplay = document.getElementById('rain-claim-log');
435        if (!logDisplay) return;
436        
437        const timestamp = new Date().toLocaleTimeString();
438        const logEntry = document.createElement('div');
439        logEntry.className = 'log-entry';
440        logEntry.textContent = `[${timestamp}] ${message}`;
441        
442        logDisplay.appendChild(logEntry);
443        logDisplay.classList.add('show');
444        
445        // Keep only last 5 entries
446        while (logDisplay.children.length > 5) {
447            logDisplay.removeChild(logDisplay.firstChild);
448        }
449        
450        // Auto-hide after 5 seconds
451        setTimeout(() => {
452            if (logDisplay.children.length === 0) {
453                logDisplay.classList.remove('show');
454            }
455        }, 5000);
456    }
457
458    // Start the extension
459    if (document.readyState === 'loading') {
460        document.addEventListener('DOMContentLoaded', init);
461    } else {
462        init();
463    }
464
465})();