summaryrefslogtreecommitdiffstats
path: root/g4f/Provider/npm/node_modules/funcaptcha/src
diff options
context:
space:
mode:
Diffstat (limited to 'g4f/Provider/npm/node_modules/funcaptcha/src')
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/api.ts85
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/challenge.ts296
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/crypt.ts78
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/fingerprint.ts321
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/http.ts28
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/index.ts2
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/murmur.ts205
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/session.ts125
-rw-r--r--g4f/Provider/npm/node_modules/funcaptcha/src/util.ts197
9 files changed, 1337 insertions, 0 deletions
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/api.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/api.ts
new file mode 100644
index 00000000..6582750d
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/api.ts
@@ -0,0 +1,85 @@
+import request from "./http";
+import util from "./util";
+
+export interface GetTokenOptions {
+ pkey: string;
+ // Service URL
+ surl?: string;
+ data?: { [key: string]: string };
+ headers?: { [key: string]: string };
+ site?: string;
+ // Page URL
+ location?: string;
+ proxy?: string;
+ language?: string;
+}
+
+export interface GetTokenResult {
+ challenge_url: string;
+ challenge_url_cdn: string;
+ challenge_url_cdn_sri: string;
+ disable_default_styling: boolean | null;
+ iframe_height: number | null;
+ iframe_width: number | null;
+ // Enable keyboard biometrics
+ kbio: boolean;
+ // Enable mouse biometrics
+ mbio: boolean;
+ noscript: string;
+ // Enable touch biometrics
+ tbio: boolean;
+ // The token for the funcaptcha. Can be used 10 times before having to get a new token.
+ token: string;
+}
+
+export async function getToken(
+ options: GetTokenOptions
+): Promise<GetTokenResult> {
+ options = {
+ surl: "https://client-api.arkoselabs.com",
+ data: {},
+ ...options,
+ };
+
+ if (!options.headers)
+ options.headers = { "User-Agent": util.DEFAULT_USER_AGENT };
+ else if (!Object.keys(options.headers).map(v => v.toLowerCase()).includes("user-agent"))
+ options.headers["User-Agent"] = util.DEFAULT_USER_AGENT;
+
+ options.headers["Accept-Language"] = "en-US,en;q=0.9";
+ options.headers["Sec-Fetch-Site"] = "same-origin";
+ options.headers["Accept"] = "*/*";
+ options.headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
+ options.headers["sec-fetch-mode"] = "cors"
+
+ if (options.site) {
+ options.headers["Origin"] = options.surl
+ options.headers["Referer"] = `${options.surl}/v2/${options.pkey}/1.5.5/enforcement.fbfc14b0d793c6ef8359e0e4b4a91f67.html`
+ }
+
+ let ua = options.headers[Object.keys(options.headers).find(v => v.toLowerCase() == "user-agent")]
+
+ let res = await request(
+ options.surl,
+ {
+ method: "POST",
+ path: "/fc/gt2/public_key/" + options.pkey,
+ body: util.constructFormData({
+ bda: util.getBda(ua, options),
+ public_key: options.pkey,
+ site: options.site ? new URL(options.site).origin : undefined,
+ userbrowser: ua,
+ capi_version: "1.5.5",
+ capi_mode: "inline",
+ style_theme: "default",
+ rnd: Math.random().toString(),
+ ...Object.fromEntries(Object.keys(options.data).map(v => ["data[" + v + "]", options.data[v]])),
+ language: options.language || "en",
+ }),
+ headers: options.headers,
+ },
+ options.proxy
+ );
+
+ return JSON.parse(res.body.toString());
+}
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/challenge.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/challenge.ts
new file mode 100644
index 00000000..40090c4a
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/challenge.ts
@@ -0,0 +1,296 @@
+import request from "./http";
+import { TokenInfo } from "./session";
+import util from "./util";
+import crypt from "./crypt";
+import { assert } from "console";
+
+interface ChallengeOptions {
+ userAgent?: string;
+ proxy?: string;
+}
+
+interface ChallengeData {
+ token: string;
+ tokenInfo: TokenInfo;
+ session_token: string;
+ challengeID: string;
+ challengeURL: string;
+ game_data: {
+ gameType: number;
+ customGUI: {
+ is_using_api_breaker_v2: boolean;
+ _guiFontColr: string;
+ _challenge_imgs: string[];
+ api_breaker: string;
+ encrypted_mode: number;
+ example_images: {
+ correct: string;
+ incorrect: string;
+ }
+ };
+ waves: number;
+ game_variant?: string; // For gametype 3
+ game_difficulty?: number;
+ puzzle_name?: string; // For gametype 4
+ instruction_string?: string; // For gametype 4
+ };
+ game_sid: string;
+ lang: string;
+ string_table: {
+ [key: string]: string;
+ },
+ string_table_prefixes: string[]
+}
+
+interface AnswerResponse {
+ response: "not answered" | "answered";
+ solved?: boolean;
+ incorrect_guess?: number;
+ score?: number;
+ decryption_key?: string;
+ time_end?: number;
+ time_end_seconds?: number;
+}
+
+export abstract class Challenge {
+ public data: ChallengeData;
+ public imgs: Promise<Buffer>[];
+ public wave: number = 0;
+ protected key: Promise<string>;
+ protected userAgent: string;
+ protected proxy: string;
+
+ constructor(data: ChallengeData, challengeOptions: ChallengeOptions) {
+ this.data = data;
+ this.userAgent = challengeOptions.userAgent;
+ this.proxy = challengeOptions.proxy;
+
+ // Preload images
+ this.imgs = data.game_data.customGUI._challenge_imgs.map(async (v) => {
+ let req = await request(v, {
+ method: "GET",
+ path: undefined,
+ headers: {
+ "User-Agent": this.userAgent,
+ "Referer": this.data.tokenInfo.surl
+ },
+ });
+ return req.body;
+ });
+
+ if(data.game_data.customGUI.encrypted_mode) {
+ // Preload decryption key
+ this.key = this.getKey();
+ }
+ }
+
+ async getImage(): Promise<Buffer> {
+ let img = await this.imgs[this.wave];
+ try {
+ JSON.parse(img.toString()); // Image is encrypted
+ img = Buffer.from(
+ await crypt.decrypt(img.toString(), await this.getKey()),
+ "base64"
+ );
+ } catch (err) {
+ // Image is not encrypted
+ // All good!
+ }
+ return img;
+ }
+
+ protected async getKey() {
+ if (this.key) return await this.key;
+ let response = await request(
+ this.data.tokenInfo.surl,
+ {
+ method: "POST",
+ path: "/fc/ekey/",
+ headers: {
+ "User-Agent": this.userAgent,
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Referer": this.data.tokenInfo.surl,
+ },
+ body: util.constructFormData({
+ session_token: this.data.session_token,
+ game_token: this.data.challengeID,
+ }),
+ },
+ this.proxy
+ );
+ this.key = JSON.parse(response.body.toString()).decryption_key;
+ return this.key;
+ }
+
+ abstract answer(answer: number): Promise<AnswerResponse>;
+
+ get gameType() {
+ return this.data.game_data.gameType;
+ }
+
+ get variant() {
+ return this.data.game_data.game_variant || this.data.game_data.instruction_string;
+ }
+
+ get instruction() {
+ return this.data.string_table[`${this.data.game_data.gameType}.instructions-${this.variant}`] || this.data.string_table[`${this.data.game_data.gameType}.touch_done_info${this.data.game_data.game_variant ? `_${this.data.game_data.game_variant}` : ""}`];
+ }
+
+ get waves() {
+ return this.data.game_data.waves;
+ }
+}
+
+export class Challenge1 extends Challenge {
+ private answerHistory = [];
+ public increment;
+
+ constructor(data: ChallengeData, challengeOptions: ChallengeOptions) {
+ super(data, challengeOptions);
+
+ // But WHY?!
+ let clr = data.game_data.customGUI._guiFontColr
+ this.increment = parseInt(clr ? clr.replace("#", "").substring(3) : "28", 16)
+ this.increment = this.increment > 113 ? this.increment / 10 : this.increment
+ }
+
+ private round(num: number): string {
+ return (Math.round(num * 10) / 10).toFixed(2);
+ }
+
+ async answer(answer: number): Promise<AnswerResponse> {
+ if(answer >= 0 && answer <= Math.round(360 / 51.4) - 1)
+ this.answerHistory.push(this.round(answer * this.increment));
+ else
+ this.answerHistory.push(this.round(answer))
+
+ let encrypted = await crypt.encrypt(
+ this.answerHistory.toString(),
+ this.data.session_token
+ );
+ let req = await request(
+ this.data.tokenInfo.surl,
+ {
+ method: "POST",
+ path: "/fc/ca/",
+ headers: {
+ "User-Agent": this.userAgent,
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Referer": this.data.challengeURL
+ },
+ body: util.constructFormData({
+ session_token: this.data.session_token,
+ game_token: this.data.challengeID,
+ guess: encrypted,
+ }),
+ },
+ this.proxy
+ );
+ let reqData = JSON.parse(req.body.toString());
+ this.key = reqData.decryption_key || "";
+ this.wave++;
+ return reqData;
+ }
+}
+
+export class Challenge3 extends Challenge {
+ private answerHistory = [];
+
+ constructor(data: ChallengeData, challengeOptions: ChallengeOptions) {
+ super(data, challengeOptions);
+ }
+
+ async answer(tile: number): Promise<AnswerResponse> {
+ assert(tile >= 0 && tile <= 5, "Tile must be between 0 and 5");
+
+ let pos = util.tileToLoc(tile);
+ this.answerHistory.push(util.solveBreaker(!!this.data.game_data.customGUI.is_using_api_breaker_v2, this.data.game_data.customGUI.api_breaker, 3, pos))
+
+ let encrypted = await crypt.encrypt(
+ JSON.stringify(this.answerHistory),
+ this.data.session_token
+ );
+ let requestedId = await crypt.encrypt(JSON.stringify({}), `REQUESTED${this.data.session_token}ID`);
+ let { cookie: tCookie, value: tValue } = util.getTimestamp();
+ let req = await request(
+ this.data.tokenInfo.surl,
+ {
+ method: "POST",
+ path: "/fc/ca/",
+ headers: {
+ "User-Agent": this.userAgent,
+ "Content-Type": "application/x-www-form-urlencoded",
+ "X-Newrelic-Timestamp": tValue,
+ "X-Requested-ID": requestedId,
+ "Cookie": tCookie,
+ "Referer": this.data.challengeURL
+ },
+ body: util.constructFormData({
+ session_token: this.data.session_token,
+ game_token: this.data.challengeID,
+ guess: encrypted,
+ analytics_tier: this.data.tokenInfo.at,
+ sid: this.data.tokenInfo.r,
+ bio: this.data.tokenInfo.mbio && "eyJtYmlvIjoiMTI1MCwwLDE0NywyMDQ7MTg5NCwwLDE1MSwyMDA7MTk2MCwxLDE1MiwxOTk7MjAyOSwyLDE1MiwxOTk7MjU3NSwwLDE1NSwxOTU7MjU4NSwwLDE1NiwxOTA7MjU5NSwwLDE1OCwxODU7MjYwNCwwLDE1OSwxODA7MjYxMywwLDE2MCwxNzU7MjYyMSwwLDE2MSwxNzA7MjYzMCwwLDE2MywxNjU7MjY0MCwwLDE2NCwxNjA7MjY1MCwwLDE2NSwxNTU7MjY2NCwwLDE2NiwxNTA7MjY3NywwLDE2NiwxNDQ7MjY5NCwwLDE2NywxMzk7MjcyMCwwLDE2NywxMzM7Mjc1NCwwLDE2NywxMjc7Mjc4MywwLDE2NywxMjE7MjgxMiwwLDE2NywxMTU7Mjg0MywwLDE2NywxMDk7Mjg2MywwLDE2NywxMDM7Mjg3NSwwLDE2Niw5ODsyOTA1LDAsMTY1LDkzOzMyMzIsMCwxNjUsOTk7MzI2MiwwLDE2NSwxMDU7MzI5OSwwLDE2NCwxMTA7MzM0MCwwLDE2MSwxMTU7MzM3MiwwLDE1NywxMjA7MzM5NSwwLDE1MywxMjQ7MzQwOCwwLDE0OCwxMjc7MzQyMCwwLDE0MywxMzA7MzQyOSwwLDEzOCwxMzE7MzQ0MSwwLDEzMywxMzQ7MzQ1MCwwLDEyOCwxMzU7MzQ2MSwwLDEyMywxMzg7MzQ3NiwwLDExOCwxNDA7MzQ4OSwwLDExMywxNDI7MzUwMywwLDEwOCwxNDM7MzUxOCwwLDEwMywxNDQ7MzUzNCwwLDk4LDE0NTszNTU2LDAsOTMsMTQ2OzM2MTUsMCw4OCwxNDg7MzY2MiwwLDgzLDE1MTszNjgzLDAsNzgsMTU0OzM3MDEsMCw3MywxNTc7MzcyNSwwLDY5LDE2MTszNzkzLDEsNjgsMTYyOzM4NTEsMiw2OCwxNjI7IiwidGJpbyI6IiIsImtiaW8iOiIifQ=="
+ }),
+ },
+ this.proxy
+ );
+ let reqData = JSON.parse(req.body.toString());
+ this.key = reqData.decryption_key || "";
+ this.wave++;
+ return reqData;
+ }
+}
+
+export class Challenge4 extends Challenge {
+ private answerHistory = [];
+
+ constructor(data: ChallengeData, challengeOptions: ChallengeOptions) {
+ super(data, challengeOptions);
+ }
+
+ async answer(index: number): Promise<AnswerResponse> {
+ assert(index >= 0 && index <= this.data.game_data.game_difficulty - 1, "Index must be between 0 and " + (this.data.game_data.game_difficulty - 1));
+ this.answerHistory.push(util.solveBreaker(!!this.data.game_data.customGUI.is_using_api_breaker_v2, this.data.game_data.customGUI.api_breaker, 4, { index }))
+
+ let encrypted = await crypt.encrypt(
+ JSON.stringify(this.answerHistory),
+ this.data.session_token
+ );
+ let requestedId = await crypt.encrypt(JSON.stringify({}), `REQUESTED${this.data.session_token}ID`);
+ let { cookie: tCookie, value: tValue } = util.getTimestamp();
+ let req = await request(
+ this.data.tokenInfo.surl,
+ {
+ method: "POST",
+ path: "/fc/ca/",
+ headers: {
+ "User-Agent": this.userAgent,
+ "Content-Type": "application/x-www-form-urlencoded",
+ "X-Newrelic-Timestamp": tValue,
+ "X-Requested-ID": requestedId,
+ "Cookie": tCookie,
+ "Referer": this.data.challengeURL
+ },
+ body: util.constructFormData({
+ session_token: this.data.session_token,
+ game_token: this.data.challengeID,
+ guess: encrypted,
+ analytics_tier: this.data.tokenInfo.at,
+ sid: this.data.tokenInfo.r,
+ bio: this.data.tokenInfo.mbio && "eyJtYmlvIjoiMTI1MCwwLDE0NywyMDQ7MTg5NCwwLDE1MSwyMDA7MTk2MCwxLDE1MiwxOTk7MjAyOSwyLDE1MiwxOTk7MjU3NSwwLDE1NSwxOTU7MjU4NSwwLDE1NiwxOTA7MjU5NSwwLDE1OCwxODU7MjYwNCwwLDE1OSwxODA7MjYxMywwLDE2MCwxNzU7MjYyMSwwLDE2MSwxNzA7MjYzMCwwLDE2MywxNjU7MjY0MCwwLDE2NCwxNjA7MjY1MCwwLDE2NSwxNTU7MjY2NCwwLDE2NiwxNTA7MjY3NywwLDE2NiwxNDQ7MjY5NCwwLDE2NywxMzk7MjcyMCwwLDE2NywxMzM7Mjc1NCwwLDE2NywxMjc7Mjc4MywwLDE2NywxMjE7MjgxMiwwLDE2NywxMTU7Mjg0MywwLDE2NywxMDk7Mjg2MywwLDE2NywxMDM7Mjg3NSwwLDE2Niw5ODsyOTA1LDAsMTY1LDkzOzMyMzIsMCwxNjUsOTk7MzI2MiwwLDE2NSwxMDU7MzI5OSwwLDE2NCwxMTA7MzM0MCwwLDE2MSwxMTU7MzM3MiwwLDE1NywxMjA7MzM5NSwwLDE1MywxMjQ7MzQwOCwwLDE0OCwxMjc7MzQyMCwwLDE0MywxMzA7MzQyOSwwLDEzOCwxMzE7MzQ0MSwwLDEzMywxMzQ7MzQ1MCwwLDEyOCwxMzU7MzQ2MSwwLDEyMywxMzg7MzQ3NiwwLDExOCwxNDA7MzQ4OSwwLDExMywxNDI7MzUwMywwLDEwOCwxNDM7MzUxOCwwLDEwMywxNDQ7MzUzNCwwLDk4LDE0NTszNTU2LDAsOTMsMTQ2OzM2MTUsMCw4OCwxNDg7MzY2MiwwLDgzLDE1MTszNjgzLDAsNzgsMTU0OzM3MDEsMCw3MywxNTc7MzcyNSwwLDY5LDE2MTszNzkzLDEsNjgsMTYyOzM4NTEsMiw2OCwxNjI7IiwidGJpbyI6IiIsImtiaW8iOiIifQ=="
+ }),
+ },
+ this.proxy
+ );
+ let reqData = JSON.parse(req.body.toString());
+ this.key = reqData.decryption_key || "";
+ this.wave++;
+ return reqData;
+ }
+
+ get difficulty(): number {
+ return this.data.game_data.game_difficulty;
+ }
+} \ No newline at end of file
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/crypt.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/crypt.ts
new file mode 100644
index 00000000..fb883660
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/crypt.ts
@@ -0,0 +1,78 @@
+import { createHash, createCipheriv, createDecipheriv } from "crypto";
+
+interface EncryptionData {
+ ct: string;
+ iv: string;
+ s: string;
+}
+
+function encrypt(data: string, key: string): string {
+ let salt = "";
+ let salted = "";
+ let dx = Buffer.alloc(0);
+
+ // Generate salt, as 8 random lowercase letters
+ salt = String.fromCharCode(...Array(8).fill(0).map(_ => Math.floor(Math.random() * 26) + 97))
+
+ // Our final key and iv come from the key and salt being repeatedly hashed
+ // dx = md5(md5(md5(key + salt) + key + salt) + key + salt)
+ // For each round of hashing, we append the result to salted, resulting in a 96 character string
+ // The first 64 characters are the key, and the last 32 are the iv
+ for (let x = 0; x < 3; x++) {
+ dx = createHash("md5")
+ .update(
+ Buffer.concat([
+ Buffer.from(dx),
+ Buffer.from(key),
+ Buffer.from(salt),
+ ])
+ )
+ .digest();
+
+ salted += dx.toString("hex");
+ }
+
+ let aes = createCipheriv(
+ "aes-256-cbc",
+ Buffer.from(salted.substring(0, 64), "hex"), // Key
+ Buffer.from(salted.substring(64, 64 + 32), "hex") // IV
+ );
+
+ return JSON.stringify({
+ ct: aes.update(data, null, "base64") + aes.final("base64"),
+ iv: salted.substring(64, 64 + 32),
+ s: Buffer.from(salt).toString("hex"),
+ });
+}
+
+function decrypt(rawData: string, key: string): string {
+ let data: EncryptionData = JSON.parse(rawData);
+
+ // We get our decryption key by doing the inverse of the encryption process
+ let dk = Buffer.concat([Buffer.from(key), Buffer.from(data.s, "hex")]);
+ let arr = [Buffer.from(createHash("md5").update(dk).digest()).toString("hex")];
+ let result = arr[0];
+
+ for (let x = 1; x < 3; x++) {
+ arr.push(
+ Buffer.from(
+ createHash("md5")
+ .update(Buffer.concat([Buffer.from(arr[x - 1], "hex"), dk]))
+ .digest()
+ ).toString("hex")
+ );
+ result += arr[x];
+ }
+
+ let aes = createDecipheriv(
+ "aes-256-cbc",
+ Buffer.from(result.substring(0, 64), "hex"),
+ Buffer.from(data.iv, "hex")
+ );
+ return aes.update(data.ct, "base64", "utf8") + aes.final("utf8");
+}
+
+export default {
+ encrypt,
+ decrypt,
+};
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/fingerprint.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/fingerprint.ts
new file mode 100644
index 00000000..b4d24ea2
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/fingerprint.ts
@@ -0,0 +1,321 @@
+import x64hash128 from "./murmur";
+import { randomBytes } from "crypto";
+
+const baseFingerprint = {
+ DNT: "unknown", // Do not track On/Off | Previous Value: 1
+ L: "en-US", // Browser language
+ D: 24, // Screen color depth (in bits)
+ PR: 1, // Pixel ratio
+ S: [1920, 1200], // Screen resolution
+ AS: [1920, 1200], // Available screen resolution
+ TO: 9999, // Timezone offset
+ SS: true, // Screen orientation (landscape/portrait)
+ LS: true, // Local storage available
+ IDB: true, // IndexedDB available
+ B: false, // addBehaviour support
+ ODB: true, // OpenDatabase support
+ CPUC: "unknown", // CPU Class
+ PK: "Win32", // Platform
+ CFP: `canvas winding:yes~canvas fp:data:image/png;base64,${Buffer.from(
+ Math.random().toString()
+ ).toString("base64")}`, // Canvas fingerprint (if canvas is supported)
+ FR: false, // Fake screen resolution?
+ FOS: false, // Fake OS?
+ FB: false, // Fake Browser?
+ JSF: [
+ "Andale Mono",
+ "Arial",
+ "Arial Black",
+ "Arial Hebrew",
+ "Arial MT",
+ "Arial Narrow",
+ "Arial Rounded MT Bold",
+ "Arial Unicode MS",
+ "Bitstream Vera Sans Mono",
+ "Book Antiqua",
+ "Bookman Old Style",
+ "Calibri",
+ "Cambria",
+ "Cambria Math",
+ "Century",
+ "Century Gothic",
+ "Century Schoolbook",
+ "Comic Sans",
+ "Comic Sans MS",
+ "Consolas",
+ "Courier",
+ "Courier New",
+ "Garamond",
+ "Geneva",
+ "Georgia",
+ "Helvetica",
+ "Helvetica Neue",
+ "Impact",
+ "Lucida Bright",
+ "Lucida Calligraphy",
+ "Lucida Console",
+ "Lucida Fax",
+ "LUCIDA GRANDE",
+ "Lucida Handwriting",
+ "Lucida Sans",
+ "Lucida Sans Typewriter",
+ "Lucida Sans Unicode",
+ "Microsoft Sans Serif",
+ "Monaco",
+ "Monotype Corsiva",
+ "MS Gothic",
+ "MS Outlook",
+ "MS PGothic",
+ "MS Reference Sans Serif",
+ "MS Sans Serif",
+ "MS Serif",
+ "MYRIAD",
+ "MYRIAD PRO",
+ "Palatino",
+ "Palatino Linotype",
+ "Segoe Print",
+ "Segoe Script",
+ "Segoe UI",
+ "Segoe UI Light",
+ "Segoe UI Semibold",
+ "Segoe UI Symbol",
+ "Tahoma",
+ "Times",
+ "Times New Roman",
+ "Times New Roman PS",
+ "Trebuchet MS",
+ "Verdana",
+ "Wingdings",
+ "Wingdings 2",
+ "Wingdings 3",
+ ], // Available fonts
+ P: [
+ "Chrome PDF Plugin::Portable Document Format::application/x-google-chrome-pdf~pdf",
+ "Chrome PDF Viewer::::application/pdf~pdf",
+ "Native Client::::application/x-nacl~,application/x-pnacl~",
+ ], // Plugins
+ T: [0, false, false], // Touch screen (maxTouchPoints, TouchEvent event listener support, ontouchstart support)
+ H: 24, // Cpu threads
+ SWF: false, // Flash support
+};
+
+const languages = [
+ "af", "af-ZA", "ar", "ar-AE", "ar-BH", "ar-DZ", "ar-EG", "ar-IQ", "ar-JO", "ar-KW", "ar-LB", "ar-LY", "ar-MA", "ar-OM", "ar-QA", "ar-SA",
+ "ar-SY", "ar-TN", "ar-YE", "az", "az-AZ", "az-AZ", "be", "be-BY", "bg", "bg-BG", "bs-BA", "ca", "ca-ES", "cs", "cs-CZ", "cy",
+ "cy-GB", "da", "da-DK", "de", "de-AT", "de-CH", "de-DE", "de-LI", "de-LU", "dv", "dv-MV", "el", "el-GR", "en", "en-AU", "en-BZ",
+ "en-CA", "en-CB", "en-GB", "en-IE", "en-JM", "en-NZ", "en-PH", "en-TT", "en-US", "en-ZA", "en-ZW", "eo", "es", "es-AR", "es-BO", "es-CL",
+ "es-CO", "es-CR", "es-DO", "es-EC", "es-ES", "es-ES", "es-GT", "es-HN", "es-MX", "es-NI", "es-PA", "es-PE", "es-PR", "es-PY", "es-SV", "es-UY",
+ "es-VE", "et", "et-EE", "eu", "eu-ES", "fa", "fa-IR", "fi", "fi-FI", "fo", "fo-FO", "fr", "fr-BE", "fr-CA", "fr-CH", "fr-FR",
+ "fr-LU", "fr-MC", "gl", "gl-ES", "gu", "gu-IN", "he", "he-IL", "hi", "hi-IN", "hr", "hr-BA", "hr-HR", "hu", "hu-HU", "hy",
+ "hy-AM", "id", "id-ID", "is", "is-IS", "it", "it-CH", "it-IT", "ja", "ja-JP", "ka", "ka-GE", "kk", "kk-KZ", "kn", "kn-IN",
+ "ko", "ko-KR", "kok", "kok-IN", "ky", "ky-KG", "lt", "lt-LT", "lv", "lv-LV", "mi", "mi-NZ", "mk", "mk-MK", "mn", "mn-MN",
+ "mr", "mr-IN", "ms", "ms-BN", "ms-MY", "mt", "mt-MT", "nb", "nb-NO", "nl", "nl-BE", "nl-NL", "nn-NO", "ns", "ns-ZA", "pa",
+ "pa-IN", "pl", "pl-PL", "ps", "ps-AR", "pt", "pt-BR", "pt-PT", "qu", "qu-BO", "qu-EC", "qu-PE", "ro", "ro-RO", "ru", "ru-RU",
+ "sa", "sa-IN", "se", "se-FI", "se-FI", "se-FI", "se-NO", "se-NO", "se-NO", "se-SE", "se-SE", "se-SE", "sk", "sk-SK", "sl", "sl-SI",
+ "sq", "sq-AL", "sr-BA", "sr-BA", "sr-SP", "sr-SP", "sv", "sv-FI", "sv-SE", "sw", "sw-KE", "syr", "syr-SY", "ta", "ta-IN", "te",
+ "te-IN", "th", "th-TH", "tl", "tl-PH", "tn", "tn-ZA", "tr", "tr-TR", "tt", "tt-RU", "ts", "uk", "uk-UA", "ur", "ur-PK",
+ "uz", "uz-UZ", "uz-UZ", "vi", "vi-VN", "xh", "xh-ZA", "zh", "zh-CN", "zh-HK", "zh-MO", "zh-SG", "zh-TW", "zu", "zu-ZA"
+];
+
+let screenRes = [
+ [1920, 1080],
+ [1920, 1200],
+ [2048, 1080],
+ [2560, 1440],
+ [1366, 768],
+ [1440, 900],
+ [1536, 864],
+ [1680, 1050],
+ [1280, 1024],
+ [1280, 800],
+ [1280, 720],
+ [1600, 1200],
+ [1600, 900],
+];
+function randomScreenRes() {
+ return screenRes[Math.floor(Math.random() * screenRes.length)];
+}
+
+// Get fingerprint
+function getFingerprint() {
+ let fingerprint = { ...baseFingerprint }; // Create a copy of the base fingerprint
+
+ // Randomization time!
+ fingerprint["DNT"] = "unknown";
+ fingerprint["L"] = languages[Math.floor(Math.random() * languages.length)];
+ fingerprint["D"] = [8, 24][
+ Math.floor(Math.random() * 2)
+ ];
+ fingerprint["PR"] = Math.round(Math.random() * 100) / 100 * 2 + 0.5;
+ fingerprint["S"] = randomScreenRes();
+ fingerprint["AS"] = fingerprint.S;
+ fingerprint["TO"] = (Math.floor(Math.random() * 24) - 12) * 60;
+ fingerprint["SS"] = Math.random() > 0.5;
+ fingerprint["LS"] = Math.random() > 0.5;
+ fingerprint["IDB"] = Math.random() > 0.5;
+ fingerprint["B"] = Math.random() > 0.5;
+ fingerprint["ODB"] = Math.random() > 0.5;
+ fingerprint["CPUC"] = "unknown";
+ fingerprint["PK"] = "Win32"
+ fingerprint["CFP"] = "canvas winding:yes~canvas fp:data:image/png;base64," + randomBytes(128).toString("base64");
+ fingerprint["FR"] = false; // Fake Resolution
+ fingerprint["FOS"] = false; // Fake Operating System
+ fingerprint["FB"] = false; // Fake Browser
+ fingerprint["JSF"] = fingerprint["JSF"].filter(() => Math.random() > 0.5);
+ fingerprint["P"] = fingerprint["P"].filter(() => Math.random() > 0.5);
+ fingerprint["T"] = [
+ Math.floor(Math.random() * 8),
+ Math.random() > 0.5,
+ Math.random() > 0.5,
+ ];
+ fingerprint["H"] = 2 ** Math.floor(Math.random() * 6);
+ fingerprint["SWF"] = fingerprint["SWF"]; // RIP Flash
+
+ return fingerprint;
+}
+
+function prepareF(fingerprint) {
+ let f = [];
+ let keys = Object.keys(fingerprint);
+ for (let i = 0; i < keys.length; i++) {
+ if (fingerprint[keys[i]].join) f.push(fingerprint[keys[i]].join(";"));
+ else f.push(fingerprint[keys[i]]);
+ }
+ return f.join("~~~");
+}
+
+function prepareFe(fingerprint) {
+ let fe = [];
+ let keys = Object.keys(fingerprint);
+ for (let i = 0; i < keys.length; i++) {
+ switch (keys[i]) {
+ case "CFP":
+ fe.push(`${keys[i]}:${cfpHash(fingerprint[keys[i]])}`);
+ break;
+ case "P":
+ fe.push(
+ `${keys[i]}:${fingerprint[keys[i]].map(
+ (v) => v.split("::")[0]
+ )}`
+ );
+ break;
+ default:
+ fe.push(`${keys[i]}:${fingerprint[keys[i]]}`);
+ break;
+ }
+ }
+ return fe;
+}
+
+function cfpHash(H8W) {
+ var l8W, U8W;
+ if (!H8W) return "";
+ if (Array.prototype.reduce)
+ return H8W.split("").reduce(function (p8W, z8W) {
+ p8W = (p8W << 5) - p8W + z8W.charCodeAt(0);
+ return p8W & p8W;
+ }, 0);
+ l8W = 0;
+ if (H8W.length === 0) return l8W;
+ for (var k8W = 0; k8W < H8W.length; k8W++) {
+ U8W = H8W.charCodeAt(k8W);
+ l8W = (l8W << 5) - l8W + U8W;
+ l8W = l8W & l8W;
+ }
+ return l8W;
+}
+
+let baseEnhancedFingerprint = {
+ "webgl_extensions": "ANGLE_instanced_arrays;EXT_blend_minmax;EXT_color_buffer_half_float;EXT_disjoint_timer_query;EXT_float_blend;EXT_frag_depth;EXT_shader_texture_lod;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;EXT_sRGB;KHR_parallel_shader_compile;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_color_buffer_float;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_multi_draw",
+ "webgl_extensions_hash": "58a5a04a5bef1a78fa88d5c5098bd237",
+ "webgl_renderer": "WebKit WebGL",
+ "webgl_vendor": "WebKit",
+ "webgl_version": "WebGL 1.0 (OpenGL ES 2.0 Chromium)",
+ "webgl_shading_language_version": "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)",
+ "webgl_aliased_line_width_range": "[1, 1]",
+ "webgl_aliased_point_size_range": "[1, 1023]",
+ "webgl_antialiasing": "yes",
+ "webgl_bits": "8,8,24,8,8,0",
+ "webgl_max_params": "16,64,16384,4096,8192,32,8192,31,16,32,4096",
+ "webgl_max_viewport_dims": "[8192, 8192]",
+ "webgl_unmasked_vendor": "Google Inc. (Google)",
+ "webgl_unmasked_renderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver)",
+ "webgl_vsf_params": "23,127,127,23,127,127,23,127,127",
+ "webgl_vsi_params": "0,31,30,0,31,30,0,31,30",
+ "webgl_fsf_params": "23,127,127,23,127,127,23,127,127",
+ "webgl_fsi_params": "0,31,30,0,31,30,0,31,30",
+ "webgl_hash_webgl": null,
+ "user_agent_data_brands": "Chromium,Google Chrome,Not=A?Brand",
+ "user_agent_data_mobile": null,
+ "navigator_connection_downlink": null,
+ "navigator_connection_downlink_max": null,
+ "network_info_rtt": null,
+ "network_info_save_data": false,
+ "network_info_rtt_type": null,
+ "screen_pixel_depth": 24,
+ "navigator_device_memory": 0.5,
+ "navigator_languages": "en-US,fr-BE,fr,en-BE,en",
+ "window_inner_width": 0,
+ "window_inner_height": 0,
+ "window_outer_width": 2195,
+ "window_outer_height": 1195,
+ "browser_detection_firefox": false,
+ "browser_detection_brave": false,
+ "audio_codecs": "{\"ogg\":\"probably\",\"mp3\":\"probably\",\"wav\":\"probably\",\"m4a\":\"maybe\",\"aac\":\"probably\"}",
+ "video_codecs": "{\"ogg\":\"probably\",\"h264\":\"probably\",\"webm\":\"probably\",\"mpeg4v\":\"\",\"mpeg4a\":\"\",\"theora\":\"\"}",
+ "media_query_dark_mode": true,
+ "headless_browser_phantom": false,
+ "headless_browser_selenium": false,
+ "headless_browser_nightmare_js": false,
+ "document__referrer": "https://www.roblox.com/",
+ "window__ancestor_origins": [
+ "https://www.roblox.com",
+ ],
+ "window__tree_index": [
+ 0
+ ],
+ "window__tree_structure": "[[]]",
+ "window__location_href": "https://roblox-api.arkoselabs.com/v2/1.5.5/enforcement.fbfc14b0d793c6ef8359e0e4b4a91f67.html#476068BF-9607-4799-B53D-966BE98E2B81",
+ "client_config__sitedata_location_href": "https://www.roblox.com/arkose/iframe",
+ "client_config__surl": "https://roblox-api.arkoselabs.com",
+ "client_config__language": null,
+ "navigator_battery_charging": true,
+ "audio_fingerprint": "124.04347527516074"
+}
+function getEnhancedFingerprint(fp: typeof baseFingerprint, ua: string, opts: any) {
+ let fingerprint = { ...baseEnhancedFingerprint };
+
+ fingerprint.webgl_extensions = fingerprint.webgl_extensions.split(";").filter(_ => Math.random() > 0.5).join(";");
+ fingerprint.webgl_extensions_hash = x64hash128(fingerprint.webgl_extensions, 0);
+ fingerprint.screen_pixel_depth = fp.D;
+ fingerprint.navigator_languages = fp.L;
+ fingerprint.window_outer_height = fp.S[0];
+ fingerprint.window_outer_width = fp.S[1];
+ fingerprint.window_inner_height = fp.S[0];
+ fingerprint.window_inner_width = fp.S[1];
+ fingerprint.screen_pixel_depth = fp.D;
+ fingerprint.browser_detection_firefox = !!ua.match(/Firefox\/\d+/)
+ fingerprint.browser_detection_brave = !!ua.match(/Brave\/\d+/)
+ fingerprint.media_query_dark_mode = Math.random() > 0.9;
+ fingerprint.webgl_hash_webgl = x64hash128(Object.entries(fingerprint).filter(([k, v]) => k.startsWith("webgl_") && k != "webgl_hash_webgl").map(([k, v]) => v).join(","), 0);
+
+ fingerprint.client_config__language = opts.language || null;
+ fingerprint.window__location_href = `${opts.surl}/v2/1.5.5/enforcement.fbfc14b0d793c6ef8359e0e4b4a91f67.html#${opts.pkey}`
+ if (opts.site) {
+ fingerprint.document__referrer = opts.site;
+ fingerprint.window__ancestor_origins = [opts.site];
+ fingerprint.client_config__sitedata_location_href = opts.site;
+ }
+
+ fingerprint.client_config__surl = opts.surl || "https://client-api.arkoselabs.com";
+ fingerprint.audio_fingerprint = (124.04347527516074 + Math.random() * 0.001 - 0.0005).toString();
+
+ return Object.entries(fingerprint).map(([k, v]) => ({ key: k, value: v }));
+}
+
+export default {
+ getFingerprint,
+ prepareF,
+ prepareFe,
+ getEnhancedFingerprint,
+};
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/http.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/http.ts
new file mode 100644
index 00000000..e0fa3acb
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/http.ts
@@ -0,0 +1,28 @@
+import { request, ProxyAgent } from "undici";
+// @ts-ignore
+import { RequestOptions } from "undici/types/dispatcher";
+
+async function req(url: string, options: RequestOptions, proxy?: string) {
+ let auth = undefined;
+ if (proxy) {
+ let proxyUrl = new URL(proxy);
+ if(proxyUrl.username && proxyUrl.password) {
+ auth = Buffer.from(proxyUrl.username + ":" + proxyUrl.password).toString("base64")
+ }
+ }
+ let dispatcher = proxy ? new ProxyAgent({
+ uri: proxy,
+ auth
+ }) : undefined;
+
+ let req = await request(url, {
+ ...options,
+ dispatcher,
+ });
+ return {
+ headers: req.headers,
+ body: Buffer.from(await req.body.arrayBuffer()),
+ };
+}
+
+export default req;
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/index.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/index.ts
new file mode 100644
index 00000000..97b1212a
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/index.ts
@@ -0,0 +1,2 @@
+export * from "./api";
+export * from "./session";
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/murmur.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/murmur.ts
new file mode 100644
index 00000000..32750f3d
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/murmur.ts
@@ -0,0 +1,205 @@
+// MurmurHash3 related functions
+// https://github.com/markogresak/fingerprintjs2/blob/master/src/x64hash128.js
+
+// Given two 64bit ints (as an array of two 32bit ints) returns the two
+// added together as a 64bit int (as an array of two 32bit ints).
+var x64Add = function (t, r) {
+ (t = [t[0] >>> 16, 65535 & t[0], t[1] >>> 16, 65535 & t[1]]),
+ (r = [r[0] >>> 16, 65535 & r[0], r[1] >>> 16, 65535 & r[1]]);
+ var e = [0, 0, 0, 0];
+ return (
+ (e[3] += t[3] + r[3]),
+ (e[2] += e[3] >>> 16),
+ (e[3] &= 65535),
+ (e[2] += t[2] + r[2]),
+ (e[1] += e[2] >>> 16),
+ (e[2] &= 65535),
+ (e[1] += t[1] + r[1]),
+ (e[0] += e[1] >>> 16),
+ (e[1] &= 65535),
+ (e[0] += t[0] + r[0]),
+ (e[0] &= 65535),
+ [(e[0] << 16) | e[1], (e[2] << 16) | e[3]]
+ );
+ },
+ // Given two 64bit ints (as an array of two 32bit ints) returns the two
+ // multiplied together as a 64bit int (as an array of two 32bit ints).
+ x64Multiply = function (t, r) {
+ (t = [t[0] >>> 16, 65535 & t[0], t[1] >>> 16, 65535 & t[1]]),
+ (r = [r[0] >>> 16, 65535 & r[0], r[1] >>> 16, 65535 & r[1]]);
+ var e = [0, 0, 0, 0];
+ return (
+ (e[3] += t[3] * r[3]),
+ (e[2] += e[3] >>> 16),
+ (e[3] &= 65535),
+ (e[2] += t[2] * r[3]),
+ (e[1] += e[2] >>> 16),
+ (e[2] &= 65535),
+ (e[2] += t[3] * r[2]),
+ (e[1] += e[2] >>> 16),
+ (e[2] &= 65535),
+ (e[1] += t[1] * r[3]),
+ (e[0] += e[1] >>> 16),
+ (e[1] &= 65535),
+ (e[1] += t[2] * r[2]),
+ (e[0] += e[1] >>> 16),
+ (e[1] &= 65535),
+ (e[1] += t[3] * r[1]),
+ (e[0] += e[1] >>> 16),
+ (e[1] &= 65535),
+ (e[0] += t[0] * r[3] + t[1] * r[2] + t[2] * r[1] + t[3] * r[0]),
+ (e[0] &= 65535),
+ [(e[0] << 16) | e[1], (e[2] << 16) | e[3]]
+ );
+ },
+ // Given a 64bit int (as an array of two 32bit ints) and an int
+ // representing a number of bit positions, returns the 64bit int (as an
+ // array of two 32bit ints) rotated left by that number of positions.
+ x64Rotl = function (t, r) {
+ return 32 === (r %= 64)
+ ? [t[1], t[0]]
+ : r < 32
+ ? [
+ (t[0] << r) | (t[1] >>> (32 - r)),
+ (t[1] << r) | (t[0] >>> (32 - r)),
+ ]
+ : ((r -= 32),
+ [
+ (t[1] << r) | (t[0] >>> (32 - r)),
+ (t[0] << r) | (t[1] >>> (32 - r)),
+ ]);
+ },
+ // Given a 64bit int (as an array of two 32bit ints) and an int
+ // representing a number of bit positions, returns the 64bit int (as an
+ // array of two 32bit ints) shifted left by that number of positions.
+ x64LeftShift = function (t, r) {
+ return 0 === (r %= 64)
+ ? t
+ : r < 32
+ ? [(t[0] << r) | (t[1] >>> (32 - r)), t[1] << r]
+ : [t[1] << (r - 32), 0];
+ },
+ // Given two 64bit ints (as an array of two 32bit ints) returns the two
+ // xored together as a 64bit int (as an array of two 32bit ints).
+ x64Xor = function (t, r) {
+ return [t[0] ^ r[0], t[1] ^ r[1]];
+ },
+ // Given a block, returns murmurHash3's final x64 mix of that block.
+ // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the
+ // only place where we need to right shift 64bit ints.)
+ x64Fmix = function (t) {
+ return (
+ (t = x64Xor(t, [0, t[0] >>> 1])),
+ (t = x64Multiply(t, [4283543511, 3981806797])),
+ (t = x64Xor(t, [0, t[0] >>> 1])),
+ (t = x64Multiply(t, [3301882366, 444984403])),
+ (t = x64Xor(t, [0, t[0] >>> 1]))
+ );
+ },
+ // Given a string and an optional seed as an int, returns a 128 bit
+ // hash using the x64 flavor of MurmurHash3, as an unsigned hex.
+ x64hash128 = function (t, r) {
+ r = r || 0;
+ for (
+ var e = (t = t || "").length % 16,
+ o = t.length - e,
+ x = [0, r],
+ c = [0, r],
+ h = [0, 0],
+ a = [0, 0],
+ d = [2277735313, 289559509],
+ i = [1291169091, 658871167],
+ l = 0;
+ l < o;
+ l += 16
+ )
+ (h = [
+ (255 & t.charCodeAt(l + 4)) |
+ ((255 & t.charCodeAt(l + 5)) << 8) |
+ ((255 & t.charCodeAt(l + 6)) << 16) |
+ ((255 & t.charCodeAt(l + 7)) << 24),
+ (255 & t.charCodeAt(l)) |
+ ((255 & t.charCodeAt(l + 1)) << 8) |
+ ((255 & t.charCodeAt(l + 2)) << 16) |
+ ((255 & t.charCodeAt(l + 3)) << 24),
+ ]),
+ (a = [
+ (255 & t.charCodeAt(l + 12)) |
+ ((255 & t.charCodeAt(l + 13)) << 8) |
+ ((255 & t.charCodeAt(l + 14)) << 16) |
+ ((255 & t.charCodeAt(l + 15)) << 24),
+ (255 & t.charCodeAt(l + 8)) |
+ ((255 & t.charCodeAt(l + 9)) << 8) |
+ ((255 & t.charCodeAt(l + 10)) << 16) |
+ ((255 & t.charCodeAt(l + 11)) << 24),
+ ]),
+ (h = x64Multiply(h, d)),
+ (h = x64Rotl(h, 31)),
+ (h = x64Multiply(h, i)),
+ (x = x64Xor(x, h)),
+ (x = x64Rotl(x, 27)),
+ (x = x64Add(x, c)),
+ (x = x64Add(x64Multiply(x, [0, 5]), [0, 1390208809])),
+ (a = x64Multiply(a, i)),
+ (a = x64Rotl(a, 33)),
+ (a = x64Multiply(a, d)),
+ (c = x64Xor(c, a)),
+ (c = x64Rotl(c, 31)),
+ (c = x64Add(c, x)),
+ (c = x64Add(x64Multiply(c, [0, 5]), [0, 944331445]));
+ switch (((h = [0, 0]), (a = [0, 0]), e)) {
+ case 15:
+ a = x64Xor(a, x64LeftShift([0, t.charCodeAt(l + 14)], 48));
+ case 14:
+ a = x64Xor(a, x64LeftShift([0, t.charCodeAt(l + 13)], 40));
+ case 13:
+ a = x64Xor(a, x64LeftShift([0, t.charCodeAt(l + 12)], 32));
+ case 12:
+ a = x64Xor(a, x64LeftShift([0, t.charCodeAt(l + 11)], 24));
+ case 11:
+ a = x64Xor(a, x64LeftShift([0, t.charCodeAt(l + 10)], 16));
+ case 10:
+ a = x64Xor(a, x64LeftShift([0, t.charCodeAt(l + 9)], 8));
+ case 9:
+ (a = x64Xor(a, [0, t.charCodeAt(l + 8)])),
+ (a = x64Multiply(a, i)),
+ (a = x64Rotl(a, 33)),
+ (a = x64Multiply(a, d)),
+ (c = x64Xor(c, a));
+ case 8:
+ h = x64Xor(h, x64LeftShift([0, t.charCodeAt(l + 7)], 56));
+ case 7:
+ h = x64Xor(h, x64LeftShift([0, t.charCodeAt(l + 6)], 48));
+ case 6:
+ h = x64Xor(h, x64LeftShift([0, t.charCodeAt(l + 5)], 40));
+ case 5:
+ h = x64Xor(h, x64LeftShift([0, t.charCodeAt(l + 4)], 32));
+ case 4:
+ h = x64Xor(h, x64LeftShift([0, t.charCodeAt(l + 3)], 24));
+ case 3:
+ h = x64Xor(h, x64LeftShift([0, t.charCodeAt(l + 2)], 16));
+ case 2:
+ h = x64Xor(h, x64LeftShift([0, t.charCodeAt(l + 1)], 8));
+ case 1:
+ (h = x64Xor(h, [0, t.charCodeAt(l)])),
+ (h = x64Multiply(h, d)),
+ (h = x64Rotl(h, 31)),
+ (h = x64Multiply(h, i)),
+ (x = x64Xor(x, h));
+ }
+ return (
+ (x = x64Xor(x, [0, t.length])),
+ (c = x64Xor(c, [0, t.length])),
+ (x = x64Add(x, c)),
+ (c = x64Add(c, x)),
+ (x = x64Fmix(x)),
+ (c = x64Fmix(c)),
+ (x = x64Add(x, c)),
+ (c = x64Add(c, x)),
+ ("00000000" + (x[0] >>> 0).toString(16)).slice(-8) +
+ ("00000000" + (x[1] >>> 0).toString(16)).slice(-8) +
+ ("00000000" + (c[0] >>> 0).toString(16)).slice(-8) +
+ ("00000000" + (c[1] >>> 0).toString(16)).slice(-8)
+ );
+ };
+export default x64hash128;
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/session.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/session.ts
new file mode 100644
index 00000000..9244daae
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/session.ts
@@ -0,0 +1,125 @@
+import { GetTokenResult } from "./api";
+import { Challenge, Challenge1, Challenge3, Challenge4 } from "./challenge";
+import http from "./http";
+import util from "./util";
+
+export interface TokenInfo {
+ token: string;
+ r: string;
+ metabgclr: string;
+ mainbgclr: string;
+ guitextcolor: string;
+ metaiconclr: string;
+ meta_height: string;
+ meta_width: string;
+ meta: string;
+ pk: string;
+ dc: string;
+ at: string;
+ cdn_url: string;
+ lurl: string;
+ surl: string;
+ smurl: string;
+ // Enable keyboard biometrics
+ kbio: boolean;
+ // Enable mouse biometrics
+ mbio: boolean;
+ // Enable touch biometrics
+ tbio: boolean;
+}
+
+export interface SessionOptions {
+ userAgent?: string;
+ proxy?: string;
+}
+
+let parseToken = (token: string): TokenInfo =>
+ Object.fromEntries(
+ token
+ .split("|")
+ .map((v) => v.split("=").map((v) => decodeURIComponent(v)))
+ );
+
+export class Session {
+ public token: string;
+ public tokenInfo: TokenInfo;
+ private userAgent: string;
+ private proxy: string;
+
+ constructor(
+ token: string | GetTokenResult,
+ sessionOptions?: SessionOptions
+ ) {
+ if (typeof token === "string") {
+ this.token = token;
+ } else {
+ this.token = token.token;
+ }
+ if (!this.token.startsWith("token="))
+ this.token = "token=" + this.token;
+
+ this.tokenInfo = parseToken(this.token);
+ this.tokenInfo.mbio = typeof(token) !== "string" ? token.mbio ?? false : false
+ this.userAgent = sessionOptions?.userAgent || util.DEFAULT_USER_AGENT;
+ this.proxy = sessionOptions?.proxy;
+ }
+
+ async getChallenge(): Promise<Challenge> {
+ let res = await http(
+ this.tokenInfo.surl,
+ {
+ path: "/fc/gfct/",
+ method: "POST",
+ body: util.constructFormData({
+ sid: this.tokenInfo.r,
+ render_type: "canvas",
+ token: this.tokenInfo.token,
+ analytics_tier: this.tokenInfo.at,
+ "data%5Bstatus%5D": "init",
+ lang: "en",
+ apiBreakerVersion: "green"
+ }),
+ headers: {
+ "User-Agent": this.userAgent,
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Accept-Language": "en-US,en;q=0.9",
+ "Sec-Fetch-Site": "same-origin",
+ "Referer": this.getEmbedUrl()
+ },
+ },
+ this.proxy
+ );
+
+ let data = JSON.parse(res.body.toString());
+ data.token = this.token;
+ data.tokenInfo = this.tokenInfo;
+
+ if (data.game_data.gameType == 1) {
+ return new Challenge1(data, {
+ proxy: this.proxy,
+ userAgent: this.userAgent,
+ });
+ } else if (data.game_data.gameType == 3) {
+ return new Challenge3(data, {
+ proxy: this.proxy,
+ userAgent: this.userAgent,
+ });
+ } else if (data.game_data.gameType == 4) {
+ return new Challenge4(data, {
+ proxy: this.proxy,
+ userAgent: this.userAgent,
+ });
+ } else {
+ throw new Error(
+ "Unsupported game type: " + data.game_data.gameType
+ );
+ }
+ //return res.body.toString()
+ }
+
+ getEmbedUrl(): string {
+ return `${this.tokenInfo.surl}/fc/gc/?${util.constructFormData(
+ this.tokenInfo
+ )}`;
+ }
+}
diff --git a/g4f/Provider/npm/node_modules/funcaptcha/src/util.ts b/g4f/Provider/npm/node_modules/funcaptcha/src/util.ts
new file mode 100644
index 00000000..da9412ac
--- /dev/null
+++ b/g4f/Provider/npm/node_modules/funcaptcha/src/util.ts
@@ -0,0 +1,197 @@
+import fingerprint from "./fingerprint";
+import murmur from "./murmur";
+import crypt from "./crypt";
+
+interface TimestampData {
+ cookie: string;
+ value: string;
+}
+
+const DEFAULT_USER_AGENT =
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36";
+
+let apiBreakers = {
+ v1: {
+ 3: {
+ default: (c) => c,
+ method_1: (c) => ({ x: c.y, y: c.x }),
+ method_2: (c) => ({ x: c.x, y: (c.y + c.x) * c.x }),
+ method_3: (c) => ({ a: c.x, b: c.y }),
+ method_4: (c) => [c.x, c.y],
+ method_5: (c) => [c.y, c.x].map((v) => Math.sqrt(v)),
+ },
+ 4: {
+ default: (c) => c
+ }
+ },
+ v2: {
+ 3: {
+ value: {
+ alpha: (c) => ({ x: c.x, y: (c.y + c.x) * c.x, px: c.px, py: c.py }),
+ beta: (c) => ({ x: c.y, y: c.x, py: c.px, px: c.py }),
+ gamma: (c) => ({ x: c.y + 1, y: -c.x, px: c.px, py: c.py }),
+ delta: (c) => ({ x: c.y + 0.25, y: c.x + 0.5, px: c.px, py: c.py }),
+ epsilon: (c) => ({ x: c.x * 0.5, y: c.y * 5, px: c.px, py: c.py }),
+ zeta: (c) => ({ x: c.x + 1, y: c.y + 2, px: c.px, py: c.py }),
+ method_1: (c) => ({ x: c.x, y: c.y, px: c.px, py: c.py }),
+ method_2: (c) => ({ x: c.y, y: (c.y + c.x) * c.x, px: c.px, py: c.py }),
+ method_3: (c) => ({ x: Math.sqrt(c.x), y: Math.sqrt(c.y), px: c.px, py: c.py }),
+ },
+ key: {
+ alpha: (c) => [c.y, c.px, c.py, c.x],
+ beta: (c) => JSON.stringify({ x: c.x, y: c.y, px: c.px, py: c.py }),
+ gamma: (c) => [c.x, c.y, c.px, c.py].join(" "),
+ delta: (c) => [1, c.x, 2, c.y, 3, c.px, 4, c.py],
+ epsilon: (c) => ({ answer: { x: c.x, y: c.y, px: c.px, py: c.py } }),
+ zeta: (c) => [c.x, [c.y, [c.px, [c.py]]]],
+ method_1: (c) => ({ a: c.x, b: c.y, px: c.px, py: c.py }),
+ method_2: (c) => [c.x, c.y],
+ method_3: (c) => [c.y, c.x],
+ }
+ },
+ 4: {
+ value: {
+ // @ts-ignore
+ alpha: (c) => ({ index: String(c.index) + 1 - 2 }),
+ beta: (c) => ({ index: -c.index }),
+ gamma: (c) => ({ index: 3 * (3 - c.index) }),
+ delta: (c) => ({ index: 7 * c.index }),
+ epsilon: (c) => ({ index: 2 * c.index }),
+ zeta: (c) => ({ index: c.index ? 100 / c.index : c.index }),
+ va: (c) => ({ index: c.index + 3 }),
+ vb: (c) => ({ index: -c.index }),
+ vc: (c) => ({ index: 10 - c.index }),
+ vd: (c) => ({ index: 3 * c.index }),
+ },
+ key: {
+ alpha: (c) => [Math.round(100 * Math.random()), c.index, Math.round(100 * Math.random())],
+ beta: (c) => ({ size: 50 - c.index, id: c.index, limit: 10 * c.index, req_timestamp: Date.now() }),
+ gamma: (c) => c.index,
+ delta: (c) => ({ index: c.index }),
+ epsilon: (c) => {
+ const arr: any = [];
+ const len = Math.round(5 * Math.random()) + 1;
+ const rand = Math.round(Math.random() * len);
+ for (let i = 0; i < len; i++) {
+ arr.push(i === rand ? c.index : Math.round(10 * Math.random()));
+ }
+ arr.push(rand);
+ return arr;
+ },
+ zeta: (c) => Array(Math.round(5 * Math.random()) + 1).concat(c.index),
+ ka: (c) => c.index,
+ kb: (c) => [c.index],
+ kc: (c) => ({ guess: c.index }),
+ }
+ }
+ }
+}
+
+interface TileLoc {
+ x: number;
+ y: number;
+ px: number;
+ py: number;
+}
+function tileToLoc(tile: number): TileLoc {
+ let xClick = (tile % 3) * 100 + (tile % 3) * 3 + 3 + 10 + Math.floor(Math.random() * 80);
+ let yClick = Math.floor(tile / 3) * 100 + Math.floor(tile / 3) * 3 + 3 + 10 + Math.floor(Math.random() * 80);
+ return {
+ x: xClick,
+ y: yClick,
+ px: xClick / 300,
+ py: yClick / 200,
+ }
+}
+
+function constructFormData(data: {}): string {
+ return Object.keys(data)
+ .filter((v) => data[v] !== undefined)
+ .map((k) => `${k}=${encodeURIComponent(data[k])}`)
+ .join("&");
+}
+
+function random(): string {
+ return Array(32)
+ .fill(0)
+ .map(() => "0123456789abcdef"[Math.floor(Math.random() * 16)])
+ .join("");
+}
+
+function getTimestamp(): TimestampData {
+ const time = (new Date()).getTime().toString()
+ const value = `${time.substring(0, 7)}00${time.substring(7, 13)}`
+
+ return { cookie: `timestamp=${value};path=/;secure;samesite=none`, value }
+}
+
+function getBda(userAgent: string, opts: object): string {
+ let fp = fingerprint.getFingerprint();
+ let fe = fingerprint.prepareFe(fp);
+
+ let bda = [
+ { key: "api_type", value: "js" },
+ { key: "p", value: 1 },
+ { key: "f", value: murmur(fingerprint.prepareF(fingerprint), 31) },
+ {
+ key: "n",
+ value: Buffer.from(
+ Math.round(Date.now() / (1000 - 0)).toString()
+ ).toString("base64"),
+ },
+ { key: "wh", value: `${random()}|${random()}` },
+ {
+ "key": "enhanced_fp",
+ "value": fingerprint.getEnhancedFingerprint(fp, userAgent, opts)
+ },
+ { key: "fe", value: fe },
+ { key: "ife_hash", value: murmur(fe.join(", "), 38) },
+ { key: "cs", value: 1 },
+ {
+ key: "jsbd",
+ value: JSON.stringify({
+ HL: 4,
+ DT: "",
+ NWD: "false",
+ DOTO: 1,
+ DMTO: 1,
+ }),
+ },
+ ];
+
+ let time = new Date().getTime() / 1000;
+ let key = userAgent + Math.round(time - (time % 21600));
+
+ let s = JSON.stringify(bda);
+ let encrypted = crypt.encrypt(s, key);
+ return Buffer.from(encrypted).toString("base64");
+}
+
+function solveBreaker(v2: boolean, breaker: { value: string[], key: string } | string = "default", gameType: number, value: object) {
+ if (!v2 && typeof breaker === "string")
+ return (apiBreakers.v1[gameType][breaker || "default"] || ((v: any) => v))(value)
+
+ if (typeof breaker !== "string") {
+ let b = apiBreakers.v2[gameType]
+ let v = breaker.value.reduce((acc, cur) => {
+ if (b.value[cur])
+ return b.value[cur](acc)
+ else
+ return cur
+ }, value)
+ return b.key[breaker.key](v)
+ } else {
+ return value
+ }
+}
+
+export default {
+ DEFAULT_USER_AGENT,
+ tileToLoc,
+ constructFormData,
+ getBda,
+ apiBreakers,
+ getTimestamp,
+ random,
+ solveBreaker
+};