From 272f61da79ce73f4e0c8b9ef6beb675a515c5da4 Mon Sep 17 00:00:00 2001 From: bezzz Date: Mon, 18 Aug 2025 10:02:15 +0300 Subject: [PATCH 1/2] CCL-6505 added Yidun task --- README.md | 1 + package.json | 2 +- src/CapMonsterCloudClient.ts | 12 +++ src/CapMonsterCloudClientFactory.i.spec.ts | 39 +++++++++ src/GetResultTimeouts.ts | 9 +++ src/Requests/YidunRequest/YidunRequest.ts | 18 +++++ .../YidunRequest/YuidunRequestBase.ts | 80 +++++++++++++++++++ src/Requests/YidunRequest/index.ts | 1 + src/Responses/YidunResponse.ts | 6 ++ src/TaskType.ts | 1 + src/index.ts | 4 + 11 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/Requests/YidunRequest/YidunRequest.ts create mode 100644 src/Requests/YidunRequest/YuidunRequestBase.ts create mode 100644 src/Requests/YidunRequest/index.ts create mode 100644 src/Responses/YidunResponse.ts diff --git a/README.md b/README.md index fbfff1a..1385be3 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,4 @@ DEBUG=cmc-* node app.js - [Temu](https://zenno.link/doc-temu-en) - [TenDIRequest](https://zenno.link/doc-tendi-en) - [TurnstileRequest](https://zenno.link/doc-turnstile-proxy-en) +- [Yidun](https://zenno.link/doc-yidun-en) diff --git a/package.json b/package.json index c57319b..bd4cee2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zennolab_com/capmonstercloud-client", - "version": "2.2.0", + "version": "2.3.0", "description": "Official JS client library for https://capmonster.cloud/ captcha recognition service", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/CapMonsterCloudClient.ts b/src/CapMonsterCloudClient.ts index bd395c1..47bfe97 100644 --- a/src/CapMonsterCloudClient.ts +++ b/src/CapMonsterCloudClient.ts @@ -49,6 +49,8 @@ import { ProsopoRequest } from './Requests/ProsopoRequest'; import { ProsopoResponse } from './Responses/ProsopoResponse'; import { TemuRequest } from './Requests/TemuRequest'; import { TemuResponse } from './Responses/TemuResponse'; +import { YidunRequest } from './Requests/YidunRequest'; +import { YidunResponse } from './Responses/YidunResponse'; type CustomTaskUnion = TemuRequest | BasiliskRequest | ImpervaRequest; @@ -327,6 +329,16 @@ export class CapMonsterCloudClient { resultTimeouts?: GetResultTimeouts, cancellationController?: AbortController, ): Promise>; + + /** + * Solve Yidun task + * You will get response within 10 - 180 secs period depending on service workload. + */ + public async Solve( + task: YidunRequest, + resultTimeouts?: GetResultTimeouts, + cancellationController?: AbortController, + ): Promise>; /** * Solve Complex Image FunCaptcha Task * You will get response within 10 - 180 secs period depending on service workload. diff --git a/src/CapMonsterCloudClientFactory.i.spec.ts b/src/CapMonsterCloudClientFactory.i.spec.ts index 7e80165..7b26e1d 100644 --- a/src/CapMonsterCloudClientFactory.i.spec.ts +++ b/src/CapMonsterCloudClientFactory.i.spec.ts @@ -17,6 +17,7 @@ import { AmazonRequest } from './Requests/AmazonRequest'; import { HCaptchaRequest } from './Requests/HCaptchaRequest'; import { ProsopoRequest } from './Requests/ProsopoRequest'; import { TemuRequest } from './Requests/TemuRequest'; +import { YidunRequest } from './Requests/YidunRequest'; const { version } = require('../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires @@ -1090,4 +1091,42 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => { expect(await srv.destroy()).toBeUndefined(); }); + + it('should solve Yidun Task Proxyless', async () => { + expect.assertions(5); + + const srv = await createServerMock({ + responses: [ + { responseBody: '{"errorId":0,"taskId":1234567}' }, + { + responseBody: + '{"errorId":0,"status":"ready","solution":{"token":"0x00016c68747470733a2f2f70726f6e6f6465332e70726f736f706f2e696fc0354550516f4d5a454463354c704e376774784d4d7a5950547a4136"}}', + }, + ], + }); + + const cmcClient = CapMonsterCloudClientFactory.Create( + new ClientOptions({ clientKey: '', serviceUrl: `http://localhost:${srv.address.port}` }), + ); + + const yidunRequest = new YidunRequest({ + websiteURL: 'https://id7.cloud.example.com/IframeLogin.html', + websiteKey: 'websiteKey', + }); + + const task = await cmcClient.Solve(yidunRequest); + + expect(srv.caughtRequests[0]).toHaveProperty( + 'body', + '{"clientKey":"","task":{"type":"YidunTask","websiteURL":"https://id7.cloud.example.com/IframeLogin.html","websiteKey":"websiteKey"},"softId":54}', + ); + expect(srv.caughtRequests[1]).toHaveProperty('body', '{"clientKey":"","taskId":1234567}'); + expect(task).toHaveProperty('solution'); + expect(task).toHaveProperty( + 'solution.token', + '0x00016c68747470733a2f2f70726f6e6f6465332e70726f736f706f2e696fc0354550516f4d5a454463354c704e376774784d4d7a5950547a4136', + ); + + expect(await srv.destroy()).toBeUndefined(); + }); }); diff --git a/src/GetResultTimeouts.ts b/src/GetResultTimeouts.ts index 706d9db..e3c6e80 100644 --- a/src/GetResultTimeouts.ts +++ b/src/GetResultTimeouts.ts @@ -96,6 +96,13 @@ export const ProsopoTaskTimeouts = { timeout: 1000 * 80, } as GetResultTimeouts; +export const YidunTaskTimeouts = { + firstRequestDelay: 1000 * 1, + firstRequestNoCacheDelay: 1000 * 10, + requestsInterval: 1000 * 1, + timeout: 1000 * 80, +} as GetResultTimeouts; + export function detectResultTimeouts(task: Task): GetResultTimeouts { switch (task.type) { case TaskType.FunCaptchaTask: @@ -126,6 +133,8 @@ export function detectResultTimeouts(task: Task): GetResultTimeouts { return BinanceTaskTimeouts; case TaskType.ProsopoTask: return ProsopoTaskTimeouts; + case TaskType.YidunTask: + return YidunTaskTimeouts; default: throw new Error(`Could not detect result timeouts for provided task type = ${task.type}`); } diff --git a/src/Requests/YidunRequest/YidunRequest.ts b/src/Requests/YidunRequest/YidunRequest.ts new file mode 100644 index 0000000..5d29f53 --- /dev/null +++ b/src/Requests/YidunRequest/YidunRequest.ts @@ -0,0 +1,18 @@ +import { TaskType } from '../../TaskType'; +import { YidunRequestBase, YidunRequestBaseIn } from './YuidunRequestBase'; +import { ProxyInfo, ProxyInfoIn } from '../ProxyInfo'; + +export type YidunRequestIn = Pick> & { proxy?: ProxyInfoIn }; +/** + * Yidun recognition request (with proxy). + * {@link https://zenno.link/doc-yidun-en} + */ +export class YidunRequest extends YidunRequestBase { + constructor({ proxy, ...restArgsObj }: YidunRequestIn) { + super({ type: TaskType.YidunTask, ...restArgsObj }); + + if (proxy) { + Object.assign(this, new ProxyInfo(proxy)); + } + } +} diff --git a/src/Requests/YidunRequest/YuidunRequestBase.ts b/src/Requests/YidunRequest/YuidunRequestBase.ts new file mode 100644 index 0000000..973a5a7 --- /dev/null +++ b/src/Requests/YidunRequest/YuidunRequestBase.ts @@ -0,0 +1,80 @@ +import { CaptchaRequestBase, CaptchaRequestBaseIn } from '../CaptchaRequestBase'; + +export type YidunRequestBaseIn = { + websiteURL: string; + websiteKey: string; + userAgent?: string; + yidunGetLib?: string; + yidunApiServerSubdomain?: string; + challenge?: string; + hcg?: string; + hct?: number; +} & CaptchaRequestBaseIn; + +/** + * Base Yidun recognition request + */ +export abstract class YidunRequestBase extends CaptchaRequestBase { + /** + * Full URL of the page with the captcha. + */ + public websiteURL!: string; + + /** + * The siteKey value found on the page. + */ + public websiteKey!: string; + + /** + * Browser User-Agent. Pass only the actual UA from Windows OS + */ + public userAgent?: string; + + /** + * Path to the JavaScript file responsible for loading the captcha on the page. + */ + public yidunGetLib?: string; + + /** + * Subdomain of the Yidun API server. + */ + public yidunApiServerSubdomain?: string; + + /** + * Unique identifier of the current captcha. + */ + public challenge?: string; + + /** + * Captcha hash used in the request. + */ + public hcg?: string; + + /** + * Numeric timestamp used in Enterprise version validation. + */ + public hct?: number; + + constructor({ + type, + nocache, + websiteURL, + websiteKey, + userAgent, + yidunGetLib, + yidunApiServerSubdomain, + challenge, + hcg, + hct, + }: YidunRequestBaseIn) { + super({ type, nocache }); + this.websiteURL = websiteURL; + this.websiteKey = websiteKey; + this.userAgent = userAgent; + this.yidunGetLib = yidunGetLib; + this.yidunApiServerSubdomain = yidunApiServerSubdomain; + this.challenge = challenge; + this.hcg = hcg; + this.hct = hct; + } +} diff --git a/src/Requests/YidunRequest/index.ts b/src/Requests/YidunRequest/index.ts new file mode 100644 index 0000000..42f813c --- /dev/null +++ b/src/Requests/YidunRequest/index.ts @@ -0,0 +1 @@ +export * from './YidunRequest'; diff --git a/src/Responses/YidunResponse.ts b/src/Responses/YidunResponse.ts new file mode 100644 index 0000000..ed5fc85 --- /dev/null +++ b/src/Responses/YidunResponse.ts @@ -0,0 +1,6 @@ +/** + * Yidun recognition response base + */ +export type YidunResponse = { + token: string; +}; diff --git a/src/TaskType.ts b/src/TaskType.ts index b727e3e..194a7e1 100644 --- a/src/TaskType.ts +++ b/src/TaskType.ts @@ -14,4 +14,5 @@ export enum TaskType { AmazonTask = 'AmazonTask', BinanceTask = 'BinanceTask', ProsopoTask = 'ProsopoTask', + YidunTask = 'YidunTask', } diff --git a/src/index.ts b/src/index.ts index 5ec46c9..b5c3781 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ import { BinanceRequest, BinanceRequestIn } from './Requests/BinanceRequest'; import { ComplexImageRecognitionRequestIn, ComplexImageTaskRecognitionRequest } from './Requests/ComplexImageTaskRecognitionRequest'; import { ProsopoRequest, ProsopoRequestIn } from './Requests/ProsopoRequest'; import { TemuRequest, TemuRequestIn } from './Requests/TemuRequest'; +import { YidunRequest, YidunRequestIn } from './Requests/YidunRequest'; export default { CapMonsterCloudClientFactory, @@ -48,6 +49,7 @@ export default { ComplexImageTaskRecognitionRequest, ProsopoRequest, TemuRequest, + YidunRequest, }; export { @@ -97,4 +99,6 @@ export { ProsopoRequestIn, TemuRequest, TemuRequestIn, + YidunRequest, + YidunRequestIn, }; From 97c0991712e836a89abeb2539fb64c667bab0172 Mon Sep 17 00:00:00 2001 From: bezzz Date: Mon, 18 Aug 2025 10:16:23 +0300 Subject: [PATCH 2/2] CCL-6505 added MTCaptcha --- README.md | 1 + package.json | 2 +- src/CapMonsterCloudClient.ts | 13 +++++ src/CapMonsterCloudClientFactory.i.spec.ts | 41 ++++++++++++++++ src/GetResultTimeouts.ts | 9 ++++ .../MTCaptchaRequest/MTCaptchaRequest.ts | 18 +++++++ .../MTCaptchaRequest/MTCaptchaRequestBase.ts | 48 +++++++++++++++++++ src/Requests/MTCaptchaRequest/index.ts | 1 + src/Responses/MTCaptchaResponse.ts | 6 +++ src/TaskType.ts | 1 + src/index.ts | 4 ++ 11 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/Requests/MTCaptchaRequest/MTCaptchaRequest.ts create mode 100644 src/Requests/MTCaptchaRequest/MTCaptchaRequestBase.ts create mode 100644 src/Requests/MTCaptchaRequest/index.ts create mode 100644 src/Responses/MTCaptchaResponse.ts diff --git a/README.md b/README.md index 1385be3..8c0155e 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ DEBUG=cmc-* node app.js - [HcaptchaComplexImageTaskRequest](https://zenno.link/doc-complextask-hc-en) - [ImageToTextRequest](https://zenno.link/doc-ImageToTextTask-en) - [ImpervaRequest](https://zenno.link/doc-imperva-en) +- [MTCaptcha](https://zenno.link/doc-mt-captcha-en) - [ProsopoRequest](https://zenno.link/doc-prosopo-en) - [RecaptchaComplexImageTaskRequest](https://zenno.link/doc-complextask-rc-en) - [RecaptchaV2EnterpriseRequest](https://zenno.link/doc-recaptcha2e-proxy-en) diff --git a/package.json b/package.json index bd4cee2..af8666e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zennolab_com/capmonstercloud-client", - "version": "2.3.0", + "version": "2.4.0", "description": "Official JS client library for https://capmonster.cloud/ captcha recognition service", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/CapMonsterCloudClient.ts b/src/CapMonsterCloudClient.ts index 47bfe97..83ca14e 100644 --- a/src/CapMonsterCloudClient.ts +++ b/src/CapMonsterCloudClient.ts @@ -51,6 +51,8 @@ import { TemuRequest } from './Requests/TemuRequest'; import { TemuResponse } from './Responses/TemuResponse'; import { YidunRequest } from './Requests/YidunRequest'; import { YidunResponse } from './Responses/YidunResponse'; +import { MTCaptchaRequest } from './Requests/MTCaptchaRequest'; +import { MTCaptchaResponse } from './Responses/MTCaptchaResponse'; type CustomTaskUnion = TemuRequest | BasiliskRequest | ImpervaRequest; @@ -339,6 +341,17 @@ export class CapMonsterCloudClient { resultTimeouts?: GetResultTimeouts, cancellationController?: AbortController, ): Promise>; + + /** + * Solve MTCaptcha task + * You will get response within 10 - 180 secs period depending on service workload. + */ + public async Solve( + task: MTCaptchaRequest, + resultTimeouts?: GetResultTimeouts, + cancellationController?: AbortController, + ): Promise>; + /** * Solve Complex Image FunCaptcha Task * You will get response within 10 - 180 secs period depending on service workload. diff --git a/src/CapMonsterCloudClientFactory.i.spec.ts b/src/CapMonsterCloudClientFactory.i.spec.ts index 7b26e1d..adbfde3 100644 --- a/src/CapMonsterCloudClientFactory.i.spec.ts +++ b/src/CapMonsterCloudClientFactory.i.spec.ts @@ -18,6 +18,7 @@ import { HCaptchaRequest } from './Requests/HCaptchaRequest'; import { ProsopoRequest } from './Requests/ProsopoRequest'; import { TemuRequest } from './Requests/TemuRequest'; import { YidunRequest } from './Requests/YidunRequest'; +import { MTCaptchaRequest } from './Requests/MTCaptchaRequest'; const { version } = require('../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires @@ -1129,4 +1130,44 @@ describe('Check integration tests for CapMonsterCloudClientFactory()', () => { expect(await srv.destroy()).toBeUndefined(); }); + + it('should solve MTCaptcha Task Proxyless', async () => { + expect.assertions(5); + + const srv = await createServerMock({ + responses: [ + { responseBody: '{"errorId":0,"taskId":1234567}' }, + { + responseBody: + '{"errorId":0,"status":"ready","solution":{"token":"0x00016c68747470733a2f2f70726f6e6f6465332e70726f736f706f2e696fc0354550516f4d5a454463354c704e376774784d4d7a5950547a4136"}}', + }, + ], + }); + + const cmcClient = CapMonsterCloudClientFactory.Create( + new ClientOptions({ clientKey: '', serviceUrl: `http://localhost:${srv.address.port}` }), + ); + + const mtCaptchaRequest = new MTCaptchaRequest({ + websiteURL: 'https://www.example.com', + websiteKey: 'websiteKey', + pageAction: 'login', + isInvisible: false, + }); + + const task = await cmcClient.Solve(mtCaptchaRequest); + + expect(srv.caughtRequests[0]).toHaveProperty( + 'body', + '{"clientKey":"","task":{"type":"MTCaptchaTask","websiteURL":"https://www.example.com","websiteKey":"websiteKey","pageAction":"login","isInvisible":false},"softId":54}', + ); + expect(srv.caughtRequests[1]).toHaveProperty('body', '{"clientKey":"","taskId":1234567}'); + expect(task).toHaveProperty('solution'); + expect(task).toHaveProperty( + 'solution.token', + '0x00016c68747470733a2f2f70726f6e6f6465332e70726f736f706f2e696fc0354550516f4d5a454463354c704e376774784d4d7a5950547a4136', + ); + + expect(await srv.destroy()).toBeUndefined(); + }); }); diff --git a/src/GetResultTimeouts.ts b/src/GetResultTimeouts.ts index e3c6e80..fe170b4 100644 --- a/src/GetResultTimeouts.ts +++ b/src/GetResultTimeouts.ts @@ -103,6 +103,13 @@ export const YidunTaskTimeouts = { timeout: 1000 * 80, } as GetResultTimeouts; +export const MTCaptchaTaskTimeouts = { + firstRequestDelay: 1000 * 1, + firstRequestNoCacheDelay: 1000 * 10, + requestsInterval: 1000 * 1, + timeout: 1000 * 80, +} as GetResultTimeouts; + export function detectResultTimeouts(task: Task): GetResultTimeouts { switch (task.type) { case TaskType.FunCaptchaTask: @@ -135,6 +142,8 @@ export function detectResultTimeouts(task: Task): GetResultTimeouts { return ProsopoTaskTimeouts; case TaskType.YidunTask: return YidunTaskTimeouts; + case TaskType.MTCaptchaTask: + return MTCaptchaTaskTimeouts; default: throw new Error(`Could not detect result timeouts for provided task type = ${task.type}`); } diff --git a/src/Requests/MTCaptchaRequest/MTCaptchaRequest.ts b/src/Requests/MTCaptchaRequest/MTCaptchaRequest.ts new file mode 100644 index 0000000..da51190 --- /dev/null +++ b/src/Requests/MTCaptchaRequest/MTCaptchaRequest.ts @@ -0,0 +1,18 @@ +import { TaskType } from '../../TaskType'; +import { MTCaptchaRequestBase, MTCaptchaRequestBaseIn } from './MTCaptchaRequestBase'; +import { ProxyInfo, ProxyInfoIn } from '../ProxyInfo'; + +export type MTCaptchaRequestIn = Pick> & { proxy?: ProxyInfoIn }; +/** + * MTCaptcha recognition request (with proxy). + * {@link https://zenno.link/doc-mt-captcha-en} + */ +export class MTCaptchaRequest extends MTCaptchaRequestBase { + constructor({ proxy, ...restArgsObj }: MTCaptchaRequestIn) { + super({ type: TaskType.MTCaptchaTask, ...restArgsObj }); + + if (proxy) { + Object.assign(this, new ProxyInfo(proxy)); + } + } +} diff --git a/src/Requests/MTCaptchaRequest/MTCaptchaRequestBase.ts b/src/Requests/MTCaptchaRequest/MTCaptchaRequestBase.ts new file mode 100644 index 0000000..9280386 --- /dev/null +++ b/src/Requests/MTCaptchaRequest/MTCaptchaRequestBase.ts @@ -0,0 +1,48 @@ +import { CaptchaRequestBase, CaptchaRequestBaseIn } from '../CaptchaRequestBase'; + +export type MTCaptchaRequestBaseIn = { + websiteURL: string; + websiteKey: string; + userAgent?: string; + pageAction?: string; + isInvisible?: boolean; +} & CaptchaRequestBaseIn; + +/** + * Base MTCaptcha recognition request + */ +export abstract class MTCaptchaRequestBase extends CaptchaRequestBase { + /** + * Full URL of the page with the captcha. + */ + public websiteURL!: string; + + /** + * The MTcaptcha key, passed in the request parameters as sk + */ + public websiteKey!: string; + + /** + * Browser User-Agent. Pass only the actual UA from Windows OS + */ + public userAgent?: string; + + /** + * The action parameter is passed in the request as act and displayed when validating the token. + */ + public pageAction?: string; + + /** + * true if the captcha is invisible, i.e., has a hidden confirmation field. If bot suspicion occurs, an additional verification is triggered. + */ + public isInvisible?: boolean; + + constructor({ type, nocache, websiteURL, websiteKey, userAgent, pageAction, isInvisible }: MTCaptchaRequestBaseIn) { + super({ type, nocache }); + this.websiteURL = websiteURL; + this.websiteKey = websiteKey; + this.userAgent = userAgent; + this.pageAction = pageAction; + this.isInvisible = isInvisible; + } +} diff --git a/src/Requests/MTCaptchaRequest/index.ts b/src/Requests/MTCaptchaRequest/index.ts new file mode 100644 index 0000000..11291ee --- /dev/null +++ b/src/Requests/MTCaptchaRequest/index.ts @@ -0,0 +1 @@ +export * from './MTCaptchaRequest'; diff --git a/src/Responses/MTCaptchaResponse.ts b/src/Responses/MTCaptchaResponse.ts new file mode 100644 index 0000000..9279750 --- /dev/null +++ b/src/Responses/MTCaptchaResponse.ts @@ -0,0 +1,6 @@ +/** + * MTCaptcha recognition response base + */ +export type MTCaptchaResponse = { + token: string; +}; diff --git a/src/TaskType.ts b/src/TaskType.ts index 194a7e1..2589e23 100644 --- a/src/TaskType.ts +++ b/src/TaskType.ts @@ -15,4 +15,5 @@ export enum TaskType { BinanceTask = 'BinanceTask', ProsopoTask = 'ProsopoTask', YidunTask = 'YidunTask', + MTCaptchaTask = 'MTCaptchaTask', } diff --git a/src/index.ts b/src/index.ts index b5c3781..2b77078 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,6 +25,7 @@ import { ComplexImageRecognitionRequestIn, ComplexImageTaskRecognitionRequest } import { ProsopoRequest, ProsopoRequestIn } from './Requests/ProsopoRequest'; import { TemuRequest, TemuRequestIn } from './Requests/TemuRequest'; import { YidunRequest, YidunRequestIn } from './Requests/YidunRequest'; +import { MTCaptchaRequest, MTCaptchaRequestIn } from './Requests/MTCaptchaRequest'; export default { CapMonsterCloudClientFactory, @@ -50,6 +51,7 @@ export default { ProsopoRequest, TemuRequest, YidunRequest, + MTCaptchaRequest, }; export { @@ -101,4 +103,6 @@ export { TemuRequestIn, YidunRequest, YidunRequestIn, + MTCaptchaRequest, + MTCaptchaRequestIn, };