diff --git a/CapMonsterCloud.Client.IntegrationTests/Gen.cs b/CapMonsterCloud.Client.IntegrationTests/Gen.cs index 8c40a38..50b6c98 100644 --- a/CapMonsterCloud.Client.IntegrationTests/Gen.cs +++ b/CapMonsterCloud.Client.IntegrationTests/Gen.cs @@ -96,6 +96,15 @@ public static List ListOfValues(Func createFunc, int count) return Enumerable.Repeat(0, count).Select(x => createFunc()).ToList(); } + public static T[] ArrayOfValues(Func createFunc) => + ArrayOfValues(createFunc, 5); + + public static T[] ArrayOfValues(Func createFunc, int count) + { + if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count)); + return Enumerable.Repeat(0, count).Select(x => createFunc()).ToArray(); + } + public static bool RandomBool() => Rnd.NextDouble() >= 0.5; public static string RandomGuid() diff --git a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs index d6f6fbb..7efb922 100644 --- a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs +++ b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs @@ -465,6 +465,52 @@ public async Task DataDome_ShouldSolve() actual.Should().BeEquivalentTo(expectedResult); } + [Test] + public async Task Imperva_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.CustomTask.CreateImpervaTask(); + var expectedResult = ObjectGen.CustomTask.CreateImpervaSolution(); + + 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); + } + [Test] public async Task RecaptchaComplexImageTask_ShouldSolve() { @@ -472,7 +518,7 @@ public async Task RecaptchaComplexImageTask_ShouldSolve() var taskId = Gen.RandomInt(); var captchaRequest = ObjectGen.ComplexImageTask.CreateRecaptchaComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateSolution(); + var expectedResult = ObjectGen.ComplexImageTask.CreateGridComplexImageTaskSolution(); var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> { @@ -512,13 +558,13 @@ public async Task RecaptchaComplexImageTask_ShouldSolve() } [Test] - public async Task HCaptchaComplexImageTask_ShouldSolve() + public async Task HCaptchaComplexImageTaskWithNumericAnswer_ShouldSolve() { var clientKey = Gen.RandomString(); var taskId = Gen.RandomInt(); var captchaRequest = ObjectGen.ComplexImageTask.CreateHCaptchaComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateSolution(); + var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithNumericAnswer(); var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> { @@ -541,7 +587,8 @@ public async Task HCaptchaComplexImageTask_ShouldSolve() status = "ready", solution = new { - answer = expectedResult.Solution.Answer + answer = expectedResult.Solution.Answer.NumericAnswer, + metadata = new { AnswerType = "NumericArray" } }, errorId = 0, errorCode = (string)null! @@ -555,8 +602,55 @@ public async Task HCaptchaComplexImageTask_ShouldSolve() sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); actual.Should().BeEquivalentTo(expectedResult); - } - + } + + [Test] + public async Task HCaptchaComplexImageTaskWithGridAnswer_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.ComplexImageTask.CreateHCaptchaComplexImageTask(); + var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithGridAnswer(); + + 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 + { + answer = expectedResult.Solution.Answer.GridAnswer, + metadata = new { AnswerType = "Grid" } + }, + 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); + } + [Test] public async Task FunCaptchaComplexImageTask_ShouldSolve() { @@ -564,7 +658,7 @@ public async Task FunCaptchaComplexImageTask_ShouldSolve() var taskId = Gen.RandomInt(); var captchaRequest = ObjectGen.ComplexImageTask.CreateFunCaptchaComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateSolution(); + var expectedResult = ObjectGen.ComplexImageTask.CreateGridComplexImageTaskSolution(); var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> { @@ -602,7 +696,101 @@ public async Task FunCaptchaComplexImageTask_ShouldSolve() sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); actual.Should().BeEquivalentTo(expectedResult); } - + + [Test] + public async Task RecognitionComplexImageTaskWithGridAnswer_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.ComplexImageTask.CreateRecognitionComplexImageTask(); + var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithGridAnswer(); + + 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 + { + answer = expectedResult.Solution.Answer.GridAnswer, + metadata = new { AnswerType = "Grid" } + }, + 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); + } + + [Test] + public async Task RecognitionComplexImageTaskWithNumericAnswer_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.ComplexImageTask.CreateRecognitionComplexImageTask(); + var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithNumericAnswer(); + + 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 + { + answer = expectedResult.Solution.Answer.NumericAnswer, + metadata = new { AnswerType = "NumericArray" } + }, + 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); + } + [Test] public async Task RecaptchaV2_IncorrectWebsiteUrl_ShouldThrowValidationException() { diff --git a/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs b/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs index 2873173..652ca8c 100644 --- a/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs +++ b/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs @@ -311,7 +311,24 @@ public static FunCaptchaComplexImageTaskRequest CreateFunCaptchaComplexImageTask }; } - public static CaptchaResult CreateSolution() + + public static RecognitionComplexImageTaskRequest CreateRecognitionComplexImageTask() + { + return new RecognitionComplexImageTaskRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + Metadata = new RecognitionComplexImageTaskRequest.RecognitionMetadata + { + Task = Gen.RandomString(), + TaskArgument = Gen.RandomString() + }, + ImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), + ImagesBase64 = Gen.ListOfValues(Gen.RandomString), + UserAgent = Gen.UserAgent() + }; + } + + public static CaptchaResult CreateGridComplexImageTaskSolution() { return new CaptchaResult { @@ -322,6 +339,32 @@ public static CaptchaResult CreateSolution() } }; } + + public static CaptchaResult CreateDynamicComplexImageTaskSolutionWithGridAnswer() + { + return new CaptchaResult + { + Error = null, + Solution = new DynamicComplexImageTaskResponse + { + Answer = new RecognitionAnswer{ GridAnswer = Gen.ArrayOfValues(Gen.RandomBool) }, + Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata { AnswerType = "Grid" } + } + }; + } + + public static CaptchaResult CreateDynamicComplexImageTaskSolutionWithNumericAnswer() + { + return new CaptchaResult + { + Error = null, + Solution = new DynamicComplexImageTaskResponse + { + Answer = new RecognitionAnswer { NumericAnswer = Gen.ArrayOfValues(Gen.RandomDecimal) }, + Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata { AnswerType = "NumericArray" } + } + }; + } } public static class CustomTask @@ -409,6 +452,37 @@ public static CaptchaResult CreateBasiliskSolution() } }; } + + public static ImpervaCustomTaskRequest CreateImpervaTask() + { + return new ImpervaCustomTaskRequest(Gen.RandomString(), Gen.RandomString(), 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 CreateImpervaSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new CustomTaskResponse + { + Domains = new Dictionary + { + { + Gen.RandomString(), + new CustomTaskResponse.DomainInfo() + { + Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, + } + } + } + } + }; + } } public static class AmazonWaf diff --git a/CapMonsterCloud.Client.IntegrationTests/Sut.cs b/CapMonsterCloud.Client.IntegrationTests/Sut.cs index 22b48c1..57cc413 100644 --- a/CapMonsterCloud.Client.IntegrationTests/Sut.cs +++ b/CapMonsterCloud.Client.IntegrationTests/Sut.cs @@ -71,8 +71,8 @@ public async Task> SolveAsync ( public async Task> SolveAsync ( RecaptchaComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); - public async Task> SolveAsync ( - HCaptchaComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); + public async Task> SolveAsync ( + HCaptchaComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync ( FunCaptchaComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); @@ -92,6 +92,12 @@ public async Task> SolveAsync( public async Task> SolveAsync( BinanceTaskRequest request) => await _cloudClient.SolveAsync(request); + public async Task> SolveAsync( + ImpervaCustomTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + RecognitionComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); + public async Task GetBalanceAsync() { return await _cloudClient.GetBalanceAsync(); diff --git a/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj b/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj index 8662f22..96e416b 100644 --- a/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj +++ b/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -15,8 +15,8 @@ README.md logo.png https://github.com/ZennoLab/capmonstercloud-client-dotnet - 2.0.0 - Changed way of using proxy + 3.0.0 + Added RecognitionComplexImageTask. Added dynamic responses for HCaptchaComplexImageTask diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs index 307ffda..2cd856c 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Zennolab.CapMonsterCloud.Requests; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud { @@ -78,7 +79,7 @@ private static GetResultTimeouts GetTimeouts(Type type) } }, { - typeof(ComplexImageTaskRequestBase), + typeof(ComplexImageTaskRequestBase<>), new GetResultTimeouts { FirstRequestDelay = TimeSpan.FromMilliseconds(350), @@ -172,7 +173,16 @@ private static GetResultTimeouts GetTimeouts(Type type) RequestsInterval = TimeSpan.FromSeconds(1), Timeout = TimeSpan.FromSeconds(20) } - } + }, + { + typeof(ImpervaCustomTaskRequest), + new GetResultTimeouts + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + RequestsInterval = TimeSpan.FromSeconds(1), + Timeout = TimeSpan.FromSeconds(15) + } + }, }; } } diff --git a/CapMonsterCloud.Client/Json/RecognitionAnswerJsonConverter.cs b/CapMonsterCloud.Client/Json/RecognitionAnswerJsonConverter.cs new file mode 100644 index 0000000..18eef12 --- /dev/null +++ b/CapMonsterCloud.Client/Json/RecognitionAnswerJsonConverter.cs @@ -0,0 +1,82 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using Zennolab.CapMonsterCloud.Responses; + +namespace Zennolab.CapMonsterCloud.Json +{ + internal class RecognitionAnswerJsonConverter : JsonConverter + { + public override DynamicComplexImageTaskResponse ReadJson(JsonReader reader, Type objectType, DynamicComplexImageTaskResponse existingValue, bool hasExistingValue, JsonSerializer serializer) + { + JObject obj = JObject.Load(reader); + + if (!obj.ContainsKey("metadata") || !obj.ContainsKey("answer")) + throw new JsonSerializationException("Missing 'metadata' or 'answer' field in response"); + + string answerType = obj["metadata"]?["AnswerType"]?.ToString(); + if (string.IsNullOrEmpty(answerType)) + throw new JsonSerializationException("AnswerType is missing in metadata"); + + JToken answerToken = obj["answer"]; + DynamicComplexImageTaskResponse response = new DynamicComplexImageTaskResponse + { + Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata + { + AnswerType = answerType + } + }; + + switch (answerType) + { + case "NumericArray": + response.Answer = new RecognitionAnswer + { + NumericAnswer = answerToken.ToObject() + }; + break; + case "Grid": + response.Answer = new RecognitionAnswer + { + GridAnswer = answerToken.ToObject() + }; + break; + default: + throw new JsonSerializationException($"Unknown AnswerType: {answerType}"); + } + + return response; + } + + public override void WriteJson(JsonWriter writer, DynamicComplexImageTaskResponse value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + writer.WriteStartObject(); + + writer.WritePropertyName("answer"); + + if (value.Metadata?.AnswerType == "Grid" && value.Answer?.GridAnswer != null) + { + serializer.Serialize(writer, value.Answer.GridAnswer); + } + else if (value.Metadata?.AnswerType == "NumericArray" && value.Answer?.NumericAnswer != null) + { + serializer.Serialize(writer, value.Answer.NumericAnswer); + } + else + { + throw new JsonSerializationException("Invalid or missing answer data for the specified AnswerType."); + } + + writer.WritePropertyName("metadata"); + serializer.Serialize(writer, value.Metadata); + + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs b/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs index 2bd9719..56f6f18 100644 --- a/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs +++ b/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs @@ -8,7 +8,7 @@ namespace Zennolab.CapMonsterCloud.Requests /// /// ComplexImageTask recognition request /// - public abstract class ComplexImageTaskRequestBase : CaptchaRequestBase + public abstract class ComplexImageTaskRequestBase : CaptchaRequestBase where TResponse : CaptchaResponseBase { /// /// Recognition task type diff --git a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs index 32301d3..584be88 100644 --- a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs @@ -3,9 +3,28 @@ /// /// DataDome CustomTask recognition request /// - public sealed class DataDomeCustomTaskRequest : DataDomeCustomTaskRequestBase + public sealed class DataDomeCustomTaskRequest : CustomTaskRequestBase { /// - public DataDomeCustomTaskRequest(string datadomeCookie, string captchaUrl = null, string htmlPageBase64 = null) : base(datadomeCookie, captchaUrl, htmlPageBase64) { } + public override string Class => "DataDome"; + + /// + /// + /// These values will be set to Metadata property. + /// + /// - captchaUrl: "captchaUrl": "..." + /// Field is required if metadata.htmlPageBase64 is not filled. + /// You can take the link from the page with the captcha. + /// Often it looks like https://geo.captcha-delivery.com/captcha/?initialCid=... + /// + /// - htmlPageBase64: "htmlPageBase64": "PGh0bWw+PGhlYWQ+PHRpdGxlPmJs...N0E5QTA1" + /// Field is required if 'captchaUrl' is not filled. + /// A base64 encoded html page that comes with a 403 code and a Set-Cookie: datadome="..." header in response to a get request to the target site. + /// + /// - datadomeCookie: "datadomeCookie": "datadome=6BvxqELMoorFNoo7GT1...JyfP_mhz" + /// Field is required. Your cookies from datadome. You can get it on the page using "document.cookie" or in the Set-Cookie request header: "datadome=..." + /// + /// + public DataDomeCustomTaskRequest(string datadomeCookie, string captchaUrl, string htmlPageBase64) => Metadata = new { datadomeCookie, captchaUrl, htmlPageBase64 }; } } diff --git a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequestBase.cs b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequestBase.cs deleted file mode 100644 index a79939c..0000000 --- a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequestBase.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// DataDome CustomTask recognition request base - /// - public abstract class DataDomeCustomTaskRequestBase : CustomTaskRequestBase - { - /// - public override string Class => "DataDome"; - - /// - /// - /// These values will be set to Metadata property. - /// - /// - captchaUrl: "captchaUrl": "..." - /// Field is required if metadata.htmlPageBase64 is not filled. - /// You can take the link from the page with the captcha. - /// Often it looks like https://geo.captcha-delivery.com/captcha/?initialCid=... - /// - /// - htmlPageBase64: "htmlPageBase64": "PGh0bWw+PGhlYWQ+PHRpdGxlPmJs...N0E5QTA1" - /// Field is required if 'captchaUrl' is not filled. - /// A base64 encoded html page that comes with a 403 code and a Set-Cookie: datadome="..." header in response to a get request to the target site. - /// - /// - datadomeCookie: "datadomeCookie": "datadome=6BvxqELMoorFNoo7GT1...JyfP_mhz" - /// Field is required. Your cookies from datadome. You can get it on the page using "document.cookie" or in the Set-Cookie request header: "datadome=..." - /// - /// - protected DataDomeCustomTaskRequestBase(string datadomeCookie, string captchaUrl, string htmlPageBase64) => Metadata = new { datadomeCookie, captchaUrl, htmlPageBase64 }; - } -} diff --git a/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs b/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs index 2da4322..8e4a6fe 100644 --- a/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs +++ b/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs @@ -1,12 +1,13 @@ using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// /// ComplexImageTask recognition request for funcaptcha images /// - public sealed class FunCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase + public sealed class FunCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase { /// /// Metadata for recognition diff --git a/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs b/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs index 4e6d8f7..e2f62eb 100644 --- a/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs +++ b/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs @@ -1,13 +1,14 @@ using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// /// ComplexImageTask recognition request for hcaptcha images /// - public sealed class HCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase + public sealed class HCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase { /// /// Metadata for recognition diff --git a/CapMonsterCloud.Client/Requests/ImpervaCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/ImpervaCustomTaskRequest.cs new file mode 100644 index 0000000..22309e4 --- /dev/null +++ b/CapMonsterCloud.Client/Requests/ImpervaCustomTaskRequest.cs @@ -0,0 +1,28 @@ +namespace Zennolab.CapMonsterCloud.Requests +{ + /// + /// Imperva CustomTask recognition request + /// + public class ImpervaCustomTaskRequest : CustomTaskRequestBase + { + /// + public override string Class => "Imperva"; + + /// + /// + /// These values will be set to Metadata property. + /// + /// - incapsulaScriptBase64: "incapsulaScriptBase64": "..." + /// the base64-encoded content of the Incapsula JavaScript script. To obtain this value, you need to go to the script page. + /// The script content loaded by this src must be encoded in base64 format. This will be the value for the parameter incapsulaScriptBase64. + /// + /// - incapsulaSessionCookie: "incapsulaSessionCookie": "l/LsGnrvyB9lNhXI8borDKa2IGcAAAAAX0qAEHheCWuNDquzwb44cw=" + /// Your cookies from Incapsula. You can obtain them on the page using "document.cookie" or in the request header Set-Cookie: "incap_sess_*=..." + /// + /// - reese84UrlEndpoint: "reese84UrlEndpoint": "Built-with-the-For-hopence-Hurleysurfecting-the-" + /// The name of the endpoint where the reese84 fingerprint is sent can be found among the requests and ends with ?d=site.com + /// + /// + public ImpervaCustomTaskRequest(string incapsulaScriptBase64, string incapsulaSessionCookie, string reese84UrlEndpoint) => Metadata = new { incapsulaScriptBase64, incapsulaSessionCookie, reese84UrlEndpoint }; + } +} diff --git a/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs b/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs index 257d004..3b633b3 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs @@ -1,13 +1,14 @@ using System; using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// /// ComplexImageTask recognition request for recaptcha images /// - public sealed class RecaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase + public sealed class RecaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase { /// /// Metadata for recognition diff --git a/CapMonsterCloud.Client/Requests/RecognitionComplexImageTaskRequest.cs b/CapMonsterCloud.Client/Requests/RecognitionComplexImageTaskRequest.cs new file mode 100644 index 0000000..679f946 --- /dev/null +++ b/CapMonsterCloud.Client/Requests/RecognitionComplexImageTaskRequest.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; + +namespace Zennolab.CapMonsterCloud.Requests +{ + /// + /// ComplexImageTask recognition request for Recognition images + /// + public sealed class RecognitionComplexImageTaskRequest : ComplexImageTaskRequestBase + { + /// + public override string Class => "recognition"; + + /// + /// Metadata for recognition + /// + public sealed class RecognitionMetadata + { + /// + /// Task definition. Required. + /// + /// + /// oocl_rotate_new + /// + [Required] + [JsonProperty("Task")] + public string Task { get; set; } + + /// + /// Additional task argument definition. Optional. + /// + /// + /// 546 + /// + [JsonProperty("TaskArgument")] + public string TaskArgument { get; set; } + } + + /// + /// Metadata for recognition + /// + [JsonProperty("metadata")] + [Required] + public RecognitionMetadata Metadata { get; set; } + } +} diff --git a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs index 6d5369d..ef9efb5 100644 --- a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs @@ -1,9 +1,22 @@ -namespace Zennolab.CapMonsterCloud.Requests +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; + +namespace Zennolab.CapMonsterCloud.Requests { /// /// TenDi CustomTask recognition request /// - public sealed class TenDiCustomTaskRequest : TenDiCustomTaskRequestBase - { + public sealed class TenDiCustomTaskRequest : CustomTaskRequestBase + { + /// + public override string Class => "TenDI"; + + /// + /// captchaAppId. For example "websiteKey": "189123456" - is a unique parameter for your site. You can take it from an html page with a captcha or from traffic. + /// + /// 189123456 + [JsonProperty("websiteKey", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } } } diff --git a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequestBase.cs b/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequestBase.cs deleted file mode 100644 index d1baef1..0000000 --- a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequestBase.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// TenDi CustomTask recognition request base - /// - public abstract class TenDiCustomTaskRequestBase : CustomTaskRequestBase - { - /// - public override string Class => "TenDI"; - - /// - /// captchaAppId. For example "websiteKey": "189123456" - is a unique parameter for your site. You can take it from an html page with a captcha or from traffic (see description below). - /// - /// 189123456 - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } - } -} diff --git a/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs b/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs index 72d472c..2b2be64 100644 --- a/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs +++ b/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs @@ -1,5 +1,8 @@ namespace Zennolab.CapMonsterCloud.Responses { + /// + /// CaptchaResponse base class + /// public abstract class CaptchaResponseBase { } diff --git a/CapMonsterCloud.Client/Responses/DynamicComplexImageTaskResponse.cs b/CapMonsterCloud.Client/Responses/DynamicComplexImageTaskResponse.cs new file mode 100644 index 0000000..a448d0f --- /dev/null +++ b/CapMonsterCloud.Client/Responses/DynamicComplexImageTaskResponse.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; +using Zennolab.CapMonsterCloud.Json; +using static Zennolab.CapMonsterCloud.Requests.RecognitionComplexImageTaskRequest; + +namespace Zennolab.CapMonsterCloud.Responses +{ + /// + /// Response for Recognition grid-like tasks + /// + [JsonConverter(typeof(RecognitionAnswerJsonConverter))] + public class DynamicComplexImageTaskResponse : CaptchaResponseBase + { + /// + /// Metadata class containing AnswerType + /// + public class RecognitionMetadata + { + [JsonProperty("AnswerType")] + public string AnswerType { get; set; } + } + + /// + /// Bool or decimal collection with answers + /// + /// + /// [false,true,false,true,false,false,true,false,false] + /// [4,4,4,4,4,3,1,2,2] + /// [130.90909] + /// + [JsonProperty("answer")] + public RecognitionAnswer Answer { get; set; } + + /// + /// Metadata containing the answer type + /// + [JsonProperty("metadata")] + public RecognitionMetadata Metadata { get; set; } + } +} diff --git a/CapMonsterCloud.Client/Responses/RecognitionAnswer.cs b/CapMonsterCloud.Client/Responses/RecognitionAnswer.cs new file mode 100644 index 0000000..13fb70d --- /dev/null +++ b/CapMonsterCloud.Client/Responses/RecognitionAnswer.cs @@ -0,0 +1,24 @@ +namespace Zennolab.CapMonsterCloud.Responses +{ + /// + /// Recognition captcha answer + /// + public class RecognitionAnswer + { + /// + /// Decimal answer + /// + public decimal[] NumericAnswer { get; set; } + + /// + /// Bool answer + /// + public bool[] GridAnswer { get; set; } + + /// + public bool IsNumeric => NumericAnswer != null; + + /// + public bool IsGrid => GridAnswer != null; + } +}