Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.
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
14 changes: 14 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules
dist
coverage
*.log

# TypeScript
*.d.ts

# IDE
.vscode
.idea

# OS
.DS_Store
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,30 @@ run()
});
```

## Usage with CommonCaptcha

Use `CommonCaptcha` when you want to pass a full task payload directly, for example when a new captcha type is not yet covered by a dedicated request class.

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

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

const commonCaptcha = new CommonCaptcha({
task: {
type: 'RecaptchaV2Task',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
},
});

console.log(await cmcClient.Solve(commonCaptcha));
}

run();
```

## Usage with Browser (with or without Typescript)

Browser implementation use [fetch](https://caniuse.com/fetch) instead of [http(s)](https://nodejs.org/api/http.html).
Expand Down
43 changes: 43 additions & 0 deletions js-fix-esm-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint @typescript-eslint/no-var-requires: 0 */
const fs = require('fs');
const path = require('path');

const esmDir = path.join(__dirname, 'dist', 'esm');

function walk(dir) {
return fs.readdirSync(dir).flatMap((entry) => {
const fullPath = path.join(dir, entry);
return fs.statSync(fullPath).isDirectory() ? walk(fullPath) : [fullPath];
});
}

function resolveImportPath(filePath, importPath) {
if (!importPath.startsWith('.')) {
return importPath;
}

const absoluteTarget = path.resolve(path.dirname(filePath), importPath);
if (fs.existsSync(`${absoluteTarget}.js`)) {
return `${importPath}.js`;
}

if (fs.existsSync(path.join(absoluteTarget, 'index.js'))) {
return `${importPath}/index.js`;
}

return importPath;
}

function fixImports(filePath) {
const source = fs.readFileSync(filePath, 'utf8');
const fixed = source.replace(/(from\s+['"])([^'"]+)(['"])/g, (_, prefix, importPath, suffix) => {
return `${prefix}${resolveImportPath(filePath, importPath)}${suffix}`;
});
fs.writeFileSync(filePath, fixed);
}

if (fs.existsSync(esmDir)) {
walk(esmDir)
.filter((filePath) => filePath.endsWith('.js'))
.forEach(fixImports);
}
17 changes: 14 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
{
"name": "@zennolab_com/capmonstercloud-client",
"version": "2.7.0",
"version": "2.8.0",
"description": "Official JS client library for https://capmonster.cloud/ captcha recognition service",
"main": "dist/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/esm/index.js",
"require": "./dist/index.js",
"default": "./dist/esm/index.js"
}
},
"engines": {
"node": ">=18"
},
Expand All @@ -21,10 +30,12 @@
"ZennoPoster"
],
"scripts": {
"build": "node js-rimraf.js && npx tsc",
"build": "node js-rimraf.js && npx tsc && npx tsc -p tsconfig.esm.json && node js-fix-esm-imports.js && node -e \"require('fs').writeFileSync('dist/esm/package.json', JSON.stringify({ type: 'module' }, null, 2))\"",
"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
10 changes: 10 additions & 0 deletions src/CapMonsterCloudClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ import { AlibabaRequest } from './Requests/AlibabaRequest';
import { AlibabaResponse } from './Responses/AlibabaResponse';
import { FriendlyRequest } from './Requests/FriendlyRequest';
import { FriendlyResponse } from './Responses/FriendlyResponse';
import { CommonCaptchaRequest } from './Requests/CommonCaptchaRequest';
import { CommonCaptchaResponse } from './Responses/CommonCaptchaResponse';

/**
* Base type for capmonster.cloud Client exceptions
Expand Down Expand Up @@ -446,6 +448,14 @@ export class CapMonsterCloudClient {
resultTimeouts?: GetResultTimeouts,
cancellationController?: AbortController,
): Promise<CaptchaResult<ComplexImageResponse>>;
/**
* Solve CommonCaptchaRequest task.
*/
public async Solve(
task: CommonCaptchaRequest,
resultTimeouts?: GetResultTimeouts,
cancellationController?: AbortController,
): Promise<CaptchaResult<CommonCaptchaResponse>>;
/**
* Solve {Task} task
*/
Expand Down
60 changes: 50 additions & 10 deletions src/CapMonsterCloudClientFactory.i.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { TSPDRequest } from './Requests/TSPDRequest';
import { HuntRequest } from './Requests/HuntRequest';
import { AlibabaRequest } from './Requests/AlibabaRequest';
import { FriendlyRequest } from './Requests/FriendlyRequest';
import { CommonCaptchaRequest } from './Requests/CommonCaptchaRequest';

const { version } = require('../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires

Expand Down Expand Up @@ -130,6 +131,45 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
expect(await srv.destroy()).toBeUndefined();
});

it('should call createTask and getTaskResult methods with specified objects for CommonCaptchaRequest', async () => {
expect.assertions(5);

const srv = await createServerMock({
responses: [
{ responseBody: '{"errorId":0,"taskId":7654321}' },
{ responseBody: '{"errorId":0,"status":"ready","solution":{"gRecaptchaResponse":"answer"}}' },
],
});

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

const commonCaptchaRequest = new CommonCaptchaRequest({
task: {
type: 'RecaptchaV2Task',
websiteURL: 'https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high',
websiteKey: '6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd',
},
});

const task = await cmcClient.Solve(commonCaptchaRequest, {
firstRequestDelay: 0,
requestsInterval: 1000,
timeout: 1000,
});

expect(srv.caughtRequests[0]).toHaveProperty(
'body',
'{"clientKey":"<your capmonster.cloud API key>","task":{"type":"RecaptchaV2Task","websiteURL":"https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high","websiteKey":"6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd"},"softId":54}',
);
expect(srv.caughtRequests[1]).toHaveProperty('body', '{"clientKey":"<your capmonster.cloud API key>","taskId":7654321}');
expect(task).toHaveProperty('solution');
expect(task).toHaveProperty('solution.gRecaptchaResponse', 'answer');

expect(await srv.destroy()).toBeUndefined();
});

it('should call createTask with proxy parameters and getTaskResult methods with specified objects', async () => {
expect.assertions(5);

Expand Down Expand Up @@ -182,7 +222,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
);

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'websiteURL',
websiteURL: 'https://example.com',
websiteKey: 'websiteKey',
userAgent: 'userAgent',
});
Expand Down Expand Up @@ -217,7 +257,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
);

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'websiteURL',
websiteURL: 'https://example.com',
websiteKey: 'websiteKey',
userAgent: 'userAgent',
});
Expand Down Expand Up @@ -246,7 +286,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
);

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'websiteURL',
websiteURL: 'https://example.com',
websiteKey: 'websiteKey',
userAgent: 'userAgent',
});
Expand All @@ -268,7 +308,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {

expect(srv.caughtRequests[0]).toHaveProperty(
'body',
'{"clientKey":"<your capmonster.cloud API key>","task":{"type":"NoCaptchaTask","websiteURL":"websiteURL","websiteKey":"websiteKey","userAgent":"userAgent"},"softId":54}',
'{"clientKey":"<your capmonster.cloud API key>","task":{"type":"NoCaptchaTask","websiteURL":"https://example.com","websiteKey":"websiteKey","userAgent":"userAgent"},"softId":54}',
);
expect(task).toBeInstanceOf(CaptchaResult);
expect(task.solution).toBeUndefined();
Expand All @@ -292,7 +332,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
);

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'websiteURL',
websiteURL: 'https://example.com',
websiteKey: 'websiteKey',
userAgent: 'userAgent',
nocache: true,
Expand All @@ -315,7 +355,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {

expect(srv.caughtRequests[0]).toHaveProperty(
'body',
'{"clientKey":"<your capmonster.cloud API key>","task":{"type":"NoCaptchaTask","nocache":true,"websiteURL":"websiteURL","websiteKey":"websiteKey","userAgent":"userAgent"},"softId":54}',
'{"clientKey":"<your capmonster.cloud API key>","task":{"type":"NoCaptchaTask","nocache":true,"websiteURL":"https://example.com","websiteKey":"websiteKey","userAgent":"userAgent"},"softId":54}',
);
expect(task).toBeInstanceOf(CaptchaResult);
expect(task.solution).toBeUndefined();
Expand All @@ -339,7 +379,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
);

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'websiteURL',
websiteURL: 'https://example.com',
websiteKey: 'websiteKey',
});

Expand All @@ -352,7 +392,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {

expect(srv.caughtRequests[0]).toHaveProperty(
'body',
'{"clientKey":"<your capmonster.cloud API key>","task":{"type":"NoCaptchaTask","websiteURL":"websiteURL","websiteKey":"websiteKey"},"softId":54}',
'{"clientKey":"<your capmonster.cloud API key>","task":{"type":"NoCaptchaTask","websiteURL":"https://example.com","websiteKey":"websiteKey"},"softId":54}',
);
expect(task).toBeInstanceOf(CaptchaResult);
expect(task.solution).toBeUndefined();
Expand All @@ -379,7 +419,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
);

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'websiteURL',
websiteURL: 'https://example.com',
websiteKey: 'websiteKey',
});

Expand Down Expand Up @@ -408,7 +448,7 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => {
);

const recaptchaV2Request = new RecaptchaV2Request({
websiteURL: 'websiteURL',
websiteURL: 'https://example.com',
websiteKey: 'websiteKey',
});

Expand Down
11 changes: 9 additions & 2 deletions src/GetResultTimeouts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Task } from './Requests/Task';
import { CommonCaptchaRequest } from './Requests/CommonCaptchaRequest';
import { TaskType } from './TaskType';

export type GetResultTimeouts = {
Expand Down Expand Up @@ -118,7 +119,13 @@ export const MTCaptchaTaskTimeouts = {
} as GetResultTimeouts;

export function detectResultTimeouts(task: Task): GetResultTimeouts {
switch (task.type) {
if (task instanceof CommonCaptchaRequest) {
return CustomTaskTimeouts;
}

const taskType = task.type;

switch (taskType) {
case TaskType.FunCaptchaTask:
return FunCaptchaTimeouts;
case TaskType.GeeTestTask:
Expand Down Expand Up @@ -154,6 +161,6 @@ export function detectResultTimeouts(task: Task): GetResultTimeouts {
case TaskType.MTCaptchaTask:
return MTCaptchaTaskTimeouts;
default:
throw new Error(`Could not detect result timeouts for provided task type = ${task.type}`);
throw new Error(`Could not detect result timeouts for provided task type = ${taskType}`);
}
}
4 changes: 3 additions & 1 deletion src/GetTaskResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { TSPDResponse } from './Responses/TSPDResponse';
import { HuntResponse } from './Responses/HuntResponse';
import { AlibabaResponse } from './Responses/AlibabaResponse';
import { FriendlyResponse } from './Responses/FriendlyResponse';
import { CommonCaptchaResponse } from './Responses/CommonCaptchaResponse';

export enum TaskResultType {
Failed = 'Failed',
Expand Down Expand Up @@ -57,7 +58,8 @@ export type TaskCompletedSolution =
| TSPDResponse
| HuntResponse
| AlibabaResponse
| FriendlyResponse;
| FriendlyResponse
| CommonCaptchaResponse;

export type TaskCompleted<S extends TaskCompletedSolution> = {
type: TaskResultType.Completed;
Expand Down
2 changes: 1 addition & 1 deletion src/Requests/AlibabaRequest/AlibabaRequestBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export abstract class AlibabaRequestBase extends CaptchaRequestBase {

constructor({ type, nocache, websiteURL, userAgent, metadata, _class }: AlibabaRequestBaseIn) {
super({ type, nocache });
this.websiteURL = websiteURL;
this.websiteURL = this.validateWebsiteURL(websiteURL);
this.userAgent = userAgent;
this.metadata = metadata;
this.class = _class;
Expand Down
2 changes: 1 addition & 1 deletion src/Requests/AltchaRequest/AltchaRequestBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export abstract class AltchaRequestBase extends CaptchaRequestBase {

constructor({ type, nocache, websiteURL, websiteKey, userAgent, metadata, _class }: AltchaRequestBaseIn) {
super({ type, nocache });
this.websiteURL = websiteURL;
this.websiteURL = this.validateWebsiteURL(websiteURL);
this.websiteKey = websiteKey;
this.metadata = metadata;
this.userAgent = userAgent;
Expand Down
2 changes: 1 addition & 1 deletion src/Requests/AmazonRequest/AmazonRequestBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export abstract class AmazonRequestBase extends CaptchaRequestBase {
userAgent,
}: AmazonRequestBaseIn) {
super({ type, nocache });
this.websiteURL = websiteURL;
this.websiteURL = this.validateWebsiteURL(websiteURL);
this.challengeScript = challengeScript;
this.captchaScript = captchaScript;
this.websiteKey = websiteKey;
Expand Down
2 changes: 1 addition & 1 deletion src/Requests/BasiliskRequest/BasiliskRequestBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export abstract class BasiliskRequestBase extends CaptchaRequestBase {

constructor({ type, nocache, websiteURL, userAgent, websiteKey, _class }: BasiliskRequestBaseIn) {
super({ type, nocache });
this.websiteURL = websiteURL;
this.websiteURL = this.validateWebsiteURL(websiteURL);
this.websiteKey = websiteKey;
this.userAgent = userAgent;
this.class = _class;
Expand Down
2 changes: 1 addition & 1 deletion src/Requests/BinanceRequest/BinanceRequestBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export abstract class BinanceRequestBase extends CaptchaRequestBase {

constructor({ type, nocache, websiteURL, userAgent, websiteKey, validateId }: BinanceRequestBaseIn) {
super({ type, nocache });
this.websiteURL = websiteURL;
this.websiteURL = this.validateWebsiteURL(websiteURL);
this.websiteKey = websiteKey;
this.validateId = validateId;
this.userAgent = userAgent;
Expand Down
Loading
Loading