diff --git a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs index 7efb922..2d07a25 100644 --- a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs +++ b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs @@ -1071,5 +1071,52 @@ public async Task Binance_ShouldSolve() sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); actual.Should().BeEquivalentTo(expectedResult); } + + [Test] + public async Task Temu_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.TemuTask.CreateTask(); + var expectedResult = ObjectGen.TemuTask.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: JsonConvert.SerializeObject(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new + { + status = "ready", + solution = new + { + domains = expectedResult.Solution.Domains + }, + errorId = 0, + errorCode = (string)null! + } + }; + + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); + + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + } } \ No newline at end of file diff --git a/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs b/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs index 652ca8c..793baf5 100644 --- a/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs +++ b/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs @@ -546,5 +546,44 @@ public static CaptchaResult CreateSolution() }; } } + + public static class TemuTask + { + public static TemuCustomTaskRequest CreateTask() + { + return new TemuCustomTaskRequest(Gen.RandomString()) + { + WebsiteUrl = Gen.RandomUri().ToString(), + UserAgent = Gen.UserAgent(), + Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) + }; + } + + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new CustomTaskResponse + { + Domains = new Dictionary + { + { + "www.temu.com", + new CustomTaskResponse.DomainInfo + { + Cookies = new Dictionary + { + { "verifyAuthToken", Gen.RandomString() }, + { "api_uid", Gen.RandomString() } + } + } + } + } + } + }; + } + + } } } \ No newline at end of file diff --git a/CapMonsterCloud.Client.IntegrationTests/Sut.cs b/CapMonsterCloud.Client.IntegrationTests/Sut.cs index 57cc413..9a12bde 100644 --- a/CapMonsterCloud.Client.IntegrationTests/Sut.cs +++ b/CapMonsterCloud.Client.IntegrationTests/Sut.cs @@ -98,6 +98,10 @@ public async Task> SolveAsync( public async Task> SolveAsync( RecognitionComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); + public async Task> SolveAsync( + TemuCustomTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task GetBalanceAsync() { return await _cloudClient.GetBalanceAsync(); diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs index 2cd856c..df4508c 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs @@ -183,6 +183,15 @@ private static GetResultTimeouts GetTimeouts(Type type) Timeout = TimeSpan.FromSeconds(15) } }, + { + typeof(TemuCustomTaskRequest), + new GetResultTimeouts + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + } + }, }; } } diff --git a/CapMonsterCloud.Client/Requests/TemuCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/TemuCustomTaskRequest.cs new file mode 100644 index 0000000..915a121 --- /dev/null +++ b/CapMonsterCloud.Client/Requests/TemuCustomTaskRequest.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; + +namespace Zennolab.CapMonsterCloud.Requests +{ + /// + /// Temu CustomTask recognition request. + /// + /// + /// https://docs.capmonster.cloud/docs/captchas/temu-task + /// + public sealed class TemuCustomTaskRequest : CustomTaskRequestBase + { + /// + public override string Class => "Temu"; + + /// + /// Initializes Temu task with required metadata. + /// + /// + /// Cookies string from the page with captcha (document.cookie). + /// + public TemuCustomTaskRequest(string cookie) + { + Metadata = new { cookie }; + } + + /// + /// The full URL of the page where the CAPTCHA is loaded. + /// + [JsonProperty("websiteURL", Required = Required.Always)] + [Url] + public new string WebsiteUrl + { + get => base.WebsiteUrl; + set => base.WebsiteUrl = value; + } + + // userAgent, Proxy, Domains — уже есть в базе (CustomTaskRequestBase) + } +} \ No newline at end of file