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