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