Supabets Aviator Rain Auto-Claimer

Automatically claims Aviator rain on Supabets with toggle control and auto-scroll

Size

14.1 KB

Version

1.1.2

Created

Mar 16, 2026

Updated

about 1 month ago

1// ==UserScript==
2// @name		Supabets Aviator Rain Auto-Claimer
3// @description		Automatically claims Aviator rain on Supabets with toggle control and auto-scroll
4// @version		1.1.2
5// @match		https://*.gaming.supabets.co.za/*
6// @icon		https://gaming.supabets.co.za/images/gaming.supabets.co.za/favicon/favicon.ico
7// @grant		GM.getValue
8// @grant		GM.setValue
9// @grant		GM.xmlhttpRequest
10// ==/UserScript==
11(function() {
12    'use strict';
13
14    console.log('Supabets Aviator Rain Auto-Claimer initialized');
15
16    // Utility function for random delays to appear more human-like
17    function randomDelay(min, max) {
18        return Math.floor(Math.random() * (max - min + 1)) + min;
19    }
20
21    // Debounce function for MutationObserver
22    function debounce(func, wait) {
23        let timeout;
24        return function executedFunction(...args) {
25            const later = () => {
26                clearTimeout(timeout);
27                func(...args);
28            };
29            clearTimeout(timeout);
30            timeout = setTimeout(later, wait);
31        };
32    }
33
34    // State management
35    let isEnabled = false;
36    let toggleButton = null;
37    let processedClaims = new Set();
38
39    // Initialize state from storage
40    async function initState() {
41        isEnabled = await GM.getValue('rainAutoClaimEnabled', false);
42        console.log('Auto-claim enabled:', isEnabled);
43        updateButtonState();
44    }
45
46    // Save state to storage
47    async function saveState(enabled) {
48        await GM.setValue('rainAutoClaimEnabled', enabled);
49        isEnabled = enabled;
50        console.log('Auto-claim state saved:', enabled);
51    }
52
53    // Create toggle button UI
54    function createToggleButton() {
55        const button = document.createElement('button');
56        button.id = 'rain-auto-claim-toggle';
57        button.innerHTML = `
58            <span style="margin-right: 8px;">💰</span>
59            <span id="rain-toggle-text">Rain Auto-Claim: OFF</span>
60        `;
61        button.style.cssText = `
62            position: fixed;
63            bottom: 20px;
64            right: 20px;
65            z-index: 999999;
66            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
67            color: white;
68            border: none;
69            padding: 12px 20px;
70            border-radius: 8px;
71            font-size: 14px;
72            font-weight: 600;
73            cursor: pointer;
74            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
75            transition: all 0.3s ease;
76            display: flex;
77            align-items: center;
78            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
79        `;
80
81        button.addEventListener('mouseenter', () => {
82            button.style.transform = 'translateY(-2px)';
83            button.style.boxShadow = '0 6px 20px rgba(0, 0, 0, 0.3)';
84        });
85
86        button.addEventListener('mouseleave', () => {
87            button.style.transform = 'translateY(0)';
88            button.style.boxShadow = '0 4px 15px rgba(0, 0, 0, 0.2)';
89        });
90
91        button.addEventListener('click', async () => {
92            const newState = !isEnabled;
93            await saveState(newState);
94            updateButtonState();
95            showNotification(newState ? 'Rain Auto-Claim Enabled ✓' : 'Rain Auto-Claim Disabled');
96        });
97
98        document.body.appendChild(button);
99        toggleButton = button;
100        return button;
101    }
102
103    // Update button appearance based on state
104    function updateButtonState() {
105        if (!toggleButton) return;
106
107        const textElement = document.getElementById('rain-toggle-text');
108        if (textElement) {
109            textElement.textContent = isEnabled ? 'Rain Auto-Claim: ON' : 'Rain Auto-Claim: OFF';
110        }
111
112        if (isEnabled) {
113            toggleButton.style.background = 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)';
114        } else {
115            toggleButton.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
116        }
117    }
118
119    // Show notification
120    function showNotification(message) {
121        const notification = document.createElement('div');
122        notification.textContent = message;
123        notification.style.cssText = `
124            position: fixed;
125            top: 80px;
126            right: 20px;
127            z-index: 999999;
128            background: rgba(0, 0, 0, 0.9);
129            color: white;
130            padding: 12px 20px;
131            border-radius: 8px;
132            font-size: 14px;
133            font-weight: 500;
134            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
135            animation: slideIn 0.3s ease;
136            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
137        `;
138
139        document.body.appendChild(notification);
140
141        setTimeout(() => {
142            notification.style.animation = 'slideOut 0.3s ease';
143            setTimeout(() => notification.remove(), 300);
144        }, 3000);
145    }
146
147    // Add CSS animations
148    TM_addStyle(`
149        @keyframes slideIn {
150            from {
151                transform: translateX(400px);
152                opacity: 0;
153            }
154            to {
155                transform: translateX(0);
156                opacity: 1;
157            }
158        }
159        @keyframes slideOut {
160            from {
161                transform: translateX(0);
162                opacity: 1;
163            }
164            to {
165                transform: translateX(400px);
166                opacity: 0;
167            }
168        }
169    `);
170
171    // Auto-scroll to bottom of chat
172    function autoScrollChat(chatContainer) {
173        if (!chatContainer) return;
174        
175        // Smooth scroll with slight delay to appear natural
176        setTimeout(() => {
177            chatContainer.scrollTo({
178                top: chatContainer.scrollHeight,
179                behavior: 'smooth'
180            });
181        }, randomDelay(100, 300));
182    }
183
184    // Find and click claim button with human-like behavior
185    async function claimRain(claimButton) {
186        if (!isEnabled || !claimButton) return;
187
188        // Generate unique identifier for this claim button
189        const buttonId = claimButton.textContent + claimButton.className + Date.now();
190        
191        // Check if already processed
192        if (processedClaims.has(buttonId)) {
193            console.log('Claim button already processed, skipping');
194            return;
195        }
196
197        // Mark as processed
198        processedClaims.add(buttonId);
199
200        // Clean up old processed claims (keep only last 50)
201        if (processedClaims.size > 50) {
202            const iterator = processedClaims.values();
203            processedClaims.delete(iterator.next().value);
204        }
205
206        console.log('Rain detected! Preparing to claim...');
207
208        // Random delay to appear human-like (2-5 seconds)
209        const delay = randomDelay(2000, 5000);
210        console.log(`Waiting ${delay}ms before claiming...`);
211
212        await new Promise(resolve => setTimeout(resolve, delay));
213
214        // Check if button still exists and is clickable
215        if (!document.body.contains(claimButton)) {
216            console.log('Claim button no longer exists');
217            return;
218        }
219
220        try {
221            // Simulate human-like click with mouse events
222            const mouseEvents = ['mouseenter', 'mouseover', 'mousedown', 'mouseup', 'click'];
223            for (const eventType of mouseEvents) {
224                const event = new MouseEvent(eventType, {
225                    view: window,
226                    bubbles: true,
227                    cancelable: true,
228                    buttons: 1
229                });
230                claimButton.dispatchEvent(event);
231                await new Promise(resolve => setTimeout(resolve, randomDelay(10, 50)));
232            }
233
234            console.log('Rain claimed successfully! 💰');
235            showNotification('Rain Claimed! 💰');
236        } catch (error) {
237            console.error('Error claiming rain:', error);
238        }
239    }
240
241    // Search for rain messages and claim buttons
242    function searchForRain() {
243        if (!isEnabled) return;
244
245        // Common selectors for rain/claim buttons on gaming sites
246        const possibleSelectors = [
247            'button[class*="claim"]',
248            'button[class*="rain"]',
249            'div[class*="rain"] button',
250            'button:contains("Claim")',
251            'button:contains("claim")',
252            '[data-testid*="claim"]',
253            '[class*="claim-button"]',
254            '[class*="rain-claim"]',
255            'button[onclick*="claim"]',
256            'a[class*="claim"]',
257            'div[class*="notification"] button',
258            'div[class*="alert"] button',
259            'div[class*="message"] button[class*="claim"]'
260        ];
261
262        // Search through all possible selectors
263        for (const selector of possibleSelectors) {
264            try {
265                const elements = document.querySelectorAll(selector);
266                elements.forEach(element => {
267                    const text = element.textContent.toLowerCase();
268                    // Check if element contains rain-related keywords
269                    if (text.includes('claim') || text.includes('rain') || text.includes('collect')) {
270                        console.log('Found potential claim button:', element);
271                        claimRain(element);
272                    }
273                });
274            } catch (e) {
275                // Selector might not be valid, continue
276            }
277        }
278
279        // Also search in iframes (chat might be in iframe)
280        try {
281            const iframes = document.querySelectorAll('iframe');
282            iframes.forEach(iframe => {
283                try {
284                    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
285                    if (iframeDoc) {
286                        const buttons = iframeDoc.querySelectorAll('button, a');
287                        buttons.forEach(button => {
288                            const text = button.textContent.toLowerCase();
289                            if (text.includes('claim') || text.includes('rain')) {
290                                console.log('Found claim button in iframe:', button);
291                                claimRain(button);
292                            }
293                        });
294                    }
295                } catch (e) {
296                    // Cross-origin iframe, can't access
297                }
298            });
299        } catch (e) {
300            console.log('Could not search iframes:', e);
301        }
302    }
303
304    // Monitor DOM for changes
305    function setupMutationObserver() {
306        const debouncedSearch = debounce(() => {
307            searchForRain();
308        }, 500);
309
310        const observer = new MutationObserver((mutations) => {
311            if (!isEnabled) return;
312
313            let shouldSearch = false;
314            let chatContainer = null;
315
316            mutations.forEach((mutation) => {
317                // Check if new nodes were added
318                if (mutation.addedNodes.length > 0) {
319                    mutation.addedNodes.forEach(node => {
320                        if (node.nodeType === 1) { // Element node
321                            const text = node.textContent?.toLowerCase() || '';
322                            
323                            // Check for rain-related content
324                            if (text.includes('rain') || text.includes('claim')) {
325                                shouldSearch = true;
326                                console.log('New rain-related content detected');
327                            }
328
329                            // Find chat container for auto-scroll
330                            if (node.classList && (
331                                node.classList.contains('chat') ||
332                                node.classList.contains('messages') ||
333                                node.classList.contains('message-list') ||
334                                node.getAttribute('class')?.includes('chat') ||
335                                node.getAttribute('class')?.includes('message')
336                            )) {
337                                chatContainer = node.closest('[class*="chat"]') || node.closest('[class*="message"]');
338                            }
339                        }
340                    });
341                }
342            });
343
344            if (shouldSearch) {
345                debouncedSearch();
346                
347                // Auto-scroll if chat container found
348                if (chatContainer) {
349                    autoScrollChat(chatContainer);
350                } else {
351                    // Try to find chat container in DOM
352                    const possibleChatContainers = document.querySelectorAll(
353                        '[class*="chat"], [class*="message"], [class*="conversation"], [id*="chat"], [id*="message"]'
354                    );
355                    possibleChatContainers.forEach(container => {
356                        if (container.scrollHeight > container.clientHeight) {
357                            autoScrollChat(container);
358                        }
359                    });
360                }
361            }
362        });
363
364        // Observe entire document
365        observer.observe(document.body, {
366            childList: true,
367            subtree: true,
368            attributes: false,
369            characterData: false
370        });
371
372        console.log('MutationObserver setup complete');
373    }
374
375    // Initialize extension
376    async function init() {
377        console.log('Initializing Supabets Aviator Rain Auto-Claimer...');
378
379        // Wait for body to be ready
380        if (!document.body) {
381            setTimeout(init, 100);
382            return;
383        }
384
385        // Initialize state
386        await initState();
387
388        // Create toggle button
389        createToggleButton();
390
391        // Setup mutation observer
392        setupMutationObserver();
393
394        // Initial search for rain
395        setTimeout(() => {
396            searchForRain();
397        }, 2000);
398
399        // Periodic check every 10 seconds as backup
400        setInterval(() => {
401            if (isEnabled) {
402                searchForRain();
403            }
404        }, 10000);
405
406        console.log('Extension ready! Toggle the button to enable/disable auto-claim.');
407    }
408
409    // Start when DOM is ready
410    if (document.readyState === 'loading') {
411        document.addEventListener('DOMContentLoaded', init);
412    } else {
413        init();
414    }
415})();