feat(CaptchaHandler): Must not use due to safety concerns

This commit is contained in:
March 7th
2022-11-04 18:40:19 +07:00
parent 9616ef19a7
commit b3eedc34be
9 changed files with 112 additions and 12 deletions

View File

@@ -34,7 +34,7 @@ class APIRequest {
this.path = `${path}${queryString && `?${queryString}`}`;
}
make() {
make(captchaKey = undefined) {
agent ??=
typeof this.client.options.proxy === 'string' && this.client.options.proxy.length > 0
? new proxy(this.client.options.proxy)
@@ -101,6 +101,12 @@ class APIRequest {
headers = Object.assign(headers, body.getHeaders());
}
if (headers['Content-Type'] === 'application/json' && captchaKey && typeof captchaKey == 'string' && body) {
body = JSON.parse(body);
body.captcha_key = captchaKey;
body = JSON.stringify(body);
}
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.client.options.restRequestTimeout).unref();
return fetch(url, {

33
src/rest/CaptchaSolver.js Normal file
View File

@@ -0,0 +1,33 @@
'use strict';
module.exports = class CaptchaSolver {
constructor(service, key) {
this.service = '';
this.solver = undefined;
this._setup(service, key);
}
_setup(service, key) {
switch (service) {
case '2captcha': {
if (!key || typeof key !== 'string') throw new Error('2captcha key is not provided');
try {
const lib = require('2captcha');
this.service = '2captcha';
this.solver = new lib.Solver(key);
this.solve = siteKey =>
new Promise((resolve, reject) => {
this.solver
.hcaptcha(siteKey, 'discord.com')
.then(res => {
resolve(res.data);
})
.catch(reject);
});
break;
} catch (e) {
throw new Error('2captcha module not found, please install it with `npm i 2captcha`');
}
}
}
}
solve() {}
};

View File

@@ -38,7 +38,7 @@ class DiscordAPIError extends Error {
/**
* @typedef {Object} Captcha
* @property {Array<string>} captcha_key ['message']
* @property {string} captcha_sitekey Captcha code ???
* @property {string} captcha_sitekey Captcha sitekey (hcaptcha)
* @property {string} captcha_service hcaptcha
* @property {string} [captcha_rqdata]
* @property {string} [captcha_rqtoken]

View File

@@ -4,6 +4,7 @@ const { setInterval } = require('node:timers');
const { Collection } = require('@discordjs/collection');
const APIRequest = require('./APIRequest');
const routeBuilder = require('./APIRouter');
const CaptchaSolver = require('./CaptchaSolver');
const RequestHandler = require('./RequestHandler');
const { Error } = require('../errors');
const { Endpoints } = require('../util/Constants');
@@ -22,6 +23,12 @@ class RESTManager {
this.handlers.sweep(handler => handler._inactive);
}, client.options.restSweepInterval * 1_000).unref();
}
this.captchaService = null;
this.setup();
}
setup() {
this.captchaService = new CaptchaSolver(this.client.options.captchaService, this.client.options.captchaKey);
}
get api() {

View File

@@ -103,7 +103,7 @@ class RequestHandler {
}
}
async execute(request) {
async execute(request, captchaKey) {
/*
* After calculations have been done, pre-emptively stop further requests
* Potentially loop until this task can run if e.g. the global rate limit is hit twice
@@ -194,7 +194,7 @@ class RequestHandler {
// Perform the request
let res;
try {
res = await request.make();
res = await request.make(captchaKey);
} catch (error) {
// Retry the specified number of times for request abortions
if (request.retries === this.manager.client.options.retryLimit) {
@@ -343,6 +343,27 @@ class RequestHandler {
let data;
try {
data = await parseResponse(res);
if (data?.captcha_service && this.manager.client.options.captchaService) {
// Retry the request after a captcha is solved
this.manager.client.emit(
DEBUG,
`Hit a captcha while executing a request. Solving captcha ...
Method : ${request.method}
Path : ${request.path}
Route : ${request.route}
Sitekey : ${data.captcha_sitekey}`,
);
const captcha = await this.manager.captchaService.solve(data.captcha_sitekey);
this.manager.client.emit(
DEBUG,
`Captcha solved.
Method : ${request.method}
Path : ${request.path}
Route : ${request.route}
Key : ${captcha}`,
);
return this.execute(request, captcha);
}
} catch (err) {
throw new HTTPError(err.message, err.constructor.name, err.status, request);
}