Size
7.4 KB
Version
1.0.1
Created
Mar 8, 2026
Updated
14 days ago
1// ==UserScript==
2// @name BetNare Auto Rain Claimer
3// @description Automatically claims rain rewards on BetNare
4// @version 1.0.1
5// @match https://*.betnare.com/*
6// @icon https://pcdn.i-gamingplatform.com/d8a6fb4680ad860c4f6b862f1bb5b8d4/global_config/d0513638-7539-46d7-b339-37373b8f2be2.webp
7// @grant GM.getValue
8// @grant GM.setValue
9// @grant GM.xmlhttpRequest
10// ==/UserScript==
11(function() {
12 'use strict';
13
14 console.log('BetNare Auto Rain Claimer - Extension loaded');
15
16 // Configuration
17 const CONFIG = {
18 checkInterval: 1000, // Check every 1 second
19 claimDelay: 500, // Delay before claiming (ms)
20 maxRetries: 3
21 };
22
23 // Debounce function to prevent multiple rapid calls
24 function debounce(func, wait) {
25 let timeout;
26 return function executedFunction(...args) {
27 const later = () => {
28 clearTimeout(timeout);
29 func(...args);
30 };
31 clearTimeout(timeout);
32 timeout = setTimeout(later, wait);
33 };
34 }
35
36 // Function to find rain claim button
37 function findRainClaimButton() {
38 // Look for common rain claim button patterns
39 const selectors = [
40 'button[class*="rain"][class*="claim"]',
41 'button[class*="claim"][class*="rain"]',
42 'div[class*="rain"] button[class*="claim"]',
43 'div[class*="rain-modal"] button',
44 'div[class*="rain-popup"] button',
45 '[data-testid*="rain-claim"]',
46 '[data-testid*="claim-rain"]',
47 'button:has-text("Claim Rain")',
48 'button:has-text("Claim")'
49 ];
50
51 for (const selector of selectors) {
52 try {
53 const elements = document.querySelectorAll(selector);
54 for (const element of elements) {
55 const text = element.textContent?.toLowerCase() || '';
56 const classes = element.className?.toLowerCase() || '';
57 const ariaLabel = element.getAttribute('aria-label')?.toLowerCase() || '';
58
59 if (text.includes('rain') || text.includes('claim') ||
60 classes.includes('rain') || ariaLabel.includes('rain')) {
61 console.log('Found potential rain claim button:', element);
62 return element;
63 }
64 }
65 } catch (e) {
66 // Skip invalid selectors
67 }
68 }
69
70 // Fallback: search all buttons for rain-related text
71 const allButtons = document.querySelectorAll('button, [role="button"], a[class*="button"]');
72 for (const button of allButtons) {
73 const text = button.textContent?.toLowerCase() || '';
74 const classes = button.className?.toLowerCase() || '';
75
76 if ((text.includes('rain') && text.includes('claim')) ||
77 (classes.includes('rain') && classes.includes('claim'))) {
78 console.log('Found rain claim button via fallback:', button);
79 return button;
80 }
81 }
82
83 return null;
84 }
85
86 // Function to find rain notification/modal
87 function findRainNotification() {
88 const selectors = [
89 'div[class*="rain"][class*="modal"]',
90 'div[class*="rain"][class*="popup"]',
91 'div[class*="rain"][class*="notification"]',
92 'div[class*="rain"][class*="alert"]',
93 '[data-testid*="rain"]',
94 'div[role="dialog"][class*="rain"]',
95 'div[role="alertdialog"]'
96 ];
97
98 for (const selector of selectors) {
99 try {
100 const elements = document.querySelectorAll(selector);
101 for (const element of elements) {
102 const text = element.textContent?.toLowerCase() || '';
103 if (text.includes('rain') && element.offsetParent !== null) {
104 console.log('Found rain notification:', element);
105 return element;
106 }
107 }
108 } catch (e) {
109 // Skip invalid selectors
110 }
111 }
112
113 return null;
114 }
115
116 // Function to claim rain
117 async function claimRain() {
118 try {
119 const claimButton = findRainClaimButton();
120
121 if (claimButton && claimButton.offsetParent !== null) {
122 console.log('Attempting to claim rain...');
123
124 // Check if button is disabled
125 if (claimButton.disabled || claimButton.getAttribute('disabled') !== null) {
126 console.log('Claim button is disabled');
127 return false;
128 }
129
130 // Click the button
131 claimButton.click();
132 console.log('Rain claim button clicked!');
133
134 // Update statistics
135 const stats = await GM.getValue('rainClaimStats', { total: 0, lastClaim: null });
136 stats.total += 1;
137 stats.lastClaim = new Date().toISOString();
138 await GM.setValue('rainClaimStats', stats);
139
140 console.log(`Total rain claims: ${stats.total}`);
141 return true;
142 }
143 } catch (error) {
144 console.error('Error claiming rain:', error);
145 }
146
147 return false;
148 }
149
150 // Main monitoring function
151 const monitorForRain = debounce(async function() {
152 const rainNotification = findRainNotification();
153 const claimButton = findRainClaimButton();
154
155 if (rainNotification || claimButton) {
156 console.log('Rain detected! Attempting to claim...');
157
158 // Wait a bit before claiming to ensure UI is ready
159 setTimeout(async () => {
160 const claimed = await claimRain();
161 if (claimed) {
162 console.log('Rain successfully claimed!');
163 }
164 }, CONFIG.claimDelay);
165 }
166 }, 500);
167
168 // Set up MutationObserver to watch for rain notifications
169 const observer = new MutationObserver(debounce(function(mutations) {
170 monitorForRain();
171 }, 300));
172
173 // Start observing when body is ready
174 function init() {
175 if (document.body) {
176 console.log('Starting rain monitor...');
177
178 // Observe DOM changes
179 observer.observe(document.body, {
180 childList: true,
181 subtree: true,
182 attributes: true,
183 attributeFilter: ['class', 'style']
184 });
185
186 // Also check periodically
187 setInterval(monitorForRain, CONFIG.checkInterval);
188
189 // Initial check
190 monitorForRain();
191
192 console.log('Rain monitor active!');
193 } else {
194 setTimeout(init, 100);
195 }
196 }
197
198 // Display statistics on page load
199 async function displayStats() {
200 const stats = await GM.getValue('rainClaimStats', { total: 0, lastClaim: null });
201 console.log('=== Rain Claim Statistics ===');
202 console.log(`Total claims: ${stats.total}`);
203 console.log(`Last claim: ${stats.lastClaim || 'Never'}`);
204 console.log('============================');
205 }
206
207 // Start the extension
208 if (document.readyState === 'loading') {
209 document.addEventListener('DOMContentLoaded', init);
210 } else {
211 init();
212 }
213
214 // Display stats
215 displayStats();
216
217})();