cfbypass
Version:
Python based CloudFlare bypass. Utilizes [VeNoMouS's CloudScraper](https://github.com/VeNoMouS/cloudscraper) project to bypass CloudFlare. All credit goes to him.
346 lines (289 loc) • 11.3 kB
text/typescript
import { spawn } from "child_process";
import { join } from "path";
import { decode } from "js-base64";
import { load } from "cheerio";
class CloudScraper {
private isPython3:boolean;
// If you are using Python 3, set this to true
constructor(isPython3?:boolean) {
this.isPython3 = isPython3 ?? false;
this.get = this.get.bind(this);
this.post = this.post.bind(this);
this.cookie = this.cookie.bind(this);
this.tokens = this.tokens.bind(this);
this.request = this.request.bind(this);
this.install = this.install.bind(this);
this.setPython3 = this.setPython3.bind(this);
this.solveCaptcha3 = this.solveCaptcha3.bind(this);
this.solveCaptcha3FromHTML = this.solveCaptcha3FromHTML.bind(this);
}
// @param url: string options: Options = {}
public async get(url: string, options: Options = {}): Promise<Response> {
options = {
...options,
method: "GET"
};
const request:Request = {
url,
options
};
const response = await this.request(request);
return response;
}
// @param url: string options: Options = {}
public async post(url: string, options: Options = {}): Promise<Response> {
options = {
...options,
method: "POST"
};
const request:Request = {
url,
options
};
const response = await this.request(request);
return response;
}
// @param url: string options: Options = {}
public async cookie(url: string, options: Options = {}): Promise<Response> {
options = {
...options,
method: "COOKIE"
};
const request:Request = {
url,
options
};
const response = await this.request(request);
return response;
}
// @param url: string options: Options = {}
public async tokens(url: string, options: Options = {}): Promise<Response> {
options = {
...options,
method: "TOKENS"
};
const request:Request = {
url,
options
};
const response = await this.request(request);
return response;
}
public async put(url: string, options: Options = {}): Promise<Response> {
throw new Error("PUT is not supported yet! Development is in progress.");
}
public async delete(url: string, options: Options = {}): Promise<Response> {
throw new Error("DELETE is not supported! Development is in progress.");
}
public async patch(url: string, options: Options = {}): Promise<Response> {
throw new Error("PUT is not supported! Development is in progress.");
}
public async head(url: string, options: Options = {}): Promise<Response> {
throw new Error("PUT is not supported! Development is in progress.");
}
// @param url: string options: Options = {}
public async request(request: Request): Promise<Response> {
return new Promise((resolve, reject) => {
const args: string[] = [join(__dirname, "../index.py")];
args.push("--url", request.url);
args.push("--redirect", request.options.redirect ? "true" : "false");
if (request.options.method) {
args.push("--method", String(request.options.method));
}
if (request.options.headers) {
args.push("--headers", JSON.stringify(request.options.headers));
}
if (request.options.body) {
args.push("--data", JSON.stringify(request.options.body));
}
const result: any[] = [];
const childProcess = spawn(this.isPython3 ? "python3" : "python", args);
childProcess.stdout.setEncoding("utf8");
childProcess.stdout.on("data", (data) => {
const dataString = String(data).split("\n");
if (dataString.length < 3) {
return result.push({ data });
}
const body = dataString[0];
let statusCode = dataString[1];
let headers = dataString[2];
try {
statusCode = JSON.parse(statusCode);
statusCode = (statusCode as any as { statusCode: string }).statusCode;
} catch (e) {
statusCode = statusCode;
}
try {
headers = JSON.parse(headers);
let temp = (headers as any as { responseHeaders: string }).responseHeaders;
headers = decode(temp.substring(2).substring(0, temp.length - 1));
try {
headers = JSON.parse(headers);
} catch (e) {
console.error(e);
headers = headers;
}
} catch (e) {
headers = headers;
}
result.push({
"data": body,
"status": Number(statusCode),
"headers": headers
})
})
childProcess.stderr.setEncoding('utf8');
childProcess.stderr.on("data", (err) => {
err = String(err).trim();
err = err.replaceAll("\n", " ");
result.push({
"error": String(err).trim()
})
})
childProcess.on('exit', () => {
let data = "";
let statusCode = 200;
let headers = "";
const errors: any[] = [];
for (let i = 0; i < result.length; i++) {
if (result[i].error) {
errors.push(result[i]);
}
if (result[i].data) {
data += result[i].data;
}
if (result[i].status) {
statusCode = result[i].status;
}
if (result[i].headers) {
headers = result[i].headers;
}
}
data = decode(data.substring(2).substring(0, data.length - 1));
if (errors.length > 0) {
reject({
status: 500,
statusText: "ERROR",
headers: headers,
error: errors,
text: () => data,
json: () => JSON.parse(data)
})
} else {
resolve({
status: statusCode,
statusText: "OK",
headers: headers,
error: errors,
text: () => data,
json: () => JSON.parse(data)
});
}
})
})
}
// @param token: string
public async solveCaptcha3(url: string, key: string, anchorLink: string): Promise<string> {
const uri = new URL(url);
const domain = uri.protocol + '//' + uri.host;
const keyReq = await this.get(`https://www.google.com/recaptcha/api.js?render=${key}`, {
headers: {
Referer: domain,
},
});
const data = keyReq.text();
const v = data.substring(data.indexOf('/releases/'), data.lastIndexOf('/recaptcha')).split('/releases/')[1];
// ANCHOR IS SPECIFIC TO SITE
const curK = anchorLink.split('k=')[1].split('&')[0];
const curV = anchorLink.split("v=")[1].split("&")[0];
const anchor = anchorLink.replace(curK, key).replace(curV, v);
const req = await this.get(anchor);
const $ = load(req.text());
const reCaptchaToken = $('input[id="recaptcha-token"]').attr('value')
if (!reCaptchaToken) throw new Error('reCaptcha token not found')
return reCaptchaToken;
}
public async solveCaptcha3FromHTML(url: string, html: string, anchorLink: string) {
const $ = load(html);
let captcha = null;
$("script").map((index, element) => {
if ($(element).attr("src") != undefined && $(element).attr("src").includes("/recaptcha/")) {
captcha = $(element).attr("src");
}
})
if (!captcha) {
throw new Error("Couldn't fetch captcha.");
}
let captchaURI = new URL(captcha);
const captchaId = captchaURI.searchParams.get("render");
const captchaKey = await this.solveCaptcha3(url, captchaId, anchorLink);
return captchaKey;
}
// @param isPython3: boolean
public setPython3(isPython3:boolean) {
this.isPython3 = isPython3;
}
// @param isPython3: boolean
public async install() {
return new Promise((resolve, reject) => {
const args:string[] = [join(__dirname, "/cfscraper/setup.py")];
args.push("install");
const requestArgs:string[] = [join(__dirname, "/req/setup.py")];
requestArgs.push("install");
const childProcess = spawn(this.isPython3 ? "python3" : "python", requestArgs);
childProcess.stdout.setEncoding("utf8");
childProcess.stdout.on("data", (data) => {
console.log(data);
})
childProcess.stderr.setEncoding('utf8');
childProcess.stderr.on("data", (err) => {
reject(err);
})
childProcess.on('exit', () => {
const childProcess = spawn(this.isPython3 ? "python3" : "python", args);
childProcess.stdout.setEncoding("utf8");
childProcess.stdout.on("data", (data) => {
console.log(data);
})
childProcess.stderr.setEncoding('utf8');
childProcess.stderr.on("data", (err) => {
reject(err);
})
childProcess.on('exit', () => {
resolve(true);
})
})
})
}
}
type Options = {
method?: Method["GET"] | Method["POST"] | Method["COOKIE"] | Method["TOKENS"];
headers?: { [key: string]: string };
body?: string;
redirect?: boolean;
};
type Method = {
"GET": string;
"POST": string;
"COOKIE": string;
"TOKENS": string;
// THE FOLLOWING ARE UNSUPPORTED TEMPORARILY
"PUT": string;
"DELETE": string;
"PATCH": string;
"HEAD": string;
};
interface Response {
status: number;
statusText: string;
headers: string | Record<string, string>;
error: string[];
text: ()=>string;
json: ()=>string;
};
interface Request {
url: string;
options: Options;
};
export default CloudScraper;
export type { Options, Method, Response, Request };