Stormgain with 2Captcha (Miner)

Solves Stormgain Miner Captcha (GeeTest) using 2Captcha service

  1. // ==UserScript==
  2. // @name Stormgain with 2Captcha (Miner)
  3. // @description Solves Stormgain Miner Captcha (GeeTest) using 2Captcha service
  4. // @version 0.3
  5. // @author satology
  6. // @namespace sg2c.satology.onrender.com
  7. // @connect 2captcha.com
  8. // @connect miner.stormgain.com
  9. // @grant GM_xmlhttpRequest
  10. // @match https://app.stormgain.com/crypto-miner/
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=stormgain.com
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16. /* Settings */
  17. const API_KEY = 'YOUR_API_KEY';
  18. const DISPLAY_UI = true; // Let's you stop the auto process (to solve it manually) and shows some log msgs afterwards
  19. const COUNTDOWN_SECONDS = 9; // Time to wait before auto solving
  20. const LOG_TO_CONSOLE = true; // Shows log in console
  21.  
  22. let preventStartCountdown = COUNTDOWN_SECONDS;
  23. let btn_start;
  24. let statusContainer;
  25. let statusElm;
  26. let itv_start;
  27. let itv_countdown;
  28.  
  29. // Old/V3
  30. // let snd_gt = '';
  31. // let snd_challenge = '';
  32. // V4:
  33. let snd_captcha_id = '';
  34.  
  35. let api_req_id = '';
  36.  
  37. // Old/V3 Vars:
  38. // let rsp_challenge = '';
  39. // let rsp_validate = '';
  40. // let rsp_seccode = '';
  41. // V4 vars:
  42. let rsp_captcha_id = '';
  43. let rsp_lot_number = '';
  44. let rsp_pass_token = '';
  45. let rsp_gen_time = '';
  46. let rsp_captcha_output = '';
  47.  
  48. let sg_token = '';
  49. let sg_clientId = '';
  50.  
  51. let api_in = function() {
  52. // Old/V3 version:
  53. // return `https://2captcha.com/in.php?key=${API_KEY}&json=1&method=geetest&gt=${snd_gt}&challenge=${snd_challenge}&pageurl=https://app.stormgain.com/crypto-miner/`;
  54. // V4:
  55. 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/`;
  56. }
  57. let api_out = function() {
  58. return `https://2captcha.com/res.php?key=${API_KEY}&json=1&action=get&id=${api_req_id}`;
  59. };
  60. let apiResponse = '';
  61.  
  62. function logger(title = '', msg = '') {
  63. if(!LOG_TO_CONSOLE) {
  64. return;
  65. }
  66.  
  67. console.log("%c" + new Date().toISOString().slice(0, 19).replace("T", " ") + ' > ' + title, "background: yellow; font-size: large");
  68. console.log(msg);
  69. }
  70.  
  71. function toUI(msg) {
  72. if (DISPLAY_UI) { document.getElementById("sg2c-msg").innerHTML = msg; }
  73. }
  74.  
  75. async function start() {
  76. try {
  77. sg_token = JSON.parse(localStorage.AppStorage).JWTAccessToken;
  78. logger('', 'JWTAccessToken retireved');
  79. } catch(err) {
  80. logger('Unable to retrieve JWTAccessToken', err);
  81. toUI('Error!');
  82. return;
  83. }
  84.  
  85. try {
  86. sg_clientId = [...document.scripts].filter(x => x.textContent.includes('app-config'))[0].innerText.replace("'", "").split('"personCode":')[1].split(",")[0]
  87. // sg_clientId = Object.keys(JSON.parse(localStorage.AppStorage).clientPrefs)[0];
  88. toUI('CLientID retireved');
  89. } catch(err) {
  90. logger('Unable to retrieve ClientID', err);
  91. toUI('Error!');
  92. return;
  93. }
  94.  
  95. let rr = await fetch("https://miner.stormgain.com/api/v1/preactivate", {
  96. "headers": {
  97. "accept": "application/json, text/plain, */*",
  98. "authorization": "Token " + sg_token,
  99. "client-id": sg_clientId
  100. },
  101. "referrer": "https://app.stormgain.com/",
  102. "referrerPolicy": "strict-origin-when-cross-origin",
  103. "body": null,
  104. "method": "GET",
  105. "mode": "cors",
  106. "credentials": "omit"
  107. });
  108.  
  109. let content = await rr.json();
  110.  
  111. // if (content && content.data && content.data.success && content.data.gt && content.data.challenge) {
  112. if (content && content.data && content.captcha_provider == 'geetest_v4' && content.data.gt) {
  113. // snd_gt = content.data.gt;
  114. snd_captcha_id = content.data.gt;
  115. logger('', 'Challenge data retrieved');
  116. } else {
  117. logger('Error retrieving challenge data', content);
  118. toUI('Error!');
  119. return;
  120. }
  121.  
  122. GM_xmlhttpRequest({
  123. method: "GET",
  124. url: api_in(),
  125. onload: function(response) {
  126. apiResponse = JSON.parse(this.responseText);
  127. if (apiResponse.status == 0) {
  128. logger('Error in SG with captcha', apiResponse.error_text);
  129. toUI('Error!');
  130. } else {
  131. api_req_id = apiResponse.request;
  132. logger('Captcha submitted', 'Request ID: ' + api_req_id);
  133. toUI('Captcha submitted');
  134. setTimeout( () => { getSolved(); }, 15000 );
  135. }
  136. },
  137. onerror: function(e) {
  138. toUI('Error!');
  139. logger('Error submitting captcha', e);
  140. }
  141. });
  142. }
  143.  
  144. function getSolved() {
  145. toUI('Waiting for solution...');
  146. logger('', 'Retrieving solution');
  147. GM_xmlhttpRequest({
  148. method: "GET",
  149. url: api_out(),
  150. onload: function(response) {
  151. apiResponse = JSON.parse(this.responseText);
  152. logger('2C Response when retrieving', apiResponse);
  153.  
  154. if (apiResponse.status == 0) {
  155. logger('2C Message', apiResponse.request);
  156. if (apiResponse.request == 'CAPCHA_NOT_READY') {
  157. toUI('Captcha not ready yet...');
  158. setTimeout( () => { getSolved(); }, 15000 );
  159. } else if (apiResponse.request == 'ERROR_CAPTCHA_UNSOLVABLE') {
  160. toUI('Refreshing for retry...');
  161. setTimeout( () => { window.location.reload(); }, 2000);
  162. return;
  163. } else {
  164. if (apiResponse.error_text) {
  165. toUI('Error: ' + apiResponse.error_text);
  166. }
  167. if (apiResponse.request) {
  168. toUI('Error: ' + apiResponse.request);
  169. }
  170. }
  171. } else {
  172. // Old/V3 vars:
  173. // rsp_challenge = apiResponse.request.geetest_challenge;
  174. // rsp_validate = apiResponse.request.geetest_validate;
  175. // rsp_seccode = apiResponse.request.geetest_seccode;
  176.  
  177. // V4 Vars:
  178. rsp_captcha_id = apiResponse.request.captcha_id;
  179. rsp_lot_number = apiResponse.request.lot_number;
  180. rsp_pass_token = apiResponse.request.pass_token;
  181. rsp_gen_time = apiResponse.request.gen_time;
  182. rsp_captcha_output = apiResponse.request.captcha_output;
  183.  
  184. toUI('Results ready. Processing...');
  185. //TODO: send to SG
  186. // if (rsp_challenge && rsp_validate && rsp_seccode) { // <= old condition
  187. if (rsp_captcha_id && rsp_lot_number && rsp_pass_token && rsp_gen_time && rsp_captcha_output) {
  188. logger('2C solved the captcha', 'Sending to SG');
  189. sendToSg();
  190. } else {
  191. logger('Something is missing in the response. Not sending it to SG');
  192. }
  193. }
  194. },
  195. onerror: function(e) {
  196. logger('Unexpected error getting solution', e);
  197. toUI('Error!');
  198. //TODO: retry in X seconds
  199. }
  200. });
  201. }
  202.  
  203. async function sendToSg() {
  204. logger('Sending to SG');
  205. let httpData = {
  206. method: "POST",
  207. url: "https://miner.stormgain.com/api/v1/activate",
  208. headers: {
  209. "accept": "application/json, text/plain, */*",
  210. "authorization": "Token " + sg_token,
  211. "client-id": sg_clientId,
  212. "content-type": "multipart/form-data; boundary=----WebKitFormBoundaryKFzlAnieQFGQSLEZ",
  213. "cookie": document.cookie,
  214. "referrer": "https://app.stormgain.com/",
  215. "referrerPolicy": "strict-origin-when-cross-origin",
  216. "mode": "cors",
  217. "credentials": "omit",
  218. },
  219. // Old/V3:
  220. // "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",
  221. // Old/V4:
  222. // "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",
  223. "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",
  224. onload: function(response) {
  225. logger('SG Response', response);
  226. apiResponse = JSON.parse(this.responseText);
  227. if (apiResponse.active) {
  228. toUI('Success. Refreshing...');
  229. logger('SG accepted the solution', 'Refreshing...');
  230. setTimeout( () => { window.location.reload(); }, 2000);
  231. } else {
  232. toUI('Error');
  233. logger('Something went wrong. Check the SG Response', apiResponse);
  234. }
  235. },
  236. onerror: function(e) {
  237. logger('Error sending solution to SG', e);
  238. toUI('Error!');
  239. }
  240. };
  241. // console.log('headers', httpData.headers);
  242. // console.log('data', httpData.data);
  243. GM_xmlhttpRequest(httpData);
  244.  
  245. return;
  246. }
  247.  
  248. itv_start = setInterval( () => {
  249. btn_start = document.querySelector('.wrapper .activate');
  250.  
  251. if (btn_start) {
  252. clearInterval(itv_start);
  253. if (!DISPLAY_UI) {
  254. start();
  255. return;
  256. }
  257.  
  258. //load countdown/ui
  259. statusContainer = btn_start.parentNode.parentNode;
  260. 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>
  261. <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
  262. 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
  263. statusElm = document.getElementById('sg2c-countdown');
  264.  
  265. document.getElementById("sg2c-btn").addEventListener("click", function() {
  266. clearInterval(itv_countdown);
  267. let elements = document.getElementsByClassName('sg2c');
  268. for (var element of elements) {
  269. element.remove();
  270. }
  271. this.innerText = 'Navigate using the menu and come back to the Miner if the button doesn\'t work';
  272. //this.remove();
  273. return;
  274. });
  275. itv_countdown = setInterval(() => {
  276. preventStartCountdown = preventStartCountdown-1;
  277. if (preventStartCountdown < 1) {
  278. clearInterval(itv_countdown);
  279. document.getElementById("sg2c-btn").remove();
  280. document.getElementById("sg2c-msg").innerHTML = 'Started...';
  281. start();
  282. return;
  283. }
  284. if (statusElm) {
  285. statusElm.innerText = preventStartCountdown;
  286. }
  287. }, 1000);
  288. }
  289. }, 1000);
  290. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址