Skip to content

Commit

Permalink
Merge pull request #13 from 2captcha/RC-2646
Browse files Browse the repository at this point in the history
RC-2646 Add Grid method
  • Loading branch information
dzmitry-duboyski authored Jul 24, 2024
2 parents 6ea78f5 + e03686c commit 9e75fe2
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 34 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The easiest way to quickly integrate the [2Captcha](https://2captcha.com/) captc
- [MTCaptcha](#mtcaptcha)
- [Friendly Captcha](#friendly-captcha)
- [Bounding Box Method](#bounding-box-method)
- [Grid](#grid)
- [Other methods](#other-methods)
- [goodReport](#goodreport)
- [badReport](#badreport)
Expand Down Expand Up @@ -440,7 +441,7 @@ solver.coordinates({
})
```

### Bounding Box Method:
### Bounding Box Method

<sup>[API method description.](https://2captcha.com/2captcha-api#bounding_box)</sup>

Expand All @@ -462,6 +463,28 @@ solver.boundingBox({
})
```

### Grid

<sup>[API method description.](https://2captcha.com/2captcha-api#grid)</sup>

This method allows to solve any captcha where image can be divided into equal parts like reCAPTCHA V2 or hCaptcha. A grid is applied above the image. And you receive the numbers clicked boxes.

> [!IMPORTANT]
> You must to send instruction `imginstructions` or `textinstructions`.
```js
solver.grid({
body: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAR4AAACwCAIAAAB...",
textinstructions: "Select cars in the image"
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
})
```

## Other methods

### goodReport
Expand Down
14 changes: 8 additions & 6 deletions examples/boundingBox.js

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions examples/grid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const TwoCaptcha = require("../dist/index.js");
require('dotenv').config();
const APIKEY = process.env.APIKEY
const solver = new TwoCaptcha.Solver(APIKEY);
const fs = require('fs')
const imageBase64 = fs.readFileSync("./media/recaptchaGrid3x3.jpg", "base64")

solver.grid({
body: imageBase64,
textinstructions: "Select cars in the image"
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
})
27 changes: 27 additions & 0 deletions examples/grid_options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const TwoCaptcha = require("../dist/index.js");
require("dotenv").config();
const APIKEY = process.env.APIKEY;
const solver = new TwoCaptcha.Solver(APIKEY);
const fs = require("fs");
const imageBase64 = fs.readFileSync("./media/recaptchaGrid4x4.jpg", "base64");
const imageInstructionsBase64 = fs.readFileSync("./media/recaptchaGridImginstructions4x4.jpg", "base64");

solver.grid({
body: imageBase64,
textinstructions: "Select all squares with stairs",
imginstructions: imageInstructionsBase64,
cols: 4,
rows: 4,
minClicks: 2,
maxClicks: 6,
lang: "en",
canSkip: 1,
// pingback: '123.123.123.123' /* More info about pingback https://2captcha.com/setting/pingback */
// previousId: '123456789'
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
Binary file added examples/media/boundingBox.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/media/boundingBoxImginstructions.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/media/recaptchaGrid3x3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/media/recaptchaGrid4x4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
125 changes: 100 additions & 25 deletions src/structs/2captcha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as utils from "../utils/generic"
import getProviderData from "./providers/providers"
import { softId } from "./constants/constants"
import checkCaptchaParams from "../utils/checkCaptchaParams"
import renameParams from "../utils/renameParams"


const provider = getProviderData ()
Expand Down Expand Up @@ -44,15 +45,15 @@ export interface paramsHCaptcha {

// FixMe:data[key] - how to send this parameter
export interface paramsFunCaptcha {
publickey: string,
pageurl: string,
surl?: string,
header_acao?: boolean,
pingback?: string,
proxy?: string,
proxytype?: string,
userAgent?: string,
data?: string
publickey: string,
pageurl: string,
surl?: string,
header_acao?: boolean,
pingback?: string,
proxy?: string,
proxytype?: string,
userAgent?: string,
data?: string
}

export interface paramsImageCaptcha {
Expand Down Expand Up @@ -221,6 +222,21 @@ export interface paramsBoundingBox {
imginstructions?: string,
}

export interface paramsGrid {
body: string,
recaptcha: 1,
rows?: number
cols?: number
minСlicks?: number,
maxСlicks?: number,
previousId?: string,
textinstructions?: string,
imginstructions?: string,
canSkip?: number,
lang?: string,
pingback?: string,
}

/**
* An object containing properties of the captcha solution.
* @typedef {Object} CaptchaAnswer
Expand Down Expand Up @@ -398,7 +414,7 @@ export class Solver {
}

/**
* Solves a hCaptcha, returning the result as a string.
* ### Solves a hCaptcha
*
* [Read more about other hCaptcha parameters](https://2captcha.com/2captcha-api#solving_hcaptcha).
*
Expand Down Expand Up @@ -452,7 +468,9 @@ export class Solver {
}

/**
* Solves a GeeTest Captcha. [Read more about parameters and solving for Geetest captcha](https://2captcha.com/2captcha-api#solving_geetest).
* ### Solves a GeeTest Captcha.
*
* [Read more about parameters and solving for Geetest captcha](https://2captcha.com/2captcha-api#solving_geetest).
*
* @param {{ gt, challenge, api_server, offline, new_captcha,
* pageurl, pingback, proxy, proxytype, userAgent }} params
Expand Down Expand Up @@ -529,7 +547,6 @@ export class Solver {
/**
* ### Solves a GeeTest V4 Captcha.
*
*
* This method accepts an object with the following fields: `pageurl`, `captcha_id`, `pingback`, `proxy`, `proxytype`, `userAgent`.
* The `pageurl` and `captcha_id` fields are required.
*
Expand Down Expand Up @@ -581,7 +598,8 @@ export class Solver {
}

/**
* Method for sending Yandex Smart Captcha.
* ### Method for sending Yandex Smart Captcha.
*
* This method accepts an object with the following fields: `pageurl`, `sitekey`, `pingback`, `proxy`, `proxytype`.
* The `pageurl` and `sitekey` fields are required.
*
Expand Down Expand Up @@ -633,7 +651,9 @@ export class Solver {
}

/**
* Solves a image-based captcha. [Read more about parameters for image captcha](https://2captcha.com/2captcha-api#solving_normal_captcha).
* ### Solves a image-based captcha.
*
* [Read more about parameters for image captcha](https://2captcha.com/2captcha-api#solving_normal_captcha).
*
* @param {{ body,
* phrase,
Expand Down Expand Up @@ -984,17 +1004,6 @@ export class Solver {
}
}

/**
* pageurl: string,
captchakey: string,
api_server?: string,
version?: string,
header_acao?: boolean,
pingback?: string,
proxy?: string,
proxytype?: string,
*/

/**
* ### Solves Capy Puzzle captcha
*
Expand Down Expand Up @@ -1342,6 +1351,72 @@ public async boundingBox(params: paramsBoundingBox): Promise<CaptchaAnswer> {
}
}


/**
* ### Grid method
*
* The method can be used to bypass tasks where a grid is applied to an image and you need to click on grid tiles, like reCAPTCHA or hCaptcha images.
*
* @param {{ body, textinstructions, imginstructions, rows, cols, minСlicks, maxСlicks, previousId, canSkip, lang, pingback}} params Parameters Grid Method as an object.
* @param {string} params.body `Base64`- encoded captcha image.
* @param {string} params.textinstructions Text will be shown to worker to help him to select object on the image correctly. For example: "*Select cars in the image*". **Optional parameter**, if the instruction already exists in the form of the `imginstructions`.
* @param {string} params.imginstructions Image with instruction for worker to help him to select object on the image correctly. The image must be encoded in `Base64` format. **Optional parameter**, if the instruction already exists in the form of the `textinstructions`.
* @param {number} params.rows Number of rows in grid captcha.
* @param {number} params.cols Number of columns in grid captcdha.
* @param {number} params.minСlicks The minimum number of tiles that must be selected. Can't be more than `rows` * `cols`.
* @param {number} params.maxСlicks The maximum number of tiles that can be selected on the image.
* @param {string} params.previousId Id of your previous request with the same captcha challenge.
* @param {number} params.canSkip Set the value to `1` only if it's possible that there's no images matching to the instruction. We'll provide a button "No matching images" to worker and you will receive `No_matching_images` as answer.
* @param {string} params.lang Language code. [See the list of supported languages](https://2captcha.com/2captcha-api#language).
* @param {string} params.pingback params.pingback URL for pingback (callback) response that will be sent when captcha is solved. URL should be registered on the server. [More info here](https://2captcha.com/2captcha-api#pingback).
*
* @example
* solver.grid({
* body: 'iVBORw0KGgoAAAANSUhEUgAAAcIA...',
* textinstructions: "Select cars in the image",
* imginstructions: '/9j/4AAQSkZJRgABAQEA...'
* })
* .then((res) => {
* console.log(res);
* })
* .catch((err) => {
* console.log(err);
* })
*/
public async grid(params: paramsGrid): Promise<CaptchaAnswer> {
checkCaptchaParams(params, "grid")

params = await renameParams(params)

const payload = {
...params,
method: "base64",
recaptcha: 1,
...this.defaultPayload,
}

const URL = this.in
const response = await fetch(URL, {
body: JSON.stringify( payload ),
method: "post",
headers: {'Content-Type': 'application/json'}
})
const result = await response.text()

let data;
try {
data = JSON.parse(result)
} catch {
throw new APIError(result)
}

if (data.status == 1) {
return this.pollResponse(data.request)
} else {
throw new APIError(data.request)
}
}

/**
* Reports a captcha as correctly solved.
*
Expand Down
17 changes: 15 additions & 2 deletions src/utils/checkCaptchaParams.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Captcha methods for which parameter checking is available
const supportedMethods = ["userrecaptcha", "hcaptcha", "geetest", "geetest_v4","yandex","funcaptcha","lemin","amazon_waf",
"turnstile", "base64", "capy","datadome", "cybersiara", "mt_captcha", "bounding_box", 'friendly_captcha']
"turnstile", "base64", "capy","datadome", "cybersiara", "mt_captcha", "bounding_box", 'friendly_captcha', 'grid']

// Names of required fields that must be contained in the parameters captcha
const recaptchaRequiredFields = ['pageurl','googlekey']
Expand All @@ -20,6 +20,7 @@ const сyberSiARARequiredFields = ['pageurl', 'master_url_id', 'userAgent']
const mtСaptchaRequiredFields = ['pageurl', 'sitekey']
const boundingBoxRequiredFields = ['image'] // and textinstructions or imginstructions
const friendlyCaptchaFields = ['pageurl','sitekey']
const gridRequiredFields = ['body'] // and textinstructions or imginstructions

/**
* Getting required arguments for a captcha.
Expand Down Expand Up @@ -61,6 +62,9 @@ const getRequiredFildsArr = (method: string):Array<string> => {
case "base64":
requiredFieldsArr = base64RequiredFields
break;
case "grid":
requiredFieldsArr = gridRequiredFields
break;
case "capy":
requiredFieldsArr = capyPuzzleRequiredFields
break;
Expand Down Expand Up @@ -116,7 +120,16 @@ export default function checkCaptchaParams(params: Object, method: string) {
isCorrectCaptchaParams = true
} else {
isCorrectCaptchaParams = false
throw new Error(`Error when check params captcha.\nNot found "textinstructions" or "imginstructions" field in the Object. One of this field is required for "bounding_box" method. Please add field "textinstructions" or "imginstructions" in object and try again.\nPlease correct your code for the "bounding_box" method according to the code examples`)
throw new Error(`Error when check params captcha.\nNot found "textinstructions" or "imginstructions" field in the Object. One of this field is required for "bounding_box" method. Please add field "textinstructions" or "imginstructions" in object and try again.\nPlease correct your code for the "bounding_box" method according to the code examples.`)
}
}

if(method === "grid") {
if(params.hasOwnProperty('textinstructions') || params.hasOwnProperty('imginstructions')) {
isCorrectCaptchaParams = true
} else {
isCorrectCaptchaParams = false
throw new Error(`Error when check params captcha.\nNot found "textinstructions" or "imginstructions" field in the Object. One of this field is required for "Grid" method. Please add field "textinstructions" or "imginstructions" in object and try again.\nPlease correct your code for the "Grid" method according to the code examples.`)
}
}

Expand Down
35 changes: 35 additions & 0 deletions src/utils/renameParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
*
* ### Renaming captcha parameters
*
* Description: parameter names used in the API may differ from those used in the library, in such cases parameter names are renamed in accordance with those used in the API.
*
* @param params - captcha parameters as an object
* @returns returns new object with renamed params
*
*/
export default function renameParams(params: any) {
let newParams: any = new Object();

/**
* Captcha parameters that need to be renamed before sent to the API.
*/
const replaceParams: any = {
"cols" : "recaptchacols",
"rows" : "recaptcharows",
"minClicks" : "min_clicks",
"maxClicks" : "max_clicks",
"canSkip" : "can_no_answer",
"previousId" : "previousID"
}

for(let key in params) {
if(replaceParams.hasOwnProperty(key)) {
newParams[replaceParams[key]] = params[key]
} else {
newParams[key] = params[key]
}
}

return newParams
}

0 comments on commit 9e75fe2

Please sign in to comment.