// ==UserScript==
// @name Stormgain with 2Captcha
// @description Solves Stormgain Miner Captcha (GeeTest) using 2Captcha service
// @version 0.1
// @author satology
// @namespace sg2c.satology.onrender.com
// @connect 2captcha.com
// @connect miner.stormgain.com
// @grant GM_xmlhttpRequest
// @match https://app.stormgain.com/crypto-miner/
// @icon https://www.google.com/s2/favicons?sz=64&domain=stormgain.com
// ==/UserScript==
(function() {
'use strict';
/* Settings */
const API_KEY = 'YOUR_2CAPTCHA_API_KEY';
const DISPLAY_UI = true; // Let's you stop the auto process (to solve it manually) and shows some log msgs afterwards
const COUNTDOWN_SECONDS = 9; // Time to wait before auto solving
const LOG_TO_CONSOLE = true; // Shows log in console
let preventStartCountdown = COUNTDOWN_SECONDS;
let btn_start;
let statusContainer;
let statusElm;
let itv_start;
let itv_countdown;
let snd_gt = '';
let snd_challenge = '';
let api_req_id = '';
let rsp_challenge = '';
let rsp_validate = '';
let rsp_seccode = '';
let sg_token = '';
let sg_clientId = '';
let api_in = function() {
return `https://2captcha.com/in.php?key=${API_KEY}&json=1&method=geetest>=${snd_gt}&challenge=${snd_challenge}&pageurl=https://app.stormgain.com/crypto-miner/`;
}
let api_out = function() {
return `https://2captcha.com/res.php?key=${API_KEY}&json=1&action=get&id=${api_req_id}`;
};
let apiResponse = '';
function logger(title = '', msg = '') {
if(!LOG_TO_CONSOLE) {
return;
}
console.log("%c" + new Date().toISOString().slice(0, 19).replace("T", " ") + ' > ' + title, "background: yellow; font-size: large");
console.log(msg);
}
function toUI(msg) {
if (DISPLAY_UI) { document.getElementById("sg2c-msg").innerHTML = msg; }
}
async function start() {
try {
sg_token = JSON.parse(localStorage.AppStorage).JWTAccessToken;
logger('', 'JWTAccessToken retireved');
} catch(err) {
logger('Unable to retrieve JWTAccessToken', err);
toUI('Error!');
return;
}
try {
sg_clientId = Object.keys(JSON.parse(localStorage.AppStorage).clientPrefs)[0];
toUI('CLientID retireved');
} catch(err) {
logger('Unable to retrieve ClientID', err);
toUI('Error!');
return;
}
let rr = await fetch("https://miner.stormgain.com/api/v1/preactivate", {
"headers": {
"accept": "application/json, text/plain, */*",
"authorization": "Token " + sg_token,
"client-id": sg_clientId
},
"referrer": "https://app.stormgain.com/",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "omit"
});
let content = await rr.json();
if (content && content.data && content.data.success && content.data.gt && content.data.challenge) {
snd_gt = content.data.gt;
snd_challenge = content.data.challenge;
logger('', 'Challenge data retrieved');
} else {
logger('Error retrieving challenge data', content);
toUI('Error!');
return;
}
GM_xmlhttpRequest({
method: "GET",
url: api_in(),
onload: function(response) {
apiResponse = JSON.parse(this.responseText);
if (apiResponse.status == 0) {
logger('Error submitting captcha', apiResponse.error_text);
toUI('Error!');
} else {
api_req_id = apiResponse.request;
logger('Captcha submitted', 'Request ID: ' + api_req_id);
toUI('Captcha submitted');
setTimeout( () => { getSolved(); }, 15000 );
}
},
onerror: function(e) {
toUI('Error: ' + e);
console.log(e);
}
});
}
function getSolved() {
toUI('Waiting for solution...');
logger('', 'Retrieving solution');
GM_xmlhttpRequest({
method: "GET",
url: api_out(),
onload: function(response) {
apiResponse = JSON.parse(this.responseText);
logger('2C Response when retrieving', apiResponse);
if (apiResponse.status == 0) {
logger('2C Message', apiResponse.request);
if (apiResponse.request == 'CAPCHA_NOT_READY') {
toUI('Captcha not ready yet...');
setTimeout( () => { getSolved(); }, 15000 );
} else if (apiResponse.request == 'ERROR_CAPTCHA_UNSOLVABLE') {
toUI('Refreshing for retry...');
setTimeout( () => { window.location.reload(); }, 2000);
return;
} else {
if (apiResponse.error_text) {
toUI('Error: ' + apiResponse.error_text);
}
if (apiResponse.request) {
toUI('Error: ' + apiResponse.request);
}
}
} else {
rsp_challenge = apiResponse.request.geetest_challenge;
rsp_validate = apiResponse.request.geetest_validate;
rsp_seccode = apiResponse.request.geetest_seccode;
toUI('Results ready. Processing...');
//TODO: send to SG
if (rsp_challenge && rsp_validate && rsp_seccode) {
logger('2C solved the captcha', 'Sending to SG');
sendToSg();
}
}
},
onerror: function(e) {
logger('Unexpected error getting solution', e);
toUI('Error!');
//TODO: retry in X seconds
}
});
}
async function sendToSg() {
logger('Sending to SG');
GM_xmlhttpRequest({
method: "POST",
url: "https://miner.stormgain.com/api/v1/activate",
headers: {
"accept": "application/json, text/plain, */*",
"authorization": "Token " + sg_token,
"client-id": sg_clientId,
"content-type": "multipart/form-data; boundary=----WebKitFormBoundaryGpbvA0qBtFeR1Kuw",
"cookie": document.cookie,
"referrer": "https://app.stormgain.com/",
"referrerPolicy": "strict-origin-when-cross-origin",
"mode": "cors",
"credentials": "omit",
},
// "data": "",
"data": "------WebKitFormBoundaryGpbvA0qBtFeR1Kuw\r\nContent-Disposition: form-data; name=\"geetest_challenge\"\r\n\r\n" + rsp_challenge + "\r\n------WebKitFormBoundaryGpbvA0qBtFeR1Kuw\r\nContent-Disposition: form-data; name=\"geetest_seccode\"\r\n\r\n" + rsp_seccode + "\r\n------WebKitFormBoundaryGpbvA0qBtFeR1Kuw\r\nContent-Disposition: form-data; name=\"geetest_validate\"\r\n\r\n" + rsp_validate + "\r\n------WebKitFormBoundaryGpbvA0qBtFeR1Kuw--\r\n",
onload: function(response) {
logger('SG Response', response);
apiResponse = JSON.parse(this.responseText);
if (apiResponse.active) {
toUI('Success. Refreshing...');
logger('SG accepted the solution', 'Refreshing...');
setTimeout( () => { window.location.reload(); }, 2000);
} else {
toUI('Error');
logger('Something went wrong. Check the SG Response', apiResponse);
}
},
onerror: function(e) {
logger('Error sending solution to SG', e);
toUI('Error!');
}
});
return;
}
itv_start = setInterval( () => {
btn_start = document.querySelector('.wrapper .activate');
if (btn_start) {
clearInterval(itv_start);
if (!DISPLAY_UI) {
start();
return;
}
//load countdown/ui
statusContainer = btn_start.parentNode.parentNode;
statusContainer.innerHTML = `<span id="sg2c-msg" class="text-36 leading-9 font-bold text-center sg2c" style="color: #FF9900">Solving in <span id="sg2c-countdown" class="sg2c">${preventStartCountdown}</span>...</span>
<button id="sg2c-btn" style="background-color: rgb(255, 153, 0)" class="relative inline-flex justify-center items-center flex-shrink-0 bg-accent text-dark-1 text-center select-none cursor-pointer border-none
self-center rounded px-2 py-2 hover-shadow-big my-5 sg2c"><!----><span class="px-4 text-15 leading-24 font-bold">Stop!, I'll do it manually!</span></button>` + statusContainer.innerHTML
statusElm = document.getElementById('sg2c-countdown');
document.getElementById("sg2c-btn").addEventListener("click", function() {
clearInterval(itv_countdown);
let elements = document.getElementsByClassName('sg2c');
for (var element of elements) {
element.remove();
}
this.remove();
return;
});
itv_countdown = setInterval(() => {
preventStartCountdown = preventStartCountdown-1;
if (preventStartCountdown < 1) {
clearInterval(itv_countdown);
document.getElementById("sg2c-btn").remove();
document.getElementById("sg2c-msg").innerHTML = 'Started...';
start();
return;
}
if (statusElm) {
statusElm.innerText = preventStartCountdown;
}
}, 1000);
}
}, 1000);
})();