diff --git a/CapMonsterCloud.Client.IntegrationTests/CapMonsterCloud.Client.IntegrationTests.csproj b/CapMonsterCloud.Client.IntegrationTests/CapMonsterCloud.Client.IntegrationTests.csproj index 329d66e..8a7fe64 100644 --- a/CapMonsterCloud.Client.IntegrationTests/CapMonsterCloud.Client.IntegrationTests.csproj +++ b/CapMonsterCloud.Client.IntegrationTests/CapMonsterCloud.Client.IntegrationTests.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 enable false CapMonsterCloud.Client.IntegrationTests diff --git a/CapMonsterCloud.Client.IntegrationTests/Gen.cs b/CapMonsterCloud.Client.IntegrationTests/Gen.cs index 50b6c98..7cae428 100644 --- a/CapMonsterCloud.Client.IntegrationTests/Gen.cs +++ b/CapMonsterCloud.Client.IntegrationTests/Gen.cs @@ -3,123 +3,122 @@ using System.Linq; using System.Security.Cryptography; -namespace CapMonsterCloud.Client.IntegrationTests +namespace CapMonsterCloud.Client.IntegrationTests; + +public static class Gen { - public static class Gen + private static readonly Random Rnd = new(); + + public static int RandomInt() { - private static readonly Random Rnd = new(); + return RandomInt( + minValue: int.MinValue, + maxValue: int.MaxValue); + } - public static int RandomInt() - { - return RandomInt( - minValue: int.MinValue, - maxValue: int.MaxValue); - } + public static int RandomInt( + int minValue, + int maxValue = int.MaxValue) + { + return Rnd.Next( + minValue: minValue, + maxValue: maxValue); + } - public static int RandomInt( - int minValue, - int maxValue = int.MaxValue) + public static long RandomLong( + long minValue, + long maxValue) + { + if (maxValue <= minValue) { - return Rnd.Next( - minValue: minValue, - maxValue: maxValue); + throw new ArgumentOutOfRangeException(nameof(maxValue), "max must be > min!"); } - public static long RandomLong( - long minValue, - long maxValue) - { - if (maxValue <= minValue) - { - throw new ArgumentOutOfRangeException(nameof(maxValue), "max must be > min!"); - } - - ulong uRange = (ulong)(maxValue - minValue); - - ulong ulongRand; - do - { - byte[] buf = new byte[8]; - Rnd.NextBytes(buf); - ulongRand = (ulong)BitConverter.ToInt64(buf, 0); - } - while (ulongRand > ulong.MaxValue - (((ulong.MaxValue % uRange) + 1) % uRange)); - - return (long)(ulongRand % uRange) + minValue; - } + ulong uRange = (ulong)(maxValue - minValue); - public static decimal RandomDecimal() + ulong ulongRand; + do { - return RandomDecimal( - minValue: decimal.MinValue, - maxValue: decimal.MaxValue); + byte[] buf = new byte[8]; + Rnd.NextBytes(buf); + ulongRand = (ulong)BitConverter.ToInt64(buf, 0); } + while (ulongRand > ulong.MaxValue - (((ulong.MaxValue % uRange) + 1) % uRange)); - public static decimal RandomDecimal( - decimal minValue, - decimal maxValue) - { - return Convert.ToDecimal(RandomDouble((double)minValue, (double)maxValue)); - } + return (long)(ulongRand % uRange) + minValue; + } - public static double RandomDouble( - double minValue, - double maxValue) => - minValue + (Rnd.NextDouble() * (maxValue - minValue)); + public static decimal RandomDecimal() + { + return RandomDecimal( + minValue: decimal.MinValue, + maxValue: decimal.MaxValue); + } - public static string RandomString() - { - return RandomString(36); - } - - public static string RandomString(int length) - { - var values = Enumerable.Repeat(0, (length / 36) + 1).Select(x => Guid.NewGuid().ToString()); - return string.Join(string.Empty, values).Substring(0, length); - } + public static decimal RandomDecimal( + decimal minValue, + decimal maxValue) + { + return Convert.ToDecimal(RandomDouble((double)minValue, (double)maxValue)); + } - public static T RandomElement(this IEnumerable elements) => - elements.OrderBy(x => RandomString()).First(); + public static double RandomDouble( + double minValue, + double maxValue) => + minValue + (Rnd.NextDouble() * (maxValue - minValue)); - public static T RandomEnum() - { - var enumValues = Enum.GetValues(typeof(T)).Cast(); - return enumValues.RandomElement(); - } + public static string RandomString() + { + return RandomString(36); + } + + public static string RandomString(int length) + { + var values = Enumerable.Repeat(0, (length / 36) + 1).Select(x => Guid.NewGuid().ToString()); + return string.Join(string.Empty, values).Substring(0, length); + } - public static List ListOfValues(Func createFunc) => - ListOfValues(createFunc, 5); + public static T RandomElement(this IEnumerable elements) => + elements.OrderBy(x => RandomString()).First(); - public static List ListOfValues(Func createFunc, int count) - { - if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count)); - return Enumerable.Repeat(0, count).Select(x => createFunc()).ToList(); - } + public static T RandomEnum() + { + var enumValues = Enum.GetValues(typeof(T)).Cast(); + return enumValues.RandomElement(); + } - public static T[] ArrayOfValues(Func createFunc) => - ArrayOfValues(createFunc, 5); + public static List ListOfValues(Func createFunc) => + ListOfValues(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 List ListOfValues(Func createFunc, int count) + { + if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count)); + return Enumerable.Repeat(0, count).Select(x => createFunc()).ToList(); + } - public static bool RandomBool() => Rnd.NextDouble() >= 0.5; - - public static string RandomGuid() - { - return Guid.NewGuid().ToString(); - } - - public static string UserAgent() - { - return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"; - } - - public static Uri RandomUri() - { - return new Uri($"https://{RandomString()}.zennolab.com"); - } + 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() + { + return Guid.NewGuid().ToString(); + } + + public static string UserAgent() + { + return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"; + } + + public static Uri RandomUri() + { + return new Uri($"https://{RandomString()}.zennolab.com"); } } diff --git a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs index cfcf589..21c4246 100644 --- a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs +++ b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs @@ -3,1251 +3,1312 @@ using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json; using NUnit.Framework; +using Zennolab.CapMonsterCloud; -namespace CapMonsterCloud.Client.IntegrationTests +namespace CapMonsterCloud.Client.IntegrationTests; + +public class IntegrationTests { - public class IntegrationTests + [Test] + public async Task GetBalance_ShouldReturn() { - [Test] - public async Task GetBalance_ShouldReturn() - { - var clientKey = Gen.RandomString(); - var balance = Gen.RandomDecimal(); + var clientKey = Gen.RandomString(); + var balance = Gen.RandomDecimal(); - var captchaResults = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.GetBalance, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey }) - ), - }; + var captchaResults = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.GetBalance, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey }) + ), + }; - var actualResponses = new List - { - new { balance = balance, errorId = 0, errorCode = (string)null! }, - }; + var actualResponses = new List + { + new { balance = balance, errorId = 0, errorCode = (string)null! }, + }; - var sut = new Sut(clientKey); - sut.SetupHttpServer(actualResponses); + var sut = new Sut(clientKey); + sut.SetupHttpServer(actualResponses); - var actual = await sut.GetBalanceAsync(); + var actual = await sut.GetBalanceAsync(); - sut.GetActualRequests().Should().BeEquivalentTo(captchaResults); - actual.Should().Be(balance); - } - - [Test] - public async Task ImageToText_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + sut.GetActualRequests().Should().BeEquivalentTo(captchaResults); + actual.Should().Be(balance); + } + + [Test] + public async Task ImageToText_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - var captchaRequest = ObjectGen.ImageToText.CreateTask(); - var expectedResult = ObjectGen.ImageToText.CreateSolution(); + var captchaRequest = ObjectGen.ImageToText.CreateTask(); + var expectedResult = ObjectGen.ImageToText.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 + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - text = expectedResult.Solution.Value - }, - 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_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + text = expectedResult.Solution.Value + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.RecaptchaV2.CreateTask(); - var expectedResult = ObjectGen.RecaptchaV2.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task RecaptchaV2_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.RecaptchaV2.CreateTask(); + var expectedResult = ObjectGen.RecaptchaV2.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - gRecaptchaResponse = expectedResult.Solution.Value - }, - 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 RecaptchaV2Enterprise_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + gRecaptchaResponse = expectedResult.Solution.Value + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.RecaptchaV2Enterprise.CreateTask(); - var expectedResult = ObjectGen.RecaptchaV2Enterprise.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task RecaptchaV2Enterprise_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.RecaptchaV2Enterprise.CreateTask(); + var expectedResult = ObjectGen.RecaptchaV2Enterprise.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - gRecaptchaResponse = expectedResult.Solution.Value - }, - 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 RecaptchaV3Proxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + gRecaptchaResponse = expectedResult.Solution.Value + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.RecaptchaV3Proxyless.CreateTask(); - var expectedResult = ObjectGen.RecaptchaV3Proxyless.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task RecaptchaV3Proxyless_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.RecaptchaV3Proxyless.CreateTask(); + var expectedResult = ObjectGen.RecaptchaV3Proxyless.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - gRecaptchaResponse = expectedResult.Solution.Value - }, - 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 FunCaptcha_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + gRecaptchaResponse = expectedResult.Solution.Value + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.FunCaptcha.CreateTask(); - var expectedResult = ObjectGen.FunCaptcha.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task RecaptchaV3Enterprise_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.RecaptchaV3Enterprise.CreateTask(); + var expectedResult = ObjectGen.RecaptchaV3Enterprise.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - token = expectedResult.Solution.Value - }, - 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 HCaptcha_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + gRecaptchaResponse = expectedResult.Solution.Value + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.HCaptcha.CreateHCaptchaTask(); - var expectedResult = ObjectGen.HCaptcha.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task FunCaptcha_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.FunCaptcha.CreateTask(); + var expectedResult = ObjectGen.FunCaptcha.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - respKey = expectedResult.Solution.RespKey, - userAgent = expectedResult.Solution.UserAgent, - gRecaptchaResponse = expectedResult.Solution.Value - }, - 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 GeeTest_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + token = expectedResult.Solution.Value + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.GeeTest.CreateTask(); - var expectedResult = ObjectGen.GeeTest.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task HCaptcha_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.HCaptcha.CreateHCaptchaTask(); + var expectedResult = ObjectGen.HCaptcha.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - challenge = expectedResult.Solution.Challenge, - validate = expectedResult.Solution.Validate, - seccode = expectedResult.Solution.SecCode, - captcha_id = expectedResult.Solution.CaptchaId, - lot_number = expectedResult.Solution.LotNumber, - pass_token = expectedResult.Solution.PassToken, - gen_time = expectedResult.Solution.GenTime, - captcha_output = expectedResult.Solution.CaptchaOutput - }, - 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 Turnstile_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + respKey = expectedResult.Solution.RespKey, + userAgent = expectedResult.Solution.UserAgent, + gRecaptchaResponse = expectedResult.Solution.Value + }, + 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 GeeTest_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - var captchaRequest = ObjectGen.Turnstile.CreateTask(); - var expectedResult = ObjectGen.Turnstile.CreateSolution(); + var captchaRequest = ObjectGen.GeeTest.CreateTask(); + var expectedResult = ObjectGen.GeeTest.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 + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - token = expectedResult.Solution.Value, - cf_clearance = expectedResult.Solution.Clearance - }, - 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 DataDome_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + challenge = expectedResult.Solution.Challenge, + validate = expectedResult.Solution.Validate, + seccode = expectedResult.Solution.SecCode, + captcha_id = expectedResult.Solution.CaptchaId, + lot_number = expectedResult.Solution.LotNumber, + pass_token = expectedResult.Solution.PassToken, + gen_time = expectedResult.Solution.GenTime, + captcha_output = expectedResult.Solution.CaptchaOutput + }, + 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); + } - var captchaRequest = ObjectGen.CustomTask.CreateDataDomeTask(); - var expectedResult = ObjectGen.CustomTask.CreateDataDomeSolution(); + [Test] + public async Task Turnstile_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + var captchaRequest = ObjectGen.Turnstile.CreateTask(); + var expectedResult = ObjectGen.Turnstile.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - ( - 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 + status = "ready", + solution = new + { + token = expectedResult.Solution.Value, + cf_clearance = expectedResult.Solution.Clearance + }, + 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 DataDome_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.CustomTask.CreateDataDomeTask(); + var expectedResult = ObjectGen.CustomTask.CreateDataDomeSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - domains = expectedResult.Solution.Domains, - url = expectedResult.Solution.Url, - fingerprint = expectedResult.Solution.Fingerprint, - headers = expectedResult.Solution.Headers - }, - 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 Imperva_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + domains = expectedResult.Solution.Domains, + url = expectedResult.Solution.Url, + fingerprint = expectedResult.Solution.Fingerprint, + headers = expectedResult.Solution.Headers + }, + 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); + } - var captchaRequest = ObjectGen.CustomTask.CreateImpervaTask(); - var expectedResult = ObjectGen.CustomTask.CreateImpervaSolution(); + [Test] + public async Task Imperva_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - 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 + var captchaRequest = ObjectGen.CustomTask.CreateImpervaTask(); + var expectedResult = ObjectGen.CustomTask.CreateImpervaSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = 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() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + domains = expectedResult.Solution.Domains + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.ComplexImageTask.CreateRecaptchaComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateGridComplexImageTaskSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task RecaptchaComplexImageTask_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.ComplexImageTask.CreateRecaptchaComplexImageTask(); + var expectedResult = ObjectGen.ComplexImageTask.CreateGridComplexImageTaskSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - answer = expectedResult.Solution.Answer - }, - 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 HCaptchaComplexImageTaskWithNumericAnswer_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + answer = expectedResult.Solution.Answer + }, + 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 HCaptchaComplexImageTaskWithNumericAnswer_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - var captchaRequest = ObjectGen.ComplexImageTask.CreateHCaptchaComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithNumericAnswer(); + var captchaRequest = ObjectGen.ComplexImageTask.CreateHCaptchaComplexImageTask(); + 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 + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = 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 HCaptchaComplexImageTaskWithGridAnswer_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + answer = expectedResult.Solution.Answer.NumericAnswer, + metadata = new { AnswerType = "NumericArray" } + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.ComplexImageTask.CreateHCaptchaComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithGridAnswer(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + 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: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = 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() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + answer = expectedResult.Solution.Answer.GridAnswer, + metadata = new { AnswerType = "Grid" } + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.ComplexImageTask.CreateFunCaptchaComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateGridComplexImageTaskSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task FunCaptchaComplexImageTask_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.ComplexImageTask.CreateFunCaptchaComplexImageTask(); + var expectedResult = ObjectGen.ComplexImageTask.CreateGridComplexImageTaskSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - answer = expectedResult.Solution.Answer - }, - 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 RecognitionComplexImageTaskWithGridAnswer_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + answer = expectedResult.Solution.Answer + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.ComplexImageTask.CreateRecognitionComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithGridAnswer(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + 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: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = 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(); + answer = expectedResult.Solution.Answer.GridAnswer, + metadata = new { AnswerType = "Grid" } + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.ComplexImageTask.CreateRecognitionComplexImageTask(); - var expectedResult = ObjectGen.ComplexImageTask.CreateDynamicComplexImageTaskSolutionWithNumericAnswer(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + 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: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = 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() - { - var clientKey = Gen.RandomString(); - var captchaRequest = ObjectGen.RecaptchaV2.CreateTask( - websiteUrl: "incorrect url"); - - var sut = new Sut(clientKey); - sut.SetupHttpServer(new List()); - - Func actual = () => sut.SolveAsync(captchaRequest); - - await actual.Should().ThrowAsync() - .WithMessage("*The WebsiteUrl field is not a valid fully-qualified*URL*"); - } + 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() + { + var clientKey = Gen.RandomString(); + var captchaRequest = ObjectGen.RecaptchaV2.CreateTask( + websiteUrl: "incorrect url"); + + var sut = new Sut(clientKey); + sut.SetupHttpServer(new List()); + + Func actual = () => sut.SolveAsync(captchaRequest); - [Test] - public async Task RecaptchaV2_IncorrectWebsiteKey_ShouldThrowValidationException() - { - var clientKey = Gen.RandomString(); - var captchaRequest = ObjectGen.RecaptchaV2.CreateTask( - websiteKey: string.Empty); - - var sut = new Sut(clientKey); - sut.SetupHttpServer(new List()); - - Func actual = () => sut.SolveAsync(captchaRequest); - - _ = await actual.Should().ThrowAsync() - .WithMessage("*The field WebsiteKey must be a string with a minimum length of 1*"); - } + await actual.Should().ThrowAsync() + .WithMessage("*The WebsiteUrl field is not a valid fully-qualified*URL*"); + } + + [Test] + public async Task RecaptchaV2_IncorrectWebsiteKey_ShouldThrowValidationException() + { + var clientKey = Gen.RandomString(); + var captchaRequest = ObjectGen.RecaptchaV2.CreateTask( + websiteKey: string.Empty); - [Test] - public async Task RecaptchaV2_IncorrectProxyPort_ShouldThrowArgumentException() - { - Action actual = () => ObjectGen.RecaptchaV2.CreateTask( - proxyPort: Gen.RandomInt(65535)); + var sut = new Sut(clientKey); + sut.SetupHttpServer(new List()); - _ = actual.Should().Throw() - .WithMessage("*Proxy port must be between 0 and 65535*"); - } + Func actual = () => sut.SolveAsync(captchaRequest); - [Test] - public async Task RecaptchaV3Proxyless_IncorrectMinScore_ShouldThrowValidationException() - { - var clientKey = Gen.RandomString(); - var captchaRequest = ObjectGen.RecaptchaV3Proxyless.CreateTask( - minScore: Gen.RandomBool() ? Gen.RandomDouble(0, 0.09) : Gen.RandomDouble(0.91, 1.5)); - - var sut = new Sut(clientKey); - sut.SetupHttpServer(new List()); - - Func actual = () => sut.SolveAsync(captchaRequest); - - _ = await actual.Should().ThrowAsync() - .WithMessage("*The field MinScore must be between 0?1 and 0?9*"); - } + _ = await actual.Should().ThrowAsync() + .WithMessage("*The field WebsiteKey must be a string with a minimum length of 1*"); + } + + [Test] + public async Task RecaptchaV2_IncorrectProxyPort_ShouldThrowArgumentException() + { + Action actual = () => ObjectGen.RecaptchaV2.CreateTask( + proxyPort: Gen.RandomInt(65535)); + + _ = actual.Should().Throw() + .WithMessage("*Proxy port must be between 0 and 65535*"); + } + + [Test] + public async Task RecaptchaV3Proxyless_IncorrectMinScore_ShouldThrowValidationException() + { + var clientKey = Gen.RandomString(); + var captchaRequest = ObjectGen.RecaptchaV3Proxyless.CreateTask( + minScore: Gen.RandomBool() ? Gen.RandomDouble(0, 0.09) : Gen.RandomDouble(0.91, 1.5)); - [Test] - public async Task ImageToTextRequest_IncorrectRecognizingThreshold_ShouldThrowValidationException() - { - var clientKey = Gen.RandomString(); - var captchaRequest = ObjectGen.ImageToText.CreateTask( - recognizingThreshold: Convert.ToByte(Gen.RandomInt(101, 150))); - - var sut = new Sut(clientKey); - sut.SetupHttpServer(new List()); - - Func actual = () => sut.SolveAsync(captchaRequest); - - _ = await actual.Should().ThrowAsync() - .WithMessage("*The field RecognizingThreshold must be between 0 and 100*"); - } + var sut = new Sut(clientKey); + sut.SetupHttpServer(new List()); + + Func actual = () => sut.SolveAsync(captchaRequest); - [Test] - public async Task GeeTest_IncorrectGt_ShouldThrowValidationException() - { - var clientKey = Gen.RandomString(); - var captchaRequest = ObjectGen.GeeTest.CreateTask( - gt: string.Empty); - - var sut = new Sut(clientKey); - sut.SetupHttpServer(new List()); - - Func actual = () => sut.SolveAsync(captchaRequest); - - _ = await actual.Should().ThrowAsync() - .WithMessage("*The field Gt must be a string with a minimum length of 1*"); - } - - [Test] - public async Task AmazonWaf_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + _ = await actual.Should().ThrowAsync() + .WithMessage("*The field MinScore must be between 0?1 and 0?9*"); + } - var captchaRequest = ObjectGen.AmazonWaf.CreateTask(); - var expectedResult = ObjectGen.AmazonWaf.CreateSolution(); + [Test] + public async Task RecaptchaV3Enterprise_IncorrectMinScore_ShouldThrowValidationException() + { + var clientKey = Gen.RandomString(); + var captchaRequest = ObjectGen.RecaptchaV3Enterprise.CreateTask( + minScore: Gen.RandomBool() ? Gen.RandomDouble(0, 0.09) : Gen.RandomDouble(0.91, 1.5)); + + var sut = new Sut(clientKey); + sut.SetupHttpServer(new List()); - 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 - { - existing_token = expectedResult.Solution.ExistingToken, - captcha_voucher = expectedResult.Solution.CaptchaVoucher, - userAgent = expectedResult.Solution.UserAgent, - cookies = expectedResult.Solution.Cookies - }, - 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 TenDi_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + Func actual = () => sut.SolveAsync(captchaRequest); + + _ = await actual.Should().ThrowAsync() + .WithMessage("*The field MinScore must be between 0?1 and 0?9*"); + } + + [Test] + public async Task ImageToTextRequest_IncorrectRecognizingThreshold_ShouldThrowValidationException() + { + var clientKey = Gen.RandomString(); + var captchaRequest = ObjectGen.ImageToText.CreateTask( + recognizingThreshold: Convert.ToByte(Gen.RandomInt(101, 150))); - var captchaRequest = ObjectGen.CustomTask.CreateTenDiTask(); - var expectedResult = ObjectGen.CustomTask.CreateTenDiSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(new List()); - 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 + Func actual = () => sut.SolveAsync(captchaRequest); + + _ = await actual.Should().ThrowAsync() + .WithMessage("*The field RecognizingThreshold must be between 0 and 100*"); + } + + [Test] + public async Task GeeTest_IncorrectGt_ShouldThrowValidationException() + { + var clientKey = Gen.RandomString(); + var captchaRequest = ObjectGen.GeeTest.CreateTask( + gt: string.Empty); + + var sut = new Sut(clientKey); + sut.SetupHttpServer(new List()); + + Func actual = () => sut.SolveAsync(captchaRequest); + + _ = await actual.Should().ThrowAsync() + .WithMessage("*The field Gt must be a string with a minimum length of 1*"); + } + + [Test] + public async Task AmazonWaf_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.AmazonWaf.CreateTask(); + var expectedResult = ObjectGen.AmazonWaf.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - data = expectedResult.Solution.Data, - headers = expectedResult.Solution.Headers - }, - 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 Basilisk_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + existing_token = expectedResult.Solution.ExistingToken, + captcha_voucher = expectedResult.Solution.CaptchaVoucher, + userAgent = expectedResult.Solution.UserAgent, + cookies = expectedResult.Solution.Cookies + }, + 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); + } - var captchaRequest = ObjectGen.CustomTask.CreateBasiliskTask(); - var expectedResult = ObjectGen.CustomTask.CreateBasiliskSolution(); + [Test] + public async Task TenDi_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - 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 + var captchaRequest = ObjectGen.CustomTask.CreateTenDiTask(); + var expectedResult = ObjectGen.CustomTask.CreateTenDiSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - data = expectedResult.Solution.Data, - headers = expectedResult.Solution.Headers - }, - 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 Binance_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + data = expectedResult.Solution.Data, + headers = expectedResult.Solution.Headers + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.BinanceTask.CreateTask(); - var expectedResult = ObjectGen.BinanceTask.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task Basilisk_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.CustomTask.CreateBasiliskTask(); + var expectedResult = ObjectGen.CustomTask.CreateBasiliskSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - token = expectedResult.Solution.Value, - userAgent = expectedResult.Solution.UserAgent, - cookies = expectedResult.Solution.Cookies - }, - 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 Temu_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + data = expectedResult.Solution.Data, + headers = expectedResult.Solution.Headers + }, + errorId = 0, + errorCode = (string)null! + } + }; - var captchaRequest = ObjectGen.TemuTask.CreateTask(); - var expectedResult = ObjectGen.TemuTask.CreateSolution(); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - 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 + var actual = await sut.SolveAsync(captchaRequest); + + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task Binance_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.BinanceTask.CreateTask(); + var expectedResult = ObjectGen.BinanceTask.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = 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 MTCaptcha_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + token = expectedResult.Solution.Value, + userAgent = expectedResult.Solution.UserAgent, + cookies = expectedResult.Solution.Cookies + }, + 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 Temu_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - var captchaRequest = ObjectGen.MTCaptchaTask.CreateTask(); - var expectedResult = ObjectGen.MTCaptchaTask.CreateSolution(); + 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 + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new { token = expectedResult.Solution.Value }, - errorId = 0, - errorCode = (string)null! - } - }; + domains = expectedResult.Solution.Domains + }, + errorId = 0, + errorCode = (string)null! + } + }; - var sut = new Sut(clientKey); - sut.SetupHttpServer(captchaResults); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - var actual = await sut.SolveAsync(captchaRequest); + var actual = await sut.SolveAsync(captchaRequest); - sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); - actual.Should().BeEquivalentTo(expectedResult); - } - - [Test] - public async Task Yidun_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task MTCaptcha_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - var captchaRequest = ObjectGen.YidunTask.CreateTask(); - var expectedResult = ObjectGen.YidunTask.CreateSolution(); + var captchaRequest = ObjectGen.MTCaptchaTask.CreateTask(); + var expectedResult = ObjectGen.MTCaptchaTask.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 + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new { token = expectedResult.Solution.Value }, - errorId = 0, - errorCode = (string)null! - } - }; + status = "ready", + solution = new { token = expectedResult.Solution.Value }, + errorId = 0, + errorCode = (string)null! + } + }; - var sut = new Sut(clientKey); - sut.SetupHttpServer(captchaResults); + var sut = new Sut(clientKey); + sut.SetupHttpServer(captchaResults); - var actual = await sut.SolveAsync(captchaRequest); + var actual = await sut.SolveAsync(captchaRequest); - sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); - actual.Should().BeEquivalentTo(expectedResult); - } - - [Test] - public async Task Prosopo_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); + sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); + actual.Should().BeEquivalentTo(expectedResult); + } + + [Test] + public async Task Yidun_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); - var captchaRequest = ObjectGen.ProsopoTask.CreateTask(); - var expectedResult = ObjectGen.ProsopoTask.CreateSolution(); + var captchaRequest = ObjectGen.YidunTask.CreateTask(); + var expectedResult = ObjectGen.YidunTask.CreateSolution(); - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - ( - 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 + status = "ready", + solution = new { token = expectedResult.Solution.Value }, + 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 Prosopo_ShouldSolve() + { + var clientKey = Gen.RandomString(); + var taskId = Gen.RandomInt(); + + var captchaRequest = ObjectGen.ProsopoTask.CreateTask(); + var expectedResult = ObjectGen.ProsopoTask.CreateSolution(); + + var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> + { + ( + Type: RequestType.CreateTask, + ExpectedRequest: CapMonsterCloudClient.ToJson(new + { clientKey = clientKey, task = captchaRequest, softId = 53 }) + ), + ( + Type: RequestType.GetTaskResult, + ExpectedRequest: CapMonsterCloudClient.ToJson(new { clientKey = clientKey, taskId = taskId }) + ), + }; + + var captchaResults = new List + { + new { taskId = taskId, errorId = 0, errorCode = (string)null! }, + new { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new + status = "ready", + solution = new { - status = "ready", - solution = new - { - token = expectedResult.Solution.Value - }, - 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); - } + token = expectedResult.Solution.Value + }, + 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 0abcf17..bf01c8b 100644 --- a/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs +++ b/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs @@ -4,674 +4,701 @@ using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Responses; -namespace CapMonsterCloud.Client.IntegrationTests +namespace CapMonsterCloud.Client.IntegrationTests; + +public static class ObjectGen { - public static class ObjectGen + public static class ImageToText { - public static class ImageToText + public static ImageToTextRequest CreateTask( + byte? recognizingThreshold = null) { - public static ImageToTextRequest CreateTask( - byte? recognizingThreshold = null) - { - return new ImageToTextRequest - { - Body = Gen.RandomString(), - CapMonsterModule = Gen.RandomString(), - RecognizingThreshold = recognizingThreshold ?? (byte)Gen.RandomInt(0,100), - CaseSensitive = Gen.RandomBool(), - Numeric = Gen.RandomBool(), - Math = Gen.RandomBool() - }; - } - - public static CaptchaResult CreateSolution() - { - return new CaptchaResult - { - Error = null, - Solution = new ImageToTextResponse - { - Value = Gen.RandomString(), - } - }; - } + return new ImageToTextRequest + { + Body = Gen.RandomString(), + CapMonsterModule = Gen.RandomString(), + RecognizingThreshold = recognizingThreshold ?? (byte)Gen.RandomInt(0,100), + CaseSensitive = Gen.RandomBool(), + Numeric = Gen.RandomBool(), + Math = Gen.RandomBool() + }; } - - public static class RecaptchaV2 + + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static RecaptchaV2Request CreateTask( - string? websiteUrl = null, - string? websiteKey = null, - int? proxyPort = null) + Error = null, + Solution = new ImageToTextResponse { - return new RecaptchaV2Request - { - WebsiteUrl = websiteUrl ?? Gen.RandomUri().ToString(), - WebsiteKey = websiteKey ?? Gen.RandomGuid(), - DataSValue = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Cookies = Gen.ListOfValues(Gen.RandomString).ToDictionary(_ => Gen.RandomString(), value => value), - NoCache = Gen.RandomBool(), - Proxy = new ProxyContainer(Gen.RandomString(), proxyPort ?? Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; + Value = Gen.RandomString(), } + }; + } + } - public static CaptchaResult CreateSolution() - { - return new CaptchaResult - { - Error = null, - Solution = new RecaptchaV2Response - { - Value = Gen.RandomString(), - } - }; - } - } - - public static class RecaptchaV3Proxyless + public static class RecaptchaV2 + { + public static RecaptchaV2Request CreateTask( + string? websiteUrl = null, + string? websiteKey = null, + int? proxyPort = null) + { + return new RecaptchaV2Request + { + WebsiteUrl = websiteUrl ?? Gen.RandomUri().ToString(), + WebsiteKey = websiteKey ?? Gen.RandomGuid(), + DataSValue = Gen.RandomString(), + UserAgent = Gen.UserAgent(), + Cookies = Gen.ListOfValues(Gen.RandomString).ToDictionary(_ => Gen.RandomString(), value => value), + NoCache = Gen.RandomBool(), + Proxy = new ProxyContainer(Gen.RandomString(), proxyPort ?? Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) + }; + } + + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static RecaptchaV3ProxylessRequest CreateTask( - double? minScore = null) + Error = null, + Solution = new RecaptchaV2Response { - return new RecaptchaV3ProxylessRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomGuid(), - MinScore = minScore ?? Gen.RandomDouble(0.1, 0.9), - PageAction = Gen.RandomString(), - NoCache = Gen.RandomBool() - }; + Value = Gen.RandomString(), } + }; + } + } - public static CaptchaResult CreateSolution() + public static class RecaptchaV3Proxyless + { + public static RecaptchaV3ProxylessRequest CreateTask( + double? minScore = null) + { + return new RecaptchaV3ProxylessRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomGuid(), + MinScore = minScore ?? Gen.RandomDouble(0.1, 0.9), + PageAction = Gen.RandomString(), + NoCache = Gen.RandomBool() + }; + } + + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new RecaptchaV3Response { - return new CaptchaResult - { - Error = null, - Solution = new RecaptchaV3Response - { - Value = Gen.RandomString(), - } - }; + Value = Gen.RandomString(), } - } - - public static class FunCaptcha + }; + } + } + + public static class RecaptchaV3Enterprise + { + public static RecaptchaV3EnterpriseRequest CreateTask( + double? minScore = null) + { + return new RecaptchaV3EnterpriseRequest { - public static FunCaptchaRequest CreateTask() + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomGuid(), + MinScore = minScore ?? Gen.RandomDouble(0.1, 0.9), + PageAction = Gen.RandomString(), + NoCache = Gen.RandomBool() + }; + } + + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new RecaptchaV3EnterpriseResponse { - return new FunCaptchaRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomGuid(), - Subdomain = Gen.RandomString(), - Data = Gen.RandomString(), - NoCache = Gen.RandomBool(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; + Value = Gen.RandomString(), } + }; + } + } + + public static class FunCaptcha + { + public static FunCaptchaRequest CreateTask() + { + return new FunCaptchaRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomGuid(), + Subdomain = Gen.RandomString(), + Data = Gen.RandomString(), + NoCache = Gen.RandomBool(), + Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) + }; + } - public static CaptchaResult CreateSolution() + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new FunCaptchaResponse { - return new CaptchaResult - { - Error = null, - Solution = new FunCaptchaResponse - { - Value = Gen.RandomString(), - } - }; + Value = Gen.RandomString(), } - } - - public static class HCaptcha + }; + } + } + + public static class HCaptcha + { + public static HCaptchaRequest CreateHCaptchaTask() + { + return new HCaptchaRequest { - public static HCaptchaRequest CreateHCaptchaTask() - { - return new HCaptchaRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomGuid(), - Invisible = Gen.RandomBool(), - Data = Gen.RandomString(), + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomGuid(), + Invisible = Gen.RandomBool(), + Data = Gen.RandomString(), #pragma warning disable CS0618 - UserAgent = Gen.UserAgent(), + UserAgent = Gen.UserAgent(), #pragma warning restore CS0618 - FallbackToActualUA = Gen.RandomBool(), - Cookies = Gen.ListOfValues(Gen.RandomString).ToDictionary(_ => Gen.RandomString(), value => value), - NoCache = Gen.RandomBool(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + FallbackToActualUA = Gen.RandomBool(), + Cookies = Gen.ListOfValues(Gen.RandomString).ToDictionary(_ => Gen.RandomString(), value => value), + NoCache = Gen.RandomBool(), + 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 HCaptchaResponse - { - RespKey = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Value = Gen.RandomString(), - } - }; - } - } - - public static class GeeTest + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static GeeTestRequest CreateTask( - string? gt = null) + Error = null, + Solution = new HCaptchaResponse { - return new GeeTestRequest() - { - WebsiteUrl = Gen.RandomUri().ToString(), - Gt = gt ?? Gen.RandomString(), - Version = Gen.RandomInt(3, 4), - InitParameters = new { riskType = "slide" }, - Challenge = Gen.RandomString(), - Subdomain = Gen.RandomString(), - GetLib = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; + RespKey = Gen.RandomString(), + UserAgent = Gen.UserAgent(), + Value = Gen.RandomString(), } + }; + } + } + + public static class GeeTest + { + public static GeeTestRequest CreateTask( + string? gt = null) + { + return new GeeTestRequest() + { + WebsiteUrl = Gen.RandomUri().ToString(), + Gt = gt ?? Gen.RandomString(), + Version = Gen.RandomInt(3, 4), + InitParameters = new { riskType = "slide" }, + Challenge = Gen.RandomString(), + Subdomain = Gen.RandomString(), + GetLib = Gen.RandomString(), + 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 GeeTestResponse - { - Challenge = Gen.RandomString(), - Validate = Gen.RandomString(), - SecCode = Gen.RandomString(), - CaptchaId = Gen.RandomString(), - LotNumber = Gen.RandomString(), - PassToken = Gen.RandomString(), - GenTime = Gen.RandomString(), - CaptchaOutput = Gen.RandomString() - } - }; - } - } - - public static class RecaptchaV2Enterprise + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static RecaptchaV2EnterpriseRequest CreateTask() - { - return new RecaptchaV2EnterpriseRequest() - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomString(), - EnterprisePayload = Gen.RandomString(), - DataSValue = Gen.RandomString(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + Error = null, + Solution = new GeeTestResponse + { + Challenge = Gen.RandomString(), + Validate = Gen.RandomString(), + SecCode = Gen.RandomString(), + CaptchaId = Gen.RandomString(), + LotNumber = Gen.RandomString(), + PassToken = Gen.RandomString(), + GenTime = Gen.RandomString(), + CaptchaOutput = Gen.RandomString() + } + }; + } + } + + public static class RecaptchaV2Enterprise + { + public static RecaptchaV2EnterpriseRequest CreateTask() + { + return new RecaptchaV2EnterpriseRequest() + { + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomString(), + EnterprisePayload = Gen.RandomString(), + DataSValue = Gen.RandomString(), + 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 RecaptchaV2EnterpriseResponse - { - Value = Gen.RandomString() - } - }; - } - } - - public static class Turnstile + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static TurnstileRequest CreateTask() + Error = null, + Solution = new RecaptchaV2EnterpriseResponse { - return new TurnstileRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomGuid(), - CloudflareTaskType = Gen.RandomString(), - PageAction = Gen.RandomString(), - Data = Gen.RandomString(), - PageData = Gen.RandomString(), - HtmlPageBase64 = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - ApiJsUrl = Gen.RandomUri().ToString(), - NoCache = Gen.RandomBool(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; + Value = Gen.RandomString() } + }; + } + } + + public static class Turnstile + { + public static TurnstileRequest CreateTask() + { + return new TurnstileRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomGuid(), + CloudflareTaskType = Gen.RandomString(), + PageAction = Gen.RandomString(), + Data = Gen.RandomString(), + PageData = Gen.RandomString(), + HtmlPageBase64 = Gen.RandomString(), + UserAgent = Gen.UserAgent(), + ApiJsUrl = Gen.RandomUri().ToString(), + NoCache = Gen.RandomBool(), + 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 TurnstileResponse - { - Value = Gen.RandomString(), - Clearance = Gen.RandomString() - } - }; - } - } - - public static class ComplexImageTask + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static RecaptchaComplexImageTaskRequest CreateRecaptchaComplexImageTask() + Error = null, + Solution = new TurnstileResponse { - return new RecaptchaComplexImageTaskRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - Metadata = new RecaptchaComplexImageTaskRequest.RecaptchaMetadata() - { - Grid = Gen.RandomEnum(), - Task = Gen.RandomString(), - TaskDefinition = Gen.RandomString() - }, - ImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), - ImagesBase64 = Gen.ListOfValues(Gen.RandomString), - UserAgent = Gen.UserAgent() - }; - } - - public static HCaptchaComplexImageTaskRequest CreateHCaptchaComplexImageTask() - { - return new HCaptchaComplexImageTaskRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - Metadata = new HCaptchaComplexImageTaskRequest.HCaptchaMetadata() - { - Task = Gen.RandomString(), - }, - ImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), - ImagesBase64 = Gen.ListOfValues(Gen.RandomString), - ExampleImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), - ExampleImagesBase64 = Gen.ListOfValues(Gen.RandomString), - UserAgent = Gen.UserAgent() - }; - } - - public static FunCaptchaComplexImageTaskRequest CreateFunCaptchaComplexImageTask() - { - return new FunCaptchaComplexImageTaskRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - Metadata = new FunCaptchaComplexImageTaskRequest.FunCaptchaMetadata() - { - Task = Gen.RandomString(), - }, - ImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), - ImagesBase64 = Gen.ListOfValues(Gen.RandomString), - UserAgent = Gen.UserAgent() - }; + Value = Gen.RandomString(), + Clearance = Gen.RandomString() } + }; + } + } + + public static class ComplexImageTask + { + public static RecaptchaComplexImageTaskRequest CreateRecaptchaComplexImageTask() + { + return new RecaptchaComplexImageTaskRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + Metadata = new RecaptchaComplexImageTaskRequest.RecaptchaMetadata() + { + Grid = Gen.RandomEnum(), + Task = Gen.RandomString(), + TaskDefinition = Gen.RandomString() + }, + ImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), + ImagesBase64 = Gen.ListOfValues(Gen.RandomString), + UserAgent = Gen.UserAgent() + }; + } + + public static HCaptchaComplexImageTaskRequest CreateHCaptchaComplexImageTask() + { + return new HCaptchaComplexImageTaskRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + Metadata = new HCaptchaComplexImageTaskRequest.HCaptchaMetadata() + { + Task = Gen.RandomString(), + }, + ImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), + ImagesBase64 = Gen.ListOfValues(Gen.RandomString), + ExampleImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), + ExampleImagesBase64 = Gen.ListOfValues(Gen.RandomString), + UserAgent = Gen.UserAgent() + }; + } + + public static FunCaptchaComplexImageTaskRequest CreateFunCaptchaComplexImageTask() + { + return new FunCaptchaComplexImageTaskRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + Metadata = new FunCaptchaComplexImageTaskRequest.FunCaptchaMetadata() + { + Task = Gen.RandomString(), + }, + ImageUrls = Gen.ListOfValues(Gen.RandomUri().ToString), + ImagesBase64 = Gen.ListOfValues(Gen.RandomString), + UserAgent = Gen.UserAgent() + }; + } - 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 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() + public static CaptchaResult CreateGridComplexImageTaskSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new GridComplexImageTaskResponse { - return new CaptchaResult - { - Error = null, - Solution = new GridComplexImageTaskResponse - { - Answer = Gen.ListOfValues(Gen.RandomBool), - } - }; + Answer = Gen.ListOfValues(Gen.RandomBool), } + }; + } - public static CaptchaResult CreateDynamicComplexImageTaskSolutionWithGridAnswer() + public static CaptchaResult CreateDynamicComplexImageTaskSolutionWithGridAnswer() + { + return new CaptchaResult + { + Error = null, + Solution = new DynamicComplexImageTaskResponse { - return new CaptchaResult - { - Error = null, - Solution = new DynamicComplexImageTaskResponse - { - Answer = new RecognitionAnswer{ GridAnswer = Gen.ArrayOfValues(Gen.RandomBool) }, - Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata { AnswerType = "Grid" } - } - }; + Answer = new RecognitionAnswer{ GridAnswer = Gen.ArrayOfValues(Gen.RandomBool) }, + Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata { AnswerType = "Grid" } } + }; + } - public static CaptchaResult CreateDynamicComplexImageTaskSolutionWithNumericAnswer() + public static CaptchaResult CreateDynamicComplexImageTaskSolutionWithNumericAnswer() + { + return new CaptchaResult + { + Error = null, + Solution = new DynamicComplexImageTaskResponse { - return new CaptchaResult - { - Error = null, - Solution = new DynamicComplexImageTaskResponse - { - Answer = new RecognitionAnswer { NumericAnswer = Gen.ArrayOfValues(Gen.RandomDecimal) }, - Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata { AnswerType = "NumericArray" } - } - }; + Answer = new RecognitionAnswer { NumericAnswer = Gen.ArrayOfValues(Gen.RandomDecimal) }, + Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata { AnswerType = "NumericArray" } } - } + }; + } + } - public static class CustomTask + public static class CustomTask + { + public static DataDomeCustomTaskRequest CreateDataDomeTask() + { + return new DataDomeCustomTaskRequest(Gen.RandomString(), Gen.RandomUri().ToString(), Gen.RandomString()) { - public static DataDomeCustomTaskRequest CreateDataDomeTask() - { - return new DataDomeCustomTaskRequest(Gen.RandomString(), Gen.RandomUri().ToString(), Gen.RandomString()) - { - WebsiteUrl = Gen.RandomUri().ToString(), - UserAgent = Gen.UserAgent(), - Domains = Gen.ListOfValues(Gen.RandomString), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + WebsiteUrl = Gen.RandomUri().ToString(), + UserAgent = Gen.UserAgent(), + Domains = Gen.ListOfValues(Gen.RandomString), + Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) + }; + } - public static CaptchaResult CreateDataDomeSolution() + public static CaptchaResult CreateDataDomeSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new CustomTaskResponse { - return new CaptchaResult + Domains = new Dictionary { - Error = null, - Solution = new CustomTaskResponse { - Domains = new Dictionary + Gen.RandomString(), + new CustomTaskResponse.DomainInfo() { - { - Gen.RandomString(), - new CustomTaskResponse.DomainInfo() - { - Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, - LocalStorage = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }, - }, - Url = Gen.RandomUri().ToString(), - Fingerprint = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, - Headers = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }; + Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, + LocalStorage = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } + } + }, + }, + Url = Gen.RandomUri().ToString(), + Fingerprint = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, + Headers = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } } + }; + } - public static TenDiCustomTaskRequest CreateTenDiTask() - { - return new TenDiCustomTaskRequest - { - WebsiteKey = Gen.RandomGuid(), - WebsiteUrl = Gen.RandomUri().ToString(), - UserAgent = Gen.UserAgent(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + public static TenDiCustomTaskRequest CreateTenDiTask() + { + return new TenDiCustomTaskRequest + { + WebsiteKey = Gen.RandomGuid(), + 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 CreateTenDiSolution() + public static CaptchaResult CreateTenDiSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new CustomTaskResponse { - return new CaptchaResult - { - Error = null, - Solution = new CustomTaskResponse - { - Headers = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, - Data = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }; + Headers = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, + Data = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } } + }; + } - public static BasiliskCustomTaskRequest CreateBasiliskTask() - { - return new BasiliskCustomTaskRequest - { - WebsiteKey = Gen.RandomGuid(), - WebsiteUrl = Gen.RandomUri().ToString(), - UserAgent = Gen.UserAgent(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + public static BasiliskCustomTaskRequest CreateBasiliskTask() + { + return new BasiliskCustomTaskRequest + { + WebsiteKey = Gen.RandomGuid(), + 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 CreateBasiliskSolution() + public static CaptchaResult CreateBasiliskSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new CustomTaskResponse { - return new CaptchaResult - { - Error = null, - Solution = new CustomTaskResponse - { - Headers = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, - Data = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }; + Headers = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, + Data = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } } + }; + } - 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 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() + public static CaptchaResult CreateImpervaSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new CustomTaskResponse { - return new CaptchaResult + Domains = new Dictionary { - Error = null, - Solution = new CustomTaskResponse { - Domains = new Dictionary + Gen.RandomString(), + new CustomTaskResponse.DomainInfo() { - { - Gen.RandomString(), - new CustomTaskResponse.DomainInfo() - { - Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, - } - } + Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } }, } } - }; + } } - } + }; + } + } - public static class AmazonWaf + public static class AmazonWaf + { + public static AmazonWafRequest CreateTask() + { + return new AmazonWafRequest() { - public static AmazonWafRequest CreateTask() - { - return new AmazonWafRequest() - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomGuid(), - CaptchaScript = Gen.RandomString(), - ChallengeScript = Gen.RandomString(), - Context = Gen.RandomString(), - Iv = Gen.RandomString(), - CookieSolution = Gen.RandomBool(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomGuid(), + CaptchaScript = Gen.RandomString(), + ChallengeScript = Gen.RandomString(), + Context = Gen.RandomString(), + Iv = Gen.RandomString(), + CookieSolution = Gen.RandomBool(), + Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) + }; + } - public static CaptchaResult CreateSolution() + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new AmazonWafResponse { - return new CaptchaResult - { - Error = null, - Solution = new AmazonWafResponse - { - CaptchaVoucher = Gen.RandomString(), - ExistingToken = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }; + CaptchaVoucher = Gen.RandomString(), + ExistingToken = Gen.RandomString(), + UserAgent = Gen.UserAgent(), + Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } } - } + }; + } + } - public static class BinanceTask + public static class BinanceTask + { + public static BinanceTaskRequest CreateTask() + { + return new BinanceTaskRequest() { - public static BinanceTaskRequest CreateTask() - { - return new BinanceTaskRequest() - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomGuid(), - ValidateId = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomGuid(), + ValidateId = Gen.RandomString(), + UserAgent = Gen.UserAgent(), + Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) + }; + } - public static CaptchaResult CreateSolution() + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new BinanceTaskResponse { - return new CaptchaResult - { - Error = null, - Solution = new BinanceTaskResponse - { - Value = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }; + Value = Gen.RandomString(), + UserAgent = Gen.UserAgent(), + Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } } - } + }; + } + } - public static class TemuTask + public static class TemuTask + { + public static TemuCustomTaskRequest CreateTask() + { + return new TemuCustomTaskRequest(Gen.RandomString()) { - 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()) - }; - } + 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() + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new CustomTaskResponse { - return new CaptchaResult + Domains = new Dictionary { - Error = null, - Solution = new CustomTaskResponse { - Domains = new Dictionary + "www.temu.com", + new CustomTaskResponse.DomainInfo { + Cookies = new Dictionary { - "www.temu.com", - new CustomTaskResponse.DomainInfo - { - Cookies = new Dictionary - { - { "verifyAuthToken", Gen.RandomString() }, - { "api_uid", Gen.RandomString() } - } - } + { "verifyAuthToken", Gen.RandomString() }, + { "api_uid", Gen.RandomString() } } } } - }; + } } - } - - public static class MTCaptchaTask + }; + } + } + + public static class MTCaptchaTask + { + public static MTCaptchaTaskRequest CreateTask() + { + return new MTCaptchaTaskRequest { - public static MTCaptchaTaskRequest CreateTask() - { - return new MTCaptchaTaskRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomString(), - Invisible = Gen.RandomBool(), - PageAction = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Proxy = new ProxyContainer( - Gen.RandomString(), Gen.RandomInt(0, 65535), - Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; - } + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomString(), + Invisible = Gen.RandomBool(), + PageAction = Gen.RandomString(), + 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 MTCaptchaTaskResponse - { - Value = Gen.RandomString() - } - }; - } - } - - public static class YidunTask + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static YidunTaskRequest CreateTask() + Error = null, + Solution = new MTCaptchaTaskResponse { - return new YidunTaskRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - // Enterprise : - YidunGetLib = Gen.RandomUri().ToString(), - YidunApiServerSubdomain = Gen.RandomString(), - Challenge = Gen.RandomString(), - Hcg = Gen.RandomString(), - Hct = Gen.RandomLong(1, long.MaxValue), - Proxy = new ProxyContainer( - Gen.RandomString(), Gen.RandomInt(0, 65535), - Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; + Value = Gen.RandomString() } + }; + } + } + + public static class YidunTask + { + public static YidunTaskRequest CreateTask() + { + return new YidunTaskRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomString(), + UserAgent = Gen.UserAgent(), + // Enterprise : + YidunGetLib = Gen.RandomUri().ToString(), + YidunApiServerSubdomain = Gen.RandomString(), + Challenge = Gen.RandomString(), + Hcg = Gen.RandomString(), + Hct = Gen.RandomLong(1, long.MaxValue), + 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 YidunTaskResponse - { - Value = Gen.RandomString(), - } - }; - } - } - - public static class ProsopoTask + public static CaptchaResult CreateSolution() + { + return new CaptchaResult { - public static ProsopoTaskRequest CreateTask() + Error = null, + Solution = new YidunTaskResponse { - return new ProsopoTaskRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomString(), - Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) - }; + Value = Gen.RandomString(), } + }; + } + } + + public static class ProsopoTask + { + public static ProsopoTaskRequest CreateTask() + { + return new ProsopoTaskRequest + { + WebsiteUrl = Gen.RandomUri().ToString(), + WebsiteKey = Gen.RandomString(), + Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) + }; + } - public static CaptchaResult CreateSolution() + public static CaptchaResult CreateSolution() + { + return new CaptchaResult + { + Error = null, + Solution = new ProsopoTaskResponse { - return new CaptchaResult - { - Error = null, - Solution = new ProsopoTaskResponse - { - Value = Gen.RandomString(), - } - }; + Value = Gen.RandomString(), } - } + }; + } } } \ No newline at end of file diff --git a/CapMonsterCloud.Client.IntegrationTests/RequestType.cs b/CapMonsterCloud.Client.IntegrationTests/RequestType.cs index cb8c319..8ca0006 100644 --- a/CapMonsterCloud.Client.IntegrationTests/RequestType.cs +++ b/CapMonsterCloud.Client.IntegrationTests/RequestType.cs @@ -1,9 +1,8 @@ -namespace CapMonsterCloud.Client.IntegrationTests +namespace CapMonsterCloud.Client.IntegrationTests; + +public enum RequestType { - public enum RequestType - { - GetBalance, - CreateTask, - GetTaskResult - } + GetBalance, + CreateTask, + GetTaskResult } \ No newline at end of file diff --git a/CapMonsterCloud.Client.IntegrationTests/Sut.cs b/CapMonsterCloud.Client.IntegrationTests/Sut.cs index 8ed962a..2c4a9d7 100644 --- a/CapMonsterCloud.Client.IntegrationTests/Sut.cs +++ b/CapMonsterCloud.Client.IntegrationTests/Sut.cs @@ -1,155 +1,157 @@ using Moq; using Moq.Contrib.HttpClient; using Moq.Protected; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Net; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Zennolab.CapMonsterCloud; using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Responses; -namespace CapMonsterCloud.Client.IntegrationTests +namespace CapMonsterCloud.Client.IntegrationTests; + +public class Sut { - public class Sut + private readonly Mock _httpMessageHandler; + private readonly ICapMonsterCloudClient _cloudClient; + private readonly List<(RequestType, string)> _actualRequests = new(); + + public Sut(string clientKey) { - private readonly Mock _httpMessageHandler; - private readonly ICapMonsterCloudClient _cloudClient; - private readonly List<(RequestType, string)> _actualRequests = new(); + _httpMessageHandler = new Mock(MockBehavior.Strict); - public Sut(string clientKey) + var clientOptions = new ClientOptions { - _httpMessageHandler = new Mock(MockBehavior.Strict); - - var clientOptions = new ClientOptions + ServiceUri = Gen.RandomUri(), + ClientKey = clientKey + }; + + var httpClient = _httpMessageHandler.CreateClient(); + + var cloudClientFactory = new CapMonsterCloudClientFactory( + clientOptions, + () => _httpMessageHandler.Object, + (_) => { - ServiceUri = Gen.RandomUri(), - ClientKey = clientKey - }; - - var httpClient = _httpMessageHandler.CreateClient(); - - var cloudClientFactory = new CapMonsterCloudClientFactory( - clientOptions, - () => _httpMessageHandler.Object, - (_) => - { - _httpMessageHandler.CreateClient(); - httpClient.BaseAddress = clientOptions.ServiceUri; - }); - - _cloudClient = cloudClientFactory.Create(); - } + _httpMessageHandler.CreateClient(); + httpClient.BaseAddress = clientOptions.ServiceUri; + }); + + _cloudClient = cloudClientFactory.Create(); + } - public async Task> SolveAsync ( + public async Task> SolveAsync( ImageToTextRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - RecaptchaV2Request request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - RecaptchaV3ProxylessRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - FunCaptchaRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - HCaptchaRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - GeeTestRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - RecaptchaV2EnterpriseRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - TurnstileRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - RecaptchaComplexImageTaskRequest 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); - - public async Task> SolveAsync( - DataDomeCustomTaskRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - AmazonWafRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - TenDiCustomTaskRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - BasiliskCustomTaskRequest request) => await _cloudClient.SolveAsync(request); - - 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> SolveAsync( - ProsopoTaskRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - TemuCustomTaskRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - MTCaptchaTaskRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - YidunTaskRequest request) => await _cloudClient.SolveAsync(request); - - public async Task GetBalanceAsync() - { - return await _cloudClient.GetBalanceAsync(); - } - public List<(RequestType, string)> GetActualRequests() - { - return _actualRequests; - } + public async Task> SolveAsync( + RecaptchaV2Request request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + RecaptchaV3ProxylessRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + RecaptchaV3EnterpriseRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + FunCaptchaRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + HCaptchaRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + GeeTestRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + RecaptchaV2EnterpriseRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + TurnstileRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + RecaptchaComplexImageTaskRequest 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); + + public async Task> SolveAsync( + DataDomeCustomTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + AmazonWafRequest request) => await _cloudClient.SolveAsync(request); - public void SetupHttpServer( - List expectedResponses) + public async Task> SolveAsync( + TenDiCustomTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + BasiliskCustomTaskRequest request) => await _cloudClient.SolveAsync(request); + + 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> SolveAsync( + ProsopoTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + TemuCustomTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + MTCaptchaTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task> SolveAsync( + YidunTaskRequest request) => await _cloudClient.SolveAsync(request); + + public async Task GetBalanceAsync() + { + return await _cloudClient.GetBalanceAsync(); + } + + public List<(RequestType, string)> GetActualRequests() + { + return _actualRequests; + } + + public void SetupHttpServer( + List expectedResponses) + { + var part = _httpMessageHandler + .Protected() + .SetupSequence>( + "SendAsync", + ItExpr.Is(request => SaveRequest(request).Result), + ItExpr.IsAny() + ); + + foreach (var item in expectedResponses) { - var part = _httpMessageHandler - .Protected() - .SetupSequence>( - "SendAsync", - ItExpr.Is(request => SaveRequest(request).Result), - ItExpr.IsAny() - ); - - foreach (var item in expectedResponses) - { - part.ReturnsResponse(HttpStatusCode.OK, new StringContent(JsonConvert.SerializeObject(item))); - } + part.ReturnsResponse(HttpStatusCode.OK, new StringContent(CapMonsterCloudClient.ToJson(item))); } + } - private async Task SaveRequest(HttpRequestMessage request) - { - if (request.RequestUri == null && request.Content == null) - return false; - - var path = request.RequestUri!.GetComponents(UriComponents.Path, UriFormat.Unescaped); - var type = Enum.Parse(path,true); + private async Task SaveRequest(HttpRequestMessage request) + { + if (request.RequestUri == null && request.Content == null) + return false; - var requestStringContent = await request.Content!.ReadAsStringAsync(); + var path = request.RequestUri!.GetComponents(UriComponents.Path, UriFormat.Unescaped); + var type = Enum.Parse(path, true); - _actualRequests.Add(new(type, requestStringContent)); + var requestStringContent = await request.Content!.ReadAsStringAsync(); - return true; - } + _actualRequests.Add(new(type, requestStringContent)); + + return true; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client.Tests/CapMonsterCloud.Client.Tests.csproj b/CapMonsterCloud.Client.Tests/CapMonsterCloud.Client.Tests.csproj index 1e041b8..2c1138c 100644 --- a/CapMonsterCloud.Client.Tests/CapMonsterCloud.Client.Tests.csproj +++ b/CapMonsterCloud.Client.Tests/CapMonsterCloud.Client.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 enable false diff --git a/CapMonsterCloud.Client.Tests/ErrorCodeConverterTests.cs b/CapMonsterCloud.Client.Tests/ErrorCodeConverterTests.cs index 37ed593..3001e92 100644 --- a/CapMonsterCloud.Client.Tests/ErrorCodeConverterTests.cs +++ b/CapMonsterCloud.Client.Tests/ErrorCodeConverterTests.cs @@ -1,34 +1,33 @@ -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; -namespace Zennolab.CapMonsterCloud.Client.Tests +namespace Zennolab.CapMonsterCloud.Client.Tests; + +public class ErrorCodeConverterTests { - public class ErrorCodeConverterTests + [TestCase("ERROR_KEY_DOES_NOT_EXIST", ErrorType.KEY_DOES_NOT_EXIST)] + [TestCase("ERROR_ZERO_CAPTCHA_FILESIZE", ErrorType.ZERO_CAPTCHA_FILESIZE)] + [TestCase("ERROR_TOO_BIG_CAPTCHA_FILESIZE", ErrorType.TOO_BIG_CAPTCHA_FILESIZE)] + [TestCase("ERROR_ZERO_BALANCE", ErrorType.ZERO_BALANCE)] + [TestCase("ERROR_IP_NOT_ALLOWED", ErrorType.IP_NOT_ALLOWED)] + [TestCase("ERROR_CAPTCHA_UNSOLVABLE", ErrorType.CAPTCHA_UNSOLVABLE)] + [TestCase("ERROR_NO_SUCH_CAPCHA_ID", ErrorType.NO_SUCH_CAPCHA_ID)] + [TestCase("WRONG_CAPTCHA_ID", ErrorType.NO_SUCH_CAPCHA_ID)] + [TestCase("CAPTCHA_NOT_READY", ErrorType.Unknown)] + [TestCase("ERROR_IP_BANNED", ErrorType.IP_BANNED)] + [TestCase("ERROR_NO_SUCH_METHOD", ErrorType.NO_SUCH_METHOD)] + [TestCase("ERROR_TOO_MUCH_REQUESTS", ErrorType.TOO_MUCH_REQUESTS)] + [TestCase("ERROR_DOMAIN_NOT_ALLOWED", ErrorType.DOMAIN_NOT_ALLOWED)] + [TestCase("ERROR_TOKEN_EXPIRED", ErrorType.TOKEN_EXPIRED)] + [TestCase("ERROR_NO_SLOT_AVAILABLE", ErrorType.NO_SLOT_AVAILABLE)] + public void Convert__ShouldConvert(string errorCode, ErrorType expected) { - [TestCase("ERROR_KEY_DOES_NOT_EXIST", ErrorType.KEY_DOES_NOT_EXIST)] - [TestCase("ERROR_ZERO_CAPTCHA_FILESIZE", ErrorType.ZERO_CAPTCHA_FILESIZE)] - [TestCase("ERROR_TOO_BIG_CAPTCHA_FILESIZE", ErrorType.TOO_BIG_CAPTCHA_FILESIZE)] - [TestCase("ERROR_ZERO_BALANCE", ErrorType.ZERO_BALANCE)] - [TestCase("ERROR_IP_NOT_ALLOWED", ErrorType.IP_NOT_ALLOWED)] - [TestCase("ERROR_CAPTCHA_UNSOLVABLE", ErrorType.CAPTCHA_UNSOLVABLE)] - [TestCase("ERROR_NO_SUCH_CAPCHA_ID", ErrorType.NO_SUCH_CAPCHA_ID)] - [TestCase("WRONG_CAPTCHA_ID", ErrorType.NO_SUCH_CAPCHA_ID)] - [TestCase("CAPTCHA_NOT_READY", ErrorType.Unknown)] - [TestCase("ERROR_IP_BANNED", ErrorType.IP_BANNED)] - [TestCase("ERROR_NO_SUCH_METHOD", ErrorType.NO_SUCH_METHOD)] - [TestCase("ERROR_TOO_MUCH_REQUESTS", ErrorType.TOO_MUCH_REQUESTS)] - [TestCase("ERROR_DOMAIN_NOT_ALLOWED", ErrorType.DOMAIN_NOT_ALLOWED)] - [TestCase("ERROR_TOKEN_EXPIRED", ErrorType.TOKEN_EXPIRED)] - [TestCase("ERROR_NO_SLOT_AVAILABLE", ErrorType.NO_SLOT_AVAILABLE)] - public void Convert__ShouldConvert(string errorCode, ErrorType expected) - { - // Arrange + // Arrange - // Act - var actual = ErrorCodeConverter.Convert(errorCode); + // Act + var actual = ErrorCodeConverter.Convert(errorCode); - // Assert - actual.Should().Be(expected); - } + // Assert + actual.Should().Be(expected); } } diff --git a/CapMonsterCloud.Client.Tests/JsonConverterTests.cs b/CapMonsterCloud.Client.Tests/JsonConverterTests.cs index a8a6823..b4be235 100644 --- a/CapMonsterCloud.Client.Tests/JsonConverterTests.cs +++ b/CapMonsterCloud.Client.Tests/JsonConverterTests.cs @@ -1,85 +1,84 @@ -using FluentAssertions; -using Newtonsoft.Json; +using FluentAssertions; using NUnit.Framework; using System; using System.Collections.Generic; +using System.Text.Json; using Zennolab.CapMonsterCloud.Requests; -namespace Zennolab.CapMonsterCloud.Client +namespace Zennolab.CapMonsterCloud.Client; + +public class JsonConverterTests { - public class JsonConverterTests + [Test] + public void NumericFlagConverter_ShouldDeserialize([Values] bool numeric) { - [Test] - public void NumericFlagConverter_ShouldDeserialize([Values] bool numeric) + // Arrange + var request = new ImageToTextRequest { - // Arrange - var request = new ImageToTextRequest - { - Body = "some base64 body", - CapMonsterModule = CapMonsterModules.YandexWave, - CaseSensitive = true, - Numeric = numeric, - RecognizingThreshold = 65, - Math = false - }; + Body = "some base64 body", + CapMonsterModule = CapMonsterModules.YandexWave, + CaseSensitive = true, + Numeric = numeric, + RecognizingThreshold = 65, + Math = false + }; - var target = JsonConvert.SerializeObject(request); + var target = CapMonsterCloudClient.ToJson(request); - // Act - var actual = JsonConvert.DeserializeObject(target); + // Act + var actual = CapMonsterCloudClient.FromJson(target); - // Assert - _ = actual!.Numeric.Should().Be(request.Numeric); - } + // Assert + _ = actual!.Numeric.Should().Be(request.Numeric); + } - [Test] - public void DictionaryToSemicolonSplittedStringConverter_ShouldDeserialize() + [Test] + public void DictionaryToSemicolonSplittedStringConverter_ShouldDeserialize() + { + // Arrange + var request = new HCaptchaRequest() { - // Arrange - var request = new HCaptchaRequest() + WebsiteUrl = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", + WebsiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", + Invisible = false, + Data = "some data", + Cookies = new Dictionary { - WebsiteUrl = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", - WebsiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", - Invisible = false, - Data = "some data", - Cookies = new Dictionary - { - { "cookieA", "value#A" }, - { "cookieB", "value#B" } - } - }; + { "cookieA", "value#A" }, + { "cookieB", "value#B" } + } + }; - var target = JsonConvert.SerializeObject(request); + var target = CapMonsterCloudClient.ToJson(request); - // Act - var actual = JsonConvert.DeserializeObject(target); + // Act + var actual = CapMonsterCloudClient.FromJson(target); - // Assert - _ = actual!.Cookies.Should().ContainKeys(request.Cookies.Keys); - _ = actual.Cookies.Should().ContainValues(request.Cookies.Values); - } + // Assert + _ = actual!.Cookies.Should().ContainKeys(request.Cookies.Keys); + _ = actual.Cookies.Should().ContainValues(request.Cookies.Values); + } - [Test] - public void DictionaryToSemicolonSplittedStringConverter_ShouldThrowJsonReaderException() - { - // Arrange - var target = JsonConvert.SerializeObject( - new - { - type = "HCaptchaTask", - websiteURL = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", - websiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", - isInvisible = false, - data = "some data", - userAgent = "PostmanRuntime/7.29.0", - cookies = "some invalid cookie string" - }); + [Test] + public void DictionaryToSemicolonSplittedStringConverter_ShouldThrowJsonException() + { + // Arrange + var target = JsonSerializer.Serialize( + new + { + type = "HCaptchaTask", + websiteURL = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", + websiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", + isInvisible = false, + data = "some data", + userAgent = "PostmanRuntime/7.29.0", + cookies = "some invalid cookie string" + }); - // Act - Func act = () => JsonConvert.DeserializeObject(target)!; + // Act + Func act = () => CapMonsterCloudClient.FromJson(target); - // Assert - _ = act.Should().Throw(); - } + // Assert + _ = act.Should().Throw(); } } diff --git a/CapMonsterCloud.Client.Tests/ProxyValidatorTests.cs b/CapMonsterCloud.Client.Tests/ProxyValidatorTests.cs index a54a1ff..a812a13 100644 --- a/CapMonsterCloud.Client.Tests/ProxyValidatorTests.cs +++ b/CapMonsterCloud.Client.Tests/ProxyValidatorTests.cs @@ -1,26 +1,25 @@ -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; using Zennolab.CapMonsterCloud.Validation; -namespace Zennolab.CapMonsterCloud.Client.Tests +namespace Zennolab.CapMonsterCloud.Client.Tests; + +public class ProxyValidatorTests { - public class ProxyValidatorTests + [Test] + [TestCase("8.8.8.8", true)] + [TestCase("192.168.1.1", false)] + [TestCase("10.0.0.1", false)] + [TestCase("example.com", true)] + [TestCase("localhost", false)] + [TestCase("256.256.256.256", false)] + [TestCase("google.com", true)] + public void IsValidProxy__ReturnsExpectedResult(string address, bool expectedResult) { - [Test] - [TestCase("8.8.8.8", true)] - [TestCase("192.168.1.1", false)] - [TestCase("10.0.0.1", false)] - [TestCase("example.com", true)] - [TestCase("localhost", false)] - [TestCase("256.256.256.256", false)] - [TestCase("google.com", true)] - public void IsValidProxy__ReturnsExpectedResult(string address, bool expectedResult) - { - // Act - var actual = ProxyValidator.IsValidProxy(address); + // Act + var actual = ProxyValidator.IsValidProxy(address); - // Assert - actual.Should().Be(expectedResult); - } + // Assert + actual.Should().Be(expectedResult); } } diff --git a/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs b/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs index 9fd2190..add88fb 100644 --- a/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs +++ b/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs @@ -1,188 +1,224 @@ using FluentAssertions; -using Newtonsoft.Json; using NUnit.Framework; using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Nodes; using Zennolab.CapMonsterCloud.Requests; #pragma warning disable IDE0058 // Expression value is never used -namespace Zennolab.CapMonsterCloud.Client +namespace Zennolab.CapMonsterCloud.Client; + +public class RequestsSerializationTests { - public class RequestsSerializationTests + [Test] + public void RecaptchaV2Request__ShouldSerialize([Values] ProxyType proxyType) + { + // Arrange + var target = new RecaptchaV2Request + { + WebsiteUrl = "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", + WebsiteKey = "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd", + DataSValue = "some data-s value", + UserAgent = "PostmanRuntime/7.29.0", + Cookies = new Dictionary + { + { "cookieA", "value#A" }, + { "cookieB", "value#B" } + }, + Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") + }; + + // Act + var actual = CapMonsterCloudClient.ToJson(target); + + // Assert + var expected = JsonSerializer.Serialize( + new + { + type = "NoCaptchaTask", + websiteURL = target.WebsiteUrl, + websiteKey = target.WebsiteKey, + recaptchaDataSValue = target.DataSValue, + userAgent = target.UserAgent, + cookies = "cookieA=value#A;cookieB=value#B", + proxyAddress = target.ProxyAddress, + proxyPort = target.ProxyPort, + proxyType = proxyType.ToString().ToLower(), + proxyLogin = target.ProxyLogin, + proxyPassword = target.ProxyPassword + }); + + JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected)).Should().BeTrue(); + } + + [Test] + [TestCase(true, 1)] + [TestCase(false, 0)] + public void ImageToTextRequest__ShouldSerialize(bool numeric, byte numericJson) { - [Test] - public void RecaptchaV2Request__ShouldSerialize([Values] ProxyType proxyType) + // Arrange + var target = new ImageToTextRequest { - // Arrange - var target = new RecaptchaV2Request + Body = "some base64 body", + CapMonsterModule = CapMonsterModules.YandexWave, + CaseSensitive = true, + Numeric = numeric, + RecognizingThreshold = 65, + Math = false + }; + + // Act + var actual = CapMonsterCloudClient.ToJson(target); + + // Assert + var expected = JsonSerializer.Serialize( + new { - WebsiteUrl = "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", - WebsiteKey = "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd", - DataSValue = "some data-s value", - UserAgent = "PostmanRuntime/7.29.0", - Cookies = new Dictionary - { - { "cookieA", "value#A" }, - { "cookieB", "value#B" } - }, - Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "NoCaptchaTask", - websiteURL = target.WebsiteUrl, - websiteKey = target.WebsiteKey, - recaptchaDataSValue = target.DataSValue, - userAgent = target.UserAgent, - cookies = "cookieA=value#A;cookieB=value#B", - proxyAddress = target.ProxyAddress, - proxyPort = target.ProxyPort, - proxyType = proxyType.ToString().ToLower(), - proxyLogin = target.ProxyLogin, - proxyPassword = target.ProxyPassword - })); - } - - [Test] - [TestCase(true, 1)] - [TestCase(false, 0)] - public void ImageToTextRequest__ShouldSerialize(bool numeric, byte numericJson) + type = "ImageToTextTask", + body = target.Body, + CapMonsterModule = target.CapMonsterModule, + recognizingThreshold = target.RecognizingThreshold, + Case = target.CaseSensitive, + numeric = numericJson, + math = target.Math + }); + + JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected)).Should().BeTrue(); + } + + [Test] + public void RecaptchaV3ProxylessRequest__ShouldSerialize() + { + // Arrange + var target = new RecaptchaV3ProxylessRequest { - // Arrange - var target = new ImageToTextRequest + WebsiteUrl = "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", + WebsiteKey = "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd", + MinScore = 0.6, + PageAction = "some-action" + }; + + // Act + var actual = CapMonsterCloudClient.ToJson(target); + + // Assert + var expected = JsonSerializer.Serialize( + new { - Body = "some base64 body", - CapMonsterModule = CapMonsterModules.YandexWave, - CaseSensitive = true, - Numeric = numeric, - RecognizingThreshold = 65, - Math = false - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "ImageToTextTask", - body = target.Body, - CapMonsterModule = target.CapMonsterModule, - recognizingThreshold = target.RecognizingThreshold, - Case = target.CaseSensitive, - numeric = numericJson, - math = target.Math - })); - } - - [Test] - public void RecaptchaV3ProxylessRequest__ShouldSerialize() + type = "RecaptchaV3TaskProxyless", + websiteURL = target.WebsiteUrl, + websiteKey = target.WebsiteKey, + minScore = target.MinScore, + pageAction = target.PageAction + }); + + JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected)).Should().BeTrue(); + } + + [Test] + public void RecaptchaV3EnterpriseRequest__ShouldSerialize() + { + // Arrange + var target = new RecaptchaV3EnterpriseRequest { - // Arrange - var target = new RecaptchaV3ProxylessRequest + WebsiteUrl = "https://example.com/recaptcha/enterprise", + WebsiteKey = "6Le0xVgUAAAAAIt20XEB4rVhYOODgTl00d4TuRTE", + MinScore = 0.7, + PageAction = "login_test" + }; + + // Act + var actual = CapMonsterCloudClient.ToJson(target); + + // Assert + var expected = JsonSerializer.Serialize( + new { - WebsiteUrl = "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", - WebsiteKey = "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd", - MinScore = 0.6, - PageAction = "some-action" - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "RecaptchaV3TaskProxyless", - websiteURL = target.WebsiteUrl, - websiteKey = target.WebsiteKey, - minScore = target.MinScore, - pageAction = target.PageAction - })); - } - - - - [Test] - public void FunCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) + type = "RecaptchaV3EnterpriseTask", + websiteURL = target.WebsiteUrl, + websiteKey = target.WebsiteKey, + minScore = target.MinScore, + pageAction = target.PageAction + }); + + JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected)).Should().BeTrue(); + } + + [Test] + public void FunCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) + { + // Arrange + var target = new FunCaptchaRequest { - // Arrange - var target = new FunCaptchaRequest + WebsiteUrl = "https://funcaptcha.com/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", + WebsiteKey = "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", + Data = "{\"blob\":\"dyXvXANMbHj1iDyz.Qj97JtSqR2n%2BuoY1V%2FbdgbrG7p%2FmKiqdU9AwJ6MifEt0np4vfYn6TTJDJEfZDlcz9Q1XMn9przeOV%2FCr2%2FIpi%2FC1s%3D\"}", + Subdomain = "mywebsite-api.funcaptcha.com", + Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") + }; + + // Act + var actual = CapMonsterCloudClient.ToJson(target); + + // Assert + var expected = JsonSerializer.Serialize( + new { - WebsiteUrl = "https://funcaptcha.com/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", - WebsiteKey = "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", - Data = "{\"blob\":\"dyXvXANMbHj1iDyz.Qj97JtSqR2n%2BuoY1V%2FbdgbrG7p%2FmKiqdU9AwJ6MifEt0np4vfYn6TTJDJEfZDlcz9Q1XMn9przeOV%2FCr2%2FIpi%2FC1s%3D\"}", - Subdomain = "mywebsite-api.funcaptcha.com", - Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "FunCaptchaTask", - websiteURL = target.WebsiteUrl, - websitePublicKey = target.WebsiteKey, - funcaptchaApiJSSubdomain = target.Subdomain, - data = target.Data, - proxyAddress = target.ProxyAddress, - proxyPort = target.ProxyPort, - proxyType = proxyType.ToString().ToLower(), - proxyLogin = target.ProxyLogin, - proxyPassword = target.ProxyPassword - })); - } - - [Test] - public void HCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) + type = "FunCaptchaTask", + websiteURL = target.WebsiteUrl, + websitePublicKey = target.WebsiteKey, + funcaptchaApiJSSubdomain = target.Subdomain, + data = target.Data, + proxyAddress = target.ProxyAddress, + proxyPort = target.ProxyPort, + proxyType = proxyType.ToString().ToLower(), + proxyLogin = target.ProxyLogin, + proxyPassword = target.ProxyPassword + }); + + JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected)).Should().BeTrue(); + } + + [Test] + public void HCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) + { + // Arrange + var target = new HCaptchaRequest { - // Arrange - var target = new HCaptchaRequest + WebsiteUrl = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", + WebsiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", + Data = "some data", + Cookies = new Dictionary + { + { "cookieA", "value#A" }, + { "cookieB", "value#B" } + }, + Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") + }; + + // Act + var actual = CapMonsterCloudClient.ToJson(target); + + // Assert + var expected = JsonSerializer.Serialize( + new { - WebsiteUrl = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", - WebsiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", - Data = "some data", - Cookies = new Dictionary - { - { "cookieA", "value#A" }, - { "cookieB", "value#B" } - }, - Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "HCaptchaTask", - websiteURL = target.WebsiteUrl, - websiteKey = target.WebsiteKey, - isInvisible = target.Invisible, - data = target.Data, - userAgent = default(string), - cookies = "cookieA=value#A;cookieB=value#B", - proxyAddress = target.ProxyAddress, - proxyPort = target.ProxyPort, - proxyType = proxyType.ToString().ToLower(), - proxyLogin = target.ProxyLogin, - proxyPassword = target.ProxyPassword - })); - } + type = "HCaptchaTask", + websiteURL = target.WebsiteUrl, + websiteKey = target.WebsiteKey, + isInvisible = target.Invisible, + data = target.Data, + cookies = "cookieA=value#A;cookieB=value#B", + proxyAddress = target.ProxyAddress, + proxyPort = target.ProxyPort, + proxyType = proxyType.ToString().ToLower(), + proxyLogin = target.ProxyLogin, + proxyPassword = target.ProxyPassword + }); + + JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected)).Should().BeTrue(); } } -#pragma warning restore IDE0058 // Expression value is never used \ No newline at end of file +#pragma warning restore IDE0058 // Expression value is never used diff --git a/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj b/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj index dbf5e3d..bcaa800 100644 --- a/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj +++ b/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj @@ -1,13 +1,15 @@ - + - netstandard2.0 + net8.0 + enable + latest Zennolab.CapMonsterCloud.Client Zennolab.CapMonsterCloud Zennolab Zennolab Official C# client library for capmonster.cloud captcha recognition service - Zennolab, 2023 + Zennolab, 2026 git capmonster capmonster.cloud zennolab recaptcha funcaptcha hcaptcha captcha geetest MIT @@ -15,8 +17,8 @@ README.md logo.png https://github.com/ZennoLab/capmonstercloud-client-dotnet - 3.1.0 - Add Yidun, Temu, Prosopo, MTCaptcha solving support + 4.0.0 + Target .NET 8; remove external dependencies; add IHttpClientFactory DI support; C# 12 modernization @@ -31,8 +33,7 @@ - - + diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient.cs b/CapMonsterCloud.Client/CapMonsterCloudClient.cs index f3f769e..de72903 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient.cs @@ -1,215 +1,195 @@ -using Newtonsoft.Json; using System; using System.ComponentModel.DataAnnotations; using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Responses; using Zennolab.CapMonsterCloud.Validation; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// capmonster.cloud Client +/// +public partial class CapMonsterCloudClient(ClientOptions options, HttpClient httpClient) : ICapMonsterCloudClient { - /// - /// capmonster.cloud Client - /// - public partial class CapMonsterCloudClient : ICapMonsterCloudClient + private const string TaskReady = "ready"; + + private static readonly JsonSerializerOptions SerializerOptions = new() { - private const string TaskReady = "ready"; + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; - private readonly ClientOptions _options; + internal HttpClient HttpClient { get; } = httpClient; - /// - /// Creates new capmonster.cloud Client - /// - /// client options - /// - public CapMonsterCloudClient(ClientOptions options, HttpClient httpClient) - { - _options = options; + /// + /// exception on processing HTTP request to capmonster.cloud + public async Task GetBalanceAsync(CancellationToken cancellationToken) + { + var response = await HttpClient.PostAsync( + "getBalance", + new StringContent(ToJson( + new { clientKey = options.ClientKey })), + cancellationToken); - HttpClient = httpClient; + if (!response.IsSuccessStatusCode) + { + throw new HttpRequestException($"Cannot get balance. Status code was {response.StatusCode}"); } - internal HttpClient HttpClient { get; } + var responseBody = await response.Content.ReadAsStringAsync(); - /// - /// exception on processing HTTP request to capmonster.cloud - public async Task GetBalanceAsync(CancellationToken cancellationToken) - { - var response = await HttpClient.PostAsync( - "getBalance", - new StringContent(ToJson( - new { clientKey = _options.ClientKey })), - cancellationToken); - if (!response.IsSuccessStatusCode) - { - throw new HttpRequestException($"Cannot get balance. Status code was {response.StatusCode}"); - } + var result = FromJson(responseBody) + ?? throw new HttpRequestException($"Cannot parse get balance response. Response was: {responseBody}"); - var responseBody = await response.Content.ReadAsStringAsync(); - - var result = FromJson(responseBody); - if (result == null) - { - throw new HttpRequestException($"Cannot parse get balance response. Response was: {responseBody}"); - } + if (result.ErrorId != 0) + { + throw new GetBalanceException(ToErrorType(result.ErrorCode)); + } - if (result.errorId != 0) - { - throw new GetBalanceException(ToErrorType(result.errorCode)); - } + return result.Balance; + } - return result.balance; - } + /// + /// malformed task object + /// exception on processing HTTP request to capmonster.cloud + public async Task> SolveAsync( + CaptchaRequestBase task, + CancellationToken cancellationToken) where TSolution : CaptchaResponseBase + { + ValidateTask, TSolution>(task); - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public async Task> SolveAsync( - CaptchaRequestBase task, - CancellationToken cancellationToken) where TSolution : CaptchaResponseBase + var createdTask = await CreateTask(task, cancellationToken); + if (createdTask.ErrorId != 0) { - ValidateTask, TSolution>(task); + return new CaptchaResult { Error = ToErrorType(createdTask.ErrorCode) }; + } - var createdTask = await CreateTask(task, cancellationToken); - if (createdTask.errorId != 0) - { - return new CaptchaResult { Error = ToErrorType(createdTask.errorCode) }; - } + var getResultTimeouts = GetTimeouts(task.GetType()); + + using var getResultTimeoutCts = new CancellationTokenSource(getResultTimeouts.Timeout); + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, getResultTimeoutCts.Token); - var getResultTimeouts = GetTimeouts(task.GetType()); + var firstRequestDelay = (task.UseNoCache ? getResultTimeouts.FirstRequestNoCacheDelay : null) + ?? getResultTimeouts.FirstRequestDelay; + await Task.Delay(firstRequestDelay, linkedCts.Token); - using (var getResultTimeoutCts = new CancellationTokenSource(getResultTimeouts.Timeout)) - using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, getResultTimeoutCts.Token)) + while (!linkedCts.IsCancellationRequested) + { + try { - TimeSpan firstRequestDelay = (task.UseNoCache ? getResultTimeouts.FirstRequestNoCacheDelay : null) - ?? getResultTimeouts.FirstRequestDelay; - await Task.Delay(firstRequestDelay, linkedCts.Token); + var result = await GetTaskResult(createdTask.TaskId, linkedCts.Token); - while (!linkedCts.IsCancellationRequested) + switch (result) { - try - { - var result = await GetTaskResult(createdTask.taskId, linkedCts.Token); - switch (result) - { - case TaskResult.TaskFailed failed: - return new CaptchaResult { Error = failed.Error }; - case TaskResult.TaskCompleted completed: - return new CaptchaResult { Solution = CastSolution(completed.Solution) }; - case TaskResult.TaskInProgress inProgress: - default: - break; - } - } - catch (OperationCanceledException) - { - if (getResultTimeoutCts.IsCancellationRequested) - { - break; - } - - throw; - } - - await Task.Delay(getResultTimeouts.RequestsInterval, linkedCts.Token); + case TaskResult.TaskFailed failed: + return new CaptchaResult { Error = failed.Error }; + case TaskResult.TaskCompleted completed: + return new CaptchaResult { Solution = CastSolution(completed.Solution) }; } } + catch (OperationCanceledException) + { + if (getResultTimeoutCts.IsCancellationRequested) + { + break; + } - return new CaptchaResult { Error = ErrorType.Timeout }; - } + throw; + } - private void ValidateTask(TTask task) where TTask : CaptchaRequestBase where TSolution : CaptchaResponseBase - => TaskValidator.ValidateObjectIncludingInternals(task); + await Task.Delay(getResultTimeouts.RequestsInterval, linkedCts.Token); + } - private async Task CreateTask(CaptchaRequestBase task, CancellationToken cancellationToken) where TSolution : CaptchaResponseBase - { - var body = ToJson( - new CreateTaskRequest - { - clientKey = _options.ClientKey, - task = task, - softId = _options.SoftId ?? ClientOptions.DefaultSoftId - }); + return new CaptchaResult { Error = ErrorType.Timeout }; + } - var response = await HttpClient.PostAsync("createTask", new StringContent(body), cancellationToken); + private static void ValidateTask(TTask task) where TTask : CaptchaRequestBase where TSolution : CaptchaResponseBase + => TaskValidator.ValidateObjectIncludingInternals(task); - if (!response.IsSuccessStatusCode) + private async Task CreateTask(CaptchaRequestBase task, CancellationToken cancellationToken) where TSolution : CaptchaResponseBase + { + var body = ToJson( + new CreateTaskRequest { - throw new HttpRequestException($"Cannot create task. Status code was {response.StatusCode}"); - } + ClientKey = options.ClientKey, + Task = task, + SoftId = options.SoftId ?? ClientOptions.DefaultSoftId + }); - var responseBody = await response.Content.ReadAsStringAsync(); + var response = await HttpClient.PostAsync("createTask", new StringContent(body), cancellationToken); - var result = FromJson(responseBody); - if (result == null) - { - throw new HttpRequestException($"Cannot parse create task response. Response was: {responseBody}"); - } - - return result; + if (!response.IsSuccessStatusCode) + { + throw new HttpRequestException($"Cannot create task. Status code was {response.StatusCode}"); } - private async Task GetTaskResult(int taskId, CancellationToken cancellationToken) - { - var body = ToJson( - new - { - clientKey = _options.ClientKey, - taskId - }); + var responseBody = await response.Content.ReadAsStringAsync(); - var response = await HttpClient.PostAsync("getTaskResult", new StringContent(body), cancellationToken); + return FromJson(responseBody) + ?? throw new HttpRequestException($"Cannot parse create task response. Response was: {responseBody}"); + } - if (response.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable) + private async Task GetTaskResult(int taskId, CancellationToken cancellationToken) + { + var body = ToJson( + new { - return TaskResult.InProgress; - } + clientKey = options.ClientKey, + taskId + }); - if (!response.IsSuccessStatusCode) - { - throw new HttpRequestException($"Cannot get task result. Status code was {response.StatusCode}"); - } + var response = await HttpClient.PostAsync("getTaskResult", new StringContent(body), cancellationToken); - var responseBody = await response.Content.ReadAsStringAsync(); + if (response.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable) + { + return TaskResult.InProgress; + } - var result = FromJson(responseBody); - if (result == null) - { - throw new HttpRequestException($"Cannot parse get task result response. Response was: {responseBody}"); - } + if (!response.IsSuccessStatusCode) + { + throw new HttpRequestException($"Cannot get task result. Status code was {response.StatusCode}"); + } - if (result.errorId != 0) - { - if ("CAPTCHA_NOT_READY".Equals(result.errorCode, StringComparison.OrdinalIgnoreCase)) - { - return TaskResult.InProgress; - } - else - { - return TaskResult.Failed(ToErrorType(result.errorCode)); - } - } + var responseBody = await response.Content.ReadAsStringAsync(); - if (TaskReady.Equals(result.status, StringComparison.OrdinalIgnoreCase)) - { - return TaskResult.Completed(result.solution); - } + var result = FromJson(responseBody) + ?? throw new HttpRequestException($"Cannot parse get task result response. Response was: {responseBody}"); - return TaskResult.InProgress; + if (result.ErrorId != 0) + { + return "CAPTCHA_NOT_READY".Equals(result.ErrorCode, StringComparison.OrdinalIgnoreCase) + ? TaskResult.InProgress + : TaskResult.Failed(ToErrorType(result.ErrorCode)); } - private static TSolution CastSolution(object solution) - => FromJson(solution.ToString()); + if (TaskReady.Equals(result.Status, StringComparison.OrdinalIgnoreCase)) + { + return TaskResult.Completed(result.Solution); + } - private static ErrorType ToErrorType(string errorCode) - => ErrorCodeConverter.Convert(errorCode); + return TaskResult.InProgress; + } - private static string ToJson(object data) - => JsonConvert.SerializeObject(data, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + private static TSolution CastSolution(object? solution) + { + var json = solution is JsonElement element + ? element.GetRawText() + : solution?.ToString() ?? "{}"; - private static TOut FromJson(string json) - => JsonConvert.DeserializeObject(json); + return FromJson(json) + ?? throw new JsonException($"Cannot deserialize solution to {typeof(TSolution).Name}"); } + + private static ErrorType ToErrorType(string? errorCode) + => ErrorCodeConverter.Convert(errorCode); + + internal static string ToJson(object data) + => JsonSerializer.Serialize(data, data.GetType(), SerializerOptions); + + internal static TOut? FromJson(string json) + => JsonSerializer.Deserialize(json, SerializerOptions); } diff --git a/CapMonsterCloud.Client/CapMonsterCloudClientException.cs b/CapMonsterCloud.Client/CapMonsterCloudClientException.cs index 2903eaa..81e1f97 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClientException.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClientException.cs @@ -1,28 +1,27 @@ -using System; +using System; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// Base type for capmonster.cloud Client exceptions +/// +public class CapmonsterCloudClientException : ApplicationException { /// - /// Base type for capmonster.cloud Client exceptions + /// Creates new capmonster.cloud Client exception /// - public class CapmonsterCloudClientException : ApplicationException + /// message + public CapmonsterCloudClientException(string message) : base(message) { - /// - /// Creates new capmonster.cloud Client exception - /// - /// message - public CapmonsterCloudClientException(string message) : base(message) - { - } + } - /// - /// Creates new capmonster.cloud Client exception - /// - /// message - /// inner exception - public CapmonsterCloudClientException(string message, Exception innerException) - : base(message, innerException) - { - } + /// + /// Creates new capmonster.cloud Client exception + /// + /// message + /// inner exception + public CapmonsterCloudClientException(string message, Exception innerException) + : base(message, innerException) + { } } diff --git a/CapMonsterCloud.Client/CapMonsterCloudClientFactory.cs b/CapMonsterCloud.Client/CapMonsterCloudClientFactory.cs index f46d3fe..c816d27 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClientFactory.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClientFactory.cs @@ -1,98 +1,77 @@ -using System; +using System; using System.Collections.Concurrent; using System.Linq; using System.Net.Http; using System.Reflection; -namespace Zennolab.CapMonsterCloud -{ - /// - /// Factory for - /// - public class CapMonsterCloudClientFactory : ICapMonsterCloudClientFactory, IDisposable - { - private static readonly TimeSpan HttpTimeout = TimeSpan.FromSeconds(21); - - private static readonly ConcurrentDictionary HttpClients = new ConcurrentDictionary(); - - private readonly ClientOptions _options; - private readonly Func _httpMessageHandlerFactory; - private readonly Action _configureClient; - - /// - /// Creates new instance of factory - /// - /// client options - /// optional HTTP message handler factory - /// optional configurator - public CapMonsterCloudClientFactory( - ClientOptions options, - Func httpMessageHandlerFactory = null, - Action configureClient = null) - { - _options = options; - _httpMessageHandlerFactory = httpMessageHandlerFactory; - _configureClient = configureClient; - } - - /// - public ICapMonsterCloudClient Create() - => Create(_options, _httpMessageHandlerFactory, _configureClient); - - /// - /// Creates instance of - /// - /// client options - /// Instance of - public static ICapMonsterCloudClient Create(ClientOptions options) - => Create(options, httpMessageHandlerFactory: null, configureClient: null); +namespace Zennolab.CapMonsterCloud; - private static ICapMonsterCloudClient Create( - ClientOptions options, - Func httpMessageHandlerFactory, - Action configureClient) - => new CapMonsterCloudClient( - options, - HttpClients.GetOrAdd( - options.ServiceUri, - uri => CreateHttpClient(uri, httpMessageHandlerFactory, configureClient))); +/// +/// Factory for +/// +public class CapMonsterCloudClientFactory( + ClientOptions options, + Func? httpMessageHandlerFactory = null, + Action? configureClient = null) : ICapMonsterCloudClientFactory, IDisposable +{ + private static readonly TimeSpan HttpTimeout = TimeSpan.FromSeconds(21); - private static HttpClient CreateHttpClient( - Uri uri, - Func httpMessageHandlerFactory, - Action configureClient) - { - var handler = httpMessageHandlerFactory?.Invoke(); + private static readonly ConcurrentDictionary HttpClients = new(); - var httpClient = handler != null - ? new HttpClient(handler, true) - : new HttpClient(); + /// + public ICapMonsterCloudClient Create() + => Create(options, httpMessageHandlerFactory, configureClient); - httpClient.BaseAddress = uri; - httpClient.Timeout = HttpTimeout; + /// + /// Creates instance of + /// + /// client options + /// Instance of + public static ICapMonsterCloudClient Create(ClientOptions options) + => Create(options, httpMessageHandlerFactory: null, configureClient: null); + + private static ICapMonsterCloudClient Create( + ClientOptions options, + Func? httpMessageHandlerFactory, + Action? configureClient) + => new CapMonsterCloudClient( + options, + HttpClients.GetOrAdd( + options.ServiceUri, + uri => CreateHttpClient(uri, httpMessageHandlerFactory, configureClient))); + + private static HttpClient CreateHttpClient( + Uri uri, + Func? httpMessageHandlerFactory, + Action? configureClient) + { + var handler = httpMessageHandlerFactory?.Invoke(); - configureClient?.Invoke(httpClient); + var httpClient = handler is not null + ? new HttpClient(handler, true) + : new HttpClient(); - httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd(CreateUserAgentString()); + httpClient.BaseAddress = uri; + httpClient.Timeout = HttpTimeout; - return httpClient; - } + configureClient?.Invoke(httpClient); - private static string CreateUserAgentString() - { - var fileVersionInfo = Assembly.GetExecutingAssembly().GetName(); - - return $"{fileVersionInfo.Name}/{fileVersionInfo.Version}"; - } + httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd(CreateUserAgentString()); - /// - public void Dispose() - { - var clients = HttpClients.Values.ToList(); + return httpClient; + } - HttpClients.Clear(); + private static string CreateUserAgentString() + { + var fileVersionInfo = Assembly.GetExecutingAssembly().GetName(); + return $"{fileVersionInfo.Name}/{fileVersionInfo.Version}"; + } - clients.ForEach(c => c.Dispose()); - } + /// + public void Dispose() + { + var clients = HttpClients.Values.ToList(); + HttpClients.Clear(); + clients.ForEach(c => c.Dispose()); } } diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs index c587a40..4a8c00e 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs @@ -1,227 +1,174 @@ -using System; +using System; using System.Collections.Generic; using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +public partial class CapMonsterCloudClient { - public partial class CapMonsterCloudClient + private struct GetResultTimeouts { - private struct GetResultTimeouts - { - public TimeSpan FirstRequestDelay { get; set; } - - public TimeSpan? FirstRequestNoCacheDelay { get; set; } + public TimeSpan FirstRequestDelay { get; set; } + public TimeSpan? FirstRequestNoCacheDelay { get; set; } + public TimeSpan RequestsInterval { get; set; } + public TimeSpan Timeout { get; set; } + } - public TimeSpan RequestsInterval { get; set; } + private static readonly GetResultTimeouts DefaultResultTimeouts = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(1), + Timeout = TimeSpan.FromSeconds(80) + }; - public TimeSpan Timeout { get; set; } + private static GetResultTimeouts GetTimeouts(Type type) + { + if (!ResultTimeouts.TryGetValue(type, out var getResultTimeouts) && + !ResultTimeouts.TryGetValue(type.BaseType!, out getResultTimeouts)) + { + getResultTimeouts = DefaultResultTimeouts; } - private static readonly GetResultTimeouts DefaultResultTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(80) - }; + return getResultTimeouts; + } - private static GetResultTimeouts GetTimeouts(Type type) + private static readonly IReadOnlyDictionary ResultTimeouts = + new Dictionary { - if (!ResultTimeouts.TryGetValue(type, out var getResultTimeouts) && - !ResultTimeouts.TryGetValue(type.BaseType, out getResultTimeouts)) + [typeof(RecaptchaV2Request)] = new() { - getResultTimeouts = DefaultResultTimeouts; - } - - return getResultTimeouts; - } - - private static readonly IReadOnlyDictionary ResultTimeouts = - new Dictionary - { - { - typeof(RecaptchaV2Request), new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(RecaptchaV2EnterpriseRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(RecaptchaV3ProxylessRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(ImageToTextRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromMilliseconds(350), - RequestsInterval = TimeSpan.FromMilliseconds(200), - Timeout = TimeSpan.FromSeconds(10) - } - }, - { - typeof(ComplexImageTaskRequestBase<>), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromMilliseconds(350), - RequestsInterval = TimeSpan.FromMilliseconds(200), - Timeout = TimeSpan.FromSeconds(10) - } - }, - { - typeof(FunCaptchaRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(80) - } - }, - { - typeof(HCaptchaRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(GeeTestRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(80) - } - }, - { - typeof(TurnstileRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(80) - } - }, - { - typeof(DataDomeCustomTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(AmazonWafRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(TenDiCustomTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(BasiliskCustomTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(100) - } - }, - { - typeof(BinanceTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(20) - } - }, - { - typeof(ImpervaCustomTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(15) - } - }, - { - typeof(TemuCustomTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(MTCaptchaTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(YidunTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - { - typeof(ProsopoTaskRequest), - new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - } - }, - }; - } + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(RecaptchaV2EnterpriseRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(RecaptchaV3ProxylessRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(RecaptchaV3EnterpriseRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(ImageToTextRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromMilliseconds(350), + RequestsInterval = TimeSpan.FromMilliseconds(200), + Timeout = TimeSpan.FromSeconds(10) + }, + [typeof(ComplexImageTaskRequestBase<>)] = new() + { + FirstRequestDelay = TimeSpan.FromMilliseconds(350), + RequestsInterval = TimeSpan.FromMilliseconds(200), + Timeout = TimeSpan.FromSeconds(10) + }, + [typeof(FunCaptchaRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(1), + Timeout = TimeSpan.FromSeconds(80) + }, + [typeof(HCaptchaRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(GeeTestRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + RequestsInterval = TimeSpan.FromSeconds(1), + Timeout = TimeSpan.FromSeconds(80) + }, + [typeof(TurnstileRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(1), + Timeout = TimeSpan.FromSeconds(80) + }, + [typeof(DataDomeCustomTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(AmazonWafRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(TenDiCustomTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(BasiliskCustomTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(100) + }, + [typeof(BinanceTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + RequestsInterval = TimeSpan.FromSeconds(1), + Timeout = TimeSpan.FromSeconds(20) + }, + [typeof(ImpervaCustomTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + RequestsInterval = TimeSpan.FromSeconds(1), + Timeout = TimeSpan.FromSeconds(15) + }, + [typeof(TemuCustomTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(MTCaptchaTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(YidunTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + [typeof(ProsopoTaskRequest)] = new() + { + FirstRequestDelay = TimeSpan.FromSeconds(1), + FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), + RequestsInterval = TimeSpan.FromSeconds(3), + Timeout = TimeSpan.FromSeconds(180) + }, + }; } diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs b/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs index f380c82..dd2fa76 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs @@ -1,86 +1,71 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +public partial class CapMonsterCloudClient { - public partial class CapMonsterCloudClient + private class ResponseBase { - private class ResponseBase - { -#pragma warning disable IDE1006 // Naming Styles - public int errorId { get; set; } - - public string errorCode { get; set; } -#pragma warning restore IDE1006 // Naming Styles - } + [JsonPropertyName("errorId")] + public int ErrorId { get; set; } - private class GetBalanceResponse : ResponseBase - { -#pragma warning disable IDE1006 // Naming Styles - public decimal balance { get; set; } -#pragma warning restore IDE1006 // Naming Styles - } - - private class CreateTaskResponse : ResponseBase - { -#pragma warning disable IDE1006 // Naming Styles - public int taskId { get; set; } -#pragma warning restore IDE1006 // Naming Styles - } - - private class GetTaskResultResponse : ResponseBase - { -#pragma warning disable IDE1006 // Naming Styles - public string status { get; set; } + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; set; } + } - public object solution { get; set; } -#pragma warning restore IDE1006 // Naming Styles - } + private class GetBalanceResponse : ResponseBase + { + [JsonPropertyName("balance")] + public decimal Balance { get; set; } + } - private abstract class TaskResult - { - public class TaskInProgress : TaskResult - { - } + private class CreateTaskResponse : ResponseBase + { + [JsonPropertyName("taskId")] + public int TaskId { get; set; } + } - public class TaskFailed : TaskResult - { - public TaskFailed(ErrorType error) - { - this.Error = error; + private class GetTaskResultResponse : ResponseBase + { + [JsonPropertyName("status")] + public string? Status { get; set; } - } + [JsonPropertyName("solution")] + public object? Solution { get; set; } + } - public ErrorType Error { get; } - } + private abstract class TaskResult + { + public class TaskInProgress : TaskResult; - public class TaskCompleted : TaskResult - { - public TaskCompleted(object solution) - { - this.Solution = solution; - } + public class TaskFailed(ErrorType error) : TaskResult + { + public ErrorType Error { get; } = error; + } - public object Solution { get; } - } + public class TaskCompleted(object? solution) : TaskResult + { + public object? Solution { get; } = solution; + } - public static TaskInProgress InProgress { get; } = new TaskInProgress(); + public static TaskInProgress InProgress { get; } = new(); - public static TaskFailed Failed(ErrorType error) => new TaskFailed(error); + public static TaskFailed Failed(ErrorType error) => new(error); - public static TaskCompleted Completed(object solution) => new TaskCompleted(solution); - } + public static TaskCompleted Completed(object? solution) => new(solution); + } - private class CreateTaskRequest where TSolution : CaptchaResponseBase - { -#pragma warning disable IDE1006 // Naming Styles - public string clientKey { get; set; } + private class CreateTaskRequest where TSolution : CaptchaResponseBase + { + [JsonPropertyName("clientKey")] + public string? ClientKey { get; set; } - public Requests.CaptchaRequestBase task { get; set; } + [JsonPropertyName("task")] + public Requests.CaptchaRequestBase? Task { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int? softId { get; set; } -#pragma warning restore IDE1006 // Naming Styles - } + [JsonPropertyName("softId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? SoftId { get; set; } } } diff --git a/CapMonsterCloud.Client/CapMonsterModules.cs b/CapMonsterCloud.Client/CapMonsterModules.cs index 3ebdc37..4ee1f9f 100644 --- a/CapMonsterCloud.Client/CapMonsterModules.cs +++ b/CapMonsterCloud.Client/CapMonsterModules.cs @@ -1,50 +1,36 @@ -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// capmonster.cloud recognition modules +/// +public static class CapMonsterModules { - /// - /// capmonster.cloud recognition modules - /// - public static class CapMonsterModules - { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - public const string Amazon = "amazon"; - - public const string BotDetect = "botdetect"; - - public const string Facebook = "facebook"; - - public const string Gmx = "gmx"; - - public const string Google = "google"; - - public const string Hotmail = "hotmail"; - - public const string MailRu = "mailru"; - - public const string Ok = "ok"; + public const string Amazon = "amazon"; + public const string BotDetect = "botdetect"; + public const string Facebook = "facebook"; + public const string Gmx = "gmx"; + public const string Google = "google"; + public const string Hotmail = "hotmail"; + public const string MailRu = "mailru"; + public const string Ok = "ok"; + public const string OkNew = "oknew"; + public const string RamblerRus = "ramblerrus"; + public const string SolveMedia = "solvemedia"; + public const string Steam = "steam"; + public const string Vk = "vk"; + public const string Yandex = "yandex"; - public const string OkNew = "oknew"; - - public const string RamblerRus = "ramblerrus"; - - public const string SolveMedia = "solvemedia"; - - public const string Steam = "steam"; - - public const string Vk = "vk"; - - public const string Yandex = "yandex"; - - /// - /// Yandex (two words) - /// - public const string YandexNew = "yandexnew"; + /// + /// Yandex (two words) + /// + public const string YandexNew = "yandexnew"; - public const string YandexWave = "yandexwave"; + public const string YandexWave = "yandexwave"; - /// - /// All other text captcha types - /// - public const string Universal = "universal"; + /// + /// All other text captcha types + /// + public const string Universal = "universal"; #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - } } diff --git a/CapMonsterCloud.Client/CaptchaResult.cs b/CapMonsterCloud.Client/CaptchaResult.cs index faab61b..a91b032 100644 --- a/CapMonsterCloud.Client/CaptchaResult.cs +++ b/CapMonsterCloud.Client/CaptchaResult.cs @@ -1,21 +1,20 @@ -using Zennolab.CapMonsterCloud.Responses; +using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// General captcha recognition result +/// +/// Concrete captcha result type +public class CaptchaResult where TSolution : CaptchaResponseBase { /// - /// General captcha recognition result + /// Error code /// - /// Concrete captcha result type - public class CaptchaResult where TSolution : CaptchaResponseBase - { - /// - /// Error code - /// - public ErrorType? Error { get; internal set; } + public ErrorType? Error { get; internal set; } - /// - /// Task result. Different for each type of task. - /// - public TSolution Solution { get; internal set; } - } + /// + /// Task result. Different for each type of task. + /// + public TSolution? Solution { get; internal set; } } diff --git a/CapMonsterCloud.Client/ClientOptions.cs b/CapMonsterCloud.Client/ClientOptions.cs index 7277f9c..820e570 100644 --- a/CapMonsterCloud.Client/ClientOptions.cs +++ b/CapMonsterCloud.Client/ClientOptions.cs @@ -1,27 +1,27 @@ -using System; +using System; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// Client options +/// +public class ClientOptions { + internal const int DefaultSoftId = 53; + /// - /// Client options + /// capmonster.cloud API URI. + /// By default https://api.capmonster.cloud /// - public class ClientOptions - { - internal const int DefaultSoftId = 53; - /// - /// capmonster.cloud API URI. - /// By default https://api.capmonster.cloud - /// - public Uri ServiceUri { get; set; } = new Uri("https://api.capmonster.cloud"); + public Uri ServiceUri { get; set; } = new("https://api.capmonster.cloud"); - /// - /// capmonster.cloud API key - /// - public string ClientKey { get; set; } + /// + /// capmonster.cloud API key + /// + public string ClientKey { get; set; } = string.Empty; - /// - /// SoftId - /// - public int? SoftId { get; set; } - } + /// + /// SoftId + /// + public int? SoftId { get; set; } } diff --git a/CapMonsterCloud.Client/ErrorCodeConverter.cs b/CapMonsterCloud.Client/ErrorCodeConverter.cs index ea17b2c..bbed402 100644 --- a/CapMonsterCloud.Client/ErrorCodeConverter.cs +++ b/CapMonsterCloud.Client/ErrorCodeConverter.cs @@ -1,24 +1,25 @@ -using System; +using System; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +internal static class ErrorCodeConverter { - internal static class ErrorCodeConverter + public static ErrorType Convert(string? errorCode) { - public static ErrorType Convert(string errorCode) - { - const string Prefix = "ERROR_"; - const int PrefixLen = 6; + if (string.IsNullOrEmpty(errorCode)) + return ErrorType.Unknown; - if (errorCode.StartsWith(Prefix)) - { - errorCode = errorCode.Substring(PrefixLen, errorCode.Length - PrefixLen); - } + const string Prefix = "ERROR_"; - return Enum.TryParse(errorCode, ignoreCase: true, out var result) - ? result - : errorCode.Equals("WRONG_CAPTCHA_ID", StringComparison.OrdinalIgnoreCase) - ? ErrorType.NO_SUCH_CAPCHA_ID - : ErrorType.Unknown; + if (errorCode.StartsWith(Prefix, StringComparison.Ordinal)) + { + errorCode = errorCode[Prefix.Length..]; } + + return Enum.TryParse(errorCode, ignoreCase: true, out var result) + ? result + : errorCode.Equals("WRONG_CAPTCHA_ID", StringComparison.OrdinalIgnoreCase) + ? ErrorType.NO_SUCH_CAPCHA_ID + : ErrorType.Unknown; } } diff --git a/CapMonsterCloud.Client/ErrorType.cs b/CapMonsterCloud.Client/ErrorType.cs index 8ad33d2..c18db6a 100644 --- a/CapMonsterCloud.Client/ErrorType.cs +++ b/CapMonsterCloud.Client/ErrorType.cs @@ -1,76 +1,75 @@ -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// Error types +/// +public enum ErrorType { /// - /// Error types + /// Captcha recognition timeout is expired /// - public enum ErrorType - { - /// - /// Captcha recognition timeout is expired - /// - Timeout = -2, - /// - /// Unknown error. Maybe client library is outdated - /// - Unknown = -1, - /// - /// Account authorization key not found in the system or has incorrect format - /// - KEY_DOES_NOT_EXIST = 0, - /// - /// The size of the captcha you are uploading is less than 100 bytes. - /// - ZERO_CAPTCHA_FILESIZE, - /// - /// The size of the captcha you are uploading is more than 50,000 bytes. - /// - TOO_BIG_CAPTCHA_FILESIZE, - /// - /// Account has zero balance - /// - ZERO_BALANCE, - /// - /// Request with current account key is not allowed from your IP - /// - IP_NOT_ALLOWED, - /// - /// This type of captchas is not supported by the service - /// or the image does not contain an answer, perhaps it is too noisy. - /// It could also mean that the image is corrupted or was incorrectly rendered. - /// - CAPTCHA_UNSOLVABLE, - /// - /// The captcha that you are requesting was not found. - /// Make sure you are requesting a status update only within 5 minutes of uploading. - /// - NO_SUCH_CAPCHA_ID, - /// - /// You have exceeded the limit of requests with the wrong api key, - /// check the correctness of your api key in the control panel and after some time, try again - /// - IP_BANNED, - /// - /// This method is not supported or empty - /// - NO_SUCH_METHOD, - /// - /// You have exceeded the limit of requests to receive an answer for one task. - /// Try to request the result of the task no more than 1 time in 2 seconds. - /// - TOO_MUCH_REQUESTS, - /// - /// Captcha from some domains cannot be solved in CapMonster Cloud. - /// If you try to create a task for such a domain, this error will return. - /// - DOMAIN_NOT_ALLOWED, - /// - /// Captcha provider server reported that the additional token has expired. - /// Try creating task with a new token. - /// - TOKEN_EXPIRED, - /// - /// You have excedded requests rate limit, try to decrease parallel tasks amount. - /// - NO_SLOT_AVAILABLE - } + Timeout = -2, + /// + /// Unknown error. Maybe client library is outdated + /// + Unknown = -1, + /// + /// Account authorization key not found in the system or has incorrect format + /// + KEY_DOES_NOT_EXIST = 0, + /// + /// The size of the captcha you are uploading is less than 100 bytes. + /// + ZERO_CAPTCHA_FILESIZE, + /// + /// The size of the captcha you are uploading is more than 50,000 bytes. + /// + TOO_BIG_CAPTCHA_FILESIZE, + /// + /// Account has zero balance + /// + ZERO_BALANCE, + /// + /// Request with current account key is not allowed from your IP + /// + IP_NOT_ALLOWED, + /// + /// This type of captchas is not supported by the service + /// or the image does not contain an answer, perhaps it is too noisy. + /// It could also mean that the image is corrupted or was incorrectly rendered. + /// + CAPTCHA_UNSOLVABLE, + /// + /// The captcha that you are requesting was not found. + /// Make sure you are requesting a status update only within 5 minutes of uploading. + /// + NO_SUCH_CAPCHA_ID, + /// + /// You have exceeded the limit of requests with the wrong api key, + /// check the correctness of your api key in the control panel and after some time, try again + /// + IP_BANNED, + /// + /// This method is not supported or empty + /// + NO_SUCH_METHOD, + /// + /// You have exceeded the limit of requests to receive an answer for one task. + /// Try to request the result of the task no more than 1 time in 2 seconds. + /// + TOO_MUCH_REQUESTS, + /// + /// Captcha from some domains cannot be solved in CapMonster Cloud. + /// If you try to create a task for such a domain, this error will return. + /// + DOMAIN_NOT_ALLOWED, + /// + /// Captcha provider server reported that the additional token has expired. + /// Try creating task with a new token. + /// + TOKEN_EXPIRED, + /// + /// You have excedded requests rate limit, try to decrease parallel tasks amount. + /// + NO_SLOT_AVAILABLE } diff --git a/CapMonsterCloud.Client/GetBalanceException.cs b/CapMonsterCloud.Client/GetBalanceException.cs index e2a40a4..ebab12a 100644 --- a/CapMonsterCloud.Client/GetBalanceException.cs +++ b/CapMonsterCloud.Client/GetBalanceException.cs @@ -1,23 +1,13 @@ -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// Exception on getting balance +/// +public class GetBalanceException(ErrorType error) + : CapmonsterCloudClientException($"Cannot get balance. Error was {error}") { /// - /// Exception on getting balance + /// Gets occured error /// - public class GetBalanceException : CapmonsterCloudClientException - { - /// - /// Creates new exception on getting balance - /// - /// error - public GetBalanceException(ErrorType error) - : base($"Cannot get balance. Error was {error}") - { - Error = error; - } - - /// - /// Gets occured error - /// - public ErrorType Error { get; } - } + public ErrorType Error { get; } = error; } diff --git a/CapMonsterCloud.Client/ICapMonsterCloudClient.cs b/CapMonsterCloud.Client/ICapMonsterCloudClient.cs index a72913f..8dec61d 100644 --- a/CapMonsterCloud.Client/ICapMonsterCloudClient.cs +++ b/CapMonsterCloud.Client/ICapMonsterCloudClient.cs @@ -1,36 +1,34 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Net.Http; -using System.Runtime.InteropServices.ComTypes; using System.Threading; using System.Threading.Tasks; using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// CapMonsterCloud client +/// +public interface ICapMonsterCloudClient { /// - /// CapMonsterCloud client + /// Gets current amount of money on balance /// - public interface ICapMonsterCloudClient - { - /// - /// Gets current amount of money on balance - /// - /// - /// Current amount of money - /// - Task GetBalanceAsync(CancellationToken cancellationToken = default); + /// + /// Current amount of money + /// + Task GetBalanceAsync(CancellationToken cancellationToken = default); - /// - /// Solve task - /// - /// - /// - /// Captcha recognition result - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - Task> SolveAsync( - CaptchaRequestBase task, - CancellationToken cancellationToken = default) where TSolution : CaptchaResponseBase; - } + /// + /// Solve task + /// + /// + /// + /// Captcha recognition result + /// malformed task object + /// exception on processing HTTP request to capmonster.cloud + Task> SolveAsync( + CaptchaRequestBase task, + CancellationToken cancellationToken = default) where TSolution : CaptchaResponseBase; } diff --git a/CapMonsterCloud.Client/ICapMonsterCloudClientFactory.cs b/CapMonsterCloud.Client/ICapMonsterCloudClientFactory.cs index 636235f..fd40c83 100644 --- a/CapMonsterCloud.Client/ICapMonsterCloudClientFactory.cs +++ b/CapMonsterCloud.Client/ICapMonsterCloudClientFactory.cs @@ -1,14 +1,13 @@ -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// Interface for factory +/// +public interface ICapMonsterCloudClientFactory { /// - /// Interface for factory + /// Creates new instance of /// - public interface ICapMonsterCloudClientFactory - { - /// - /// Creates new instance of - /// - /// instance of - ICapMonsterCloudClient Create(); - } -} \ No newline at end of file + /// instance of + ICapMonsterCloudClient Create(); +} diff --git a/CapMonsterCloud.Client/Json/CamelCaseStringEnumConverter.cs b/CapMonsterCloud.Client/Json/CamelCaseStringEnumConverter.cs new file mode 100644 index 0000000..4dde0c1 --- /dev/null +++ b/CapMonsterCloud.Client/Json/CamelCaseStringEnumConverter.cs @@ -0,0 +1,6 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Zennolab.CapMonsterCloud.Json; + +internal sealed class CamelCaseStringEnumConverter() : JsonStringEnumConverter(JsonNamingPolicy.CamelCase); diff --git a/CapMonsterCloud.Client/Json/DictionaryToSemicolonSplittedStringConverter.cs b/CapMonsterCloud.Client/Json/DictionaryToSemicolonSplittedStringConverter.cs index 7d52222..6d0b791 100644 --- a/CapMonsterCloud.Client/Json/DictionaryToSemicolonSplittedStringConverter.cs +++ b/CapMonsterCloud.Client/Json/DictionaryToSemicolonSplittedStringConverter.cs @@ -1,39 +1,39 @@ -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; -namespace Zennolab.CapMonsterCloud.Json +namespace Zennolab.CapMonsterCloud.Json; + +internal sealed class DictionaryToSemicolonSplittedStringConverter : JsonConverter> { - internal sealed class DictionaryToSemicolonSplittedStringConverter : JsonConverter + public override IDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override bool CanConvert(Type objectType) - => objectType.IsAssignableFrom(typeof(IDictionary)); + if (reader.TokenType != JsonTokenType.String) + throw new JsonException("Expected a string token for semicolon-separated dictionary"); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var value = reader.Value?.ToString(); - var regexValidation = new Regex("^((.*)(=?){2};?)+$"); + var value = reader.GetString(); + var regexValidation = new Regex("^((.*)(=?){2};?)+$"); - if (regexValidation.IsMatch(value)) - try + if (value is not null && regexValidation.IsMatch(value)) + try + { + return value.Split(';').Select(item => { - return value.Split(';').Select(item => - { - var keyValueItem = item.Split('='); - return new KeyValuePair(keyValueItem[0], keyValueItem[1]); - }).ToDictionary(x => x.Key, x => x.Value); - } - catch (Exception) { } + var keyValueItem = item.Split('='); + return new KeyValuePair(keyValueItem[0], keyValueItem[1]); + }).ToDictionary(x => x.Key, x => x.Value); + } + catch (Exception) { } - throw new JsonReaderException(); - } + throw new JsonException("Invalid semicolon-separated dictionary format"); + } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var result = string.Join(";", ((IDictionary)value).Select(kvp => $"{kvp.Key}={kvp.Value}")); - writer.WriteValue(result); - } + public override void Write(Utf8JsonWriter writer, IDictionary value, JsonSerializerOptions options) + { + var result = string.Join(";", value.Select(kvp => $"{kvp.Key}={kvp.Value}")); + writer.WriteStringValue(result); } } diff --git a/CapMonsterCloud.Client/Json/NumericFlagConverter.cs b/CapMonsterCloud.Client/Json/NumericFlagConverter.cs index 0a901f5..b701e8f 100644 --- a/CapMonsterCloud.Client/Json/NumericFlagConverter.cs +++ b/CapMonsterCloud.Client/Json/NumericFlagConverter.cs @@ -1,17 +1,20 @@ -using Newtonsoft.Json; using System; +using System.Text.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Json -{ - internal sealed class NumericFlagConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - => objectType == typeof(bool); +namespace Zennolab.CapMonsterCloud.Json; - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => - Convert.ToBoolean(reader.Value); +internal sealed class NumericFlagConverter : JsonConverter +{ + public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.Number => reader.GetInt32() != 0, + JsonTokenType.True => true, + JsonTokenType.False => false, + _ => throw new JsonException($"Unexpected token type {reader.TokenType} for bool") + }; - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => - writer.WriteValue((bool)value ? 1 : 0); - } + public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) => + writer.WriteNumberValue(value ? 1 : 0); } diff --git a/CapMonsterCloud.Client/Json/RecognitionAnswerJsonConverter.cs b/CapMonsterCloud.Client/Json/RecognitionAnswerJsonConverter.cs index 18eef12..6213e7d 100644 --- a/CapMonsterCloud.Client/Json/RecognitionAnswerJsonConverter.cs +++ b/CapMonsterCloud.Client/Json/RecognitionAnswerJsonConverter.cs @@ -1,82 +1,73 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; +using System.Text.Json; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Json +namespace Zennolab.CapMonsterCloud.Json; + +internal class RecognitionAnswerJsonConverter : JsonConverter { - internal class RecognitionAnswerJsonConverter : JsonConverter + public override DynamicComplexImageTaskResponse Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override DynamicComplexImageTaskResponse ReadJson(JsonReader reader, Type objectType, DynamicComplexImageTaskResponse existingValue, bool hasExistingValue, JsonSerializer serializer) - { - JObject obj = JObject.Load(reader); + using var doc = JsonDocument.ParseValue(ref reader); + var root = doc.RootElement; - if (!obj.ContainsKey("metadata") || !obj.ContainsKey("answer")) - throw new JsonSerializationException("Missing 'metadata' or 'answer' field in response"); + if (!root.TryGetProperty("metadata", out var metadataEl) || + !root.TryGetProperty("answer", out var answerEl)) + throw new JsonException("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}"); - } + var answerType = metadataEl.TryGetProperty("AnswerType", out var atEl) + ? atEl.GetString() + : null; - return response; - } + if (string.IsNullOrEmpty(answerType)) + throw new JsonException("AnswerType is missing in metadata"); - public override void WriteJson(JsonWriter writer, DynamicComplexImageTaskResponse value, JsonSerializer serializer) + var response = new DynamicComplexImageTaskResponse { - if (value == null) + Metadata = new DynamicComplexImageTaskResponse.RecognitionMetadata { - writer.WriteNull(); - return; + AnswerType = answerType + }, + Answer = answerType switch + { + "NumericArray" => new RecognitionAnswer + { + NumericAnswer = JsonSerializer.Deserialize(answerEl.GetRawText(), options) + }, + "Grid" => new RecognitionAnswer + { + GridAnswer = JsonSerializer.Deserialize(answerEl.GetRawText(), options) + }, + _ => throw new JsonException($"Unknown AnswerType: {answerType}") } + }; - writer.WriteStartObject(); - - writer.WritePropertyName("answer"); + return response; + } - 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."); - } + public override void Write(Utf8JsonWriter writer, DynamicComplexImageTaskResponse value, JsonSerializerOptions options) + { + writer.WriteStartObject(); - writer.WritePropertyName("metadata"); - serializer.Serialize(writer, value.Metadata); + writer.WritePropertyName("answer"); - writer.WriteEndObject(); + if (value.Metadata?.AnswerType == "Grid" && value.Answer?.GridAnswer is not null) + { + JsonSerializer.Serialize(writer, value.Answer.GridAnswer, options); + } + else if (value.Metadata?.AnswerType == "NumericArray" && value.Answer?.NumericAnswer is not null) + { + JsonSerializer.Serialize(writer, value.Answer.NumericAnswer, options); } + else + { + throw new JsonException("Invalid or missing answer data for the specified AnswerType."); + } + + writer.WritePropertyName("metadata"); + JsonSerializer.Serialize(writer, value.Metadata, options); + + writer.WriteEndObject(); } -} \ No newline at end of file +} diff --git a/CapMonsterCloud.Client/ProxyContainer.cs b/CapMonsterCloud.Client/ProxyContainer.cs index 4449e01..4e56ec5 100644 --- a/CapMonsterCloud.Client/ProxyContainer.cs +++ b/CapMonsterCloud.Client/ProxyContainer.cs @@ -1,72 +1,70 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Validation; -namespace Zennolab.CapMonsterCloud +namespace Zennolab.CapMonsterCloud; + +/// +/// Represents a proxy configuration container with validation. +/// +public class ProxyContainer { /// - /// Represents a proxy configuration container with validation. + /// Initializes a new instance of the class. /// - public class ProxyContainer + /// The proxy IP address (IPv4/IPv6). Must not be a transparent proxy, or from a local network. + /// The proxy port number (0-65535). + /// The type of the proxy. + /// The login credential for proxies requiring authentication. + /// The password credential for proxies requiring authentication. + /// Thrown when the port is out of range or the address is invalid. + public ProxyContainer( + string address, + int port, + ProxyType type, + string? login, + string? password) { - /// - /// Initializes a new instance of the class. - /// - /// The proxy IP address (IPv4/IPv6). Must not be a transparent proxy, or from a local network. - /// The proxy port number (0-65535). - /// The type of the proxy. - /// The login credential for proxies requiring authentication. - /// The password credential for proxies requiring authentication. - /// Thrown when the port is out of range or the address is invalid. - public ProxyContainer( - string address, - int port, - ProxyType type, - string login, - string password) - { - if (port < 0 || port > 65535) - throw new ArgumentException("Proxy port must be between 0 and 65535"); - - if (!ProxyValidator.IsValidProxy(address)) - throw new ArgumentException("Proxy address is not valid"); + if (port is < 0 or > 65535) + throw new ArgumentException("Proxy port must be between 0 and 65535"); - ProxyAddress = address; - ProxyPort = port; + if (!ProxyValidator.IsValidProxy(address)) + throw new ArgumentException("Proxy address is not valid"); - ProxyType = type; - ProxyLogin = login; - ProxyPassword = password; - } + ProxyAddress = address; + ProxyPort = port; + ProxyType = type; + ProxyLogin = login; + ProxyPassword = password; + } - /// - /// Proxy IP address IPv4/IPv6. Not allowed to use: - /// - host names instead of IPs - /// - transparent proxies(where client IP is visible) - /// - proxies from local networks(192.., 10.., 127...) - /// - public string ProxyAddress { get; private set; } + /// + /// Proxy IP address IPv4/IPv6. Not allowed to use: + /// - host names instead of IPs + /// - transparent proxies(where client IP is visible) + /// - proxies from local networks(192.., 10.., 127...) + /// + public string ProxyAddress { get; private set; } - /// - /// Proxy port - /// - [Range(0, 65535)] - public int ProxyPort { get; private set; } + /// + /// Proxy port + /// + [Range(0, 65535)] + public int ProxyPort { get; private set; } - /// - /// Type of the proxy - /// - public ProxyType ProxyType { get; private set; } + /// + /// Type of the proxy + /// + public ProxyType ProxyType { get; private set; } - /// - /// Login for proxy which requires authorization (basic) - /// - public string ProxyLogin { get; private set; } + /// + /// Login for proxy which requires authorization (basic) + /// + public string? ProxyLogin { get; private set; } - /// - /// Proxy password - /// - public string ProxyPassword { get; private set; } - } + /// + /// Proxy password + /// + public string? ProxyPassword { get; private set; } } diff --git a/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs b/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs index 2f8f9c6..cd08a24 100644 --- a/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs +++ b/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs @@ -1,72 +1,71 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// AmazonWaf recognition request. +/// +/// +/// https://docs.capmonster.cloud/docs/captchas/amazon-task +/// +public class AmazonWafRequest : CaptchaRequestBaseWithProxy { /// - /// AmazonWaf recognition request. + /// Recognition task type /// - /// - /// https://docs.capmonster.cloud/docs/captchas/amazon-task - /// - public class AmazonWafRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "AmazonTask"; + public const string TaskType = "AmazonTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// The address of the main page where captcha is solved. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// The address of the main page where captcha is solved. + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// A string that can be retrieved from an html page with a captcha or with javascript by executing the window.gokuProps.key - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// A string that can be retrieved from an html page with a captcha or with javascript by executing the window.gokuProps.key + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// Link to challenge.js (see description below the table) - /// - [JsonProperty("challengeScript", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string ChallengeScript { get; set; } + /// + /// Link to challenge.js (see description below the table) + /// + [JsonPropertyName("challengeScript")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string ChallengeScript { get; set; } = null!; - /// - /// Link to captcha.js (see description below the table) - /// - [JsonProperty("captchaScript", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string CaptchaScript { get; set; } + /// + /// Link to captcha.js (see description below the table) + /// + [JsonPropertyName("captchaScript")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string CaptchaScript { get; set; } = null!; - /// - /// A string that can be retrieved from an html page with a captcha or with javascript by executing the window.gokuProps.context - /// - [JsonProperty("context", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string Context { get; set; } + /// + /// A string that can be retrieved from an html page with a captcha or with javascript by executing the window.gokuProps.context + /// + [JsonPropertyName("context")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string Context { get; set; } = null!; - /// - /// A string that can be retrieved from an html page with a captcha or with javascript by executing the window.gokuProps.iv - /// - [JsonProperty("iv", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string Iv { get; set; } + /// + /// A string that can be retrieved from an html page with a captcha or with javascript by executing the window.gokuProps.iv + /// + [JsonPropertyName("iv")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string Iv { get; set; } = null!; - /// - /// By default false. If you need to use cookies "aws-waf-token", specify the value true. Otherwise, what you will get in return is "captcha_voucher" and "existing_token". - /// - [JsonProperty("cookieSolution")] - public bool CookieSolution { get; set; } - } + /// + /// By default false. If you need to use cookies "aws-waf-token", specify the value true. Otherwise, what you will get in return is "captcha_voucher" and "existing_token". + /// + [JsonPropertyName("cookieSolution")] + public bool CookieSolution { get; set; } } diff --git a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs index aeecb88..7260402 100644 --- a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs @@ -1,22 +1,21 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Basilisk CustomTask recognition request +/// +public class BasiliskCustomTaskRequest : CustomTaskRequestBase { + /// + public override string Class => "Basilisk"; + /// - /// Basilisk CustomTask recognition request + /// Can be found in the html code in the attribute data-sitekey of the captcha container or in the payload of a POST request to the https://basiliskcaptcha.com/challenge/check-site in the field site_key /// - public class BasiliskCustomTaskRequest : CustomTaskRequestBase - { - /// - public override string Class => "Basilisk"; - - /// - /// Can be found in the html code in the attribute data-sitekey of the captcha container or in the payload of a POST request to the https://basiliskcaptcha.com/challenge/check-site in the field site_key - /// - /// b7890hre5cf2544b2759c19fb2600897 - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } - } + /// b7890hre5cf2544b2759c19fb2600897 + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; } diff --git a/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs b/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs index 553e883..7ecc0b4 100644 --- a/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs @@ -1,54 +1,53 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// BinanceTask recognition request. +/// +/// +/// https://docs.capmonster.cloud/docs/captchas/binance +/// +public sealed class BinanceTaskRequest : CaptchaRequestBaseWithProxy { /// - /// BinanceTask recognition request. + /// Recognition task type /// - /// - /// https://docs.capmonster.cloud/docs/captchas/binance - /// - public sealed class BinanceTaskRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "BinanceTask"; + public const string TaskType = "BinanceTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// The address of the main page where the captcha is solved. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// The address of the main page where the captcha is solved. + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// A unique parameter for your website's section. The value of the parameter bizId, bizType, or bizCode. It can be taken from the traffic - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// A unique parameter for your website's section. The value of the parameter bizId, bizType, or bizCode. It can be taken from the traffic + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// A dynamic key. The value of the parameter validateId, securityId, or securityCheckResponseValidateId. It can be taken from the traffic. - /// - [JsonProperty("validateId", Required = Required.Always)] - public string ValidateId { get; set; } + /// + /// A dynamic key. The value of the parameter validateId, securityId, or securityCheckResponseValidateId. It can be taken from the traffic. + /// + [JsonPropertyName("validateId")] + public string ValidateId { get; set; } = null!; - /// - /// Browser's User-Agent which is used in emulation. - /// - /// - /// It is required that you use a signature of a modern browser, - /// otherwise Google will ask you to "update your browser". - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } - } + /// + /// Browser's User-Agent which is used in emulation. + /// + /// + /// It is required that you use a signature of a modern browser, + /// otherwise Google will ask you to "update your browser". + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } } diff --git a/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs b/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs index 03aa2d7..71d436f 100644 --- a/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs +++ b/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs @@ -1,18 +1,17 @@ -using Zennolab.CapMonsterCloud.Responses; +using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests -{ +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Base captcha recognition request +/// +public abstract class CaptchaRequestBase where TResponse : CaptchaResponseBase +{ /// - /// Base captcha recognition request + /// Gets recognition task type /// - public abstract class CaptchaRequestBase where TResponse : CaptchaResponseBase - { - /// - /// Gets recognition task type - /// - public abstract string Type { get; } + public abstract string Type { get; } - internal virtual bool UseNoCache => false; - } + internal virtual bool UseNoCache => false; } diff --git a/CapMonsterCloud.Client/Requests/CaptchaRequestBaseWithProxy.cs b/CapMonsterCloud.Client/Requests/CaptchaRequestBaseWithProxy.cs index c2f49c9..99a8e06 100644 --- a/CapMonsterCloud.Client/Requests/CaptchaRequestBaseWithProxy.cs +++ b/CapMonsterCloud.Client/Requests/CaptchaRequestBaseWithProxy.cs @@ -1,59 +1,62 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Base captcha recognition request +/// +public abstract class CaptchaRequestBaseWithProxy : CaptchaRequestBase, IProxyInfo where TResponse : CaptchaResponseBase { - /// - /// Base captcha recognition request - /// - public abstract class CaptchaRequestBaseWithProxy : CaptchaRequestBase, IProxyInfo where TResponse : CaptchaResponseBase + /// + [JsonIgnore] + public ProxyContainer? Proxy { - /// - [JsonIgnore] - public ProxyContainer Proxy + get { - get - { - if (!string.IsNullOrEmpty(ProxyAddress)) - return new ProxyContainer(ProxyAddress, ProxyPort, ProxyType, ProxyLogin, ProxyPassword); + if (!string.IsNullOrEmpty(ProxyAddress)) + return new ProxyContainer(ProxyAddress, ProxyPort, ProxyType, ProxyLogin, ProxyPassword); - return null; - } - set + return null; + } + set + { + if (value is not null) { - if (value != null) - { - ProxyAddress = value.ProxyAddress; - ProxyPort = value.ProxyPort; - ProxyType = value.ProxyType; - ProxyLogin = value.ProxyLogin; - ProxyPassword = value.ProxyPassword; - } + ProxyAddress = value.ProxyAddress; + ProxyPort = value.ProxyPort; + ProxyType = value.ProxyType; + ProxyLogin = value.ProxyLogin; + ProxyPassword = value.ProxyPassword; } } + } - /// - [JsonProperty("proxyAddress")] - internal string ProxyAddress { get; set; } + /// + [JsonPropertyName("proxyAddress")] + [JsonInclude] + internal string? ProxyAddress { get; set; } - /// - [JsonProperty("proxyPort")] - [Range(0, 65535)] - protected internal int ProxyPort { get; set; } + /// + [JsonPropertyName("proxyPort")] + [JsonInclude] + [Range(0, 65535)] + protected internal int ProxyPort { get; set; } - /// - [JsonProperty("proxyType")] - [JsonConverter(typeof(StringEnumConverter))] - internal ProxyType ProxyType { get; set; } + /// + [JsonPropertyName("proxyType")] + [JsonInclude] + [JsonConverter(typeof(Json.CamelCaseStringEnumConverter))] + internal ProxyType ProxyType { get; set; } - /// - [JsonProperty("proxyLogin")] - internal string ProxyLogin { get; set; } + /// + [JsonPropertyName("proxyLogin")] + [JsonInclude] + internal string? ProxyLogin { get; set; } - /// - [JsonProperty("proxyPassword")] - internal string ProxyPassword { get; set; } - } + /// + [JsonPropertyName("proxyPassword")] + [JsonInclude] + internal string? ProxyPassword { get; set; } } diff --git a/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs b/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs index 56f6f18..568aff8 100644 --- a/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs +++ b/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs @@ -1,56 +1,55 @@ -using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// ComplexImageTask recognition request +/// +public abstract class ComplexImageTaskRequestBase : CaptchaRequestBase where TResponse : CaptchaResponseBase { /// - /// ComplexImageTask recognition request + /// Recognition task type + /// + public const string TaskType = "ComplexImageTask"; + + /// + [JsonPropertyName("type")] + public sealed override string Type => TaskType; + + /// + /// Class(subtype) of ComplexImageTask + /// + [JsonPropertyName("class")] + public abstract string Class { get; } + + /// + /// Collection with image urls. Must be populated if not. + /// + [JsonPropertyName("imageUrls")] + public ICollection? ImageUrls { get; set; } + + /// + /// Collection with base64 encoded images. Must be populated if not. + /// + [JsonPropertyName("imagesBase64")] + public ICollection? ImagesBase64 { get; set; } + + /// + /// Browser's User-Agent which is used in emulation. + /// + /// + /// It is required that you use a signature of a modern browser + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } + + /// + /// Address of a webpage with captcha /// - public abstract class ComplexImageTaskRequestBase : CaptchaRequestBase where TResponse : CaptchaResponseBase - { - /// - /// Recognition task type - /// - public const string TaskType = "ComplexImageTask"; - - /// - [JsonProperty("type", Required = Required.Always)] - public sealed override string Type => TaskType; - - /// - /// Class(subtype) of ComplexImageTask - /// - [JsonProperty("class", Required = Required.Always)] - public abstract string Class { get; } - - /// - /// Collection with image urls. Must be populated if not. - /// - [JsonProperty("imageUrls")] - public ICollection ImageUrls { get; set; } - - /// - /// Collection with base64 encoded images. Must be populated if not. - /// - [JsonProperty("imagesBase64")] - public ICollection ImagesBase64 { get; set; } - - /// - /// Browser's User-Agent which is used in emulation. - /// - /// - /// It is required that you use a signature of a modern browser - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } - - /// - /// Address of a webpage with captcha - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - } + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; } diff --git a/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs b/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs index 6a69ff8..a0c9272 100644 --- a/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs +++ b/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs @@ -1,58 +1,59 @@ -using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// CustomTask recognition request +/// +public abstract class CustomTaskRequestBase : CaptchaRequestBaseWithProxy { /// - /// CustomTask recognition request + /// Recognition task type + /// + public const string TaskType = "CustomTask"; + + /// + [JsonPropertyName("type")] + public sealed override string Type => TaskType; + + /// + /// Class (subtype) of CustomTask + /// + [JsonPropertyName("class")] + public abstract string Class { get; } + + /// + /// Address of the main page where the captcha is solved + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = string.Empty; + + /// + /// The object that contains additional data about the captcha - captchaUrl: "captchaUrl": "..." + /// You can take the link from the page with the captcha. + /// Often it looks like https://geo.captcha-delivery.com/captcha/?initialCid=... + /// + [JsonPropertyName("metadata")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public object? Metadata { get; set; } + + /// + /// Browser's User-Agent which is used in emulation. + /// + /// + /// Pass only the actual User-Agent from Windows OS. + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } + + /// + /// For the specified domains the corresponding cookies will be returned in the response. /// - public abstract class CustomTaskRequestBase : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "CustomTask"; - - /// - [JsonProperty("type", Required = Required.Always)] - public sealed override string Type => TaskType; - - /// - /// Class (subtype) of CustomTask - /// - [JsonProperty("class", Required = Required.Always)] - public abstract string Class { get; } - - /// - /// Address of the main page where the captcha is solved - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - - /// - /// The object that contains additional data about the captcha - captchaUrl: "captchaUrl": "..." - /// You can take the link from the page with the captcha. - /// Often it looks like https://geo.captcha-delivery.com/captcha/?initialCid=... - /// - [JsonProperty("metadata", Required = Required.Always, NullValueHandling = NullValueHandling.Ignore)] - public object Metadata { get; set; } - - /// - /// Browser's User-Agent which is used in emulation. - /// - /// - /// Pass only the actual User-Agent from Windows OS. - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } - - /// - /// For the specified domains the corresponding cookies will be returned in the response. - /// - [JsonProperty("domains", NullValueHandling = NullValueHandling.Ignore)] - public ICollection Domains { get; set; } - } + [JsonPropertyName("domains")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ICollection? Domains { get; set; } } diff --git a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs index 4294d7f..519e281 100644 --- a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs @@ -1,30 +1,29 @@ -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// DataDome CustomTask recognition request +/// +public sealed class DataDomeCustomTaskRequest : CustomTaskRequestBase { + /// + public override string Class => "DataDome"; + /// - /// DataDome CustomTask recognition request + /// + /// 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 sealed class DataDomeCustomTaskRequest : 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=..." - /// - /// - public DataDomeCustomTaskRequest(string datadomeCookie, string captchaUrl = null, string htmlPageBase64 = null) => Metadata = new { datadomeCookie, captchaUrl, htmlPageBase64 }; - } + public DataDomeCustomTaskRequest(string datadomeCookie, string? captchaUrl = null, string? htmlPageBase64 = null) => Metadata = new { datadomeCookie, captchaUrl, htmlPageBase64 }; } diff --git a/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs b/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs index 87d4b94..b04ddeb 100644 --- a/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs +++ b/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs @@ -1,65 +1,65 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// FunCaptcha recognition request. +/// +public sealed class FunCaptchaRequest : CaptchaRequestBaseWithProxy { /// - /// FunCaptcha recognition request. + /// Recognition task type /// - public sealed class FunCaptchaRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "FunCaptchaTask"; + public const string TaskType = "FunCaptchaTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// Address of a webpage with FunCaptcha - /// - /// https://funcaptcha.com/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Address of a webpage with FunCaptcha + /// + /// https://funcaptcha.com/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// FunCaptcha website key. - /// ]]> - /// - /// 69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC - [JsonProperty("websitePublicKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// FunCaptcha website key. + /// ]]> + /// + /// 69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC + [JsonPropertyName("websitePublicKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// A special subdomain of funcaptcha.com, from which the JS captcha widget should be loaded. - /// Most FunCaptcha installations work from shared domains, so this option is only needed in certain rare cases. - /// - /// mywebsite-api.funcaptcha.com - [JsonProperty("funcaptchaApiJSSubdomain")] - public string Subdomain { get; set; } + /// + /// A special subdomain of funcaptcha.com, from which the JS captcha widget should be loaded. + /// Most FunCaptcha installations work from shared domains, so this option is only needed in certain rare cases. + /// + /// mywebsite-api.funcaptcha.com + [JsonPropertyName("funcaptchaApiJSSubdomain")] + public string? Subdomain { get; set; } - /// - /// Additional parameter that may be required by FunCaptcha implementation. - /// Use this property to send "blob" value as a stringified array. See example how it may look like. - /// - /// - /// "{\"blob\":\"dyXvXANMbHj1iDyz.Qj97JtSqR2n%2BuoY1V%2FbdgbrG7p%2FmKiqdU9AwJ6MifEt0np4vfYn6TTJDJEfZDlcz9Q1XMn9przeOV%2FCr2%2FIpi%2FC1s%3D\"}" - [JsonProperty("data")] - // UNDONE: вроде как не используется, может тогда и не добавлять в request? - public string Data { get; set; } + /// + /// Additional parameter that may be required by FunCaptcha implementation. + /// Use this property to send "blob" value as a stringified array. See example how it may look like. + /// + /// + /// "{\"blob\":\"dyXvXANMbHj1iDyz.Qj97JtSqR2n%2BuoY1V%2FbdgbrG7p%2FmKiqdU9AwJ6MifEt0np4vfYn6TTJDJEfZDlcz9Q1XMn9przeOV%2FCr2%2FIpi%2FC1s%3D\"}" + [JsonPropertyName("data")] + // UNDONE: вроде как не используется, может тогда и не добавлять в request? + public string? Data { get; set; } - /// - /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. - /// https://zenno.link/doc-token-accept-en - /// - [JsonProperty("nocache", NullValueHandling = NullValueHandling.Ignore)] - public bool? NoCache { get; set; } + /// + /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. + /// https://zenno.link/doc-token-accept-en + /// + [JsonPropertyName("nocache")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NoCache { get; set; } - internal override bool UseNoCache => this.NoCache ?? false; - } + internal override bool UseNoCache => this.NoCache ?? false; } diff --git a/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs b/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs index 8e4a6fe..2e34f99 100644 --- a/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs +++ b/CapMonsterCloud.Client/Requests/FuncaptchaComplexImageTask.cs @@ -1,38 +1,37 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// ComplexImageTask recognition request for funcaptcha images +/// +public sealed class FunCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase { /// - /// ComplexImageTask recognition request for funcaptcha images + /// Metadata for recognition /// - public sealed class FunCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase - { - /// - /// Metadata for recognition - /// - public sealed class FunCaptchaMetadata - { - /// - /// Task text(in english). Required. - /// - /// - /// Pick the image that is the correct way up - /// - [Required] - [JsonProperty("Task")] - public string Task { get; set; } - } - - /// - public override string Class => "funcaptcha"; - + public sealed class FunCaptchaMetadata + { /// - /// Metadata for recognition + /// Task text(in english). Required. /// - [JsonProperty("metadata")] + /// + /// Pick the image that is the correct way up + /// [Required] - public FunCaptchaMetadata Metadata { get; set; } + [JsonPropertyName("Task")] + public string Task { get; set; } = null!; } + + /// + public override string Class => "funcaptcha"; + + /// + /// Metadata for recognition + /// + [JsonPropertyName("metadata")] + [Required] + public FunCaptchaMetadata Metadata { get; set; } = null!; } diff --git a/CapMonsterCloud.Client/Requests/GeeTestRequest.cs b/CapMonsterCloud.Client/Requests/GeeTestRequest.cs index 4303c84..72d1081 100644 --- a/CapMonsterCloud.Client/Requests/GeeTestRequest.cs +++ b/CapMonsterCloud.Client/Requests/GeeTestRequest.cs @@ -1,92 +1,91 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// GeeTest recognition request. +/// +/// +/// https://zenno.link/doc-geetest-proxy-en +/// +public sealed class GeeTestRequest : CaptchaRequestBaseWithProxy { /// - /// GeeTest recognition request. + /// Recognition task type /// - /// - /// https://zenno.link/doc-geetest-proxy-en - /// - public sealed class GeeTestRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "GeeTestTask"; + public const string TaskType = "GeeTestTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// Address of the page on which the captcha is recognized - /// - /// https://example.com/geetest.php - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Address of the page on which the captcha is recognized + /// + /// https://example.com/geetest.php + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// The GeeTest identifier key for the domain. - /// Static value, rarely updated. - /// - /// 81dc9bdb52d04dc20036dbd8313ed055 - [JsonProperty("gt", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string Gt { get; set; } + /// + /// The GeeTest identifier key for the domain. + /// Static value, rarely updated. + /// + /// 81dc9bdb52d04dc20036dbd8313ed055 + [JsonPropertyName("gt")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string Gt { get; set; } = null!; - /// - /// Version number. The default value is 3. Versions 4 is supported. - /// - /// 4 - [JsonProperty("version")] - public int? Version { get; set; } + /// + /// Version number. The default value is 3. Versions 4 is supported. + /// + /// 4 + [JsonPropertyName("version")] + public int? Version { get; set; } - /// - /// Additional initialization parameters for version 4. - /// - /// { "riskType": "slide" } - [JsonProperty("initParameters")] - public object InitParameters { get; set; } + /// + /// Additional initialization parameters for version 4. + /// + /// { "riskType": "slide" } + [JsonPropertyName("initParameters")] + public object? InitParameters { get; set; } - /// - /// A dynamic key. - /// Each time our API is called, we need to get a new key value. - /// If the captcha is loaded on the page, then the challenge value is no longer valid and you will get error. - /// IMPORTANT. You will be charged for tasks with error! - /// - /// d93591bdf7860e1e4ee2fca799911215 - /// - /// It is necessary to examine the requests and find the one in which this value is returned and, - /// before each creation of the recognition task, execute this request and parse the from it. - /// - [JsonProperty("challenge")] - public string Challenge { get; set; } + /// + /// A dynamic key. + /// Each time our API is called, we need to get a new key value. + /// If the captcha is loaded on the page, then the challenge value is no longer valid and you will get error. + /// IMPORTANT. You will be charged for tasks with error! + /// + /// d93591bdf7860e1e4ee2fca799911215 + /// + /// It is necessary to examine the requests and find the one in which this value is returned and, + /// before each creation of the recognition task, execute this request and parse the from it. + /// + [JsonPropertyName("challenge")] + public string? Challenge { get; set; } - /// - /// May be required for some sites. - /// - [JsonProperty("geetestApiServerSubdomain")] - public string Subdomain { get; set; } + /// + /// May be required for some sites. + /// + [JsonPropertyName("geetestApiServerSubdomain")] + public string? Subdomain { get; set; } - /// - /// May be required for some sites. - /// Send JSON as a string. - /// - [JsonProperty("geetestGetLib")] - public string GetLib { get; set; } + /// + /// May be required for some sites. + /// Send JSON as a string. + /// + [JsonPropertyName("geetestGetLib")] + public string? GetLib { get; set; } - /// - /// Browser's User-Agent which is used in emulation. - /// - /// - /// It is required that you use a signature of a modern browser, - /// otherwise Google will ask you to "update your browser". - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } - } + /// + /// Browser's User-Agent which is used in emulation. + /// + /// + /// It is required that you use a signature of a modern browser, + /// otherwise Google will ask you to "update your browser". + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs b/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs index e2f62eb..c21c125 100644 --- a/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs +++ b/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs @@ -1,51 +1,50 @@ -using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// ComplexImageTask recognition request for hcaptcha images +/// +public sealed class HCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase { /// - /// ComplexImageTask recognition request for hcaptcha images + /// Metadata for recognition /// - public sealed class HCaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase + public sealed class HCaptchaMetadata { /// - /// Metadata for recognition + /// Task text(in english). Required. /// - public sealed class HCaptchaMetadata - { - /// - /// Task text(in english). Required. - /// - /// - /// Please click each image containing a mountain - /// - [Required] - [JsonProperty("Task")] - public string Task { get; set; } - } + /// + /// Please click each image containing a mountain + /// + [Required] + [JsonPropertyName("Task")] + public string Task { get; set; } = null!; + } - /// - public override string Class => "hcaptcha"; + /// + public override string Class => "hcaptcha"; - /// - /// Metadata for recognition - /// - [JsonProperty("metadata")] - [Required] - public HCaptchaMetadata Metadata { get; set; } + /// + /// Metadata for recognition + /// + [JsonPropertyName("metadata")] + [Required] + public HCaptchaMetadata Metadata { get; set; } = null!; - /// - /// Collection with image urls. Number of elements depends on type of captcha. Must be populated if not. - /// - [JsonProperty("exampleImageUrls")] - public ICollection ExampleImageUrls { get; set; } + /// + /// Collection with image urls. Number of elements depends on type of captcha. Must be populated if not. + /// + [JsonPropertyName("exampleImageUrls")] + public ICollection? ExampleImageUrls { get; set; } - /// - /// Collection with base64 encoded images. Number of elements depends on type of captcha. Must be populated if not. - /// - [JsonProperty("exampleImagesBase64")] - public ICollection ExampleImagesBase64 { get; set; } - } + /// + /// Collection with base64 encoded images. Number of elements depends on type of captcha. Must be populated if not. + /// + [JsonPropertyName("exampleImagesBase64")] + public ICollection? ExampleImagesBase64 { get; set; } } diff --git a/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs b/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs index 5fcb383..2d082a1 100644 --- a/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs +++ b/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs @@ -1,96 +1,97 @@ -using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// HCaptcha recognition request. +/// +/// +/// https://zenno.link/doc-hcaptcha-proxy-en +/// +public sealed class HCaptchaRequest : CaptchaRequestBaseWithProxy { /// - /// HCaptcha recognition request. + /// Recognition task type /// - /// - /// https://zenno.link/doc-hcaptcha-proxy-en - /// - public sealed class HCaptchaRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "HCaptchaTask"; + public const string TaskType = "HCaptchaTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// Address of a webpage with hCaptcha. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Address of a webpage with hCaptcha. + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// hCaptcha website key. - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// hCaptcha website key. + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// Set true for invisible version of hCaptcha - /// - [JsonProperty("isInvisible")] - public bool Invisible { get; set; } + /// + /// Set true for invisible version of hCaptcha + /// + [JsonPropertyName("isInvisible")] + public bool Invisible { get; set; } - /// - /// Custom data that is used in some implementations of hCaptcha, mostly with =true. - /// In most cases you see it as inside network requests. - /// IMPORTANT: you MUST provide if you submit captcha with data parameter. - /// The value should match the User-Agent you use when interacting with the target website. - /// - [JsonProperty("data")] - public string Data { get; set; } + /// + /// Custom data that is used in some implementations of hCaptcha, mostly with =true. + /// In most cases you see it as inside network requests. + /// IMPORTANT: you MUST provide if you submit captcha with data parameter. + /// The value should match the User-Agent you use when interacting with the target website. + /// + [JsonPropertyName("data")] + public string? Data { get; set; } - /// - /// Obsolete, use instead. Browser's User-Agent which is used in emulation. - /// - /// - /// It is required that you use a signature of a modern browser. - /// - [Obsolete("UserAgent is deprecated, please use UserAgent from HCaptchaResponse instead.")] - [JsonProperty("userAgent")] - public string UserAgent { get; set; } + /// + /// Obsolete, use instead. Browser's User-Agent which is used in emulation. + /// + /// + /// It is required that you use a signature of a modern browser. + /// + [Obsolete("UserAgent is deprecated, please use UserAgent from HCaptchaResponse instead.")] + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } - /// - /// true - when specifying this parameter, we ignore the irrelevant User Agent - /// that users send in the request, and return our own (relevant) one with - /// getTaskResult. This will improve the acceptance of tokens. - /// - /// false - we insert the User Agent that is specified in the request. If the User - /// Agent is invalid, you will receive an error ERROR_WRONG_USERAGENT - /// (USERAGENT IS EXPIRED in the log). - /// - /// null - we insert the User Agent that is specified in the request, - /// but we don’t validate it - /// - [JsonProperty("fallbackToActualUA", NullValueHandling = NullValueHandling.Ignore)] - public bool? FallbackToActualUA { get; set; } + /// + /// true - when specifying this parameter, we ignore the irrelevant User Agent + /// that users send in the request, and return our own (relevant) one with + /// getTaskResult. This will improve the acceptance of tokens. + /// + /// false - we insert the User Agent that is specified in the request. If the User + /// Agent is invalid, you will receive an error ERROR_WRONG_USERAGENT + /// (USERAGENT IS EXPIRED in the log). + /// + /// null - we insert the User Agent that is specified in the request, + /// but we don't validate it + /// + [JsonPropertyName("fallbackToActualUA")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? FallbackToActualUA { get; set; } - /// - /// Additional cookies which we must use during interaction with target page. - /// - [JsonProperty("cookies")] - [JsonConverter(typeof(Json.DictionaryToSemicolonSplittedStringConverter))] - public IDictionary Cookies { get; set; } = new Dictionary(); + /// + /// Additional cookies which we must use during interaction with target page. + /// + [JsonPropertyName("cookies")] + [JsonConverter(typeof(Json.DictionaryToSemicolonSplittedStringConverter))] + public IDictionary Cookies { get; set; } = new Dictionary(); - /// - /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. - /// https://zenno.link/doc-token-accept-en - /// - [JsonProperty("nocache", NullValueHandling = NullValueHandling.Ignore)] - public bool? NoCache { get; set; } + /// + /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. + /// https://zenno.link/doc-token-accept-en + /// + [JsonPropertyName("nocache")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NoCache { get; set; } - internal override bool UseNoCache => this.NoCache ?? false; - } + internal override bool UseNoCache => this.NoCache ?? false; } diff --git a/CapMonsterCloud.Client/Requests/IProxyInfo.cs b/CapMonsterCloud.Client/Requests/IProxyInfo.cs index 6e940c5..3c5bb76 100644 --- a/CapMonsterCloud.Client/Requests/IProxyInfo.cs +++ b/CapMonsterCloud.Client/Requests/IProxyInfo.cs @@ -1,13 +1,12 @@ -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Interface for captcha recognition with proxy +/// +public interface IProxyInfo { /// - /// Interface for captcha recognition with proxy + /// Proxy settings /// - public interface IProxyInfo - { - /// - /// Proxy settings - /// - ProxyContainer Proxy { get; } - } + ProxyContainer? Proxy { get; } } diff --git a/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs b/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs index 9a6f0da..9dc6fca 100644 --- a/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs +++ b/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs @@ -1,69 +1,68 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// ImageToText recognition request +/// +/// +/// https://zenno.link/doc-ImageToTextTask-en +/// +public sealed class ImageToTextRequest : CaptchaRequestBase { /// - /// ImageToText recognition request + /// Recognition task type /// - /// - /// https://zenno.link/doc-ImageToTextTask-en - /// - public sealed class ImageToTextRequest : CaptchaRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "ImageToTextTask"; + public const string TaskType = "ImageToTextTask"; - /// - [JsonProperty("type")] - public override string Type => TaskType; + /// + [JsonPropertyName("type")] + public override string Type => TaskType; - /// - /// File body encoded in base64. Make sure to send it without line breaks. - /// - [JsonProperty("body", Required = Required.Always)] - public string Body { get; set; } + /// + /// File body encoded in base64. Make sure to send it without line breaks. + /// + [JsonPropertyName("body")] + public string Body { get; set; } = null!; - /// - /// Name of recognizing module. Supported module names are - /// - [JsonProperty("CapMonsterModule")] - public string CapMonsterModule { get; set; } + /// + /// Name of recognizing module. Supported module names are + /// + [JsonPropertyName("CapMonsterModule")] + public string? CapMonsterModule { get; set; } - /// - /// Captcha recognition threshold with a possible value from 0 to 100. - /// - /// - /// If was set to 90 and the task was solved with a confidence of 80, you won't be charged. - /// In this case the user will get a response . - /// - [JsonProperty("recognizingThreshold")] - [Range(0, 100)] - public byte RecognizingThreshold { get; set; } + /// + /// Captcha recognition threshold with a possible value from 0 to 100. + /// + /// + /// If was set to 90 and the task was solved with a confidence of 80, you won't be charged. + /// In this case the user will get a response . + /// + [JsonPropertyName("recognizingThreshold")] + [Range(0, 100)] + public byte RecognizingThreshold { get; set; } - /// - /// Set true if captcha is case sensitive. - /// - [JsonProperty("Case")] - public bool CaseSensitive { get; set; } + /// + /// Set true if captcha is case sensitive. + /// + [JsonPropertyName("Case")] + public bool CaseSensitive { get; set; } - /// - /// Set true if captcha contains numbers only - /// - [JsonProperty("numeric")] - [JsonConverter(typeof(Json.NumericFlagConverter))] - public bool Numeric { get; set; } + /// + /// Set true if captcha contains numbers only + /// + [JsonPropertyName("numeric")] + [JsonConverter(typeof(Json.NumericFlagConverter))] + public bool Numeric { get; set; } - /// - /// Set true if captcha requires a mathematical operation. - /// - /// - /// Captcha 2 + 6 = will return a value of 8 - /// - [JsonProperty("math")] - public bool Math { get; set; } - } + /// + /// Set true if captcha requires a mathematical operation. + /// + /// + /// Captcha 2 + 6 = will return a value of 8 + /// + [JsonPropertyName("math")] + public bool Math { get; set; } } diff --git a/CapMonsterCloud.Client/Requests/ImpervaCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/ImpervaCustomTaskRequest.cs index 22309e4..3962fa3 100644 --- a/CapMonsterCloud.Client/Requests/ImpervaCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/ImpervaCustomTaskRequest.cs @@ -1,28 +1,27 @@ -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Imperva CustomTask recognition request +/// +public class ImpervaCustomTaskRequest : CustomTaskRequestBase { + /// + public override string Class => "Imperva"; + /// - /// Imperva CustomTask recognition request + /// + /// 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 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 }; - } + public ImpervaCustomTaskRequest(string incapsulaScriptBase64, string incapsulaSessionCookie, string reese84UrlEndpoint) => Metadata = new { incapsulaScriptBase64, incapsulaSessionCookie, reese84UrlEndpoint }; } diff --git a/CapMonsterCloud.Client/Requests/MTCaptchaTaskRequest.cs b/CapMonsterCloud.Client/Requests/MTCaptchaTaskRequest.cs index 94f59a2..c159046 100644 --- a/CapMonsterCloud.Client/Requests/MTCaptchaTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/MTCaptchaTaskRequest.cs @@ -1,57 +1,56 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// MTCaptcha recognition request. +/// +/// +/// https://docs.capmonster.cloud/docs/captchas/mtcaptcha-task/ +/// +public sealed class MTCaptchaTaskRequest : CaptchaRequestBaseWithProxy { /// - /// MTCaptcha recognition request. + /// Recognition task type + /// + public const string TaskType = "MTCaptchaTask"; + + /// + [JsonPropertyName("type")] + public sealed override string Type => TaskType; + + /// + /// Address of a web page with MTCaptcha. + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; + + /// + /// The MTCaptcha key (sk/sitekey). + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; + + /// + /// true for invisible widget (has hidden confirmation field). + /// + [JsonPropertyName("isInvisible")] + public bool Invisible { get; set; } + + /// + /// Action value (passed as "act" and shown during token validation). + /// Provide only if it differs from default "%24". + /// + [JsonPropertyName("pageAction")] + public string? PageAction { get; set; } + + /// + /// Browser's User-Agent (actual Windows UA recommended). /// - /// - /// https://docs.capmonster.cloud/docs/captchas/mtcaptcha-task/ - /// - public sealed class MTCaptchaTaskRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "MTCaptchaTask"; - - /// - [JsonProperty("type", Required = Required.Always)] - public sealed override string Type => TaskType; - - /// - /// Address of a web page with MTCaptcha. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - - /// - /// The MTCaptcha key (sk/sitekey). - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } - - /// - /// true for invisible widget (has hidden confirmation field). - /// - [JsonProperty("isInvisible")] - public bool Invisible { get; set; } - - /// - /// Action value (passed as "act" and shown during token validation). - /// Provide only if it differs from default "%24". - /// - [JsonProperty("pageAction")] - public string PageAction { get; set; } - - /// - /// Browser's User-Agent (actual Windows UA recommended). - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } - } + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/ProsopoTaskRequest.cs b/CapMonsterCloud.Client/Requests/ProsopoTaskRequest.cs index 0a2cfe7..0371a55 100644 --- a/CapMonsterCloud.Client/Requests/ProsopoTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/ProsopoTaskRequest.cs @@ -1,38 +1,37 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Prosopo Procaptcha recognition request. +/// +/// +/// https://docs.capmonster.cloud/docs/captchas/prosopo-task +/// +public sealed class ProsopoTaskRequest : CaptchaRequestBaseWithProxy { /// - /// Prosopo Procaptcha recognition request. + /// Recognition task type /// - /// - /// https://docs.capmonster.cloud/docs/captchas/prosopo-task - /// - public sealed class ProsopoTaskRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "ProsopoTask"; + public const string TaskType = "ProsopoTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public sealed override string Type => TaskType; + /// + [JsonPropertyName("type")] + public sealed override string Type => TaskType; - /// - /// The full URL of the CAPTCHA page. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// The full URL of the CAPTCHA page. + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// The value of the "siteKey" parameter found on the page. - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } - } + /// + /// The value of the "siteKey" parameter found on the page. + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/ProxyType.cs b/CapMonsterCloud.Client/Requests/ProxyType.cs index ff2b0c0..7a760c8 100644 --- a/CapMonsterCloud.Client/Requests/ProxyType.cs +++ b/CapMonsterCloud.Client/Requests/ProxyType.cs @@ -1,21 +1,20 @@ -using System.Runtime.Serialization; +using System.Runtime.Serialization; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Proxy types +/// +public enum ProxyType { - /// - /// Proxy types - /// - public enum ProxyType - { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - [EnumMember(Value = "http")] - Http, - [EnumMember(Value = "https")] - Https, - [EnumMember(Value = "socks4")] - Socks4, - [EnumMember(Value = "socks5")] - Socks5 + [EnumMember(Value = "http")] + Http, + [EnumMember(Value = "https")] + Https, + [EnumMember(Value = "socks4")] + Socks4, + [EnumMember(Value = "socks5")] + Socks5 #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - } } diff --git a/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs b/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs index 3b633b3..e12b82c 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaComplexImageTask.cs @@ -1,82 +1,81 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// ComplexImageTask recognition request for recaptcha images +/// +public sealed class RecaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase { /// - /// ComplexImageTask recognition request for recaptcha images + /// Metadata for recognition /// - public sealed class RecaptchaComplexImageTaskRequest : ComplexImageTaskRequestBase + public sealed class RecaptchaMetadata { + private const string EnumPrefix = "Grid"; + /// - /// Metadata for recognition + /// Image grid. /// - public sealed class RecaptchaMetadata + public enum GridSize { - private const string EnumPrefix = "Grid"; - - /// - /// Image grid. - /// - public enum GridSize - { #pragma warning disable CS1591 - Grid3x3, - Grid4x4, - Grid1x1 + Grid3x3, + Grid4x4, + Grid1x1 #pragma warning restore CS1591 - } - - /// - [JsonIgnore] - public GridSize Grid - { - get => (GridSize)Enum.Parse(typeof(GridSize), EnumPrefix + GridString); - set => GridString = value.ToString().Replace(EnumPrefix, string.Empty); - } - - [JsonProperty("Grid")] - internal string GridString { get; set; } + } + /// + [JsonIgnore] + public GridSize Grid + { + get => (GridSize)Enum.Parse(typeof(GridSize), EnumPrefix + GridString); + set => GridString = value.ToString().Replace(EnumPrefix, string.Empty); + } - /// - /// Preferred way to identify what should we looking for on image. - /// Task text(in english). Required if is empty. - /// - /// - /// Click on traffic lights - /// - [Required] - [JsonProperty("Task")] - public string Task { get; set; } + [JsonPropertyName("Grid")] + internal string? GridString { get; set; } - /// - /// Secondary way to identify what should we looking for on image. - /// Required if is empty. - /// Useful if you can sniff traffic between page and recaptcha backend. - /// The data can be found in responses to "/recaptcha/{recaptchaApi}/reload" or "/recaptcha/{recaptchaApi}/userverify" requests, - /// where recaptchaApi is "enterprise" or "api2" depending on the Recaptcha type. - /// The response contains json, - /// in which one can take a list of TaskDefinitions for loaded captchas. - /// - /// - /// /m/015qff - /// - [Required] - [JsonProperty("TaskDefinition")] - public string TaskDefinition { get; set; } - } - /// - public override string Class => "recaptcha"; + /// + /// Preferred way to identify what should we looking for on image. + /// Task text(in english). Required if is empty. + /// + /// + /// Click on traffic lights + /// + [Required] + [JsonPropertyName("Task")] + public string? Task { get; set; } /// - /// Metadata for recognition + /// Secondary way to identify what should we looking for on image. + /// Required if is empty. + /// Useful if you can sniff traffic between page and recaptcha backend. + /// The data can be found in responses to "/recaptcha/{recaptchaApi}/reload" or "/recaptcha/{recaptchaApi}/userverify" requests, + /// where recaptchaApi is "enterprise" or "api2" depending on the Recaptcha type. + /// The response contains json, + /// in which one can take a list of TaskDefinitions for loaded captchas. /// - [JsonProperty("metadata")] + /// + /// /m/015qff + /// [Required] - public RecaptchaMetadata Metadata { get; set; } + [JsonPropertyName("TaskDefinition")] + public string? TaskDefinition { get; set; } } + + /// + public override string Class => "recaptcha"; + + /// + /// Metadata for recognition + /// + [JsonPropertyName("metadata")] + [Required] + public RecaptchaMetadata Metadata { get; set; } = null!; } diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs index 6b2e6eb..9749f5a 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs @@ -1,69 +1,69 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Recaptcha V2 Enterprise recognition request. +/// +public sealed class RecaptchaV2EnterpriseRequest : CaptchaRequestBaseWithProxy { /// - /// Recaptcha V2 Enterprise recognition request. + /// Recognition task type /// - public sealed class RecaptchaV2EnterpriseRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "RecaptchaV2EnterpriseTask"; + public const string TaskType = "RecaptchaV2EnterpriseTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// Address of a webpage with Google ReCaptcha - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// Recaptcha website key. - /// ]]> - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// Recaptcha website key. + /// ]]> + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// Additional parameters which should be passed to "grecaptcha.enterprise.render" method along with sitekey. - /// Example of what you should search for: - /// - /// In this example, you will notice a parameter "s" which is not documented, but obviously required. - /// Send it to the API, so that we render the Recaptcha widget with this parameter properly. - /// - [JsonProperty("enterprisePayload")] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string EnterprisePayload { get; set; } + /// + /// Additional parameters which should be passed to "grecaptcha.enterprise.render" method along with sitekey. + /// Example of what you should search for: + /// + /// In this example, you will notice a parameter "s" which is not documented, but obviously required. + /// Send it to the API, so that we render the Recaptcha widget with this parameter properly. + /// + [JsonPropertyName("enterprisePayload")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string? EnterprisePayload { get; set; } - /// - /// Some custom implementations may contain additional "data-s" parameter in ReCaptcha2 div, which is in fact a one-time token and must be grabbed every time you want to solve a ReCaptcha2. - /// ]]> - /// - [JsonProperty("recaptchaDataSValue")] - public string DataSValue { get; set; } + /// + /// Some custom implementations may contain additional "data-s" parameter in ReCaptcha2 div, which is in fact a one-time token and must be grabbed every time you want to solve a ReCaptcha2. + /// ]]> + /// + [JsonPropertyName("recaptchaDataSValue")] + public string? DataSValue { get; set; } - /// - /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. - /// https://zenno.link/doc-token-accept-en - /// - [JsonProperty("nocache", NullValueHandling = NullValueHandling.Ignore)] - public bool? NoCache { get; set; } + /// + /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. + /// https://zenno.link/doc-token-accept-en + /// + [JsonPropertyName("nocache")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NoCache { get; set; } - internal override bool UseNoCache => this.NoCache ?? false; - } + internal override bool UseNoCache => this.NoCache ?? false; } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs index 9db01d7..e081a67 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs @@ -1,73 +1,73 @@ -using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Recaptcha V2 recognition request. +/// +/// +/// https://zenno.link/doc-recaptcha2-proxy-en +/// +public sealed class RecaptchaV2Request : CaptchaRequestBaseWithProxy { /// - /// Recaptcha V2 recognition request. + /// Recognition task type /// - /// - /// https://zenno.link/doc-recaptcha2-proxy-en - /// - public sealed class RecaptchaV2Request : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "NoCaptchaTask"; + public const string TaskType = "NoCaptchaTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// Address of a webpage with Google ReCaptcha - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// Recaptcha website key. - /// ]]> - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// Recaptcha website key. + /// ]]> + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// Some custom implementations may contain additional "data-s" parameter in ReCaptcha2 div, which is in fact a one-time token and must be grabbed every time you want to solve a ReCaptcha2. - /// ]]> - /// - [JsonProperty("recaptchaDataSValue")] - public string DataSValue { get; set; } + /// + /// Some custom implementations may contain additional "data-s" parameter in ReCaptcha2 div, which is in fact a one-time token and must be grabbed every time you want to solve a ReCaptcha2. + /// ]]> + /// + [JsonPropertyName("recaptchaDataSValue")] + public string? DataSValue { get; set; } - /// - /// Browser's User-Agent which is used in emulation. - /// - /// - /// It is required that you use a signature of a modern browser, - /// otherwise Google will ask you to "update your browser". - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } + /// + /// Browser's User-Agent which is used in emulation. + /// + /// + /// It is required that you use a signature of a modern browser, + /// otherwise Google will ask you to "update your browser". + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } - /// - /// Additional cookies which we must use during interaction with target page or Google. - /// - [JsonProperty("cookies")] - [JsonConverter(typeof(Json.DictionaryToSemicolonSplittedStringConverter))] - public IDictionary Cookies { get; set; } = new Dictionary(); + /// + /// Additional cookies which we must use during interaction with target page or Google. + /// + [JsonPropertyName("cookies")] + [JsonConverter(typeof(Json.DictionaryToSemicolonSplittedStringConverter))] + public IDictionary Cookies { get; set; } = new Dictionary(); - /// - /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. - /// https://zenno.link/doc-token-accept-en - /// - [JsonProperty("nocache", NullValueHandling = NullValueHandling.Ignore)] - public bool? NoCache { get; set; } + /// + /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. + /// https://zenno.link/doc-token-accept-en + /// + [JsonPropertyName("nocache")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NoCache { get; set; } - internal override bool UseNoCache => this.NoCache ?? false; - } + internal override bool UseNoCache => this.NoCache ?? false; } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV3EnterpriseRequest.cs b/CapMonsterCloud.Client/Requests/RecaptchaV3EnterpriseRequest.cs new file mode 100644 index 0000000..23b7ec4 --- /dev/null +++ b/CapMonsterCloud.Client/Requests/RecaptchaV3EnterpriseRequest.cs @@ -0,0 +1,67 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Zennolab.CapMonsterCloud.Responses; + +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// reCAPTCHA v3 Enterprise recognition request. +/// The task is executed through CapMonster Cloud's own proxy servers (no user proxy needed). +/// +/// +/// https://docs.capmonster.cloud/docs/captchas/recaptcha-v3-enterprise-task/ +/// +public sealed class RecaptchaV3EnterpriseRequest : CaptchaRequestBase +{ + /// + /// Recognition task type + /// + public const string TaskType = "RecaptchaV3EnterpriseTask"; + + /// + [JsonPropertyName("type")] + public override string Type => TaskType; + + /// + /// Address of a webpage with Google reCAPTCHA Enterprise. + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; + + /// + /// The reCAPTCHA v3 Enterprise site key on the target page. + /// + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; + + /// + /// Value from 0.1 to 0.9. + /// + [JsonPropertyName("minScore")] + [Range(0.1, 0.9)] + public double MinScore { get; set; } + + /// + /// The action parameter value passed by the reCAPTCHA widget to Google, + /// which is visible to the site owner during server-side verification. + /// Default value: verify + /// + /// + /// + /// + [JsonPropertyName("pageAction")] + public string PageAction { get; set; } = "verify"; + + /// + /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. + /// https://zenno.link/doc-token-accept-en + /// + [JsonPropertyName("nocache")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NoCache { get; set; } + + internal override bool UseNoCache => this.NoCache ?? false; +} diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs b/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs index b38f42f..611ebe7 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs @@ -1,69 +1,69 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Recaptcha V3 recognition request (without proxy). +/// +/// +/// https://zenno.link/doc-recaptcha3-en +/// +public sealed class RecaptchaV3ProxylessRequest : CaptchaRequestBase { /// - /// Recaptcha V3 recognition request (without proxy). + /// Recognition task type /// - /// - /// https://zenno.link/doc-recaptcha3-en - /// - public sealed class RecaptchaV3ProxylessRequest : CaptchaRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "RecaptchaV3TaskProxyless"; + public const string TaskType = "RecaptchaV3TaskProxyless"; - /// - [JsonProperty("type")] - public override string Type => TaskType; + /// + [JsonPropertyName("type")] + public override string Type => TaskType; - /// - /// Address of a webpage with Google ReCaptcha - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// Recaptcha website key. - /// - /// ]]> - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// Recaptcha website key. + /// + /// ]]> + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// Value from 0.1 to 0.9. - /// - [JsonProperty("minScore")] - [Range(0.1, 0.9)] - public double MinScore { get; set; } + /// + /// Value from 0.1 to 0.9. + /// + [JsonPropertyName("minScore")] + [Range(0.1, 0.9)] + public double MinScore { get; set; } - /// - /// Widget action value. - /// Website owner defines what user is doing on the page through this parameter. - /// Default value: verify - /// - /// - /// - /// - [JsonProperty("pageAction")] - public string PageAction { get; set; } = "verify"; + /// + /// Widget action value. + /// Website owner defines what user is doing on the page through this parameter. + /// Default value: verify + /// + /// + /// + /// + [JsonPropertyName("pageAction")] + public string PageAction { get; set; } = "verify"; - /// - /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. - /// https://zenno.link/doc-token-accept-en - /// - [JsonProperty("nocache", NullValueHandling = NullValueHandling.Ignore)] - public bool? NoCache { get; set; } + /// + /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. + /// https://zenno.link/doc-token-accept-en + /// + [JsonPropertyName("nocache")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NoCache { get; set; } - internal override bool UseNoCache => this.NoCache ?? false; - } + internal override bool UseNoCache => this.NoCache ?? false; } diff --git a/CapMonsterCloud.Client/Requests/RecognitionComplexImageTaskRequest.cs b/CapMonsterCloud.Client/Requests/RecognitionComplexImageTaskRequest.cs index 679f946..5ee44e5 100644 --- a/CapMonsterCloud.Client/Requests/RecognitionComplexImageTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/RecognitionComplexImageTaskRequest.cs @@ -1,47 +1,46 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// ComplexImageTask recognition request for Recognition images +/// +public sealed class RecognitionComplexImageTaskRequest : ComplexImageTaskRequestBase { + /// + public override string Class => "recognition"; + /// - /// ComplexImageTask recognition request for Recognition images + /// Metadata for recognition /// - public sealed class RecognitionComplexImageTaskRequest : ComplexImageTaskRequestBase + public sealed class RecognitionMetadata { - /// - public override string Class => "recognition"; - /// - /// Metadata for recognition + /// Task definition. Required. /// - 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; } - } + /// + /// oocl_rotate_new + /// + [Required] + [JsonPropertyName("Task")] + public string Task { get; set; } = null!; /// - /// Metadata for recognition + /// Additional task argument definition. Optional. /// - [JsonProperty("metadata")] - [Required] - public RecognitionMetadata Metadata { get; set; } + /// + /// 546 + /// + [JsonPropertyName("TaskArgument")] + public string? TaskArgument { get; set; } } + + /// + /// Metadata for recognition + /// + [JsonPropertyName("metadata")] + [Required] + public RecognitionMetadata Metadata { get; set; } = null!; } diff --git a/CapMonsterCloud.Client/Requests/TemuCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/TemuCustomTaskRequest.cs index 915a121..d08828b 100644 --- a/CapMonsterCloud.Client/Requests/TemuCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/TemuCustomTaskRequest.cs @@ -1,41 +1,40 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Requests +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"; + /// - /// Temu CustomTask recognition request. + /// Initializes Temu task with required metadata. /// - /// - /// https://docs.capmonster.cloud/docs/captchas/temu-task - /// - public sealed class TemuCustomTaskRequest : CustomTaskRequestBase + /// + /// Cookies string from the page with captcha (document.cookie). + /// + public TemuCustomTaskRequest(string cookie) { - /// - 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; - } + Metadata = new { cookie }; + } - // userAgent, Proxy, Domains — уже есть в базе (CustomTaskRequestBase) + /// + /// The full URL of the page where the CAPTCHA is loaded. + /// + [JsonPropertyName("websiteURL")] + [Url] + public new string WebsiteUrl + { + get => base.WebsiteUrl; + set => base.WebsiteUrl = value; } + + // userAgent, Proxy, Domains — уже есть в базе (CustomTaskRequestBase) } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs index ef9efb5..d447c6d 100644 --- a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs @@ -1,22 +1,21 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// TenDi CustomTask recognition request +/// +public sealed class TenDiCustomTaskRequest : CustomTaskRequestBase { + /// + public override string Class => "TenDI"; + /// - /// TenDi CustomTask recognition request + /// 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. /// - 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; } - } + /// 189123456 + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; } diff --git a/CapMonsterCloud.Client/Requests/TurnstileRequest.cs b/CapMonsterCloud.Client/Requests/TurnstileRequest.cs index f24e524..b7869f2 100644 --- a/CapMonsterCloud.Client/Requests/TurnstileRequest.cs +++ b/CapMonsterCloud.Client/Requests/TurnstileRequest.cs @@ -1,101 +1,101 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Turnstile recognition request. +/// +/// +/// https://zenno.link/doc-turnstile-proxy-en +/// +public sealed class TurnstileRequest : CaptchaRequestBaseWithProxy { /// - /// Turnstile recognition request. + /// Recognition task type /// - /// - /// https://zenno.link/doc-turnstile-proxy-en - /// - public sealed class TurnstileRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "TurnstileTask"; + public const string TaskType = "TurnstileTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override string Type => TaskType; + /// + [JsonPropertyName("type")] + public override string Type => TaskType; - /// - /// Address of a webpage with Turnstile captcha - /// - /// https://tsinvisble.zlsupport.com - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Address of a webpage with Turnstile captcha + /// + /// https://tsinvisble.zlsupport.com + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// Turnstile website key. - /// - /// 0x4AAAAAAABUY0VLtOUMAHxE - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// Turnstile website key. + /// + /// 0x4AAAAAAABUY0VLtOUMAHxE + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. - /// https://zenno.link/doc-token-accept-en - /// - [JsonProperty("nocache", NullValueHandling = NullValueHandling.Ignore)] - public bool? NoCache { get; set; } + /// + /// Set true if the site only accepts a portion of the tokens from CapMonster Cloud. + /// https://zenno.link/doc-token-accept-en + /// + [JsonPropertyName("nocache")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? NoCache { get; set; } - internal override bool UseNoCache => this.NoCache ?? false; + internal override bool UseNoCache => this.NoCache ?? false; - /// - /// cf_clearance - if cookies are needed - /// token - if token is needed - /// - /// token - [JsonProperty("cloudflareTaskType")] - public string CloudflareTaskType { get; set; } + /// + /// cf_clearance - if cookies are needed + /// token - if token is needed + /// + /// token + [JsonPropertyName("cloudflareTaskType")] + public string? CloudflareTaskType { get; set; } - /// - /// Action field, which can be found in the callback function for loading captcha - /// Usually "managed" or "non-interactive" - /// - /// managed - [JsonProperty("pageAction")] - public string PageAction { get; set; } + /// + /// Action field, which can be found in the callback function for loading captcha + /// Usually "managed" or "non-interactive" + /// + /// managed + [JsonPropertyName("pageAction")] + public string? PageAction { get; set; } - /// - /// cData - /// - /// 7ea32c865ef0b936 - [JsonProperty("data")] - public string Data { get; set; } + /// + /// cData + /// + /// 7ea32c865ef0b936 + [JsonPropertyName("data")] + public string? Data { get; set; } - /// - /// chlPageData - /// - /// 3gAFo2l2MbhCQ3F...Ua3pPVFkzTnk0Mk1ERT0= - [JsonProperty("pageData")] - public string PageData { get; set; } + /// + /// chlPageData + /// + /// 3gAFo2l2MbhCQ3F...Ua3pPVFkzTnk0Mk1ERT0= + [JsonPropertyName("pageData")] + public string? PageData { get; set; } - /// - /// A base64 encoded html page with a captcha. - /// - /// PCFET0NUWVBFIGh0...vYm9keT48L2h0bWw+ - [JsonProperty("htmlPageBase64")] - public string HtmlPageBase64 { get; set; } + /// + /// A base64 encoded html page with a captcha. + /// + /// PCFET0NUWVBFIGh0...vYm9keT48L2h0bWw+ + [JsonPropertyName("htmlPageBase64")] + public string? HtmlPageBase64 { get; set; } - /// - /// Browser's User-Agent which is used in emulation. - /// - /// - /// It is required that you use a signature of a modern browser - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } + /// + /// Browser's User-Agent which is used in emulation. + /// + /// + /// It is required that you use a signature of a modern browser + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } - /// - /// The string that contains a link to the captcha script. - /// - [JsonProperty("apiJsUrl")] - public string ApiJsUrl { get; set; } - } + /// + /// The string that contains a link to the captcha script. + /// + [JsonPropertyName("apiJsUrl")] + public string? ApiJsUrl { get; set; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/YidunTaskRequest.cs b/CapMonsterCloud.Client/Requests/YidunTaskRequest.cs index 7b6030c..4bf0cc5 100644 --- a/CapMonsterCloud.Client/Requests/YidunTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/YidunTaskRequest.cs @@ -1,74 +1,73 @@ -using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Responses; -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests; + +/// +/// Yidun (NECaptcha) recognition request. +/// +/// +/// https://docs.capmonster.cloud/docs/captchas/yidun-task +/// +public sealed class YidunTaskRequest : CaptchaRequestBaseWithProxy { /// - /// Yidun (NECaptcha) recognition request. + /// Recognition task type /// - /// - /// https://docs.capmonster.cloud/docs/captchas/yidun-task - /// - public sealed class YidunTaskRequest : CaptchaRequestBaseWithProxy - { - /// - /// Recognition task type - /// - public const string TaskType = "YidunTask"; + public const string TaskType = "YidunTask"; - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; + /// + [JsonPropertyName("type")] + public override sealed string Type => TaskType; - /// - /// Full URL of the page with the captcha. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } + /// + /// Full URL of the page with the captcha. + /// + [JsonPropertyName("websiteURL")] + [Url] + public string WebsiteUrl { get; set; } = null!; - /// - /// The siteKey value found on the page. - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } + /// + /// The siteKey value found on the page. + /// + [JsonPropertyName("websiteKey")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } = null!; - /// - /// Browser User-Agent (actual Windows UA recommended). - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } + /// + /// Browser User-Agent (actual Windows UA recommended). + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } - /// - /// Full URL of JS loader (Enterprise cases). - /// - [JsonProperty("yidunGetLib")] - public string YidunGetLib { get; set; } + /// + /// Full URL of JS loader (Enterprise cases). + /// + [JsonPropertyName("yidunGetLib")] + public string? YidunGetLib { get; set; } - /// - /// Custom API server subdomain (Enterprise cases). - /// - [JsonProperty("yidunApiServerSubdomain")] - public string YidunApiServerSubdomain { get; set; } + /// + /// Custom API server subdomain (Enterprise cases). + /// + [JsonPropertyName("yidunApiServerSubdomain")] + public string? YidunApiServerSubdomain { get; set; } - /// - /// Enterprise: current captcha challenge id. - /// - [JsonProperty("challenge")] - public string Challenge { get; set; } + /// + /// Enterprise: current captcha challenge id. + /// + [JsonPropertyName("challenge")] + public string? Challenge { get; set; } - /// - /// Enterprise: captcha hash. - /// - [JsonProperty("hcg")] - public string Hcg { get; set; } + /// + /// Enterprise: captcha hash. + /// + [JsonPropertyName("hcg")] + public string? Hcg { get; set; } - /// - /// Enterprise: numeric timestamp. - /// - [JsonProperty("hct")] - public long? Hct { get; set; } - } + /// + /// Enterprise: numeric timestamp. + /// + [JsonPropertyName("hct")] + public long? Hct { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs b/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs index 00f3001..5d61967 100644 --- a/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs +++ b/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs @@ -1,27 +1,28 @@ -using Newtonsoft.Json; using System.Collections.Generic; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// AmazonWaf recognition response +/// +public sealed class AmazonWafResponse : CaptchaResponseBase { - /// - /// AmazonWaf recognition response - /// - public sealed class AmazonWafResponse : CaptchaResponseBase - { - /// - [JsonProperty("captcha_voucher")] - public string CaptchaVoucher { get; set; } + /// + [JsonPropertyName("captcha_voucher")] + public string? CaptchaVoucher { get; set; } - /// - [JsonProperty("existing_token")] - public string ExistingToken { get; set; } + /// + [JsonPropertyName("existing_token")] + public string? ExistingToken { get; set; } - /// - [JsonProperty("cookies", NullValueHandling = NullValueHandling.Ignore)] - public Dictionary Cookies { get; set; } + /// + [JsonPropertyName("cookies")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary? Cookies { get; set; } - /// - [JsonProperty("userAgent", NullValueHandling = NullValueHandling.Ignore)] - public string UserAgent { get; set; } - } + /// + [JsonPropertyName("userAgent")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? UserAgent { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs b/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs index ffeecf7..8c32ab1 100644 --- a/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs @@ -1,28 +1,29 @@ -using Newtonsoft.Json; using System.Collections.Generic; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses -{ +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// BinanceTask recognition response +/// +public sealed class BinanceTaskResponse : CaptchaResponseBase +{ /// - /// BinanceTask recognition response + /// BinanceTask token /// - public sealed class BinanceTaskResponse : CaptchaResponseBase - { - /// - /// BinanceTask token - /// - /// - /// captcha#09ba4905a79f44f2a99e44f234439644-ioVA7neog7eRHCDAsC0MixpZvt5kc99maS943qIsquNP9D77 - /// - [JsonProperty("token")] - public string Value { get; set; } + /// + /// captcha#09ba4905a79f44f2a99e44f234439644-ioVA7neog7eRHCDAsC0MixpZvt5kc99maS943qIsquNP9D77 + /// + [JsonPropertyName("token")] + public string? Value { get; set; } - /// - [JsonProperty("userAgent", NullValueHandling = NullValueHandling.Ignore)] - public string UserAgent { get; set; } + /// + [JsonPropertyName("userAgent")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? UserAgent { get; set; } - /// - [JsonProperty("cookies", NullValueHandling = NullValueHandling.Ignore)] - public Dictionary Cookies { get; set; } - } + /// + [JsonPropertyName("cookies")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary? Cookies { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs b/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs index 2b2be64..3e87aa1 100644 --- a/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs +++ b/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs @@ -1,9 +1,8 @@ -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// CaptchaResponse base class +/// +public abstract class CaptchaResponseBase { - /// - /// CaptchaResponse base class - /// - public abstract class CaptchaResponseBase - { - } } diff --git a/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs b/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs index 3c4cd32..48b47e8 100644 --- a/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs @@ -1,20 +1,19 @@ -using Newtonsoft.Json; using System.Collections.Generic; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Response for grid-like tasks +/// +public class GridComplexImageTaskResponse : CaptchaResponseBase { /// - /// Response for grid-like tasks + /// Collection with answers. Click on images with 'true' /// - public class GridComplexImageTaskResponse : CaptchaResponseBase - { - /// - /// Collection with answers. Click on images with 'true' - /// - /// - /// [false,true,false,true,false,false,true,false,false] - /// - [JsonProperty("answer")] - public ICollection Answer { get; set; } - } + /// + /// [false,true,false,true,false,false,true,false,false] + /// + [JsonPropertyName("answer")] + public ICollection? Answer { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs b/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs index a4dab7d..ed7eaad 100644 --- a/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs @@ -1,43 +1,42 @@ -using System.Collections.Generic; -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Response for custom tasks +/// +public class CustomTaskResponse : CaptchaResponseBase { - /// - /// Response for custom tasks - /// - public class CustomTaskResponse : CaptchaResponseBase + /// + public sealed class DomainInfo { /// - public sealed class DomainInfo - { - /// - [JsonProperty("cookies")] - public Dictionary Cookies { get; set; } - - /// - [JsonProperty("localStorage")] - public Dictionary LocalStorage { get; set; } - } + [JsonPropertyName("cookies")] + public Dictionary? Cookies { get; set; } /// - [JsonProperty("domains")] - public Dictionary Domains { get; set; } + [JsonPropertyName("localStorage")] + public Dictionary? LocalStorage { get; set; } + } - /// - [JsonProperty("url")] - public string Url { get; set; } + /// + [JsonPropertyName("domains")] + public Dictionary? Domains { get; set; } - /// - [JsonProperty("fingerprint")] - public Dictionary Fingerprint { get; set; } + /// + [JsonPropertyName("url")] + public string? Url { get; set; } - /// - [JsonProperty("headers")] - public Dictionary Headers { get; set; } + /// + [JsonPropertyName("fingerprint")] + public Dictionary? Fingerprint { get; set; } - /// - [JsonProperty("data")] - public Dictionary Data; - } + /// + [JsonPropertyName("headers")] + public Dictionary? Headers { get; set; } + + /// + [JsonPropertyName("data")] + public Dictionary? Data; } diff --git a/CapMonsterCloud.Client/Responses/DynamicComplexImageTaskResponse.cs b/CapMonsterCloud.Client/Responses/DynamicComplexImageTaskResponse.cs index a448d0f..df62416 100644 --- a/CapMonsterCloud.Client/Responses/DynamicComplexImageTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/DynamicComplexImageTaskResponse.cs @@ -1,39 +1,38 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Zennolab.CapMonsterCloud.Json; using static Zennolab.CapMonsterCloud.Requests.RecognitionComplexImageTaskRequest; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Response for Recognition grid-like tasks +/// +[JsonConverter(typeof(RecognitionAnswerJsonConverter))] +public class DynamicComplexImageTaskResponse : CaptchaResponseBase { /// - /// Response for Recognition grid-like tasks + /// Metadata class containing AnswerType /// - [JsonConverter(typeof(RecognitionAnswerJsonConverter))] - public class DynamicComplexImageTaskResponse : CaptchaResponseBase + public class RecognitionMetadata { - /// - /// Metadata class containing AnswerType - /// - public class RecognitionMetadata - { - [JsonProperty("AnswerType")] - public string AnswerType { get; set; } - } + [JsonPropertyName("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; } + /// + /// 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] + /// + [JsonPropertyName("answer")] + public RecognitionAnswer? Answer { get; set; } - /// - /// Metadata containing the answer type - /// - [JsonProperty("metadata")] - public RecognitionMetadata Metadata { get; set; } - } + /// + /// Metadata containing the answer type + /// + [JsonPropertyName("metadata")] + public RecognitionMetadata? Metadata { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs b/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs index b32c090..1961402 100644 --- a/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs +++ b/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs @@ -1,21 +1,20 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// FunCaptcha recognition response +/// +public class FunCaptchaResponse : CaptchaResponseBase { /// - /// FunCaptcha recognition response + /// FunCaptcha token that needs to be substituted into the form. /// - public class FunCaptchaResponse : CaptchaResponseBase - { - /// - /// FunCaptcha token that needs to be substituted into the form. - /// - /// - /// - /// - [JsonProperty("token")] - public string Value { get; set; } - } + /// + /// + /// + [JsonPropertyName("token")] + public string? Value { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/GeeTestResponse.cs b/CapMonsterCloud.Client/Responses/GeeTestResponse.cs index d7adc2d..5fe28e6 100644 --- a/CapMonsterCloud.Client/Responses/GeeTestResponse.cs +++ b/CapMonsterCloud.Client/Responses/GeeTestResponse.cs @@ -1,59 +1,65 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// GeeTest recognition response +/// +public class GeeTestResponse : CaptchaResponseBase { /// - /// GeeTest recognition response /// - [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] - public class GeeTestResponse : CaptchaResponseBase - { - /// - /// - /// 0f759dd1ea6c4wc76cedc2991039ca4f23 - [JsonProperty("challenge")] - public string Challenge { get; set; } + /// 0f759dd1ea6c4wc76cedc2991039ca4f23 + [JsonPropertyName("challenge")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Challenge { get; set; } - /// - /// - /// 6275e26419211d1f526e674d97110e15 - [JsonProperty("validate")] - public string Validate { get; set; } + /// + /// + /// 6275e26419211d1f526e674d97110e15 + [JsonPropertyName("validate")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Validate { get; set; } - /// - /// - /// 510cd9735583edcb158601067195a5eb|jordan - [JsonProperty("seccode")] - public string SecCode { get; set; } - - /// - /// - /// f5c2ad5a8a3cf37192d8b9c039950f79 - [JsonProperty("captcha_id")] - public string CaptchaId { get; set; } + /// + /// + /// 510cd9735583edcb158601067195a5eb|jordan + [JsonPropertyName("seccode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? SecCode { get; set; } + + /// + /// + /// f5c2ad5a8a3cf37192d8b9c039950f79 + [JsonPropertyName("captcha_id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? CaptchaId { get; set; } - /// - /// - /// bcb2c6ce2f8e4e9da74f2c1fa63bd713 - [JsonProperty("lot_number")] - public string LotNumber { get; set; } + /// + /// + /// bcb2c6ce2f8e4e9da74f2c1fa63bd713 + [JsonPropertyName("lot_number")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? LotNumber { get; set; } - /// - /// - /// edc7a17716535a5ae624ef4707cb6e7e478dc557608b068d202682c8297695cf - [JsonProperty("pass_token")] - public string PassToken { get; set; } - - /// - /// - /// 1683794919 - [JsonProperty("gen_time")] - public string GenTime { get; set; } - - /// - /// - /// XwmTZEJCJEnRIJBlvtEAZ662T...[cut]...SQ3fX-MyoYOVDMDXWSRQig56 - [JsonProperty("captcha_output")] - public string CaptchaOutput { get; set; } - } + /// + /// + /// edc7a17716535a5ae624ef4707cb6e7e478dc557608b068d202682c8297695cf + [JsonPropertyName("pass_token")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? PassToken { get; set; } + + /// + /// + /// 1683794919 + [JsonPropertyName("gen_time")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? GenTime { get; set; } + + /// + /// + /// XwmTZEJCJEnRIJBlvtEAZ662T...[cut]...SQ3fX-MyoYOVDMDXWSRQig56 + [JsonPropertyName("captcha_output")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? CaptchaOutput { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/HCaptchaResponse.cs b/CapMonsterCloud.Client/Responses/HCaptchaResponse.cs index 27c4eb2..5ceec83 100644 --- a/CapMonsterCloud.Client/Responses/HCaptchaResponse.cs +++ b/CapMonsterCloud.Client/Responses/HCaptchaResponse.cs @@ -1,28 +1,27 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// HCaptcha recognition response +/// +public sealed class HCaptchaResponse : RecaptchaResponseBase { /// - /// HCaptcha recognition response + /// The result of the "window.hcaptcha.getRespKey()" function when available. Some sites use this value for additional verification. /// - public sealed class HCaptchaResponse : RecaptchaResponseBase - { - /// - /// The result of the "window.hcaptcha.getRespKey()" function when available. Some sites use this value for additional verification. - /// - /// - /// E0_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidjQ3RjlqZGFYTllFQXlZZFYyRTlaWlBVQUdLaFpPakpRNjBXRTljVW40VnY3NnhuN2V3R0wwVWd1MW1Wai90WEdoYmt5a2NqVGlGdWpsSlpmVjczaTZOTTFrN0ErWnNueGNDd1BLc1lUVTBsSUw2N0k4ZVRFTXN4Z01acnU2S3hYTXdOVlBjMlNNQ2xjZUZiKytQTVRhbUlHWTUrZ2xqRTRpTkgzYUFGbE5VcVZGanhEWWNya0Mvb1Y3ZGZteEJXbmlrVGpJSVBrMTh1dmpPRnRTVHB5SFY0Y3RDTnE2T1RlL2s0bE1xZ245dFcvUjYxK1A1NE12bHk4SzAxcmFXZHpaSUZmRTNtOCtReFUvQmxDQT09UGxYaHN4aTRqMkF6ZHJNZyJ9.lPi7EmuKyKoAErvt7MqqO0Zf9XmFJBswYvQCSpUxRp8 - /// - [JsonProperty("respKey")] - public string RespKey { get; set; } + /// + /// E0_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidjQ3RjlqZGFYTllFQXlZZFYyRTlaWlBVQUdLaFpPakpRNjBXRTljVW40VnY3NnhuN2V3R0wwVWd1MW1Wai90WEdoYmt5a2NqVGlGdWpsSlpmVjczaTZOTTFrN0ErWnNueGNDd1BLc1lUVTBsSUw2N0k4ZVRFTXN4Z01acnU2S3hYTXdOVlBjMlNNQ2xjZUZiKytQTVRhbUlHWTUrZ2xqRTRpTkgzYUFGbE5VcVZGanhEWWNya0Mvb1Y3ZGZteEJXbmlrVGpJSVBrMTh1dmpPRnRTVHB5SFY0Y3RDTnE2T1RlL2s0bE1xZ245dFcvUjYxK1A1NE12bHk4SzAxcmFXZHpaSUZmRTNtOCtReFUvQmxDQT09UGxYaHN4aTRqMkF6ZHJNZyJ9.lPi7EmuKyKoAErvt7MqqO0Zf9XmFJBswYvQCSpUxRp8 + /// + [JsonPropertyName("respKey")] + public string? RespKey { get; set; } - /// - /// During submitting, you should use the same User Agent with which hCaptcha was solved. - /// - /// - /// Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 - /// - [JsonProperty("userAgent")] - public string UserAgent { get; set; } - } + /// + /// During submitting, you should use the same User Agent with which hCaptcha was solved. + /// + /// + /// Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 + /// + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs b/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs index 07495cc..3de8323 100644 --- a/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs +++ b/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs @@ -1,16 +1,15 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// ImageToText recognition response +/// +public class ImageToTextResponse : CaptchaResponseBase { /// - /// ImageToText recognition response + /// Captcha answer /// - public class ImageToTextResponse : CaptchaResponseBase - { - /// - /// Captcha answer - /// - [JsonProperty("text")] - public string Value { get; set; } - } + [JsonPropertyName("text")] + public string? Value { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/MTCaptchaTaskResponse.cs b/CapMonsterCloud.Client/Responses/MTCaptchaTaskResponse.cs index 298e743..f76a3ba 100644 --- a/CapMonsterCloud.Client/Responses/MTCaptchaTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/MTCaptchaTaskResponse.cs @@ -1,16 +1,15 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// MTCaptcha recognition response +/// +public sealed class MTCaptchaTaskResponse : CaptchaResponseBase { /// - /// MTCaptcha recognition response + /// MTCaptcha token to submit to the target site. /// - public sealed class MTCaptchaTaskResponse : CaptchaResponseBase - { - /// - /// MTCaptcha token to submit to the target site. - /// - [JsonProperty("token")] - public string Value { get; set; } - } + [JsonPropertyName("token")] + public string? Value { get; set; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Responses/ProsopoTaskResponse.cs b/CapMonsterCloud.Client/Responses/ProsopoTaskResponse.cs index 87069c9..51bdafd 100644 --- a/CapMonsterCloud.Client/Responses/ProsopoTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/ProsopoTaskResponse.cs @@ -1,19 +1,18 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Prosopo Procaptcha recognition response +/// +public sealed class ProsopoTaskResponse : CaptchaResponseBase { /// - /// Prosopo Procaptcha recognition response + /// Prosopo token /// - public sealed class ProsopoTaskResponse : CaptchaResponseBase - { - /// - /// Prosopo token - /// - /// - /// 0x00016c68747470733a2f2f70726f6e6f6465332e70726f736f706f2e696f... - /// - [JsonProperty("token")] - public string Value { get; set; } - } + /// + /// 0x00016c68747470733a2f2f70726f6e6f6465332e70726f736f706f2e696f... + /// + [JsonPropertyName("token")] + public string? Value { get; set; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs b/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs index 8bfe054..b157a0f 100644 --- a/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs +++ b/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs @@ -1,23 +1,22 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Recaptcha recognition response base +/// +public abstract class RecaptchaResponseBase : CaptchaResponseBase { /// - /// Recaptcha recognition response base + /// Hash which should be inserted into Recaptcha2 submit form in + /// + /// ]]> + /// It has a length of 500 to 2190 bytes. /// - public abstract class RecaptchaResponseBase : CaptchaResponseBase - { - /// - /// Hash which should be inserted into Recaptcha2 submit form in - /// - /// ]]> - /// It has a length of 500 to 2190 bytes. - /// - /// - /// - /// - [JsonProperty("gRecaptchaResponse")] - public string Value { get; set; } - } + /// + /// + /// + [JsonPropertyName("gRecaptchaResponse")] + public string? Value { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/RecaptchaV2EnterpriseResponse.cs b/CapMonsterCloud.Client/Responses/RecaptchaV2EnterpriseResponse.cs index a6b5ec0..86c9cbb 100644 --- a/CapMonsterCloud.Client/Responses/RecaptchaV2EnterpriseResponse.cs +++ b/CapMonsterCloud.Client/Responses/RecaptchaV2EnterpriseResponse.cs @@ -1,9 +1,8 @@ -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// RecaptchaV2 Enterprise recognition response +/// +public sealed class RecaptchaV2EnterpriseResponse : RecaptchaResponseBase { - /// - /// RecaptchaV2 Enterprise recognition response - /// - public sealed class RecaptchaV2EnterpriseResponse : RecaptchaResponseBase - { - } } diff --git a/CapMonsterCloud.Client/Responses/RecaptchaV2Response.cs b/CapMonsterCloud.Client/Responses/RecaptchaV2Response.cs index b4295fd..56b556e 100644 --- a/CapMonsterCloud.Client/Responses/RecaptchaV2Response.cs +++ b/CapMonsterCloud.Client/Responses/RecaptchaV2Response.cs @@ -1,9 +1,8 @@ -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// RecaptchaV2 recognition response +/// +public sealed class RecaptchaV2Response : RecaptchaResponseBase { - /// - /// RecaptchaV2 recognition response - /// - public sealed class RecaptchaV2Response : RecaptchaResponseBase - { - } } diff --git a/CapMonsterCloud.Client/Responses/RecaptchaV3EnterpriseResponse.cs b/CapMonsterCloud.Client/Responses/RecaptchaV3EnterpriseResponse.cs new file mode 100644 index 0000000..e9a010f --- /dev/null +++ b/CapMonsterCloud.Client/Responses/RecaptchaV3EnterpriseResponse.cs @@ -0,0 +1,8 @@ +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// reCAPTCHA v3 Enterprise recognition response +/// +public sealed class RecaptchaV3EnterpriseResponse : RecaptchaResponseBase +{ +} diff --git a/CapMonsterCloud.Client/Responses/RecaptchaV3Response.cs b/CapMonsterCloud.Client/Responses/RecaptchaV3Response.cs index 4ac05f8..b76162e 100644 --- a/CapMonsterCloud.Client/Responses/RecaptchaV3Response.cs +++ b/CapMonsterCloud.Client/Responses/RecaptchaV3Response.cs @@ -1,9 +1,8 @@ -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// RecaptchaV3 recognition response +/// +public sealed class RecaptchaV3Response : RecaptchaResponseBase { - /// - /// RecaptchaV3 recognition response - /// - public sealed class RecaptchaV3Response : RecaptchaResponseBase - { - } } diff --git a/CapMonsterCloud.Client/Responses/RecognitionAnswer.cs b/CapMonsterCloud.Client/Responses/RecognitionAnswer.cs index 13fb70d..3681683 100644 --- a/CapMonsterCloud.Client/Responses/RecognitionAnswer.cs +++ b/CapMonsterCloud.Client/Responses/RecognitionAnswer.cs @@ -1,24 +1,23 @@ -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Recognition captcha answer +/// +public class RecognitionAnswer { /// - /// Recognition captcha answer + /// Decimal answer /// - public class RecognitionAnswer - { - /// - /// Decimal answer - /// - public decimal[] NumericAnswer { get; set; } + public decimal[]? NumericAnswer { get; set; } - /// - /// Bool answer - /// - public bool[] GridAnswer { get; set; } + /// + /// Bool answer + /// + public bool[]? GridAnswer { get; set; } - /// - public bool IsNumeric => NumericAnswer != null; + /// + public bool IsNumeric => NumericAnswer is not null; - /// - public bool IsGrid => GridAnswer != null; - } + /// + public bool IsGrid => GridAnswer is not null; } diff --git a/CapMonsterCloud.Client/Responses/TurnstileResponse.cs b/CapMonsterCloud.Client/Responses/TurnstileResponse.cs index ceae626..e44932d 100644 --- a/CapMonsterCloud.Client/Responses/TurnstileResponse.cs +++ b/CapMonsterCloud.Client/Responses/TurnstileResponse.cs @@ -1,25 +1,24 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Turnstile recognition response +/// +public sealed class TurnstileResponse : CaptchaResponseBase { /// - /// Turnstile recognition response + /// Turnstile token + /// + /// + /// 0.WqM7dkt15_d0Tgzhhsq21HtccdYp__spC7TehwqQ2ZoxhRVsFgnURYP5kyXb3njzY7jPqPGBoCewzPCmtmx_NygYniItwxy-On2ibrp1duuyUG3aH3l5WLvWeLHduY0gbalmJku57_nCoV-rS5qLNmEcqOlvLH60iUcjVfPhWBK2fO_ivCtnYt-S_FscDyDvt8dRwUs2D71aHRjF9Obzy8DmgyCNEsPWEbJrxM1FNQhwKfsgE2hp_HgudxAuomZzUtBVAi9aQXuDBUweh730iEHteOPCdkaroc6f9i5_tcJNXywGmNr8nQ-id57sakvqPOvjvLK-iIpBxRcK7MX-xJ9bFj1fG-m0q8DbWlLJf4Q.e8FaqGtmWuFkDOrmP03Ftg.763f08299cca9038d18afe499f6cb5427aa5f2d7e34f5cd585d34a0062158083 + /// + [JsonPropertyName("token")] + public string? Value { get; set; } + + /// + /// Special cloudflare cookies that can be set in the browser /// - public sealed class TurnstileResponse : CaptchaResponseBase - { - /// - /// Turnstile token - /// - /// - /// 0.WqM7dkt15_d0Tgzhhsq21HtccdYp__spC7TehwqQ2ZoxhRVsFgnURYP5kyXb3njzY7jPqPGBoCewzPCmtmx_NygYniItwxy-On2ibrp1duuyUG3aH3l5WLvWeLHduY0gbalmJku57_nCoV-rS5qLNmEcqOlvLH60iUcjVfPhWBK2fO_ivCtnYt-S_FscDyDvt8dRwUs2D71aHRjF9Obzy8DmgyCNEsPWEbJrxM1FNQhwKfsgE2hp_HgudxAuomZzUtBVAi9aQXuDBUweh730iEHteOPCdkaroc6f9i5_tcJNXywGmNr8nQ-id57sakvqPOvjvLK-iIpBxRcK7MX-xJ9bFj1fG-m0q8DbWlLJf4Q.e8FaqGtmWuFkDOrmP03Ftg.763f08299cca9038d18afe499f6cb5427aa5f2d7e34f5cd585d34a0062158083 - /// - [JsonProperty("token")] - public string Value { get; set; } - - /// - /// Special cloudflare cookies that can be set in the browser - /// - [JsonProperty("cf_clearance")] - public string Clearance { get; set; } - } + [JsonPropertyName("cf_clearance")] + public string? Clearance { get; set; } } diff --git a/CapMonsterCloud.Client/Responses/YidunTaskResponse.cs b/CapMonsterCloud.Client/Responses/YidunTaskResponse.cs index 456cb67..b876995 100644 --- a/CapMonsterCloud.Client/Responses/YidunTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/YidunTaskResponse.cs @@ -1,16 +1,15 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; -namespace Zennolab.CapMonsterCloud.Responses +namespace Zennolab.CapMonsterCloud.Responses; + +/// +/// Yidun (NECaptcha) recognition response +/// +public sealed class YidunTaskResponse : CaptchaResponseBase { /// - /// Yidun (NECaptcha) recognition response + /// Yidun token to submit. /// - public sealed class YidunTaskResponse : CaptchaResponseBase - { - /// - /// Yidun token to submit. - /// - [JsonProperty("token")] - public string Value { get; set; } - } + [JsonPropertyName("token")] + public string? Value { get; set; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/ServiceCollectionExtensions.cs b/CapMonsterCloud.Client/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..a746ccf --- /dev/null +++ b/CapMonsterCloud.Client/ServiceCollectionExtensions.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; + +namespace Zennolab.CapMonsterCloud; + +/// +/// Extension methods for registering CapMonsterCloud services with dependency injection. +/// +public static class ServiceCollectionExtensions +{ + private static readonly TimeSpan HttpTimeout = TimeSpan.FromSeconds(21); + + /// + /// Registers with the DI container using IHttpClientFactory. + /// + /// The service collection. + /// Action to configure . + /// An for further HTTP client configuration. + public static IHttpClientBuilder AddCapMonsterCloud( + this IServiceCollection services, + Action configure) + { + var options = new ClientOptions(); + configure(options); + services.AddSingleton(options); + + return services.AddHttpClient(client => + { + client.BaseAddress = options.ServiceUri; + client.Timeout = HttpTimeout; + var asm = typeof(CapMonsterCloudClient).Assembly.GetName(); + client.DefaultRequestHeaders.UserAgent.TryParseAdd($"{asm.Name}/{asm.Version}"); + }); + } +} diff --git a/CapMonsterCloud.Client/Validation/ProxyValidator.cs b/CapMonsterCloud.Client/Validation/ProxyValidator.cs index bb072df..9361b62 100644 --- a/CapMonsterCloud.Client/Validation/ProxyValidator.cs +++ b/CapMonsterCloud.Client/Validation/ProxyValidator.cs @@ -1,69 +1,64 @@ -using System; +using System; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; -namespace Zennolab.CapMonsterCloud.Validation +namespace Zennolab.CapMonsterCloud.Validation; + +internal class ProxyValidator { - internal class ProxyValidator + private static readonly Regex HostnameRegex = new(@"^(?!\d+(\.\d+){3}$)([a-zA-Z0-9.-]+)$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + + internal static bool IsValidProxy(string? proxy) { - private static readonly Regex HostnameRegex = new Regex(@"^(?!\d+(\.\d+){3}$)([a-zA-Z0-9.-]+)$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + if (string.IsNullOrWhiteSpace(proxy)) + return false; - internal static bool IsValidProxy(string proxy) + if (IPAddress.TryParse(proxy, out var ipAddress)) { - if (string.IsNullOrWhiteSpace(proxy)) - return false; - - if (IPAddress.TryParse(proxy, out IPAddress ipAddress)) - { - return IsPublicIPAddress(ipAddress); - } - - return IsValidHostname(proxy); + return IsPublicIPAddress(ipAddress); } - private static bool IsValidHostname(string hostname) - { - if (hostname.Equals("localhost", StringComparison.OrdinalIgnoreCase)) - return false; + return IsValidHostname(proxy); + } - // Ensure it follows domain name rules - return hostname.Length <= 253 && HostnameRegex.IsMatch(hostname); - } + private static bool IsValidHostname(string hostname) + { + if (hostname.Equals("localhost", StringComparison.OrdinalIgnoreCase)) + return false; - private static bool IsPublicIPAddress(IPAddress ip) - { - if (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6) - { - byte[] bytes = ip.GetAddressBytes(); + return hostname.Length <= 253 && HostnameRegex.IsMatch(hostname); + } - // Private IPv4 ranges - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - if (bytes[0] == 10 || - (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) || - (bytes[0] == 192 && bytes[1] == 168)) - { - return false; - } - } + private static bool IsPublicIPAddress(IPAddress ip) + { + if (ip.AddressFamily is AddressFamily.InterNetwork or AddressFamily.InterNetworkV6) + { + var bytes = ip.GetAddressBytes(); - // IPv6 private/local addresses - if (ip.IsIPv6LinkLocal || ip.IsIPv6SiteLocal) + if (ip.AddressFamily == AddressFamily.InterNetwork) + { + if (bytes[0] == 10 || + (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) || + (bytes[0] == 192 && bytes[1] == 168)) { return false; } + } - // Loopback and unspecified addresses - if (IPAddress.IsLoopback(ip) || ip.Equals(IPAddress.Any) || ip.Equals(IPAddress.IPv6Any)) - { - return false; - } + if (ip.IsIPv6LinkLocal || ip.IsIPv6SiteLocal) + { + return false; + } - return true; + if (IPAddress.IsLoopback(ip) || ip.Equals(IPAddress.Any) || ip.Equals(IPAddress.IPv6Any)) + { + return false; } - return false; + return true; } + + return false; } } diff --git a/CapMonsterCloud.Client/Validation/TaskValidator.cs b/CapMonsterCloud.Client/Validation/TaskValidator.cs index 5e8b9fe..381ef94 100644 --- a/CapMonsterCloud.Client/Validation/TaskValidator.cs +++ b/CapMonsterCloud.Client/Validation/TaskValidator.cs @@ -1,47 +1,46 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; -namespace Zennolab.CapMonsterCloud.Validation +namespace Zennolab.CapMonsterCloud.Validation; + +internal static class TaskValidator { - internal static class TaskValidator + internal static void ValidateObjectIncludingInternals(object obj) { - internal static void ValidateObjectIncludingInternals(object obj) - { - var results = new List(); + var results = new List(); - var properties = obj.GetType() - .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(p => p.GetCustomAttributes(typeof(ValidationAttribute), true).Any()) - .ToList(); + var properties = obj.GetType() + .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(p => p.GetCustomAttributes(typeof(ValidationAttribute), true).Any()) + .ToList(); - foreach (var property in properties) - { - var value = property.GetValue(obj); - var attributes = property.GetCustomAttributes(typeof(ValidationAttribute), true) - .Cast(); + foreach (var property in properties) + { + var value = property.GetValue(obj); + var attributes = property.GetCustomAttributes(typeof(ValidationAttribute), true) + .Cast(); - foreach (var attribute in attributes) + foreach (var attribute in attributes) + { + var context = new ValidationContext(obj) { - var context = new ValidationContext(obj) - { - MemberName = property.Name - }; + MemberName = property.Name + }; - var result = attribute.GetValidationResult(value, context); - if (result != ValidationResult.Success) - { - results.Add(result); - } + var result = attribute.GetValidationResult(value, context); + if (result is not null && result != ValidationResult.Success) + { + results.Add(result); } } + } - if (results.Any()) - { - throw new AggregateException(results.Select(r => new ValidationException(r.ErrorMessage))); - } + if (results.Count > 0) + { + throw new AggregateException(results.Select(r => new ValidationException(r.ErrorMessage))); } } }