diff --git a/README.md b/README.md index fbfff1a..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) @@ -91,3 +92,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..af8666e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zennolab_com/capmonstercloud-client", - "version": "2.2.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 bd395c1..83ca14e 100644 --- a/src/CapMonsterCloudClient.ts +++ b/src/CapMonsterCloudClient.ts @@ -49,6 +49,10 @@ 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'; +import { MTCaptchaRequest } from './Requests/MTCaptchaRequest'; +import { MTCaptchaResponse } from './Responses/MTCaptchaResponse'; type CustomTaskUnion = TemuRequest | BasiliskRequest | ImpervaRequest; @@ -327,6 +331,27 @@ 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 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 7e80165..adbfde3 100644 --- a/src/CapMonsterCloudClientFactory.i.spec.ts +++ b/src/CapMonsterCloudClientFactory.i.spec.ts @@ -17,6 +17,8 @@ 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'; +import { MTCaptchaRequest } from './Requests/MTCaptchaRequest'; const { version } = require('../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires @@ -1090,4 +1092,82 @@ 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(); + }); + + 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 706d9db..fe170b4 100644 --- a/src/GetResultTimeouts.ts +++ b/src/GetResultTimeouts.ts @@ -96,6 +96,20 @@ 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 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: @@ -126,6 +140,10 @@ export function detectResultTimeouts(task: Task): GetResultTimeouts { return BinanceTaskTimeouts; case TaskType.ProsopoTask: 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/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/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/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..2589e23 100644 --- a/src/TaskType.ts +++ b/src/TaskType.ts @@ -14,4 +14,6 @@ export enum TaskType { AmazonTask = 'AmazonTask', BinanceTask = 'BinanceTask', ProsopoTask = 'ProsopoTask', + YidunTask = 'YidunTask', + MTCaptchaTask = 'MTCaptchaTask', } diff --git a/src/index.ts b/src/index.ts index 5ec46c9..2b77078 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,8 @@ 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'; +import { MTCaptchaRequest, MTCaptchaRequestIn } from './Requests/MTCaptchaRequest'; export default { CapMonsterCloudClientFactory, @@ -48,6 +50,8 @@ export default { ComplexImageTaskRecognitionRequest, ProsopoRequest, TemuRequest, + YidunRequest, + MTCaptchaRequest, }; export { @@ -97,4 +101,8 @@ export { ProsopoRequestIn, TemuRequest, TemuRequestIn, + YidunRequest, + YidunRequestIn, + MTCaptchaRequest, + MTCaptchaRequestIn, };