Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
coverage
64 changes: 45 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,47 @@ Via [NPM](https://www.npmjs.com/package/@zennolab_com/capmonstercloud-client):
## Usage with Node (with or without Typescript)

```javascript
const { CapMonsterCloudClientFactory, ClientOptions, RecaptchaV2Request } = require('@zennolab_com/capmonstercloud-client');
const { CapMonsterCloudClientFactory, ClientOptions } = require('@zennolab_com/capmonstercloud-client');

async function run() {
const cmcClient = CapMonsterCloudClientFactory.Create(new ClientOptions({ clientKey: '<your capmonster.cloud API key>' }));
console.log(await cmcClient.getBalance());

console.log(await cmcClient.solve({
task: {
type: 'NoCaptchaTaskProxyless',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
},
}));
}

run().catch(console.error);
```

You can also use the lower-level API if you want to manage polling yourself:

```javascript
const createdTask = await cmcClient.createTask({
task: {
type: 'NoCaptchaTaskProxyless',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
},
});

const result = await cmcClient.getTaskResult(createdTask.taskId);
```

The plain payload API validates required fields before sending a request. Invalid input throws `ValidationError`.

The legacy class-based API is still supported:

```javascript
const { CapMonsterCloudClientFactory, ClientOptions, RecaptchaV2Request } = require('@zennolab_com/capmonstercloud-client');

async function runLegacy() {
const cmcClient = CapMonsterCloudClientFactory.Create(new ClientOptions({ clientKey: '<your capmonster.cloud API key>' }));
const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
Expand All @@ -25,15 +60,7 @@ async function run() {
console.log(await cmcClient.Solve(recaptchaV2Request));
}

run()
.then(() => {
console.log('DONE');
process.exit(0);
})
.catch((err) => {
console.error(err);
process.exit(1);
});
runLegacy().catch(console.error);
```

## Usage with Browser (with or without Typescript)
Expand All @@ -42,26 +69,25 @@ Browser implementation use [fetch](https://caniuse.com/fetch) instead of [http(s
For browser usage you need some Module Bundler (e.g. [Webpack](https://webpack.js.org/)).

```javascript
import { CapMonsterCloudClientFactory, ClientOptions, RecaptchaV2Request } from '@zennolab_com/capmonstercloud-client';
import { CapMonsterCloudClientFactory, ClientOptions } from '@zennolab_com/capmonstercloud-client';

document.addEventListener('DOMContentLoaded', async () => {
const cmcClient = CapMonsterCloudClientFactory.Create(new ClientOptions({ clientKey: '<your capmonster.cloud API key>' }));
console.log(await cmcClient.getBalance());

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
proxy: {
console.log(await cmcClient.solve({
task: {
type: 'NoCaptchaTask',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
proxyType: 'http',
proxyAddress: '8.8.8.8',
proxyPort: 8080,
proxyLogin: 'proxyLoginHere',
proxyPassword: 'proxyPasswordHere',
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.132 Safari/537.36',
},
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.132 Safari/537.36',
});

console.log(await cmcClient.Solve(recaptchaV2Request));
}));
});
```

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zennolab_com/capmonstercloud-client",
"version": "2.7.0",
"version": "3.0.0",
"description": "Official JS client library for https://capmonster.cloud/ captcha recognition service",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -24,7 +24,9 @@
"build": "node js-rimraf.js && npx tsc",
"test": "jest",
"test-unit": "jest -c jest.config.unit.js",
"test-integr": "jest -c jest.config.integration.js"
"test-integr": "jest -c jest.config.integration.js",
"lint": "eslint . --ext .ts,.js",
"lint:fix": "eslint . --ext .ts,.js --fix"
},
"author": "",
"license": "ISC",
Expand Down
193 changes: 193 additions & 0 deletions src/CapMonsterCloudClient.new-api.i.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { createServerMock } from '../tests/helpers';
import { CapMonsterCloudClientFactory } from './CapMonsterCloudClientFactory';
import { ClientOptions } from './ClientOptions';
import { TaskResultType } from './GetTaskResult';
import { CreateTaskPayload, ValidationError } from './TaskPayload';

describe('Check integration tests for plain CapMonsterCloudClient API', () => {
it('should call createTask with plain task payload', async () => {
const srv = await createServerMock({
responses: [{ responseBody: '{"errorId":0,"taskId":123456}' }],
});

const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: '<your capmonster.cloud API key>', serviceUrl: `http://127.0.0.1:${srv.address.port}`, softId: 55 }),
);

const result = await cmcClient.createTask({
task: {
type: 'NoCaptchaTaskProxyless',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
},
});

expect(result).toMatchObject({ errorId: 0, taskId: 123456 });
expect(JSON.parse(srv.caughtRequests[0].body as string)).toMatchObject({
clientKey: '<your capmonster.cloud API key>',
softId: 55,
task: {
type: 'NoCaptchaTaskProxyless',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
},
});

await srv.destroy();
});

it('should use payload softId over client options softId', async () => {
const srv = await createServerMock({
responses: [{ responseBody: '{"errorId":0,"taskId":123456}' }],
});

const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: '<your capmonster.cloud API key>', serviceUrl: `http://127.0.0.1:${srv.address.port}`, softId: 55 }),
);

await cmcClient.createTask({
softId: 56,
task: {
type: 'CustomTask',
class: 'CustomCaptcha',
},
});

expect(JSON.parse(srv.caughtRequests[0].body as string)).toMatchObject({
softId: 56,
task: {
type: 'CustomTask',
class: 'CustomCaptcha',
},
});

await srv.destroy();
});

it('should call createTask with Cloudflare Waiting Room payload', async () => {
const srv = await createServerMock({
responses: [{ responseBody: '{"errorId":0,"taskId":123456}' }],
});

const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: '<your capmonster.cloud API key>', serviceUrl: `http://127.0.0.1:${srv.address.port}` }),
);

const payload: CreateTaskPayload = {
task: {
type: 'TurnstileTask',
websiteURL: 'https://example.com',
websiteKey: 'xxxxxxxxxx',
cloudflareTaskType: 'wait_room',
htmlPageBase64: 'PCFET0NUWVBFIGh0...vYm9keT48L2h0bWw+',
userAgent: 'userAgentPlaceholder',
proxyType: 'http',
proxyAddress: '8.8.8.8',
proxyPort: 8080,
proxyLogin: 'proxyLoginHere',
proxyPassword: 'proxyPasswordHere',
},
};

const result = await cmcClient.createTask(payload);

expect(result).toMatchObject({ errorId: 0, taskId: 123456 });
expect(JSON.parse(srv.caughtRequests[0].body as string)).toMatchObject({
task: {
type: 'TurnstileTask',
cloudflareTaskType: 'wait_room',
htmlPageBase64: 'PCFET0NUWVBFIGh0...vYm9keT48L2h0bWw+',
proxyType: 'http',
},
});

await srv.destroy();
});

it('should request getTaskResult by taskId', async () => {
const srv = await createServerMock({
responses: [{ responseBody: '{"errorId":0,"status":"ready","solution":{"gRecaptchaResponse":"token"}}' }],
});

const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: '<your capmonster.cloud API key>', serviceUrl: `http://127.0.0.1:${srv.address.port}` }),
);

const result = await cmcClient.getTaskResult(123456);

expect(result).toMatchObject({
type: TaskResultType.Completed,
solution: {
gRecaptchaResponse: 'token',
},
});
expect(JSON.parse(srv.caughtRequests[0].body as string)).toMatchObject({
clientKey: '<your capmonster.cloud API key>',
taskId: 123456,
});

await srv.destroy();
});

it('should request getTaskResult with Cloudflare clearance solution', async () => {
const srv = await createServerMock({
responses: [{ responseBody: '{"errorId":0,"status":"ready","solution":{"cf_clearance":"cookie-value"}}' }],
});

const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: '<your capmonster.cloud API key>', serviceUrl: `http://127.0.0.1:${srv.address.port}` }),
);

const result = await cmcClient.getTaskResult(123456);

expect(result).toMatchObject({
type: TaskResultType.Completed,
solution: {
cf_clearance: 'cookie-value',
},
});

await srv.destroy();
});

it('should solve plain task payload', async () => {
const srv = await createServerMock({
responses: [
{ responseBody: '{"errorId":0,"taskId":123456}' },
{ responseBody: '{"errorId":0,"status":"ready","solution":{"gRecaptchaResponse":"token"}}' },
],
});

const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: '<your capmonster.cloud API key>', serviceUrl: `http://127.0.0.1:${srv.address.port}` }),
);

const result = await cmcClient.solve(
{
task: {
type: 'NoCaptchaTaskProxyless',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
},
},
{
firstRequestDelay: 0,
requestsInterval: 0,
timeout: 100,
},
);

expect(result.solution).toMatchObject({
gRecaptchaResponse: 'token',
});
expect(srv.caughtRequests).toHaveLength(2);

await srv.destroy();
});

it('should validate plain createTask payload before request', async () => {
const cmcClient = CapMonsterCloudClientFactory.Create(new ClientOptions({ clientKey: '<your capmonster.cloud API key>' }));

await expect(cmcClient.createTask({ task: {} as never })).rejects.toBeInstanceOf(ValidationError);
});
});
Loading
Loading