diff --git a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs index c5d43e1..d6f6fbb 100644 --- a/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs +++ b/CapMonsterCloud.Client.IntegrationTests/IntegrationTests.cs @@ -175,98 +175,6 @@ public async Task RecaptchaV2Enterprise_ShouldSolve() sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); actual.Should().BeEquivalentTo(expectedResult); } - - [Test] - public async Task RecaptchaV2Proxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.RecaptchaV2.CreateProxylessTask(); - var expectedResult = ObjectGen.RecaptchaV2.CreateSolution(); - - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.CreateTask, - ExpectedRequest: JsonConvert.SerializeObject(new - { clientKey = clientKey, task = captchaRequest, softId = 53 }) - ), - ( - Type: RequestType.GetTaskResult, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) - ), - }; - - var captchaResults = new List - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new - { - 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 RecaptchaV2EnterpriseProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.RecaptchaV2Enterprise.CreateProxylessTask(); - var expectedResult = ObjectGen.RecaptchaV2Enterprise.CreateSolution(); - - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.CreateTask, - ExpectedRequest: JsonConvert.SerializeObject(new - { clientKey = clientKey, task = captchaRequest, softId = 53 }) - ), - ( - Type: RequestType.GetTaskResult, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) - ), - }; - - var captchaResults = new List - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new - { - 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() @@ -360,52 +268,6 @@ public async Task FunCaptcha_ShouldSolve() actual.Should().BeEquivalentTo(expectedResult); } - [Test] - public async Task FunCaptchaProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.FunCaptcha.CreateProxylessTask(); - var expectedResult = ObjectGen.FunCaptcha.CreateSolution(); - - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.CreateTask, - ExpectedRequest: JsonConvert.SerializeObject(new - { clientKey = clientKey, task = captchaRequest, softId = 53 }) - ), - ( - Type: RequestType.GetTaskResult, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) - ), - }; - - var captchaResults = new List - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new - { - 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() { @@ -453,54 +315,6 @@ public async Task HCaptcha_ShouldSolve() sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); actual.Should().BeEquivalentTo(expectedResult); } - - [Test] - public async Task HCaptchaProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.HCaptcha.CreateHCaptchaProxylessTask(); - var expectedResult = ObjectGen.HCaptcha.CreateSolution(); - - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.CreateTask, - ExpectedRequest: JsonConvert.SerializeObject(new - { clientKey = clientKey, task = captchaRequest, softId = 53 }) - ), - ( - Type: RequestType.GetTaskResult, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) - ), - }; - - var captchaResults = new List - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new - { - 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() @@ -554,68 +368,15 @@ public async Task GeeTest_ShouldSolve() sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); actual.Should().BeEquivalentTo(expectedResult); } - - [Test] - public async Task GeeTestProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.GeeTest.CreateProxylessTask(); - 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 - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - 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 TurnstileProxyless_ShouldSolve() + public async Task Turnstile_ShouldSolve() { var clientKey = Gen.RandomString(); var taskId = Gen.RandomInt(); - var captchaRequest = ObjectGen.TurnstileProxyless.CreateTask(); - var expectedResult = ObjectGen.TurnstileProxyless.CreateSolution(); + var captchaRequest = ObjectGen.Turnstile.CreateTask(); + var expectedResult = ObjectGen.Turnstile.CreateSolution(); var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> { @@ -704,55 +465,6 @@ public async Task DataDome_ShouldSolve() actual.Should().BeEquivalentTo(expectedResult); } - [Test] - public async Task DataDomeProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.CustomTask.CreateDataDomeProxylessTask(); - var expectedResult = ObjectGen.CustomTask.CreateDataDomeSolution(); - - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.CreateTask, - ExpectedRequest: JsonConvert.SerializeObject(new - { clientKey = clientKey, task = captchaRequest, softId = 53 }) - ), - ( - Type: RequestType.GetTaskResult, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) - ), - }; - - var captchaResults = new List - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new - { - domains = expectedResult.Solution.Domains, - 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 RecaptchaComplexImageTask_ShouldSolve() { @@ -924,19 +636,13 @@ public async Task RecaptchaV2_IncorrectWebsiteKey_ShouldThrowValidationException } [Test] - public async Task RecaptchaV2_IncorrectProxyPort_ShouldThrowValidationException() + public async Task RecaptchaV2_IncorrectProxyPort_ShouldThrowArgumentException() { - var clientKey = Gen.RandomString(); - var captchaRequest = ObjectGen.RecaptchaV2.CreateTask( + Action actual = () => ObjectGen.RecaptchaV2.CreateTask( proxyPort: Gen.RandomInt(65535)); - - var sut = new Sut(clientKey); - sut.SetupHttpServer(new List()); - Func actual = () => sut.SolveAsync(captchaRequest); - - _ = await actual.Should().ThrowAsync() - .WithMessage("*The field ProxyPort must be between 0 and 65535*"); + _ = actual.Should().Throw() + .WithMessage("*Proxy port must be between 0 and 65535*"); } [Test] @@ -1036,55 +742,6 @@ public async Task AmazonWaf_ShouldSolve() actual.Should().BeEquivalentTo(expectedResult); } - [Test] - public async Task AmazonWafProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.AmazonWafProxyless.CreateTask(); - var expectedResult = ObjectGen.AmazonWafProxyless.CreateSolution(); - - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.CreateTask, - ExpectedRequest: JsonConvert.SerializeObject(new - { clientKey = clientKey, task = captchaRequest, softId = 53 }) - ), - ( - Type: RequestType.GetTaskResult, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) - ), - }; - - var captchaResults = new List - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new - { - 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() { @@ -1132,53 +789,6 @@ public async Task TenDi_ShouldSolve() actual.Should().BeEquivalentTo(expectedResult); } - [Test] - public async Task TenDiProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.CustomTask.CreateTenDiProxylessTask(); - var expectedResult = ObjectGen.CustomTask.CreateTenDiSolution(); - - 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 - { - 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() { @@ -1226,53 +836,6 @@ public async Task Basilisk_ShouldSolve() actual.Should().BeEquivalentTo(expectedResult); } - [Test] - public async Task BasiliskProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.CustomTask.CreateBasiliskProxylessTask(); - var expectedResult = ObjectGen.CustomTask.CreateBasiliskSolution(); - - 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 - { - 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() { @@ -1320,53 +883,5 @@ public async Task Binance_ShouldSolve() sut.GetActualRequests().Should().BeEquivalentTo(expectedRequests); actual.Should().BeEquivalentTo(expectedResult); } - - [Test] - public async Task BinanceProxyless_ShouldSolve() - { - var clientKey = Gen.RandomString(); - var taskId = Gen.RandomInt(); - - var captchaRequest = ObjectGen.BinanceTaskProxyless.CreateTask(); - var expectedResult = ObjectGen.BinanceTaskProxyless.CreateSolution(); - - var expectedRequests = new List<(RequestType Type, string ExpectedRequest)> - { - ( - Type: RequestType.CreateTask, - ExpectedRequest: JsonConvert.SerializeObject(new - { clientKey = clientKey, task = captchaRequest, softId = 53 }) - ), - ( - Type: RequestType.GetTaskResult, - ExpectedRequest: JsonConvert.SerializeObject(new { clientKey = clientKey, taskId = taskId }) - ), - }; - - var captchaResults = new List - { - new { taskId = taskId, errorId = 0, errorCode = (string)null! }, - new - { - status = "ready", - solution = new - { - 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); - } } } \ No newline at end of file diff --git a/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs b/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs index 73551c0..2873173 100644 --- a/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs +++ b/CapMonsterCloud.Client.IntegrationTests/ObjectGen.cs @@ -52,24 +52,7 @@ public static RecaptchaV2Request CreateTask( UserAgent = Gen.UserAgent(), Cookies = Gen.ListOfValues(Gen.RandomString).ToDictionary(_ => Gen.RandomString(), value => value), NoCache = Gen.RandomBool(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = proxyPort ?? Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static RecaptchaV2ProxylessRequest CreateProxylessTask() - { - return new RecaptchaV2ProxylessRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - 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()) }; } @@ -125,23 +108,7 @@ public static FunCaptchaRequest CreateTask() Subdomain = Gen.RandomString(), Data = Gen.RandomString(), NoCache = Gen.RandomBool(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static FunCaptchaProxylessRequest CreateProxylessTask() - { - return new FunCaptchaProxylessRequest - { - 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()) }; } @@ -174,28 +141,7 @@ public static HCaptchaRequest CreateHCaptchaTask() FallbackToActualUA = Gen.RandomBool(), Cookies = Gen.ListOfValues(Gen.RandomString).ToDictionary(_ => Gen.RandomString(), value => value), NoCache = Gen.RandomBool(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static HCaptchaProxylessRequest CreateHCaptchaProxylessTask() - { - return new HCaptchaProxylessRequest - { - WebsiteUrl = Gen.RandomUri().ToString(), - WebsiteKey = Gen.RandomGuid(), - Invisible = Gen.RandomBool(), - Data = Gen.RandomString(), -#pragma warning disable CS0618 - 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()) }; } @@ -229,26 +175,7 @@ public static GeeTestRequest CreateTask( Subdomain = Gen.RandomString(), GetLib = Gen.RandomString(), UserAgent = Gen.UserAgent(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static GeeTestProxylessRequest CreateProxylessTask() - { - return new GeeTestProxylessRequest() - { - WebsiteUrl = Gen.RandomUri().ToString(), - 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()) }; } @@ -282,22 +209,7 @@ public static RecaptchaV2EnterpriseRequest CreateTask() WebsiteKey = Gen.RandomString(), EnterprisePayload = Gen.RandomString(), DataSValue = Gen.RandomString(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static RecaptchaV2EnterpriseProxylessRequest CreateProxylessTask() - { - return new RecaptchaV2EnterpriseProxylessRequest() - { - 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()) }; } @@ -314,11 +226,11 @@ public static CaptchaResult CreateSolution() } } - public static class TurnstileProxyless + public static class Turnstile { - public static TurnstileProxylessRequest CreateTask() + public static TurnstileRequest CreateTask() { - return new TurnstileProxylessRequest + return new TurnstileRequest { WebsiteUrl = Gen.RandomUri().ToString(), WebsiteKey = Gen.RandomGuid(), @@ -329,7 +241,8 @@ public static TurnstileProxylessRequest CreateTask() HtmlPageBase64 = Gen.RandomString(), UserAgent = Gen.UserAgent(), ApiJsUrl = Gen.RandomUri().ToString(), - NoCache = Gen.RandomBool() + NoCache = Gen.RandomBool(), + Proxy = new ProxyContainer(Gen.RandomString(), Gen.RandomInt(0, 65535), Gen.RandomEnum(), Gen.RandomString(), Gen.RandomString()) }; } @@ -420,21 +333,7 @@ public static DataDomeCustomTaskRequest CreateDataDomeTask() WebsiteUrl = Gen.RandomUri().ToString(), UserAgent = Gen.UserAgent(), Domains = Gen.ListOfValues(Gen.RandomString), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static DataDomeCustomTaskProxylessRequest CreateDataDomeProxylessTask() - { - return new DataDomeCustomTaskProxylessRequest(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()) }; } @@ -470,21 +369,7 @@ public static TenDiCustomTaskRequest CreateTenDiTask() WebsiteKey = Gen.RandomGuid(), WebsiteUrl = Gen.RandomUri().ToString(), UserAgent = Gen.UserAgent(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static TenDiCustomTaskProxylessRequest CreateTenDiProxylessTask() - { - return new TenDiCustomTaskProxylessRequest - { - 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()) }; } @@ -508,21 +393,7 @@ public static BasiliskCustomTaskRequest CreateBasiliskTask() WebsiteKey = Gen.RandomGuid(), WebsiteUrl = Gen.RandomUri().ToString(), UserAgent = Gen.UserAgent(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static BasiliskCustomTaskProxylessRequest CreateBasiliskProxylessTask() - { - return new BasiliskCustomTaskProxylessRequest() - { - 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()) }; } @@ -553,43 +424,7 @@ public static AmazonWafRequest CreateTask() Context = Gen.RandomString(), Iv = Gen.RandomString(), CookieSolution = Gen.RandomBool(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static CaptchaResult CreateSolution() - { - return new CaptchaResult - { - Error = null, - Solution = new AmazonWafResponse - { - CaptchaVoucher = Gen.RandomString(), - ExistingToken = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }; - } - } - - public static class AmazonWafProxyless - { - public static AmazonWafProxylessRequest CreateTask() - { - return new AmazonWafProxylessRequest() - { - 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()) }; } @@ -619,39 +454,7 @@ public static BinanceTaskRequest CreateTask() WebsiteKey = Gen.RandomGuid(), ValidateId = Gen.RandomString(), UserAgent = Gen.UserAgent(), - ProxyType = Gen.RandomEnum(), - ProxyAddress = Gen.RandomString(), - ProxyPort = Gen.RandomInt(0, 65535), - ProxyLogin = Gen.RandomString(), - ProxyPassword = Gen.RandomString() - }; - } - - public static CaptchaResult CreateSolution() - { - return new CaptchaResult - { - Error = null, - Solution = new BinanceTaskResponse - { - Value = Gen.RandomString(), - UserAgent = Gen.UserAgent(), - Cookies = new Dictionary { { Gen.RandomString(), Gen.RandomString() } } - } - }; - } - } - - public static class BinanceTaskProxyless - { - public static BinanceTaskProxylessRequest CreateTask() - { - return new BinanceTaskProxylessRequest() - { - 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()) }; } diff --git a/CapMonsterCloud.Client.IntegrationTests/Sut.cs b/CapMonsterCloud.Client.IntegrationTests/Sut.cs index ee6a0eb..22b48c1 100644 --- a/CapMonsterCloud.Client.IntegrationTests/Sut.cs +++ b/CapMonsterCloud.Client.IntegrationTests/Sut.cs @@ -1,13 +1,13 @@ +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.Threading; using System.Threading.Tasks; -using Moq; -using Moq.Contrib.HttpClient; -using Moq.Protected; -using Newtonsoft.Json; using Zennolab.CapMonsterCloud; using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Responses; @@ -49,9 +49,6 @@ public async Task> SolveAsync ( public async Task> SolveAsync ( RecaptchaV2Request request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - RecaptchaV2ProxylessRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync ( RecaptchaV3ProxylessRequest request) => await _cloudClient.SolveAsync(request); @@ -59,71 +56,41 @@ public async Task> SolveAsync ( public async Task> SolveAsync ( FunCaptchaRequest request) => await _cloudClient.SolveAsync(request); - public async Task> SolveAsync ( - FunCaptchaProxylessRequest request) => await _cloudClient.SolveAsync(request); - public async Task> SolveAsync ( - HCaptchaRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - HCaptchaProxylessRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - GeeTestRequest request) => await _cloudClient.SolveAsync(request); + HCaptchaRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync ( - GeeTestProxylessRequest request) => await _cloudClient.SolveAsync(request); + GeeTestRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync ( - RecaptchaV2EnterpriseRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - RecaptchaV2EnterpriseProxylessRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync ( - TurnstileRequest request) => await _cloudClient.SolveAsync(request); + RecaptchaV2EnterpriseRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync ( - TurnstileProxylessRequest request) => await _cloudClient.SolveAsync(request); + TurnstileRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync ( - RecaptchaComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); + 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); + HCaptchaComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); - public async Task> SolveAsync( - DataDomeCustomTaskRequest request) => await _cloudClient.SolveAsync(request); + public async Task> SolveAsync ( + FunCaptchaComplexImageTaskRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync( - DataDomeCustomTaskProxylessRequest request) => await _cloudClient.SolveAsync(request); + DataDomeCustomTaskRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync( - AmazonWafProxylessRequest 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); + AmazonWafRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync( - TenDiCustomTaskProxylessRequest request) => await _cloudClient.SolveAsync(request); + TenDiCustomTaskRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync( - BasiliskCustomTaskRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - BasiliskCustomTaskProxylessRequest request) => await _cloudClient.SolveAsync(request); - - public async Task> SolveAsync( - BinanceTaskRequest request) => await _cloudClient.SolveAsync(request); + BasiliskCustomTaskRequest request) => await _cloudClient.SolveAsync(request); public async Task> SolveAsync( - BinanceTaskProxylessRequest request) => await _cloudClient.SolveAsync(request); + BinanceTaskRequest request) => await _cloudClient.SolveAsync(request); public async Task GetBalanceAsync() { diff --git a/CapMonsterCloud.Client.Tests/JsonConverterTests.cs b/CapMonsterCloud.Client.Tests/JsonConverterTests.cs index b5f3d62..a8a6823 100644 --- a/CapMonsterCloud.Client.Tests/JsonConverterTests.cs +++ b/CapMonsterCloud.Client.Tests/JsonConverterTests.cs @@ -36,7 +36,7 @@ public void NumericFlagConverter_ShouldDeserialize([Values] bool numeric) public void DictionaryToSemicolonSplittedStringConverter_ShouldDeserialize() { // Arrange - var request = new HCaptchaProxylessRequest + var request = new HCaptchaRequest() { WebsiteUrl = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", WebsiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", @@ -52,7 +52,7 @@ public void DictionaryToSemicolonSplittedStringConverter_ShouldDeserialize() var target = JsonConvert.SerializeObject(request); // Act - var actual = JsonConvert.DeserializeObject(target); + var actual = JsonConvert.DeserializeObject(target); // Assert _ = actual!.Cookies.Should().ContainKeys(request.Cookies.Keys); @@ -66,7 +66,7 @@ public void DictionaryToSemicolonSplittedStringConverter_ShouldThrowJsonReaderEx var target = JsonConvert.SerializeObject( new { - type = "HCaptchaTaskProxyless", + type = "HCaptchaTask", websiteURL = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", websiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", isInvisible = false, @@ -76,7 +76,7 @@ public void DictionaryToSemicolonSplittedStringConverter_ShouldThrowJsonReaderEx }); // Act - Func act = () => JsonConvert.DeserializeObject(target)!; + Func act = () => JsonConvert.DeserializeObject(target)!; // Assert _ = act.Should().Throw(); diff --git a/CapMonsterCloud.Client.Tests/ProxyValidatorTests.cs b/CapMonsterCloud.Client.Tests/ProxyValidatorTests.cs new file mode 100644 index 0000000..a54a1ff --- /dev/null +++ b/CapMonsterCloud.Client.Tests/ProxyValidatorTests.cs @@ -0,0 +1,26 @@ +using FluentAssertions; +using NUnit.Framework; +using Zennolab.CapMonsterCloud.Validation; + +namespace Zennolab.CapMonsterCloud.Client.Tests +{ + 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) + { + // Act + var actual = ProxyValidator.IsValidProxy(address); + + // Assert + actual.Should().Be(expectedResult); + } + } +} diff --git a/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs b/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs index 322f5bf..9fd2190 100644 --- a/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs +++ b/CapMonsterCloud.Client.Tests/RequestsSerializationTests.cs @@ -10,39 +10,6 @@ namespace Zennolab.CapMonsterCloud.Client { public class RequestsSerializationTests { - [Test] - public void RecaptchaV2ProxylessRequest__ShouldSerialize() - { - // Arrange - var target = new RecaptchaV2ProxylessRequest - { - 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" } - } - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "NoCaptchaTaskProxyless", - websiteURL = target.WebsiteUrl, - websiteKey = target.WebsiteKey, - recaptchaDataSValue = target.DataSValue, - userAgent = target.UserAgent, - cookies = "cookieA=value#A;cookieB=value#B" - })); - } - [Test] public void RecaptchaV2Request__ShouldSerialize([Values] ProxyType proxyType) { @@ -58,11 +25,7 @@ public void RecaptchaV2Request__ShouldSerialize([Values] ProxyType proxyType) { "cookieA", "value#A" }, { "cookieB", "value#B" } }, - ProxyType = proxyType, - ProxyAddress = "https://proxy.com", - ProxyPort = 6045, - ProxyLogin = "login", - ProxyPassword = "p@ssword" + Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") }; // Act @@ -73,16 +36,16 @@ public void RecaptchaV2Request__ShouldSerialize([Values] ProxyType proxyType) new { type = "NoCaptchaTask", - proxyType = proxyType.ToString().ToLower(), - proxyAddress = target.ProxyAddress, - proxyPort = target.ProxyPort, - proxyLogin = target.ProxyLogin, - proxyPassword = target.ProxyPassword, websiteURL = target.WebsiteUrl, websiteKey = target.WebsiteKey, recaptchaDataSValue = target.DataSValue, userAgent = target.UserAgent, - cookies = "cookieA=value#A;cookieB=value#B" + cookies = "cookieA=value#A;cookieB=value#B", + proxyAddress = target.ProxyAddress, + proxyPort = target.ProxyPort, + proxyType = proxyType.ToString().ToLower(), + proxyLogin = target.ProxyLogin, + proxyPassword = target.ProxyPassword })); } @@ -146,32 +109,7 @@ public void RecaptchaV3ProxylessRequest__ShouldSerialize() })); } - [Test] - public void FunCaptchaProxylessRequest__ShouldSerialize() - { - // Arrange - var target = new FunCaptchaProxylessRequest - { - 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" - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "FunCaptchaTaskProxyless", - websiteURL = target.WebsiteUrl, - websitePublicKey = target.WebsiteKey, - funcaptchaApiJSSubdomain = target.Subdomain, - data = target.Data, - })); - } + [Test] public void FunCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) @@ -183,11 +121,7 @@ public void FunCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) WebsiteKey = "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", Data = "{\"blob\":\"dyXvXANMbHj1iDyz.Qj97JtSqR2n%2BuoY1V%2FbdgbrG7p%2FmKiqdU9AwJ6MifEt0np4vfYn6TTJDJEfZDlcz9Q1XMn9przeOV%2FCr2%2FIpi%2FC1s%3D\"}", Subdomain = "mywebsite-api.funcaptcha.com", - ProxyType = proxyType, - ProxyAddress = "https://proxy.com", - ProxyPort = 6045, - ProxyLogin = "login", - ProxyPassword = "p@ssword" + Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") }; // Act @@ -198,49 +132,15 @@ public void FunCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) new { type = "FunCaptchaTask", - proxyType = proxyType.ToString().ToLower(), - proxyAddress = target.ProxyAddress, - proxyPort = target.ProxyPort, - proxyLogin = target.ProxyLogin, - proxyPassword = target.ProxyPassword, websiteURL = target.WebsiteUrl, websitePublicKey = target.WebsiteKey, funcaptchaApiJSSubdomain = target.Subdomain, data = target.Data, - })); - } - - [Test] - public void HCaptchaProxylessRequest__ShouldSerialize([Values] bool invisible) - { - // Arrange - var target = new HCaptchaProxylessRequest - { - WebsiteUrl = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", - WebsiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", - Invisible = invisible, - Data = "some data", - Cookies = new Dictionary - { - { "cookieA", "value#A" }, - { "cookieB", "value#B" } - } - }; - - // Act - var actual = JsonConvert.SerializeObject(target); - - // Assert - actual.Should().Be(JsonConvert.SerializeObject( - new - { - type = "HCaptchaTaskProxyless", - websiteURL = target.WebsiteUrl, - websiteKey = target.WebsiteKey, - isInvisible = 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 })); } @@ -258,11 +158,7 @@ public void HCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) { "cookieA", "value#A" }, { "cookieB", "value#B" } }, - ProxyType = proxyType, - ProxyAddress = "https://proxy.com", - ProxyPort = 6045, - ProxyLogin = "login", - ProxyPassword = "p@ssword" + Proxy = new ProxyContainer("proxy.com", 6045, proxyType, "login", "p@ssword") }; // Act @@ -273,17 +169,17 @@ public void HCaptchaRequest__ShouldSerialize([Values] ProxyType proxyType) new { type = "HCaptchaTask", - proxyType = proxyType.ToString().ToLower(), - proxyAddress = target.ProxyAddress, - proxyPort = target.ProxyPort, - proxyLogin = target.ProxyLogin, - proxyPassword = target.ProxyPassword, websiteURL = target.WebsiteUrl, websiteKey = target.WebsiteKey, isInvisible = target.Invisible, data = target.Data, userAgent = default(string), - cookies = "cookieA=value#A;cookieB=value#B" + cookies = "cookieA=value#A;cookieB=value#B", + proxyAddress = target.ProxyAddress, + proxyPort = target.ProxyPort, + proxyType = proxyType.ToString().ToLower(), + proxyLogin = target.ProxyLogin, + proxyPassword = target.ProxyPassword })); } } diff --git a/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj b/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj index 3a6d2c7..8662f22 100644 --- a/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj +++ b/CapMonsterCloud.Client/CapMonsterCloud.Client.csproj @@ -15,8 +15,8 @@ README.md logo.png https://github.com/ZennoLab/capmonstercloud-client-dotnet - 1.11.0 - Added BinanceTask. Added apiJsUrl parameter to Turnstile + 2.0.0 + Changed way of using proxy @@ -35,7 +35,7 @@ - + True \ diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient.cs b/CapMonsterCloud.Client/CapMonsterCloudClient.cs index 366450f..f3f769e 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Zennolab.CapMonsterCloud.Requests; using Zennolab.CapMonsterCloud.Responses; +using Zennolab.CapMonsterCloud.Validation; namespace Zennolab.CapMonsterCloud { @@ -60,230 +61,16 @@ public async Task GetBalanceAsync(CancellationToken cancellationToken) } return result.balance; - } - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - ImageToTextRequest task, - CancellationToken cancellationToken = default) - => Solve(task, ImageToTextTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - RecaptchaV2Request task, - CancellationToken cancellationToken = default) - => Solve(task, RecaptchaV2Timeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - RecaptchaV2ProxylessRequest task, - CancellationToken cancellationToken = default) - => Solve(task, RecaptchaV2Timeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - RecaptchaV3ProxylessRequest task, - CancellationToken cancellationToken = default) - => Solve(task, RecaptchaV3Timeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - FunCaptchaRequest task, - CancellationToken cancellationToken = default) - => Solve(task, FunCaptchaTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - FunCaptchaProxylessRequest task, - CancellationToken cancellationToken = default) - => Solve(task, FunCaptchaTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - HCaptchaRequest task, - CancellationToken cancellationToken = default) - => Solve(task, HCaptchaTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - HCaptchaProxylessRequest task, - CancellationToken cancellationToken = default) - => Solve(task, HCaptchaTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - GeeTestRequest task, - CancellationToken cancellationToken = default) - => Solve(task, GeeTestTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - GeeTestProxylessRequest task, - CancellationToken cancellationToken = default) - => Solve(task, GeeTestTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - RecaptchaV2EnterpriseRequest task, - CancellationToken cancellationToken) - => Solve(task, RecaptchaV2EnterpriseTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - RecaptchaV2EnterpriseProxylessRequest task, - CancellationToken cancellationToken) - => Solve(task, RecaptchaV2EnterpriseTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - TurnstileRequest task, - CancellationToken cancellationToken) - => Solve(task, TurnstileTimeouts, cancellationToken); + } /// /// malformed task object /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - TurnstileProxylessRequest task, - CancellationToken cancellationToken) - => Solve(task, TurnstileTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - RecaptchaComplexImageTaskRequest taskRequest, - CancellationToken cancellationToken) - => Solve(taskRequest, ImageToTextTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - HCaptchaComplexImageTaskRequest taskRequest, - CancellationToken cancellationToken) - => Solve(taskRequest, ImageToTextTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - FunCaptchaComplexImageTaskRequest taskRequest, - CancellationToken cancellationToken) - => Solve(taskRequest, ImageToTextTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - DataDomeCustomTaskRequest task, - CancellationToken cancellationToken) - => Solve(task, DataDomeTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - DataDomeCustomTaskProxylessRequest task, - CancellationToken cancellationToken) - => Solve(task, DataDomeTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - AmazonWafRequest task, - CancellationToken cancellationToken) - => Solve(task, AmazonWafTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - AmazonWafProxylessRequest task, - CancellationToken cancellationToken) - => Solve(task, AmazonWafTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - TenDiCustomTaskRequest task, - CancellationToken cancellationToken) - => Solve(task, TenDiTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - TenDiCustomTaskProxylessRequest task, - CancellationToken cancellationToken) - => Solve(task, TenDiTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - BasiliskCustomTaskRequest task, - CancellationToken cancellationToken) - => Solve(task, BasiliskTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - BasiliskCustomTaskProxylessRequest task, - CancellationToken cancellationToken) - => Solve(task, BasiliskTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - BinanceTaskRequest task, - CancellationToken cancellationToken) - => Solve(task, BinanceTimeouts, cancellationToken); - - /// - /// malformed task object - /// exception on processing HTTP request to capmonster.cloud - public Task> SolveAsync( - BinanceTaskProxylessRequest task, - CancellationToken cancellationToken) - => Solve(task, BinanceTimeouts, cancellationToken); - - private async Task> Solve( - CaptchaRequestBase task, - GetResultTimeouts getResultTimeouts, - CancellationToken cancellationToken) + public async Task> SolveAsync( + CaptchaRequestBase task, + CancellationToken cancellationToken) where TSolution : CaptchaResponseBase { - ValidateTask(task); + ValidateTask, TSolution>(task); var createdTask = await CreateTask(task, cancellationToken); if (createdTask.errorId != 0) @@ -291,6 +78,8 @@ private async Task> Solve( 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)) { @@ -331,13 +120,13 @@ private async Task> Solve( return new CaptchaResult { Error = ErrorType.Timeout }; } - private void ValidateTask(TTask task) where TTask : CaptchaRequestBase - => Validator.ValidateObject(task, new ValidationContext(task), validateAllProperties: true); + private void ValidateTask(TTask task) where TTask : CaptchaRequestBase where TSolution : CaptchaResponseBase + => TaskValidator.ValidateObjectIncludingInternals(task); - private async Task CreateTask(CaptchaRequestBase task, CancellationToken cancellationToken) + private async Task CreateTask(CaptchaRequestBase task, CancellationToken cancellationToken) where TSolution : CaptchaResponseBase { var body = ToJson( - new CreateTaskRequest + new CreateTaskRequest { clientKey = _options.ClientKey, task = task, diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs index 2b956c0..307ffda 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient_GetResultTimeouts.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using Zennolab.CapMonsterCloud.Requests; namespace Zennolab.CapMonsterCloud { @@ -15,38 +17,7 @@ private struct GetResultTimeouts public TimeSpan Timeout { get; set; } } - private static readonly GetResultTimeouts RecaptchaV2Timeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - }; - - private static readonly GetResultTimeouts RecaptchaV2EnterpriseTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - }; - - private static readonly GetResultTimeouts RecaptchaV3Timeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - }; - - private static readonly GetResultTimeouts ImageToTextTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromMilliseconds(350), - RequestsInterval = TimeSpan.FromMilliseconds(200), - Timeout = TimeSpan.FromSeconds(10) - }; - - private static readonly GetResultTimeouts FunCaptchaTimeouts = new GetResultTimeouts + private static readonly GetResultTimeouts DefaultResultTimeouts = new GetResultTimeouts { FirstRequestDelay = TimeSpan.FromSeconds(1), FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), @@ -54,65 +25,154 @@ private struct GetResultTimeouts Timeout = TimeSpan.FromSeconds(80) }; - private static readonly GetResultTimeouts HCaptchaTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - }; - - private static readonly GetResultTimeouts GeeTestTimeouts = new GetResultTimeouts + private static GetResultTimeouts GetTimeouts(Type type) { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(80) - }; + if (!ResultTimeouts.TryGetValue(type, out var getResultTimeouts) && + !ResultTimeouts.TryGetValue(type.BaseType, out getResultTimeouts)) + { + getResultTimeouts = DefaultResultTimeouts; + } - private static readonly GetResultTimeouts TurnstileTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(80) - }; - - private static readonly GetResultTimeouts DataDomeTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - }; - - private static readonly GetResultTimeouts AmazonWafTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - }; - - private static readonly GetResultTimeouts TenDiTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(180) - }; - - private static readonly GetResultTimeouts BasiliskTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - FirstRequestNoCacheDelay = TimeSpan.FromSeconds(10), - RequestsInterval = TimeSpan.FromSeconds(3), - Timeout = TimeSpan.FromSeconds(100) - }; + return getResultTimeouts; + } - private static readonly GetResultTimeouts BinanceTimeouts = new GetResultTimeouts - { - FirstRequestDelay = TimeSpan.FromSeconds(1), - RequestsInterval = TimeSpan.FromSeconds(1), - Timeout = TimeSpan.FromSeconds(20) - }; + 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) + } + } + }; } } diff --git a/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs b/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs index 3cba5c2..f380c82 100644 --- a/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs +++ b/CapMonsterCloud.Client/CapMonsterCloudClient_nestedClasses.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud { @@ -70,12 +71,12 @@ public TaskCompleted(object solution) public static TaskCompleted Completed(object solution) => new TaskCompleted(solution); } - private class CreateTaskRequest + private class CreateTaskRequest where TSolution : CaptchaResponseBase { #pragma warning disable IDE1006 // Naming Styles public string clientKey { get; set; } - public Requests.CaptchaRequestBase task { get; set; } + public Requests.CaptchaRequestBase task { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public int? softId { get; set; } diff --git a/CapMonsterCloud.Client/CaptchaResult.cs b/CapMonsterCloud.Client/CaptchaResult.cs index d959af9..faab61b 100644 --- a/CapMonsterCloud.Client/CaptchaResult.cs +++ b/CapMonsterCloud.Client/CaptchaResult.cs @@ -1,10 +1,12 @@ -namespace Zennolab.CapMonsterCloud +using Zennolab.CapMonsterCloud.Responses; + +namespace Zennolab.CapMonsterCloud { /// /// General captcha recognition result /// /// Concrete captcha result type - public class CaptchaResult + public class CaptchaResult where TSolution : CaptchaResponseBase { /// /// Error code diff --git a/CapMonsterCloud.Client/ICapMonsterCloudClient.cs b/CapMonsterCloud.Client/ICapMonsterCloudClient.cs index 39bc3d9..a72913f 100644 --- a/CapMonsterCloud.Client/ICapMonsterCloudClient.cs +++ b/CapMonsterCloud.Client/ICapMonsterCloudClient.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Net.Http; +using System.Runtime.InteropServices.ComTypes; using System.Threading; using System.Threading.Tasks; using Zennolab.CapMonsterCloud.Requests; @@ -21,300 +22,15 @@ public interface ICapMonsterCloudClient Task GetBalanceAsync(CancellationToken cancellationToken = default); /// - /// Solve task + /// Solve task /// /// /// - /// - /// You will get response within 300ms to 6s period depending on service workload. - /// /// Captcha recognition result - Task> SolveAsync(ImageToTextRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(RecaptchaV2Request task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(RecaptchaV2ProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(RecaptchaV3ProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 80 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(FunCaptchaRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 80 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(FunCaptchaProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(HCaptchaRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(HCaptchaProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 80 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(GeeTestRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 80 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(GeeTestProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(RecaptchaV2EnterpriseRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(RecaptchaV2EnterpriseProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 80 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(TurnstileRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 80 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(TurnstileProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'recaptcha' class - /// - /// - /// - /// - /// You will get response within 10secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(RecaptchaComplexImageTaskRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'hcaptcha' class - /// - /// - /// - /// - /// You will get response within 10 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(HCaptchaComplexImageTaskRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'funcaptcha' class - /// - /// - /// - /// - /// You will get response within 10 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(FunCaptchaComplexImageTaskRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'DataDome' class - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(DataDomeCustomTaskRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'DataDome' class - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(DataDomeCustomTaskProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(AmazonWafProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(AmazonWafRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'TenDi' class - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(TenDiCustomTaskRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'TenDi' class - /// - /// - /// - /// - /// You will get response within 10 - 180 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(TenDiCustomTaskProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'Basilisk' class - /// - /// - /// - /// - /// You will get response within 10 - 100 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(BasiliskCustomTaskRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task with 'Basilisk' class - /// - /// - /// - /// - /// You will get response within 10 - 100 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(BasiliskCustomTaskProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 1 - 20 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(BinanceTaskProxylessRequest task, CancellationToken cancellationToken = default); - - /// - /// Solve task - /// - /// - /// - /// - /// You will get response within 1 - 20 secs period depending on service workload. - /// - /// Captcha recognition result - Task> SolveAsync(BinanceTaskRequest task, CancellationToken cancellationToken = default); + /// 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/ProxyContainer.cs b/CapMonsterCloud.Client/ProxyContainer.cs new file mode 100644 index 0000000..4449e01 --- /dev/null +++ b/CapMonsterCloud.Client/ProxyContainer.cs @@ -0,0 +1,72 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Requests; +using Zennolab.CapMonsterCloud.Validation; + +namespace Zennolab.CapMonsterCloud +{ + /// + /// Represents a proxy configuration container with validation. + /// + public class ProxyContainer + { + /// + /// 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"); + + 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 port + /// + [Range(0, 65535)] + public int ProxyPort { 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; } + + /// + /// Proxy password + /// + public string ProxyPassword { get; private set; } + } +} diff --git a/CapMonsterCloud.Client/Requests/AmazonWafProxylessRequest.cs b/CapMonsterCloud.Client/Requests/AmazonWafProxylessRequest.cs deleted file mode 100644 index 5c16136..0000000 --- a/CapMonsterCloud.Client/Requests/AmazonWafProxylessRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// AmazonTask recognition request (without proxy). - /// - /// - /// https://docs.capmonster.cloud/docs/captchas/amazon-task - /// - public class AmazonWafProxylessRequest : AmazonWafRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "AmazonTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs b/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs index 4a5dac5..2f8f9c6 100644 --- a/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs +++ b/CapMonsterCloud.Client/Requests/AmazonWafRequest.cs @@ -1,16 +1,16 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// AmazonWaf recognition request (with proxy). + /// AmazonWaf recognition request. /// /// /// https://docs.capmonster.cloud/docs/captchas/amazon-task /// - public class AmazonWafRequest : AmazonWafRequestBase, IProxyInfo + public class AmazonWafRequest : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -21,26 +21,52 @@ public class AmazonWafRequest : AmazonWafRequestBase, IProxyInfo [JsonProperty("type", Required = Required.Always)] public override sealed string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + /// + /// The address of the main page where captcha is solved. + /// + [JsonProperty("websiteURL", Required = Required.Always)] + [Url] + public string WebsiteUrl { get; set; } - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// 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; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { get; set; } + /// + /// Link to challenge.js (see description below the table) + /// + [JsonProperty("challengeScript", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string ChallengeScript { get; set; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { get; set; } + /// + /// Link to captcha.js (see description below the table) + /// + [JsonProperty("captchaScript", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string CaptchaScript { get; set; } - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { get; set; } + /// + /// 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.iv + /// + [JsonProperty("iv", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string Iv { 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". + /// + [JsonProperty("cookieSolution")] + public bool CookieSolution { get; set; } } } diff --git a/CapMonsterCloud.Client/Requests/AmazonWafRequestBase.cs b/CapMonsterCloud.Client/Requests/AmazonWafRequestBase.cs deleted file mode 100644 index c993b3a..0000000 --- a/CapMonsterCloud.Client/Requests/AmazonWafRequestBase.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base AmazonWaf recognition request - /// - public abstract class AmazonWafRequestBase : CaptchaRequestBase - { - /// - /// The address of the main page where captcha is solved. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - - /// - /// 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; } - - /// - /// 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 captcha.js (see description below the table) - /// - [JsonProperty("captchaScript", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string CaptchaScript { get; set; } - - /// - /// 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.iv - /// - [JsonProperty("iv", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string Iv { 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". - /// - [JsonProperty("cookieSolution")] - public bool CookieSolution { get; set; } - } -} diff --git a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskProxylessRequest.cs b/CapMonsterCloud.Client/Requests/BasiliskCustomTaskProxylessRequest.cs deleted file mode 100644 index f6d04c9..0000000 --- a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskProxylessRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Basilisk CustomTask recognition request without proxy - /// - public class BasiliskCustomTaskProxylessRequest : BasiliskCustomTaskRequestBase { } -} diff --git a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs index 298c705..aeecb88 100644 --- a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequest.cs @@ -1,37 +1,22 @@ -using Newtonsoft.Json.Converters; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; +using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; -using System.Text; namespace Zennolab.CapMonsterCloud.Requests { /// - /// Basilisk CustomTask recognition request with proxy + /// Basilisk CustomTask recognition request /// - public class BasiliskCustomTaskRequest : BasiliskCustomTaskRequestBase + public class BasiliskCustomTaskRequest : CustomTaskRequestBase { /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + public override string Class => "Basilisk"; - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } - - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { get; set; } - - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { get; set; } - - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { get; set; } + /// + /// 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; } } } diff --git a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequestBase.cs b/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequestBase.cs deleted file mode 100644 index 3bfefaa..0000000 --- a/CapMonsterCloud.Client/Requests/BasiliskCustomTaskRequestBase.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Basilisk CustomTask recognition request base - /// - public class BasiliskCustomTaskRequestBase : 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; } - } -} diff --git a/CapMonsterCloud.Client/Requests/BinanceTaskProxylessRequest.cs b/CapMonsterCloud.Client/Requests/BinanceTaskProxylessRequest.cs deleted file mode 100644 index 7ec311f..0000000 --- a/CapMonsterCloud.Client/Requests/BinanceTaskProxylessRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// BinanceTask recognition request (without proxy). - /// - /// - /// https://docs.capmonster.cloud/docs/captchas/binance - /// - public sealed class BinanceTaskProxylessRequest : BinanceTaskRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "BinanceTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs b/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs index 84bfbf0..553e883 100644 --- a/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/BinanceTaskRequest.cs @@ -1,16 +1,16 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// BinanceTask recognition request (with proxy). + /// BinanceTask recognition request. /// /// /// https://docs.capmonster.cloud/docs/captchas/binance /// - public sealed class BinanceTaskRequest : BinanceTaskRequestBase + public sealed class BinanceTaskRequest : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -21,26 +21,34 @@ public sealed class BinanceTaskRequest : BinanceTaskRequestBase [JsonProperty("type", Required = Required.Always)] public override sealed string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } - - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// The address of the main page where the captcha is solved. + /// + [JsonProperty("websiteURL", Required = Required.Always)] + [Url] + public string WebsiteUrl { get; set; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { 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 + /// + [JsonProperty("websiteKey", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { get; set; } + /// + /// 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; } - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { 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; } } } diff --git a/CapMonsterCloud.Client/Requests/BinanceTaskRequestBase.cs b/CapMonsterCloud.Client/Requests/BinanceTaskRequestBase.cs deleted file mode 100644 index f6bdc94..0000000 --- a/CapMonsterCloud.Client/Requests/BinanceTaskRequestBase.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base BinanceTask recognition request - /// - public abstract class BinanceTaskRequestBase : CaptchaRequestBase - { - /// - /// The address of the main page where the captcha is solved. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { 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 - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } - - /// - /// 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; } - - /// - /// 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; } - } -} diff --git a/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs b/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs index d699dc7..03aa2d7 100644 --- a/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs +++ b/CapMonsterCloud.Client/Requests/CaptchaRequestBase.cs @@ -1,15 +1,18 @@ -namespace Zennolab.CapMonsterCloud.Requests -{ +using Zennolab.CapMonsterCloud.Responses; + +namespace Zennolab.CapMonsterCloud.Requests +{ /// /// Base captcha recognition request /// - public abstract class CaptchaRequestBase - { + public abstract class CaptchaRequestBase where TResponse : CaptchaResponseBase + { /// /// Gets recognition task type /// public abstract string Type { get; } + internal virtual bool UseNoCache => false; } } diff --git a/CapMonsterCloud.Client/Requests/CaptchaRequestBaseWithProxy.cs b/CapMonsterCloud.Client/Requests/CaptchaRequestBaseWithProxy.cs new file mode 100644 index 0000000..c2f49c9 --- /dev/null +++ b/CapMonsterCloud.Client/Requests/CaptchaRequestBaseWithProxy.cs @@ -0,0 +1,59 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; + +namespace Zennolab.CapMonsterCloud.Requests +{ + /// + /// Base captcha recognition request + /// + public abstract class CaptchaRequestBaseWithProxy : CaptchaRequestBase, IProxyInfo where TResponse : CaptchaResponseBase + { + /// + [JsonIgnore] + public ProxyContainer Proxy + { + get + { + if (!string.IsNullOrEmpty(ProxyAddress)) + return new ProxyContainer(ProxyAddress, ProxyPort, ProxyType, ProxyLogin, ProxyPassword); + + return null; + } + set + { + if (value != null) + { + ProxyAddress = value.ProxyAddress; + ProxyPort = value.ProxyPort; + ProxyType = value.ProxyType; + ProxyLogin = value.ProxyLogin; + ProxyPassword = value.ProxyPassword; + } + } + } + + /// + [JsonProperty("proxyAddress")] + internal string ProxyAddress { get; set; } + + /// + [JsonProperty("proxyPort")] + [Range(0, 65535)] + protected internal int ProxyPort { get; set; } + + /// + [JsonProperty("proxyType")] + [JsonConverter(typeof(StringEnumConverter))] + internal ProxyType ProxyType { get; set; } + + /// + [JsonProperty("proxyLogin")] + internal string ProxyLogin { get; set; } + + /// + [JsonProperty("proxyPassword")] + internal string ProxyPassword { get; set; } + } +} diff --git a/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs b/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs index 0d17eb6..2bd9719 100644 --- a/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs +++ b/CapMonsterCloud.Client/Requests/ComplexImageTaskBase.cs @@ -1,15 +1,14 @@ -using System; +using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Text; -using Newtonsoft.Json; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// /// ComplexImageTask recognition request /// - public abstract class ComplexImageTaskRequestBase : CaptchaRequestBase + public abstract class ComplexImageTaskRequestBase : CaptchaRequestBase { /// /// Recognition task type diff --git a/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs b/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs index 5245d41..6a69ff8 100644 --- a/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs +++ b/CapMonsterCloud.Client/Requests/CustomTaskRequestBase.cs @@ -1,13 +1,14 @@ using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// /// CustomTask recognition request /// - public abstract class CustomTaskRequestBase : CaptchaRequestBase + public abstract class CustomTaskRequestBase : CaptchaRequestBaseWithProxy { /// /// Recognition task type diff --git a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskProxylessRequest.cs b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskProxylessRequest.cs deleted file mode 100644 index a0e8e8c..0000000 --- a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskProxylessRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// DataDome CustomTask recognition request without proxy - /// - public sealed class DataDomeCustomTaskProxylessRequest : DataDomeCustomTaskRequestBase - { - /// - public DataDomeCustomTaskProxylessRequest(string datadomeCookie, string captchaUrl = null, string htmlPageBase64 = null) : base(datadomeCookie, captchaUrl, htmlPageBase64) { } - } -} diff --git a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs index 6849d7f..32301d3 100644 --- a/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/DataDomeCustomTaskRequest.cs @@ -1,39 +1,11 @@ -using Newtonsoft.Json.Converters; -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests { /// - /// DataDome CustomTask recognition request with proxy + /// DataDome CustomTask recognition request /// public sealed class DataDomeCustomTaskRequest : DataDomeCustomTaskRequestBase { /// public DataDomeCustomTaskRequest(string datadomeCookie, string captchaUrl = null, string htmlPageBase64 = null) : base(datadomeCookie, captchaUrl, htmlPageBase64) { } - - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } - - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } - - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { get; set; } - - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { get; set; } - - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { get; set; } - - } } diff --git a/CapMonsterCloud.Client/Requests/FunCaptchaProxylessRequest.cs b/CapMonsterCloud.Client/Requests/FunCaptchaProxylessRequest.cs deleted file mode 100644 index 90b501a..0000000 --- a/CapMonsterCloud.Client/Requests/FunCaptchaProxylessRequest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// FunCaptcha recognition request (without proxy). - /// - public sealed class FunCaptchaProxylessRequest : FunCaptchaRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "FunCaptchaTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs b/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs index 8ae9173..87d4b94 100644 --- a/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs +++ b/CapMonsterCloud.Client/Requests/FunCaptchaRequest.cs @@ -1,13 +1,13 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// FunCaptcha recognition request (with proxy). + /// FunCaptcha recognition request. /// - public sealed class FunCaptchaRequest : FunCaptchaRequestBase, IProxyInfo + public sealed class FunCaptchaRequest : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -18,26 +18,48 @@ public sealed class FunCaptchaRequest : FunCaptchaRequestBase, IProxyInfo [JsonProperty("type", Required = Required.Always)] public override sealed string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + /// + /// 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; } - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// FunCaptcha website key. + /// ]]> + /// + /// 69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC + [JsonProperty("websitePublicKey", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { 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 + [JsonProperty("funcaptchaApiJSSubdomain")] + public string Subdomain { get; set; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { 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; } - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { 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; } + + internal override bool UseNoCache => this.NoCache ?? false; } } diff --git a/CapMonsterCloud.Client/Requests/FunCaptchaRequestBase.cs b/CapMonsterCloud.Client/Requests/FunCaptchaRequestBase.cs deleted file mode 100644 index a14d92b..0000000 --- a/CapMonsterCloud.Client/Requests/FunCaptchaRequestBase.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base FunCaptcha recognition request - /// - public abstract class FunCaptchaRequestBase : CaptchaRequestBase - { - /// - /// 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; } - - /// - /// FunCaptcha website key. - /// ]]> - /// - /// 69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC - [JsonProperty("websitePublicKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { 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 - [JsonProperty("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; } - - /// - /// 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; } - - internal override bool UseNoCache => this.NoCache ?? false; - } -} diff --git a/CapMonsterCloud.Client/Requests/GeeTestProxylessRequest.cs b/CapMonsterCloud.Client/Requests/GeeTestProxylessRequest.cs deleted file mode 100644 index 2721597..0000000 --- a/CapMonsterCloud.Client/Requests/GeeTestProxylessRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// GeeTest recognition request (without proxy). - /// - /// - /// https://zenno.link/doc-geetest-en - /// - public sealed class GeeTestProxylessRequest : GeeTestRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "GeeTestTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/GeeTestRequest.cs b/CapMonsterCloud.Client/Requests/GeeTestRequest.cs index 233e0b9..4303c84 100644 --- a/CapMonsterCloud.Client/Requests/GeeTestRequest.cs +++ b/CapMonsterCloud.Client/Requests/GeeTestRequest.cs @@ -1,16 +1,16 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// GeeTest recognition request (with proxy). + /// GeeTest recognition request. /// /// /// https://zenno.link/doc-geetest-proxy-en /// - public sealed class GeeTestRequest : GeeTestRequestBase, IProxyInfo + public sealed class GeeTestRequest : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -21,26 +21,72 @@ public sealed class GeeTestRequest : GeeTestRequestBase, IProxyInfo [JsonProperty("type", Required = Required.Always)] public override sealed string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + /// + /// 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; } - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// 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; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { get; set; } + /// + /// Version number. The default value is 3. Versions 4 is supported. + /// + /// 4 + [JsonProperty("version")] + public int? Version { get; set; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { get; set; } + /// + /// Additional initialization parameters for version 4. + /// + /// { "riskType": "slide" } + [JsonProperty("initParameters")] + public object InitParameters { get; set; } - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { 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; } + + /// + /// May be required for some sites. + /// + [JsonProperty("geetestApiServerSubdomain")] + public string Subdomain { get; set; } + + /// + /// May be required for some sites. + /// Send JSON as a string. + /// + [JsonProperty("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; } } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/GeeTestRequestBase.cs b/CapMonsterCloud.Client/Requests/GeeTestRequestBase.cs deleted file mode 100644 index 479c5f2..0000000 --- a/CapMonsterCloud.Client/Requests/GeeTestRequestBase.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base GeeTest recognition request - /// - public abstract class GeeTestRequestBase : CaptchaRequestBase - { - /// - /// 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; } - - /// - /// 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; } - - /// - /// Version number. The default value is 3. Versions 4 is supported. - /// - /// 4 - [JsonProperty("version")] - public int? Version { get; set; } - - /// - /// Additional initialization parameters for version 4. - /// - /// { "riskType": "slide" } - [JsonProperty("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; } - - /// - /// May be required for some sites. - /// - [JsonProperty("geetestApiServerSubdomain")] - public string Subdomain { get; set; } - - /// - /// May be required for some sites. - /// Send JSON as a string. - /// - [JsonProperty("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; } - } -} diff --git a/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs b/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs index 2dfabb6..4e6d8f7 100644 --- a/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs +++ b/CapMonsterCloud.Client/Requests/HCaptchaComplexImageTask.cs @@ -1,7 +1,6 @@ -using System; +using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; namespace Zennolab.CapMonsterCloud.Requests { diff --git a/CapMonsterCloud.Client/Requests/HCaptchaProxylessRequest.cs b/CapMonsterCloud.Client/Requests/HCaptchaProxylessRequest.cs deleted file mode 100644 index b270c59..0000000 --- a/CapMonsterCloud.Client/Requests/HCaptchaProxylessRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// HCaptcha recognition request (without proxy). - /// - /// - /// https://zenno.link/doc-hcaptcha-en - /// - public sealed class HCaptchaProxylessRequest : HCaptchaRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "HCaptchaTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs b/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs index 9d86ddc..5fcb383 100644 --- a/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs +++ b/CapMonsterCloud.Client/Requests/HCaptchaRequest.cs @@ -1,16 +1,18 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// HCaptcha recognition request (with proxy). + /// HCaptcha recognition request. /// /// /// https://zenno.link/doc-hcaptcha-proxy-en /// - public sealed class HCaptchaRequest : HCaptchaRequestBase, IProxyInfo + public sealed class HCaptchaRequest : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -21,26 +23,74 @@ public sealed class HCaptchaRequest : HCaptchaRequestBase, IProxyInfo [JsonProperty("type", Required = Required.Always)] public override sealed string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + /// + /// Address of a webpage with hCaptcha. + /// + [JsonProperty("websiteURL", Required = Required.Always)] + [Url] + public string WebsiteUrl { get; set; } - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// hCaptcha website key. + /// + [JsonProperty("websiteKey", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { get; set; } + /// + /// Set true for invisible version of hCaptcha + /// + [JsonProperty("isInvisible")] + public bool Invisible { get; set; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { 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; } - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { 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; } + + /// + /// 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; } + + /// + /// Additional cookies which we must use during interaction with target page. + /// + [JsonProperty("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; } + + internal override bool UseNoCache => this.NoCache ?? false; } } diff --git a/CapMonsterCloud.Client/Requests/HCaptchaRequestBase.cs b/CapMonsterCloud.Client/Requests/HCaptchaRequestBase.cs deleted file mode 100644 index f697c0b..0000000 --- a/CapMonsterCloud.Client/Requests/HCaptchaRequestBase.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Zennolab.CapMonsterCloud.Responses; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base HCaptcha recognition request - /// - public abstract class HCaptchaRequestBase : CaptchaRequestBase - { - /// - /// Address of a webpage with hCaptcha. - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - - /// - /// hCaptcha website key. - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { get; set; } - - /// - /// Set true for invisible version of hCaptcha - /// - [JsonProperty("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; } - - /// - /// 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; } - - /// - /// 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; } - - /// - /// Additional cookies which we must use during interaction with target page. - /// - [JsonProperty("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; } - - internal override bool UseNoCache => this.NoCache ?? false; - } -} diff --git a/CapMonsterCloud.Client/Requests/IProxyInfo.cs b/CapMonsterCloud.Client/Requests/IProxyInfo.cs index 0b80c19..6e940c5 100644 --- a/CapMonsterCloud.Client/Requests/IProxyInfo.cs +++ b/CapMonsterCloud.Client/Requests/IProxyInfo.cs @@ -1,36 +1,13 @@ namespace Zennolab.CapMonsterCloud.Requests { /// - /// Interface for captcha recognition with proxy requests + /// Interface for captcha recognition with proxy /// public interface IProxyInfo - { + { /// - /// Type of the proxy + /// Proxy settings /// - ProxyType ProxyType { get; } - - /// - /// 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...) - /// - string ProxyAddress { get; } - - /// - /// Proxy port - /// - int ProxyPort { get; } - - /// - /// Login for proxy which requires authorizaiton (basic) - /// - string ProxyLogin { get; } - - /// - /// Proxy password - /// - string ProxyPassword { get; } + ProxyContainer Proxy { get; } } } diff --git a/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs b/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs index 6f8204f..9a6f0da 100644 --- a/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs +++ b/CapMonsterCloud.Client/Requests/ImageToTextRequest.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { @@ -9,7 +10,7 @@ namespace Zennolab.CapMonsterCloud.Requests /// /// https://zenno.link/doc-ImageToTextTask-en /// - public sealed class ImageToTextRequest : CaptchaRequestBase + public sealed class ImageToTextRequest : CaptchaRequestBase { /// /// Recognition task type diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseProxylessRequest.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseProxylessRequest.cs deleted file mode 100644 index 141fdf3..0000000 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseProxylessRequest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Recaptcha V2 Enterprise recognition request (without proxy). - /// - public sealed class RecaptchaV2EnterpriseProxylessRequest : RecaptchaV2EnterpriseRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "RecaptchaV2EnterpriseTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs index e3e9898..6b2e6eb 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequest.cs @@ -1,13 +1,13 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// Recaptcha V2 Enterprise recognition request (with proxy). + /// Recaptcha V2 Enterprise recognition request. /// - public sealed class RecaptchaV2EnterpriseRequest : RecaptchaV2EnterpriseRequestBase, IProxyInfo + public sealed class RecaptchaV2EnterpriseRequest : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -18,26 +18,52 @@ public sealed class RecaptchaV2EnterpriseRequest : RecaptchaV2EnterpriseRequestB [JsonProperty("type", Required = Required.Always)] public override sealed string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonProperty("websiteURL", Required = Required.Always)] + [Url] + public string WebsiteUrl { get; set; } - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// Recaptcha website key. + /// ]]> + /// + [JsonProperty("websiteKey", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { 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. + /// + [JsonProperty("enterprisePayload")] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string EnterprisePayload { get; set; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { 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; } - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { 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; } + + internal override bool UseNoCache => this.NoCache ?? false; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequestBase.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequestBase.cs deleted file mode 100644 index d6a651c..0000000 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2EnterpriseRequestBase.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base Recaptcha V2 Enterprise recognition request - /// - public abstract class RecaptchaV2EnterpriseRequestBase : CaptchaRequestBase - { - /// - /// Address of a webpage with Google ReCaptcha - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - - /// - /// Recaptcha website key. - /// ]]> - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { 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. - /// - [JsonProperty("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; } - - /// - /// 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; } - - internal override bool UseNoCache => this.NoCache ?? false; - } -} diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2ProxylessRequest.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2ProxylessRequest.cs deleted file mode 100644 index 20f228b..0000000 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2ProxylessRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Recaptcha V2 recognition request (without proxy). - /// - /// - /// https://zenno.link/doc-recaptcha2-en - /// - public sealed class RecaptchaV2ProxylessRequest : RecaptchaV2RequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "NoCaptchaTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override sealed string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs index e0e5375..9db01d7 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaV2Request.cs @@ -1,16 +1,17 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// Recaptcha V2 recognition request (with proxy). + /// Recaptcha V2 recognition request. /// /// /// https://zenno.link/doc-recaptcha2-proxy-en /// - public sealed class RecaptchaV2Request : RecaptchaV2RequestBase, IProxyInfo + public sealed class RecaptchaV2Request : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -21,26 +22,52 @@ public sealed class RecaptchaV2Request : RecaptchaV2RequestBase, IProxyInfo [JsonProperty("type", Required = Required.Always)] public override sealed string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonProperty("websiteURL", Required = Required.Always)] + [Url] + public string WebsiteUrl { get; set; } - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// Recaptcha website key. + /// ]]> + /// + [JsonProperty("websiteKey", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { 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; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { 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; } - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { 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(); + + /// + /// 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; } + + internal override bool UseNoCache => this.NoCache ?? false; } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV2RequestBase.cs b/CapMonsterCloud.Client/Requests/RecaptchaV2RequestBase.cs deleted file mode 100644 index 00b23c7..0000000 --- a/CapMonsterCloud.Client/Requests/RecaptchaV2RequestBase.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base Recaptcha V2 recognition request - /// - public abstract class RecaptchaV2RequestBase : CaptchaRequestBase - { - /// - /// Address of a webpage with Google ReCaptcha - /// - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - - /// - /// Recaptcha website key. - /// ]]> - /// - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { 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; } - - /// - /// 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; } - - /// - /// 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(); - - /// - /// 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; } - - internal override bool UseNoCache => this.NoCache ?? false; - } -} diff --git a/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs b/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs index 728c13d..b38f42f 100644 --- a/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs +++ b/CapMonsterCloud.Client/Requests/RecaptchaV3ProxylessRequest.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { @@ -9,7 +10,7 @@ namespace Zennolab.CapMonsterCloud.Requests /// /// https://zenno.link/doc-recaptcha3-en /// - public sealed class RecaptchaV3ProxylessRequest : CaptchaRequestBase + public sealed class RecaptchaV3ProxylessRequest : CaptchaRequestBase { /// /// Recognition task type diff --git a/CapMonsterCloud.Client/Requests/TenDiCustomTaskProxylessRequest.cs b/CapMonsterCloud.Client/Requests/TenDiCustomTaskProxylessRequest.cs deleted file mode 100644 index 4ae1e00..0000000 --- a/CapMonsterCloud.Client/Requests/TenDiCustomTaskProxylessRequest.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// TenDi CustomTask recognition request without proxy - /// - public sealed class TenDiCustomTaskProxylessRequest : TenDiCustomTaskRequestBase { } -} diff --git a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs b/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs index 13a9984..6d5369d 100644 --- a/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs +++ b/CapMonsterCloud.Client/Requests/TenDiCustomTaskRequest.cs @@ -1,34 +1,9 @@ -using Newtonsoft.Json.Converters; -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; - -namespace Zennolab.CapMonsterCloud.Requests +namespace Zennolab.CapMonsterCloud.Requests { /// - /// TenDi CustomTask recognition request with proxy + /// TenDi CustomTask recognition request /// public sealed class TenDiCustomTaskRequest : TenDiCustomTaskRequestBase { - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } - - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } - - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { get; set; } - - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { get; set; } - - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { get; set; } } } diff --git a/CapMonsterCloud.Client/Requests/TurnstileProxylessRequest.cs b/CapMonsterCloud.Client/Requests/TurnstileProxylessRequest.cs deleted file mode 100644 index 2d5bc34..0000000 --- a/CapMonsterCloud.Client/Requests/TurnstileProxylessRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Turnstile recognition request (without proxy). - /// - /// - /// https://zenno.link/doc-turnstile-en - /// - public sealed class TurnstileProxylessRequest : TurnstileRequestBase - { - /// - /// Recognition task type - /// - public const string TaskType = "TurnstileTaskProxyless"; - - /// - [JsonProperty("type", Required = Required.Always)] - public override string Type => TaskType; - } -} diff --git a/CapMonsterCloud.Client/Requests/TurnstileRequest.cs b/CapMonsterCloud.Client/Requests/TurnstileRequest.cs index 91a0b3c..f24e524 100644 --- a/CapMonsterCloud.Client/Requests/TurnstileRequest.cs +++ b/CapMonsterCloud.Client/Requests/TurnstileRequest.cs @@ -1,16 +1,16 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using System.ComponentModel.DataAnnotations; +using Zennolab.CapMonsterCloud.Responses; namespace Zennolab.CapMonsterCloud.Requests { /// - /// Turnstile recognition request (with proxy). + /// Turnstile recognition request. /// /// /// https://zenno.link/doc-turnstile-proxy-en /// - public sealed class TurnstileRequest : TurnstileRequestBase, IProxyInfo + public sealed class TurnstileRequest : CaptchaRequestBaseWithProxy { /// /// Recognition task type @@ -21,26 +21,81 @@ public sealed class TurnstileRequest : TurnstileRequestBase, IProxyInfo [JsonProperty("type", Required = Required.Always)] public override string Type => TaskType; - /// - [JsonProperty("proxyType", Required = Required.Always)] - [JsonConverter(typeof(StringEnumConverter))] - public ProxyType ProxyType { get; set; } + /// + /// Address of a webpage with Turnstile captcha + /// + /// https://tsinvisble.zlsupport.com + [JsonProperty("websiteURL", Required = Required.Always)] + [Url] + public string WebsiteUrl { get; set; } - /// - [JsonProperty("proxyAddress", Required = Required.Always)] - public string ProxyAddress { get; set; } + /// + /// Turnstile website key. + /// + /// 0x4AAAAAAABUY0VLtOUMAHxE + [JsonProperty("websiteKey", Required = Required.Always)] + [StringLength(int.MaxValue, MinimumLength = 1)] + public string WebsiteKey { get; set; } - /// - [JsonProperty("proxyPort", Required = Required.Always)] - [Range(0, 65535)] - public int ProxyPort { 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; } - /// - [JsonProperty("proxyLogin")] - public string ProxyLogin { get; set; } + internal override bool UseNoCache => this.NoCache ?? false; - /// - [JsonProperty("proxyPassword")] - public string ProxyPassword { get; set; } + /// + /// cf_clearance - if cookies are needed + /// token - if token is needed + /// + /// token + [JsonProperty("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; } + + /// + /// cData + /// + /// 7ea32c865ef0b936 + [JsonProperty("data")] + public string Data { get; set; } + + /// + /// chlPageData + /// + /// 3gAFo2l2MbhCQ3F...Ua3pPVFkzTnk0Mk1ERT0= + [JsonProperty("pageData")] + public string PageData { get; set; } + + /// + /// A base64 encoded html page with a captcha. + /// + /// PCFET0NUWVBFIGh0...vYm9keT48L2h0bWw+ + [JsonProperty("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; } + + /// + /// The string that contains a link to the captcha script. + /// + [JsonProperty("apiJsUrl")] + public string ApiJsUrl { get; set; } } } \ No newline at end of file diff --git a/CapMonsterCloud.Client/Requests/TurnstileRequestBase.cs b/CapMonsterCloud.Client/Requests/TurnstileRequestBase.cs deleted file mode 100644 index d8b4165..0000000 --- a/CapMonsterCloud.Client/Requests/TurnstileRequestBase.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; - -namespace Zennolab.CapMonsterCloud.Requests -{ - /// - /// Base Turnstile recognition request - /// - public abstract class TurnstileRequestBase : CaptchaRequestBase - { - /// - /// Address of a webpage with Turnstile captcha - /// - /// https://tsinvisble.zlsupport.com - [JsonProperty("websiteURL", Required = Required.Always)] - [Url] - public string WebsiteUrl { get; set; } - - /// - /// Turnstile website key. - /// - /// 0x4AAAAAAABUY0VLtOUMAHxE - [JsonProperty("websiteKey", Required = Required.Always)] - [StringLength(int.MaxValue, MinimumLength = 1)] - public string WebsiteKey { 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; } - - 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; } - - /// - /// 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; } - - /// - /// cData - /// - /// 7ea32c865ef0b936 - [JsonProperty("data")] - public string Data { get; set; } - - /// - /// chlPageData - /// - /// 3gAFo2l2MbhCQ3F...Ua3pPVFkzTnk0Mk1ERT0= - [JsonProperty("pageData")] - public string PageData { get; set; } - - /// - /// A base64 encoded html page with a captcha. - /// - /// PCFET0NUWVBFIGh0...vYm9keT48L2h0bWw+ - [JsonProperty("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; } - - /// - /// The string that contains a link to the captcha script. - /// - [JsonProperty("apiJsUrl")] - public string ApiJsUrl { get; set; } - } -} diff --git a/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs b/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs index 8738afe..00f3001 100644 --- a/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs +++ b/CapMonsterCloud.Client/Responses/AmazonWafResponse.cs @@ -6,7 +6,7 @@ namespace Zennolab.CapMonsterCloud.Responses /// /// AmazonWaf recognition response /// - public sealed class AmazonWafResponse + public sealed class AmazonWafResponse : CaptchaResponseBase { /// [JsonProperty("captcha_voucher")] diff --git a/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs b/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs index 0609052..ffeecf7 100644 --- a/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/BinanceTaskResponse.cs @@ -6,7 +6,7 @@ namespace Zennolab.CapMonsterCloud.Responses /// /// BinanceTask recognition response /// - public sealed class BinanceTaskResponse + public sealed class BinanceTaskResponse : CaptchaResponseBase { /// /// BinanceTask token diff --git a/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs b/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs new file mode 100644 index 0000000..72d472c --- /dev/null +++ b/CapMonsterCloud.Client/Responses/CaptchaResponseBase.cs @@ -0,0 +1,6 @@ +namespace Zennolab.CapMonsterCloud.Responses +{ + public abstract class CaptchaResponseBase + { + } +} diff --git a/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs b/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs index 6d73dac..3c4cd32 100644 --- a/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/ComplexImageTaskResponse.cs @@ -1,14 +1,12 @@ -using System; +using Newtonsoft.Json; using System.Collections.Generic; -using System.Text; -using Newtonsoft.Json; namespace Zennolab.CapMonsterCloud.Responses { /// /// Response for grid-like tasks /// - public class GridComplexImageTaskResponse + public class GridComplexImageTaskResponse : CaptchaResponseBase { /// /// Collection with answers. Click on images with 'true' diff --git a/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs b/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs index c85ee22..a4dab7d 100644 --- a/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs +++ b/CapMonsterCloud.Client/Responses/CustomTaskResponse.cs @@ -6,7 +6,7 @@ namespace Zennolab.CapMonsterCloud.Responses /// /// Response for custom tasks /// - public class CustomTaskResponse + public class CustomTaskResponse : CaptchaResponseBase { /// public sealed class DomainInfo diff --git a/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs b/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs index efd48e8..b32c090 100644 --- a/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs +++ b/CapMonsterCloud.Client/Responses/FunCaptchaResponse.cs @@ -5,7 +5,7 @@ namespace Zennolab.CapMonsterCloud.Responses /// /// FunCaptcha recognition response /// - public class FunCaptchaResponse + public class FunCaptchaResponse : CaptchaResponseBase { /// /// FunCaptcha token that needs to be substituted into the form. diff --git a/CapMonsterCloud.Client/Responses/GeeTestResponse.cs b/CapMonsterCloud.Client/Responses/GeeTestResponse.cs index 7a14649..d7adc2d 100644 --- a/CapMonsterCloud.Client/Responses/GeeTestResponse.cs +++ b/CapMonsterCloud.Client/Responses/GeeTestResponse.cs @@ -6,7 +6,7 @@ namespace Zennolab.CapMonsterCloud.Responses /// GeeTest recognition response /// [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] - public class GeeTestResponse + public class GeeTestResponse : CaptchaResponseBase { /// /// diff --git a/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs b/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs index c38380c..07495cc 100644 --- a/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs +++ b/CapMonsterCloud.Client/Responses/ImageToTextResponse.cs @@ -5,7 +5,7 @@ namespace Zennolab.CapMonsterCloud.Responses /// /// ImageToText recognition response /// - public class ImageToTextResponse + public class ImageToTextResponse : CaptchaResponseBase { /// /// Captcha answer diff --git a/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs b/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs index 8e3b964..8bfe054 100644 --- a/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs +++ b/CapMonsterCloud.Client/Responses/RecaptchaResponseBase.cs @@ -5,7 +5,7 @@ namespace Zennolab.CapMonsterCloud.Responses /// /// Recaptcha recognition response base /// - public abstract class RecaptchaResponseBase + public abstract class RecaptchaResponseBase : CaptchaResponseBase { /// /// Hash which should be inserted into Recaptcha2 submit form in diff --git a/CapMonsterCloud.Client/Responses/TurnstileResponse.cs b/CapMonsterCloud.Client/Responses/TurnstileResponse.cs index eac42ec..ceae626 100644 --- a/CapMonsterCloud.Client/Responses/TurnstileResponse.cs +++ b/CapMonsterCloud.Client/Responses/TurnstileResponse.cs @@ -1,14 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Zennolab.CapMonsterCloud.Responses { /// /// Turnstile recognition response /// - public sealed class TurnstileResponse + public sealed class TurnstileResponse : CaptchaResponseBase { /// /// Turnstile token diff --git a/CapMonsterCloud.Client/Validation/ProxyValidator.cs b/CapMonsterCloud.Client/Validation/ProxyValidator.cs new file mode 100644 index 0000000..bb072df --- /dev/null +++ b/CapMonsterCloud.Client/Validation/ProxyValidator.cs @@ -0,0 +1,69 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text.RegularExpressions; + +namespace Zennolab.CapMonsterCloud.Validation +{ + internal class ProxyValidator + { + private static readonly Regex HostnameRegex = new Regex(@"^(?!\d+(\.\d+){3}$)([a-zA-Z0-9.-]+)$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + + internal static bool IsValidProxy(string proxy) + { + if (string.IsNullOrWhiteSpace(proxy)) + return false; + + if (IPAddress.TryParse(proxy, out IPAddress ipAddress)) + { + return IsPublicIPAddress(ipAddress); + } + + return IsValidHostname(proxy); + } + + private static bool IsValidHostname(string hostname) + { + if (hostname.Equals("localhost", StringComparison.OrdinalIgnoreCase)) + return false; + + // Ensure it follows domain name rules + return hostname.Length <= 253 && HostnameRegex.IsMatch(hostname); + } + + private static bool IsPublicIPAddress(IPAddress ip) + { + if (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6) + { + byte[] bytes = ip.GetAddressBytes(); + + // 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; + } + } + + // IPv6 private/local addresses + if (ip.IsIPv6LinkLocal || ip.IsIPv6SiteLocal) + { + return false; + } + + // Loopback and unspecified addresses + if (IPAddress.IsLoopback(ip) || ip.Equals(IPAddress.Any) || ip.Equals(IPAddress.IPv6Any)) + { + return false; + } + + return true; + } + + return false; + } + } +} diff --git a/CapMonsterCloud.Client/Validation/TaskValidator.cs b/CapMonsterCloud.Client/Validation/TaskValidator.cs new file mode 100644 index 0000000..5e8b9fe --- /dev/null +++ b/CapMonsterCloud.Client/Validation/TaskValidator.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; + +namespace Zennolab.CapMonsterCloud.Validation +{ + internal static class TaskValidator + { + internal static void ValidateObjectIncludingInternals(object obj) + { + var results = new List(); + + 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 attribute in attributes) + { + var context = new ValidationContext(obj) + { + MemberName = property.Name + }; + + var result = attribute.GetValidationResult(value, context); + if (result != ValidationResult.Success) + { + results.Add(result); + } + } + } + + if (results.Any()) + { + throw new AggregateException(results.Select(r => new ValidationException(r.ErrorMessage))); + } + } + } +} diff --git a/README.md b/README.md index 1b723e0..e3ac794 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,24 @@ Via .NET CLI var cmCloudClient = CapMonsterCloudClientFactory.Create(clientOptions); // solve RecaptchaV2 (without proxy) - var recaptchaV2Request = new RecaptchaV2ProxylessRequest + var recaptchaV2Request = new RecaptchaV2Request { WebsiteUrl = "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", WebsiteKey = "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd", }; var recaptchaV2Result = await cmCloudClient.SolveAsync(recaptchaV2Request); + // solve RecaptchaV2 (with proxy) + var recaptchaV2Request = new RecaptchaV2Request + { + WebsiteUrl = "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", + WebsiteKey = "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd", + Proxy = new ProxyContainer("203.0.113.45", 8080, ProxyType.Http, "login", "password") + }; + var recaptchaV2Result = await cmCloudClient.SolveAsync(recaptchaV2Request); + // solve HCaptcha (without proxy) - var hcaptchaRequest = new HCaptchaProxylessRequest + var hcaptchaRequest = new HCaptchaRequest { WebsiteUrl = "https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", WebsiteKey = "472fc7af-86a4-4382-9a49-ca9090474471", @@ -39,17 +48,12 @@ Via .NET CLI Supported captcha recognition requests: -- [GeeTestProxylessRequest](https://zenno.link/doc-geetest-en) - [GeeTestRequest](https://zenno.link/doc-geetest-proxy-en) -- [HCaptchaProxylessRequest](https://zenno.link/doc-hcaptcha-en) - [HCaptchaRequest](https://zenno.link/doc-hcaptcha-proxy-en) - [ImageToTextRequest](https://zenno.link/doc-ImageToTextTask-en) -- [RecaptchaV2ProxylessRequest](https://zenno.link/doc-recaptcha2-en) - [RecaptchaV2Request](https://zenno.link/doc-recaptcha2-proxy-en) - [RecaptchaV3ProxylessRequest](https://zenno.link/doc-recaptcha3-en) -- [RecaptchaV2EnterpriseProxylessRequest](https://zenno.link/doc-recaptcha2e-en) - [RecaptchaV2EnterpriseRequest](https://zenno.link/doc-recaptcha2e-proxy-en) -- [TurnstileProxylessRequest](https://zenno.link/doc-turnstile-en) - [TurnstileRequest](https://zenno.link/doc-turnstile-proxy-en) - [RecaptchaComplexImageTaskRequest](https://zenno.link/doc-complextask-rc-en) - [HcaptchaComplexImageTaskRequest](https://zenno.link/doc-complextask-hc-en)