From ce520d126d5e677a373ba6d7b0e53d768a5a91af Mon Sep 17 00:00:00 2001 From: alperensert <63921520+alperensert@users.noreply.github.com> Date: Tue, 25 Jul 2023 02:30:36 +0300 Subject: [PATCH] Initial commit --- .github/workflows/publish.yml | 21 +++ .gitignore | 6 + CapMonster.Cloud.Tests/BaseMethodTests.cs | 12 ++ .../CapMonster.Cloud.Tests.csproj | 36 +++++ .../Resources/imagetotext.png | Bin 0 -> 6767 bytes CapMonster.Cloud.Tests/TaskTests.cs | 110 +++++++++++++ CapMonster.Cloud.Tests/Usings.cs | 3 + CapMonster.Cloud.sln | 22 +++ CapMonster.Cloud/CapMonster.Cloud.csproj | 25 +++ CapMonster.Cloud/CapMonsterClient.cs | 146 ++++++++++++++++++ CapMonster.Cloud/Endpoints.cs | 10 ++ CapMonster.Cloud/Models/CreateTaskResponse.cs | 13 ++ CapMonster.Cloud/Models/ErrorResponse.cs | 15 ++ CapMonster.Cloud/Models/GetBalance.cs | 10 ++ CapMonster.Cloud/Models/Proxy.cs | 35 +++++ CapMonster.Cloud/Models/TaskResponse.cs | 17 ++ CapMonster.Cloud/Models/VanillaTask.cs | 31 ++++ CapMonster.Cloud/Tasks/FunCaptchaTask.cs | 64 ++++++++ CapMonster.Cloud/Tasks/GeeTestTask.cs | 81 ++++++++++ CapMonster.Cloud/Tasks/HCaptchaTask.cs | 57 +++++++ CapMonster.Cloud/Tasks/ImageToTextTask.cs | 77 +++++++++ .../Tasks/ReCaptchaV2EnterpriseTask.cs | 82 ++++++++++ CapMonster.Cloud/Tasks/ReCaptchaV2Task.cs | 66 ++++++++ CapMonster.Cloud/Tasks/ReCaptchaV3Task.cs | 55 +++++++ .../Tasks/Responses/FunCaptchaResponse.cs | 11 ++ .../Tasks/Responses/GeeTestResponse.cs | 19 +++ .../Tasks/Responses/HCaptchaResponse.cs | 14 ++ .../Tasks/Responses/ImageToTextResponse.cs | 11 ++ .../Tasks/Responses/ReCaptchaV2Response.cs | 11 ++ .../Tasks/Responses/ReCaptchaV3Response.cs | 6 + .../Tasks/Responses/TurnstileResponse.cs | 13 ++ CapMonster.Cloud/Tasks/TurnstileTask.cs | 70 +++++++++ .../Utilities/CapMonsterException.cs | 18 +++ .../Utilities/CloudFlareTaskType.cs | 7 + CapMonster.Cloud/Utilities/ICookieTask.cs | 6 + CapMonster.Cloud/Utilities/IProxyTask.cs | 6 + CapMonster.Cloud/Utilities/ITask.cs | 6 + CapMonster.Cloud/Utilities/ITaskResponse.cs | 6 + CapMonster.Cloud/Utilities/IUserAgentTask.cs | 6 + LICENSE | 21 +++ README.md | 61 ++++++++ 41 files changed, 1286 insertions(+) create mode 100644 .github/workflows/publish.yml create mode 100644 .gitignore create mode 100644 CapMonster.Cloud.Tests/BaseMethodTests.cs create mode 100644 CapMonster.Cloud.Tests/CapMonster.Cloud.Tests.csproj create mode 100644 CapMonster.Cloud.Tests/Resources/imagetotext.png create mode 100644 CapMonster.Cloud.Tests/TaskTests.cs create mode 100644 CapMonster.Cloud.Tests/Usings.cs create mode 100644 CapMonster.Cloud.sln create mode 100644 CapMonster.Cloud/CapMonster.Cloud.csproj create mode 100644 CapMonster.Cloud/CapMonsterClient.cs create mode 100644 CapMonster.Cloud/Endpoints.cs create mode 100644 CapMonster.Cloud/Models/CreateTaskResponse.cs create mode 100644 CapMonster.Cloud/Models/ErrorResponse.cs create mode 100644 CapMonster.Cloud/Models/GetBalance.cs create mode 100644 CapMonster.Cloud/Models/Proxy.cs create mode 100644 CapMonster.Cloud/Models/TaskResponse.cs create mode 100644 CapMonster.Cloud/Models/VanillaTask.cs create mode 100644 CapMonster.Cloud/Tasks/FunCaptchaTask.cs create mode 100644 CapMonster.Cloud/Tasks/GeeTestTask.cs create mode 100644 CapMonster.Cloud/Tasks/HCaptchaTask.cs create mode 100644 CapMonster.Cloud/Tasks/ImageToTextTask.cs create mode 100644 CapMonster.Cloud/Tasks/ReCaptchaV2EnterpriseTask.cs create mode 100644 CapMonster.Cloud/Tasks/ReCaptchaV2Task.cs create mode 100644 CapMonster.Cloud/Tasks/ReCaptchaV3Task.cs create mode 100644 CapMonster.Cloud/Tasks/Responses/FunCaptchaResponse.cs create mode 100644 CapMonster.Cloud/Tasks/Responses/GeeTestResponse.cs create mode 100644 CapMonster.Cloud/Tasks/Responses/HCaptchaResponse.cs create mode 100644 CapMonster.Cloud/Tasks/Responses/ImageToTextResponse.cs create mode 100644 CapMonster.Cloud/Tasks/Responses/ReCaptchaV2Response.cs create mode 100644 CapMonster.Cloud/Tasks/Responses/ReCaptchaV3Response.cs create mode 100644 CapMonster.Cloud/Tasks/Responses/TurnstileResponse.cs create mode 100644 CapMonster.Cloud/Tasks/TurnstileTask.cs create mode 100644 CapMonster.Cloud/Utilities/CapMonsterException.cs create mode 100644 CapMonster.Cloud/Utilities/CloudFlareTaskType.cs create mode 100644 CapMonster.Cloud/Utilities/ICookieTask.cs create mode 100644 CapMonster.Cloud/Utilities/IProxyTask.cs create mode 100644 CapMonster.Cloud/Utilities/ITask.cs create mode 100644 CapMonster.Cloud/Utilities/ITaskResponse.cs create mode 100644 CapMonster.Cloud/Utilities/IUserAgentTask.cs create mode 100644 LICENSE create mode 100644 README.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..092b907 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,21 @@ +name: Publish to NuGet Package Registry + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "6.x" + - name: Install dependencies + run: dotnet restore + - name: Pack the Package + run: dotnet pack ./CapMonster.Cloud/CapMonster.Cloud.csproj --configuration Release -p:PackageVersion=${{ github.event.release.tag_name }} + - name: PushNuget + run: dotnet nuget push **/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_PUBLISH_KEY }} --skip-duplicate \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96a13ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +**/bin +**/obj +**/.idea +**/.runsettings +**/.git +**/*.sln.DotSettings.user \ No newline at end of file diff --git a/CapMonster.Cloud.Tests/BaseMethodTests.cs b/CapMonster.Cloud.Tests/BaseMethodTests.cs new file mode 100644 index 0000000..b8fc63f --- /dev/null +++ b/CapMonster.Cloud.Tests/BaseMethodTests.cs @@ -0,0 +1,12 @@ +namespace CapMonster.Cloud.Tests; + +public class BaseMethodTests +{ + [Fact] + public async void TestBalance() + { + var client = new CapMonsterClient(Environment.GetEnvironmentVariable("APIKEY")!); + var balance = await client.GetBalanceAsync(); + Assert.IsType(balance); + } +} \ No newline at end of file diff --git a/CapMonster.Cloud.Tests/CapMonster.Cloud.Tests.csproj b/CapMonster.Cloud.Tests/CapMonster.Cloud.Tests.csproj new file mode 100644 index 0000000..d2fe462 --- /dev/null +++ b/CapMonster.Cloud.Tests/CapMonster.Cloud.Tests.csproj @@ -0,0 +1,36 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + PreserveNewest + + + + diff --git a/CapMonster.Cloud.Tests/Resources/imagetotext.png b/CapMonster.Cloud.Tests/Resources/imagetotext.png new file mode 100644 index 0000000000000000000000000000000000000000..71d66f7484a121907fa6f3e7507562ae39c755f2 GIT binary patch literal 6767 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 + var response = await client.JoinTaskResult(id); + Assert.NotNull(response.Text); + Assert.IsType(response.Text); + } + + [Theory] + [InlineData("https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd")] + [InlineData("https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high", "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd", true)] + public async void ReCaptchaV2_Test(string websiteUrl, string websiteKey, bool useProxy = false) + { + var client = new CapMonsterClient(Environment.GetEnvironmentVariable("APIKEY")!); + UseProxy(client, useProxy); + var task = new ReCaptchaV2Task(websiteUrl, websiteKey); + var id = await client.CreateTask(task); + Assert.IsType(id); + var response = await client.JoinTaskResult(id); + Assert.NotNull(response.GReCaptchaResponse); + Assert.IsType(response.GReCaptchaResponse); + } + + [Theory] + [InlineData("https://lessons.zennolab.com/captchas/recaptcha/v3.php?level=beta", "6Le0xVgUAAAAAIt20XEB4rVhYOODgTl00d8juDob")] + [InlineData("https://lessons.zennolab.com/captchas/recaptcha/v3.php?level=beta", "6Le0xVgUAAAAAIt20XEB4rVhYOODgTl00d8juDob", true)] + public async void ReCaptchaV3_Test(string websiteUrl, string websiteKey, bool useProxy = false) + { + var client = new CapMonsterClient(Environment.GetEnvironmentVariable("APIKEY")!); + UseProxy(client, useProxy); + var task = new ReCaptchaV3Task(websiteUrl, websiteKey); + var id = await client.CreateTask(task); + Assert.IsType(id); + var response = await client.JoinTaskResult(id); + Assert.NotNull(response.GReCaptchaResponse); + Assert.IsType(response.GReCaptchaResponse); + } + + [Theory] + [InlineData("https://funcaptcha.com/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC")] + [InlineData("https://funcaptcha.com/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", true)] + public async void FunCaptcha_Test(string websiteUrl, string websiteKey, bool useProxy = false) + { + var client = new CapMonsterClient(Environment.GetEnvironmentVariable("APIKEY")!); + UseProxy(client, useProxy); + var task = new FunCaptchaTask(websiteUrl, websiteKey); + var id = await client.CreateTask(task); + Assert.IsType(id); + var response = await client.JoinTaskResult(id); + Assert.NotNull(response.Token); + Assert.IsType(response.Token); + } + + [Theory] + [InlineData("https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", "472fc7af-86a4-4382-9a49-ca9090474471")] + [InlineData("https://lessons.zennolab.com/captchas/hcaptcha/?level=easy", "472fc7af-86a4-4382-9a49-ca9090474471", true)] + public async void HCaptcha_Test(string websiteUrl, string websiteKey, bool useProxy = false) + { + var client = new CapMonsterClient(Environment.GetEnvironmentVariable("APIKEY")!); + UseProxy(client, useProxy); + var task = new FunCaptchaTask(websiteUrl, websiteKey); + var id = await client.CreateTask(task); + Assert.IsType(id); + var response = await client.JoinTaskResult(id); + Assert.NotNull(response.GReCaptchaResponse); + Assert.IsType(response.GReCaptchaResponse); + Assert.NotNull(response.ResponseKey); + Assert.IsType(response.ResponseKey); + Assert.NotNull(response.UserAgent); + Assert.IsType(response.UserAgent); + } + + [Theory] + [InlineData("https://nowsecure.nl", "0x4AAAAAAADnPIDROrmt1Wwj")] + [InlineData("https://nowsecure.nl", "0x4AAAAAAADnPIDROrmt1Wwj", true)] + public async void Turnstile_Test(string websiteUrl, string websiteKey, bool useProxy = false) + { + var client = new CapMonsterClient(Environment.GetEnvironmentVariable("APIKEY")!); + UseProxy(client, useProxy); + var task = new TurnstileTask(websiteUrl, websiteKey); + var id = await client.CreateTask(task); + Assert.IsType(id); + var response = await client.JoinTaskResult(id); + Assert.NotNull(response.Token); + Assert.IsType(response.Token); + } + + private static void UseProxy(CapMonsterClient client, bool useProxy) + { + if (useProxy) + client.SetProxy(new Proxy(Environment.GetEnvironmentVariable("PROXY_TYPE")!, + Environment.GetEnvironmentVariable("PROXY_ADDRESS")!, + int.Parse(Environment.GetEnvironmentVariable("PROXY_PORT")!), + Environment.GetEnvironmentVariable("PROXY_LOGIN")!, + Environment.GetEnvironmentVariable("PROXY_PASSWORD")!)); + } +} \ No newline at end of file diff --git a/CapMonster.Cloud.Tests/Usings.cs b/CapMonster.Cloud.Tests/Usings.cs new file mode 100644 index 0000000..166cace --- /dev/null +++ b/CapMonster.Cloud.Tests/Usings.cs @@ -0,0 +1,3 @@ +global using Xunit; +global using System; +global using CapMonster.Cloud; \ No newline at end of file diff --git a/CapMonster.Cloud.sln b/CapMonster.Cloud.sln new file mode 100644 index 0000000..fb6031c --- /dev/null +++ b/CapMonster.Cloud.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapMonster.Cloud", "CapMonster.Cloud\CapMonster.Cloud.csproj", "{BB5A1167-6BC5-41F4-9895-E8D282E15558}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapMonster.Cloud.Tests", "CapMonster.Cloud.Tests\CapMonster.Cloud.Tests.csproj", "{C7741552-9193-4717-9695-40343A339397}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BB5A1167-6BC5-41F4-9895-E8D282E15558}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB5A1167-6BC5-41F4-9895-E8D282E15558}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB5A1167-6BC5-41F4-9895-E8D282E15558}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB5A1167-6BC5-41F4-9895-E8D282E15558}.Release|Any CPU.Build.0 = Release|Any CPU + {C7741552-9193-4717-9695-40343A339397}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7741552-9193-4717-9695-40343A339397}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7741552-9193-4717-9695-40343A339397}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7741552-9193-4717-9695-40343A339397}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/CapMonster.Cloud/CapMonster.Cloud.csproj b/CapMonster.Cloud/CapMonster.Cloud.csproj new file mode 100644 index 0000000..9108022 --- /dev/null +++ b/CapMonster.Cloud/CapMonster.Cloud.csproj @@ -0,0 +1,25 @@ + + + + net6.0 + enable + enable + CapMonster.Cloud + 1.0.0-alpha + Alperen Sert + Alperen Sert + Capmonster,ReCaptcha v2 solver, Bypass captcha, anti-captcha, + Capmonster.cloud Library for .NET Core + MIT + https://github.com/alperensert/CapMonster.Cloud + https://github.com/alperensert/CapMonster.Cloud + README.md + git + + + + + + + + diff --git a/CapMonster.Cloud/CapMonsterClient.cs b/CapMonster.Cloud/CapMonsterClient.cs new file mode 100644 index 0000000..c1df7fb --- /dev/null +++ b/CapMonster.Cloud/CapMonsterClient.cs @@ -0,0 +1,146 @@ +using System.Text; +using CapMonster.Cloud.Models; +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace CapMonster.Cloud; + +public class CapMonsterClient +{ + private readonly HttpClient _httpClient; + + private Proxy? _proxy; + + private static readonly Uri Host = new Uri("https://api.capmonster.cloud"); + + private readonly string _clientKey; + + public CapMonsterClient(string clientKey) + { + _clientKey = clientKey; + _httpClient = new HttpClient() + { + BaseAddress = Host + }; + } + + public async Task CreateTask(ITask task) + { + var t = new VanillaTask(_clientKey); + t.UseSoftId(); + string data; + switch (task) + { + case IProxyTask when IsProxyActive(): + { + var vt = JObject.FromObject(t); + var to = JObject.FromObject(task); + var p = JObject.FromObject(_proxy!); + p.Merge(to); + vt["task"] = p; + data = vt.ToString(); + break; + } + case IProxyTask when !IsProxyActive(): + { + var vt = JObject.FromObject(t); + var to = JObject.FromObject(task); + to["type"] += "Proxyless"; + vt["task"] = to; + data = vt.ToString(); + break; + } + default: + { + var vt = JObject.FromObject(t); + var to = JObject.FromObject(task); + vt["task"] = to; + data = vt.ToString(); + break; + } + } + var r = await CheckResponse(await MakeRequest(Endpoints.CreateTask, data)); + return r.TaskId; + } + + public async Task GetBalanceAsync() + { + var data = new VanillaTask(_clientKey); + var response = await MakeRequest(Endpoints.Balance, JsonConvert.SerializeObject(data)); + var r = await CheckResponse(response); + return r.Balance; + } + + public async Task> GetTaskResultAsync(int taskId) where T : ITaskResponse + { + var vt = new VanillaTask(_clientKey) + { + TaskId = taskId + }; + var response = await MakeRequest(Endpoints.TaskResult, JsonConvert.SerializeObject(vt)); + var r = await CheckResponse>(response); + return r; + } + + public async Task JoinTaskResult(int taskId, int maximumTime = 120) where T : ITaskResponse + { + var vt = new VanillaTask(_clientKey) + { + TaskId = taskId + }; + for (var i = 0; i < maximumTime; i++) + { + var response = await MakeRequest(Endpoints.TaskResult, JsonConvert.SerializeObject(vt)); + var r = await CheckResponse>(response); + if (IsReady(r) && r.Solution != null) + return r.Solution; + await Task.Delay(1000); + } + throw new CapMonsterException(-1, "MAXIMUM_TIME_EXCEED", "Maximum time is exceed."); + } + + public bool IsProxyActive() => _proxy != null; + + public void SetProxy(Proxy proxy) => _proxy = proxy; + + public void DisableProxy() => _proxy = null; + + private static bool IsReady(TaskResponse response) where T : ITaskResponse => response.Status == "ready"; + + private async Task MakeRequest(string endpoint, string data) + { + var dataString = new StringContent(data, Encoding.UTF8, "application/json"); + HttpResponseMessage response; + try + { + response = await _httpClient.PostAsync(endpoint, dataString); + } + catch (Exception) + { + throw new CapMonsterException(-1, "UNABLE_TO_MAKE_REQUEST", "Something is happened while making request"); + } + CheckResponse(JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync())); + return response; + } + + private static void CheckResponse(ErrorResponse? response) + { + if (response == null) + throw new CapMonsterException(-1, "NO_RESPONSE", "The response is empty."); + if (response.ErrorId != 0) + throw new CapMonsterException(response.ErrorId, response.ErrorCode!, response.ErrorDescription!); + } + + private static async Task CheckResponse(HttpResponseMessage response) where T : ErrorResponse + { + try + { + return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync())!; + } + catch(Exception) + { + throw new CapMonsterException(-2, "INVALID_RESPONSE", "The response is not valid."); + } + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Endpoints.cs b/CapMonster.Cloud/Endpoints.cs new file mode 100644 index 0000000..073f457 --- /dev/null +++ b/CapMonster.Cloud/Endpoints.cs @@ -0,0 +1,10 @@ +namespace CapMonster.Cloud; + +internal class Endpoints +{ + public const string Balance = "/getBalance"; + + public const string CreateTask = "/createTask"; + + public const string TaskResult = "/getTaskResult"; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Models/CreateTaskResponse.cs b/CapMonster.Cloud/Models/CreateTaskResponse.cs new file mode 100644 index 0000000..e313c61 --- /dev/null +++ b/CapMonster.Cloud/Models/CreateTaskResponse.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Models; + +public class CreateTaskResponse : ErrorResponse +{ + [JsonRequired] + [JsonProperty("taskId")] + public int TaskId { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } = null!; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Models/ErrorResponse.cs b/CapMonster.Cloud/Models/ErrorResponse.cs new file mode 100644 index 0000000..3e89131 --- /dev/null +++ b/CapMonster.Cloud/Models/ErrorResponse.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Models; + +public class ErrorResponse +{ + [JsonProperty("errorId")] + public int ErrorId { get; set; } + + [JsonProperty("errorCode", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ErrorCode { get; set; } + + [JsonProperty("errorDescription", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ErrorDescription { get; set; } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Models/GetBalance.cs b/CapMonster.Cloud/Models/GetBalance.cs new file mode 100644 index 0000000..74845a0 --- /dev/null +++ b/CapMonster.Cloud/Models/GetBalance.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Models; + +public class GetBalance : ErrorResponse +{ + [JsonRequired] + [JsonProperty("balance", DefaultValueHandling = DefaultValueHandling.Ignore)] + public double Balance { get; set; } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Models/Proxy.cs b/CapMonster.Cloud/Models/Proxy.cs new file mode 100644 index 0000000..a535402 --- /dev/null +++ b/CapMonster.Cloud/Models/Proxy.cs @@ -0,0 +1,35 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Models; + +public class Proxy : IProxyTask +{ + [JsonProperty("proxyType", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyType { get; set; } + + [JsonProperty("proxyAddress", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyAddress { get; set; } + + [JsonProperty("proxyPort", NullValueHandling = NullValueHandling.Ignore)] + public int ProxyPort { get; set; } + + [JsonProperty("proxyLogin", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyLogin { get; set; } + + [JsonProperty("proxyPassword", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyPassword { get; set; } + + public Proxy(string proxyType, + string proxyAddress, + int proxyPort, + string? proxyLogin = null, + string? proxyPassword = null) + { + ProxyType = proxyType; + ProxyAddress = proxyAddress; + ProxyPort = proxyPort; + ProxyLogin = proxyLogin; + ProxyPassword = proxyPassword; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Models/TaskResponse.cs b/CapMonster.Cloud/Models/TaskResponse.cs new file mode 100644 index 0000000..65f31b0 --- /dev/null +++ b/CapMonster.Cloud/Models/TaskResponse.cs @@ -0,0 +1,17 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Models; + +public class TaskResponse : ErrorResponse where T : ITaskResponse +{ + [JsonRequired] + [JsonProperty("status")] + public string Status { get; set; } = null!; + + [JsonProperty("solution", NullValueHandling = NullValueHandling.Include)] + public T? Solution { get; set; } + + [JsonProperty("taskId")] + public int TaskId { get; set; } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Models/VanillaTask.cs b/CapMonster.Cloud/Models/VanillaTask.cs new file mode 100644 index 0000000..e801c6d --- /dev/null +++ b/CapMonster.Cloud/Models/VanillaTask.cs @@ -0,0 +1,31 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Models; + +internal class VanillaTask +{ + /// + /// The client key (api key) which belongs to your account + /// + [JsonRequired] + [JsonProperty("clientKey", NullValueHandling = NullValueHandling.Ignore)] + public string ClientKey { get; set; } + + [JsonProperty("task", NullValueHandling = NullValueHandling.Ignore)] + public ITask? Task { get; private set; } + + [JsonProperty("taskId", NullValueHandling = NullValueHandling.Ignore)] + public int? TaskId { get; set; } + + [JsonProperty("softId", NullValueHandling = NullValueHandling.Ignore)] + private int? SoftId { get; set; } + + public VanillaTask(string clientKey, ITask? task = null) + { + Task = task; + ClientKey = clientKey; + } + + public void UseSoftId() => SoftId = 30; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/FunCaptchaTask.cs b/CapMonster.Cloud/Tasks/FunCaptchaTask.cs new file mode 100644 index 0000000..a5ff1d8 --- /dev/null +++ b/CapMonster.Cloud/Tasks/FunCaptchaTask.cs @@ -0,0 +1,64 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +/// +/// This task type is used to solve FunCaptcha +/// +public class FunCaptchaTask : ITask, IProxyTask, IUserAgentTask +{ + [JsonProperty("type")] + private readonly string _type = "FunCaptchaTask"; + + /// + /// Address of a webpage with Fun captcha + /// + [JsonProperty("websiteURL")] + public string WebsiteUrl { get; set; } + + /// + /// Fun captcha website key. + /// + [JsonProperty("websitePublicKey")] + public string WebsitePublicKey { get; set; } + + /// + /// A special subdomain of fun captcha.com, from which the JS captcha widget should be loaded. Most FunCaptcha installations work from shared domains. + /// + [JsonProperty("funcaptchaApiJSSubdomain")] + public string? ApiJsSubdomain { 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. + /// + [JsonProperty("data")] + public string? Data { 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", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + /// + /// Prepare a FunCaptcha task + /// + /// Address of a webpage with Funcaptcha + /// Funcaptcha website key. + /// A special subdomain of funcaptcha.com, from which the JS captcha widget should be loaded. Most FunCaptcha installations work from shared domains. + /// 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. + public FunCaptchaTask(string websiteUrl, + string websitePublicKey, + string? apiJsSubdomain = null, + string? data = null, + string? userAgent = null) + { + WebsiteUrl = websiteUrl; + WebsitePublicKey = websitePublicKey; + ApiJsSubdomain = apiJsSubdomain; + Data = data; + UserAgent = userAgent; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/GeeTestTask.cs b/CapMonster.Cloud/Tasks/GeeTestTask.cs new file mode 100644 index 0000000..c2a1e28 --- /dev/null +++ b/CapMonster.Cloud/Tasks/GeeTestTask.cs @@ -0,0 +1,81 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +/// +/// This task is used to solve GeeTest. +/// +public class GeeTestTask : ITask, IProxyTask, IUserAgentTask, ICookieTask +{ + [JsonProperty("type")] + private readonly string _type = "GeeTestTask"; + + /// + /// Address of a webpage with GeeTest + /// + public string WebsiteUrl { get; set; } + + /// + /// The domain public key, rarely updated. + /// + [JsonProperty("gt", NullValueHandling = NullValueHandling.Ignore)] + public string? Gt { get; set; } + + /// + /// Changing token key. Make sure you grab a fresh one for each captcha; otherwise, you'll be charged for an error task. + /// + [JsonProperty("challenge", NullValueHandling = NullValueHandling.Ignore)] + public string? Challenge { get; set; } + + /// + /// Optional API subdomain. May be required for some implementations. + /// + [JsonProperty("geetestApiServerSubdomain", NullValueHandling = NullValueHandling.Ignore)] + public string? ApiServerSubdomain { 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", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + [JsonProperty("initParameters")] + public object? InitParameters { get; set; } + + [JsonProperty("geetestGetLib", NullValueHandling = NullValueHandling.Ignore)] + public string? GeeTestGetLib { get; set; } + + [JsonProperty("version")] + public int? Version { get; set; } + + /// + /// Prepare a GeeTest task + /// + /// Address of a webpage with GeeTest + /// The domain public key, rarely updated. + /// Changing token key. Make sure you grab a fresh one for each captcha; otherwise, you'll be charged for an error task. + /// Optional API subdomain. May be required for some implementations. + /// Additional parameters for version 4. + /// Optional parameter. May be required for some sites. Send JSON as a string. + /// Version number (default is 3). Possible values: 3, 4. + /// Browser's User-Agent which is used in emulation. + public GeeTestTask(string websiteUrl, + string? gt = null, + string? challenge = null, + string? apiServerSubdomain = null, + object? initParameters = null, + string? geeTestGetLib = null, + int? version = null, + string? userAgent = null) + { + WebsiteUrl = websiteUrl; + Gt = gt; + Challenge = challenge; + ApiServerSubdomain = apiServerSubdomain; + UserAgent = userAgent; + Version = version; + InitParameters = initParameters; + GeeTestGetLib = geeTestGetLib; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/HCaptchaTask.cs b/CapMonster.Cloud/Tasks/HCaptchaTask.cs new file mode 100644 index 0000000..4fc1b46 --- /dev/null +++ b/CapMonster.Cloud/Tasks/HCaptchaTask.cs @@ -0,0 +1,57 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +/// +/// This task type is used to solve HCaptcha. +/// +public class HCaptchaTask : ITask, IProxyTask, IUserAgentTask +{ + [JsonProperty("type")] + private readonly string _type = "HCaptchaTask"; + + /// + /// Address of a webpage with hCaptcha + /// + [JsonRequired] + [JsonProperty("websiteURL")] + public string WebsiteUrl { get; set; } + + /// + /// hCaptcha website key + /// + [JsonRequired] + [JsonProperty("websiteKey")] + public string WebsiteKey { get; set; } + + /// + /// Use true for invisible version of h captcha + /// + [JsonProperty("isInvisible", NullValueHandling = NullValueHandling.Ignore)] + public bool? IsInvisible { 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", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + /// + /// Prepare a HCaptcha task. + /// + /// Address of a webpage with hCaptcha + /// hCaptcha website key + /// Use true for invisible version of h captcha + /// Browser's User-Agent which is used in emulation. + public HCaptchaTask(string websiteUrl, + string websiteKey, + bool? isInvisible = null, + string? userAgent = null) + { + WebsiteUrl = websiteUrl; + WebsiteKey = websiteKey; + IsInvisible = isInvisible; + UserAgent = userAgent; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/ImageToTextTask.cs b/CapMonster.Cloud/Tasks/ImageToTextTask.cs new file mode 100644 index 0000000..8a5e8a2 --- /dev/null +++ b/CapMonster.Cloud/Tasks/ImageToTextTask.cs @@ -0,0 +1,77 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +/// +/// This task type is used to recognize image captcha. +/// +public class ImageToTextTask : ITask +{ + /// + /// Task's type. + /// + [JsonProperty("type")] + private readonly string _type = "ImageToTextTask"; + + /// + /// Base64 encoded content of the image (without line breaks) + /// + [JsonProperty("body")] + public string Body { get; set; } + + /// + /// Specifies the module. Currently, the supported modules are common and queue it. + /// + [JsonProperty("CapMonsterModule")] + public string? Module { get; set; } + + /// + /// Captcha recognition threshold with a possible value from 0 to 100. For example,
+ /// if recognizingThreshold was set to 90 and the task was solved with a confidence of 80, you won't be charged. + ///
+ /// 0-100 + [JsonProperty("recognizingThreshold")] + public int? RecognizingThreshold { get; set; } + + /// + /// Case sensitive or not + /// + [JsonProperty(nameof(Case))] + public bool? Case { get; set; } + + /// + /// 1 - if captcha contains numbers only + /// + [JsonProperty("numeric")] + public int Numeric { get; set; } + + /// + /// true if captcha requires a mathematical operation (for example: captcha 2 + 6 = will return a value of 8) + /// + [JsonProperty("math", DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] + public bool? Math { get; set; } + + /// + /// Prepare an image to text task. + /// + /// Base64 encoded content of the image (without line breaks) + /// Specifies the module. Currently, the supported modules are common and queue it. + /// + /// Captcha recognition threshold with a possible value from 0 to 100. For example,
+ /// if recognizingThreshold was set to 90 and the task was solved with a confidence of 80, you won't be charged. + /// + /// Case sensitive or not + /// true - if captcha contains numbers only + /// true if captcha requires a mathematical operation (for example: captcha 2 + 6 = will return a value of 8) + public ImageToTextTask(string body, string? module = null, int? recognizingThreshold = null, + bool? caseSensitive = null, bool? numeric = null, bool? math = null) + { + Body = body; + Module = module; + RecognizingThreshold = recognizingThreshold; + Case = caseSensitive; + Numeric = numeric == true ? 1 : 0; + Math = math; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/ReCaptchaV2EnterpriseTask.cs b/CapMonster.Cloud/Tasks/ReCaptchaV2EnterpriseTask.cs new file mode 100644 index 0000000..1791bfa --- /dev/null +++ b/CapMonster.Cloud/Tasks/ReCaptchaV2EnterpriseTask.cs @@ -0,0 +1,82 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +/// +/// This task type is used to solve the ReCaptchaV2 Enterprise version. +/// +public class ReCaptchaV2Enterprise : ITask, IProxyTask, IUserAgentTask, ICookieTask +{ + /// + /// Task's type. + /// + [JsonProperty("type")] + private readonly string _type = "ReCaptchaV2EnterpriseTask"; + + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonRequired] + [JsonProperty("websiteURL")] + public string WebsiteUrl { get; set; } + + /// + /// ReCaptchaV2 website key. + /// + [JsonRequired] + [JsonProperty("websiteKey")] + public string WebsiteKey { get; set; } + + /// + /// Some implementations of the reCAPTCHA Enterprise widget + /// may contain additional parameters that are passed to the + /// “gRecaptcha.enterprise.render” method along with the site key. + /// + [JsonProperty("enterprisePayload", NullValueHandling = NullValueHandling.Ignore)] + public object? EnterprisePayload { get; set; } + + /// + /// Domain address from which to load reCAPTCHA Enterprise. + /// + [JsonProperty("apiDomain", NullValueHandling = NullValueHandling.Ignore)] + public string? ApiDomain { 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", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + /// + /// Additional cookies which we must use during interaction with target page or Google. + /// + [JsonProperty("cookies", NullValueHandling = NullValueHandling.Ignore)] + public string? Cookies { get; set; } + + /// + /// Prepare a ReCaptchaV2 Enterprise task. + /// + /// Address of a webpage with Google ReCaptcha + /// ReCaptchaV2 website key. + /// Some implementations of the reCAPTCHA Enterprise widget + /// may contain additional parameters that are passed to the + /// “gRecaptcha.enterprise.render” method along with the site key. + /// Domain address from which to load reCAPTCHA Enterprise. + /// Additional cookies which we must use during interaction with target page or Google. + /// Browser's User-Agent which is used in emulation. + public ReCaptchaV2Enterprise(string websiteUrl, + string websiteKey, + object? enterprisePayload = null, + string? apiDomain = null, + string? userAgent = null, + string? cookies= null) + { + WebsiteUrl = websiteUrl; + WebsiteKey = websiteKey; + EnterprisePayload = enterprisePayload; + ApiDomain = apiDomain; + UserAgent = userAgent; + Cookies = cookies; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/ReCaptchaV2Task.cs b/CapMonster.Cloud/Tasks/ReCaptchaV2Task.cs new file mode 100644 index 0000000..ed3d1aa --- /dev/null +++ b/CapMonster.Cloud/Tasks/ReCaptchaV2Task.cs @@ -0,0 +1,66 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +/// +/// This task type is used to solve the reCaptchaV2 version +/// +public class ReCaptchaV2Task : ITask, IUserAgentTask, ICookieTask, IProxyTask +{ + [JsonProperty("type")] + private readonly string _type = "NoCaptchaTask"; + + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonRequired] + [JsonProperty("websiteURL")] + public string WebsiteUrl { get; set; } + + /// + /// Recaptcha website key.
+ ///
+ [JsonRequired] + [JsonProperty("websiteKey")] + public string WebsiteKey { get; set; } + + /// + /// Some custom implementations may contain additional "data-s" parameter in ReCaptcha2 div + /// + [JsonProperty("recaptchaDataSValue", NullValueHandling = NullValueHandling.Ignore)] + public string? RecaptchaDataSValue { 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", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + /// + /// Additional cookies which we must use during interaction with target page or Google. + /// + [JsonProperty("cookies", NullValueHandling = NullValueHandling.Ignore)] + public string? Cookies { get; set; } + + /// + /// Prepare a ReCaptchaV2 task + /// + /// Address of a webpage with Google ReCaptcha + /// Recaptcha website key.
+ /// Some custom implementations may contain additional "data-s" parameter in ReCaptcha2 div + /// Browser's User-Agent which is used in emulation. + /// Additional cookies which we must use during interaction with target page or Google. + public ReCaptchaV2Task(string websiteUrl, + string websiteKey, + string? recaptchaDataSValue = null, + string? userAgent = null, + string? cookies = null) + { + WebsiteUrl = websiteUrl; + WebsiteKey = websiteKey; + RecaptchaDataSValue = recaptchaDataSValue; + UserAgent = userAgent; + Cookies = cookies; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/ReCaptchaV3Task.cs b/CapMonster.Cloud/Tasks/ReCaptchaV3Task.cs new file mode 100644 index 0000000..de408fd --- /dev/null +++ b/CapMonster.Cloud/Tasks/ReCaptchaV3Task.cs @@ -0,0 +1,55 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +public class ReCaptchaV3Task : ITask +{ + [JsonProperty("type")] + private readonly string _type = "ReCaptchaV3Task"; + + /// + /// Address of a webpage with Google ReCaptcha + /// + [JsonRequired] + [JsonProperty("websiteURL")] + public string WebsiteUrl { get; set; } + + /// + /// ReCaptchaV3 website key. + /// + [JsonRequired] + [JsonProperty("websiteKey")] + public string WebsiteKey { get; set; } + + /// + /// Widget action value. Website owner defines what user is doing on the page through this parameter. Default value: verify + /// + [JsonRequired] + [JsonProperty("pageAction", NullValueHandling = NullValueHandling.Ignore)] + public string? PageAction { get; set; } + + /// + /// Value from 0.1 to 0.9 + /// + [JsonProperty("minScore", NullValueHandling = NullValueHandling.Ignore)] + public double? MinimumScore { get; set; } + + /// + /// Prepare a ReCaptchaV3 task. + /// + /// Address of a webpage with Google ReCaptcha + /// ReCaptchaV3 website key. + /// Widget action value. Website owner defines what user is doing on the page through this parameter. Default value: verify + /// Value from 0.1 to 0.9 + public ReCaptchaV3Task(string websiteUrl, + string websiteKey, + string? pageAction = null, + double? minimumScore = null) + { + WebsiteKey = websiteKey; + WebsiteUrl = websiteUrl; + PageAction = pageAction; + MinimumScore = minimumScore; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/Responses/FunCaptchaResponse.cs b/CapMonster.Cloud/Tasks/Responses/FunCaptchaResponse.cs new file mode 100644 index 0000000..fab8719 --- /dev/null +++ b/CapMonster.Cloud/Tasks/Responses/FunCaptchaResponse.cs @@ -0,0 +1,11 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks.Responses; + +public class FunCaptchaResponse : ITaskResponse +{ + [JsonRequired] + [JsonProperty("token")] + public string Token { get; set; } = null!; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/Responses/GeeTestResponse.cs b/CapMonster.Cloud/Tasks/Responses/GeeTestResponse.cs new file mode 100644 index 0000000..1b61c3d --- /dev/null +++ b/CapMonster.Cloud/Tasks/Responses/GeeTestResponse.cs @@ -0,0 +1,19 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks.Responses; + +public class GeeTestResponse : ITaskResponse +{ + [JsonRequired] + [JsonProperty("challenge")] + public string Challenge { get; init; } = null!; + + [JsonRequired] + [JsonProperty("validate")] + public string Validate { get; init; } = null!; + + [JsonRequired] + [JsonProperty("seccode")] + public string SecCode { get; init; } = null!; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/Responses/HCaptchaResponse.cs b/CapMonster.Cloud/Tasks/Responses/HCaptchaResponse.cs new file mode 100644 index 0000000..a303b87 --- /dev/null +++ b/CapMonster.Cloud/Tasks/Responses/HCaptchaResponse.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks.Responses; + +public class HCaptchaResponse : ReCaptchaV2Response +{ + [JsonRequired] + [JsonProperty("respKey")] + public string ResponseKey { get; init; } = null!; + + [JsonRequired] + [JsonProperty("userAgent")] + public string UserAgent { get; init; } = null!; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/Responses/ImageToTextResponse.cs b/CapMonster.Cloud/Tasks/Responses/ImageToTextResponse.cs new file mode 100644 index 0000000..a9c6f48 --- /dev/null +++ b/CapMonster.Cloud/Tasks/Responses/ImageToTextResponse.cs @@ -0,0 +1,11 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks.Responses; + +public class ImageToTextResponse : ITaskResponse +{ + [JsonRequired] + [JsonProperty("text")] + public string Text { get; init; } = null!; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/Responses/ReCaptchaV2Response.cs b/CapMonster.Cloud/Tasks/Responses/ReCaptchaV2Response.cs new file mode 100644 index 0000000..62c16eb --- /dev/null +++ b/CapMonster.Cloud/Tasks/Responses/ReCaptchaV2Response.cs @@ -0,0 +1,11 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks.Responses; + +public class ReCaptchaV2Response : ITaskResponse +{ + [JsonRequired] + [JsonProperty("gRecaptchaResponse")] + public string GReCaptchaResponse { get; init; } = null!; +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/Responses/ReCaptchaV3Response.cs b/CapMonster.Cloud/Tasks/Responses/ReCaptchaV3Response.cs new file mode 100644 index 0000000..859557d --- /dev/null +++ b/CapMonster.Cloud/Tasks/Responses/ReCaptchaV3Response.cs @@ -0,0 +1,6 @@ +namespace CapMonster.Cloud.Tasks.Responses; + +public class ReCaptchaV3Response : ReCaptchaV2Response +{ + +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/Responses/TurnstileResponse.cs b/CapMonster.Cloud/Tasks/Responses/TurnstileResponse.cs new file mode 100644 index 0000000..5c19ade --- /dev/null +++ b/CapMonster.Cloud/Tasks/Responses/TurnstileResponse.cs @@ -0,0 +1,13 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks.Responses; + +public class TurnstileResponse : ITaskResponse +{ + [JsonProperty("cf_clearance")] + public string? CfClearance { get; init; } + + [JsonProperty("token")] + public string? Token { get; init; } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Tasks/TurnstileTask.cs b/CapMonster.Cloud/Tasks/TurnstileTask.cs new file mode 100644 index 0000000..0f08c2e --- /dev/null +++ b/CapMonster.Cloud/Tasks/TurnstileTask.cs @@ -0,0 +1,70 @@ +using CapMonster.Cloud.Utilities; +using Newtonsoft.Json; + +namespace CapMonster.Cloud.Tasks; + +public class TurnstileTask : ITask, IUserAgentTask +{ + [JsonProperty("type")] + private readonly string _type = "TurnstileTask"; + + [JsonRequired] + [JsonProperty("websiteURL")] + public string WebsiteUrl { get; set; } + + [JsonRequired] + [JsonProperty("websiteKey")] + public string WebsiteKey { get; set; } + + [JsonRequired] + [JsonProperty("pageAction", NullValueHandling = NullValueHandling.Ignore)] + public string? PageAction { get; set; } + + /// + /// cf_clearance - if cookies are required;
+ /// token - if required token from Turnstile + ///
+ /// cf_clearance or token + [JsonRequired] + [JsonProperty("cloudflareTaskType", NullValueHandling = NullValueHandling.Ignore)] + public string? CloudFlareTaskType { get; set; } + + /// + /// Base64 encoded html page with captcha. Required if cloudflareTaskType is equal to cf_clearance. + /// + [JsonRequired] + [JsonProperty("htmlPageBase64", NullValueHandling = NullValueHandling.Ignore)] + public string? HtmlPageBase64 { get; set; } + + /// + /// Only the latest UAs from Chrome are supported. + /// Required if cloudflareTaskType is specified. + /// + [JsonRequired] + [JsonProperty("userAgent", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + public TurnstileTask(string websiteUrl, string websiteKey, string? pageAction = null, CloudFlareTaskType? cloudflareTaskType = null, + string? htmlPageBase64 = null, string? userAgent = null) + { + WebsiteKey = websiteKey; + WebsiteUrl = websiteUrl; + PageAction = pageAction; + if (cloudflareTaskType != null) + UserAgent = userAgent ?? throw new CapMonsterException(12, "USER_AGENT_REQUIRED", "User agent is required if cloudFlareTaskType is specified."); + if (cloudflareTaskType == Utilities.CloudFlareTaskType.CfClearance) + { + HtmlPageBase64 = htmlPageBase64 ?? throw new CapMonsterException( + 12, + "HTML_PAGE_BASE64_REQUIRED", + "HtmlPageBase64 is required if cloudFlareTaskType is CFClearance."); + } + CloudFlareTaskType = cloudflareTaskType switch + { + Utilities.CloudFlareTaskType.CfClearance => "cf_clearance", + Utilities.CloudFlareTaskType.Token => "token", + null => null, + _ => throw new ArgumentOutOfRangeException(nameof(cloudflareTaskType), cloudflareTaskType, null) + }; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Utilities/CapMonsterException.cs b/CapMonster.Cloud/Utilities/CapMonsterException.cs new file mode 100644 index 0000000..52ec23e --- /dev/null +++ b/CapMonster.Cloud/Utilities/CapMonsterException.cs @@ -0,0 +1,18 @@ +namespace CapMonster.Cloud.Utilities; + +public class CapMonsterException : Exception +{ + public int ErrorId { get; set; } + + public string ErrorCode { get; set; } + + public string ErrorDescription { get; set; } + + public CapMonsterException(int errorId, string errorCode, string errorDescription) : base( + $"[{errorCode}]: {errorDescription}") + { + ErrorId = errorId; + ErrorCode = errorCode; + ErrorDescription = errorDescription; + } +} \ No newline at end of file diff --git a/CapMonster.Cloud/Utilities/CloudFlareTaskType.cs b/CapMonster.Cloud/Utilities/CloudFlareTaskType.cs new file mode 100644 index 0000000..0fa4c38 --- /dev/null +++ b/CapMonster.Cloud/Utilities/CloudFlareTaskType.cs @@ -0,0 +1,7 @@ +namespace CapMonster.Cloud.Utilities; + +public enum CloudFlareTaskType +{ + CfClearance, + Token +} \ No newline at end of file diff --git a/CapMonster.Cloud/Utilities/ICookieTask.cs b/CapMonster.Cloud/Utilities/ICookieTask.cs new file mode 100644 index 0000000..8b77d6b --- /dev/null +++ b/CapMonster.Cloud/Utilities/ICookieTask.cs @@ -0,0 +1,6 @@ +namespace CapMonster.Cloud.Utilities; + +public interface ICookieTask +{ + +} \ No newline at end of file diff --git a/CapMonster.Cloud/Utilities/IProxyTask.cs b/CapMonster.Cloud/Utilities/IProxyTask.cs new file mode 100644 index 0000000..29c7122 --- /dev/null +++ b/CapMonster.Cloud/Utilities/IProxyTask.cs @@ -0,0 +1,6 @@ +namespace CapMonster.Cloud.Utilities; + +public interface IProxyTask +{ + +} \ No newline at end of file diff --git a/CapMonster.Cloud/Utilities/ITask.cs b/CapMonster.Cloud/Utilities/ITask.cs new file mode 100644 index 0000000..b24b55b --- /dev/null +++ b/CapMonster.Cloud/Utilities/ITask.cs @@ -0,0 +1,6 @@ +namespace CapMonster.Cloud.Utilities; + +public interface ITask +{ + +} \ No newline at end of file diff --git a/CapMonster.Cloud/Utilities/ITaskResponse.cs b/CapMonster.Cloud/Utilities/ITaskResponse.cs new file mode 100644 index 0000000..246ad8b --- /dev/null +++ b/CapMonster.Cloud/Utilities/ITaskResponse.cs @@ -0,0 +1,6 @@ +namespace CapMonster.Cloud.Utilities; + +public interface ITaskResponse +{ + +} \ No newline at end of file diff --git a/CapMonster.Cloud/Utilities/IUserAgentTask.cs b/CapMonster.Cloud/Utilities/IUserAgentTask.cs new file mode 100644 index 0000000..78a61eb --- /dev/null +++ b/CapMonster.Cloud/Utilities/IUserAgentTask.cs @@ -0,0 +1,6 @@ +namespace CapMonster.Cloud.Utilities; + +public interface IUserAgentTask +{ + +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d0087a6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9cc5cb6 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Capmonster.cloud Library for .NET Core +![Nuget](https://img.shields.io/nuget/dt/CapMonster.Cloud?style=for-the-badge) ![Nuget](https://img.shields.io/nuget/v/CapMonster.Cloud?style=for-the-badge) ![GitHub last commit](https://img.shields.io/github/last-commit/alperensert/CapMonster.Cloud?style=for-the-badge) ![GitHub Release Date](https://img.shields.io/github/release-date/alperensert/CapMonster.Cloud?style=for-the-badge) ![GitHub Repo stars](https://img.shields.io/github/stars/alperensert/CapMonster.Cloud?style=for-the-badge) + +[Capmonster.cloud](https://capmonster.cloud) Library for .NET Core. + +### Installation +via Package Manager: +``` +NuGet\Install-Package CapMonster.Cloud -Version 1.0.0 +``` +This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package. + +via .NET CLI: +```ssh +dotnet add package CapMonster.Cloud --version 1.0.0 +``` + +via adding PackageReference: +```xml + +``` +For projects that support PackageReference, copy this XML node into the project file to reference the package. + +### Supported Captcha Types +- Image to text +- ReCaptcha V2 +- ReCaptcha V2 Enterprise +- ReCaptcha V3 +- HCaptcha +- FunCaptcha +- Turnstile +- GeeTest + +### Usage Examples +--- +### Creating a client +```csharp +var client = new CapMonsterClient("apikey"); +``` +### Get balance +```csharp +var client = new CapMonsterClient("apikey"); +await client.GetBalanceAsync(); +``` +#### ReCaptchaV2 Task +```csharp +var client = new CapMonsterClient("apikey", false); +var task = new ReCaptchaV2Task("recaptcha-site", "recaptcha-site-key"); +string id = await client.CreateTask(task); +var response = await client.JoinTaskResult(id); +``` + +#### FunCaptcha Task +```csharp +var client = new CapMonsterClient("apikey", false); +var task = new FunCaptchaTask("funcaptcha-site", "funcaptcha-key", "funcaptcha-js-source"); +string id = await client.CreateTask(task); +var response = await client.JoinTaskResult(id); +``` + +For other examples and api documentation please visit [wiki](https://zennolab.atlassian.net/wiki/spaces/APIS/pages/491575/English+Documentation) \ No newline at end of file