// ==UserScript== // @name Auto Claim with Monitor/Manager UI: StormGain Miner + 15 Faucets + Promo Codes processing // @namespace satology.onrender.com // @version 1.0.7 // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description MAIN FEATURES: // @description > Automatic hourly rolls for 15 faucets (ADA, BNB, BTC, DASH, DOGE, ETH, LINK, LTC, NEO, STEAM, TRX, USDC, USDT, XEM, XRP) // @description > Automatic activation of StormGain Miner (free BTC every 4 hours) // @description > Accepts promotion codes (http://twitter.com/cryptosfaucets, free roll shortlinks) for the 15 faucets // @description > Simple Monitor UI on top of a website to track progress (claims, next rolls, promo codes) // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description > The idea is to release future versions with more faucets & PTC (some for FaucetPay/ExpressCrypto) and user-friendly configurations // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description IMPORTANT CONSIDERATIONS: // @description 0. You need to enable popups on the Manager UI website to be able to open the faucets // @description 1. Promo codes (for now) must be manually added through the Manager UI: // @description You can add multiple codes and will be processed for each faucet after rolling. // @description After adding a new promo code, it will take a minute or so to save it. Then will // @description try to activate it right AFTER the next scheduled roll for each faucet and roll again. // @description For a smoother perfomance, once in a while click Remove ALL to delete the codes if the faucets already processed them. // @description 2. FAUCETS WEBSITES MUST OPEN IN ENGLISH TO BE ABLE TO RECOGNIZE IF THE PROMO CODE WAS ACCEPTED // @description In case you don't want to have them in English, you need to change the 3 strings the code uses for validation // @description (Search for localeStrings in the code and replace them) // @description 3. Autorolls will trigger ONLY when the faucet was opened by the Manager UI. // @description This is to allow users to navigate the websites to get the ShortLinks extra rolls, for example, // @description without having to stop the script. // @description 4. No AutoLogin implemented yet, so YOU MUST BE LOGGED IN // @description 5. You can disable faucets from the script in case you need to or you are not registered yet. // @description It would be great if you could use my referral links listed below if you need an account. // @description To disable them, just set enabled: false in the webList array, save the script & refresh the manager // @description 6. All data stored for tracking and to be displayed is stored locally in your environment. Nothing is uploaded. // @description // @description Always opened to feedback. I'd be glad to hear from you if you find any bugs, have suggestions or new enhancements/features you'd like to see // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @note [@1.0.7] Added 3 variables to a config object, to avoid endless waits: // @note > maxRefreshAttempts [default 4]: how many times a faucet will refresh if the roll fails in case of errors such as "Please refresh the // @note page to prove you are not a robot" alert; before moving to the next one. // @note > timeout [default 5 min]: time to wait for a faucet result after opening one // @note > moveAfterTimeout [default true]: to skip the faucet for 20 minutes if it timed out // @note More details about how these are used can be found in comments next to their declaration. // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description About the code: // @description Manager UI (monitor): // @description - Let's you keep track of last claimed amount, accumulated claims, total balance and next rolls. // @description - Controls the 'flow'. Opens a new tab to roll when needed and reads the results. // @description - Let's you add the promo codes and shows you the status of them for each faucet // @description - Everything is stored locally, but the Manager UI runs on top of a personal website with some ads // @description You can, of course, replace it with another URL, but please consider keeping it as a 'thank you' if you find the script useful/helpful // @description - HTML and CSS are basic/simple as the goal is only to show status data and I'm not a UI/UX expert // @description SGProcessor: // @description - Works on StormGain website // @description - Activates the miner whenever is stopped (every 4 hours) // @description - Saves the balance to be displayed on the Manager UI // @description CFProcessor: // @description - Works on the 15 faucets (.../free) // @description - Creates some random 'interaction'. You can disable interactions or adjust them a little. // @description Search for RandomInteractionLevel // @description - After clicking the roll button, waits for the countdown or reloads the page if the invisible captcha validation fails // @description - Stores the claimed amount, balance, rolled number and time for the Manager UI to update itself // @description - If the Roll button is not there, stores the countdown value to adjust the Manager UI next roll time // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description For upcoming updates: // @description - Keep a second window always opened to do all the navigation // @description - Extra antibot random actions like going back and forward from a random page in the website (FAQ, Stats, etc.) // @description - Display a message in the faucets UI to let the user know the process current status // @description - AutoLogin using local variables to store the credentials // @description - Enable/Disable faucet from the Manager UI // @description - Code refactor // @description - Implement FaucetPay PTC autoclicker (https://faucetpay.io/?r=1140585) // @description - Implement Freebitco.in autoclaim (https://freebitco.in/?r=41092365) // @description - Implement autonavigator for captcha faucets to automatically open and prompt for input when you can claim // @description - Implement auto claim for other faucets with 'weak' captcha validations // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description Links to create a new account using my referral: https://app.stormgain.com/friend/BNS17722452 https://freecardano.com/?ref=346495 https://freebinancecoin.com/?ref=176633 https://freebitcoin.io/?ref=506599 https://freedash.io/?ref=132684 https://free-doge.com/?ref=199022 https://freeethereum.com/?ref=218020 https://freechainlink.io/?ref=84537 https://free-ltc.com/?ref=127524 https://freeneo.io/?ref=107888 https://freesteam.io/?ref=122086 https://free-tron.com/?ref=237317 https://freeusdcoin.com/?ref=139322 https://freetether.com/?ref=190533 https://freenem.com/?ref=300886 https://coinfaucet.io/?ref=822473 // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @description If you wanna team up or just share some ideas, you can contact me at satology@protonmail.com // @description ------------------------------------------------------------------------------------------------------------------------------------------------ // @author satology // @grant GM_setValue // @grant GM_getValue // @grant window.close // @grant GM_openInTab // @icon https://www.google.com/s2/favicons?domain=stormgain.com // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js // @match https://satology.onrender.com/faucets/referrals* // @match https://app.stormgain.com/crypto-miner/ // @match https://freecardano.com/free // @match https://freebinancecoin.com/free // @match https://freebitcoin.io/free // @match https://freedash.io/free // @match https://free-doge.com/free // @match https://freeethereum.com/free // @match https://freechainlink.io/free // @match https://free-ltc.com/free // @match https://freeneo.io/free // @match https://freesteam.io/free // @match https://free-tron.com/free // @match https://freeusdcoin.com/free // @match https://freetether.com/free // @match https://freenem.com/free // @match https://coinfaucet.io/free // @match https://freecardano.com/promotion/* // @match https://freebinancecoin.com/promotion/* // @match https://freebitcoin.io/promotion/* // @match https://free-doge.com/promotion/* // @match https://freedash.io/promotion/* // @match https://freeethereum.com/promotion/* // @match https://freechainlink.io/promotion/* // @match https://free-ltc.com/promotion/* // @match https://freeneo.io/promotion/* // @match https://freesteam.io/promotion/* // @match https://free-tron.com/promotion/* // @match https://freeusdcoin.com/promotion/* // @match https://freetether.com/promotion/* // @match https://freenem.com/promotion/* // @match https://coinfaucet.io/promotion/* // ==/UserScript== (function() { 'use strict'; /** * Specific string values to check if a promotion code was succesfully processed (used via indexOf). * Defaults are set for English. * If you are viewing the faucets in another language, you will need to change them or * switch the faucets to English */ const localeConfig = { stringSearches: { promoCodeAccepted: 'roll', promoCodeUsed: 'already used', promoCodeInvalid: 'not found', promoCodeInvalid2: 'only alphanumeric' } }; const WebType = { CRYPTOSFAUCETS: 1, STORMGAIN: 2 }; const PromoStatus = { NOCODE: 0, PENDING: 1, ACCEPTED: 2, USEDBEFORE: 3, INVALID: 4, UNKNOWNERROR: 5 }; const RandomInteractionLevel = { NONE: 0, LOW: 1, MEDIUM: 2, HIGH: 3 }; let config = { maxRefreshAttempts: 4, // Use -1 to do unlimit attempts. Is the number of consecutive times it will try to refresh a faucet when failing to roll due to invisible captcha // ("Please Refresh the page to prove you are not a robot") // After reaching maxRefreshAttempts, the script will move to the next faucet, to prevent being flagged which could ban the IP for 24hs in that specific faucet. timeout: 5, // Use -1 to disable the timeout functionality. In minutes. Max time the monitor will wait for a result/roll. // After timeout is reached, the Manager/Monitor will assume something is wrong // (usually faucet tab being accidentally closed or failed to connect) // Will trigger a refresh on the Monitor website and carry on the process moveAfterTimeout: true, // If set to true, after a timeout the Monitor will skip the faucet, move to the next one and refresh itself } let persistence, shared, manager, ui, CFPromotions, interactions, SGProcessor, CFProcessor, CFHistory; let helpers = { cleanString: function(input) { var output = ""; for (var i=0; i enm[key] === value); }, randomMs: function (a, b){ return a + (b - a) * Math.random(); }, addMinutes: function(date, mins) { return date.setMinutes(date.getMinutes() + parseInt(mins) + 1); }, randomInt: function(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); }, addMilliseconds: function(date, ms) { return date.setMilliseconds(date.getMilliseconds() + ms); }, getEmojiForPromoStatus: function(promoStatus) { switch (promoStatus) { case PromoStatus.NOCODE: return '⚪'; break; case PromoStatus.PENDING: return '⏳'; break; case PromoStatus.ACCEPTED: return '✔️'; break; case PromoStatus.USEDBEFORE: return '🕙'; break; case PromoStatus.INVALID: return '❌'; break; case PromoStatus.UNKNOWNERROR: return '❗'; break; } } } let objectGenerator = { createPersistence: function() { const prefix = 'autoWeb_'; function save(key, value, parseIt = false) { GM_setValue(prefix + key, parseIt ? JSON.stringify(value) : value); }; function load(key, parseIt = false) { let value = GM_getValue(prefix + key); if(value && parseIt) { value = JSON.parse(value); } return value; }; return { save: save, load: load }; }, createShared: function() { let flowControl; function isOpenedByManager(currentUrl) { loadFlowControl(); if(!flowControl) { return false; } let millisecondsDistance = new Date() - flowControl.requestedTime; if(flowControl.opened || flowControl.url != currentUrl || millisecondsDistance > 120000) { return false; } return true; }; function setFlowControl(id, url, webType) { flowControl = { id: id, url: url, type: webType, requestedTime: new Date(), opened: false, maxRefreshAttempts: config.maxRefreshAttempts, refreshAttempts: 0, maxRefreshReached: false, result: {} }; persistence.save('flowControl', flowControl, true); }; function wasVisited(expectedId) { loadFlowControl(); return flowControl.id == expectedId && flowControl.opened; }; function wasMaxRefreshReached(expectedId) { loadFlowControl(); return flowControl.id == expectedId && flowControl.maxRefreshReached; }; function getResult() { return flowControl.result; }; function getCurrent() { let current = {}; current.url = flowControl.url; current.type = flowControl.type; return current; }; function saveAndclose(runDetails, delay = 0) { markAsVisited(runDetails); if(delay) { setTimeout(window.close, delay); } else { window.close(); } }; function closeWithMaxRefreshReached(delay = 0) { markAsMaxRefreshReached(); if(delay) { setTimeout(window.close, delay); } else { window.close(); } }; function loadFlowControl() { flowControl = persistence.load('flowControl', true); }; function markAsVisited(runDetails) { flowControl.opened = true; flowControl.result = runDetails; persistence.save('flowControl', flowControl, true); }; function markAsMaxRefreshReached() { flowControl.maxRefreshReached = true; persistence.save('flowControl', flowControl, true); }; function canDoRefresh() { flowControl.refreshAttempts++; persistence.save('flowControl', flowControl, true); if (flowControl.maxRefreshAttempts == -1 || flowControl.maxRefreshAttempts >= flowControl.refreshAttempts) { return true; } else { return false; } } return { setFlowControl: setFlowControl, wasVisited: wasVisited, isOpenedByManager: isOpenedByManager, getCurrent: getCurrent, getResult: getResult, closeWindow: saveAndclose, wasMaxRefreshReached: wasMaxRefreshReached, closeWithMaxRefreshReached: closeWithMaxRefreshReached, canDoRefresh: canDoRefresh }; }, createManager: function() { let timestamp = null; let timeWaiting = 0; let promoInterval; let webList = [ { id: '1', name: 'ADA', url: 'https://freecardano.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '2', name: 'BNB', url: 'https://freebinancecoin.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '3', name: 'BTC', url: 'https://freebitcoin.io/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '4', name: 'DASH', url: 'https://freedash.io/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '5', name: 'ETH', url: 'https://freeethereum.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '6', name: 'LINK', url: 'https://freechainlink.io/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '7', name: 'LTC', url: 'https://free-ltc.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '8', name: 'NEO', url: 'https://freeneo.io/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '9', name: 'STEAM', url: 'https://freesteam.io/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '10', name: 'TRX', url: 'https://free-tron.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '11', name: 'USDC', url: 'https://freeusdcoin.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '12', name: 'USDT', url: 'https://freetether.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '13', name: 'XEM', url: 'https://freenem.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '14', name: 'XRP', url: 'https://coinfaucet.io/free', type: WebType.CRYPTOSFAUCETS, enabled: true }, { id: '15', name: 'StormGain', url: 'https://app.stormgain.com/crypto-miner/', type: WebType.STORMGAIN, enabled: true }, { id: '16', name: 'DOGE', url: 'https://free-doge.com/free', type: WebType.CRYPTOSFAUCETS, enabled: true } ]; function start(){ loader.initialize(); ui.init(getCFlist()); update(); promoInterval = setInterval(manager.readNewPromoCode, 5000); setTimeout(manager.process, 10000); }; let loader = function() { function initialize() { setTimestamp(); initializeWebList(); initializePromotions(); initializeHistory(); }; function initializeWebList() { let storedData = persistence.load('webList', true); if(storedData) { let newOnes = addNewOnes(storedData); if (newOnes) { newOnes.forEach( function (element, idx, arr) { storedData.push(element); }); } let disabledList = webList.filter( x => !x.enabled ).map( x => x.id ); storedData.forEach( function (element, idx, arr) { arr[idx].nextRoll = element.nextRoll ? new Date(element.nextRoll) : new Date(); arr[idx].enabled = !disabledList.includes(element.id); }); webList = storedData; setup(); } else { setup(true); } }; function addNewOnes(storedData) { let allIds = webList.map( x => x.id ); let storedIds = storedData.map( x => x.id ); let newOnes = allIds.filter( x => !storedIds.includes(x) ); return webList.filter( x => newOnes.includes(x.id) ); }; function initializePromotions() { let storedData = persistence.load('CFPromotions', true); if (storedData) { storedData.forEach( function (element, idx, arr) { arr[idx].added = new Date(element.added); }); CFPromotions.load(storedData); } }; function initializeHistory() { CFHistory.initOrLoad(); }; function setTimestamp() { timestamp = new Date().getTime(); persistence.save('timestamp', timestamp); }; function removeDisabledFaucets() { webList = webList.filter(x => x.enabled); }; function setup(reset = false) { removeDisabledFaucets(); if(reset) { helpers.shuffle(webList); } let timeDistance = 0; webList.forEach( function (element, idx, arr) { if (reset || !element.lastClaim) { arr[idx].lastClaim = 0; } if (reset || !element.aggregate) { arr[idx].aggregate = 0; } if (reset || !element.balance) { arr[idx].balance = 0; } if (reset || !element.nextRoll) { timeDistance += helpers.randomMs(10000, 15000); arr[idx].nextRoll = new Date(helpers.addMilliseconds(new Date(), timeDistance)); } }); }; return { initialize: initialize }; }(); function update(sortIt = true) { if(sortIt) { webList.sort((a,b) => a.nextRoll.getTime() - b.nextRoll.getTime()); } persistence.save('webList', webList, true); ui.refresh(webList, CFPromotions.getAll()); updateRollStatsSpan(); }; function process() { if(isObsolete()) { return; } if(webList[0].nextRoll.getTime() < (new Date()).getTime()) { ui.log('Opening: ' + webList[0].name); open(); } else { let timeUntilNext = webList[0].nextRoll.getTime() - (new Date()).getTime() + helpers.randomMs(1000, 2000); ui.log('Waiting ' + (timeUntilNext/1000/60).toFixed(2) + ' minutes...'); setTimeout(manager.process, timeUntilNext); } }; function isObsolete() { let savedTimestamp = persistence.load('timestamp'); if (savedTimestamp && savedTimestamp > timestamp) { ui.log('STOPING EXECUTION! A new Manager UI window was opened. Process should continue there'); clearInterval(promoInterval); return true; } return false; }; function open(promoCode) { let navUrl = webList[0].url; if(promoCode) { navUrl = getPromoUrl(promoCode); } shared.setFlowControl(webList[0].id, navUrl, webList[0].type); setTimeout(manager.resultReader, 15000); // var win = window.open(navUrl, '', 'noopener,noreferrer'); GM_openInTab(navUrl, 'loadInBackground'); }; function getPromoUrl(promoCode) { ui.log('Creating Promo Code URL...'); let url = webList[0].url; url = url.slice(0, url.length - 4); return url + "promotion/" + promoCode; } function resultReader() { if(isObsolete()) { return; } if(shared.wasVisited(webList[0].id)) { timeWaiting = 0; let result = shared.getResult(); if (result) { updateWebListItem(result); if ( (webList[0].type == WebType.CRYPTOSFAUCETS) && ( (result.claimed) || (result.promoStatus && result.promoStatus != PromoStatus.ACCEPTED) )) { let promoCode = CFPromotions.hasPromoAvailable(webList[0].id); if (promoCode) { update(false); open(promoCode); return; } } } else { ui.log('Unable to read last run result, for ID: ' + webList[0].id + ' > ' + webList[0].name); } update(true); process(); return; } else if (shared.wasMaxRefreshReached(webList[0].id)) { timeWaiting = 0; webList[0].nextRoll = new Date(helpers.addMinutes(new Date(), "20")); ui.log('Skipping ' + webList[0].name + ': too many unsuccessful attempts to roll'); update(true); process(); return; } else { timeWaiting += 15; if (config.timeout != -1 && (timeWaiting > (config.timeout * 60)) ) { ui.log('Waited too much time for ' + webList[0].name + ' results: triggering timeout'); if (config.moveAfterTimeout) { webList[0].nextRoll = new Date(helpers.addMinutes(new Date(), "20")); update(true); } window.location.reload(); return; } ui.log('Waiting for ' + webList[0].name + ' results...', timeWaiting); setTimeout(manager.resultReader, 15000); } }; function updateWebListItem(result) { ui.log('Updating data: ' + JSON.stringify(result)); if (result.claimed) { result.claimed = parseFloat(result.claimed); if(!isNaN(result.claimed)) { webList[0].lastClaim = result.claimed; webList[0].aggregate += result.claimed; } } if(result.balance) { webList[0].balance = result.balance; } if(result.nextRoll) { webList[0].nextRoll = new Date(result.nextRoll); } if(result.promoStatus) { CFPromotions.updateFaucetForCode(result.promoCode, webList[0].id, result.promoStatus); } if(result.rolledNumber) { CFHistory.addRoll(result.rolledNumber); } }; function readNewPromoCode() { let promoCodeElement = $('#promo-code-new')[0]; let promoCode = helpers.cleanString(promoCodeElement.innerText); let promoDisplayStatus = $('#promo-display-status')[0]; if (promoCode == 'REMOVEALLPROMOS' ) { CFPromotions.removeAll(); promoCodeElement.innerText = ''; promoDisplayStatus.innerHTML = 'Promo codes removed!'; ui.refresh(null, CFPromotions.getAll()); } else if(promoCode != '') { CFPromotions.addNew(promoCode); promoCodeElement.innerText = ''; $('#promo-text-input').val(''); promoDisplayStatus.innerHTML = 'Code ' + promoCode + ' added!'; ui.log('Promo code ' + promoCode + ' added'); ui.refresh(null, CFPromotions.getAll()); } }; function updateRollStatsSpan() { let rollsSpanElement = $('#rolls-span')[0]; rollsSpanElement.innerText = CFHistory.getRollsMeta().join(','); }; function getCFlist() { let items; items = webList.filter(f => f.type === WebType.CRYPTOSFAUCETS); items = items.map(x => { return { id: x.id, name: x.name };}); items.sort((a, b) => (a.name > b.name) ? 1 : -1); return items; }; return{ init:start, process: process, resultReader: resultReader, getFaucetsForPromotion: getCFlist, readNewPromoCode: readNewPromoCode }; }, createUi: function() { let logLines = ['', '', '', '', '']; function init(cfFaucets) { appendCSS(); appendJavaScript(); appendHtml(); createPromoTable(cfFaucets); }; function appendCSS() { let css = ''; $('head').append(css); }; function appendJavaScript() { let js = ''; js += ''; $('head').append(js); }; function appendHtml() { let html =''; html += '
Loading...
'; html += ''; html += '

Schedule

'; html += '
'; html += '
'; html += '
'; html +='
'; html +='

Promo Codes

'; html +='
'; html +='
'; html +='
'; html +='
Remove All'; html +='
'; html +='
'; $('#referral-table').before(html); $('#schedule-container').append( createScheduleTable() ); }; function createPromoTable(faucets) { let tableStructure = ''; tableStructure += ''; tableStructure += ''; tableStructure += ''; for (let i = 0, all = faucets.length; i < all; i++) { tableStructure += ''; } tableStructure += '
⏳ Pending ✔️ Accepted 🕙 Used Before ❌ Invalid code ❗ Unknown error ⚪ No code
CodeAdded' + faucets[i].name + '
'; $('#promo-container').append( tableStructure ); }; function createScheduleTable() { let tableStructure = ''; tableStructure += ''; tableStructure += ''; tableStructure += ''; tableStructure += '
#NameLast ClaimAggregateBalanceNext Roll
'; return tableStructure; }; function loadScheduleTable(data) { let tableBody = ''; for(let i=0, all = data.length; i < all; i++) { tableBody += ''; tableBody +='' + (i + 1).toString() + ''; tableBody +='' + data[i].name + ''; tableBody +='' + data[i].lastClaim.toFixed(8) + ''; tableBody +='' + data[i].aggregate.toFixed(8) + ''; tableBody +='' + (data[i].balance ? data[i].balance.split(' ')[0] : "") + ''; tableBody +='' + helpers.getPrintableTime(data[i].nextRoll) + ''; tableBody +=''; } $('#schedule-table-body').html(tableBody); }; function loadPromotionTable(codes) { let tableBody = ''; for(let c=0; c < codes.length; c++) { let data = codes[c]; tableBody += ''; tableBody += '' + data.code + ''; tableBody +='' + helpers.getPrintableDateTime(data.added) + ''; for(let i=0, all = data.statusPerFaucet.length; i < all; i++) { tableBody +='' + helpers.getEmojiForPromoStatus(data.statusPerFaucet[i].status ?? 0) + ''; } tableBody +=''; } $('#promo-table-body').html(tableBody); }; function refresh(scheduleData, promotionData) { if (scheduleData) { loadScheduleTable(scheduleData); } if (promotionData) { loadPromotionTable(promotionData); } }; function log(msg, elapsed = false) { if(msg) { let previous = logLines[0].split(' ')[1]; if(elapsed && (previous == msg)) { logLines[0] = helpers.getPrintableTime() + ' ' + msg + ' [Elapsed time: ' + elapsed + ' seconds]'; } else { logLines.pop(); logLines.unshift(helpers.getPrintableTime() + ' ' + msg); } $('#console-log').html(logLines.join('
')); } }; return { init: init, refresh: refresh, loadPromotionTable: loadPromotionTable, log: log } }, createCFPromotions: function() { let codes = []; function PromotionCode(id, code) { this.id = id; this.code = code; this.added = new Date(); this.statusPerFaucet = []; }; function updateFaucetStatusInPromo(promo, faucetId, newStatus) { }; function getFaucetStatusInPromo(promo, faucetId) { let faucet = promo.statusPerFaucet.find(x => x.id == faucetId); return faucet.status ?? PromoStatus.NOCODE; }; function addNew(code) { let newPromo = new PromotionCode(codes.length, code); newPromo.statusPerFaucet = manager.getFaucetsForPromotion(); newPromo.statusPerFaucet.forEach(function (element, idx, arr) { arr[idx].status = PromoStatus.PENDING; }); codes.push(newPromo); codes.sort((a, b) => (a.id < b.id) ? 1 : -1); save(); }; function getAll() { return codes; }; function updateFaucetForCode(code, faucetId, newStatus) { let promo = codes.find(x => x.code == code); let faucet = promo.statusPerFaucet.find(x => x.id == faucetId); if(faucet) { faucet.status = newStatus; } save(); }; function hasPromoAvailable(faucetId) { let resp = false; codes.forEach(function (promotion, idx, arr) { let status = getFaucetStatusInPromo(promotion, faucetId); if (status == PromoStatus.PENDING) { resp = promotion.code; return; } }); return resp; }; function save() { persistence.save('CFPromotions', getAll(), true); }; function load(data) { codes = data; }; function removeAll() { codes = []; save(); }; return { addNew: addNew, removeAll: removeAll, getAll: getAll, load: load, updateFaucetForCode: updateFaucetForCode, hasPromoAvailable: hasPromoAvailable } }, createInteractions: function(){ let randomInteractionLevel = RandomInteractionLevel.LOW; let maxActions = 0; let performedActions = -1; let selectableElements; let actions = { available: [ function() { $('html, body').animate({ scrollTop: helpers.randomInt(0, $('html, body').get(0).scrollHeight) }, { complete: setTimeout(interactions.addPerformed, helpers.randomMs(100, 3000)), duration: helpers.randomMs(100, 1500) }); }, function() { let element = interactions.selectableElements[helpers.randomInt(0, interactions.selectableElements.length - 1)]; try { if (document.body.createTextRange) { const range = document.body.createTextRange(); range.moveToElementText(element); range.select(); } else if (window.getSelection) { const selection = window.getSelection(); const range = document.createRange(); range.selectNodeContents(element); selection.removeAllRanges(); selection.addRange(range); } } catch (err) { } interactions.addPerformed(); } ] }; function start(selectableElements) { performedActions = 0; switch(randomInteractionLevel) { case RandomInteractionLevel.NONE: maxActions = 0; break; case RandomInteractionLevel.LOW: maxActions = helpers.randomInt(2, 4); break; case RandomInteractionLevel.MEDIUM: maxActions = helpers.randomInt(5, 8); break; case RandomInteractionLevel.HIGH: maxActions = helpers.randomInt(12, 16); break; } interactions.selectableElements = selectableElements; performActions(); } function performActions() { if(performedActions >= maxActions) { return; } let delay = 0; for(let i = 0; i < maxActions; i++) { delay += helpers.randomMs(350, 1500); setTimeout(actions.available[helpers.randomInt(0, actions.available.length - 1)], delay); } } function addPerformed() { performedActions++; } function completed() { return (performedActions >= maxActions); } return { start: start, completed: completed, addPerformed: addPerformed, selectableElements: selectableElements }; }, createSGProcessor: function() { let timerSpans; function run() { if(isLoading()) { setTimeout(SGProcessor.run, helpers.randomMs(5000, 10000)); return; } else { if(isMinerActive()) { processRunDetails(); } else { activateMiner(); } } }; function isLoading() { return $('#loader-logo').length; }; function isMinerActive() { timerSpans = $('.mb-8 .wrapper .mb-1 span'); if(timerSpans.length > 0) { return true; } else { return false; } return (timerSpans.length === 0); }; function activateMiner() { const activateButton = document.querySelector('.mb-8 .wrapper button'); if (activateButton) { activateButton.click(); setTimeout(SGProcessor.processRunDetails, helpers.randomMs(10000, 20000)); } else { if(!is404Error()) { SGProcessor.processRunDetails() } } }; function is404Error() { const h1 = document.getElementsByTagName('h1'); if (h1.length > 0 && h1[0].innerText.includes('404')) { window.location.reload(); return true; } return false; } function processRunDetails() { let result = {}; result.nextRoll = helpers.addMinutes(new Date(), readCountdown().toString()); result.balance = readBalance(); shared.closeWindow(result); }; function readCountdown() { let mins = 241; try { let timeLeft = timerSpans.last().text().split(':'); if(timeLeft.length === 3) { mins = parseInt(timeLeft[0]) * 60 + parseInt(timeLeft[1]); } } catch (err) { } return mins; }; function readBalance() { let balance = ""; try { balance = $('span.text-accent').first().text() + " BTC"; } catch (err) { } return balance; }; return { run: run, processRunDetails: processRunDetails }; }, createCFProcessor: function() { const NavigationProcess = { ROLLING: 1, PROCESSING_PROMOTION: 2 }; let navigationProcess; let countdown; let rollButton; let promotionTag; let timeWaiting= 0; function run() { navigationProcess = NavigationProcess.ROLLING; displayStatusUi(); setTimeout(CFProcessor.findCountdownOrRollButton, helpers.randomMs(2000, 5000)); }; function runPromotion() { navigationProcess = NavigationProcess.PROCESSING_PROMOTION displayStatusUi(); setTimeout(CFProcessor.findPromotionTag, helpers.randomMs(1000, 3000)); }; function findCountdownOrRollButton() { if( isCountdownVisible() && !isRollButtonVisible() ) { timeWaiting = 0; processRunDetails(); } else if ( !isCountdownVisible() && isRollButtonVisible() ) { timeWaiting = 0; interact(); } else { if (timeWaiting/1000 > helpers.randomInt(5, 65)) { if (shared.canDoRefresh()) { window.location.reload(); } else { shared.closeWithMaxRefreshReached(); } return; } timeWaiting += 3000; setTimeout(CFProcessor.findCountdownOrRollButton, helpers.randomMs(2000, 5000)); } }; function interact() { let selectables = [] selectables = selectables.concat($('td').toArray()); selectables = selectables.concat($('p').toArray()); selectables = selectables.concat($('th').toArray()); interactions.start(selectables); setTimeout(CFProcessor.waitInteractions, helpers.randomMs(2000, 4000)); } function waitInteractions() { if(interactions.completed()) { roll(); } else { setTimeout(CFProcessor.waitInteractions, helpers.randomMs(2000, 4000)); } } function isCountdownVisible() { countdown = $('.timeout-wrapper'); return ($(countdown).length > 0 && $(countdown[0]).is(':visible')); }; function isRollButtonVisible() { rollButton = $('.main-button-2.roll-button.bg-2'); return ($(rollButton).length > 0 && $(rollButton[0]).is(':visible')); }; function roll() { $(rollButton[0]).click(); setTimeout(CFProcessor.findCountdownOrRollButton, helpers.randomMs(2000, 3000)); } function isPromotionTagVisible() { let pTags = $('p'); if (pTags.length > 0) { promotionTag = $('p')[0]; return true; } return false; }; function findPromotionTag() { if( isPromotionTagVisible() ) { processRunDetails(); } else { setTimeout(CFProcessor.findPromotionTag, helpers.randomMs(2000, 5000)); } }; function processRunDetails() { let result = {}; if(navigationProcess == NavigationProcess.ROLLING) { result.nextRoll = readCountdown(); result.claimed = readClaimed(); result.balance = readBalance(); if(result.claimed != 0) { result.rolledNumber = readRolledNumber(); } result.balance = readBalance(); } else if (navigationProcess == NavigationProcess.PROCESSING_PROMOTION) { result.promoStatus = readPromoStatus(); result.promoCode = readPromoCode(); if (result.promoStatus == PromoStatus.ACCEPTED) { result.nextRoll = helpers.addMinutes(new Date(), "-120"); } } shared.closeWindow(result); }; function readCountdown() { let minsElement = $('.timeout-container .minutes .digits'); let mins = "0"; if ($(minsElement).length > 0) { mins = $(minsElement)[0].innerHTML; } if (mins) { return helpers.addMinutes(new Date(), mins.toString()); } else { return null; } }; function readClaimed() { let claimed = 0; try { claimed = $('.result')[0].innerHTML; claimed = claimed.trim(); claimed = claimed.slice(claimed.lastIndexOf(" ") + 1); } catch(err) { } return claimed; }; function readRolledNumber() { let number = 0; try { number = $('.lucky-number').toArray().map(x => x.innerText).join(''); number = parseInt(number); } catch(err) { } return number; }; function readBalance() { let balance = ""; try { balance = $('.navbar-coins.bg-1 a').first().text(); } catch(err) { } return balance; }; function readPromoStatus() { let promoStatus = PromoStatus.UNKNOWNERROR; try { if(promotionTag.innerHTML.indexOf(localeConfig.stringSearches.promoCodeAccepted) > 0) { return PromoStatus.ACCEPTED; } else if(promotionTag.innerHTML.indexOf(localeConfig.stringSearches.promoCodeUsed) > 0) { return PromoStatus.USEDBEFORE; } else if(promotionTag.innerHTML.indexOf(localeConfig.stringSearches.promoCodeInvalid) > 0) { return PromoStatus.INVALID; } else if(promotionTag.innerHTML.indexOf(localeConfig.stringSearches.promoCodeInvalid) > 0) { return PromoStatus.INVALID; } } catch ( err ) { } return promoStatus; }; function readPromoCode() { var urlSplit = window.location.href.split('/'); return urlSplit[urlSplit.length - 1]; }; function displayStatusUi() { $( 'body' ).prepend( '
⚙️ Processing
' ); }; return { run: run, runPromotion: runPromotion, findCountdownOrRollButton: findCountdownOrRollButton, findPromotionTag: findPromotionTag, waitInteractions: waitInteractions }; }, createCFHistory: function() { let rollsMeta = [ { id: 0, range: '0000-9885', count: 0 }, { id: 1, range: '9886-9985', count: 0 }, { id: 2, range: '9986-9993', count: 0 }, { id: 3, range: '9994-9997', count: 0 }, { id: 4, range: '9998-9999', count: 0 }, { id: 5, range: '10000', count: 0 } ]; function initOrLoad() { let storedData = persistence.load('CFHistory', true); if(storedData) { rollsMeta = storedData; } }; function addRoll(number) { switch(true) { case (number <= 9885): rollsMeta[0].count++; break; case (number <= 9985): rollsMeta[1].count++; break; case (number <= 9993): rollsMeta[2].count++; break; case (number <= 9997): rollsMeta[3].count++; break; case (number <= 9999): rollsMeta[4].count++; break; case (number == 10000): rollsMeta[5].count++; break; default: break; } save(); }; function getRollsMeta() { return rollsMeta.map(x => x.count); }; function save() { persistence.save('CFHistory', rollsMeta, true); }; return { initOrLoad: initOrLoad, addRoll: addRoll, getRollsMeta: getRollsMeta } } }; /** * Prevents alert popups to be able to reload the faucet if invisible captcha validation fails */ function overrideSelectNativeJS_Functions () { window.alert = function alert (message) { console.log (message); } } function addJS_Node (text, s_URL, funcToRun) { var scriptNode= document.createElement ('script'); scriptNode.type= "text/javascript"; if (text)scriptNode.textContent= text; if (s_URL)scriptNode.src= s_URL; if (funcToRun)scriptNode.textContent = '(' + funcToRun.toString() + ')()'; var element = document.getElementsByTagName ('head')[0] || document.body || document.documentElement; element.appendChild (scriptNode); } function detectWeb() { if(!shared.isOpenedByManager(window.location.href)) { return; } addJS_Node (null, null, overrideSelectNativeJS_Functions); if(window.location.href.indexOf('promotion') > 0) { CFProcessor = objectGenerator.createCFProcessor(); interactions = objectGenerator.createInteractions(); setTimeout(CFProcessor.runPromotion, helpers.randomMs(5000, 10000)); } else if (shared.getCurrent().type == WebType.CRYPTOSFAUCETS) { CFProcessor = objectGenerator.createCFProcessor(); interactions = objectGenerator.createInteractions(); setTimeout(CFProcessor.run, helpers.randomMs(1000, 3000)); } else if (shared.getCurrent().type == WebType.STORMGAIN) { SGProcessor = objectGenerator.createSGProcessor(); setTimeout(SGProcessor.run, helpers.randomMs(10000, 20000)); } } function init() { shared = objectGenerator.createShared(); persistence = objectGenerator.createPersistence(); if(window.location.host === 'satology.onrender.com') { manager = objectGenerator.createManager(); CFPromotions = objectGenerator.createCFPromotions(); ui = objectGenerator.createUi(); CFHistory = objectGenerator.createCFHistory(); manager.init(); } else { detectWeb(); } } init(); })();