- // ==UserScript==
- // @name Stormgain with 2Captcha (Miner)
- // @description Solves Stormgain Miner Captcha (GeeTest) using 2Captcha service
- // @version 0.3
- // @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_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;
-
- // Old/V3
- // let snd_gt = '';
- // let snd_challenge = '';
- // V4:
- let snd_captcha_id = '';
-
- let api_req_id = '';
-
- // Old/V3 Vars:
- // let rsp_challenge = '';
- // let rsp_validate = '';
- // let rsp_seccode = '';
- // V4 vars:
- let rsp_captcha_id = '';
- let rsp_lot_number = '';
- let rsp_pass_token = '';
- let rsp_gen_time = '';
- let rsp_captcha_output = '';
-
- let sg_token = '';
- let sg_clientId = '';
-
- let api_in = function() {
- // Old/V3 version:
- // 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/`;
- // V4:
- return `https://2captcha.com/in.php?key=${API_KEY}&json=1&method=geetest_v4&captcha_id=${snd_captcha_id}&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 = [...document.scripts].filter(x => x.textContent.includes('app-config'))[0].innerText.replace("'", "").split('"personCode":')[1].split(",")[0]
- // 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) {
- if (content && content.data && content.captcha_provider == 'geetest_v4' && content.data.gt) {
- // snd_gt = content.data.gt;
- snd_captcha_id = content.data.gt;
- 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 in SG with 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!');
- logger('Error submitting captcha', 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 {
- // Old/V3 vars:
- // rsp_challenge = apiResponse.request.geetest_challenge;
- // rsp_validate = apiResponse.request.geetest_validate;
- // rsp_seccode = apiResponse.request.geetest_seccode;
-
- // V4 Vars:
- rsp_captcha_id = apiResponse.request.captcha_id;
- rsp_lot_number = apiResponse.request.lot_number;
- rsp_pass_token = apiResponse.request.pass_token;
- rsp_gen_time = apiResponse.request.gen_time;
- rsp_captcha_output = apiResponse.request.captcha_output;
-
- toUI('Results ready. Processing...');
- //TODO: send to SG
- // if (rsp_challenge && rsp_validate && rsp_seccode) { // <= old condition
- if (rsp_captcha_id && rsp_lot_number && rsp_pass_token && rsp_gen_time && rsp_captcha_output) {
- logger('2C solved the captcha', 'Sending to SG');
- sendToSg();
- } else {
- logger('Something is missing in the response. Not sending it to SG');
- }
- }
- },
- onerror: function(e) {
- logger('Unexpected error getting solution', e);
- toUI('Error!');
- //TODO: retry in X seconds
- }
- });
- }
-
- async function sendToSg() {
- logger('Sending to SG');
- let httpData = {
- 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=----WebKitFormBoundaryKFzlAnieQFGQSLEZ",
- "cookie": document.cookie,
- "referrer": "https://app.stormgain.com/",
- "referrerPolicy": "strict-origin-when-cross-origin",
- "mode": "cors",
- "credentials": "omit",
- },
- // Old/V3:
- // "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",
- // Old/V4:
- // "body": "------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_lot_number\"\r\n\r\ \r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_captcha_output\"\r\n\r\n. \r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_pass_token\"\r\n\r\n \r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_gen_time\"\r\n\r\n. 1696613876\r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ--\r\n",
- "data": "------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_lot_number\"\r\n\r\n" + rsp_lot_number + "\r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_captcha_output\"\r\n\r\n" + rsp_captcha_output + "\r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_pass_token\"\r\n\r\n" + rsp_pass_token + "\r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ\r\nContent-Disposition: form-data; name=\"geetest_gen_time\"\r\n\r\n" + rsp_gen_time + "\r\n------WebKitFormBoundaryKFzlAnieQFGQSLEZ--\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!');
- }
- };
- // console.log('headers', httpData.headers);
- // console.log('data', httpData.data);
- GM_xmlhttpRequest(httpData);
-
- 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.innerText = 'Navigate using the menu and come back to the Miner if the button doesn\'t work';
- //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);
- })();