Automatically claims vouchers on Easybet by filling forms, submitting twice, closing tabs, and refreshing the voucher drop page
Size
19.0 KB
Version
1.0.1
Created
Nov 22, 2025
Updated
about 2 months ago
1// ==UserScript==
2// @name Easybet Voucher Auto Claimer
3// @description Automatically claims vouchers on Easybet by filling forms, submitting twice, closing tabs, and refreshing the voucher drop page
4// @version 1.0.1
5// @match https://*.easybet.co.za/*
6// @icon https://s3.eu-central-1.wasabisys.com/online-sa5-c13d78733b/public/17c0075c-eb4e-3e31-75b8-1728a00cc543
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 // =================================================================
12 // GLOBAL CONFIG & STATE
13 // =================================================================
14 let config = {
15 enabled: false,
16 userId: '5376545',
17 clickedSurveys: [],
18
19 // General Settings
20 scanIntervalMin: 3000,
21 scanIntervalMax: 7000,
22 selectionStrategy: 'first',
23
24 // Humanization Settings
25 reloadScanDelayMin: 2000,
26 reloadScanDelayMax: 5000,
27
28 voucherClickDelayMin: 300,
29 voucherClickDelayMax: 800,
30
31 surveyDelayMin: 1000,
32 surveyDelayMax: 2500,
33
34 typingDelayMin: 80,
35 typingDelayMax: 200,
36
37 validationDelayMin: 200,
38 validationDelayMax: 500,
39
40 submitDelayMin: 500,
41 submitDelayMax: 1200,
42
43 doubleSubmitDelayMin: 500,
44 doubleSubmitDelayMax: 1000,
45
46 doneDelayMin: 1000,
47 doneDelayMax: 2000
48 };
49
50 let easybetInterval = null;
51 let completionListener = null;
52
53 // =================================================================
54 // HELPER FUNCTIONS
55 // =================================================================
56
57 function randomDelay(min, max) {
58 let nMin = parseInt(min, 10);
59 let nMax = parseInt(max, 10);
60 if (isNaN(nMin)) nMin = 1000;
61 if (isNaN(nMax)) nMax = 3000;
62 if (nMin > nMax) [nMin, nMax] = [nMax, nMin];
63 return Math.floor(Math.random() * (nMax - nMin + 1) + nMin);
64 }
65
66 function logDebug(message) {
67 console.log(`[EB Bot] ${message}`);
68 const logArea = document.getElementById('eb-debug-log');
69 if (logArea) {
70 logArea.value += `[${new Date().toLocaleTimeString()}] ${message}\n`;
71 logArea.scrollTop = logArea.scrollHeight;
72 }
73 }
74
75 function typeText(element, text, callback) {
76 let i = 0;
77 element.focus();
78 function nextChar() {
79 if (i < text.length) {
80 element.value += text[i];
81 element.dispatchEvent(new Event('input', { bubbles: true }));
82 i++;
83 setTimeout(nextChar, randomDelay(config.typingDelayMin, config.typingDelayMax));
84 } else {
85 element.dispatchEvent(new Event('change', { bubbles: true }));
86 element.blur();
87 if (callback) callback();
88 }
89 }
90 setTimeout(nextChar, randomDelay(300, 800));
91 }
92
93 // =================================================================
94 // CORE AUTOMATION LOGIC
95 // =================================================================
96
97 // --- 1. Easybet Page Logic ---
98 function findAndClickVoucher() {
99 const allUnlockedVouchers = document.querySelectorAll('div.voucher[data-status="unlocked"]');
100 if (allUnlockedVouchers.length === 0) return;
101
102 const eligibleVouchers = Array.from(allUnlockedVouchers).filter(voucher => {
103 const surveyLink = voucher.getAttribute('data-survey-link');
104 if (!surveyLink) return false;
105 try {
106 const surveyId = new URL(surveyLink).searchParams.get('id');
107 return surveyId && !config.clickedSurveys.includes(surveyId);
108 } catch (e) { return false; }
109 });
110
111 if (eligibleVouchers.length === 0) return;
112
113 let voucherToClick;
114 if (config.selectionStrategy === 'random') {
115 logDebug(`Found ${eligibleVouchers.length} eligible vouchers. Picking one at random.`);
116 voucherToClick = eligibleVouchers[Math.floor(Math.random() * eligibleVouchers.length)];
117 } else {
118 logDebug(`Found ${eligibleVouchers.length} eligible vouchers. Picking the first one.`);
119 voucherToClick = eligibleVouchers[0];
120 }
121
122 const voucherId = voucherToClick.id;
123 const surveyId = new URL(voucherToClick.getAttribute('data-survey-link')).searchParams.get('id');
124
125 if (easybetInterval) {
126 clearInterval(easybetInterval);
127 easybetInterval = null;
128 }
129
130 logDebug(`Selected voucher #${voucherId} (Survey: ${surveyId}).`);
131 logDebug("--- SCANNER STOPPED --- Waiting for survey completion signal.");
132
133 config.clickedSurveys.push(surveyId);
134 GM.setValue('eb_clicked_surveys', config.clickedSurveys);
135
136 const clickDelay = randomDelay(config.voucherClickDelayMin, config.voucherClickDelayMax);
137 setTimeout(() => {
138 logDebug(`Clicking voucher #${voucherId}.`);
139 voucherToClick.click();
140 }, clickDelay);
141 }
142
143 // --- 2. SurveyMonkey Page Logic ---
144 function runSurveyBot(userId) {
145 logDebug("Running on SurveyMonkey page.");
146 window.addEventListener('load', () => {
147 const surveyWaitTime = randomDelay(config.surveyDelayMin, config.surveyDelayMax);
148 logDebug(`Survey loaded. Waiting ${surveyWaitTime}ms before filling...`);
149 setTimeout(() => {
150 handleSurvey(userId);
151 }, surveyWaitTime);
152 });
153 }
154
155 function handleSurvey(userId) {
156 const submitButton = document.querySelector('#view-pageNavigation button');
157
158 // --- THANK YOU PAGE LOGIC ---
159 if (document.body.innerText.includes("We hope you get the voucher!")) {
160 logDebug('On "Thank You" page.');
161
162 if (submitButton) {
163 const doneWaitTime = randomDelay(config.doneDelayMin, config.doneDelayMax);
164 logDebug(`Found "Done" button. Finishing in ${doneWaitTime}ms.`);
165
166 setTimeout(() => {
167 submitButton.click(); // Click Done
168
169 logDebug("Signal sent: Job Complete. Closing tab...");
170 // 1. Send Signal to Easybet Tab
171 GM.setValue('eb_completion_signal', Date.now());
172
173 // 2. Close this tab
174 setTimeout(() => {
175 window.close();
176 }, 500);
177
178 }, doneWaitTime);
179 } else {
180 // Even if button isn't found, we should probably close
181 logDebug('Done button not found, closing anyway in 2s.');
182 GM.setValue('eb_completion_signal', Date.now());
183 setTimeout(() => { window.close(); }, 2000);
184 }
185 }
186 // --- FORM FILLING LOGIC ---
187 else {
188 logDebug('On survey form page. Looking for fields...');
189 const inputField = document.querySelector('input[type="text"][class*="smqr-inputField-"]');
190
191 if (inputField && submitButton) {
192 logDebug(`Found fields. Submitting ID: ${userId}`);
193
194 typeText(inputField, userId, () => {
195 logDebug('Typing complete.');
196
197 const validationWaitTime = randomDelay(config.validationDelayMin, config.validationDelayMax);
198 setTimeout(() => {
199 const submitWaitTime = randomDelay(config.submitDelayMin, config.submitDelayMax);
200 logDebug(`Validation done. Submitting in ${submitWaitTime}ms.`);
201
202 setTimeout(() => {
203 logDebug('Submit 1/2.');
204 submitButton.click();
205
206 // Double Submit Logic
207 const doubleWait = randomDelay(config.doubleSubmitDelayMin, config.doubleSubmitDelayMax);
208 setTimeout(() => {
209 logDebug('Submit 2/2 (Confirmation Click).');
210 submitButton.click();
211 }, doubleWait);
212
213 }, submitWaitTime);
214
215 }, validationWaitTime);
216 });
217 } else {
218 if (!inputField) logDebug('Error: Input field not found.');
219 if (!submitButton) logDebug('Error: Submit button not found.');
220 }
221 }
222 }
223
224 // =================================================================
225 // UI & SETTINGS FUNCTIONS
226 // =================================================================
227
228 function injectUIAndListeners() {
229 TM_addStyle(`
230 #eb-float-button { position: fixed; bottom: 20px; right: 20px; width: 60px; height: 60px; background-color: #19c2e8; color: white; border-radius: 50%; border: none; font-size: 24px; line-height: 60px; text-align: center; cursor: pointer; box-shadow: 0 4px 12px rgba(0,0,0,0.3); z-index: 9998; font-family: sans-serif; }
231 #eb-modal-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); z-index: 9999; display: flex; align-items: center; justify-content: center; overflow-y: auto; }
232 #eb-modal-content { background-color: #333; color: #fff; padding: 20px; border-radius: 10px; width: 90%; max-width: 600px; font-family: sans-serif; margin: 20px 0; }
233 #eb-modal-content h2 { margin-top: 0; color: #fed600; }
234 .eb-setting-row { margin-bottom: 15px; display: grid; grid-template-columns: 200px 1fr; align-items: center; gap: 15px; }
235 .eb-setting-row input[type="text"], .eb-setting-row input[type="number"], .eb-setting-row select { padding: 8px; border-radius: 5px; border: 1px solid #777; background: #555; color: #fff; width: 100%; box-sizing: border-box; }
236 .eb-setting-row .eb-time-inputs { display: flex; gap: 10px; }
237 .eb-setting-row .eb-time-inputs input { width: 100px; }
238 #eb-debug-log { width: 100%; height: 150px; background: #222; color: #0f0; border: 1px solid #777; border-radius: 5px; font-family: monospace; font-size: 12px; resize: vertical; box-sizing: border-box; }
239 .eb-button { background-color: #fed600; color: #000; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 600; cursor: pointer; }
240 .eb-button.eb-button-secondary { background-color: #555; color: #fff; }
241 .eb-switch { position: relative; display: inline-block; width: 60px; height: 34px; justify-self: start; }
242 .eb-switch input { opacity: 0; width: 0; height: 0; }
243 .eb-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 34px; }
244 .eb-slider:before { position: absolute; content: ""; height: 26px; width: 26px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; }
245 input:checked + .eb-slider { background-color: #19c2e8; }
246 input:checked + .eb-slider:before { transform: translateX(26px); }
247 .eb-hidden { display: none; }
248 small { color: #aaa; grid-column: 2; margin-top: -10px; }
249 `);
250
251 const buttonHTML = `<button id="eb-float-button" title="Easybet Bot Settings">🤖</button>`;
252 const modalHTML = `
253 <div id="eb-modal-backdrop" class="eb-hidden">
254 <div id="eb-modal-content">
255 <span id="eb-modal-close" title="Close" style="float:right;cursor:pointer;font-size:24px;">×</span>
256 <h2>🤖 Easybet Bot Settings</h2>
257 <div class="eb-setting-row">
258 <label>Enable Bot</label>
259 <label class="eb-switch"><input type="checkbox" id="eb-setting-enabled"><span class="eb-slider"></span></label>
260 </div>
261 <div class="eb-setting-row"><label>User ID</label><input type="text" id="eb-setting-userid"></div>
262 <div class="eb-setting-row"><label>Strategy</label><select id="eb-setting-strategy"><option value="first">First</option><option value="random">Random</option></select></div>
263 <h3>Delays (ms)</h3>
264 <div class="eb-setting-row"><label>Scan Interval</label><div class="eb-time-inputs"><input type="number" id="eb-setting-scan-min" placeholder="Min"><input type="number" id="eb-setting-scan-max" placeholder="Max"></div></div>
265 <div class="eb-setting-row"><label>Double Click</label><div class="eb-time-inputs"><input type="number" id="eb-setting-double-min" placeholder="Min"><input type="number" id="eb-setting-double-max" placeholder="Max"></div></div>
266 <textarea id="eb-debug-log" readonly></textarea>
267 <div style="margin-top: 20px; display: flex; gap: 10px;">
268 <button id="eb-save-button" class="eb-button">Save</button>
269 <button id="eb-clear-button" class="eb-button eb-button-secondary">Clear Clicked</button>
270 <span id="eb-save-status" style="color: #0f0;"></span>
271 </div>
272 </div>
273 </div>
274 `;
275 document.body.insertAdjacentHTML('beforeend', buttonHTML);
276 document.body.insertAdjacentHTML('beforeend', modalHTML);
277
278 document.getElementById('eb-float-button').addEventListener('click', openModal);
279 document.getElementById('eb-modal-close').addEventListener('click', closeModal);
280 document.getElementById('eb-save-button').addEventListener('click', saveSettings);
281 document.getElementById('eb-clear-button').addEventListener('click', clearClickedList);
282 document.getElementById('eb-modal-backdrop').addEventListener('click', (e) => {
283 if (e.target === e.currentTarget) closeModal();
284 });
285 }
286
287 function openModal() {
288 document.getElementById('eb-setting-enabled').checked = config.enabled;
289 document.getElementById('eb-setting-userid').value = config.userId;
290 document.getElementById('eb-setting-strategy').value = config.selectionStrategy;
291 document.getElementById('eb-setting-scan-min').value = config.scanIntervalMin;
292 document.getElementById('eb-setting-scan-max').value = config.scanIntervalMax;
293 document.getElementById('eb-setting-double-min').value = config.doubleSubmitDelayMin;
294 document.getElementById('eb-setting-double-max').value = config.doubleSubmitDelayMax;
295 document.getElementById('eb-save-status').innerText = '';
296 document.getElementById('eb-modal-backdrop').classList.remove('eb-hidden');
297 }
298
299 function closeModal() {
300 document.getElementById('eb-modal-backdrop').classList.add('eb-hidden');
301 }
302
303 function getIntFromUI(id, defaultValue) {
304 let val = parseInt(document.getElementById(id).value, 10);
305 return isNaN(val) ? defaultValue : val;
306 }
307
308 async function loadSettings() {
309 config.enabled = await GM.getValue('eb_enabled', false);
310 config.userId = await GM.getValue('eb_userId', '5376545');
311 config.clickedSurveys = await GM.getValue('eb_clicked_surveys', []);
312 config.scanIntervalMin = await GM.getValue('eb_scan_min', 3000);
313 config.scanIntervalMax = await GM.getValue('eb_scan_max', 7000);
314 config.selectionStrategy = await GM.getValue('eb_strategy', 'first');
315 config.doubleSubmitDelayMin = await GM.getValue('eb_double_min', 500);
316 config.doubleSubmitDelayMax = await GM.getValue('eb_double_max', 1000);
317 }
318
319 async function saveSettings() {
320 config.enabled = document.getElementById('eb-setting-enabled').checked;
321 config.userId = document.getElementById('eb-setting-userid').value.trim();
322 config.selectionStrategy = document.getElementById('eb-setting-strategy').value;
323 config.scanIntervalMin = getIntFromUI('eb-setting-scan-min', 3000);
324 config.scanIntervalMax = getIntFromUI('eb-setting-scan-max', 7000);
325 config.doubleSubmitDelayMin = getIntFromUI('eb-setting-double-min', 500);
326 config.doubleSubmitDelayMax = getIntFromUI('eb-setting-double-max', 1000);
327
328 await GM.setValue('eb_enabled', config.enabled);
329 await GM.setValue('eb_userId', config.userId);
330 await GM.setValue('eb_strategy', config.selectionStrategy);
331 await GM.setValue('eb_scan_min', config.scanIntervalMin);
332 await GM.setValue('eb_scan_max', config.scanIntervalMax);
333 await GM.setValue('eb_double_min', config.doubleSubmitDelayMin);
334 await GM.setValue('eb_double_max', config.doubleSubmitDelayMax);
335
336 const saveStatus = document.getElementById('eb-save-status');
337 saveStatus.innerText = 'Saved!';
338 setTimeout(() => { saveStatus.innerText = ''; }, 2000);
339 startAutomation();
340 }
341
342 async function clearClickedList() {
343 if (confirm('Clear clicked vouchers list?')) {
344 config.clickedSurveys = [];
345 await GM.setValue('eb_clicked_surveys', []);
346 document.getElementById('eb-save-status').innerText = 'Cleared!';
347 }
348 }
349
350 // =================================================================
351 // INITIALIZATION
352 // =================================================================
353
354 async function startAutomation() {
355 // Clear existing intervals
356 if (easybetInterval) clearInterval(easybetInterval);
357 if (completionListener) clearInterval(completionListener);
358
359 if (!config.enabled) {
360 logDebug("Bot is disabled.");
361 return;
362 }
363
364 const hostname = window.location.hostname;
365
366 // --- EASYBET PAGE ---
367 if (hostname.includes('easybet.co.za')) {
368 const firstScanDelay = randomDelay(config.reloadScanDelayMin, config.reloadScanDelayMax);
369 logDebug(`On Easybet. Starting scan in ${firstScanDelay}ms...`);
370
371 // 1. Start Scanners
372 setTimeout(() => {
373 findAndClickVoucher();
374 const scanTime = randomDelay(config.scanIntervalMin, config.scanIntervalMax);
375 easybetInterval = setInterval(findAndClickVoucher, scanTime);
376 }, firstScanDelay);
377
378 // 2. Start Completion Listener (Checks for signal from Survey tab)
379 let loadTime = Date.now();
380 completionListener = setInterval(async () => {
381 const lastSignal = await GM.getValue('eb_completion_signal', 0);
382 if (lastSignal > loadTime) {
383 logDebug("Received completion signal! Redirecting to Voucher Drop...");
384 loadTime = Date.now();
385 window.location.href = 'https://easybet.co.za/page/voucher-drop';
386 }
387 }, 1000);
388 }
389 // --- SURVEYMONKEY PAGE ---
390 else if (hostname.includes('surveymonkey.com')) {
391 runSurveyBot(config.userId);
392 }
393 }
394
395 // =================================================================
396 // MAIN EXECUTION
397 // =================================================================
398
399 async function init() {
400 await loadSettings();
401
402 if (window.top === window.self) {
403 TM_runBody(() => {
404 injectUIAndListeners();
405 });
406 }
407
408 startAutomation();
409 }
410
411 init();
412
413})();