Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ on the target page.
| GeeTest v4 | ✅ | ✅ |
| Image to Text | ✅ | ❌ |
| Yandex SmartCaptcha | ✅ | ✅ |
| Yandex SmartCaptcha Image | ✅ | ❌ |
| Coordinates (click captcha) | ✅ | ❌ |
| Tencent | ✅ | ✅ |

Expand Down Expand Up @@ -450,10 +451,10 @@ With proxy, use `Tasks.GeeTest` (same proxy fields as reCAPTCHA v2).
<sup>[API method description.](https://captcha-solver.com/en/docs/captcha-types#yandex-smartcaptcha)</sup>

Use this method to solve the token-based Yandex SmartCaptcha and obtain a token
for the widget on the target page. Use the coordinates method for image challenges.
for the widget on the target page. Use the dedicated image method for image challenges.

`Tasks.YandexSmartCaptchaTaskProxyless` / `Tasks.YandexSmartCaptchaTask` -- token-based
challenge. For the image challenge instead, use `Tasks.CoordinatesTask` with
challenge.

| Field | Required | Description |
|---|---|---|
Expand Down Expand Up @@ -481,15 +482,41 @@ console.log(result.token);
With proxy, use `Tasks.YandexSmartCaptchaTask` (same proxy fields as reCAPTCHA v2,
plus the `https` option above).

#### Image challenge

Use `Tasks.YandexSmartCaptchaImageTask` when SmartCaptcha shows a main image and
a separate strip indicating the symbols to click. Encode both images as base64;
the SDK sends API v2 type `SmartCaptchaTask`, and the returned coordinates are
already ordered for clicking.

| Field | Required | Description |
|---|---|---|
| `image` | yes | Main captcha image, base64-encoded. |
| `imgInstructions` | yes | Click-order instruction image, base64-encoded. |
| `comment` | no | Optional text hint for the worker. |

```javascript
import fs from 'node:fs';
import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk';

const task = new Tasks.YandexSmartCaptchaImageTask({
image: fs.readFileSync('captcha.png').toString('base64'),
imgInstructions: fs.readFileSync('instructions.png').toString('base64')
});
const result = await new CaptchaClient({ clientKey: 'your_api_key' }).solve(task);
console.log(result.coordinates);
```

See the runnable [async](examples/async/yandex_smartcaptcha_image.js) and
[promise-chain](examples/sync/yandex_smartcaptcha_image.js) examples.

### Coordinates (click captcha)

<sup>[API method description.](https://captcha-solver.com/en/docs/captcha-types#coordinates)</sup>

Use this method to identify points that a worker should click in an image. It
supports generic click captchas and the image version of Yandex SmartCaptcha.
Use this method to identify points that a worker should click in a generic image.

`Tasks.CoordinatesTask`. Used both for generic "click on X" captchas and for
directly.
`Tasks.CoordinatesTask`.

| Field | Required | Description |
|---|---|---|
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ twice the balance.
| GeeTest v3 | [geetest_v3.js](async/geetest_v3.js) | [geetest_v3.js](sync/geetest_v3.js) | `GeeTestProxyless`, `GeeTest` | `challenge`, `validate`, `seccode` |
| GeeTest v4 | [geetest_v4.js](async/geetest_v4.js) | [geetest_v4.js](sync/geetest_v4.js) | `GeeTestProxyless`, `GeeTest` | `captcha_id`, `lot_number`, `pass_token`, `gen_time`, `captcha_output` |
| Yandex SmartCaptcha | [yandex_smartcaptcha.js](async/yandex_smartcaptcha.js) | [yandex_smartcaptcha.js](sync/yandex_smartcaptcha.js) | `YandexSmartCaptchaTaskProxyless`, `YandexSmartCaptchaTask` | `token` |
| Yandex SmartCaptcha Image | [yandex_smartcaptcha_image.js](async/yandex_smartcaptcha_image.js) | [yandex_smartcaptcha_image.js](sync/yandex_smartcaptcha_image.js) | `YandexSmartCaptchaImageTask` | `coordinates` |
| Tencent | [tencent.js](async/tencent.js) | [tencent.js](sync/tencent.js) | `TencentTaskProxyless`, `TencentTask` | `appid`, `ret`, `ticket`, `randstr` |
| Image to Text | [image_to_text.js](async/image_to_text.js) | [image_to_text.js](sync/image_to_text.js) | `ImageToText` | `text` |
| Coordinates (click) | [coordinates.js](async/coordinates.js) | [coordinates.js](sync/coordinates.js) | `CoordinatesTask` | `coordinates` |
Expand Down
Binary file added examples/assets/yandex-smartcaptcha-image.png
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.
1 change: 1 addition & 0 deletions examples/async/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ spends balance twice. Comment out the one you don't need.
| [geetest_v3.js](geetest_v3.js) | Fetches a fresh `challenge`, then solves GeeTest v3 | Fetch · Proxyless · With proxy |
| [geetest_v4.js](geetest_v4.js) | Solves GeeTest v4 via `captcha_id` | Proxyless · With proxy |
| [yandex_smartcaptcha.js](yandex_smartcaptcha.js) | Solves Yandex SmartCaptcha (token challenge) | Proxyless · With proxy |
| [yandex_smartcaptcha_image.js](yandex_smartcaptcha_image.js) | Solves Yandex SmartCaptcha image challenge | single task |
| [tencent.js](tencent.js) | Solves a Tencent captcha | Proxyless · With proxy |
| [image_to_text.js](image_to_text.js) | Recognises text on a captcha image | Basic · Advanced · With language pool |

Expand Down
26 changes: 26 additions & 0 deletions examples/async/yandex_smartcaptcha_image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/** Solve the image-based Yandex SmartCaptcha challenge. */
import 'dotenv/config';
import fs from 'node:fs';
import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk';

const captchaSolver = new CaptchaClient({
clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'
});

const image = fs.readFileSync(
new URL('../assets/yandex-smartcaptcha-image.png', import.meta.url)
).toString('base64');
const imgInstructions = fs.readFileSync(
new URL('../assets/yandex-smartcaptcha-instructions.png', import.meta.url)
).toString('base64');

try {
const task = new Tasks.YandexSmartCaptchaImageTask({
image,
imgInstructions
});
const result = await captchaSolver.solve(task);
console.log('Coordinates in click order:', result.coordinates);
} catch (error) {
console.error(error);
}
6 changes: 2 additions & 4 deletions examples/sync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Errors are handled per chain with a trailing `.catch()`; a failing block does no
| [geetest_v3.js](geetest_v3.js) | Fetches a fresh `challenge`, then solves GeeTest v3 | one nested chain |
| [geetest_v4.js](geetest_v4.js) | Solves GeeTest v4 via `captcha_id` | Proxyless · With proxy |
| [yandex_smartcaptcha.js](yandex_smartcaptcha.js) | Solves Yandex SmartCaptcha (token challenge) | Proxyless · With proxy |
| [yandex_smartcaptcha_image.js](yandex_smartcaptcha_image.js) | Solves Yandex SmartCaptcha image challenge | single chain |
| [tencent.js](tencent.js) | Solves a Tencent captcha | Proxyless · With proxy |
| [image_to_text.js](image_to_text.js) | Recognises text on a captcha image | Basic · Advanced · With language pool |

Expand Down Expand Up @@ -154,10 +155,7 @@ directory** and the script runs as-is. Solution: `text`.
Three chains, no proxy variant, all active. *Basic* (`basicTask`) submits the shipped grid captcha
(`coordinates-captcha.png`) plus a `comment` telling the worker what to click. *Advanced*
(`advancedTask`) solves a different image, a real traffic-lights photo (`traffic-lights.png`), with
`traffic-lights-instructions.png` as `imgInstructions` and `minClicks`/`maxClicks` limits. *Yandex
SmartCaptcha image mode* (`yandexTask`) reuses the same `CoordinatesTask` with
click-order hint is baked into the image itself, so no separate `imgInstructions` is passed — this is
how the image variant of Yandex SmartCaptcha is solved.
`traffic-lights-instructions.png` as `imgInstructions` and `minClicks`/`maxClicks` limits.

Reads its images via
`new URL(..., import.meta.url)`, so the path holds **regardless of the working directory** and the
Expand Down
28 changes: 28 additions & 0 deletions examples/sync/yandex_smartcaptcha_image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/** Solve the image-based Yandex SmartCaptcha challenge using promises. */
import 'dotenv/config';
import fs from 'node:fs';
import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk';

const captchaSolver = new CaptchaClient({
clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'
});

const image = fs.readFileSync(
new URL('../assets/yandex-smartcaptcha-image.png', import.meta.url)
).toString('base64');
const imgInstructions = fs.readFileSync(
new URL('../assets/yandex-smartcaptcha-instructions.png', import.meta.url)
).toString('base64');

const task = new Tasks.YandexSmartCaptchaImageTask({
image,
imgInstructions
});

captchaSolver.solve(task)
.then((result) => {
console.log('Coordinates in click order:', result.coordinates);
})
.catch((error) => {
console.error(error);
});
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type {
YandexSmartCaptchaTaskProxylessParams,
YandexSmartCaptchaTaskParams,
CoordinatesTaskParams,
YandexSmartCaptchaImageTaskParams,
TencentTaskProxylessParams,
TencentTaskParams
} from './tasks.js';
Expand Down
30 changes: 29 additions & 1 deletion src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,6 @@ export interface CoordinatesTaskParams {

/**
* Click captcha: the worker marks points in an image (`CoordinatesTask`).
* Used for generic "click on X" captchas and the image version of Yandex SmartCaptcha.
* Solution: {@link CoordinatesSolution}.
*
* @see https://captcha-solver.com/en/docs/captcha-types#coordinates
Expand All @@ -564,6 +563,35 @@ export class CoordinatesTask extends BaseTask<CoordinatesSolution> {
}
}

/* ------------------------------------------------------------------ */
/* Yandex SmartCaptcha image */
/* ------------------------------------------------------------------ */

/** Parameters of {@link YandexSmartCaptchaImageTask}. */
export interface YandexSmartCaptchaImageTaskParams {
/** The main SmartCaptcha challenge image, base64-encoded. */
image: string;
/** The SmartCaptcha click-order instruction image, base64-encoded. */
imgInstructions: string;
/** Optional text hint shown to the worker. */
comment?: string | null;
}

/**
* Image-based Yandex SmartCaptcha challenge.
*
* Serialized as the API v2 `SmartCaptchaTask`. Coordinates are returned in
* the order in which the symbols must be clicked.
* Solution: {@link CoordinatesSolution}.
*
* @see https://captcha-solver.com/en/docs/captcha-types#yandex-smartcaptcha
*/
export class YandexSmartCaptchaImageTask extends BaseTask<CoordinatesSolution> {
constructor({ image, imgInstructions, comment = null }: YandexSmartCaptchaImageTaskParams) {
super({ type: 'SmartCaptchaTask', image, imgInstructions, comment });
}
}

/* ------------------------------------------------------------------ */
/* Tencent */
/* ------------------------------------------------------------------ */
Expand Down
34 changes: 33 additions & 1 deletion tests/integration/coordinates.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@ import { Tasks } from '@captcha-solver-api/javascript-sdk';
import { describeIntegration, createClient } from './helpers.js';

const imagePath = new URL('../../examples/assets/coordinates-captcha.png', import.meta.url);
const yandexImagePath = new URL(
'../../examples/assets/yandex-smartcaptcha-image.png',
import.meta.url
);
const yandexInstructionsPath = new URL(
'../../examples/assets/yandex-smartcaptcha-instructions.png',
import.meta.url
);

// The sample is a 4x4 grid captcha whose own header reads "Select all squares
// with street signs". The comment repeats that instruction for the worker,
// because nothing guarantees they read the text baked into the image.
const comment = 'click on all squares with street signs';

describeIntegration('Coordinates against the real API', () => {
describeIntegration('Image coordinate tasks against the real API', () => {
test('solve returns the clicked points', async () => {
const image = fs.readFileSync(imagePath);

Expand Down Expand Up @@ -58,4 +66,28 @@ describeIntegration('Coordinates against the real API', () => {
}
}, 130000);

test('Yandex SmartCaptcha image returns ordered click coordinates', async () => {
const image = fs.readFileSync(yandexImagePath);
const instructions = fs.readFileSync(yandexInstructionsPath);
const task = new Tasks.YandexSmartCaptchaImageTask({
image: image.toString('base64'),
imgInstructions: instructions.toString('base64')
});

const solution = await createClient().solve(task);

expect(solution.coordinates).toHaveLength(5);

const width = image.readUInt32BE(16);
const height = image.readUInt32BE(20);
for (const point of solution.coordinates) {
expect(typeof point.x).toBe('number');
expect(typeof point.y).toBe('number');
expect(point.x).toBeGreaterThanOrEqual(0);
expect(point.y).toBeGreaterThanOrEqual(0);
expect(point.x).toBeLessThanOrEqual(width);
expect(point.y).toBeLessThanOrEqual(height);
}
}, 130000);

});
1 change: 1 addition & 0 deletions tests/unit/public-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const TASK_CLASSES = [
'YandexSmartCaptchaTaskProxyless',
'YandexSmartCaptchaTask',
'CoordinatesTask',
'YandexSmartCaptchaImageTask',
'TencentTaskProxyless',
'TencentTask'
];
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/yandex_smartcaptcha_image.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Tasks } from '@captcha-solver-api/javascript-sdk';

describe('YandexSmartCaptchaImageTask', () => {
test('serializes as an API v2 SmartCaptchaTask with both images', () => {
const task = new Tasks.YandexSmartCaptchaImageTask({
image: 'captcha-image',
imgInstructions: 'instruction-image',
comment: 'click the symbols in the specified order'
});

expect(task.toDict()).toEqual({
type: 'SmartCaptchaTask',
image: 'captcha-image',
imgInstructions: 'instruction-image',
comment: 'click the symbols in the specified order'
});
});
});
Loading