From 549dae1a4494ae85a45183be1949313db0ed68d0 Mon Sep 17 00:00:00 2001 From: Ruri Date: Thu, 11 Jul 2024 20:08:14 +0200 Subject: [PATCH 01/67] Switched to .NET 8, added sonar and .editorconfig --- .editorconfig | 353 ++++++++++++++++++ .../CaptchaSharp.Services.More.csproj | 9 +- CaptchaSharp.Tests/AntiCaptchaServiceTests.cs | 3 +- CaptchaSharp.Tests/CaptchaSharp.Tests.csproj | 6 +- CaptchaSharp/CaptchaSharp.csproj | 6 +- 5 files changed, 372 insertions(+), 5 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..89541e5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,353 @@ +[*.cs] + +#Core editorconfig formatting - indentation + +#use soft tabs (spaces) for indentation +indent_style = space + +#Formatting - indentation options + +#indent switch case contents. +csharp_indent_case_contents = true +#labels are placed at the same indent as the current context +csharp_indent_labels = no_change +#indent switch labels +csharp_indent_switch_labels = true + +#Formatting - new line options + +#place catch statements on a new line +csharp_new_line_before_catch = true +#place else statements on a new line +csharp_new_line_before_else = true +#require finally statements to be on a new line after the closing brace +csharp_new_line_before_finally = true +#require members of anonymous types to be on separate lines +csharp_new_line_before_members_in_anonymous_types = true +#require members of object intializers to be on separate lines +csharp_new_line_before_members_in_object_initializers = false +#require braces to be on a new line for object_collection_array_initializers, accessors, types, control_blocks, lambdas, methods, anonymous_types, and properties (also known as "Allman" style) + +#Formatting - organize using options + +#do not place System.* using directives before other using directives + +#Formatting - spacing options + +#require NO space between a cast and the value +csharp_space_after_cast = false +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_after_colon_in_inheritance_clause = true +#require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_before_colon_in_inheritance_clause = true +#remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +#remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +#remove space within empty parameter list parentheses for a method declaration +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Formatting - wrapping options + +#leave code block on single line +csharp_preserve_single_line_blocks = true +#leave statements and member declarations on the same line +csharp_preserve_single_line_statements = true + +#Style - Code block preferences + +#always prefer curly braces if allowed +csharp_prefer_braces = true:suggestion + +#Style - expression bodied member options + +#prefer expression-bodied members for accessors +csharp_style_expression_bodied_accessors = true:suggestion +#prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false:suggestion +#prefer expression-bodied members for methods +csharp_style_expression_bodied_methods = true:suggestion +#prefer expression-bodied members for properties +csharp_style_expression_bodied_properties = true:suggestion + +#Style - expression level options + +#prefer out variables to be declared inline in the argument list of a method call when possible +csharp_style_inlined_variable_declaration = true:suggestion +#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_member_access = true:suggestion + +#Style - Expression-level preferences + +#prefer default over default(T) +csharp_prefer_simple_default_expression = true:suggestion +#prefer objects to be initialized using object initializers when possible +dotnet_style_object_initializer = true:suggestion +#prefer inferred anonymous type member names +dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion +#prefer inferred tuple element names +dotnet_style_prefer_inferred_tuple_names = true:suggestion + +#Style - implicit and explicit types + +#prefer var over explicit type in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = true:suggestion +#prefer var is used to declare variables with built-in system types such as int +csharp_style_var_for_built_in_types = true:suggestion +#prefer var when the type is already mentioned on the right-hand side of a declaration expression +csharp_style_var_when_type_is_apparent = true:suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +#Style - Miscellaneous preferences + +#prefer anonymous functions over local functions +csharp_style_pattern_local_over_anonymous_function = false:suggestion + +#Style - modifier options + +#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +#Style - Modifier preferences + +#when this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion + +#Style - qualification options + +#prefer events not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_event = false:suggestion +#prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion +#prefer methods not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_method = false:suggestion +#prefer properties not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_property = false:suggestion +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent + +# Microsoft .NET properties +csharp_style_prefer_utf8_string_literals = true:suggestion +dotnet_naming_rule.private_constants_rule.import_to_resharper = True +dotnet_naming_rule.private_constants_rule.resharper_description = Constant fields (private) +dotnet_naming_rule.private_constants_rule.resharper_guid = 236f7aa5-7b06-43ca-bf2a-9b31bfcff09a +dotnet_naming_rule.private_constants_rule.severity = warning +dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = True +dotnet_naming_rule.private_instance_fields_rule.resharper_description = Instance fields (private) +dotnet_naming_rule.private_instance_fields_rule.resharper_guid = 4a98fdf6-7d98-4f5a-afeb-ea44ad98c70c +dotnet_naming_rule.private_instance_fields_rule.severity = warning +dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_static_fields_rule.import_to_resharper = True +dotnet_naming_rule.private_static_fields_rule.resharper_description = Static fields (private) +dotnet_naming_rule.private_static_fields_rule.resharper_guid = f9fce829-e6f4-4cb2-80f1-5497c44f51df +dotnet_naming_rule.private_static_fields_rule.severity = warning +dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = True +dotnet_naming_rule.private_static_readonly_rule.resharper_description = Static readonly fields (private) +dotnet_naming_rule.private_static_readonly_rule.resharper_guid = 15b5b1f1-457c-4ca6-b278-5615aedc07d3 +dotnet_naming_rule.private_static_readonly_rule.severity = warning +dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True +dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field +dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef +dotnet_naming_rule.unity_serialized_field_rule.severity = warning +dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style +dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_constants_symbols.resharper_applicable_kinds = constant_field +dotnet_naming_symbols.private_constants_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_instance_fields_symbols.resharper_applicable_kinds = field, readonly_field +dotnet_naming_symbols.private_instance_fields_symbols.resharper_required_modifiers = instance +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_fields_symbols.resharper_applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = readonly, static +dotnet_naming_symbols.private_static_readonly_symbols.resharper_applicable_kinds = readonly_field +dotnet_naming_symbols.private_static_readonly_symbols.resharper_required_modifiers = static +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field +dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none + +# ReSharper properties +resharper_autodetect_indent_settings = true +resharper_braces_for_for = not_required +resharper_braces_for_foreach = not_required +resharper_braces_for_ifelse = not_required_for_both +resharper_braces_for_while = not_required +resharper_braces_redundant = true +resharper_csharp_insert_final_newline = true +resharper_csharp_keep_existing_enum_arrangement = false +resharper_formatter_off_tag = @formatter:off +resharper_formatter_on_tag = @formatter:on +resharper_formatter_tags_enabled = true +resharper_keep_existing_declaration_block_arrangement = false +resharper_keep_existing_embedded_block_arrangement = false +resharper_method_or_operator_body = block_body +resharper_use_heuristics_for_body_style = true +resharper_use_indent_from_vs = false +resharper_xmldoc_indent_child_elements = RemoveIndent +resharper_xmldoc_indent_text = RemoveIndent + +# ReSharper inspection severities +resharper_arrange_constructor_or_destructor_body_highlighting = none +resharper_arrange_method_or_operator_body_highlighting = none +resharper_arrange_namespace_body_highlighting = hint +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_enforce_do_while_statement_braces_highlighting = none +resharper_enforce_fixed_statement_braces_highlighting = none +resharper_enforce_foreach_statement_braces_highlighting = none +resharper_enforce_for_statement_braces_highlighting = none +resharper_enforce_if_statement_braces_highlighting = none +resharper_enforce_lock_statement_braces_highlighting = none +resharper_enforce_using_statement_braces_highlighting = none +resharper_enforce_while_statement_braces_highlighting = none +resharper_redundant_base_qualifier_highlighting = warning +resharper_remove_redundant_braces_highlighting = none +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_web_config_module_not_resolved_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = warning +resharper_web_config_wrong_module_highlighting = warning + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_qualification_for_field = false:suggestion + +dotnet_naming_style.camel_case_leading_underscore.capitalization = camel_case +dotnet_naming_style.camel_case_leading_underscore.required_prefix = _ + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +dotnet_naming_rule.private_fields_should_be_camel_case_leading_underscore.severity = warning +dotnet_naming_rule.private_fields_should_be_camel_case_leading_underscore.symbols = private_fields +dotnet_naming_rule.private_fields_should_be_camel_case_leading_underscore.style = camel_case_leading_underscore + +# SonarAnalyzer.CSharp + +# S2094: Remove this empty class, write its code or make it an "interface" +dotnet_diagnostic.s2094.severity = none + +# S1075: Refactor your code not to use hardcoded absolute paths or URIs. +dotnet_diagnostic.s1075.severity = none + +[*] +charset = utf-8 +end_of_line = crlf +trim_trailing_whitespace = false +insert_final_newline = true +indent_style = space +indent_size = 4 + +[{*.har,*.jsb2,*.jsb3,*.json,*.jsonc,*.postman_collection,*.postman_collection.json,*.postman_environment,*.postman_environment.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}] +indent_style = space +indent_size = 2 + +[*.scss] +indent_style = space +indent_size = 2 + +[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,c++m,cc,ccm,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,cxxm,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,mxx,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj b/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj index 1e11a30..fe8bef4 100644 --- a/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj +++ b/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net8.0 Adds additional services to CaptchaSharp. MIT Ruri @@ -16,5 +16,12 @@ + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs index 7a0ac23..5a64de5 100644 --- a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs @@ -1,5 +1,4 @@ using CaptchaSharp.Services; -using System; using System.Threading.Tasks; using Xunit; @@ -30,4 +29,4 @@ public AntiCaptchaServiceTests(AntiCaptchaFixture fixture) : base(fixture) { } [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); } -} \ No newline at end of file +} diff --git a/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj b/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj index f210d18..7b0c002 100644 --- a/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj +++ b/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 false @@ -16,6 +16,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/CaptchaSharp/CaptchaSharp.csproj b/CaptchaSharp/CaptchaSharp.csproj index 2a3aa6d..3afd73d 100644 --- a/CaptchaSharp/CaptchaSharp.csproj +++ b/CaptchaSharp/CaptchaSharp.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net8.0 false CaptchaSharp Ruri @@ -21,6 +21,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 2361f9858453569248d82bb5ca39729cdc1b8e59 Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 12 Jul 2024 10:29:29 +0200 Subject: [PATCH 02/67] Fixed 2captcha tests for existing captcha types --- CaptchaSharp.Tests/ServiceTests.cs | 421 +++++++++++-------- CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 62 +-- CaptchaSharp/Enums/CaptchaType.cs | 51 ++- CaptchaSharp/Enums/ProxyType.cs | 23 +- CaptchaSharp/Models/CaptchaResponse.cs | 39 +- CaptchaSharp/Models/Proxy.cs | 69 +-- 6 files changed, 370 insertions(+), 295 deletions(-) diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index 4c718df..03168b5 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -7,206 +7,285 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class ServiceTests { - public class ServiceTests + private readonly ServiceFixture fixture; + + private readonly string _captchaImageBase64 = + "iVBORw0KGgoAAAANSUhEUgAAAGsAAAAgCAYAAAAVIIajAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAA5/SURBVGhD7Zr3k1VVEsdh8gAlOStLWiwyKLAqsErQMkIVSyqhFAa0VBBYVl1BESUskraUDGK5FpKHXCRByZlVgkOGIQ6TB/6C3vPp9/runTtvHqjgLhY/fOu9ubdvd5/+dvfpc9+UKLhZIPdxb+A+WfcQ7pP1GyD/Rn4hFNz47zX7Hum5IO46WebU7Tp0pxC0+b+yD3Lzc0PfCyIjryBPYfKR9IEiZPkfiPSg3VcEDEWS457nbEDmTsNs+P3KzctV+34/77YfwPPF4czZM7Ju3Trp07ePDBw4UD799FMZN26cvPf392T+/Ply5OgRycrKKhQnENRZiCwTsgBHWpz9fePGjRBu3igi45fLys6SH4/8qEFDn1/mTsLs5eTlhHxyhJ04dUK+3/a9HDx8UPLy3FrctVsljl0v7r4fpscP/73svGzZu3evVK5cWWJjYiU+Nl7iYuMkLsahZOgzpkSMPPDAA9KyRUvZtWuXl2h+XYaIZF26dEnOnz8vmVmZ3jW7jyICsnHjRnnuueekdevWcuTIkSIG7Ll33nlH4uLiZOvWreqI3b+TMFtgz949MnTYUGncuLEkJSZJyRIlNVCPP/64zJo9S86cORMxGH4d+KkIyARh8v49yJ7hkzjt3bdXCYotGauIKRnjQcmDOAfulS5dWkaNGiXXs65H9LEIWWTekKFDdIGdO3eW3NzCFaYyrkomTpwo8XHxUqpUKdm/f7/k5ReW0WfcggcMHCCJiYly6NAhdd5vKxr8fkWDyeP3gQMHpFy5chqIhLgELxAgPiZeSpQoIVWrVlXCrML8evA5IzND5s6dKz+l/aR/+235YTaPHz8ujz32mJw6dUrXZzr5pLIWLFigCUMFAWLRuEljeeihh5QcEjm2RIhII3PEiBERO1ERsgj64cOHlazWrVorWbkFoYXx8LWMa3Lh4gV55plnVDEGKOGr164WIkwNuYzr0aOHlC1bVs6ePVuoBXGfxXGN73zyN99Nxu9bcUCO5/Yf2C/ly5X3stTLWhcMgmXX+Xz22Wc1GP5KN7vTp01X+Z49ekatLvXR3Z8zZ47ExMQoKabD7mfnZsv6DetD1R0m4o033pDsnGy5fv26HDt+TLZs3SJdu3aVkiVDMoCES0tL05j4bUasrN17dqvixIREmT5jupLgD+LBgwelfPnyIQdctuDMsmXLJCc3lFkA+dEfjVYdKSkpcvnKZY8YkHE9Q/bt2yebN22WVatWyfr162XPnj1FWu+toHIFBfLRqI+0cixLqaC27dpqu546daqUSi7lZTdyu3fv1mCaHXxCz5gxY3Tt/fr3i0oW8jk5OVoh6GNQ8PvNJ0m0aNEiJUJtO72DBw/WOBELi8ely5dkyLAhUrpUafWdimNriUoWwMHjPx0PZaPrtW3bttVrKFVn3HccwLBlK87OmDFDK9DkMDR79mxJiE/QDTQ9PV2zmQDhyIMPPqjPBfHkk09q0IKOFgdsYXPsuLFeBb300kty8+ZNBffAihUrJCEh1BqRW7J0SaG2pX67tfXq3UuSE5OlSZMmUcnCv9OnT0tyUrLG4K1Bb+nAZfLmF0n5xBNPhBLbxWzGTBcnqtrdN+BHdna2NG3aVDvawAEDVb/pMhQhCyFaX4MGDbTvM0AUqiy3gNTUVCWhWtVqUqFCBXWCkZS2Z8FBz9dffy1VqlSRD0d96PVgBhecImjo7969u3Tq1Em6/aWb9OzVU1avWa3PIhv0LRLMp/HjxysJYPTo0XrNFqyf7m/2loTYBJXBTiSypk2bppVQu05trXLzQ+2EZfnkOpMu60Z+3rx53jbgl0cn/hAjsGTJEo8sv670i+mawJDFzECr5FlkDEXIMqdff/11Vc6GmHYiTRfGPZQ0a9ZMy3r48OGaUUkJSZqJyBAYPq9duya1a9fWjFqxcoWW/sWLF5VcHGrXrp2OtYzZVBJEG9SH8GJuBeSoIIKGLZKgQ8cOurcaGXxu2bJF7SIDWUyn/jZochs2bNAKp3WzVi9RXUyCYD1Dhw5VstasWeMlh+njuczMTN2z8Yt4sj/xrK0R0JGoLGKCj8nJydp98Me/1qJtMOw0+4dlKtMJBri+cuXKQtdTV6Rqr6VlcvDTCnILXLhooW7ukH323FnJz8/XFsDCatWqJRkZGRpkFqyEOdu2WEPQt0hAjoVPmDBBfdK9wW347dq2k8uXQ/skfk+cNFEDQcB69+6tyeRPCr6TKNOmT1OyaKXsd+Dbb7/V6RDwHL6fOn1KXu7zsq4HedqqDWLmV05+jg4RdCFrg0OGDAnt7WHCSBgq9M233vTkmAeuXL2iz/vXGpEsXYRT1qlzJx0QqCT+ZjG0G5zDyUOHD+lUU69ePXnqqae0lAkOGfnII49om3v00Ue1l/Ms1ce1+vXr6xmtVatWWmkp/VNk5MiR3rjsD+KtgBywkZv9UavH+ceAsWPHDk2e1157Tcli39q1c1cRG8hQ4Uy5TJIEFmg1OnBmA0xqTJ1kv953tojHzp07I1YqXQmfTF+vXr1CncQBkhZ8s0DbM3qM0GF/HaZxxEfzDxQhC2gAHDlT/zk1tHCXsQsXLtTrH3zwgToHCThDJU2aNEleeOEFzS6CdvLkyRCh7jlGVXSxD9IWaQcsnoBAHJ/IIf/www97U5A/kMVB/Qz7yplGB5earu87n9WO+2zUqJF88sknGmD+7tatm7YmbPh1KVkuoUhQJctVKJ0BUkDlKpW1nVWtVlXKlCnjHXRtupw8ZbI+b37ziY30S+lSsVJFlQU1a9aUxYsXy8cffyxt2rTRpEIHUyw+V6xYUXgpESkGxZIFq+wptDGCy3mJCuGNBMF99913dYG6aOfkiy++KHHxcdo2yDIlyzlCJULqtm3bVA+Z06RpE5kzd46sXbtWW8zkyZOlS5cuesjWgSYvpDfobBDm57lz53QktmEnKSlJ6tatq34SIIIPuE8nwJ+gbs1it47nn38+JB8fL1/M/0LbGNMxgxEHX6ZakuLLL7/UjoJ+1qqju0sa08sndk6eOik1atTwyNIKc21abbiDOtcgCeDv+HHjvWEs6GNEsgDBIgg6EDiFLVu21PZGGUMCAYYsWySVFRsbq4c8KovF8vd3330nmdmZeq6BjIoVKsrpM6e9tqrZ6L5zaKblVq5UWadN/8RUHLiPnwMGDNAAkO1t/tTGO0PhS58+fUL3XDBoY28Pflv3A/YXvy6rrI4dO+rA9Oqrr+o+iw3AOgvByc+YPkODD1kcDahu89meY220Onwwwqy1GsqULiMdOnTQBKAD2bN+/0CxZOEQAeOchTMtWrTQ9sHZiY2VN8nImEO8SYZENvrPPvtMnaJ9IJeVk6UHXxyuXr26XLnighWuHMB3qrZO3TqacTjPJo7+SL4ZuA8hllA1qteQY8eOeZkJYXwf/PZgb/OmtTEZEmx/QNSW20c4RrAOAmz++W0CrjEkLF261COL4cq/Z5kcsWEa9shy5DRq2EiTvn9Kf5k0eZKe1/RMGE7+SDZBsWTxAA9TXewlNWrW0LPEoEGDCp0pAMGGRJymmgCZ3LBhQ61GAvb55597LWD16tWeU/Y8ZHG2o/paNG8R1WnAPXwYMXKE2kI3ExzP+XXzncrW6nMyBKt9+/b61gC7pk+fc/oa/LGBtiNeI+kg4LNpQC8tDrIgFtAmg+1Vbbuz2tOdn1ai0Muhn1asxDjgg373+e235UdUsiyIvLHGGIulWr7611chxS5rLCBkKy91NXvCGTThHxN0weiBNF4BJcYnSsqAFH3WHARkKpMWbwTWrlurz0RznHvoYO+k9+Nbv379vLcofjmCyG9GtEEL2vLU5VoJfjl8rFSpkt4nIaORxbO0PojC9oULF4qQxRquZlzVvc0SqkfPHt6adQ1h+PUXh2LJAqrQBWTWrFneqxrIYsS2YAI17rKDvmtTEu1m46aNHinsB126uiHCtTmIfOWVV3Sf4nXMNwu/kebNm+uzderUCb08vgVZppNMNbLa/7m9BjwYCHSlnUxT31kDZHA+DJJFwvDbEwRs2rRJn/PbLCTriDn878PaTUhCDuHqk09O/XDr53WT+QhZuk87HX7Z20FUslCIQcqWg6xlBwv1ZxEykPX+iPdDU44DY679FMF9Dng//PiDBgwduje5EbhatWpKEmM818eOHes9E21BFghO/QQC3zhMclj12gt+OUAKv8qiH+DD0aNHdQ2mD1sQDVm0at5w+O/7YXHhRS5dZ+bMmWoz6K/azs729lRsQxZy0dZWHKKSBVg0p3Z7b1Wndh39CVqDFTZozvNan4Mu56kxY8eEJjoXUHMOmRMnTkjfvn29X08JMqTRKpYtXxaSDz8T9MUP1ecCNOrDUUq8vrlwoEJTl6fqXkulULm80ae9acU7exxDgjbwDX+pbMjavn17sWQB5Bmc6tWvF3qJ7eIU9FltuImSSdoSvXuP7re1vki4JVkoRTnTGRXFuQFHAeXsyTjoNfq8g10LgkVxn9+/aJObNm+S8+nndVE875cN+uKH6aKVkrkEw8igNdG2a/2hltfWCJS1Z852Zsv02d/80swkCNG6Hp9NP7hHu+YHWpIsUmUpKQ68ltOEcj7wksDflX4Obo8sB12MM2yLKuJY+BoBtCxTmTChQV0qE16MXTPd/meiAVn08HalXNlyuhdBlhFnIFC8IeD+8L8Nl+uZoZ/Ng7oUUdboh92HpOLk9brTN3feXCWKpOFne8gK2r8d3JIsgzkSdOiXwq/nl+rkOV20I5d/LWDYsD3RKkk/3VGiabOmMmXKFK1qC24kfZG+R0R4SEBXNH3c4xDOQZ/XTIz4xcnfCrdN1v8rWDRggKEt8Spo85bNsmrNKlm0eJF+375ju270VKEF6pcE6+fC7GCTamLQ+TX273mygD8oXmv1w+6F5UAkPXcDQVu/xv7vgqwg/MH4LYm52/hdkvV7xX2y7hkUyH8AeIrWJFR4fQAAAAAASUVORK5CYII="; + protected CaptchaService Service => fixture.Service; + + public ServiceTests(ServiceFixture fixture) { - private readonly ServiceFixture fixture; - protected CaptchaService Service => fixture.Service; + this.fixture = fixture; + } - public ServiceTests(ServiceFixture fixture) - { - this.fixture = fixture; - } + protected static Task ShouldNotBeSupported(Func method) => Assert.ThrowsAsync(method); - protected static Task ShouldNotBeSupported(Func method) => Assert.ThrowsAsync(method); + protected async Task BalanceTest() + { + var balance = await Service.GetBalanceAsync(); + Assert.True(balance > 0); + } - protected async Task BalanceTest() + protected async Task ReportSolutionTest() + { + var options = new ImageCaptchaOptions { - var balance = await Service.GetBalanceAsync(); - Assert.True(balance > 0); - } + IsPhrase = true, + CaseSensitive = true, + CharacterSet = CharacterSet.NotSpecified, + RequiresCalculation = false, + MinLength = 0, + MaxLength = 0, + CaptchaLanguageGroup = CaptchaLanguageGroup.NotSpecified, + CaptchaLanguage = CaptchaLanguage.NotSpecified, + TextInstructions = "" + }; + + var solution = await Service.SolveImageCaptchaAsync( + base64: _captchaImageBase64, + options); + + await Service.ReportSolution( + solution.Id, CaptchaType.ImageCaptcha, correct: true); + + Assert.True(true); + } - protected async Task TextCaptchaTest() + protected async Task TextCaptchaTest() + { + var options = new TextCaptchaOptions { - var options = new TextCaptchaOptions - { - CaptchaLanguageGroup = CaptchaLanguageGroup.NotSpecified, - CaptchaLanguage = CaptchaLanguage.NotSpecified - }; + CaptchaLanguageGroup = CaptchaLanguageGroup.NotSpecified, + CaptchaLanguage = CaptchaLanguage.NotSpecified + }; - var solution = await Service.SolveTextCaptchaAsync( - text: "What is 2+2?", - options); + var solution = await Service.SolveTextCaptchaAsync( + text: "What is 2+2?", + options); - Assert.Equal("4", solution.Response); - } + Assert.Equal("4", solution.Response); + } - protected async Task ImageCaptchaTest() - { - var options = new ImageCaptchaOptions - { - IsPhrase = true, - CaseSensitive = true, - CharacterSet = CharacterSet.NotSpecified, - RequiresCalculation = false, - MinLength = 0, - MaxLength = 0, - CaptchaLanguageGroup = CaptchaLanguageGroup.NotSpecified, - CaptchaLanguage = CaptchaLanguage.NotSpecified, - TextInstructions = "" - }; - - var solution = await Service.SolveImageCaptchaAsync( - base64: "iVBORw0KGgoAAAANSUhEUgAAAGsAAAAgCAYAAAAVIIajAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAA5/SURBVGhD7Zr3k1VVEsdh8gAlOStLWiwyKLAqsErQMkIVSyqhFAa0VBBYVl1BESUskraUDGK5FpKHXCRByZlVgkOGIQ6TB/6C3vPp9/runTtvHqjgLhY/fOu9ubdvd5/+dvfpc9+UKLhZIPdxb+A+WfcQ7pP1GyD/Rn4hFNz47zX7Hum5IO46WebU7Tp0pxC0+b+yD3Lzc0PfCyIjryBPYfKR9IEiZPkfiPSg3VcEDEWS457nbEDmTsNs+P3KzctV+34/77YfwPPF4czZM7Ju3Trp07ePDBw4UD799FMZN26cvPf392T+/Ply5OgRycrKKhQnENRZiCwTsgBHWpz9fePGjRBu3igi45fLys6SH4/8qEFDn1/mTsLs5eTlhHxyhJ04dUK+3/a9HDx8UPLy3FrctVsljl0v7r4fpscP/73svGzZu3evVK5cWWJjYiU+Nl7iYuMkLsahZOgzpkSMPPDAA9KyRUvZtWuXl2h+XYaIZF26dEnOnz8vmVmZ3jW7jyICsnHjRnnuueekdevWcuTIkSIG7Ll33nlH4uLiZOvWreqI3b+TMFtgz949MnTYUGncuLEkJSZJyRIlNVCPP/64zJo9S86cORMxGH4d+KkIyARh8v49yJ7hkzjt3bdXCYotGauIKRnjQcmDOAfulS5dWkaNGiXXs65H9LEIWWTekKFDdIGdO3eW3NzCFaYyrkomTpwo8XHxUqpUKdm/f7/k5ReW0WfcggcMHCCJiYly6NAhdd5vKxr8fkWDyeP3gQMHpFy5chqIhLgELxAgPiZeSpQoIVWrVlXCrML8evA5IzND5s6dKz+l/aR/+235YTaPHz8ujz32mJw6dUrXZzr5pLIWLFigCUMFAWLRuEljeeihh5QcEjm2RIhII3PEiBERO1ERsgj64cOHlazWrVorWbkFoYXx8LWMa3Lh4gV55plnVDEGKOGr164WIkwNuYzr0aOHlC1bVs6ePVuoBXGfxXGN73zyN99Nxu9bcUCO5/Yf2C/ly5X3stTLWhcMgmXX+Xz22Wc1GP5KN7vTp01X+Z49ekatLvXR3Z8zZ47ExMQoKabD7mfnZsv6DetD1R0m4o033pDsnGy5fv26HDt+TLZs3SJdu3aVkiVDMoCES0tL05j4bUasrN17dqvixIREmT5jupLgD+LBgwelfPnyIQdctuDMsmXLJCc3lFkA+dEfjVYdKSkpcvnKZY8YkHE9Q/bt2yebN22WVatWyfr162XPnj1FWu+toHIFBfLRqI+0cixLqaC27dpqu546daqUSi7lZTdyu3fv1mCaHXxCz5gxY3Tt/fr3i0oW8jk5OVoh6GNQ8PvNJ0m0aNEiJUJtO72DBw/WOBELi8ely5dkyLAhUrpUafWdimNriUoWwMHjPx0PZaPrtW3bttVrKFVn3HccwLBlK87OmDFDK9DkMDR79mxJiE/QDTQ9PV2zmQDhyIMPPqjPBfHkk09q0IKOFgdsYXPsuLFeBb300kty8+ZNBffAihUrJCEh1BqRW7J0SaG2pX67tfXq3UuSE5OlSZMmUcnCv9OnT0tyUrLG4K1Bb+nAZfLmF0n5xBNPhBLbxWzGTBcnqtrdN+BHdna2NG3aVDvawAEDVb/pMhQhCyFaX4MGDbTvM0AUqiy3gNTUVCWhWtVqUqFCBXWCkZS2Z8FBz9dffy1VqlSRD0d96PVgBhecImjo7969u3Tq1Em6/aWb9OzVU1avWa3PIhv0LRLMp/HjxysJYPTo0XrNFqyf7m/2loTYBJXBTiSypk2bppVQu05trXLzQ+2EZfnkOpMu60Z+3rx53jbgl0cn/hAjsGTJEo8sv670i+mawJDFzECr5FlkDEXIMqdff/11Vc6GmHYiTRfGPZQ0a9ZMy3r48OGaUUkJSZqJyBAYPq9duya1a9fWjFqxcoWW/sWLF5VcHGrXrp2OtYzZVBJEG9SH8GJuBeSoIIKGLZKgQ8cOurcaGXxu2bJF7SIDWUyn/jZochs2bNAKp3WzVi9RXUyCYD1Dhw5VstasWeMlh+njuczMTN2z8Yt4sj/xrK0R0JGoLGKCj8nJydp98Me/1qJtMOw0+4dlKtMJBri+cuXKQtdTV6Rqr6VlcvDTCnILXLhooW7ukH323FnJz8/XFsDCatWqJRkZGRpkFqyEOdu2WEPQt0hAjoVPmDBBfdK9wW347dq2k8uXQ/skfk+cNFEDQcB69+6tyeRPCr6TKNOmT1OyaKXsd+Dbb7/V6RDwHL6fOn1KXu7zsq4HedqqDWLmV05+jg4RdCFrg0OGDAnt7WHCSBgq9M233vTkmAeuXL2iz/vXGpEsXYRT1qlzJx0QqCT+ZjG0G5zDyUOHD+lUU69ePXnqqae0lAkOGfnII49om3v00Ue1l/Ms1ce1+vXr6xmtVatWWmkp/VNk5MiR3rjsD+KtgBywkZv9UavH+ceAsWPHDk2e1157Tcli39q1c1cRG8hQ4Uy5TJIEFmg1OnBmA0xqTJ1kv953tojHzp07I1YqXQmfTF+vXr1CncQBkhZ8s0DbM3qM0GF/HaZxxEfzDxQhC2gAHDlT/zk1tHCXsQsXLtTrH3zwgToHCThDJU2aNEleeOEFzS6CdvLkyRCh7jlGVXSxD9IWaQcsnoBAHJ/IIf/www97U5A/kMVB/Qz7yplGB5earu87n9WO+2zUqJF88sknGmD+7tatm7YmbPh1KVkuoUhQJctVKJ0BUkDlKpW1nVWtVlXKlCnjHXRtupw8ZbI+b37ziY30S+lSsVJFlQU1a9aUxYsXy8cffyxt2rTRpEIHUyw+V6xYUXgpESkGxZIFq+wptDGCy3mJCuGNBMF99913dYG6aOfkiy++KHHxcdo2yDIlyzlCJULqtm3bVA+Z06RpE5kzd46sXbtWW8zkyZOlS5cuesjWgSYvpDfobBDm57lz53QktmEnKSlJ6tatq34SIIIPuE8nwJ+gbs1it47nn38+JB8fL1/M/0LbGNMxgxEHX6ZakuLLL7/UjoJ+1qqju0sa08sndk6eOik1atTwyNIKc21abbiDOtcgCeDv+HHjvWEs6GNEsgDBIgg6EDiFLVu21PZGGUMCAYYsWySVFRsbq4c8KovF8vd3330nmdmZeq6BjIoVKsrpM6e9tqrZ6L5zaKblVq5UWadN/8RUHLiPnwMGDNAAkO1t/tTGO0PhS58+fUL3XDBoY28Pflv3A/YXvy6rrI4dO+rA9Oqrr+o+iw3AOgvByc+YPkODD1kcDahu89meY220Onwwwqy1GsqULiMdOnTQBKAD2bN+/0CxZOEQAeOchTMtWrTQ9sHZiY2VN8nImEO8SYZENvrPPvtMnaJ9IJeVk6UHXxyuXr26XLnighWuHMB3qrZO3TqacTjPJo7+SL4ZuA8hllA1qteQY8eOeZkJYXwf/PZgb/OmtTEZEmx/QNSW20c4RrAOAmz++W0CrjEkLF261COL4cq/Z5kcsWEa9shy5DRq2EiTvn9Kf5k0eZKe1/RMGE7+SDZBsWTxAA9TXewlNWrW0LPEoEGDCp0pAMGGRJymmgCZ3LBhQ61GAvb55597LWD16tWeU/Y8ZHG2o/paNG8R1WnAPXwYMXKE2kI3ExzP+XXzncrW6nMyBKt9+/b61gC7pk+fc/oa/LGBtiNeI+kg4LNpQC8tDrIgFtAmg+1Vbbuz2tOdn1ai0Muhn1asxDjgg373+e235UdUsiyIvLHGGIulWr7611chxS5rLCBkKy91NXvCGTThHxN0weiBNF4BJcYnSsqAFH3WHARkKpMWbwTWrlurz0RznHvoYO+k9+Nbv379vLcofjmCyG9GtEEL2vLU5VoJfjl8rFSpkt4nIaORxbO0PojC9oULF4qQxRquZlzVvc0SqkfPHt6adQ1h+PUXh2LJAqrQBWTWrFneqxrIYsS2YAI17rKDvmtTEu1m46aNHinsB126uiHCtTmIfOWVV3Sf4nXMNwu/kebNm+uzderUCb08vgVZppNMNbLa/7m9BjwYCHSlnUxT31kDZHA+DJJFwvDbEwRs2rRJn/PbLCTriDn878PaTUhCDuHqk09O/XDr53WT+QhZuk87HX7Z20FUslCIQcqWg6xlBwv1ZxEykPX+iPdDU44DY679FMF9Dng//PiDBgwduje5EbhatWpKEmM818eOHes9E21BFghO/QQC3zhMclj12gt+OUAKv8qiH+DD0aNHdQ2mD1sQDVm0at5w+O/7YXHhRS5dZ+bMmWoz6K/azs729lRsQxZy0dZWHKKSBVg0p3Z7b1Wndh39CVqDFTZozvNan4Mu56kxY8eEJjoXUHMOmRMnTkjfvn29X08JMqTRKpYtXxaSDz8T9MUP1ecCNOrDUUq8vrlwoEJTl6fqXkulULm80ae9acU7exxDgjbwDX+pbMjavn17sWQB5Bmc6tWvF3qJ7eIU9FltuImSSdoSvXuP7re1vki4JVkoRTnTGRXFuQFHAeXsyTjoNfq8g10LgkVxn9+/aJObNm+S8+nndVE875cN+uKH6aKVkrkEw8igNdG2a/2hltfWCJS1Z852Zsv02d/80swkCNG6Hp9NP7hHu+YHWpIsUmUpKQ68ltOEcj7wksDflX4Obo8sB12MM2yLKuJY+BoBtCxTmTChQV0qE16MXTPd/meiAVn08HalXNlyuhdBlhFnIFC8IeD+8L8Nl+uZoZ/Ng7oUUdboh92HpOLk9brTN3feXCWKpOFne8gK2r8d3JIsgzkSdOiXwq/nl+rkOV20I5d/LWDYsD3RKkk/3VGiabOmMmXKFK1qC24kfZG+R0R4SEBXNH3c4xDOQZ/XTIz4xcnfCrdN1v8rWDRggKEt8Spo85bNsmrNKlm0eJF+375ju270VKEF6pcE6+fC7GCTamLQ+TX273mygD8oXmv1w+6F5UAkPXcDQVu/xv7vgqwg/MH4LYm52/hdkvV7xX2y7hkUyH8AeIrWJFR4fQAAAAAASUVORK5CYII=", - options); - - Assert.Equal("w68hp", solution.Response.Replace(" ", "").ToLower()); - } - - private async Task RecaptchaV2Test(Proxy proxy) + protected async Task ImageCaptchaTest() + { + var options = new ImageCaptchaOptions { - var solution = await Service.SolveRecaptchaV2Async( - siteKey: "6Ld2sf4SAAAAAKSgzs0Q13IZhY02Pyo31S2jgOB5", - siteUrl: "https://patrickhlauke.github.io/recaptcha/", - dataS: "", - enterprise: false, - invisible: false, - proxy); + IsPhrase = true, + CaseSensitive = true, + CharacterSet = CharacterSet.NotSpecified, + RequiresCalculation = false, + MinLength = 0, + MaxLength = 0, + CaptchaLanguageGroup = CaptchaLanguageGroup.NotSpecified, + CaptchaLanguage = CaptchaLanguage.NotSpecified, + TextInstructions = "" + }; + + var solution = await Service.SolveImageCaptchaAsync( + base64: _captchaImageBase64, + options); + + Assert.Equal("w68hp", solution.Response.Replace(" ", "").ToLower()); + } - Assert.NotEqual(string.Empty, solution.Response); - } + private async Task RecaptchaV2Test(Proxy proxy) + { + var solution = await Service.SolveRecaptchaV2Async( + siteKey: "6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u", + siteUrl: "https://2captcha.com/demo/recaptcha-v2", + dataS: "", + enterprise: false, + invisible: false, + proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } - protected Task RecaptchaV2Test_NoProxy() => RecaptchaV2Test(null); + protected Task RecaptchaV2Test_NoProxy() => RecaptchaV2Test(null); - protected Task RecaptchaV2Test_WithProxy() => RecaptchaV2Test(fixture.Config.Proxy); + protected Task RecaptchaV2Test_WithProxy() => RecaptchaV2Test(fixture.Config.Proxy); - private async Task RecaptchaV3Test(Proxy proxy) - { - var solution = await Service.SolveRecaptchaV3Async( - siteKey: "6LcFcoAUAAAAAN7Um8IRZOtbzgsV5ei2meTmRi6m", - siteUrl: "https://contactform7.com/contact/", - action: "action", - minScore: 0.3f, - enterprise: false, - proxy); + private async Task RecaptchaV2InvisibleTest(Proxy proxy) + { + var solution = await Service.SolveRecaptchaV2Async( + siteKey: "6LdO5_IbAAAAAAeVBL9TClS19NUTt5wswEb3Q7C5", + siteUrl: "https://2captcha.com/demo/recaptcha-v2-invisible", + dataS: "", + enterprise: false, + invisible: true, + proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } + + protected Task RecaptchaV2InvisibleTest_NoProxy() => RecaptchaV2InvisibleTest(null); + + protected Task RecaptchaV2InvisibleTest_WithProxy() => RecaptchaV2InvisibleTest(fixture.Config.Proxy); + + private async Task RecaptchaV2EnterpriseTest(Proxy proxy) + { + var solution = await Service.SolveRecaptchaV2Async( + siteKey: "6Lf26sUnAAAAAIKLuWNYgRsFUfmI-3Lex3xT5N-s", + siteUrl: "https://2captcha.com/demo/recaptcha-v2-enterprise", + dataS: "", + enterprise: true, + invisible: true, + proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } + + protected Task RecaptchaV2EnterpriseTest_NoProxy() => RecaptchaV2EnterpriseTest(null); + + protected Task RecaptchaV2EnterpriseTest_WithProxy() => RecaptchaV2EnterpriseTest(fixture.Config.Proxy); + + private async Task RecaptchaV3Test(Proxy proxy) + { + var solution = await Service.SolveRecaptchaV3Async( + siteKey: "6LfB5_IbAAAAAMCtsjEHEHKqcB9iQocwwxTiihJu", + siteUrl: "https://2captcha.com/demo/recaptcha-v3", + action: "demo_action", + minScore: 0.9f, + enterprise: false, + proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } - Assert.NotEqual(string.Empty, solution.Response); - } + protected Task RecaptchaV3Test_NoProxy() => RecaptchaV3Test(null); + protected Task RecaptchaV3Test_WithProxy() => RecaptchaV3Test(fixture.Config.Proxy); - protected Task RecaptchaV3Test_NoProxy() => RecaptchaV3Test(null); - protected Task RecaptchaV3Test_WithProxy() => RecaptchaV3Test(fixture.Config.Proxy); + private async Task RecaptchaV3EnterpriseTest(Proxy proxy) + { + var solution = await Service.SolveRecaptchaV3Async( + siteKey: "6Lel38UnAAAAAMRwKj9qLH2Ws4Tf2uTDQCyfgR6b", + siteUrl: "https://2captcha.com/demo/recaptcha-v3-enterprise", + action: "demo_action", + minScore: 0.9f, + enterprise: true, + proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } + + protected Task RecaptchaV3EnterpriseTest_NoProxy() => RecaptchaV3EnterpriseTest(null); + + protected Task RecaptchaV3EnterpriseTest_WithProxy() => RecaptchaV3EnterpriseTest(fixture.Config.Proxy); + + private async Task FunCaptchaTest(Proxy proxy) + { + // TODO: Find a valid funcaptcha to test! + var solution = await Service.SolveFuncaptchaAsync( + publicKey: "3EE79F8D-13A6-474B-9278-448EA19F79B3", + serviceUrl: "https://client-api.arkoselabs.com", + siteUrl: "https://www.arkoselabs.com/arkose-matchkey/", + noJS: false, + proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } - private async Task FunCaptchaTest(Proxy proxy) - { - // TODO: Find a valid funcaptcha to test! - var solution = await Service.SolveFuncaptchaAsync( - publicKey: "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", - serviceUrl: "https://api.funcaptcha.com/fc/api/nojs/", - siteUrl: "https://api.funcaptcha.com/fc/api/nojs/", - noJS: false, - proxy); + protected Task FunCaptchaTest_NoProxy() => FunCaptchaTest(null); + protected Task FunCaptchaTest_WithProxy() => FunCaptchaTest(fixture.Config.Proxy); - Assert.NotEqual(string.Empty, solution.Response); - } + private async Task HCaptchaTest(Proxy proxy) + { + var solution = await Service.SolveHCaptchaAsync( + siteKey: "f7de0da3-3303-44e8-ab48-fa32ff8ccc7b", + siteUrl: "https://2captcha.com/demo/hcaptcha", + proxy); - protected Task FunCaptchaTest_NoProxy() => FunCaptchaTest(null); - protected Task FunCaptchaTest_WithProxy() => FunCaptchaTest(fixture.Config.Proxy); + Assert.NotEqual(string.Empty, solution.Response); + } - private async Task HCaptchaTest(Proxy proxy) - { - var solution = await Service.SolveHCaptchaAsync( - siteKey: "13257c82-e129-4f09-a733-2a7cb3102832", - siteUrl: "https://dashboard.hcaptcha.com/signup", - proxy); + protected Task HCaptchaTest_NoProxy() => HCaptchaTest(null); + protected Task HCaptchaTest_WithProxy() => HCaptchaTest(fixture.Config.Proxy); - Assert.NotEqual(string.Empty, solution.Response); - } + private async Task KeyCaptchaTest(Proxy proxy) + { + // Get the required parameters from the page since they are not static + var siteUrl = $"{"https"}://www.keycaptcha.com/contact-us/"; + using var httpClient = new HttpClient(); + using var response = await httpClient.GetAsync(siteUrl); + var pageSource = await response.Content.ReadAsStringAsync(); + + var userId = Regex.Match(pageSource, "var s_s_c_user_id = '([^']*)'").Groups[1].Value; + var sessionId = Regex.Match(pageSource, "var s_s_c_session_id = '([^']*)'").Groups[1].Value; + var webServerSign1 = Regex.Match(pageSource, "var s_s_c_web_server_sign = '([^']*)'").Groups[1].Value; + var webServerSign2 = Regex.Match(pageSource, "var s_s_c_web_server_sign2 = '([^']*)'").Groups[1].Value; + + var solution = await Service.SolveKeyCaptchaAsync( + userId, + sessionId, + webServerSign1, + webServerSign2, + siteUrl, + proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } - protected Task HCaptchaTest_NoProxy() => HCaptchaTest(null); - protected Task HCaptchaTest_WithProxy() => HCaptchaTest(fixture.Config.Proxy); + protected Task KeyCaptchaTest_NoProxy() => KeyCaptchaTest(null); + protected Task KeyCaptchaTest_WithProxy() => KeyCaptchaTest(fixture.Config.Proxy); - private async Task KeyCaptchaTest(Proxy proxy) - { - // Get the required parameters from the page since they are not static - var siteUrl = $"{"https"}://www.keycaptcha.com/contact-us/"; - using var httpClient = new HttpClient(); - using var response = await httpClient.GetAsync(siteUrl); - var pageSource = await response.Content.ReadAsStringAsync(); - - var userId = Regex.Match(pageSource, "var s_s_c_user_id = '([^']*)'").Groups[1].Value; - var sessionId = Regex.Match(pageSource, "var s_s_c_session_id = '([^']*)'").Groups[1].Value; - var webServerSign1 = Regex.Match(pageSource, "var s_s_c_web_server_sign = '([^']*)'").Groups[1].Value; - var webServerSign2 = Regex.Match(pageSource, "var s_s_c_web_server_sign2 = '([^']*)'").Groups[1].Value; - - var solution = await Service.SolveKeyCaptchaAsync( - userId, - sessionId, - webServerSign1, - webServerSign2, - siteUrl, - proxy); - - Assert.NotEqual(string.Empty, solution.Response); - } - - protected Task KeyCaptchaTest_NoProxy() => KeyCaptchaTest(null); - protected Task KeyCaptchaTest_WithProxy() => KeyCaptchaTest(fixture.Config.Proxy); - - private async Task GeeTestTest(Proxy proxy) - { - // Get the required parameters from the page since they are not static - var unixTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - var siteUrl = $"{"https"}://www.geetest.com/demo/gt/register-enFullpage-official?t={unixTime}"; - using var httpClient = new HttpClient(); - using var response = await httpClient.GetAsync(siteUrl); - var pageSource = await response.Content.ReadAsStringAsync(); - var obj = JObject.Parse(pageSource); - - var gt = obj.Value("gt"); - var challenge = obj.Value("challenge"); - - var solution = await Service.SolveGeeTestAsync( - gt, - challenge, - apiServer: "api.geetest.com", - siteUrl, - proxy); - - Assert.NotEqual("", solution.Challenge); - Assert.NotEqual("", solution.SecCode); - Assert.NotEqual("", solution.Validate); - } - - protected Task GeeTestTest_NoProxy() => GeeTestTest(null); - protected Task GeeTestTest_WithProxy() => GeeTestTest(fixture.Config.Proxy); - - private async Task CapyTest(Proxy proxy) - { - var solution = await Service.SolveCapyAsync( - siteKey: "PUZZLE_Cme4hZLjuZRMYC3uh14C52D3uNms5w", - siteUrl: $"{"https"}://www.capy.me/account/signin", - proxy); + private async Task GeeTestTest(Proxy proxy) + { + // Get the required parameters from the page since they are not static + var unixTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + var siteUrl = $"{"https"}://www.geetest.com/demo/gt/register-enFullpage-official?t={unixTime}"; + using var httpClient = new HttpClient(); + using var response = await httpClient.GetAsync(siteUrl); + var pageSource = await response.Content.ReadAsStringAsync(); + var obj = JObject.Parse(pageSource); + + var gt = obj.Value("gt"); + var challenge = obj.Value("challenge"); + + var solution = await Service.SolveGeeTestAsync( + gt, + challenge, + apiServer: "api.geetest.com", + siteUrl, + proxy); + + Assert.NotEqual("", solution.Challenge); + Assert.NotEqual("", solution.SecCode); + Assert.NotEqual("", solution.Validate); + } - Assert.NotEqual(string.Empty, solution.ChallengeKey); - Assert.NotEqual(string.Empty, solution.CaptchaKey); - Assert.NotEqual(string.Empty, solution.Answer); - } + protected Task GeeTestTest_NoProxy() => GeeTestTest(null); + protected Task GeeTestTest_WithProxy() => GeeTestTest(fixture.Config.Proxy); - protected Task CapyTest_NoProxy() => CapyTest(null); - protected Task CapyTest_WithProxy() => CapyTest(fixture.Config.Proxy); + private async Task CapyTest(Proxy proxy) + { + var solution = await Service.SolveCapyAsync( + siteKey: "PUZZLE_Cme4hZLjuZRMYC3uh14C52D3uNms5w", + siteUrl: $"{"https"}://www.capy.me/account/signin", + proxy); + + Assert.NotEqual(string.Empty, solution.ChallengeKey); + Assert.NotEqual(string.Empty, solution.CaptchaKey); + Assert.NotEqual(string.Empty, solution.Answer); + } - private async Task DataDomeTest(Proxy proxy) - { - var solution = await Service.SolveDataDomeAsync( - siteUrl: "", // Fill this when testing - captchaUrl: "", // Fill this when testing - proxy); + protected Task CapyTest_NoProxy() => CapyTest(null); + protected Task CapyTest_WithProxy() => CapyTest(fixture.Config.Proxy); - Assert.NotEqual(string.Empty, solution.Response); - } + private async Task DataDomeTest(Proxy proxy) + { + // https://antoinevastel.com/bots/datadome + var solution = await Service.SolveDataDomeAsync( + siteUrl: "https://antoinevastel.com/bots/datadome", // Fill this when testing + captchaUrl: "", // Fill this when testing + proxy); - protected Task DataDomeTest_NoProxy() => DataDomeTest(null); - protected Task DataDomeTest_WithProxy() => DataDomeTest(fixture.Config.Proxy); + Assert.NotEqual(string.Empty, solution.Response); } + + protected Task DataDomeTest_NoProxy() => DataDomeTest(null); + protected Task DataDomeTest_WithProxy() => DataDomeTest(fixture.Config.Proxy); } diff --git a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs index d0e4c4f..69237ab 100644 --- a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs @@ -2,36 +2,40 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class TwoCaptchaFixture : ServiceFixture { - public class TwoCaptchaFixture : ServiceFixture + public TwoCaptchaFixture() { - public TwoCaptchaFixture() - { - Service = new TwoCaptchaService(Config.Credentials.TwoCaptchaApiKey); - } + Service = new TwoCaptchaService(Config.Credentials.TwoCaptchaApiKey); } +} - public class TwoCaptchaServiceTests : ServiceTests, IClassFixture - { - public TwoCaptchaServiceTests(TwoCaptchaFixture fixture) : base(fixture) { } - - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveTextCaptchaAsync_ValidCaptcha_ValidSolution() => TextCaptchaTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); - [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); - [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); - [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); - [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); - [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); - [Fact] public Task SolveKeyCaptchaAsync_NoProxy_ValidSolution() => KeyCaptchaTest_NoProxy(); - [Fact] public Task SolveKeyCaptchaAsync_WithProxy_ValidSolution() => KeyCaptchaTest_WithProxy(); - [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); - [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); - [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); - [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); - } -} \ No newline at end of file +public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture) : ServiceTests(fixture), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); + [Fact] public Task SolveTextCaptchaAsync_ValidCaptcha_ValidSolution() => TextCaptchaTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveKeyCaptchaAsync_NoProxy_ValidSolution() => KeyCaptchaTest_NoProxy(); + [Fact] public Task SolveKeyCaptchaAsync_WithProxy_ValidSolution() => KeyCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); + [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); +} diff --git a/CaptchaSharp/Enums/CaptchaType.cs b/CaptchaSharp/Enums/CaptchaType.cs index 98b0067..640832d 100644 --- a/CaptchaSharp/Enums/CaptchaType.cs +++ b/CaptchaSharp/Enums/CaptchaType.cs @@ -1,39 +1,38 @@ using System; -namespace CaptchaSharp.Enums +namespace CaptchaSharp.Enums; + +/// +[Flags] +public enum CaptchaType { - /// - [Flags] - public enum CaptchaType - { - /// A captcha that is a plaintext question. - TextCaptcha, + /// A captcha that is a plaintext question. + TextCaptcha = 1 << 0, - /// A captcha that is made by an image with some text to recognize. - ImageCaptcha, + /// A captcha that is made by an image with some text to recognize. + ImageCaptcha = 1 << 1, - /// A type of puzzle captcha. - FunCaptcha, + /// A type of puzzle captcha. + FunCaptcha = 1 << 2, - /// The Google ReCaptcha v2. - ReCaptchaV2, + /// The Google ReCaptcha v2. + ReCaptchaV2 = 1 << 3, - /// The Google ReCaptcha v3. - ReCaptchaV3, + /// The Google ReCaptcha v3. + ReCaptchaV3 = 1 << 4, - /// A type of token captcha. - HCaptcha, + /// A type of token captcha. + HCaptcha = 1 << 5, - /// A type of token captcha. - KeyCaptcha, + /// A type of token captcha. + KeyCaptcha = 1 << 6, - /// A type of challenge based captcha. - GeeTest, + /// A type of challenge based captcha. + GeeTest = 1 << 7, - /// A type of token captcha. - Capy, + /// A type of token captcha. + Capy = 1 << 8, - /// A type of challenge based captcha. - DataDome - } + /// A type of challenge based captcha. + DataDome = 1 << 9, } diff --git a/CaptchaSharp/Enums/ProxyType.cs b/CaptchaSharp/Enums/ProxyType.cs index 0323f38..eef157b 100644 --- a/CaptchaSharp/Enums/ProxyType.cs +++ b/CaptchaSharp/Enums/ProxyType.cs @@ -1,18 +1,17 @@ -namespace CaptchaSharp.Enums +namespace CaptchaSharp.Enums; + +/// +public enum ProxyType { /// - public enum ProxyType - { - /// - HTTP, + HTTP = 0, - /// - HTTPS, + /// + HTTPS = 1, - /// - SOCKS4, + /// + SOCKS4 = 2, - /// - SOCKS5 - } + /// + SOCKS5 = 3 } diff --git a/CaptchaSharp/Models/CaptchaResponse.cs b/CaptchaSharp/Models/CaptchaResponse.cs index 414a3d1..a6e1d59 100644 --- a/CaptchaSharp/Models/CaptchaResponse.cs +++ b/CaptchaSharp/Models/CaptchaResponse.cs @@ -1,30 +1,23 @@ using System; -namespace CaptchaSharp.Models +namespace CaptchaSharp.Models; + +/// A generic captcha response. +public class CaptchaResponse { - /// A generic captcha response. - public class CaptchaResponse + /// The captcha id which is needed to report the solution as bad. + public long Id { - private string id = "0"; - - /// The captcha id which is needed to report the solution as bad. - public long Id - { - get => long.Parse(id); - set => id = value.ToString(); - } + get => long.Parse(IdString); + set => IdString = value.ToString(); + } - /// - /// The captcha id which is needed to report the solution, if it's - /// a string instead of a long int. - /// - public string IdString - { - get => id; - set => id = value; - } + /// + /// The captcha id which is needed to report the solution, if it's + /// a string instead of a long int. + /// + public string IdString { get; set; } = "0"; - /// The time when the solution was received. - public DateTime Time { get; set; } = DateTime.Now; - } + /// The time when the solution was received. + public DateTime Time { get; set; } = DateTime.Now; } diff --git a/CaptchaSharp/Models/Proxy.cs b/CaptchaSharp/Models/Proxy.cs index 1d05ac3..7efadaf 100644 --- a/CaptchaSharp/Models/Proxy.cs +++ b/CaptchaSharp/Models/Proxy.cs @@ -1,49 +1,50 @@ using CaptchaSharp.Enums; using System.Linq; +using Newtonsoft.Json; -namespace CaptchaSharp.Models -{ - /// A generic proxy class. - public class Proxy - { - /// - public string Host { get; set; } +namespace CaptchaSharp.Models; - /// - public int Port { get; set; } +/// A generic proxy class. +public class Proxy +{ + /// + public string Host { get; set; } - /// - public ProxyType Type { get; set; } + /// + public int Port { get; set; } - /// - public string Username { get; set; } + /// + public ProxyType Type { get; set; } - /// - public string Password { get; set; } + /// + public string Username { get; set; } - /// The User-Agent header to be used in requests. - public string UserAgent { get; set; } + /// + public string Password { get; set; } - /// The cookies needed to get to the page where the captcha is shown. - public (string, string)[] Cookies { get; set; } + /// The User-Agent header to be used in requests. + public string UserAgent { get; set; } - /// Whether the proxy requires authentication. - public bool RequiresAuthentication => !string.IsNullOrEmpty(Username); + /// The cookies needed to get to the page where the captcha is shown. + public (string, string)[] Cookies { get; set; } - /// - public Proxy() { } + /// Whether the proxy requires authentication. + [JsonIgnore] + public bool RequiresAuthentication => !string.IsNullOrEmpty(Username); - /// - public Proxy(string host, int port, ProxyType type = ProxyType.HTTP, string username = "", string password = "") - { - Host = host; - Port = port; - Type = type; - Username = username; - Password = password; - } + /// + public Proxy() { } - internal string GetCookieString() - => Cookies != null ? string.Join("; ", Cookies.Select(c => $"{c.Item1}={c.Item2}")) : string.Empty; + /// + public Proxy(string host, int port, ProxyType type = ProxyType.HTTP, string username = "", string password = "") + { + Host = host; + Port = port; + Type = type; + Username = username; + Password = password; } + + internal string GetCookieString() + => Cookies != null ? string.Join("; ", Cookies.Select(c => $"{c.Item1}={c.Item2}")) : string.Empty; } From f3e108d595625fe1b92681c8f117c9140ec9427a Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 12 Jul 2024 13:04:25 +0200 Subject: [PATCH 03/67] Added datadome to TwoCaptchaService and its test --- CaptchaSharp.Tests/ServiceTests.cs | 38 +++++++++++++++++--- CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 1 + CaptchaSharp/CaptchaSharp.xml | 3 ++ CaptchaSharp/Services/TwoCaptchaService.cs | 37 +++++++++++++++++-- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index 03168b5..ca9e67a 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -2,6 +2,7 @@ using CaptchaSharp.Models; using Newtonsoft.Json.Linq; using System; +using System.Net; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -178,7 +179,6 @@ private async Task RecaptchaV3EnterpriseTest(Proxy proxy) private async Task FunCaptchaTest(Proxy proxy) { - // TODO: Find a valid funcaptcha to test! var solution = await Service.SolveFuncaptchaAsync( publicKey: "3EE79F8D-13A6-474B-9278-448EA19F79B3", serviceUrl: "https://client-api.arkoselabs.com", @@ -275,17 +275,45 @@ private async Task CapyTest(Proxy proxy) protected Task CapyTest_NoProxy() => CapyTest(null); protected Task CapyTest_WithProxy() => CapyTest(fixture.Config.Proxy); + // Proxy and User-Agent required private async Task DataDomeTest(Proxy proxy) { - // https://antoinevastel.com/bots/datadome + var site = "https://antoinevastel.com/bots/datadome"; + + // If it doesn't work, try a few times until it triggers + // the captcha + var cookieContainer = new CookieContainer(); + using var httpClientHandler = new HttpClientHandler(); + httpClientHandler.UseCookies = true; + httpClientHandler.CookieContainer = cookieContainer; + using var httpClient = new HttpClient(httpClientHandler); + + // The User-Agent must be the same as the one used to get the page + httpClient.DefaultRequestHeaders.Add("User-Agent", proxy.UserAgent); + + using var response = await httpClient.GetAsync(site); + var pageSource = await response.Content.ReadAsStringAsync(); + + var host = Regex.Match(pageSource, "'host':'([^']*)'").Groups[1].Value; + var initialCid = Regex.Match(pageSource, "'cid':'([^']*)'").Groups[1].Value; + var t = Regex.Match(pageSource, "'t':'([^']*)'").Groups[1].Value; + var s = Regex.Match(pageSource, @"'s':(\d+)").Groups[1].Value; + var e = Regex.Match(pageSource, "'e':'([^']*)'").Groups[1].Value; + var hsh = Regex.Match(pageSource, "'hsh':'([^']*)'").Groups[1].Value; + + // Get cid from "datadome" cookie + var cid = cookieContainer.GetCookies(new Uri(site))["datadome"]?.Value; + + var captchaUrl = + $"https://{host}/captcha/?initialCid={WebUtility.UrlEncode(initialCid)}&hash={hsh}&cid={cid}&t={t}&referer={WebUtility.UrlEncode(site)}&s={s}&e={e}&dm=cd"; + var solution = await Service.SolveDataDomeAsync( - siteUrl: "https://antoinevastel.com/bots/datadome", // Fill this when testing - captchaUrl: "", // Fill this when testing + siteUrl: site, + captchaUrl: captchaUrl, proxy); Assert.NotEqual(string.Empty, solution.Response); } - protected Task DataDomeTest_NoProxy() => DataDomeTest(null); protected Task DataDomeTest_WithProxy() => DataDomeTest(fixture.Config.Proxy); } diff --git a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs index 69237ab..2506a60 100644 --- a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs @@ -38,4 +38,5 @@ public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture) : ServiceTests(fi [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); + [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 8815129..2addf94 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1250,6 +1250,9 @@ + + + diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 8909f70..e6d2b50 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -313,6 +313,32 @@ public async override Task SolveCapyAsync : await TryGetResult(response, CaptchaType.Capy, cancellationToken).ConfigureAwait(false) ) as CapyResponse; } + + /// + public override async Task SolveDataDomeAsync(string siteUrl, string captchaUrl, Proxy proxy = null, + CancellationToken cancellationToken = default) + { + var response = await httpClient.PostMultipartToStringAsync + ("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "datadome") + .Add("captcha_url", captchaUrl) + .Add("pageurl", siteUrl) + .Add("soft_id", softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddACAOHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return (UseJsonFlag + ? await TryGetResult(response.Deserialize(), CaptchaType.DataDome, cancellationToken).ConfigureAwait(false) + : await TryGetResult(response, CaptchaType.DataDome, cancellationToken).ConfigureAwait(false) + ) as StringResponse; + } + #endregion #region Getting the result @@ -446,15 +472,22 @@ public async override Task ReportSolution protected IEnumerable<(string, string)> ConvertProxy(Proxy proxy) { if (proxy == null) - return new (string, string)[] { }; + return []; - return new (string, string)[] + var proxyParams = new List<(string, string)> { ("proxy", proxy.RequiresAuthentication ? $"{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}" : $"{proxy.Host}:{proxy.Port}"), ("proxytype", proxy.Type.ToString()) }; + + if (proxy.UserAgent is not null) + { + proxyParams.Add(("userAgent", proxy.UserAgent)); + } + + return proxyParams; } #endregion From 4a89c6abb7b0b73663dade2970403fd92326f0b2 Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 12 Jul 2024 16:14:37 +0200 Subject: [PATCH 04/67] Added cf turnstile captcha for 2captcha --- CaptchaSharp.Tests/ServiceTests.cs | 18 +++++ CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 2 + CaptchaSharp/CaptchaService.cs | 31 +++++++++ CaptchaSharp/CaptchaSharp.xml | 33 +++++++++ CaptchaSharp/Enums/CaptchaType.cs | 3 + CaptchaSharp/Services/TwoCaptchaService.cs | 71 +++++++++++++++++--- 6 files changed, 148 insertions(+), 10 deletions(-) diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index ca9e67a..c917a84 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -316,4 +316,22 @@ private async Task DataDomeTest(Proxy proxy) } protected Task DataDomeTest_WithProxy() => DataDomeTest(fixture.Config.Proxy); + + private async Task CloudflareTurnstileTest(Proxy proxy) + { + var solution = await Service.SolveCloudflareTurnstileAsync( + siteKey: "0x4AAAAAAAVrOwQWPlm3Bnr5", + siteUrl: "https://2captcha.com/demo/cloudflare-turnstile", + proxy: proxy); + + Assert.NotEqual(string.Empty, solution.Response); + } + + protected Task CloudflareTurnstileTest_NoProxy() => CloudflareTurnstileTest(new Proxy + { + // User-Agent required + UserAgent = fixture.Config.Proxy.UserAgent + }); + + protected Task CloudflareTurnstileTest_WithProxy() => CloudflareTurnstileTest(fixture.Config.Proxy); } diff --git a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs index 2506a60..941f65a 100644 --- a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs @@ -39,4 +39,6 @@ public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture) : ServiceTests(fi [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); } diff --git a/CaptchaSharp/CaptchaService.cs b/CaptchaSharp/CaptchaService.cs index 65778c5..dc1ddc2 100644 --- a/CaptchaSharp/CaptchaService.cs +++ b/CaptchaSharp/CaptchaService.cs @@ -388,6 +388,37 @@ public virtual Task SolveDataDomeAsync { throw new NotSupportedException(); } + + /// Solves a Cloudflare Turnstile captcha. + /// + /// The site key, can be found in the webpage source or by sniffing requests. + /// The URL where the captcha appears. + /// Value of optional action parameter you found on page, can be defined in data-action attribute or passed to turnstile.render call. + /// The value of cData passed to turnstile.render call. Also can be defined in data-cdata attribute. + /// The value of chlPageData passed to turnstile.render call. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveCloudflareTurnstileAsync + (string siteKey, string siteUrl, string action = null, string data = null, string pageData = null, Proxy proxy = null, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } /// /// Reports a captcha solution as good or bad to the service. diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 2addf94..6837a3a 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -333,6 +333,33 @@ + + Solves a Cloudflare Turnstile captcha. + + The site key, can be found in the webpage source or by sniffing requests. + The URL where the captcha appears. + Value of optional action parameter you found on page, can be defined in data-action attribute or passed to turnstile.render call. + The value of cData passed to turnstile.render call. Also can be defined in data-cdata attribute. + The value of chlPageData passed to turnstile.render call. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + + Reports a captcha solution as good or bad to the service. @@ -606,6 +633,9 @@ A type of challenge based captcha. + + Cloudflare's Turnstile captcha. + @@ -1253,6 +1283,9 @@ + + + diff --git a/CaptchaSharp/Enums/CaptchaType.cs b/CaptchaSharp/Enums/CaptchaType.cs index 640832d..9fa700f 100644 --- a/CaptchaSharp/Enums/CaptchaType.cs +++ b/CaptchaSharp/Enums/CaptchaType.cs @@ -35,4 +35,7 @@ public enum CaptchaType /// A type of challenge based captcha. DataDome = 1 << 9, + + /// Cloudflare's Turnstile captcha. + CloudflareTurnstile = 1 << 10, } diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index e6d2b50..1dfc8c9 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -318,6 +318,12 @@ public async override Task SolveCapyAsync public override async Task SolveDataDomeAsync(string siteUrl, string captchaUrl, Proxy proxy = null, CancellationToken cancellationToken = default) { + // Make sure there is a proxy with a User-Agent + if (proxy == null || string.IsNullOrEmpty(proxy.Host) || string.IsNullOrEmpty(proxy.UserAgent)) + { + throw new ArgumentException("A proxy with a User-Agent is required for DataDome captchas."); + } + var response = await httpClient.PostMultipartToStringAsync ("in.php", new StringPairCollection() @@ -339,6 +345,41 @@ public override async Task SolveDataDomeAsync(string siteUrl, st ) as StringResponse; } + /// + public override async Task SolveCloudflareTurnstileAsync(string siteKey, string siteUrl, + string action, string data, string pageData, Proxy proxy = null, + CancellationToken cancellationToken = default) + { + // Make sure there is a proxy with a User-Agent + if (proxy == null || string.IsNullOrEmpty(proxy.UserAgent)) + { + throw new ArgumentException("A User-Agent is required for Cloudflare Turnstile captchas."); + } + + var response = await httpClient.PostMultipartToStringAsync + ("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "turnstile") + .Add("sitekey", siteKey) + .Add("pageurl", siteUrl) + .Add("action", action, !string.IsNullOrEmpty(action)) + .Add("data", data, !string.IsNullOrEmpty(data)) + .Add("pagedata", pageData, !string.IsNullOrEmpty(pageData)) + .Add("soft_id", softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddACAOHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return (UseJsonFlag + ? await TryGetResult(response.Deserialize(), CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false) + : await TryGetResult(response, CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false) + ) as StringResponse; + } + #endregion #region Getting the result @@ -471,22 +512,32 @@ public async override Task ReportSolution /// protected IEnumerable<(string, string)> ConvertProxy(Proxy proxy) { - if (proxy == null) - return []; - - var proxyParams = new List<(string, string)> + // TODO: Remove UserAgent and Cookies from Proxy class + if (proxy is null) { - ("proxy", proxy.RequiresAuthentication - ? $"{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}" - : $"{proxy.Host}:{proxy.Port}"), - ("proxytype", proxy.Type.ToString()) - }; - + return []; + } + + var proxyParams = new List<(string, string)>(); + if (proxy.UserAgent is not null) { proxyParams.Add(("userAgent", proxy.UserAgent)); } + if (string.IsNullOrEmpty(proxy.Host)) + { + return proxyParams; + } + + proxyParams.AddRange( + [ + ("proxy", proxy.RequiresAuthentication + ? $"{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}" + : $"{proxy.Host}:{proxy.Port}"), + ("proxytype", proxy.Type.ToString()) + ]); + return proxyParams; } #endregion From 37e378259277829e5295fa2aac2c922cf06c9ccb Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 12 Jul 2024 19:42:58 +0200 Subject: [PATCH 05/67] Huge refactor WIP (broken build don't use) --- .../AnyCaptchaService.cs | 13 +- .../AzCaptchaService.cs | 13 +- .../CaptchaSharp.Services.More.csproj | 1 + .../CaptchasIOService.cs | 2 +- .../RuCaptchaService.cs | 2 +- .../SolveCaptchaService.cs | 2 +- .../SolveRecaptchaService.cs | 152 +-- .../TrueCaptchaService.cs | 4 +- CaptchaSharp.Tests/CaptchaSharp.Tests.csproj | 1 + CaptchaSharp.Tests/ServiceTests.cs | 2 +- CaptchaSharp/CaptchaService.cs | 845 +++++++-------- CaptchaSharp/CaptchaSharp.csproj | 1 + CaptchaSharp/Enums/CaptchaLanguage.cs | 231 +++-- CaptchaSharp/Enums/CaptchaLanguageGroup.cs | 19 +- .../Enums/CaptchaServiceCapabilities.cs | 51 +- CaptchaSharp/Enums/CharacterSet.cs | 27 +- .../Exceptions/BadAuthenticationException.cs | 33 +- .../Exceptions/TaskCreationException.cs | 33 +- .../Exceptions/TaskReportException.cs | 35 +- .../Exceptions/TaskSolutionException.cs | 33 +- CaptchaSharp/Extensions.cs | 188 ---- .../Extensions/CaptchaLanguageExtensions.cs | 71 ++ .../Extensions/HttpClientExtensions.cs | 93 ++ CaptchaSharp/Extensions/StringExtensions.cs | 38 + .../LowercasePropertyNamesContractResolver.cs | 11 + .../LowercasePropertyNamesContractResolver.cs | 10 - CaptchaSharp/Models/CaptchaResponse.cs | 2 +- CaptchaSharp/Models/CaptchaTask.cs | 65 +- CaptchaSharp/Models/CapyResponse.cs | 21 +- CaptchaSharp/Models/GeeTestResponse.cs | 21 +- CaptchaSharp/Models/ImageCaptchaOptions.cs | 45 +- CaptchaSharp/Models/Proxy.cs | 12 +- CaptchaSharp/Models/StringPairCollection.cs | 79 +- CaptchaSharp/Models/StringResponse.cs | 13 +- CaptchaSharp/Models/TextCaptchaOptions.cs | 17 +- CaptchaSharp/Services/AntiCaptchaService.cs | 25 +- CaptchaSharp/Services/CapMonsterService.cs | 25 +- CaptchaSharp/Services/CapSolverService.cs | 19 +- .../Services/CustomAntiCaptchaService.cs | 2 +- .../Services/CustomTwoCaptchaService.cs | 79 +- CaptchaSharp/Services/DeCaptcherService.cs | 290 +++--- .../Services/DeathByCaptchaService.cs | 444 ++++---- CaptchaSharp/Services/ImageTyperzService.cs | 576 ++++++----- CaptchaSharp/Services/NineKWService.cs | 484 +++++---- CaptchaSharp/Services/TwoCaptchaService.cs | 975 ++++++++++-------- 45 files changed, 2672 insertions(+), 2433 deletions(-) delete mode 100644 CaptchaSharp/Extensions.cs create mode 100644 CaptchaSharp/Extensions/CaptchaLanguageExtensions.cs create mode 100644 CaptchaSharp/Extensions/HttpClientExtensions.cs create mode 100644 CaptchaSharp/Extensions/StringExtensions.cs create mode 100644 CaptchaSharp/Helpers/LowercasePropertyNamesContractResolver.cs delete mode 100644 CaptchaSharp/LowercasePropertyNamesContractResolver.cs diff --git a/CaptchaSharp.Services.More/AnyCaptchaService.cs b/CaptchaSharp.Services.More/AnyCaptchaService.cs index d5a7cf4..b719a38 100644 --- a/CaptchaSharp.Services.More/AnyCaptchaService.cs +++ b/CaptchaSharp.Services.More/AnyCaptchaService.cs @@ -4,12 +4,17 @@ namespace CaptchaSharp.Services.More { - /// The service provided by https://anycaptcha.com/ + /// + /// The service provided by https://anycaptcha.com/ + /// public class AnyCaptchaService : CustomAntiCaptchaService { - /// Initializes a using the given and - /// . If is null, a default one will be created. - public AnyCaptchaService(string apiKey, HttpClient httpClient = null) + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public AnyCaptchaService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("https://api.anycaptcha.com"), httpClient) { SupportedCaptchaTypes = diff --git a/CaptchaSharp.Services.More/AzCaptchaService.cs b/CaptchaSharp.Services.More/AzCaptchaService.cs index 4c2252e..aef85db 100644 --- a/CaptchaSharp.Services.More/AzCaptchaService.cs +++ b/CaptchaSharp.Services.More/AzCaptchaService.cs @@ -4,12 +4,17 @@ namespace CaptchaSharp.Services.More { - /// The service provided by https://azcaptcha.com/ + /// + /// The service provided by https://azcaptcha.com/ + /// public class AzCaptchaService : CustomTwoCaptchaService { - /// Initializes a using the given and - /// . If is null, a default one will be created. - public AzCaptchaService(string apiKey, HttpClient httpClient = null) + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public AzCaptchaService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("http://azcaptcha.com"), httpClient, false) { SupportedCaptchaTypes = diff --git a/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj b/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj index fe8bef4..a26a420 100644 --- a/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj +++ b/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj @@ -11,6 +11,7 @@ https://github.com/openbullet/CaptchaSharp 1.0.6 True + enable diff --git a/CaptchaSharp.Services.More/CaptchasIOService.cs b/CaptchaSharp.Services.More/CaptchasIOService.cs index c94bed7..65b4f6c 100644 --- a/CaptchaSharp.Services.More/CaptchasIOService.cs +++ b/CaptchaSharp.Services.More/CaptchasIOService.cs @@ -9,7 +9,7 @@ public class CaptchasIOService : CustomTwoCaptchaService { /// Initializes a using the given and /// . If is null, a default one will be created. - public CaptchasIOService(string apiKey, HttpClient httpClient = null) + public CaptchasIOService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("https://api.captchas.io"), httpClient, false) { SupportedCaptchaTypes = diff --git a/CaptchaSharp.Services.More/RuCaptchaService.cs b/CaptchaSharp.Services.More/RuCaptchaService.cs index 183e90b..d6021fe 100644 --- a/CaptchaSharp.Services.More/RuCaptchaService.cs +++ b/CaptchaSharp.Services.More/RuCaptchaService.cs @@ -8,7 +8,7 @@ public class RuCaptchaService : CustomTwoCaptchaService { /// Initializes a using the given and /// . If is null, a default one will be created. - public RuCaptchaService(string apiKey, HttpClient httpClient = null) + public RuCaptchaService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("http://rucaptcha.com"), httpClient) { } } } diff --git a/CaptchaSharp.Services.More/SolveCaptchaService.cs b/CaptchaSharp.Services.More/SolveCaptchaService.cs index 9ff9644..650dcf7 100644 --- a/CaptchaSharp.Services.More/SolveCaptchaService.cs +++ b/CaptchaSharp.Services.More/SolveCaptchaService.cs @@ -9,7 +9,7 @@ public class SolveCaptchaService : CustomTwoCaptchaService { /// Initializes a using the given and /// . If is null, a default one will be created. - public SolveCaptchaService(string apiKey, HttpClient httpClient = null) + public SolveCaptchaService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("http://api.solvecaptcha.com"), httpClient, false) { SupportedCaptchaTypes = diff --git a/CaptchaSharp.Services.More/SolveRecaptchaService.cs b/CaptchaSharp.Services.More/SolveRecaptchaService.cs index 18e27fd..5d2cb82 100644 --- a/CaptchaSharp.Services.More/SolveRecaptchaService.cs +++ b/CaptchaSharp.Services.More/SolveRecaptchaService.cs @@ -6,104 +6,104 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services.More +namespace CaptchaSharp.Services.More; + +/// The service provided by https://solverecaptcha.com/ +public class SolveRecaptchaService : CustomTwoCaptchaService { - /// The service provided by https://solverecaptcha.com/ - public class SolveRecaptchaService : CustomTwoCaptchaService + /// Initializes a using the given and + /// . If is null, a default one will be created. + public SolveRecaptchaService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("http://api.solverecaptcha.com"), httpClient) { - /// Initializes a using the given and - /// . If is null, a default one will be created. - public SolveRecaptchaService(string apiKey, HttpClient httpClient = null) - : base(apiKey, new Uri("http://api.solverecaptcha.com"), httpClient) - { - this.httpClient.DefaultRequestHeaders.Host = "api.solverecaptcha.com"; - this.httpClient.Timeout = Timeout; + this.HttpClient.DefaultRequestHeaders.Host = "api.solverecaptcha.com"; + this.HttpClient.Timeout = Timeout; - SupportedCaptchaTypes = - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3; - } + SupportedCaptchaTypes = + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3; + } - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - // There is no balance since this service has a monthly subscription - return await Task.Run(() => 999).ConfigureAwait(false); - } + /// + public override Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + // There is no balance since this service has a monthly subscription + return Task.FromResult(999m); + } - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("", + /// + public override async Task SolveRecaptchaV2Async + (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.GetStringAsync + ("", GetAuthPair() - .Add("sitekey", siteKey) - .Add("pageurl", siteUrl) - .Add("version", invisible ? "invisible" : "v2") - .Add("invisible", Convert.ToInt32(invisible).ToString()) - .Add(ConvertProxy(proxy)), + .Add("sitekey", siteKey) + .Add("pageurl", siteUrl) + .Add("version", invisible ? "invisible" : "v2") + .Add("invisible", Convert.ToInt32(invisible).ToString()) + .Add(ConvertProxy(proxy)), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - if (IsErrorCode(response)) - ThrowException(response); + if (IsErrorCode(response)) + ThrowException(response); - return new StringResponse { Id = 0, Response = TakeSecondSlice(response) }; - } + return new StringResponse { Id = 0, Response = TakeSecondSlice(response) }; + } - /// - public async override Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("", + /// + public override async Task SolveRecaptchaV3Async + (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.GetStringAsync + ("", GetAuthPair() - .Add("sitekey", siteKey) - .Add("pageurl", siteUrl) - .Add("action", action) - .Add("min_score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) - .Add("version", "v3") - .Add(ConvertProxy(proxy)), + .Add("sitekey", siteKey) + .Add("pageurl", siteUrl) + .Add("action", action) + .Add("min_score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) + .Add("version", "v3") + .Add(ConvertProxy(proxy)), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - if (IsErrorCode(response)) - ThrowException(response); + if (IsErrorCode(response)) + ThrowException(response); - return new StringResponse { Id = 0, Response = TakeSecondSlice(response) }; - } + return new StringResponse { Id = 0, Response = TakeSecondSlice(response) }; + } - /// - public override Task ReportSolution - (long taskId, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// + public override Task ReportSolution + (long taskId, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - private StringPairCollection GetAuthPair() - => new StringPairCollection().Add("key", ApiKey); + private StringPairCollection GetAuthPair() + => new StringPairCollection().Add("key", ApiKey); - private void ThrowException(string response) + private void ThrowException(string response) + { + switch (response) { - switch (response) - { - case "ERROR_API_KEY_NOT_FOUND": - case "ERROR_ACCESS_DENIED": - throw new BadAuthenticationException(response); + case "ERROR_API_KEY_NOT_FOUND": + case "ERROR_ACCESS_DENIED": + throw new BadAuthenticationException(response); - case "ERROR_NO_AVAILABLE_THREADS": - throw new TaskCreationException(response); + case "ERROR_NO_AVAILABLE_THREADS": + throw new TaskCreationException(response); - case "ERROR_CAPTCHA_UNSOLVABLE": - throw new TaskSolutionException(response); + case "ERROR_CAPTCHA_UNSOLVABLE": + throw new TaskSolutionException(response); - default: - throw new Exception(response); - } + default: + throw new Exception(response); } } } diff --git a/CaptchaSharp.Services.More/TrueCaptchaService.cs b/CaptchaSharp.Services.More/TrueCaptchaService.cs index ec9ac73..8c08b6d 100644 --- a/CaptchaSharp.Services.More/TrueCaptchaService.cs +++ b/CaptchaSharp.Services.More/TrueCaptchaService.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; namespace CaptchaSharp.Services.More { @@ -63,7 +64,8 @@ public async override Task SolveImageCaptchaAsync var response = await httpClient.PostJsonToStringAsync ("one/gettext", content, - cancellationToken, false) + camelizeKeys: false, + cancellationToken: cancellationToken) .ConfigureAwait(false); var jObject = JObject.Parse(response); diff --git a/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj b/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj index 7b0c002..e641cb6 100644 --- a/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj +++ b/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj @@ -3,6 +3,7 @@ net8.0 false + enable diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index c917a84..cd1584f 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -183,7 +183,7 @@ private async Task FunCaptchaTest(Proxy proxy) publicKey: "3EE79F8D-13A6-474B-9278-448EA19F79B3", serviceUrl: "https://client-api.arkoselabs.com", siteUrl: "https://www.arkoselabs.com/arkose-matchkey/", - noJS: false, + noJs: false, proxy); Assert.NotEqual(string.Empty, solution.Response); diff --git a/CaptchaSharp/CaptchaService.cs b/CaptchaSharp/CaptchaService.cs index dc1ddc2..16974ee 100644 --- a/CaptchaSharp/CaptchaService.cs +++ b/CaptchaSharp/CaptchaService.cs @@ -5,473 +5,428 @@ using System.Threading; using System.Threading.Tasks; -namespace CaptchaSharp -{ - /// Abstract class for a generic captcha solving service. - public abstract class CaptchaService - { - /// The maximum allowed time for captcha completion. - /// If this is exceeded, a is thrown. - public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(3); +namespace CaptchaSharp; - /// The interval at which the service will be polled for a solution. - public TimeSpan PollingInterval { get; set; } = TimeSpan.FromSeconds(5); +/// Abstract class for a generic captcha solving service. +public abstract class CaptchaService +{ + /// The maximum allowed time for captcha completion. + /// If this is exceeded, a is thrown. + public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(3); - /// Returns a list of flags that denote the capabilities of the service in terms of additional - /// parameters to provide when solving text or image based captchas. - public virtual CaptchaServiceCapabilities Capabilities => CaptchaServiceCapabilities.None; + /// The interval at which the service will be polled for a solution. + public TimeSpan PollingInterval { get; set; } = TimeSpan.FromSeconds(5); - /// Retrieves the remaining balance in USD as a . - /// Thrown when the provided credentials are invalid. - public virtual Task GetBalanceAsync - (CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Returns a list of flags that denote the capabilities of the service in terms of additional + /// parameters to provide when solving text or image based captchas. + public virtual CaptchaServiceCapabilities Capabilities => CaptchaServiceCapabilities.None; - /// Solves a text based captcha. - /// - /// The captcha question. - /// - /// - /// Any additional options like the language of the question. - /// If null they will be disregarded. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveTextCaptchaAsync - (string text, TextCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// Solves an image based captcha. - /// - /// The captcha image encoded as a base64 string. - /// - /// - /// Any additional options like whether the captcha is case sensitive. - /// If null they will be disregarded. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// Solves a Google ReCaptcha V2. - /// - /// The site key, can be found in the webpage source or by sniffing requests. - /// The URL where the captcha appears. - /// Whether the captcha is not in a clickable format on the page. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, bool invisible = false, Proxy proxy = null, CancellationToken cancellationToken = default) - => SolveRecaptchaV2Async(siteKey, siteUrl, "", false, invisible, proxy, cancellationToken); + /// Retrieves the remaining balance in USD as a . + /// Thrown when the provided credentials are invalid. + public virtual Task GetBalanceAsync( + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a Google ReCaptcha V2. - /// - /// The site key, can be found in the webpage source or by sniffing requests. - /// The URL where the captcha appears. - /// The value of the 's' or 'data-s' field (currently only for Google services). - /// Whether this is an Enterprise ReCaptcha V2. - /// Whether the captcha is not in a clickable format on the page. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a text based captcha. + /// + /// The captcha question. + /// + /// + /// Any additional options like the language of the question. + /// If null they will be disregarded. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveTextCaptchaAsync( + string text, TextCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a Google ReCaptcha V3. - /// - /// The site key, can be found in the webpage source or by sniffing requests. - /// The URL where the captcha appears. - /// The action to execute. Can be found in the webpage source or in a js file. - /// The minimum human-to-robot score necessary to solve the challenge. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action, float minScore, Proxy proxy = null, CancellationToken cancellationToken = default) - => SolveRecaptchaV3Async(siteKey, siteUrl, action, minScore, false, proxy, cancellationToken); + /// Solves an image based captcha. + /// + /// The captcha image encoded as a base64 string. + /// + /// + /// Any additional options like whether the captcha is case-sensitive. + /// If null they will be disregarded. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a Google ReCaptcha V3. - /// - /// The site key, can be found in the webpage source or by sniffing requests. - /// The URL where the captcha appears. - /// The action to execute. Can be found in the webpage source or in a js file. - /// The minimum human-to-robot score necessary to solve the challenge. - /// Whether this is an Enterprise ReCaptcha V3. - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action, float minScore, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a Google ReCaptcha V2. + /// + /// The site key, can be found in the webpage source or by sniffing requests. + /// The URL where the captcha appears. + /// The value of the 's' or 'data-s' field (currently only for Google services). + /// Whether this is an Enterprise ReCaptcha V2. + /// Whether the captcha is not in a clickable format on the page. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// Solves a Google ReCaptcha V3. + /// + /// The site key, can be found in the webpage source or by sniffing requests. + /// The URL where the captcha appears. + /// The action to execute. Can be found in the webpage source or in a js file. + /// The minimum human-to-robot score necessary to solve the challenge. + /// Whether this is an Enterprise ReCaptcha V3. + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a FunCaptcha. - /// - /// - /// Can be found inside data-pkey parameter of funcaptcha's div element or inside an input element - /// with name fc-token. Just extract the key indicated after pk from the value of this element. - /// - /// - /// - /// Can be found in the fc-token, that is a value of the surl parameter. - /// - /// - /// The URL where the captcha appears. - /// - /// - /// Whether to solve the challenge without JavaScript enabled. This is not supported by every service and - /// it provides a partial token. - /// - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveFuncaptchaAsync - (string publicKey, string serviceUrl, string siteUrl, bool noJS = false, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a FunCaptcha. + /// + /// + /// Can be found inside data-pkey parameter of funcaptcha's div element or inside an input element + /// with name fc-token. Just extract the key indicated after pk from the value of this element. + /// + /// + /// + /// Can be found in the fc-token, that is a value of the surl parameter. + /// + /// + /// The URL where the captcha appears. + /// + /// + /// Whether to solve the challenge without JavaScript enabled. This is not supported by every service and + /// it provides a partial token. + /// + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, + bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a HCaptcha. - /// - /// The site key, can be found in the webpage source or by sniffing requests. - /// The URL where the captcha appears. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveHCaptchaAsync - (string siteKey, string siteUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a HCaptcha. + /// + /// The site key, can be found in the webpage source or by sniffing requests. + /// The URL where the captcha appears. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a KeyCaptcha. - /// - /// s_s_c_user_id parameter in the webpage source code. - /// s_s_c_session_id parameter in the webpage source code. - /// s_s_c_web_server_sign parameter in the webpage source code. - /// s_s_c_web_server_sign2 parameter in the webpage source code. - /// The URL where the captcha appears. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveKeyCaptchaAsync - (string userId, string sessionId, string webServerSign1, string webServerSign2, string siteUrl, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a KeyCaptcha. + /// + /// s_s_c_user_id parameter in the webpage source code. + /// s_s_c_session_id parameter in the webpage source code. + /// s_s_c_web_server_sign parameter in the webpage source code. + /// s_s_c_web_server_sign2 parameter in the webpage source code. + /// The URL where the captcha appears. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveKeyCaptchaAsync( + string userId, string sessionId, string webServerSign1, string webServerSign2, string siteUrl, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a GeeTest captcha. - /// - /// The static public key assigned to the website found in the webpage source code. - /// The dynamic challenge key found in the webpage source code. - /// The api_server parameter found in the webpage source code. - /// The URL where the captcha appears. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and three solution parameters - /// (Challenge, Validate and SecCode) that you will need to provide when you submit the form. - /// - /// - /// - /// - /// - public virtual Task SolveGeeTestAsync - (string gt, string challenge, string apiServer, string siteUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a GeeTest captcha. + /// + /// The static public key assigned to the website found in the webpage source code. + /// The dynamic challenge key found in the webpage source code. + /// The api_server parameter found in the webpage source code. + /// The URL where the captcha appears. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and three solution parameters + /// (Challenge, Validate and SecCode) that you will need to provide when you submit the form. + /// + /// + /// + /// + /// + public virtual Task SolveGeeTestAsync( + string gt, string challenge, string apiServer, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a Capy captcha. - /// - /// The site key, can be found in the webpage source or by sniffing requests. - /// The URL where the captcha appears. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveCapyAsync - (string siteKey, string siteUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a Capy captcha. + /// + /// The site key, can be found in the webpage source or by sniffing requests. + /// The URL where the captcha appears. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveCapyAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a DataDome captcha. - /// - /// The URL where the captcha appears. - /// The URL of the captcha. It is obtained from the 'dd' object in a script - /// inside the HTML and the 'datadome' cookie - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext (a.k.a. a valid datadome session cookie). - /// - /// - /// - /// - /// - public virtual Task SolveDataDomeAsync - (string siteUrl, string captchaUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } + /// Solves a DataDome captcha. + /// + /// The URL where the captcha appears. + /// The URL of the captcha. It is obtained from the 'dd' object in a script + /// inside the HTML and the 'datadome' cookie + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext (a.k.a. a valid datadome session cookie). + /// + /// + /// + /// + /// + public virtual Task SolveDataDomeAsync( + string siteUrl, string captchaUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - /// Solves a Cloudflare Turnstile captcha. - /// - /// The site key, can be found in the webpage source or by sniffing requests. - /// The URL where the captcha appears. - /// Value of optional action parameter you found on page, can be defined in data-action attribute or passed to turnstile.render call. - /// The value of cData passed to turnstile.render call. Also can be defined in data-cdata attribute. - /// The value of chlPageData passed to turnstile.render call. - /// - /// - /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - /// fetch the captcha without using a proxy. - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - /// A containing the captcha id to be used with - /// and the - /// captcha solution as plaintext. - /// - /// - /// - /// - /// - public virtual Task SolveCloudflareTurnstileAsync - (string siteKey, string siteUrl, string action = null, string data = null, string pageData = null, Proxy proxy = null, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// Reports a captcha solution as good or bad to the service. - /// Mostly used for reporting bad solutions for image captchas and get the funds back. - /// Make sure to not abuse this system or the service might ban you from accessing it! - /// - /// - /// The ID of the captcha that you got inside your . - /// The type of captcha you want to report. - /// - /// - /// If true, the captcha will be reported as correctly solved (this is not supported by most services). - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - public virtual Task ReportSolution - (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - protected async Task TryGetResult - (CaptchaTask task, CancellationToken cancellationToken = default) - { - var start = DateTime.Now; - CaptchaResponse result; + /// Solves a Cloudflare Turnstile captcha. + /// + /// The site key, can be found in the webpage source or by sniffing requests. + /// The URL where the captcha appears. + /// Value of optional action parameter you found on page, can be defined in data-action attribute or passed to turnstile.render call. + /// The value of cData passed to turnstile.render call. Also can be defined in data-cdata attribute. + /// The value of chlPageData passed to turnstile.render call. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - // Initial delay - await Task.Delay(PollingInterval); + /// + /// Reports a captcha solution as good or bad to the service. + /// Mostly used for reporting bad solutions for image captchas and get the funds back. + /// Make sure to not abuse this system or the service might ban you from accessing it! + /// + /// + /// The ID of the captcha that you got inside your . + /// The type of captcha you want to report. + /// + /// + /// If true, the captcha will be reported as correctly solved (this is not supported by most services). + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + public virtual Task ReportSolution + (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } - do - { - cancellationToken.ThrowIfCancellationRequested(); + /// + protected async Task GetResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + var start = DateTime.Now; + T? result; - result = await CheckResult(task, cancellationToken); - await Task.Delay(PollingInterval); - } - while (!task.Completed && DateTime.Now - start < Timeout); + // Initial delay + await Task.Delay(PollingInterval, cancellationToken); - if (!task.Completed) - throw new TimeoutException(); + do + { + cancellationToken.ThrowIfCancellationRequested(); - return result; + result = await CheckResult(task, cancellationToken); + await Task.Delay(PollingInterval, cancellationToken); } + while (!task.Completed && DateTime.Now - start < Timeout); - /// - protected virtual Task CheckResult - (CaptchaTask task, CancellationToken cancellationToken = default) + if (!task.Completed || result is null) { - throw new NotImplementedException(); + throw new TimeoutException(); } + + return result; + } + + /// + protected virtual Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + throw new NotImplementedException(); } } diff --git a/CaptchaSharp/CaptchaSharp.csproj b/CaptchaSharp/CaptchaSharp.csproj index 3afd73d..cda56e8 100644 --- a/CaptchaSharp/CaptchaSharp.csproj +++ b/CaptchaSharp/CaptchaSharp.csproj @@ -13,6 +13,7 @@ 1.0.13 Added capsolver.com and datadome captcha True + enable diff --git a/CaptchaSharp/Enums/CaptchaLanguage.cs b/CaptchaSharp/Enums/CaptchaLanguage.cs index 49980b2..b6a825e 100644 --- a/CaptchaSharp/Enums/CaptchaLanguage.cs +++ b/CaptchaSharp/Enums/CaptchaLanguage.cs @@ -1,174 +1,173 @@ -namespace CaptchaSharp.Enums +namespace CaptchaSharp.Enums; + +/// +public enum CaptchaLanguage { /// - public enum CaptchaLanguage - { - /// - NotSpecified, + NotSpecified, - /// - English, + /// + English, - /// - Russian, + /// + Russian, - /// - Spanish, + /// + Spanish, - /// - Portuguese, + /// + Portuguese, - /// - Ukrainian, + /// + Ukrainian, - /// - Vietnamese, + /// + Vietnamese, - /// - French, + /// + French, - /// - Indonesian, + /// + Indonesian, - /// - Arab, + /// + Arab, - /// - Japanese, + /// + Japanese, - /// - Turkish, + /// + Turkish, - /// - German, + /// + German, - /// - Chinese, + /// + Chinese, - /// - Philippine, + /// + Philippine, - /// - Polish, + /// + Polish, - /// - Thai, + /// + Thai, - /// - Italian, + /// + Italian, - /// - Dutch, + /// + Dutch, - /// - Slovak, + /// + Slovak, - /// - Bulgarian, + /// + Bulgarian, - /// - Romanian, + /// + Romanian, - /// - Hungarian, + /// + Hungarian, - /// - Korean, + /// + Korean, - /// - Czech, + /// + Czech, - /// - Azerbaijani, + /// + Azerbaijani, - /// - Persian, + /// + Persian, - /// - Bengali, + /// + Bengali, - /// - Greek, + /// + Greek, - /// - Lithuanian, + /// + Lithuanian, - /// - Latvian, + /// + Latvian, - /// - Swedish, + /// + Swedish, - /// - Serbian, + /// + Serbian, - /// - Croatian, + /// + Croatian, - /// - Hebrew, + /// + Hebrew, - /// - Hindi, + /// + Hindi, - /// - Norwegian, + /// + Norwegian, - /// - Slovenian, + /// + Slovenian, - /// - Danish, + /// + Danish, - /// - Uzbek, + /// + Uzbek, - /// - Finnish, + /// + Finnish, - /// - Catalan, + /// + Catalan, - /// - Georgian, + /// + Georgian, - /// - Malay, + /// + Malay, - /// - Telugu, + /// + Telugu, - /// - Estonian, + /// + Estonian, - /// - Malayalam, + /// + Malayalam, - /// - Belorussian, + /// + Belorussian, - /// - Kazakh, + /// + Kazakh, - /// - Marathi, + /// + Marathi, - /// - Nepali, + /// + Nepali, - /// - Burmese, + /// + Burmese, - /// - Bosnian, + /// + Bosnian, - /// - Armenian, + /// + Armenian, - /// - Macedonian, + /// + Macedonian, - /// - Punjabi - } + /// + Punjabi } diff --git a/CaptchaSharp/Enums/CaptchaLanguageGroup.cs b/CaptchaSharp/Enums/CaptchaLanguageGroup.cs index cfce39d..4f0967e 100644 --- a/CaptchaSharp/Enums/CaptchaLanguageGroup.cs +++ b/CaptchaSharp/Enums/CaptchaLanguageGroup.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Enums +namespace CaptchaSharp.Enums; + +/// +public enum CaptchaLanguageGroup { /// - public enum CaptchaLanguageGroup - { - /// - NotSpecified = 0, + NotSpecified = 0, - /// - Cyrillic = 1, + /// + Cyrillic = 1, - /// - Latin = 2 - } + /// + Latin = 2 } diff --git a/CaptchaSharp/Enums/CaptchaServiceCapabilities.cs b/CaptchaSharp/Enums/CaptchaServiceCapabilities.cs index 3552024..db14d6d 100644 --- a/CaptchaSharp/Enums/CaptchaServiceCapabilities.cs +++ b/CaptchaSharp/Enums/CaptchaServiceCapabilities.cs @@ -1,41 +1,38 @@ using System; -using System.Collections.Generic; -using System.Text; -namespace CaptchaSharp.Enums +namespace CaptchaSharp.Enums; + +/// +[Flags] +public enum CaptchaServiceCapabilities { /// - [Flags] - public enum CaptchaServiceCapabilities - { - /// - None = 0, + None = 0, - /// - LanguageGroup = 1, + /// + LanguageGroup = 1 << 0, - /// - Language = 2, + /// + Language = 1 << 1, - /// - Phrases = 4, + /// + Phrases = 1 << 2, - /// - CaseSensitivity = 8, + /// + CaseSensitivity = 1 << 3, - /// - CharacterSets = 16, + /// + CharacterSets = 1 << 4, - /// - Calculations = 32, + /// + Calculations = 1 << 5, - /// - MinLength = 64, + /// + MinLength = 1 << 6, - /// - MaxLength = 128, + /// + MaxLength = 1 << 7, - /// - Instructions = 256 - } + /// + Instructions = 1 << 8, } diff --git a/CaptchaSharp/Enums/CharacterSet.cs b/CaptchaSharp/Enums/CharacterSet.cs index d3c2b66..0739bec 100644 --- a/CaptchaSharp/Enums/CharacterSet.cs +++ b/CaptchaSharp/Enums/CharacterSet.cs @@ -1,21 +1,20 @@ -namespace CaptchaSharp.Enums +namespace CaptchaSharp.Enums; + +/// +public enum CharacterSet { /// - public enum CharacterSet - { - /// - NotSpecified = 0, + NotSpecified = 0, - /// - OnlyNumbers = 1, + /// + OnlyNumbers = 1, - /// - OnlyLetters = 2, + /// + OnlyLetters = 2, - /// - OnlyNumbersOrOnlyLetters = 3, + /// + OnlyNumbersOrOnlyLetters = 3, - /// - BothNumbersAndLetters = 4 - } + /// + BothNumbersAndLetters = 4 } diff --git a/CaptchaSharp/Exceptions/BadAuthenticationException.cs b/CaptchaSharp/Exceptions/BadAuthenticationException.cs index 2f6320d..c438baa 100644 --- a/CaptchaSharp/Exceptions/BadAuthenticationException.cs +++ b/CaptchaSharp/Exceptions/BadAuthenticationException.cs @@ -1,25 +1,24 @@ using System; -namespace CaptchaSharp.Exceptions +namespace CaptchaSharp.Exceptions; + +/// An exception that is thrown when the credentials are invalid. +public class BadAuthenticationException : Exception { - /// An exception that is thrown when the credentials are invalid. - public class BadAuthenticationException : Exception + /// + public BadAuthenticationException() { - /// - public BadAuthenticationException() - { - } + } - /// - public BadAuthenticationException(string message) - : base(message) - { - } + /// + public BadAuthenticationException(string message) + : base(message) + { + } - /// - public BadAuthenticationException(string message, Exception inner) - : base(message, inner) - { - } + /// + public BadAuthenticationException(string message, Exception inner) + : base(message, inner) + { } } diff --git a/CaptchaSharp/Exceptions/TaskCreationException.cs b/CaptchaSharp/Exceptions/TaskCreationException.cs index 8d58c12..62db8d5 100644 --- a/CaptchaSharp/Exceptions/TaskCreationException.cs +++ b/CaptchaSharp/Exceptions/TaskCreationException.cs @@ -1,25 +1,24 @@ using System; -namespace CaptchaSharp.Exceptions +namespace CaptchaSharp.Exceptions; + +/// An exception that is thrown when a captcha task could not be created on the remote server. +public class TaskCreationException : Exception { - /// An exception that is thrown when a captcha task could not be created on the remote server. - public class TaskCreationException : Exception + /// + public TaskCreationException() { - /// - public TaskCreationException() - { - } + } - /// - public TaskCreationException(string message) - : base(message) - { - } + /// + public TaskCreationException(string message) + : base(message) + { + } - /// - public TaskCreationException(string message, Exception inner) - : base(message, inner) - { - } + /// + public TaskCreationException(string message, Exception inner) + : base(message, inner) + { } } diff --git a/CaptchaSharp/Exceptions/TaskReportException.cs b/CaptchaSharp/Exceptions/TaskReportException.cs index 43757de..7b284d9 100644 --- a/CaptchaSharp/Exceptions/TaskReportException.cs +++ b/CaptchaSharp/Exceptions/TaskReportException.cs @@ -1,27 +1,24 @@ using System; -using System.Collections.Generic; -using System.Text; -namespace CaptchaSharp.Exceptions +namespace CaptchaSharp.Exceptions; + +/// An exception that is thrown when a captcha failed to be reported as wrong. +public class TaskReportException : Exception { - /// An exception that is thrown when a captcha failed to be reported as wrong. - public class TaskReportException : Exception + /// + public TaskReportException() { - /// - public TaskReportException() - { - } + } - /// - public TaskReportException(string message) - : base(message) - { - } + /// + public TaskReportException(string message) + : base(message) + { + } - /// - public TaskReportException(string message, Exception inner) - : base(message, inner) - { - } + /// + public TaskReportException(string message, Exception inner) + : base(message, inner) + { } } diff --git a/CaptchaSharp/Exceptions/TaskSolutionException.cs b/CaptchaSharp/Exceptions/TaskSolutionException.cs index 25303a2..aba01aa 100644 --- a/CaptchaSharp/Exceptions/TaskSolutionException.cs +++ b/CaptchaSharp/Exceptions/TaskSolutionException.cs @@ -1,25 +1,24 @@ using System; -namespace CaptchaSharp.Exceptions +namespace CaptchaSharp.Exceptions; + +/// An exception that is thrown when a captcha could not be solved. +public class TaskSolutionException : Exception { - /// An exception that is thrown when a captcha could not be solved. - public class TaskSolutionException : Exception + /// + public TaskSolutionException() { - /// - public TaskSolutionException() - { - } + } - /// - public TaskSolutionException(string message) - : base(message) - { - } + /// + public TaskSolutionException(string message) + : base(message) + { + } - /// - public TaskSolutionException(string message, Exception inner) - : base(message, inner) - { - } + /// + public TaskSolutionException(string message, Exception inner) + : base(message, inner) + { } } diff --git a/CaptchaSharp/Extensions.cs b/CaptchaSharp/Extensions.cs deleted file mode 100644 index 9bc8d7f..0000000 --- a/CaptchaSharp/Extensions.cs +++ /dev/null @@ -1,188 +0,0 @@ -using CaptchaSharp.Enums; -using CaptchaSharp.Models; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace CaptchaSharp -{ - /// Extensions for an . - public static class HttpClientExtensions - { - /* - * GET METHODS - */ - - /// Automatically builds a GET query string from a - /// and appends it to the provided URL. - public async static Task GetAsync - (this HttpClient httpClient, string url, StringPairCollection pairs, - CancellationToken cancellationToken = default) - { - return await httpClient.GetAsync($"{url}?{pairs.ToHttpQueryString()}", cancellationToken); - } - - /// Automatically builds a GET query string from a - /// and appends it to the provided URL. - /// The content converted to a string. - public async static Task GetStringAsync - (this HttpClient httpClient, string url, StringPairCollection pairs, - CancellationToken cancellationToken = default) - { - var response = await httpClient.GetAsync(url, pairs, cancellationToken); - return await response.Content.ReadAsStringAsync().ConfigureAwait(false); - } - - /* - * POST METHODS - */ - - /// Automatically builds a POST query string from a - /// using encoding and the provided Content-Type. - public static async Task PostAsync - (this HttpClient httpClient, string url, StringPairCollection pairs, - CancellationToken cancellationToken = default, string mediaType = "application/x-www-form-urlencoded") - { - return await httpClient.PostAsync(url, - new StringContent(pairs.ToHttpQueryString(), Encoding.UTF8, mediaType), - cancellationToken).ConfigureAwait(false); - } - - /// Automatically builds a POST query string from a - /// using encoding and the provided Content-Type. - /// The content converted to a . - public static async Task PostToStringAsync - (this HttpClient httpClient, string url, StringPairCollection pairs, - CancellationToken cancellationToken = default, string mediaType = "application/x-www-form-urlencoded") - { - var response = await httpClient.PostAsync(url, pairs, cancellationToken, mediaType); - return await response.Content.ReadAsStringAsync().ConfigureAwait(false); - } - - /// Sends a POST request with the desired and reads the - /// response as a . - /// The content converted to a . - public static async Task PostMultipartToStringAsync - (this HttpClient httpClient, string url, MultipartFormDataContent content, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false); - return await response.Content.ReadAsStringAsync().ConfigureAwait(false); - } - - /// Automatically builds a POST json string from a given object using encoding - /// and application/json Content-Type. - /// The content converted to a . - public static async Task PostJsonToStringAsync - (this HttpClient httpClient, string url, T content, CancellationToken cancellationToken = default, bool camelizeKeys = true) - { - string json; - - if (camelizeKeys) - json = content.SerializeCamelCase(); - else - json = JsonConvert.SerializeObject(content); - - var response = await httpClient.PostAsync(url, new StringContent(json, Encoding.UTF8, "application/json"), cancellationToken); - return await response.Content.ReadAsStringAsync().ConfigureAwait(false); - } - } - - /// Extensions for a . - public static class StringExtensions - { - /// Deserializes a json string to a given type. - public static T Deserialize(this string json) - { - return JsonConvert.DeserializeObject(json); - } - - /// Serializes an object to a json string and converts the property names - /// to a camelCase based convention. - public static string SerializeCamelCase(this T obj) - { - var settings = new JsonSerializerSettings(); - settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - return JsonConvert.SerializeObject(obj, settings); - } - - /// Serializes an object to a json string and converts the property names - /// to a lowercase based convention. - public static string SerializeLowerCase(this T obj) - { - var settings = new JsonSerializerSettings(); - settings.ContractResolver = new LowercasePropertyNamesContractResolver(); - return JsonConvert.SerializeObject(obj, settings); - } - } - - /// Extensions for a . - public static class CaptchaLanguageExtensions - { - private static readonly Dictionary dict = new Dictionary - { - { CaptchaLanguage.NotSpecified, "en" }, - { CaptchaLanguage.English, "en" }, - { CaptchaLanguage.Russian, "ru" }, - { CaptchaLanguage.Spanish, "es" }, - { CaptchaLanguage.Portuguese, "pt" }, - { CaptchaLanguage.Ukrainian, "uk" }, - { CaptchaLanguage.Vietnamese, "vi" }, - { CaptchaLanguage.French, "fr" }, - { CaptchaLanguage.Indonesian, "id" }, - { CaptchaLanguage.Arab, "ar" }, - { CaptchaLanguage.Japanese, "ja" }, - { CaptchaLanguage.Turkish, "tr" }, - { CaptchaLanguage.German, "de" }, - { CaptchaLanguage.Chinese, "zh" }, - { CaptchaLanguage.Philippine, "fil" }, - { CaptchaLanguage.Polish, "pl" }, - { CaptchaLanguage.Thai, "th" }, - { CaptchaLanguage.Italian, "it" }, - { CaptchaLanguage.Dutch, "nl" }, - { CaptchaLanguage.Slovak, "sk" }, - { CaptchaLanguage.Bulgarian, "bg" }, - { CaptchaLanguage.Romanian, "ro" }, - { CaptchaLanguage.Hungarian, "hu" }, - { CaptchaLanguage.Korean, "ko" }, - { CaptchaLanguage.Czech, "cs" }, - { CaptchaLanguage.Azerbaijani, "az" }, - { CaptchaLanguage.Persian, "fa" }, - { CaptchaLanguage.Bengali, "bn" }, - { CaptchaLanguage.Greek, "el" }, - { CaptchaLanguage.Lithuanian, "lt" }, - { CaptchaLanguage.Latvian, "lv" }, - { CaptchaLanguage.Swedish, "sv" }, - { CaptchaLanguage.Serbian, "sr" }, - { CaptchaLanguage.Croatian, "hr" }, - { CaptchaLanguage.Hebrew, "he" }, - { CaptchaLanguage.Hindi, "hi" }, - { CaptchaLanguage.Norwegian, "nb" }, - { CaptchaLanguage.Slovenian, "sl" }, - { CaptchaLanguage.Danish, "da" }, - { CaptchaLanguage.Uzbek, "uz" }, - { CaptchaLanguage.Finnish, "fi" }, - { CaptchaLanguage.Catalan, "ca" }, - { CaptchaLanguage.Georgian, "ka" }, - { CaptchaLanguage.Malay, "ms" }, - { CaptchaLanguage.Telugu, "te" }, - { CaptchaLanguage.Estonian, "et" }, - { CaptchaLanguage.Malayalam, "ml" }, - { CaptchaLanguage.Belorussian, "be" }, - { CaptchaLanguage.Kazakh, "kk" }, - { CaptchaLanguage.Marathi, "mr" }, - { CaptchaLanguage.Nepali, "ne" }, - { CaptchaLanguage.Burmese, "my" }, - { CaptchaLanguage.Bosnian, "bs" }, - { CaptchaLanguage.Armenian, "hy" }, - { CaptchaLanguage.Macedonian, "mk" }, - { CaptchaLanguage.Punjabi, "pa" } - }; - - /// Converts a to an ISO-639-1 country code. - public static string ToISO6391Code(this CaptchaLanguage language) => dict[language]; - } -} diff --git a/CaptchaSharp/Extensions/CaptchaLanguageExtensions.cs b/CaptchaSharp/Extensions/CaptchaLanguageExtensions.cs new file mode 100644 index 0000000..9e06642 --- /dev/null +++ b/CaptchaSharp/Extensions/CaptchaLanguageExtensions.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using CaptchaSharp.Enums; + +namespace CaptchaSharp.Extensions; + +/// Extensions for a . +public static class CaptchaLanguageExtensions +{ + private static readonly Dictionary _dict = new() + { + [ CaptchaLanguage.NotSpecified ] = "en", + [ CaptchaLanguage.English ] = "en", + [ CaptchaLanguage.Russian ] = "ru", + [ CaptchaLanguage.Spanish ] = "es", + [ CaptchaLanguage.Portuguese ] = "pt", + [ CaptchaLanguage.Ukrainian ] = "uk", + [ CaptchaLanguage.Vietnamese ] = "vi", + [ CaptchaLanguage.French ] = "fr", + [ CaptchaLanguage.Indonesian ] = "id", + [ CaptchaLanguage.Arab ] = "ar", + [ CaptchaLanguage.Japanese ] = "ja", + [ CaptchaLanguage.Turkish ] = "tr", + [ CaptchaLanguage.German ] = "de", + [ CaptchaLanguage.Chinese ] = "zh", + [ CaptchaLanguage.Philippine ] = "fil", + [ CaptchaLanguage.Polish ] = "pl", + [ CaptchaLanguage.Thai ] = "th", + [ CaptchaLanguage.Italian ] = "it", + [ CaptchaLanguage.Dutch ] = "nl", + [ CaptchaLanguage.Slovak ] = "sk", + [ CaptchaLanguage.Bulgarian ] = "bg", + [ CaptchaLanguage.Romanian ] = "ro", + [ CaptchaLanguage.Hungarian ] = "hu", + [ CaptchaLanguage.Korean ] = "ko", + [ CaptchaLanguage.Czech ] = "cs", + [ CaptchaLanguage.Azerbaijani ] = "az", + [ CaptchaLanguage.Persian ] = "fa", + [ CaptchaLanguage.Bengali ] = "bn", + [ CaptchaLanguage.Greek ] = "el", + [ CaptchaLanguage.Lithuanian ] = "lt", + [ CaptchaLanguage.Latvian ] = "lv", + [ CaptchaLanguage.Swedish ] = "sv", + [ CaptchaLanguage.Serbian ] = "sr", + [ CaptchaLanguage.Croatian ] = "hr", + [ CaptchaLanguage.Hebrew ] = "he", + [ CaptchaLanguage.Hindi ] = "hi", + [ CaptchaLanguage.Norwegian ] = "nb", + [ CaptchaLanguage.Slovenian ] = "sl", + [ CaptchaLanguage.Danish ] = "da", + [ CaptchaLanguage.Uzbek ] = "uz", + [ CaptchaLanguage.Finnish ] = "fi", + [ CaptchaLanguage.Catalan ] = "ca", + [ CaptchaLanguage.Georgian ] = "ka", + [ CaptchaLanguage.Malay ] = "ms", + [ CaptchaLanguage.Telugu ] = "te", + [ CaptchaLanguage.Estonian ] = "et", + [ CaptchaLanguage.Malayalam ] = "ml", + [ CaptchaLanguage.Belorussian ] = "be", + [ CaptchaLanguage.Kazakh ] = "kk", + [ CaptchaLanguage.Marathi ] = "mr", + [ CaptchaLanguage.Nepali ] = "ne", + [ CaptchaLanguage.Burmese ] = "my", + [ CaptchaLanguage.Bosnian ] = "bs", + [ CaptchaLanguage.Armenian ] = "hy", + [ CaptchaLanguage.Macedonian ] = "mk", + [ CaptchaLanguage.Punjabi ] = "pa" + }; + + /// Converts a to an ISO-639-1 country code. + public static string ToIso6391Code(this CaptchaLanguage language) => _dict[language]; +} diff --git a/CaptchaSharp/Extensions/HttpClientExtensions.cs b/CaptchaSharp/Extensions/HttpClientExtensions.cs new file mode 100644 index 0000000..68f4a22 --- /dev/null +++ b/CaptchaSharp/Extensions/HttpClientExtensions.cs @@ -0,0 +1,93 @@ +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Models; +using Newtonsoft.Json; + +namespace CaptchaSharp.Extensions; + +/// Extensions for an . +public static class HttpClientExtensions +{ + /* + * GET METHODS + */ + + /// Automatically builds a GET query string from a + /// and appends it to the provided URL. + public static async Task GetAsync( + this HttpClient httpClient, string url, StringPairCollection pairs, + CancellationToken cancellationToken = default) + { + return await httpClient.GetAsync($"{url}?{pairs.ToHttpQueryString()}", cancellationToken); + } + + /// Automatically builds a GET query string from a + /// and appends it to the provided URL. + /// The content converted to a string. + public static async Task GetStringAsync( + this HttpClient httpClient, string url, StringPairCollection pairs, + CancellationToken cancellationToken = default) + { + var response = await httpClient.GetAsync(url, pairs, cancellationToken); + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } + + /* + * POST METHODS + */ + + /// Automatically builds a POST query string from a + /// using encoding and the provided Content-Type. + public static async Task PostAsync( + this HttpClient httpClient, string url, StringPairCollection pairs, + string mediaType = "application/x-www-form-urlencoded", + CancellationToken cancellationToken = default) + { + return await httpClient.PostAsync(url, + new StringContent(pairs.ToHttpQueryString(), Encoding.UTF8, mediaType), + cancellationToken).ConfigureAwait(false); + } + + /// Automatically builds a POST query string from a + /// using encoding and the provided Content-Type. + /// The content converted to a . + public static async Task PostToStringAsync( + this HttpClient httpClient, string url, StringPairCollection pairs, + string mediaType = "application/x-www-form-urlencoded", + CancellationToken cancellationToken = default) + { + var response = await httpClient.PostAsync(url, pairs, mediaType, cancellationToken); + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } + + /// Sends a POST request with the desired and reads the + /// response as a . + /// The content converted to a . + public static async Task PostMultipartToStringAsync( + this HttpClient httpClient, string url, MultipartFormDataContent content, + CancellationToken cancellationToken = default) + { + var response = await httpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false); + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } + + /// Automatically builds a POST json string from a given object using encoding + /// and application/json Content-Type. + /// The content converted to a . + public static async Task PostJsonToStringAsync( + this HttpClient httpClient, string url, T content, bool camelizeKeys = true, + CancellationToken cancellationToken = default) where T : class + { + var json = camelizeKeys + ? content.SerializeCamelCase() + : JsonConvert.SerializeObject(content); + + var response = await httpClient.PostAsync(url, + new StringContent(json, Encoding.UTF8, "application/json"), + cancellationToken); + + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } +} diff --git a/CaptchaSharp/Extensions/StringExtensions.cs b/CaptchaSharp/Extensions/StringExtensions.cs new file mode 100644 index 0000000..cd28693 --- /dev/null +++ b/CaptchaSharp/Extensions/StringExtensions.cs @@ -0,0 +1,38 @@ +using CaptchaSharp.Helpers; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace CaptchaSharp.Extensions; + +/// Extensions for a . +public static class StringExtensions +{ + /// Deserializes a json string to a given type. + public static T Deserialize(this string json) where T : notnull + { + return JsonConvert.DeserializeObject(json) + ?? throw new JsonSerializationException("Failed to deserialize json string."); + } + + /// Serializes an object to a json string and converts the property names + /// to a camelCase based convention. + public static string SerializeCamelCase(this T obj) + { + var settings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + return JsonConvert.SerializeObject(obj, settings); + } + + /// Serializes an object to a json string and converts the property names + /// to a lowercase based convention. + public static string SerializeLowerCase(this T obj) + { + var settings = new JsonSerializerSettings + { + ContractResolver = new LowercasePropertyNamesContractResolver() + }; + return JsonConvert.SerializeObject(obj, settings); + } +} diff --git a/CaptchaSharp/Helpers/LowercasePropertyNamesContractResolver.cs b/CaptchaSharp/Helpers/LowercasePropertyNamesContractResolver.cs new file mode 100644 index 0000000..3c1d4fe --- /dev/null +++ b/CaptchaSharp/Helpers/LowercasePropertyNamesContractResolver.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json.Serialization; + +namespace CaptchaSharp.Helpers; + +internal class LowercasePropertyNamesContractResolver : DefaultContractResolver +{ + protected override string ResolvePropertyName(string propertyName) + { + return propertyName.ToLower(); + } +} diff --git a/CaptchaSharp/LowercasePropertyNamesContractResolver.cs b/CaptchaSharp/LowercasePropertyNamesContractResolver.cs deleted file mode 100644 index 5b848ef..0000000 --- a/CaptchaSharp/LowercasePropertyNamesContractResolver.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Newtonsoft.Json.Serialization -{ - internal class LowercasePropertyNamesContractResolver : DefaultContractResolver - { - protected override string ResolvePropertyName(string propertyName) - { - return propertyName.ToLower(); - } - } -} diff --git a/CaptchaSharp/Models/CaptchaResponse.cs b/CaptchaSharp/Models/CaptchaResponse.cs index a6e1d59..d08d65d 100644 --- a/CaptchaSharp/Models/CaptchaResponse.cs +++ b/CaptchaSharp/Models/CaptchaResponse.cs @@ -9,7 +9,7 @@ public class CaptchaResponse public long Id { get => long.Parse(IdString); - set => IdString = value.ToString(); + init => IdString = value.ToString(); } /// diff --git a/CaptchaSharp/Models/CaptchaTask.cs b/CaptchaSharp/Models/CaptchaTask.cs index a18c797..5806904 100644 --- a/CaptchaSharp/Models/CaptchaTask.cs +++ b/CaptchaSharp/Models/CaptchaTask.cs @@ -1,47 +1,46 @@ using CaptchaSharp.Enums; using System; -namespace CaptchaSharp.Models -{ - /// Information about a captcha task - public class CaptchaTask - { - /// When the task was created - public DateTime CreationDate { get; } - - /// The type of captcha that is being solved - public CaptchaType Type { get; set; } +namespace CaptchaSharp.Models; - /// The id of the task - public long Id { get; } +/// Information about a captcha task +public class CaptchaTask +{ + /// When the task was created + public DateTime CreationDate { get; } - /// The id of the task as a string - public string IdString { get; } + /// The type of captcha that is being solved + public CaptchaType Type { get; set; } - /// Whether the task is completed - public bool Completed { get; set; } = false; + /// The id of the task + public long Id { get; } - /// Creates a from a string id - public CaptchaTask(string id, CaptchaType type) - { - IdString = id; + /// The id of the task as a string + public string IdString { get; } - if (long.TryParse(id, out long parsed)) - { - Id = parsed; - } + /// Whether the task is completed + public bool Completed { get; set; } = false; - Type = type; - CreationDate = DateTime.Now; - } + /// Creates a from a string id + public CaptchaTask(string id, CaptchaType type) + { + IdString = id; - /// Creates a from a long id - public CaptchaTask(long id, CaptchaType type) + if (long.TryParse(id, out long parsed)) { - Id = id; - IdString = id.ToString(); - Type = type; - CreationDate = DateTime.Now; + Id = parsed; } + + Type = type; + CreationDate = DateTime.Now; + } + + /// Creates a from a long id + public CaptchaTask(long id, CaptchaType type) + { + Id = id; + IdString = id.ToString(); + Type = type; + CreationDate = DateTime.Now; } } diff --git a/CaptchaSharp/Models/CapyResponse.cs b/CaptchaSharp/Models/CapyResponse.cs index 9891beb..d11d86e 100644 --- a/CaptchaSharp/Models/CapyResponse.cs +++ b/CaptchaSharp/Models/CapyResponse.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Models +namespace CaptchaSharp.Models; + +/// The solution of a Capy captcha. +public class CapyResponse : CaptchaResponse { - /// The solution of a Capy captcha. - public class CapyResponse : CaptchaResponse - { - /// - public string CaptchaKey { get; set; } + /// + public required string CaptchaKey { get; init; } - /// - public string ChallengeKey { get; set; } + /// + public required string ChallengeKey { get; init; } - /// - public string Answer { get; set; } - } + /// + public required string Answer { get; init; } } diff --git a/CaptchaSharp/Models/GeeTestResponse.cs b/CaptchaSharp/Models/GeeTestResponse.cs index 070a2ce..244cb7a 100644 --- a/CaptchaSharp/Models/GeeTestResponse.cs +++ b/CaptchaSharp/Models/GeeTestResponse.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Models +namespace CaptchaSharp.Models; + +/// The solution of a GeeTest captcha. +public class GeeTestResponse : CaptchaResponse { - /// The solution of a GeeTest captcha. - public class GeeTestResponse : CaptchaResponse - { - /// - public string Challenge { get; set; } + /// + public required string Challenge { get; init; } - /// - public string Validate { get; set; } + /// + public required string Validate { get; init; } - /// - public string SecCode { get; set; } - } + /// + public required string SecCode { get; init; } } diff --git a/CaptchaSharp/Models/ImageCaptchaOptions.cs b/CaptchaSharp/Models/ImageCaptchaOptions.cs index 1c8354d..a826a17 100644 --- a/CaptchaSharp/Models/ImageCaptchaOptions.cs +++ b/CaptchaSharp/Models/ImageCaptchaOptions.cs @@ -1,35 +1,34 @@ using CaptchaSharp.Enums; -namespace CaptchaSharp.Models +namespace CaptchaSharp.Models; + +/// Provides additional options for an image based captcha task. +public class ImageCaptchaOptions { - /// Provides additional options for an image based captcha task. - public class ImageCaptchaOptions - { - /// Whether the captcha is made of multiple words. - public bool IsPhrase { get; set; } = false; + /// Whether the captcha is made of multiple words. + public bool IsPhrase { get; init; } - /// Whether the captcha should be solved as case sensitive. - public bool CaseSensitive { get; set; } = true; + /// Whether the captcha should be solved as case-sensitive. + public bool CaseSensitive { get; init; } = true; - /// The set of allowed characters. - public CharacterSet CharacterSet { get; set; } = CharacterSet.NotSpecified; + /// The set of allowed characters. + public CharacterSet CharacterSet { get; init; } = CharacterSet.NotSpecified; - /// Whether the captcha includes mathematical calculations. - public bool RequiresCalculation { get; set; } = false; + /// Whether the captcha includes mathematical calculations. + public bool RequiresCalculation { get; init; } - /// The minimum possible length of the text. - public int MinLength { get; set; } = 0; + /// The minimum possible length of the text. + public int MinLength { get; init; } - /// The maximum possible length of the text. - public int MaxLength { get; set; } = 0; + /// The maximum possible length of the text. + public int MaxLength { get; init; } - /// The language group of the text. - public CaptchaLanguageGroup CaptchaLanguageGroup { get; set; } = CaptchaLanguageGroup.NotSpecified; + /// The language group of the text. + public CaptchaLanguageGroup CaptchaLanguageGroup { get; init; } = CaptchaLanguageGroup.NotSpecified; - /// The language of the text. - public CaptchaLanguage CaptchaLanguage { get; set; } = CaptchaLanguage.NotSpecified; + /// The language of the text. + public CaptchaLanguage CaptchaLanguage { get; init; } = CaptchaLanguage.NotSpecified; - /// Any additional text instruction (e.g. type the characters in red). - public string TextInstructions { get; set; } = ""; - } + /// Any additional text instruction (e.g. type the characters in red). + public string TextInstructions { get; init; } = string.Empty; } diff --git a/CaptchaSharp/Models/Proxy.cs b/CaptchaSharp/Models/Proxy.cs index 7efadaf..22af499 100644 --- a/CaptchaSharp/Models/Proxy.cs +++ b/CaptchaSharp/Models/Proxy.cs @@ -7,8 +7,10 @@ namespace CaptchaSharp.Models; /// A generic proxy class. public class Proxy { + // TODO: Remove UserAgent and Cookies properties from this class + /// - public string Host { get; set; } + public string? Host { get; set; } /// public int Port { get; set; } @@ -17,16 +19,16 @@ public class Proxy public ProxyType Type { get; set; } /// - public string Username { get; set; } + public string? Username { get; set; } /// - public string Password { get; set; } + public string? Password { get; set; } /// The User-Agent header to be used in requests. - public string UserAgent { get; set; } + public string? UserAgent { get; set; } /// The cookies needed to get to the page where the captcha is shown. - public (string, string)[] Cookies { get; set; } + public (string, string)[]? Cookies { get; set; } /// Whether the proxy requires authentication. [JsonIgnore] diff --git a/CaptchaSharp/Models/StringPairCollection.cs b/CaptchaSharp/Models/StringPairCollection.cs index ea35274..dfe20ed 100644 --- a/CaptchaSharp/Models/StringPairCollection.cs +++ b/CaptchaSharp/Models/StringPairCollection.cs @@ -3,51 +3,54 @@ using System.Net.Http; using System.Web; -namespace CaptchaSharp.Models +namespace CaptchaSharp.Models; + +/// A collection of string pairs. +public class StringPairCollection { - /// A collection of string pairs. - public class StringPairCollection - { - private List<(string, string)> pairs = new List<(string, string)>(); + private List<(string, string)> _pairs = []; - /// Adds a new pair to the collection if is true. - public StringPairCollection Add(string first, string second, bool addCondition = true) + /// Adds a new pair to the collection if is true. + public StringPairCollection Add(string first, string second, bool addCondition = true) + { + if (addCondition) { - if (addCondition) - pairs.Add((first, second)); - - return this; + _pairs.Add((first, second)); } - /// Adds a new pair to the collection if is true by - /// calling the ToString() method on and . - public StringPairCollection Add(A first, B second, bool addCondition = true) - { - return Add(first.ToString(), second.ToString(), addCondition); - } + return this; + } - /// Adds multiple new pairs to the collection. - public StringPairCollection Add(IEnumerable<(string, string)> pairsToAdd) - { - pairs = pairs.Concat(pairsToAdd).ToList(); - return this; - } + /// Adds a new pair to the collection if is true by + /// calling the ToString() method on and . + public StringPairCollection Add(TKey first, TValue second, bool addCondition = true) + where TKey : notnull + where TValue : notnull + { + return Add(first.ToString()!, second.ToString()!, addCondition); + } - /// Outputs a string like name1=value1&name2=value2 - public string ToHttpQueryString() - { - var query = HttpUtility.ParseQueryString(string.Empty); - pairs.ForEach(p => query.Add(p.Item1, p.Item2)); - return query.ToString(); - } + /// Adds multiple new pairs to the collection. + public StringPairCollection Add(IEnumerable<(string, string)> pairsToAdd) + { + _pairs = _pairs.Concat(pairsToAdd).ToList(); + return this; + } - /// Outputs a new where each pair - /// of the collection becomes a . - public MultipartFormDataContent ToMultipartFormDataContent() - { - var content = new MultipartFormDataContent(); - pairs.ForEach(p => content.Add(new StringContent(p.Item2), p.Item1)); - return content; - } + /// Outputs a string like name1=value1&name2=value2 + public string ToHttpQueryString() + { + var query = HttpUtility.ParseQueryString(string.Empty); + _pairs.ForEach(p => query.Add(p.Item1, p.Item2)); + return query.ToString()!; + } + + /// Outputs a new where each pair + /// of the collection becomes a . + public MultipartFormDataContent ToMultipartFormDataContent() + { + var content = new MultipartFormDataContent(); + _pairs.ForEach(p => content.Add(new StringContent(p.Item2), p.Item1)); + return content; } } diff --git a/CaptchaSharp/Models/StringResponse.cs b/CaptchaSharp/Models/StringResponse.cs index 1fb8e33..a3b0b23 100644 --- a/CaptchaSharp/Models/StringResponse.cs +++ b/CaptchaSharp/Models/StringResponse.cs @@ -1,9 +1,8 @@ -namespace CaptchaSharp.Models +namespace CaptchaSharp.Models; + +/// A captcha response with a string solution. +public class StringResponse : CaptchaResponse { - /// A captcha response with a string solution. - public class StringResponse : CaptchaResponse - { - /// The plaintext response string. - public string Response { get; set; } - } + /// The plaintext response string. + public required string Response { get; init; } } diff --git a/CaptchaSharp/Models/TextCaptchaOptions.cs b/CaptchaSharp/Models/TextCaptchaOptions.cs index d2d10fc..53ce001 100644 --- a/CaptchaSharp/Models/TextCaptchaOptions.cs +++ b/CaptchaSharp/Models/TextCaptchaOptions.cs @@ -1,14 +1,13 @@ using CaptchaSharp.Enums; -namespace CaptchaSharp.Models +namespace CaptchaSharp.Models; + +/// Provides additional options for a text based captcha task. +public class TextCaptchaOptions { - /// Provides additional options for a text based captcha task. - public class TextCaptchaOptions - { - /// The language group of the text. - public CaptchaLanguageGroup CaptchaLanguageGroup { get; set; } = CaptchaLanguageGroup.NotSpecified; + /// The language group of the text. + public CaptchaLanguageGroup CaptchaLanguageGroup { get; init; } = CaptchaLanguageGroup.NotSpecified; - /// The language of the text. - public CaptchaLanguage CaptchaLanguage { get; set; } = CaptchaLanguage.NotSpecified; - } + /// The language of the text. + public CaptchaLanguage CaptchaLanguage { get; init; } = CaptchaLanguage.NotSpecified; } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index d2de373..a626b10 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -12,6 +12,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; namespace CaptchaSharp.Services { @@ -29,7 +30,7 @@ public class AntiCaptchaService : CaptchaService /// Initializes a using the given and /// . If is null, a default one will be created. - public AntiCaptchaService(string apiKey, HttpClient httpClient = null) + public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) { ApiKey = apiKey; this.httpClient = httpClient ?? new HttpClient(); @@ -43,7 +44,7 @@ public async override Task GetBalanceAsync(CancellationToken cancellati var response = await httpClient.PostJsonToStringAsync ("getBalance", new Request() { ClientKey = ApiKey }, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); var balanceResponse = response.Deserialize(); @@ -72,7 +73,7 @@ public async override Task SolveImageCaptchaAsync Body = base64 } }, options), - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.ImageCaptcha, cancellationToken) @@ -138,7 +139,7 @@ public async override Task SolveRecaptchaV2Async var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV2, cancellationToken) @@ -170,7 +171,7 @@ public async override Task SolveRecaptchaV3Async var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV3, cancellationToken) @@ -209,7 +210,7 @@ public async override Task SolveFuncaptchaAsync var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.FunCaptcha, cancellationToken) @@ -242,7 +243,7 @@ public async override Task SolveHCaptchaAsync var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.HCaptcha, cancellationToken) @@ -280,7 +281,7 @@ public async override Task SolveGeeTestAsync var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.GeeTest, cancellationToken) @@ -306,8 +307,8 @@ protected async override Task CheckResult { var response = await httpClient.PostJsonToStringAsync ("getTaskResult", - new GetTaskResultRequest() { ClientKey = ApiKey, TaskId = (int)task.Id }, - cancellationToken).ConfigureAwait(false); + new GetTaskResultRequest { ClientKey = ApiKey, TaskId = (int)task.Id }, + cancellationToken: cancellationToken).ConfigureAwait(false); var result = response.Deserialize(); @@ -367,7 +368,7 @@ public async override Task ReportSolution response = await httpClient.PostJsonToStringAsync ("reportIncorrectImageCaptcha", new ReportIncorrectCaptchaRequest() { ClientKey = ApiKey, TaskId = taskId }, - cancellationToken).ConfigureAwait(false); + cancellationToken: cancellationToken).ConfigureAwait(false); incResponse = response.Deserialize(); break; @@ -377,7 +378,7 @@ public async override Task ReportSolution response = await httpClient.PostJsonToStringAsync ("reportIncorrectRecaptcha", new ReportIncorrectCaptchaRequest() { ClientKey = ApiKey, TaskId = taskId }, - cancellationToken).ConfigureAwait(false); + cancellationToken: cancellationToken).ConfigureAwait(false); incResponse = response.Deserialize(); break; diff --git a/CaptchaSharp/Services/CapMonsterService.cs b/CaptchaSharp/Services/CapMonsterService.cs index ef0509e..e4d02bc 100644 --- a/CaptchaSharp/Services/CapMonsterService.cs +++ b/CaptchaSharp/Services/CapMonsterService.cs @@ -4,41 +4,46 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; namespace CaptchaSharp.Services { - /// The service provided by the CapMonster OCR application by ZennoLab. + /// + /// The service provided by the CapMonster OCR application by ZennoLab. + /// public class CapMonsterService : CustomTwoCaptchaService { - /// Initializes a using the given , - /// and . - /// If is null, a default one will be created. - public CapMonsterService(string apiKey, Uri baseUri, HttpClient httpClient = null) + /// Initializes a . + /// The API key to use. + /// The base URI of the service. + /// The to use for requests. If null, a default one will be created. + public CapMonsterService(string apiKey, Uri baseUri, HttpClient? httpClient = null) : base(apiKey, baseUri, httpClient) { SupportedCaptchaTypes = CaptchaType.ImageCaptcha | CaptchaType.ReCaptchaV2 | CaptchaType.ReCaptchaV3; } /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) + public override async Task SolveImageCaptchaAsync + (string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { if (!base64.StartsWith("base64,")) { base64 = "base64," + base64; } - var response = await httpClient.PostToStringAsync + var response = await HttpClient.PostToStringAsync ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "base64") .Add("body", base64) .Add(ConvertCapabilities(options)), - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); - return (await TryGetResult(response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false)) as StringResponse; + return await GetResult( + response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false); } } } diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index 42300ef..941ce0e 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -11,6 +11,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; namespace CaptchaSharp.Services { @@ -42,7 +43,7 @@ public async override Task GetBalanceAsync(CancellationToken cancellati var response = await httpClient.PostJsonToStringAsync ("getBalance", new Request() { ClientKey = ApiKey }, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); var balanceResponse = response.Deserialize(); @@ -70,7 +71,7 @@ public async override Task SolveImageCaptchaAsync Body = base64 } }, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); // The image captcha task immediately returns the solution @@ -143,7 +144,7 @@ public async override Task SolveRecaptchaV2Async var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV2, cancellationToken) @@ -183,7 +184,7 @@ public async override Task SolveRecaptchaV3Async var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV3, cancellationToken) @@ -222,7 +223,7 @@ public async override Task SolveFuncaptchaAsync var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.FunCaptcha, cancellationToken) @@ -255,7 +256,7 @@ public async override Task SolveHCaptchaAsync var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.HCaptcha, cancellationToken) @@ -294,7 +295,7 @@ public async override Task SolveGeeTestAsync var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.GeeTest, cancellationToken) @@ -321,7 +322,7 @@ public async override Task SolveDataDomeAsync var response = await httpClient.PostJsonToStringAsync ("createTask", content, - cancellationToken) + cancellationToken: cancellationToken) .ConfigureAwait(false); return await TryGetResult(response.Deserialize(), CaptchaType.DataDome, cancellationToken) @@ -348,7 +349,7 @@ protected async override Task CheckResult var response = await httpClient.PostJsonToStringAsync ("getTaskResult", new GetTaskResultRequest() { ClientKey = ApiKey, TaskId = task.IdString }, - cancellationToken).ConfigureAwait(false); + cancellationToken: cancellationToken).ConfigureAwait(false); var result = response.Deserialize(); diff --git a/CaptchaSharp/Services/CustomAntiCaptchaService.cs b/CaptchaSharp/Services/CustomAntiCaptchaService.cs index c585087..730de3a 100644 --- a/CaptchaSharp/Services/CustomAntiCaptchaService.cs +++ b/CaptchaSharp/Services/CustomAntiCaptchaService.cs @@ -11,7 +11,7 @@ public class CustomAntiCaptchaService : AntiCaptchaService /// and . /// If is null, a default one will be created. /// - public CustomAntiCaptchaService(string apiKey, Uri baseUri, HttpClient httpClient = null) + public CustomAntiCaptchaService(string apiKey, Uri baseUri, HttpClient? httpClient = null) : base(apiKey, httpClient) { SetupHttpClient(baseUri); diff --git a/CaptchaSharp/Services/CustomTwoCaptchaService.cs b/CaptchaSharp/Services/CustomTwoCaptchaService.cs index 3751683..8a9875c 100644 --- a/CaptchaSharp/Services/CustomTwoCaptchaService.cs +++ b/CaptchaSharp/Services/CustomTwoCaptchaService.cs @@ -2,49 +2,52 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by a service that implements the 2captcha API. +/// +public class CustomTwoCaptchaService : TwoCaptchaService { - /// The service provided by a service that implements the 2captcha API. - public class CustomTwoCaptchaService : TwoCaptchaService + /// + /// Initializes a . + /// + /// The API key to use. + /// The base URI of the service. + /// The to use for requests. If null, a default one will be created. + /// Whether to override the Host header to 2captcha.com. + public CustomTwoCaptchaService( + string apiKey, Uri baseUri, HttpClient? httpClient = null, bool overrideHostHeader = true) + : base(apiKey, httpClient) { - /// Initializes a using the given , - /// and . - /// If is null, a default one will be created. - /// If is true, the Host header will be changed to 2captcha.com - public CustomTwoCaptchaService(string apiKey, Uri baseUri, HttpClient httpClient = null, bool overrideHostHeader = true) - : base(apiKey, httpClient) - { - SetupHttpClient(baseUri, overrideHostHeader); + SetupHttpClient(baseUri, overrideHostHeader); - // Services that implement the 2captcha API don't always support - // JSON responses so we will not set the json=1 flag - UseJsonFlag = false; - } + // Services that implement the 2captcha API don't always support + // JSON responses so we will not set the json=1 flag + UseJsonFlag = false; + } - /// Sets 2captcha.com as host and as - /// for the requests. - protected void SetupHttpClient(Uri baseUri, bool overrideHostHeader = true) + private void SetupHttpClient(Uri baseUri, bool overrideHostHeader = true) + { + if (overrideHostHeader) { - if (overrideHostHeader) - { - // Use 2captcha.com as host header to simulate an entry in the hosts file - httpClient.DefaultRequestHeaders.Host = "2captcha.com"; - } - - httpClient.BaseAddress = baseUri; + // Use 2captcha.com as host header to simulate an entry in the hosts file + HttpClient.DefaultRequestHeaders.Host = "2captcha.com"; } - - #region Supported Types - /// The supported captcha types for this service. - public CaptchaType SupportedCaptchaTypes { get; set; } = - CaptchaType.TextCaptcha | - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3 | - CaptchaType.FunCaptcha | - CaptchaType.HCaptcha | - CaptchaType.KeyCaptcha | - CaptchaType.GeeTest; - #endregion + + HttpClient.BaseAddress = baseUri; } + + #region Supported Types + /// The supported captcha types for this service. + public CaptchaType SupportedCaptchaTypes { get; protected set; } = + CaptchaType.TextCaptcha | + CaptchaType.ImageCaptcha | + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3 | + CaptchaType.FunCaptcha | + CaptchaType.HCaptcha | + CaptchaType.KeyCaptcha | + CaptchaType.GeeTest; + #endregion } diff --git a/CaptchaSharp/Services/DeCaptcherService.cs b/CaptchaSharp/Services/DeCaptcherService.cs index 8945b32..725dfaf 100644 --- a/CaptchaSharp/Services/DeCaptcherService.cs +++ b/CaptchaSharp/Services/DeCaptcherService.cs @@ -7,178 +7,204 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://de-captcher.com/ +/// +public class DeCaptcherService : CaptchaService { - /// The service provided by https://de-captcher.com/ - public class DeCaptcherService : CaptchaService + /// + /// Your DeCaptcher account name. + /// + public string Username { get; set; } + + /// + /// Your DeCaptcher account password. + /// + public string Password { get; set; } + + /// The default used for requests. + private readonly HttpClient _httpClient; + + /// + /// Initializes a . + /// + /// + public DeCaptcherService(string username, string password, HttpClient? httpClient = null) { - /// Your DeCaptcher account name. - public string Username { get; set; } - - /// Your DeCaptcher account password. - public string Password { get; set; } - - /// The default used for requests. - protected HttpClient httpClient; - - /// Initializes a using the given account credentials and - /// . If is null, a default one will be created. - public DeCaptcherService(string username, string password, HttpClient httpClient = null) - { - Username = username; - Password = password; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("http://poster.de-captcher.com/"); + Username = username; + Password = password; + this._httpClient = httpClient ?? new HttpClient(); + + // TODO: Use https instead of http if possible + this._httpClient.BaseAddress = new Uri("http://poster.de-captcher.com/"); - // Since this service replies directly with the solution to the task creation request - // we need to set a high timeout here or it will not finish in time - this.httpClient.Timeout = Timeout; - } + // Since this service replies directly with the solution to the task creation request + // we need to set a high timeout here, or it will not finish in time + this._httpClient.Timeout = Timeout; + } - #region Getting the Balance - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync("", + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostMultipartToStringAsync("", GetAuthPair().Add("function", "balance").ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) - return balance; - - throw new BadAuthenticationException(response); + if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) + { + return balance; } - #endregion - #region Solve Methods - /// - public async override Task SolveTextCaptchaAsync - (string text, TextCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync("", + throw new BadAuthenticationException(response); + } + #endregion + + #region Solve Methods + /// + public override async Task SolveTextCaptchaAsync( + string text, TextCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostMultipartToStringAsync("", GetAuthPair() - .Add("function", "picture2") - .Add("pict", text) - .Add("pict_type", 83) - .Add("lang", options.CaptchaLanguage.ToISO6391Code()) - .ToMultipartFormDataContent(), + .Add("function", "picture2") + .Add("pict", text) + .Add("pict_type", 83) + .Add("lang", options?.CaptchaLanguage.ToIso6391Code() ?? string.Empty, options is not null) + .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - if (DeCaptcherResponse.TryParse(response, out DeCaptcherResponse resp)) - return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; + .ConfigureAwait(false); - throw new TaskSolutionException(response); + if (DeCaptcherResponse.TryParse(response, out var resp)) + { + return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; } - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var content = GetAuthPair() - .Add("function", "picture2") - .Add("pict_type", 0) - .Add("lang", options.CaptchaLanguage.ToISO6391Code()) - .ToMultipartFormDataContent(); + throw new TaskSolutionException(response); + } + + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var content = GetAuthPair() + .Add("function", "picture2") + .Add("pict_type", 0) + .Add("lang", options?.CaptchaLanguage.ToIso6391Code() ?? string.Empty, options is not null) + .ToMultipartFormDataContent(); - var buffer = Convert.FromBase64String(base64); - content.Add(new ByteArrayContent(buffer), "pict"); + var buffer = Convert.FromBase64String(base64); + content.Add(new ByteArrayContent(buffer), "pict"); - var response = await httpClient.PostMultipartToStringAsync("", + var response = await _httpClient.PostMultipartToStringAsync("", content, cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - if (DeCaptcherResponse.TryParse(response, out DeCaptcherResponse resp)) - return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; - - throw new TaskSolutionException(response); + if (DeCaptcherResponse.TryParse(response, out var resp)) + { + return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; } - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) + throw new TaskSolutionException(response); + } + + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + if (proxy is not null) { - if (proxy != null) + if (proxy.RequiresAuthentication) { - if (proxy.RequiresAuthentication) - throw new NotSupportedException("Authenticated proxies are not supported"); + throw new NotSupportedException("Authenticated proxies are not supported"); + } - if (proxy.Type != ProxyType.SOCKS4 && proxy.Type != ProxyType.SOCKS5) - throw new NotSupportedException("Only SOCKS proxies are supported"); + if (proxy.Type != ProxyType.SOCKS4 && proxy.Type != ProxyType.SOCKS5) + { + throw new NotSupportedException("Only SOCKS proxies are supported"); + } - if (siteUrl.StartsWith("https")) - throw new NotSupportedException("Only http sites are supported"); + if (siteUrl.StartsWith("https")) + { + throw new NotSupportedException("Only http sites are supported"); } + } - var pairs = GetAuthPair() - .Add("function", "proxy_url") - .Add("url", siteUrl) - .Add("key", siteKey); + var pairs = GetAuthPair() + .Add("function", "proxy_url") + .Add("url", siteUrl) + .Add("key", siteKey); - if (proxy != null) - pairs.Add("proxy", $"{proxy.Host}:{proxy.Port}"); + if (proxy != null) + { + pairs.Add("proxy", $"{proxy.Host}:{proxy.Port}"); + } - var response = await httpClient.PostMultipartToStringAsync("", + var response = await _httpClient.PostMultipartToStringAsync("", pairs.ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - if (DeCaptcherResponse.TryParse(response, out DeCaptcherResponse resp)) - return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; + .ConfigureAwait(false); - throw new TaskSolutionException(response); + if (DeCaptcherResponse.TryParse(response, out DeCaptcherResponse resp)) + { + return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; } - #endregion - #region Reporting the solution - /// - public async override Task ReportSolution - (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - { - (int major, int minor) = LongToInts(id); + throw new TaskSolutionException(response); + } + #endregion + + #region Reporting the solution + /// + public override async Task ReportSolution( + long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + var (major, minor) = LongToInts(id); - await httpClient.PostMultipartToStringAsync("", + await _httpClient.PostMultipartToStringAsync("", GetAuthPair() - .Add("function", "picture_bad2") - .Add("major_id", major) - .Add("minor_id", minor) - .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); + .Add("function", "picture_bad2") + .Add("major_id", major) + .Add("minor_id", minor) + .ToMultipartFormDataContent(), cancellationToken) + .ConfigureAwait(false); - // TODO: Find a way to check if the api accepted the report or not - } - #endregion + // TODO: Find a way to check if the api accepted the report or not + } + #endregion - #region Private Methods - private StringPairCollection GetAuthPair() - { - return new StringPairCollection() - .Add("username", Username) - .Add("password", Password); - } + #region Private Methods + private StringPairCollection GetAuthPair() + { + return new StringPairCollection() + .Add("username", Username) + .Add("password", Password); + } - private long GetCaptchaId(DeCaptcherResponse response) - { - return IntsToLong(response.MajorID, response.MinorID); - } + private long GetCaptchaId(DeCaptcherResponse response) + { + return IntsToLong(response.MajorID, response.MinorID); + } - // Encodes two 32-bit integers as a 64-bit long - private long IntsToLong(int a, int b) - { - long l = b; - l <<= 32; - l |= (uint)a; - return l; - } + // Encodes two 32-bit integers as a 64-bit long + private static long IntsToLong(int a, int b) + { + long l = b; + l <<= 32; + l |= (uint)a; + return l; + } - // Gets two 32-bit integers by splitting a 64-bit long - private (int, int) LongToInts(long longId) - { - return ((int)(longId & uint.MaxValue), (int)(longId >> 32)); - } - #endregion + // Gets two 32-bit integers by splitting a 64-bit long + private static (int, int) LongToInts(long longId) + { + return ((int)(longId & uint.MaxValue), (int)(longId >> 32)); } + #endregion } diff --git a/CaptchaSharp/Services/DeathByCaptchaService.cs b/CaptchaSharp/Services/DeathByCaptchaService.cs index e3120e5..4c19e50 100644 --- a/CaptchaSharp/Services/DeathByCaptchaService.cs +++ b/CaptchaSharp/Services/DeathByCaptchaService.cs @@ -12,262 +12,312 @@ using System.Threading; using System.Threading.Tasks; using System.Web; +using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://www.deathbycaptcha.com/ +/// +public class DeathByCaptchaService : CaptchaService { - /// The service provided by https://www.deathbycaptcha.com/ - public class DeathByCaptchaService : CaptchaService + /// + /// Your DeathByCaptcha account name. + /// + public string Username { get; set; } + + /// + /// Your DeathByCaptcha account password. + /// + public string Password { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /* + * Sometimes the DBC API randomly replies with query strings even when json is requested, so + * we will avoid using the Accept: application/json header. + */ + + /// + /// Initializes a . + /// + /// Your DeathByCaptcha account name. + /// Your DeathByCaptcha account password. + /// The used for requests. If null, a default one will be created. + public DeathByCaptchaService(string username, string password, HttpClient? httpClient = null) { - /// Your DeathByCaptcha account name. - public string Username { get; set; } - - /// Your DeathByCaptcha account password. - public string Password { get; set; } + Username = username; + Password = password; + this._httpClient = httpClient ?? new HttpClient(); + + // TODO: Use https instead of http if possible + this._httpClient.BaseAddress = new Uri("http://api.dbcapi.me/api/"); + } - /// The default used for requests. - protected HttpClient httpClient; + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostAsync( + "user", + GetAuthPair(), + cancellationToken: cancellationToken) + .ConfigureAwait(false); - /* - * Sometimes the DBC API randomly replies with query strings even when json is requested, so - * we will avoid using the Accept: application/json header. - */ + var query = HttpUtility.ParseQueryString(await DecodeIsoResponse(response)); - /// Initializes a using the given account credentials and - /// . If is null, a default one will be created. - public DeathByCaptchaService(string username, string password, HttpClient httpClient = null) + if (IsError(query)) { - Username = username; - Password = password; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("http://api.dbcapi.me/api/"); + throw new BadAuthenticationException(GetErrorMessage(query)); } - - #region Getting the Balance - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) + + var balanceString = query["balance"]; + + if (balanceString == null) { - var response = await httpClient.PostAsync( - "user", - GetAuthPair(), - cancellationToken) - .ConfigureAwait(false); + throw new TaskCreationException("The server didn't return the balance"); + } - var query = HttpUtility.ParseQueryString(await DecodeIsoResponse(response)); + // The server returns the balance in cents + return decimal.Parse(balanceString, CultureInfo.InvariantCulture) / 100; + } + #endregion - if (IsError(query)) - throw new BadAuthenticationException(GetErrorMessage(query)); + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostAsync + ("captcha", + GetAuthPair() + .Add("captchafile", $"base64:{base64}") + .ToMultipartFormDataContent(), + cancellationToken); + + return await GetResult(HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.ImageCaptcha, cancellationToken); + } - // The server returns the balance in cents - return decimal.Parse(query["balance"], CultureInfo.InvariantCulture) / 100; - } - #endregion + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + DBCTaskProxyless task; - #region Solve Methods - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) + if (proxy is not null) { - var response = await httpClient.PostAsync - ("captcha", - GetAuthPair() - .Add("captchafile", $"base64:{base64}") - .ToMultipartFormDataContent(), - cancellationToken); - - return await TryGetResult(HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), - CaptchaType.ImageCaptcha, cancellationToken) as StringResponse; + task = new RecaptchaV2Task + { + GoogleKey = siteKey, + PageUrl = siteUrl + }.SetProxy(proxy); } - - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) + else { - DBCTaskProxyless task; - - if (proxy != null) - { - task = new RecaptchaV2Task - { - GoogleKey = siteKey, - PageUrl = siteUrl - }.SetProxy(proxy); - } - else + task = new RecaptchaV2TaskProxyless { - task = new RecaptchaV2TaskProxyless - { - GoogleKey = siteKey, - PageUrl = siteUrl - }; - } - - var response = await httpClient.PostAsync( + GoogleKey = siteKey, + PageUrl = siteUrl + }; + } + + var response = await _httpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 4) .Add("token_params", task.SerializeLowerCase()), - cancellationToken) - .ConfigureAwait(false); + cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await TryGetResult(HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), - CaptchaType.ReCaptchaV2, cancellationToken) as StringResponse; - } + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.ReCaptchaV2, cancellationToken); + } - /// - public async override Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - DBCTaskProxyless task; + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action, float minScore, bool enterprise = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + DBCTaskProxyless task; - if (proxy != null) + if (proxy is not null) + { + task = new RecaptchaV3Task { - task = new RecaptchaV3Task - { - GoogleKey = siteKey, - PageUrl = siteUrl, - Action = action, - Min_Score = minScore - }.SetProxy(proxy); - } - else + GoogleKey = siteKey, + PageUrl = siteUrl, + Action = action, + Min_Score = minScore + }.SetProxy(proxy); + } + else + { + task = new RecaptchaV3TaskProxyless { - task = new RecaptchaV3TaskProxyless - { - GoogleKey = siteKey, - PageUrl = siteUrl, - Action = action, - Min_Score = minScore - }; - } - - var response = await httpClient.PostAsync( + GoogleKey = siteKey, + PageUrl = siteUrl, + Action = action, + Min_Score = minScore + }; + } + + var response = await _httpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 5) .Add("token_params", task.SerializeLowerCase()), - cancellationToken) - .ConfigureAwait(false); + cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await TryGetResult(HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), - CaptchaType.ReCaptchaV3, cancellationToken) as StringResponse; - } + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.ReCaptchaV3, cancellationToken); + } - /// - public async override Task SolveFuncaptchaAsync - (string publicKey, string serviceUrl, string siteUrl, bool noJS = false, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - DBCTaskProxyless task; + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + DBCTaskProxyless task; - if (proxy != null) + if (proxy is not null) + { + task = new FuncaptchaTask { - task = new FuncaptchaTask - { - PublicKey = publicKey, - PageUrl = siteUrl - }.SetProxy(proxy); - } - else + PublicKey = publicKey, + PageUrl = siteUrl + }.SetProxy(proxy); + } + else + { + task = new FuncaptchaTaskProxyless { - task = new FuncaptchaTaskProxyless - { - PublicKey = publicKey, - PageUrl = siteUrl - }; - } - - var response = await httpClient.PostAsync( + PublicKey = publicKey, + PageUrl = siteUrl + }; + } + + var response = await _httpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 6) .Add("funcaptcha_params", task.SerializeLowerCase()), - cancellationToken) - .ConfigureAwait(false); + cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await TryGetResult(HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), - CaptchaType.FunCaptcha, cancellationToken) as StringResponse; - } - #endregion + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.FunCaptcha, cancellationToken); + } + #endregion - #region Getting the result - private async Task TryGetResult - (NameValueCollection response, CaptchaType type, CancellationToken cancellationToken = default) + #region Getting the result + private async Task GetResult( + NameValueCollection response, CaptchaType type, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (IsError(response)) { - if (IsError(response)) - throw new TaskCreationException(GetErrorMessage(response)); - - var task = new CaptchaTask(response["captcha"], type); - - return await TryGetResult(task, cancellationToken).ConfigureAwait(false); + throw new TaskCreationException(GetErrorMessage(response)); } - - /// - protected async override Task CheckResult(CaptchaTask task, CancellationToken cancellationToken = default) + + var captchaId = response["captcha"]; + + if (captchaId == null) { - var response = await httpClient.GetAsync($"captcha/{task.Id}", cancellationToken); - var query = HttpUtility.ParseQueryString(await DecodeIsoResponse(response)); + throw new TaskCreationException("The server didn't return the captcha ID"); + } - if (query["text"] == string.Empty) - return default; + var task = new CaptchaTask(captchaId, type); - task.Completed = true; + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } - if (IsError(query) || query["is_correct"] == "0") - throw new TaskSolutionException(GetErrorMessage(query)); + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + var response = await _httpClient.GetAsync($"captcha/{task.Id}", cancellationToken); + var query = HttpUtility.ParseQueryString(await DecodeIsoResponse(response)); - return new StringResponse() { Id = task.Id, Response = query["text"] }; + var text = query["text"]; + + if (text is null or "") + { + return null; } - #endregion - #region Reporting the solution - /// - public async override Task ReportSolution - (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + task.Completed = true; + + if (IsError(query) || query["is_correct"] == "0") { - if (correct) - throw new NotSupportedException("This service doesn't allow reporting of good solutions"); + throw new TaskSolutionException(GetErrorMessage(query)); + } + + // Only StringResponse is supported + if (typeof(T) != typeof(StringResponse)) + { + throw new NotSupportedException(); + } + + return new StringResponse { Id = task.Id, Response = text } as T; + } + #endregion + + #region Reporting the solution + /// + public override async Task ReportSolution( + long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + if (correct) + throw new NotSupportedException("This service doesn't allow reporting of good solutions"); - var response = await httpClient.PostAsync( + var response = await _httpClient.PostAsync( $"captcha/{id}/report", GetAuthPair(), - cancellationToken) - .ConfigureAwait(false); + cancellationToken: cancellationToken) + .ConfigureAwait(false); - var query = HttpUtility.ParseQueryString(await DecodeIsoResponse(response)); + var query = HttpUtility.ParseQueryString(await DecodeIsoResponse(response)); - if (IsError(query)) - throw new TaskReportException(GetErrorMessage(query)); - } - #endregion + if (IsError(query)) + throw new TaskReportException(GetErrorMessage(query)); + } + #endregion - #region Private Methods - private async Task DecodeIsoResponse(HttpResponseMessage response) - { - using (var sr = new StreamReader( - await response.Content.ReadAsStreamAsync(), Encoding.GetEncoding("iso-8859-1"))) - { - return sr.ReadToEnd(); - } - } + #region Private Methods + private async Task DecodeIsoResponse(HttpResponseMessage response) + { + using var sr = new StreamReader( + await response.Content.ReadAsStreamAsync(), + Encoding.GetEncoding("iso-8859-1")); + + return await sr.ReadToEndAsync(); + } - private StringPairCollection GetAuthPair() - { - return new StringPairCollection() - .Add("username", Username) - .Add("password", Password); - } + private StringPairCollection GetAuthPair() + { + return new StringPairCollection() + .Add("username", Username) + .Add("password", Password); + } - private bool IsError(NameValueCollection response) - { - return response["status"] == "255"; - } + private static bool IsError(NameValueCollection response) + { + return response["status"] == "255"; + } - private string GetErrorMessage(NameValueCollection response) - { - return response["error"]; - } - #endregion + private static string GetErrorMessage(NameValueCollection response) + { + return response["error"] ?? "Unknown error"; } + #endregion } diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index 56c575a..07c501a 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -7,334 +7,368 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://www.imagetyperz.com/ +/// +public class ImageTyperzService : CaptchaService { - /// The service provided by https://www.imagetyperz.com/ - public class ImageTyperzService : CaptchaService + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /// + /// The ID of the software developer. + /// + private const int _affiliateId = 671869; + + /// + /// Initializes a . + /// + /// Your secret api key. + /// The to use for requests. If null, a default one will be created. + public ImageTyperzService(string apiKey, HttpClient? httpClient = null) { - /// Your secret api key. - public string ApiKey { get; set; } - - /// The default used for requests. - private HttpClient httpClient; - - /// The ID of the software developer. - private readonly int affiliateId = 671869; - - /// Initializes a using the given and - /// . If is null, a default one will be created. - public ImageTyperzService(string apiKey, HttpClient httpClient = null) - { - ApiKey = apiKey; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("http://captchatypers.com"); - - // Since this service replies directly with the solution to the task creation request (for image captchas) - // we need to set a high timeout here or it will not finish in time - this.httpClient.Timeout = Timeout; - } + ApiKey = apiKey; + this._httpClient = httpClient ?? new HttpClient(); + + // TODO: Use https instead of http if possible + this._httpClient.BaseAddress = new Uri("http://captchatypers.com"); + + // Since this service replies directly with the solution to the task creation request (for image captchas) + // we need to set a high timeout here, or it will not finish in time + this._httpClient.Timeout = Timeout; + } - #region Getting the Balance - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - var response = await httpClient.PostToStringAsync - ("Forms/RequestBalanceToken.ashx", + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync + ("Forms/RequestBalanceToken.ashx", GetAuthPair() - .Add("action", "REQUESTBALANCE"), - cancellationToken) - .ConfigureAwait(false); + .Add("action", "REQUESTBALANCE"), + cancellationToken: cancellationToken) + .ConfigureAwait(false); - if (IsError(response)) - throw new BadAuthenticationException(GetErrorMessage(response)); + if (IsError(response)) + throw new BadAuthenticationException(GetErrorMessage(response)); - return decimal.Parse(response, CultureInfo.InvariantCulture); - } - #endregion + return decimal.Parse(response, CultureInfo.InvariantCulture); + } + #endregion - #region Solve Methods - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostToStringAsync - ("Forms/UploadFileAndGetTextNEWToken.ashx", + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync + ("Forms/UploadFileAndGetTextNEWToken.ashx", GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("file", base64) - .Add(ConvertCapabilities(options)), - cancellationToken) - .ConfigureAwait(false); + .Add("action", "UPLOADCAPTCHA") + .Add("file", base64) + .Add(ConvertCapabilities(options)), + cancellationToken: cancellationToken) + .ConfigureAwait(false); - if (IsError(response)) - throw new TaskSolutionException(GetErrorMessage(response)); + if (IsError(response)) + throw new TaskSolutionException(GetErrorMessage(response)); - var split = response.Split(new char[] { '|' }, 2); - return new StringResponse { Id = long.Parse(split[0]), Response = split[1] }; - } + var split = response.Split(['|'], 2); + return new StringResponse { Id = long.Parse(split[0]), Response = split[1] }; + } - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostToStringAsync - (enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync + (enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", siteUrl) - .Add("googlekey", siteKey) - .Add("recaptchatype", invisible ? 2 : 1, !enterprise) - .Add("enterprise_type", "v2", enterprise) - .Add("data-s", dataS, !string.IsNullOrEmpty(dataS)) - .Add(GetProxyParams(proxy)), - cancellationToken) - .ConfigureAwait(false); - - return await TryGetResult(response, CaptchaType.ReCaptchaV2, cancellationToken) as StringResponse; - } + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", siteUrl) + .Add("googlekey", siteKey) + .Add("recaptchatype", invisible ? 2 : 1, !enterprise) + .Add("enterprise_type", "v2", enterprise) + .Add("data-s", dataS, !string.IsNullOrEmpty(dataS)) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.ReCaptchaV2, cancellationToken); + } - /// - public async override Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostToStringAsync - (enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", + /// + public override async Task SolveRecaptchaV3Async + (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync + (enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", siteUrl) - .Add("googlekey", siteKey) - .Add("captchaaction", action) - .Add("score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) - .Add("recaptchatype", 3, !enterprise) - .Add("enterprise_type", "v3", enterprise) - .Add(GetProxyParams(proxy)), - cancellationToken) - .ConfigureAwait(false); - - return await TryGetResult(response, CaptchaType.ReCaptchaV2, cancellationToken) as StringResponse; - } + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", siteUrl) + .Add("googlekey", siteKey) + .Add("captchaaction", action) + .Add("score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) + .Add("recaptchatype", 3, !enterprise) + .Add("enterprise_type", "v3", enterprise) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.ReCaptchaV2, cancellationToken); + } - /// - public async override Task SolveHCaptchaAsync - (string siteKey, string siteUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - var response = await httpClient.PostToStringAsync - ("captchaapi/UploadRecaptchaToken.ashx", + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync + ("captchaapi/UploadRecaptchaToken.ashx", GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", $"{siteUrl}--hcaptcha") - .Add("googlekey", $"{siteKey}--hcaptcha") - .Add(GetProxyParams(proxy)), - cancellationToken) - .ConfigureAwait(false); - - return await TryGetResult(response, CaptchaType.HCaptcha, cancellationToken) as StringResponse; - } + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", $"{siteUrl}--hcaptcha") + .Add("googlekey", $"{siteKey}--hcaptcha") + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.HCaptcha, cancellationToken); + } - /// - public async override Task SolveGeeTestAsync - (string gt, string challenge, string apiServer, string siteUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("captchaapi/UploadGeeTestToken.ashx", + /// + public override async Task SolveGeeTestAsync( + string gt, string challenge, string apiServer, string siteUrl, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("captchaapi/UploadGeeTestToken.ashx", GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("gt", gt) - .Add("challenge", challenge) - .Add("domain", siteUrl), + .Add("action", "UPLOADCAPTCHA") + .Add("gt", gt) + .Add("challenge", challenge) + .Add("domain", siteUrl), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response, CaptchaType.GeeTest, cancellationToken) as GeeTestResponse; - } + return await GetResult( + response, CaptchaType.GeeTest, cancellationToken); + } - /// - public async override Task SolveCapyAsync - (string siteKey, string siteUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - var response = await httpClient.PostToStringAsync - ("captchaapi/UploadRecaptchaToken.ashx", + /// + public override async Task SolveCapyAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync + ("captchaapi/UploadRecaptchaToken.ashx", GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", $"{siteUrl}--capy") - .Add("googlekey", $"{siteKey}--capy") - .Add(GetProxyParams(proxy)), - cancellationToken) - .ConfigureAwait(false); + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", $"{siteUrl}--capy") + .Add("googlekey", $"{siteKey}--capy") + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + // TODO: Check this, the get result method is not implemented for Capy + return await GetResult( + response, CaptchaType.Capy, cancellationToken); + } + #endregion - return await TryGetResult(response, CaptchaType.Capy, cancellationToken) as CapyResponse; - } - #endregion + #region Getting the result + private async Task GetResult( + string response, CaptchaType type, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (IsError(response)) + throw new TaskCreationException(response); - #region Getting the result - private async Task TryGetResult - (string response, CaptchaType type, CancellationToken cancellationToken = default) - { - if (IsError(response)) - throw new TaskCreationException(response); + var task = new CaptchaTask(response, type); - var task = new CaptchaTask(response, type); + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } - return await TryGetResult(task, cancellationToken).ConfigureAwait(false); - } + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + string response; - /// - protected async override Task CheckResult - (CaptchaTask task, CancellationToken cancellationToken = default) + if (task.Type == CaptchaType.GeeTest) { - string response; - - if (task.Type == CaptchaType.GeeTest) - { - response = await httpClient.GetStringAsync - ("captchaapi/getrecaptchatext.ashx", + response = await _httpClient.GetStringAsync + ("captchaapi/getrecaptchatext.ashx", GetAuthPair() - .Add("action", "GETTEXT") - .Add("captchaID", task.Id), + .Add("action", "GETTEXT") + .Add("captchaID", task.Id), cancellationToken) - .ConfigureAwait(false); - } - else - { - response = await httpClient.PostToStringAsync - ("captchaapi/GetRecaptchaTextToken.ashx", + .ConfigureAwait(false); + } + else + { + response = await _httpClient.PostToStringAsync + ("captchaapi/GetRecaptchaTextToken.ashx", GetAuthPair() - .Add("action", "GETTEXT") - .Add("captchaID", task.Id), - cancellationToken) - .ConfigureAwait(false); - } - - if (response.Contains("NOT_DECODED")) - return default; + .Add("action", "GETTEXT") + .Add("captchaID", task.Id), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - task.Completed = true; + if (response.Contains("NOT_DECODED")) + { + return default; + } - if (IsError(response)) - throw new TaskSolutionException(response); + task.Completed = true; - if (task.Type == CaptchaType.GeeTest) + if (IsError(response)) + { + throw new TaskSolutionException(response); + } + + // GeeTestResponse needs GeeTest captcha type + if (typeof(T) == typeof(GeeTestResponse)) + { + if (task.Type is not CaptchaType.GeeTest) { - var split = response.Split(new string[] { ";;;" }, 3, StringSplitOptions.None); - return new GeeTestResponse - { - Challenge = split[0], - Validate = split[1], - SecCode = split[2] - }; + throw new TaskSolutionException("The task is not a GeeTest captcha"); } - else + + var split = response.Split([";;;"], 3, StringSplitOptions.None); + + return new GeeTestResponse { - return new StringResponse { Id = task.Id, Response = response }; - } + Challenge = split[0], + Validate = split[1], + SecCode = split[2] + } as T; } - #endregion - #region Reporting the solution - /// - public async override Task ReportSolution - (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + // If it's not a StringResponse, throw + if (typeof(T) != typeof(StringResponse)) { - var response = await httpClient.PostToStringAsync - ("Forms/SetBadImageToken.ashx", + throw new NotSupportedException("Only StringResponse and GeeTestResponse are supported"); + } + + return new StringResponse { Id = task.Id, Response = response } as T; + } + #endregion + + #region Reporting the solution + /// + public override async Task ReportSolution + (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync + ("Forms/SetBadImageToken.ashx", GetAuthPair() - .Add("imageid", id) - .Add("action", "SETBADIMAGE"), - cancellationToken) - .ConfigureAwait(false); + .Add("imageid", id) + .Add("action", "SETBADIMAGE"), + cancellationToken: cancellationToken) + .ConfigureAwait(false); - if (response != "SUCCESS") - throw new TaskReportException(response); - } - #endregion + if (response != "SUCCESS") + throw new TaskReportException(response); + } + #endregion - #region Private Methods - private StringPairCollection GetAuthPair() - => new StringPairCollection().Add("token", ApiKey); + #region Private Methods + private StringPairCollection GetAuthPair() + => new StringPairCollection().Add("token", ApiKey); - private StringPairCollection GetAuthAffiliatePair() - => GetAuthPair().Add("affiliateid", affiliateId); + private StringPairCollection GetAuthAffiliatePair() + => GetAuthPair().Add("affiliateid", _affiliateId); - private bool IsError(string response) - => response.StartsWith("ERROR:"); + private static bool IsError(string response) + => response.StartsWith("ERROR:"); - private string GetErrorMessage(string response) - => response.Replace("ERROR: ", ""); + private static string GetErrorMessage(string response) + => response.Replace("ERROR: ", ""); - private IEnumerable<(string, string)> GetProxyParams(Proxy proxy) + private static List<(string, string)> GetProxyParams(Proxy? proxy) + { + if (proxy == null) { - if (proxy == null) - return new (string, string)[] { }; - - if (proxy.Type != ProxyType.HTTP && proxy.Type != ProxyType.HTTPS) - throw new NotSupportedException("The api only supports HTTP proxies"); + return []; + } - var proxyPairs = new List<(string, string)> - { - ("useragent", proxy.UserAgent), - ("proxytype", "HTTP") - }; + if (proxy.Type != ProxyType.HTTP && proxy.Type != ProxyType.HTTPS) + { + throw new NotSupportedException("The api only supports HTTP proxies"); + } - if (proxy.RequiresAuthentication) - proxyPairs.Add(("proxy", $"{proxy.Host}:{proxy.Port}:{proxy.Username}:{proxy.Password}")); - else - proxyPairs.Add(("proxy", $"{proxy.Host}:{proxy.Port}")); + var proxyPairs = new List<(string, string)> + { + ("proxytype", "HTTP"), + proxy.RequiresAuthentication + ? ("proxy", $"{proxy.Host}:{proxy.Port}:{proxy.Username}:{proxy.Password}") + : ("proxy", $"{proxy.Host}:{proxy.Port}") + }; - return proxyPairs; + if (proxy.UserAgent is not null) + { + proxyPairs.Add(("useragent", proxy.UserAgent)); } - #endregion - - #region Capabilities - /// - public override CaptchaServiceCapabilities Capabilities => - CaptchaServiceCapabilities.Phrases | - CaptchaServiceCapabilities.CaseSensitivity | - CaptchaServiceCapabilities.CharacterSets | - CaptchaServiceCapabilities.Calculations | - CaptchaServiceCapabilities.MinLength | - CaptchaServiceCapabilities.MaxLength; - - private IEnumerable<(string, string)> ConvertCapabilities(ImageCaptchaOptions options) + + return proxyPairs; + } + #endregion + + #region Capabilities + /// + public override CaptchaServiceCapabilities Capabilities => + CaptchaServiceCapabilities.Phrases | + CaptchaServiceCapabilities.CaseSensitivity | + CaptchaServiceCapabilities.CharacterSets | + CaptchaServiceCapabilities.Calculations | + CaptchaServiceCapabilities.MinLength | + CaptchaServiceCapabilities.MaxLength; + + private static List<(string, string)> ConvertCapabilities(ImageCaptchaOptions? options) + { + // If null, don't return any parameters + if (options is null) { - // If null, don't return any parameters - if (options == null) - return new (string, string)[] { }; - - var capabilities = new List<(string, string)> - { - ("iscase", options.CaseSensitive.ToString().ToLower()), - ("isphrase", options.IsPhrase.ToString().ToLower()), - ("ismath", options.RequiresCalculation.ToString().ToLower()), - ("minlength", options.MinLength.ToString()), - ("maxlength", options.MaxLength.ToString()) - }; - - int alphanumeric; - switch (options.CharacterSet) - { - default: - alphanumeric = 0; - break; + return []; + } - case CharacterSet.OnlyNumbers: - alphanumeric = 1; - break; + var capabilities = new List<(string, string)> + { + ("iscase", options.CaseSensitive.ToString().ToLower()), + ("isphrase", options.IsPhrase.ToString().ToLower()), + ("ismath", options.RequiresCalculation.ToString().ToLower()), + ("minlength", options.MinLength.ToString()), + ("maxlength", options.MaxLength.ToString()) + }; - case CharacterSet.OnlyLetters: - alphanumeric = 2; - break; - } + var alphanumeric = options.CharacterSet switch + { + CharacterSet.OnlyNumbers => 1, + CharacterSet.OnlyLetters => 2, + _ => 0 + }; - capabilities.Add(("alphanumeric", alphanumeric.ToString())); + capabilities.Add(("alphanumeric", alphanumeric.ToString())); - return capabilities; - } - #endregion + return capabilities; } + #endregion } diff --git a/CaptchaSharp/Services/NineKWService.cs b/CaptchaSharp/Services/NineKWService.cs index fb1ce72..8040e56 100644 --- a/CaptchaSharp/Services/NineKWService.cs +++ b/CaptchaSharp/Services/NineKWService.cs @@ -8,76 +8,86 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://www.9kw.eu/ +/// +public class NineKwService : CaptchaService { - /// The service provided by https://www.9kw.eu/ - public class NineKWService : CaptchaService + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /// + /// Initializes a . + /// + /// Your secret api key. + /// The to use for requests. If null, a default one will be created. + public NineKwService(string apiKey, HttpClient? httpClient = null) { - /// Your secret api key. - public string ApiKey { get; set; } - - /// The default used for requests. - private HttpClient httpClient; - - /// Initializes a using the given and - /// . If is null, a default one will be created. - public NineKWService(string apiKey, HttpClient httpClient = null) - { - ApiKey = apiKey; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("https://www.9kw.eu/"); + ApiKey = apiKey; + this._httpClient = httpClient ?? new HttpClient(); + this._httpClient.BaseAddress = new Uri("https://www.9kw.eu/"); - // Since this service replies directly with the solution to the task creation request (for image captchas) - // we need to set a high timeout here or it will not finish in time - this.httpClient.Timeout = Timeout; - } + // Since this service replies directly with the solution to the task creation request (for image captchas) + // we need to set a high timeout here, or it will not finish in time + this._httpClient.Timeout = Timeout; + } - #region Getting the Balance - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("index.cgi", + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("index.cgi", GetAuthPair() - .Add("action", "usercaptchaguthaben"), + .Add("action", "usercaptchaguthaben"), cancellationToken) - .ConfigureAwait(false); - - if (IsError(response)) - throw new BadAuthenticationException(GetErrorMessage(response)); + .ConfigureAwait(false); - return decimal.Parse(response, CultureInfo.InvariantCulture); + if (IsError(response)) + { + throw new BadAuthenticationException(GetErrorMessage(response)); } - #endregion - #region Solve Methods - /// - public async override Task SolveTextCaptchaAsync - (string text, TextCaptchaOptions options = default, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("index.cgi", + return decimal.Parse(response, CultureInfo.InvariantCulture); + } + #endregion + + #region Solve Methods + /// + public override async Task SolveTextCaptchaAsync + (string text, TextCaptchaOptions? options = default, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostMultipartToStringAsync + ("index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") .Add("file-upload-01", text) .Add("textonly", 1) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - if (IsError(response)) - throw new TaskSolutionException(GetErrorMessage(response)); - - return await TryGetResult(response, CaptchaType.TextCaptcha, cancellationToken).ConfigureAwait(false) as StringResponse; - } + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.TextCaptcha, cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("index.cgi", + /// + public override async Task SolveImageCaptchaAsync + (string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostMultipartToStringAsync + ("index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") .Add("file-upload-01", base64) @@ -85,21 +95,24 @@ public async override Task SolveImageCaptchaAsync .Add(ConvertCapabilities(options)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - if (IsError(response)) - throw new TaskSolutionException(GetErrorMessage(response)); - - return await TryGetResult(response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false) as StringResponse; + if (IsError(response)) + { + throw new TaskSolutionException(GetErrorMessage(response)); } - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("index.cgi", + return await GetResult( + response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") .Add("interactive", 1) @@ -108,18 +121,19 @@ public async override Task SolveRecaptchaV2Async .Add("pageurl", siteUrl) .Add(ConvertProxy(proxy)), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false) as StringResponse; - } + return await GetResult( + response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("index.cgi", + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") .Add("interactive", 1) @@ -128,18 +142,19 @@ public async override Task SolveRecaptchaV3Async .Add("pageurl", siteUrl) .Add(ConvertProxy(proxy)), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false) as StringResponse; - } + return await GetResult( + response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveFuncaptchaAsync - (string publicKey, string serviceUrl, string siteUrl, bool noJS = false, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("index.cgi", + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") .Add("interactive", 1) @@ -148,17 +163,18 @@ public async override Task SolveFuncaptchaAsync .Add("pageurl", siteUrl) .Add(ConvertProxy(proxy)), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false) as StringResponse; - } + return await GetResult( + response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveHCaptchaAsync - (string siteKey, string siteUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("index.cgi", + /// + public override async Task SolveHCaptchaAsync + (string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") .Add("interactive", 1) @@ -167,163 +183,201 @@ public async override Task SolveHCaptchaAsync .Add("pageurl", siteUrl) .Add(ConvertProxy(proxy)), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false) as StringResponse; - } - #endregion + return await GetResult( + response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); + } + #endregion - #region Getting the result - private async Task TryGetResult - (string response, CaptchaType type, CancellationToken cancellationToken = default) + #region Getting the result + private async Task GetResult( + string response, CaptchaType type, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (IsError(response)) { - if (IsError(response)) - throw new TaskCreationException(response); + throw new TaskCreationException(response); + } - var task = new CaptchaTask(response, type); + var task = new CaptchaTask(response, type); - return await TryGetResult(task, cancellationToken).ConfigureAwait(false); - } + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } - /// - protected async override Task CheckResult - (CaptchaTask task, CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + var response = await _httpClient.GetStringAsync + ("index.cgi", + GetAuthPair() .Add("action", "usercaptchacorrectdata") .Add("id", task.Id), - cancellationToken) - .ConfigureAwait(false); - - // Not solved yet - if (string.IsNullOrEmpty(response) || response.Contains("CAPTCHA_NOT_READY")) - return default; + cancellationToken) + .ConfigureAwait(false); - task.Completed = true; + // Not solved yet + if (string.IsNullOrEmpty(response) || response.Contains("CAPTCHA_NOT_READY")) + { + return null; + } - if (IsError(response) || response.Contains("ERROR_NO_USER")) - throw new TaskSolutionException(response); + task.Completed = true; - return new StringResponse { Id = task.Id, Response = response }; + if (IsError(response) || response.Contains("ERROR_NO_USER")) + { + throw new TaskSolutionException(response); } - #endregion - #region Reporting the solution - /// - public async override Task ReportSolution - (long taskId, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + // Only StringResponse is supported + if (typeof(T) != typeof(StringResponse)) { - var response = await httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchacorrectback") - .Add("id", taskId.ToString()) - .Add("correct", correct ? 1 : 2), - cancellationToken); - - if (IsError(response)) - throw new TaskReportException(response); + throw new NotSupportedException(); } - #endregion + + return new StringResponse { Id = task.Id, Response = response } as T; + } + #endregion + + #region Reporting the solution + /// + public override async Task ReportSolution( + long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("index.cgi", + GetAuthPair() + .Add("action", "usercaptchacorrectback") + .Add("id", id.ToString()) + .Add("correct", correct ? 1 : 2), + cancellationToken); + + if (IsError(response)) + throw new TaskReportException(response); + } + #endregion - #region Private Methods - private StringPairCollection GetAuthPair() - => new StringPairCollection().Add("apikey", ApiKey); + #region Private Methods + private StringPairCollection GetAuthPair() + => new StringPairCollection().Add("apikey", ApiKey); - private bool IsError(string response) - => Regex.IsMatch(response, @"^\d{4} "); + private static bool IsError(string response) + => Regex.IsMatch(response, @"^\d{4} "); - private string GetErrorMessage(string response) - => Regex.Replace(response, @"^\d{4} ", ""); - #endregion + private static string GetErrorMessage(string response) + => Regex.Replace(response, @"^\d{4} ", ""); + #endregion - #region Proxies - /// - protected IEnumerable<(string, string)> ConvertProxy(Proxy proxy) + #region Proxies + /// + private static List<(string, string)> ConvertProxy(Proxy? proxy) + { + if (proxy is null) { - if (proxy == null) - return new (string, string)[] { }; + return []; + } - return new (string, string)[] - { + var proxyParams = new List<(string, string)>(); + + if (proxy.UserAgent is not null) + { + proxyParams.Add(("useragent", proxy.UserAgent)); + } + + if (proxy.Cookies is not null) + { + proxyParams.Add(("cookies", proxy.GetCookieString())); + } + + // TODO: Check if credentials are supported + + if (!string.IsNullOrEmpty(proxy.Host)) + { + proxyParams.AddRange([ ("proxy", $"{proxy.Host}:{proxy.Port}"), - ("proxytype", proxy.Type.ToString().ToLower()), - ("useragent", proxy.UserAgent), - ("cookies", proxy.GetCookieString()) - }; + ("proxytype", proxy.Type.ToString().ToLower()) + ]); } - #endregion - - #region Capabilities - /// - public new CaptchaServiceCapabilities Capabilities => - CaptchaServiceCapabilities.Phrases | - CaptchaServiceCapabilities.CaseSensitivity | - CaptchaServiceCapabilities.CharacterSets | - CaptchaServiceCapabilities.Calculations | - CaptchaServiceCapabilities.MinLength | - CaptchaServiceCapabilities.MaxLength | - CaptchaServiceCapabilities.Instructions; - - /// - protected IEnumerable<(string, string)> ConvertCapabilities(ImageCaptchaOptions options) + + return proxyParams; + } + #endregion + + #region Capabilities + /// + /// The capabilities of the service. + /// + public new CaptchaServiceCapabilities Capabilities => + CaptchaServiceCapabilities.Phrases | + CaptchaServiceCapabilities.CaseSensitivity | + CaptchaServiceCapabilities.CharacterSets | + CaptchaServiceCapabilities.Calculations | + CaptchaServiceCapabilities.MinLength | + CaptchaServiceCapabilities.MaxLength | + CaptchaServiceCapabilities.Instructions; + + /// + private IEnumerable<(string, string)> ConvertCapabilities(ImageCaptchaOptions? options) + { + // If null, don't return any parameters + if (options is null) { - // If null, don't return any parameters - if (options == null) - return new (string, string)[] { }; + return []; + } - var capabilities = new List<(string, string)>(); + var capabilities = new List<(string, string)>(); - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Phrases)) - capabilities.Add(("phrase", Convert.ToInt32(options.IsPhrase).ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Phrases)) + { + capabilities.Add(("phrase", Convert.ToInt32(options.IsPhrase).ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.CaseSensitivity)) - capabilities.Add(("case-sensitive", Convert.ToInt32(options.CaseSensitive).ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.CaseSensitivity)) + { + capabilities.Add(("case-sensitive", Convert.ToInt32(options.CaseSensitive).ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.CharacterSets)) - { - int charSet = 0; + if (Capabilities.HasFlag(CaptchaServiceCapabilities.CharacterSets)) + { + var charSet = 0; - if (options.CharacterSet != CharacterSet.OnlyNumbersOrOnlyLetters) + if (options.CharacterSet != CharacterSet.OnlyNumbersOrOnlyLetters) + { + charSet = options.CharacterSet switch { - switch (options.CharacterSet) - { - case CharacterSet.OnlyNumbers: - charSet = 1; - break; - - case CharacterSet.OnlyLetters: - charSet = 2; - break; - - case CharacterSet.BothNumbersAndLetters: - charSet = 3; - break; - - default: - break; - } - } - - capabilities.Add(("numeric", charSet.ToString())); + CharacterSet.OnlyNumbers => 1, + CharacterSet.OnlyLetters => 2, + CharacterSet.BothNumbersAndLetters => 3, + _ => 0 + }; } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Calculations)) - capabilities.Add(("math", Convert.ToInt32(options.RequiresCalculation).ToString())); + capabilities.Add(("numeric", charSet.ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.MinLength)) - capabilities.Add(("min_len", options.MinLength.ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Calculations)) + { + capabilities.Add(("math", Convert.ToInt32(options.RequiresCalculation).ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.MaxLength)) - capabilities.Add(("max_len", options.MaxLength.ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.MinLength)) + { + capabilities.Add(("min_len", options.MinLength.ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Instructions)) - capabilities.Add(("textinstructions", options.TextInstructions)); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.MaxLength)) + { + capabilities.Add(("max_len", options.MaxLength.ToString())); + } - return capabilities; + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Instructions)) + { + capabilities.Add(("textinstructions", options.TextInstructions)); } - #endregion + + return capabilities; } + #endregion } diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 1dfc8c9..f76ace5 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -8,125 +8,147 @@ using CaptchaSharp.Services.TwoCaptcha; using System.Collections.Generic; using System; +using CaptchaSharp.Extensions; using Newtonsoft.Json.Linq; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://2captcha.com/ +/// +public class TwoCaptchaService : CaptchaService { - /// The service provided by https://2captcha.com/ - public class TwoCaptchaService : CaptchaService + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + protected readonly HttpClient HttpClient; + + /// + /// Set it to false if the service does not support json responses. + /// + public bool UseJsonFlag { get; init; } = true; + + /// + /// Will include an Access-Control-Allow-Origin:* header in the response for + /// cross-domain AJAX requests in web applications. + /// + public bool AddAcaoHeader { get; set; } = false; + + /// The ID of the software developer. + private const int _softId = 2658; + + /// + /// Initializes a . + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public TwoCaptchaService(string apiKey, HttpClient? httpClient = null) { - /// Your secret api key. - public string ApiKey { get; set; } - - /// The default used for requests. - protected HttpClient httpClient; - - /// Set it to false if the service does not support json responses. - public bool UseJsonFlag { get; set; } = true; - - /// Will include an Access-Control-Allow-Origin:* header in the response for - /// cross-domain AJAX requests in web applications. - public bool AddACAOHeader { get; set; } = false; - - /// The ID of the software developer. - private readonly int softId = 2658; - - /// Initializes a using the given and - /// . If is null, a default one will be created. - public TwoCaptchaService(string apiKey, HttpClient httpClient = null) - { - ApiKey = apiKey; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("http://2captcha.com"); - } + ApiKey = apiKey; + this.HttpClient = httpClient ?? new HttpClient(); + + // TODO: Use https instead of http if possible + this.HttpClient.BaseAddress = new Uri("http://2captcha.com"); + } - #region Getting the Balance - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await HttpClient.GetStringAsync + ("res.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", "getbalance") + .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), + cancellationToken); + + if (UseJsonFlag) { - var response = await httpClient.GetStringAsync - ("res.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("action", "getbalance") - .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), - cancellationToken); + var tcResponse = response.Deserialize(); - if (UseJsonFlag) + if (tcResponse.IsErrorCode) { - var tcResponse = response.Deserialize(); - - if (tcResponse.IsErrorCode) - throw new BadAuthenticationException(tcResponse.Request); - - return decimal.Parse(tcResponse.Request, CultureInfo.InvariantCulture); + throw new BadAuthenticationException(tcResponse.Request); } - else - { - if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) - return balance; - else - throw new BadAuthenticationException(response); - } + return decimal.Parse(tcResponse.Request, CultureInfo.InvariantCulture); } - #endregion - #region Solve Methods - /// - public async override Task SolveTextCaptchaAsync - (string text, TextCaptchaOptions options = default, CancellationToken cancellationToken = default) + if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + return balance; + } + + throw new BadAuthenticationException(response); + } + #endregion + + #region Solve Methods + /// + public override async Task SolveTextCaptchaAsync( + string text, TextCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("textcaptcha", text) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertCapabilities(options)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.TextCaptcha, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.TextCaptcha, cancellationToken).ConfigureAwait(false) - ) as StringResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.TextCaptcha, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.TextCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "base64") .Add("body", base64) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertCapabilities(options)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false) - ) as StringResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.ImageCaptcha, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.ImageCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "userrecaptcha") @@ -135,27 +157,30 @@ public async override Task SolveRecaptchaV2Async .Add("data-s", dataS, !string.IsNullOrEmpty(dataS)) .Add("enterprise", Convert.ToInt32(enterprise).ToString()) .Add("invisible", Convert.ToInt32(invisible).ToString()) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertProxy(proxy)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false) - ) as StringResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.ReCaptchaV2, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.ReCaptchaV2, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "userrecaptcha") @@ -165,80 +190,90 @@ public async override Task SolveRecaptchaV3Async .Add("action", action) .Add("enterprise", Convert.ToInt32(enterprise).ToString()) .Add("min_score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertProxy(proxy)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false) - ) as StringResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.ReCaptchaV3, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.ReCaptchaV3, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveFuncaptchaAsync - (string publicKey, string serviceUrl, string siteUrl, bool noJS = false, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "funcaptcha") .Add("publickey", publicKey) .Add("surl", serviceUrl) .Add("pageurl", siteUrl) - .Add("nojs", Convert.ToInt32(noJS).ToString()) - .Add("soft_id", softId) + .Add("nojs", Convert.ToInt32(noJs).ToString()) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertProxy(proxy)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false) - ) as StringResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.FunCaptcha, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.FunCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveHCaptchaAsync - (string siteKey, string siteUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "hcaptcha") .Add("sitekey", siteKey) .Add("pageurl", siteUrl) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertProxy(proxy)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false) - ) as StringResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.HCaptcha, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.HCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveKeyCaptchaAsync - (string userId, string sessionId, string webServerSign1, string webServerSign2, string siteUrl, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveKeyCaptchaAsync( + string userId, string sessionId, string webServerSign1, string webServerSign2, string siteUrl, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "keycaptcha") @@ -247,27 +282,30 @@ public async override Task SolveKeyCaptchaAsync .Add("s_s_c_web_server_sign", webServerSign1) .Add("s_s_c_web_server_sign2", webServerSign2) .Add("pageurl", siteUrl) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertProxy(proxy)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.KeyCaptcha, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.KeyCaptcha, cancellationToken).ConfigureAwait(false) - ) as StringResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.KeyCaptcha, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.KeyCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveGeeTestAsync - (string gt, string challenge, string apiServer, string siteUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveGeeTestAsync( + string gt, string challenge, string apiServer, string siteUrl, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "geetest") @@ -275,356 +313,411 @@ public async override Task SolveGeeTestAsync .Add("challenge", challenge) .Add("api_server", apiServer) .Add("pageurl", siteUrl) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertProxy(proxy)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.GeeTest, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.GeeTest, cancellationToken).ConfigureAwait(false) - ) as GeeTestResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.GeeTest, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.GeeTest, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveCapyAsync - (string siteKey, string siteUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostMultipartToStringAsync - ("in.php", + /// + public override async Task SolveCapyAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "capy") .Add("captchakey", siteKey) .Add("pageurl", siteUrl) - .Add("soft_id", softId) + .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) + .Add("header_acao", "1", AddAcaoHeader) .Add(ConvertProxy(proxy)) .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.Capy, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.Capy, cancellationToken).ConfigureAwait(false) - ) as CapyResponse; - } + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.Capy, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.Capy, + cancellationToken).ConfigureAwait(false); + } - /// - public override async Task SolveDataDomeAsync(string siteUrl, string captchaUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) + /// + public override async Task SolveDataDomeAsync( + string siteUrl, string captchaUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + // Make sure there is a proxy with a User-Agent + if (proxy == null || string.IsNullOrEmpty(proxy.Host) || string.IsNullOrEmpty(proxy.UserAgent)) { - // Make sure there is a proxy with a User-Agent - if (proxy == null || string.IsNullOrEmpty(proxy.Host) || string.IsNullOrEmpty(proxy.UserAgent)) - { - throw new ArgumentException("A proxy with a User-Agent is required for DataDome captchas."); - } - - var response = await httpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "datadome") - .Add("captcha_url", captchaUrl) - .Add("pageurl", siteUrl) - .Add("soft_id", softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.DataDome, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.DataDome, cancellationToken).ConfigureAwait(false) - ) as StringResponse; + throw new ArgumentException("A proxy with a User-Agent is required for DataDome captchas."); } + + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "datadome") + .Add("captcha_url", captchaUrl) + .Add("pageurl", siteUrl) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.DataDome, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.DataDome, + cancellationToken).ConfigureAwait(false); + } - /// - public override async Task SolveCloudflareTurnstileAsync(string siteKey, string siteUrl, - string action, string data, string pageData, Proxy proxy = null, - CancellationToken cancellationToken = default) + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + // Make sure there is a proxy with a User-Agent + if (proxy == null || string.IsNullOrEmpty(proxy.UserAgent)) { - // Make sure there is a proxy with a User-Agent - if (proxy == null || string.IsNullOrEmpty(proxy.UserAgent)) - { - throw new ArgumentException("A User-Agent is required for Cloudflare Turnstile captchas."); - } - - var response = await httpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "turnstile") - .Add("sitekey", siteKey) - .Add("pageurl", siteUrl) - .Add("action", action, !string.IsNullOrEmpty(action)) - .Add("data", data, !string.IsNullOrEmpty(data)) - .Add("pagedata", pageData, !string.IsNullOrEmpty(pageData)) - .Add("soft_id", softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddACAOHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) - .ConfigureAwait(false); - - return (UseJsonFlag - ? await TryGetResult(response.Deserialize(), CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false) - : await TryGetResult(response, CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false) - ) as StringResponse; + throw new ArgumentException("A User-Agent is required for Cloudflare Turnstile captchas."); } + + var response = await HttpClient.PostMultipartToStringAsync + ("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "turnstile") + .Add("sitekey", siteKey) + .Add("pageurl", siteUrl) + .Add("action", action ?? string.Empty, !string.IsNullOrEmpty(action)) + .Add("data", data ?? string.Empty, !string.IsNullOrEmpty(data)) + .Add("pagedata", pageData ?? string.Empty, !string.IsNullOrEmpty(pageData)) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.CloudflareTurnstile, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.CloudflareTurnstile, + cancellationToken).ConfigureAwait(false); + } - #endregion + #endregion - #region Getting the result - internal async Task TryGetResult - (Response response, CaptchaType type, CancellationToken cancellationToken = default) - { - if (response.IsErrorCode) - throw new TaskCreationException(response.Request); + #region Getting the result + internal async Task GetResult( + Response response, CaptchaType type, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (response.IsErrorCode) + throw new TaskCreationException(response.Request); - var task = new CaptchaTask(response.Request, type); + var task = new CaptchaTask(response.Request, type); - return await TryGetResult(task, cancellationToken).ConfigureAwait(false); - } + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } - internal async Task TryGetResult - (string response, CaptchaType type, CancellationToken cancellationToken = default) - { - if (IsErrorCode(response)) - throw new TaskCreationException(response); + internal async Task GetResult( + string response, CaptchaType type, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (IsErrorCode(response)) + throw new TaskCreationException(response); - var task = new CaptchaTask(TakeSecondSlice(response), type); + var task = new CaptchaTask(TakeSecondSlice(response), type); - return await TryGetResult(task, cancellationToken).ConfigureAwait(false); - } + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } - /// - protected async override Task CheckResult - (CaptchaTask task, CancellationToken cancellationToken = default) + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + var response = await HttpClient.GetStringAsync + ("res.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", "get") + .Add("id", task.Id.ToString()) + .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), + cancellationToken); + + if (response.Contains("CAPCHA_NOT_READY")) { - var response = await httpClient.GetStringAsync - ("res.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("action", "get") - .Add("id", task.Id.ToString()) - .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), - cancellationToken); - - if (response.Contains("CAPCHA_NOT_READY")) - return default; + return null; + } - task.Completed = true; + task.Completed = true; - if (UseJsonFlag) + if (UseJsonFlag) + { + if (task.Type == CaptchaType.GeeTest) { - if (task.Type == CaptchaType.GeeTest) + var jObject = JObject.Parse(response); + var solution = jObject["request"]; + + if (solution is null) { - var jObject = JObject.Parse(response); - var solution = jObject["request"]; - - if (solution.Type == JTokenType.Object) - { - return response.Deserialize() - .Request.ToGeeTestResponse(task.Id); - } + throw new TaskSolutionException("No solution found"); } - else if (task.Type == CaptchaType.Capy) + + if (solution.Type == JTokenType.Object) { - var jObject = JObject.Parse(response); - var solution = jObject["request"]; - - if (solution.Type == JTokenType.Object) - { - return response.Deserialize() - .Request.ToCapyResponse(task.Id); - } + return response.Deserialize() + .Request.ToGeeTestResponse(task.Id) as T; } - - var tcResponse = response.Deserialize(); - - if (tcResponse.IsErrorCode) - throw new TaskSolutionException(tcResponse.Error_Text); - - return new StringResponse { Id = task.Id, Response = tcResponse.Request }; } - else + else if (task.Type == CaptchaType.Capy) { - if (IsErrorCode(response)) - throw new TaskSolutionException(response); - - response = TakeSecondSlice(response); + var jObject = JObject.Parse(response); + var solution = jObject["request"]; - switch (task.Type) + if (solution is null) + { + throw new TaskSolutionException("No solution found"); + } + + if (solution.Type == JTokenType.Object) { - case CaptchaType.GeeTest: - return response.Deserialize().ToGeeTestResponse(task.Id); + return response.Deserialize() + .Request.ToCapyResponse(task.Id) as T; + } + } - case CaptchaType.Capy: - return response.Deserialize().ToCapyResponse(task.Id); + var tcResponse = response.Deserialize(); - default: - return new StringResponse { Id = task.Id, Response = response }; - } + if (tcResponse.IsErrorCode) + { + throw new TaskSolutionException(tcResponse.Error_Text); } + + return new StringResponse { Id = task.Id, Response = tcResponse.Request } as T; } - #endregion - #region Reporting the solution - /// - public async override Task ReportSolution - (long taskId, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + if (IsErrorCode(response)) { - var action = correct ? "reportgood" : "reportbad"; + throw new TaskSolutionException(response); + } - var response = await httpClient.GetStringAsync - ("res.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("action", action) - .Add("id", taskId.ToString()) - .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), - cancellationToken); + response = TakeSecondSlice(response); - if (UseJsonFlag) - { - var tcResponse = response.Deserialize(); + return task.Type switch + { + CaptchaType.GeeTest => response.Deserialize().ToGeeTestResponse(task.Id) as T, + CaptchaType.Capy => response.Deserialize().ToCapyResponse(task.Id) as T, + _ => new StringResponse { Id = task.Id, Response = response } as T + }; + } + #endregion - if (tcResponse.IsErrorCode) - throw new TaskReportException(tcResponse.Request); - } - else - { - if (IsErrorCode(response)) - throw new TaskReportException(response); - } + #region Reporting the solution + /// + public override async Task ReportSolution( + long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + var action = correct ? "reportgood" : "reportbad"; + + var response = await HttpClient.GetStringAsync + ("res.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", action) + .Add("id", id.ToString()) + .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), + cancellationToken); + + if (UseJsonFlag) + { + var tcResponse = response.Deserialize(); + + if (tcResponse.IsErrorCode) + throw new TaskReportException(tcResponse.Request); } - #endregion + else + { + if (IsErrorCode(response)) + throw new TaskReportException(response); + } + } + #endregion - #region Proxies - /// - protected IEnumerable<(string, string)> ConvertProxy(Proxy proxy) + #region Proxies + /// + protected static IEnumerable<(string, string)> ConvertProxy(Proxy? proxy) + { + if (proxy is null) { - // TODO: Remove UserAgent and Cookies from Proxy class - if (proxy is null) - { - return []; - } + return []; + } - var proxyParams = new List<(string, string)>(); + var proxyParams = new List<(string, string)>(); - if (proxy.UserAgent is not null) - { - proxyParams.Add(("userAgent", proxy.UserAgent)); - } - - if (string.IsNullOrEmpty(proxy.Host)) - { - return proxyParams; - } - - proxyParams.AddRange( - [ - ("proxy", proxy.RequiresAuthentication - ? $"{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}" - : $"{proxy.Host}:{proxy.Port}"), - ("proxytype", proxy.Type.ToString()) - ]); + if (proxy.UserAgent is not null) + { + proxyParams.Add(("userAgent", proxy.UserAgent)); + } + if (string.IsNullOrEmpty(proxy.Host)) + { return proxyParams; } - #endregion - #region Utility methods - /// For non-json response. - protected bool IsErrorCode(string response) + proxyParams.AddRange( + [ + ("proxy", proxy.RequiresAuthentication + ? $"{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}" + : $"{proxy.Host}:{proxy.Port}"), + ("proxytype", proxy.Type.ToString()) + ]); + + return proxyParams; + } + #endregion + + #region Utility methods + /// For non-json response. + protected bool IsErrorCode(string response) + { + return !response.StartsWith("OK"); + } + + /// For non-json response. + protected static string TakeSecondSlice(string str) + { + return str.Split('|')[1]; + } + #endregion + + #region Capabilities + /// + /// The capabilities of the service. + /// + public new static CaptchaServiceCapabilities Capabilities => + CaptchaServiceCapabilities.LanguageGroup | + CaptchaServiceCapabilities.Language | + CaptchaServiceCapabilities.Phrases | + CaptchaServiceCapabilities.CaseSensitivity | + CaptchaServiceCapabilities.CharacterSets | + CaptchaServiceCapabilities.Calculations | + CaptchaServiceCapabilities.MinLength | + CaptchaServiceCapabilities.MaxLength | + CaptchaServiceCapabilities.Instructions; + + /// + private List<(string, string)> ConvertCapabilities(TextCaptchaOptions? options) + { + // If null, don't return any parameters + if (options is null) { - return !response.StartsWith("OK"); + return []; } - /// For non-json response. - protected string TakeSecondSlice(string str) + var capabilities = new List<(string, string)>(); + + if (Capabilities.HasFlag(CaptchaServiceCapabilities.LanguageGroup)) { - return str.Split('|')[1]; + capabilities.Add(("language", ((int)options.CaptchaLanguageGroup).ToString())); } - #endregion - - #region Capabilities - /// - public new CaptchaServiceCapabilities Capabilities => - CaptchaServiceCapabilities.LanguageGroup | - CaptchaServiceCapabilities.Language | - CaptchaServiceCapabilities.Phrases | - CaptchaServiceCapabilities.CaseSensitivity | - CaptchaServiceCapabilities.CharacterSets | - CaptchaServiceCapabilities.Calculations | - CaptchaServiceCapabilities.MinLength | - CaptchaServiceCapabilities.MaxLength | - CaptchaServiceCapabilities.Instructions; - - /// - protected IEnumerable<(string, string)> ConvertCapabilities(TextCaptchaOptions options) + + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Language) && + options.CaptchaLanguage != CaptchaLanguage.NotSpecified) { - // If null, don't return any parameters - if (options == null) - return new (string, string)[] { }; + capabilities.Add(("lang", options.CaptchaLanguage.ToIso6391Code())); + } - var capabilities = new List<(string, string)>(); + return capabilities; + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.LanguageGroup)) - capabilities.Add(("language", ((int)options.CaptchaLanguageGroup).ToString())); + /// + protected List<(string, string)> ConvertCapabilities(ImageCaptchaOptions? options) + { + // If null, don't return any parameters + if (options is null) + { + return []; + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Language) && options.CaptchaLanguage != CaptchaLanguage.NotSpecified) - capabilities.Add(("lang", options.CaptchaLanguage.ToISO6391Code())); + var capabilities = new List<(string, string)>(); - return capabilities; + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Phrases)) + { + capabilities.Add(("phrase", Convert.ToInt32(options.IsPhrase).ToString())); } - /// - protected IEnumerable<(string, string)> ConvertCapabilities(ImageCaptchaOptions options) + if (Capabilities.HasFlag(CaptchaServiceCapabilities.CaseSensitivity)) { - // If null, don't return any parameters - if (options == null) - return new (string, string)[] { }; - - var capabilities = new List<(string, string)>(); - - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Phrases)) - capabilities.Add(("phrase", Convert.ToInt32(options.IsPhrase).ToString())); - - if (Capabilities.HasFlag(CaptchaServiceCapabilities.CaseSensitivity)) - capabilities.Add(("regsense", Convert.ToInt32(options.CaseSensitive).ToString())); - - if (Capabilities.HasFlag(CaptchaServiceCapabilities.CharacterSets)) - capabilities.Add(("numeric", ((int)options.CharacterSet).ToString())); + capabilities.Add(("regsense", Convert.ToInt32(options.CaseSensitive).ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Calculations)) - capabilities.Add(("calc", Convert.ToInt32(options.RequiresCalculation).ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.CharacterSets)) + { + capabilities.Add(("numeric", ((int)options.CharacterSet).ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.MinLength)) - capabilities.Add(("min_len", options.MinLength.ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Calculations)) + { + capabilities.Add(("calc", Convert.ToInt32(options.RequiresCalculation).ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.MaxLength)) - capabilities.Add(("max_len", options.MaxLength.ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.MinLength)) + { + capabilities.Add(("min_len", options.MinLength.ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Instructions)) - capabilities.Add(("textinstructions", options.TextInstructions)); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.MaxLength)) + { + capabilities.Add(("max_len", options.MaxLength.ToString())); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.LanguageGroup)) - capabilities.Add(("language", ((int)options.CaptchaLanguageGroup).ToString())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Instructions)) + { + capabilities.Add(("textinstructions", options.TextInstructions)); + } - if (Capabilities.HasFlag(CaptchaServiceCapabilities.Language) && options.CaptchaLanguage != CaptchaLanguage.NotSpecified) - capabilities.Add(("lang", options.CaptchaLanguage.ToISO6391Code())); + if (Capabilities.HasFlag(CaptchaServiceCapabilities.LanguageGroup)) + { + capabilities.Add(("language", ((int)options.CaptchaLanguageGroup).ToString())); + } - return capabilities; + if (Capabilities.HasFlag(CaptchaServiceCapabilities.Language) && + options.CaptchaLanguage != CaptchaLanguage.NotSpecified) + { + capabilities.Add(("lang", options.CaptchaLanguage.ToIso6391Code())); } - #endregion + + return capabilities; } + #endregion } From ad0766709c65ca747ad4a5321f992f9672c4fd9f Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 01:25:56 +0200 Subject: [PATCH 06/67] Refactored remaining services --- .../AnyCaptchaService.cs | 35 +- .../AzCaptchaService.cs | 31 +- .../CaptchasIOService.cs | 30 +- .../RuCaptchaService.cs | 22 +- .../SolveCaptchaService.cs | 34 +- .../SolveRecaptchaService.cs | 33 +- .../TrueCaptchaService.cs | 111 +-- CaptchaSharp/Services/AntiCaptchaService.cs | 717 +++++++++--------- CaptchaSharp/Services/CapMonsterService.cs | 53 +- .../Responses/GetTaskResultResponse.cs | 2 +- CaptchaSharp/Services/CapSolverService.cs | 616 +++++++-------- .../Services/CustomAntiCaptchaService.cs | 56 +- .../Services/CustomTwoCaptchaService.cs | 2 +- CaptchaSharp/Services/TwoCaptchaService.cs | 312 ++++---- 14 files changed, 1052 insertions(+), 1002 deletions(-) diff --git a/CaptchaSharp.Services.More/AnyCaptchaService.cs b/CaptchaSharp.Services.More/AnyCaptchaService.cs index b719a38..672a618 100644 --- a/CaptchaSharp.Services.More/AnyCaptchaService.cs +++ b/CaptchaSharp.Services.More/AnyCaptchaService.cs @@ -2,27 +2,26 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More +namespace CaptchaSharp.Services.More; + +/// +/// The service provided by https://anycaptcha.com/ +/// +public class AnyCaptchaService : CustomAntiCaptchaService { /// - /// The service provided by https://anycaptcha.com/ + /// Initializes a . /// - public class AnyCaptchaService : CustomAntiCaptchaService + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public AnyCaptchaService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("https://api.anycaptcha.com"), httpClient) { - /// - /// Initializes a . - /// - /// The API key to use. - /// The to use for requests. If null, a default one will be created. - public AnyCaptchaService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("https://api.anycaptcha.com"), httpClient) - { - SupportedCaptchaTypes = - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3 | - CaptchaType.FunCaptcha | - CaptchaType.HCaptcha; - } + SupportedCaptchaTypes = + CaptchaType.ImageCaptcha | + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3 | + CaptchaType.FunCaptcha | + CaptchaType.HCaptcha; } } diff --git a/CaptchaSharp.Services.More/AzCaptchaService.cs b/CaptchaSharp.Services.More/AzCaptchaService.cs index aef85db..5f3e878 100644 --- a/CaptchaSharp.Services.More/AzCaptchaService.cs +++ b/CaptchaSharp.Services.More/AzCaptchaService.cs @@ -2,25 +2,24 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More +namespace CaptchaSharp.Services.More; + +/// +/// The service provided by https://azcaptcha.com/ +/// +public class AzCaptchaService : CustomTwoCaptchaService { /// - /// The service provided by https://azcaptcha.com/ + /// Initializes a . /// - public class AzCaptchaService : CustomTwoCaptchaService + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public AzCaptchaService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("http://azcaptcha.com"), httpClient, false) { - /// - /// Initializes a . - /// - /// The API key to use. - /// The to use for requests. If null, a default one will be created. - public AzCaptchaService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("http://azcaptcha.com"), httpClient, false) - { - SupportedCaptchaTypes = - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3; - } + SupportedCaptchaTypes = + CaptchaType.ImageCaptcha | + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3; } } diff --git a/CaptchaSharp.Services.More/CaptchasIOService.cs b/CaptchaSharp.Services.More/CaptchasIOService.cs index 65b4f6c..d70470a 100644 --- a/CaptchaSharp.Services.More/CaptchasIOService.cs +++ b/CaptchaSharp.Services.More/CaptchasIOService.cs @@ -2,20 +2,24 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More +namespace CaptchaSharp.Services.More; + +/// +/// The service provided by https://captchas.io/ +/// +public class CaptchasIOService : CustomTwoCaptchaService { - /// The service provided by https://captchas.io/ - public class CaptchasIOService : CustomTwoCaptchaService + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public CaptchasIOService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("https://api.captchas.io"), httpClient, false) { - /// Initializes a using the given and - /// . If is null, a default one will be created. - public CaptchasIOService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("https://api.captchas.io"), httpClient, false) - { - SupportedCaptchaTypes = - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3; - } + SupportedCaptchaTypes = + CaptchaType.ImageCaptcha | + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3; } } diff --git a/CaptchaSharp.Services.More/RuCaptchaService.cs b/CaptchaSharp.Services.More/RuCaptchaService.cs index d6021fe..f90e422 100644 --- a/CaptchaSharp.Services.More/RuCaptchaService.cs +++ b/CaptchaSharp.Services.More/RuCaptchaService.cs @@ -1,14 +1,18 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More +namespace CaptchaSharp.Services.More; + +/// +/// The service provided by https://rucaptcha.com/ +/// +public class RuCaptchaService : CustomTwoCaptchaService { - /// The service provided by https://rucaptcha.com/ - public class RuCaptchaService : CustomTwoCaptchaService - { - /// Initializes a using the given and - /// . If is null, a default one will be created. - public RuCaptchaService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("http://rucaptcha.com"), httpClient) { } - } + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public RuCaptchaService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("http://rucaptcha.com"), httpClient) { } } diff --git a/CaptchaSharp.Services.More/SolveCaptchaService.cs b/CaptchaSharp.Services.More/SolveCaptchaService.cs index 650dcf7..302a994 100644 --- a/CaptchaSharp.Services.More/SolveCaptchaService.cs +++ b/CaptchaSharp.Services.More/SolveCaptchaService.cs @@ -2,22 +2,26 @@ using CaptchaSharp.Enums; using System.Net.Http; -namespace CaptchaSharp.Services.More +namespace CaptchaSharp.Services.More; + +/// +/// The service provided by https://solvecaptcha.com/ +/// +public class SolveCaptchaService : CustomTwoCaptchaService { - /// The service provided by https://solvecaptcha.com/ - public class SolveCaptchaService : CustomTwoCaptchaService + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public SolveCaptchaService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("http://api.solvecaptcha.com"), httpClient, false) { - /// Initializes a using the given and - /// . If is null, a default one will be created. - public SolveCaptchaService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("http://api.solvecaptcha.com"), httpClient, false) - { - SupportedCaptchaTypes = - CaptchaType.TextCaptcha | - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.FunCaptcha | - CaptchaType.KeyCaptcha; - } + SupportedCaptchaTypes = + CaptchaType.TextCaptcha | + CaptchaType.ImageCaptcha | + CaptchaType.ReCaptchaV2 | + CaptchaType.FunCaptcha | + CaptchaType.KeyCaptcha; } } diff --git a/CaptchaSharp.Services.More/SolveRecaptchaService.cs b/CaptchaSharp.Services.More/SolveRecaptchaService.cs index 5d2cb82..fd15940 100644 --- a/CaptchaSharp.Services.More/SolveRecaptchaService.cs +++ b/CaptchaSharp.Services.More/SolveRecaptchaService.cs @@ -10,11 +10,16 @@ namespace CaptchaSharp.Services.More; -/// The service provided by https://solverecaptcha.com/ +/// +/// The service provided by https://solverecaptcha.com/ +/// public class SolveRecaptchaService : CustomTwoCaptchaService { - /// Initializes a using the given and - /// . If is null, a default one will be created. + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. public SolveRecaptchaService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("http://api.solverecaptcha.com"), httpClient) { @@ -80,7 +85,7 @@ public override async Task SolveRecaptchaV3Async /// public override Task ReportSolution - (long taskId, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } @@ -90,20 +95,12 @@ private StringPairCollection GetAuthPair() private void ThrowException(string response) { - switch (response) + throw response switch { - case "ERROR_API_KEY_NOT_FOUND": - case "ERROR_ACCESS_DENIED": - throw new BadAuthenticationException(response); - - case "ERROR_NO_AVAILABLE_THREADS": - throw new TaskCreationException(response); - - case "ERROR_CAPTCHA_UNSOLVABLE": - throw new TaskSolutionException(response); - - default: - throw new Exception(response); - } + "ERROR_API_KEY_NOT_FOUND" or "ERROR_ACCESS_DENIED" => new BadAuthenticationException(response), + "ERROR_NO_AVAILABLE_THREADS" => new TaskCreationException(response), + "ERROR_CAPTCHA_UNSOLVABLE" => new TaskSolutionException(response), + _ => new Exception(response) + }; } } diff --git a/CaptchaSharp.Services.More/TrueCaptchaService.cs b/CaptchaSharp.Services.More/TrueCaptchaService.cs index 8c08b6d..c42c9bc 100644 --- a/CaptchaSharp.Services.More/TrueCaptchaService.cs +++ b/CaptchaSharp.Services.More/TrueCaptchaService.cs @@ -8,68 +8,87 @@ using System.Threading.Tasks; using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services.More +namespace CaptchaSharp.Services.More; + +/// +/// The service provided by https://apitruecaptcha.org/ +/// +public class TrueCaptchaService : CaptchaService { - /// The service provided by https://apitruecaptcha.org/ - public class TrueCaptchaService : CaptchaService - { - /// Your user id. - public string UserId { get; set; } + /// + /// Your user id. + /// + public string UserId { get; set; } - /// Your secret api key. - public string ApiKey { get; set; } + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } - /// The default used for requests. - protected HttpClient httpClient; + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; - /// Initializes a using the given , and - /// . If is null, a default one will be created. - public TrueCaptchaService(string userId, string apiKey, HttpClient httpClient = null) - { - UserId = userId; - ApiKey = apiKey; + /// + /// Initializes a . + /// + /// Your user id. + /// Your secret api key. + /// The to use for requests. If null, a default one will be created. + public TrueCaptchaService(string userId, string apiKey, HttpClient? httpClient = null) + { + UserId = userId; + ApiKey = apiKey; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("https://api.apitruecaptcha.org/"); - this.httpClient.Timeout = Timeout; - } + this._httpClient = httpClient ?? new HttpClient(); + this._httpClient.BaseAddress = new Uri("https://api.apitruecaptcha.org/"); + this._httpClient.Timeout = Timeout; + } - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - var response = await httpClient.GetStringAsync - ("one/getbalance", + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await _httpClient.GetStringAsync + ("one/getbalance", new StringPairCollection() - .Add("username", UserId) - .Add("apikey", ApiKey), + .Add("username", UserId) + .Add("apikey", ApiKey), cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) - return balance; - - else - throw new BadAuthenticationException(response); + if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out var balance)) + { + return balance; } - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var content = new JObject(); - content.Add("userid", UserId); - content.Add("apikey", ApiKey); - content.Add("data", base64); + throw new BadAuthenticationException(response); + } - var response = await httpClient.PostJsonToStringAsync - ("one/gettext", + /// + public override async Task SolveImageCaptchaAsync + (string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + var content = new JObject(); + content.Add("userid", UserId); + content.Add("apikey", ApiKey); + content.Add("data", base64); + + var response = await _httpClient.PostJsonToStringAsync + ("one/gettext", content, camelizeKeys: false, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - var jObject = JObject.Parse(response); - return new StringResponse { Id = 0, Response = jObject["result"].ToString() }; + var jObject = JObject.Parse(response); + var result = jObject["result"]; + + if (result is null) + { + throw new TaskSolutionException(response); } + + return new StringResponse { Id = 0, Response = result.ToString() }; } } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index a626b10..032a987 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -14,457 +14,478 @@ using System.Threading.Tasks; using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://anti-captcha.com/ +/// +public class AntiCaptchaService : CaptchaService { - /// The service provided by https://anti-captcha.com/ - public class AntiCaptchaService : CaptchaService + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /// + /// The ID of the software developer. + /// + private const int _softId = 934; + + /// + /// Initializes a . + /// + /// Your secret api key. + /// The to use for requests. If null, a default one will be created. + public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) { - /// Your secret api key. - public string ApiKey { get; set; } + ApiKey = apiKey; + this._httpClient = httpClient ?? new HttpClient(); + this._httpClient.BaseAddress = new Uri("https://api.anti-captcha.com"); + } - /// The default used for requests. - protected HttpClient httpClient; + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostJsonToStringAsync( + "getBalance", new Request { ClientKey = ApiKey }, + cancellationToken: cancellationToken).ConfigureAwait(false); - /// The ID of the software developer. - private readonly int softId = 934; + var balanceResponse = response.Deserialize(); - /// Initializes a using the given and - /// . If is null, a default one will be created. - public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) + if (balanceResponse.IsError) { - ApiKey = apiKey; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("https://api.anti-captcha.com"); + throw new BadAuthenticationException($"{balanceResponse.ErrorCode}: {balanceResponse.ErrorDescription}"); } - #region Getting the Balance - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - var response = await httpClient.PostJsonToStringAsync - ("getBalance", - new Request() { ClientKey = ApiKey }, - cancellationToken: cancellationToken) - .ConfigureAwait(false); - - var balanceResponse = response.Deserialize(); - - if (balanceResponse.IsError) - throw new BadAuthenticationException($"{balanceResponse.ErrorCode}: {balanceResponse.ErrorDescription}"); - - return new decimal(balanceResponse.Balance); - } - #endregion + return new decimal(balanceResponse.Balance); + } + #endregion - #region Solve Methods - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostJsonToStringAsync - ("createTask", + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostJsonToStringAsync( + "createTask", AddImageCapabilities( new CaptchaTaskRequest { ClientKey = ApiKey, - SoftId = softId, + SoftId = _softId, Task = new ImageCaptchaTask { Body = base64 } }, options), cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.ImageCaptcha, cancellationToken) - as StringResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.ImageCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var content = CreateTaskRequest(); + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); - if (enterprise) + if (enterprise) + { + if (proxy != null) { - if (proxy != null) + content.Task = new RecaptchaV2EnterpriseTask { - content.Task = new RecaptchaV2EnterpriseTask - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - EnterprisePayload = new Dictionary() - }.SetProxy(proxy); - - if (!string.IsNullOrEmpty(dataS)) - ((RecaptchaV2EnterpriseTask)content.Task).EnterprisePayload.Add("s", dataS); - } - else + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + EnterprisePayload = new Dictionary() + }.SetProxy(proxy); + + if (!string.IsNullOrEmpty(dataS)) { - content.Task = new RecaptchaV2EnterpriseTaskProxyless - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - EnterprisePayload = new Dictionary() - }; - - if (!string.IsNullOrEmpty(dataS)) - ((RecaptchaV2EnterpriseTaskProxyless)content.Task).EnterprisePayload.Add("s", dataS); + ((RecaptchaV2EnterpriseTask)content.Task).EnterprisePayload.Add("s", dataS); } } else { - if (proxy != null) + content.Task = new RecaptchaV2EnterpriseTaskProxyless { - content.Task = new RecaptchaV2Task - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - IsInvisible = invisible - }.SetProxy(proxy); - } - else + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + EnterprisePayload = new Dictionary() + }; + + if (!string.IsNullOrEmpty(dataS)) { - content.Task = new RecaptchaV2TaskProxyless - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - IsInvisible = invisible - }; + ((RecaptchaV2EnterpriseTaskProxyless)content.Task).EnterprisePayload.Add("s", dataS); } } + } + else + { + if (proxy != null) + { + content.Task = new RecaptchaV2Task + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + IsInvisible = invisible + }.SetProxy(proxy); + } + else + { + content.Task = new RecaptchaV2TaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + IsInvisible = invisible + }; + } + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync + ("createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV2, cancellationToken) - as StringResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.ReCaptchaV2, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + if (proxy != null) { - if (proxy != null) - throw new NotSupportedException("Proxies are not supported"); - - if (minScore != 0.3F && minScore != 0.7F && minScore != 0.9F) - throw new NotSupportedException("Only min scores of 0.3, 0.7 and 0.9 are supported"); + throw new NotSupportedException("Proxies are not supported"); + } - var content = CreateTaskRequest(); + if (minScore != 0.3f && minScore != 0.7f && minScore != 0.9f) + { + throw new NotSupportedException("Only min scores of 0.3, 0.7 and 0.9 are supported"); + } - content.Task = new RecaptchaV3TaskProxyless - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - PageAction = action, - MinScore = minScore, - IsEnterprise = enterprise - }; + var content = CreateTaskRequest(); - var response = await httpClient.PostJsonToStringAsync - ("createTask", + content.Task = new RecaptchaV3TaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + PageAction = action, + MinScore = minScore, + IsEnterprise = enterprise + }; + + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV3, cancellationToken) - as StringResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.ReCaptchaV3, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveFuncaptchaAsync - (string publicKey, string serviceUrl, string siteUrl, bool noJS = false, Proxy proxy = null, - CancellationToken cancellationToken = default) + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + if (noJs) { - if (noJS) - throw new NotSupportedException("This service does not support no js solving"); + throw new NotSupportedException("This service does not support no js solving"); + } - var content = CreateTaskRequest(); + var content = CreateTaskRequest(); - if (proxy != null) + if (proxy != null) + { + content.Task = new FunCaptchaTask { - content.Task = new FunCaptchaTask - { - WebsitePublicKey = publicKey, - WebsiteURL = siteUrl, - FuncaptchaApiJSSubdomain = serviceUrl - }.SetProxy(proxy); - } - else + WebsitePublicKey = publicKey, + WebsiteURL = siteUrl, + FuncaptchaApiJSSubdomain = serviceUrl + }.SetProxy(proxy); + } + else + { + content.Task = new FunCaptchaTaskProxyless { - content.Task = new FunCaptchaTaskProxyless - { - WebsitePublicKey = publicKey, - WebsiteURL = siteUrl, - FuncaptchaApiJSSubdomain = serviceUrl - }; - } + WebsitePublicKey = publicKey, + WebsiteURL = siteUrl, + FuncaptchaApiJSSubdomain = serviceUrl + }; + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.FunCaptcha, cancellationToken) - as StringResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.FunCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveHCaptchaAsync - (string siteKey, string siteUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - var content = CreateTaskRequest(); + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); - if (proxy != null) + if (proxy != null) + { + content.Task = new HCaptchaTask { - content.Task = new HCaptchaTask - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - }.SetProxy(proxy); - } - else + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + }.SetProxy(proxy); + } + else + { + content.Task = new HCaptchaTaskProxyless { - content.Task = new HCaptchaTaskProxyless - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - }; - } + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + }; + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.HCaptcha, cancellationToken) - as StringResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.HCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveGeeTestAsync - (string gt, string challenge, string apiServer, string siteUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) - { - var content = CreateTaskRequest(); + /// + public override async Task SolveGeeTestAsync( + string gt, string challenge, string apiServer, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); - if (proxy != null) + if (proxy != null) + { + content.Task = new GeeTestTask { - content.Task = new GeeTestTask - { - WebsiteURL = siteUrl, - Gt = gt, - Challenge = challenge, - GeetestApiServerSubdomain = apiServer - }.SetProxy(proxy); - } - else + WebsiteURL = siteUrl, + Gt = gt, + Challenge = challenge, + GeetestApiServerSubdomain = apiServer + }.SetProxy(proxy); + } + else + { + content.Task = new GeeTestTaskProxyless { - content.Task = new GeeTestTaskProxyless - { - WebsiteURL = siteUrl, - Gt = gt, - Challenge = challenge, - GeetestApiServerSubdomain = apiServer - }; - } + WebsiteURL = siteUrl, + Gt = gt, + Challenge = challenge, + GeetestApiServerSubdomain = apiServer + }; + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync + ("createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.GeeTest, cancellationToken) - as GeeTestResponse; - } - #endregion + return await GetResult( + response.Deserialize(), CaptchaType.GeeTest, + cancellationToken).ConfigureAwait(false); + } + #endregion - #region Getting the result - private async Task TryGetResult - (TaskCreationResponse response, CaptchaType type, CancellationToken cancellationToken = default) + #region Getting the result + private async Task GetResult( + TaskCreationResponse response, CaptchaType type, + CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (response.IsError) { - if (response.IsError) - throw new TaskCreationException($"{response.ErrorCode}: {response.ErrorDescription}"); - - var task = new CaptchaTask(response.TaskId, type); - - return await TryGetResult(task, cancellationToken).ConfigureAwait(false); + throw new TaskCreationException($"{response.ErrorCode}: {response.ErrorDescription}"); } - /// - protected async override Task CheckResult - (CaptchaTask task, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostJsonToStringAsync - ("getTaskResult", - new GetTaskResultRequest { ClientKey = ApiKey, TaskId = (int)task.Id }, - cancellationToken: cancellationToken).ConfigureAwait(false); + var task = new CaptchaTask(response.TaskId, type); - var result = response.Deserialize(); + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } - if (!result.IsReady) - return default; + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + var response = await _httpClient.PostJsonToStringAsync( + "getTaskResult", + new GetTaskResultRequest { ClientKey = ApiKey, TaskId = (int)task.Id }, + cancellationToken: cancellationToken).ConfigureAwait(false); - task.Completed = true; + var result = response.Deserialize(); - if (result.IsError) - throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); + if (!result.IsReady) + { + return null; + } - var jObject = JObject.Parse(response); - var solution = jObject["solution"]; + task.Completed = true; - switch (task.Type) - { - case CaptchaType.ReCaptchaV2: - case CaptchaType.ReCaptchaV3: - case CaptchaType.HCaptcha: - result.Solution = solution.ToObject(); - break; - - case CaptchaType.FunCaptcha: - result.Solution = solution.ToObject(); - break; - - case CaptchaType.ImageCaptcha: - result.Solution = solution.ToObject(); - break; - - case CaptchaType.GeeTest: - result.Solution = solution.ToObject(); - break; - - default: - throw new NotSupportedException(); - } + if (result.IsError) + { + throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); + } - return result.Solution.ToCaptchaResponse(task.Id); + var jObject = JObject.Parse(response); + var solution = jObject["solution"]; + + if (solution is null) + { + throw new TaskSolutionException(response); } - #endregion - #region Reporting the solution - /// - public async override Task ReportSolution - (long taskId, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + result.Solution = task.Type switch { - if (correct) - throw new NotSupportedException("This service doesn't allow reporting of good solutions"); + CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3 or CaptchaType.HCaptcha => + solution.ToObject()! as Solution, + CaptchaType.FunCaptcha => solution.ToObject()!, + CaptchaType.ImageCaptcha => solution.ToObject(), + CaptchaType.GeeTest => solution.ToObject(), + _ => throw new NotSupportedException($"The {task.Type} captcha type is not supported") + } ?? throw new TaskSolutionException(response); + + return result.Solution.ToCaptchaResponse(task.Id) as T; + } + #endregion - string response; - ReportIncorrectCaptchaResponse incResponse; + #region Reporting the solution + /// + public override async Task ReportSolution( + long id, CaptchaType type, bool correct = false, + CancellationToken cancellationToken = default) + { + if (correct) + { + throw new NotSupportedException("This service doesn't allow reporting of good solutions"); + } - switch (type) - { - case CaptchaType.ImageCaptcha: - response = await httpClient.PostJsonToStringAsync - ("reportIncorrectImageCaptcha", - new ReportIncorrectCaptchaRequest() { ClientKey = ApiKey, TaskId = taskId }, + string response; + ReportIncorrectCaptchaResponse incResponse; + + switch (type) + { + case CaptchaType.ImageCaptcha: + response = await _httpClient.PostJsonToStringAsync( + "reportIncorrectImageCaptcha", + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); - incResponse = response.Deserialize(); - break; + incResponse = response.Deserialize(); + break; - case CaptchaType.ReCaptchaV2: - case CaptchaType.ReCaptchaV3: - response = await httpClient.PostJsonToStringAsync - ("reportIncorrectRecaptcha", - new ReportIncorrectCaptchaRequest() { ClientKey = ApiKey, TaskId = taskId }, + case CaptchaType.ReCaptchaV2: + case CaptchaType.ReCaptchaV3: + response = await _httpClient.PostJsonToStringAsync( + "reportIncorrectRecaptcha", + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); - incResponse = response.Deserialize(); - break; - - default: - throw new NotSupportedException("Reporting is not supported for this captcha type"); - } + incResponse = response.Deserialize(); + break; - if (incResponse.NotFoundOrExpired) - throw new TaskReportException("Captcha not found or expired"); + default: + throw new NotSupportedException("Reporting is not supported for this captcha type"); } - #endregion - #region Private Methods - private CaptchaTaskRequest CreateTaskRequest() + if (incResponse.NotFoundOrExpired) { - return new CaptchaTaskRequest - { - ClientKey = ApiKey, - SoftId = softId - }; + throw new TaskReportException("Captcha not found or expired"); } - #endregion - - #region Capabilities - /// - public override CaptchaServiceCapabilities Capabilities => - CaptchaServiceCapabilities.Language | - CaptchaServiceCapabilities.Phrases | - CaptchaServiceCapabilities.CaseSensitivity | - CaptchaServiceCapabilities.CharacterSets | - CaptchaServiceCapabilities.Calculations | - CaptchaServiceCapabilities.MinLength | - CaptchaServiceCapabilities.MaxLength | - CaptchaServiceCapabilities.Instructions; - - private CaptchaTaskRequest AddImageCapabilities(CaptchaTaskRequest request, ImageCaptchaOptions options) + } + #endregion + + #region Private Methods + private CaptchaTaskRequest CreateTaskRequest() + { + return new CaptchaTaskRequest { - if (options == null) - return request; + ClientKey = ApiKey, + SoftId = _softId + }; + } + #endregion + + #region Capabilities + /// + public override CaptchaServiceCapabilities Capabilities => + CaptchaServiceCapabilities.Language | + CaptchaServiceCapabilities.Phrases | + CaptchaServiceCapabilities.CaseSensitivity | + CaptchaServiceCapabilities.CharacterSets | + CaptchaServiceCapabilities.Calculations | + CaptchaServiceCapabilities.MinLength | + CaptchaServiceCapabilities.MaxLength | + CaptchaServiceCapabilities.Instructions; + + private static CaptchaTaskRequest AddImageCapabilities(CaptchaTaskRequest request, ImageCaptchaOptions? options) + { + if (options == null) + { + return request; + } - var task = request.Task as ImageCaptchaTask; + if (request.Task is not ImageCaptchaTask task) + { + throw new NotSupportedException( + "Image options are only supported for image captchas"); + } - task.Phrase = options.IsPhrase; - task.Case = options.CaseSensitive; - - switch (options.CharacterSet) - { - case CharacterSet.OnlyNumbers: - task.Numeric = 1; - break; + task.Phrase = options.IsPhrase; + task.Case = options.CaseSensitive; - case CharacterSet.OnlyLetters: - task.Numeric = 2; - break; + task.Numeric = options.CharacterSet switch + { + CharacterSet.OnlyNumbers => 1, + CharacterSet.OnlyLetters => 2, + _ => 0 + }; - default: - task.Numeric = 0; - break; - } + task.Math = options.RequiresCalculation; + task.MinLength = options.MinLength; + task.MaxLength = options.MaxLength; + task.Comment = options.TextInstructions; - task.Math = options.RequiresCalculation; - task.MinLength = options.MinLength; - task.MaxLength = options.MaxLength; - task.Comment = options.TextInstructions; - - switch (options.CaptchaLanguage) - { - case CaptchaLanguage.NotSpecified: - case CaptchaLanguage.English: - request.LanguagePool = "en"; - break; - - case CaptchaLanguage.Russian: - case CaptchaLanguage.Ukrainian: - case CaptchaLanguage.Kazakh: - case CaptchaLanguage.Belorussian: - request.LanguagePool = "rn"; - break; - - default: - throw new NotSupportedException($"The {options.CaptchaLanguage} language is not supported"); - } + request.LanguagePool = options.CaptchaLanguage switch + { + CaptchaLanguage.NotSpecified or CaptchaLanguage.English => "en", + CaptchaLanguage.Russian or CaptchaLanguage.Ukrainian or CaptchaLanguage.Kazakh + or CaptchaLanguage.Belorussian => "rn", + _ => throw new NotSupportedException($"The {options.CaptchaLanguage} language is not supported") + }; - return request; - } - #endregion + return request; } + #endregion } diff --git a/CaptchaSharp/Services/CapMonsterService.cs b/CaptchaSharp/Services/CapMonsterService.cs index e4d02bc..f824ae8 100644 --- a/CaptchaSharp/Services/CapMonsterService.cs +++ b/CaptchaSharp/Services/CapMonsterService.cs @@ -6,44 +6,43 @@ using System.Threading.Tasks; using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by the CapMonster OCR application by ZennoLab. +/// +public class CapMonsterService : CustomTwoCaptchaService { - /// - /// The service provided by the CapMonster OCR application by ZennoLab. - /// - public class CapMonsterService : CustomTwoCaptchaService + /// Initializes a . + /// The API key to use. + /// The base URI of the service. + /// The to use for requests. If null, a default one will be created. + public CapMonsterService(string apiKey, Uri baseUri, HttpClient? httpClient = null) + : base(apiKey, baseUri, httpClient) { - /// Initializes a . - /// The API key to use. - /// The base URI of the service. - /// The to use for requests. If null, a default one will be created. - public CapMonsterService(string apiKey, Uri baseUri, HttpClient? httpClient = null) - : base(apiKey, baseUri, httpClient) - { - SupportedCaptchaTypes = CaptchaType.ImageCaptcha | CaptchaType.ReCaptchaV2 | CaptchaType.ReCaptchaV3; - } + SupportedCaptchaTypes = CaptchaType.ImageCaptcha | CaptchaType.ReCaptchaV2 | CaptchaType.ReCaptchaV3; + } - /// - public override async Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + /// + public override async Task SolveImageCaptchaAsync + (string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + { + if (!base64.StartsWith("base64,")) { - if (!base64.StartsWith("base64,")) - { - base64 = "base64," + base64; - } + base64 = "base64," + base64; + } - var response = await HttpClient.PostToStringAsync - ("in.php", + var response = await HttpClient.PostToStringAsync + ("in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "base64") .Add("body", base64) .Add(ConvertCapabilities(options)), cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await GetResult( - response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false); - } + return await GetResult( + response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false); } } diff --git a/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs b/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs index d3e9a99..cdb5050 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs @@ -6,7 +6,7 @@ internal class GetTaskResultResponse : Response { public string Status { get; set; } public double Cost { get; set; } - public Solution Solution { get; set; } + public required Solution Solution { get; set; } public string Ip { get; set; } public double CreateTime { get; set; } public double EndTime { get; set; } diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index 941ce0e..eefaa86 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -13,404 +13,426 @@ using System.Threading.Tasks; using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://capsolver.com/ +/// +public class CapSolverService : CaptchaService { - /// The service provided by https://capsolver.com/ - public class CapSolverService : CaptchaService + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /// + /// The ID of the app. + /// + private const string _appId = "FE552FC0-8A06-4B44-BD30-5B9DDA2A4194"; + + /// + /// Initializes a . + /// + /// Your secret api key. + /// The to use for requests. If null, a default one will be created. + public CapSolverService(string apiKey, HttpClient? httpClient = null) { - /// Your secret api key. - public string ApiKey { get; set; } + ApiKey = apiKey; + this._httpClient = httpClient ?? new HttpClient(); + this._httpClient.BaseAddress = new Uri("https://api.capsolver.com"); + } - /// The default used for requests. - protected HttpClient httpClient; + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostJsonToStringAsync( + "getBalance", + new Request { ClientKey = ApiKey }, + cancellationToken: cancellationToken) + .ConfigureAwait(false); - /// The ID of the app. - private readonly string appId = "FE552FC0-8A06-4B44-BD30-5B9DDA2A4194"; + var balanceResponse = response.Deserialize(); - /// Initializes a using the given and - /// . If is null, a default one will be created. - public CapSolverService(string apiKey, HttpClient httpClient = null) + if (balanceResponse.IsError) { - ApiKey = apiKey; - this.httpClient = httpClient ?? new HttpClient(); - this.httpClient.BaseAddress = new Uri("https://api.capsolver.com"); + throw new BadAuthenticationException($"{balanceResponse.ErrorCode}: {balanceResponse.ErrorDescription}"); } - #region Getting the Balance - /// - public async override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - var response = await httpClient.PostJsonToStringAsync - ("getBalance", - new Request() { ClientKey = ApiKey }, - cancellationToken: cancellationToken) - .ConfigureAwait(false); - - var balanceResponse = response.Deserialize(); - - if (balanceResponse.IsError) - throw new BadAuthenticationException($"{balanceResponse.ErrorCode}: {balanceResponse.ErrorDescription}"); - - return new decimal(balanceResponse.Balance); - } - #endregion + return new decimal(balanceResponse.Balance); + } + #endregion - #region Solve Methods - /// - public async override Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions options = null, CancellationToken cancellationToken = default) - { - var response = await httpClient.PostJsonToStringAsync - ("createTask", + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostJsonToStringAsync( + "createTask", new CaptchaTaskRequest { ClientKey = ApiKey, - AppId = appId, + AppId = _appId, Task = new ImageCaptchaTask { Body = base64 } }, cancellationToken: cancellationToken) - .ConfigureAwait(false); - - // The image captcha task immediately returns the solution - var result = response.Deserialize(); - - if (result.IsError) - throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); + .ConfigureAwait(false); - var jObject = JObject.Parse(response); - var solution = jObject["solution"]; - var taskId = jObject["taskId"].Value(); + // The image captcha task immediately returns the solution + var result = response.Deserialize(); - result.Solution = solution.ToObject(); - - return result.Solution.ToCaptchaResponse(taskId) as StringResponse; + if (result.IsError) + { + throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); } - /// - public async override Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy proxy = null, CancellationToken cancellationToken = default) - { - var content = CreateTaskRequest(); + var jObject = JObject.Parse(response); + var solution = jObject["solution"]; + var taskId = jObject["taskId"]!.Value()!; - if (enterprise) - { - if (proxy != null) - { - content.Task = new RecaptchaV2EnterpriseTask - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - EnterprisePayload = dataS - }.SetProxy(proxy); - } - else - { - content.Task = new RecaptchaV2EnterpriseTaskProxyless - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - EnterprisePayload = dataS - }; - } - } - else - { - if (proxy != null) - { - content.Task = new RecaptchaV2Task - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - IsInvisible = invisible, - RecaptchaDataSValue = dataS - }.SetProxy(proxy); - } - else - { - content.Task = new RecaptchaV2TaskProxyless - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - IsInvisible = invisible, - RecaptchaDataSValue = dataS - }; - } - } + result.Solution = solution!.ToObject()!; - var response = await httpClient.PostJsonToStringAsync - ("createTask", - content, - cancellationToken: cancellationToken) - .ConfigureAwait(false); + return (result.Solution.ToCaptchaResponse(taskId) as StringResponse)!; + } - return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV2, cancellationToken) - as StringResponse; - } + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); - /// - public async override Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy proxy = null, CancellationToken cancellationToken = default) + if (enterprise) { - var content = CreateTaskRequest(); - - if (proxy is null) + if (proxy is not null) { - content.Task = new RecaptchaV3TaskProxyless + content.Task = new RecaptchaV2EnterpriseTask { WebsiteKey = siteKey, WebsiteURL = siteUrl, - PageAction = action, - MinScore = minScore, - IsEnterprise = enterprise - }; + EnterprisePayload = dataS + }.SetProxy(proxy); } else { - content.Task = new RecaptchaV3Task + content.Task = new RecaptchaV2EnterpriseTaskProxyless { WebsiteKey = siteKey, WebsiteURL = siteUrl, - PageAction = action, - MinScore = minScore, - IsEnterprise = enterprise - }.SetProxy(proxy); + EnterprisePayload = dataS + }; } - - var response = await httpClient.PostJsonToStringAsync - ("createTask", - content, - cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await TryGetResult(response.Deserialize(), CaptchaType.ReCaptchaV3, cancellationToken) - as StringResponse; } - - /// - public async override Task SolveFuncaptchaAsync - (string publicKey, string serviceUrl, string siteUrl, bool noJS = false, Proxy proxy = null, - CancellationToken cancellationToken = default) + else { - if (noJS) - throw new NotSupportedException("This service does not support no js solving"); - - var content = CreateTaskRequest(); - - if (proxy != null) + if (proxy is not null) { - content.Task = new FunCaptchaTask + content.Task = new RecaptchaV2Task { - WebsitePublicKey = publicKey, + WebsiteKey = siteKey, WebsiteURL = siteUrl, - FuncaptchaApiJSSubdomain = serviceUrl + IsInvisible = invisible, + RecaptchaDataSValue = dataS }.SetProxy(proxy); } else { - content.Task = new FunCaptchaTaskProxyless + content.Task = new RecaptchaV2TaskProxyless { - WebsitePublicKey = publicKey, + WebsiteKey = siteKey, WebsiteURL = siteUrl, - FuncaptchaApiJSSubdomain = serviceUrl + IsInvisible = invisible, + RecaptchaDataSValue = dataS }; } + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.FunCaptcha, cancellationToken) - as StringResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.ReCaptchaV2, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveHCaptchaAsync - (string siteKey, string siteUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - var content = CreateTaskRequest(); + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); - if (proxy != null) + if (proxy is null) + { + content.Task = new RecaptchaV3TaskProxyless { - content.Task = new HCaptchaTask - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - }.SetProxy(proxy); - } - else + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + PageAction = action, + MinScore = minScore, + IsEnterprise = enterprise + }; + } + else + { + content.Task = new RecaptchaV3Task { - content.Task = new HCaptchaTaskProxyless - { - WebsiteKey = siteKey, - WebsiteURL = siteUrl, - }; - } + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + PageAction = action, + MinScore = minScore, + IsEnterprise = enterprise + }.SetProxy(proxy); + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.HCaptcha, cancellationToken) - as StringResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.ReCaptchaV3, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveGeeTestAsync - (string gt, string challenge, string apiServer, string siteUrl, Proxy proxy = null, - CancellationToken cancellationToken = default) + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + if (noJs) { - var content = CreateTaskRequest(); + throw new NotSupportedException("This service does not support no js solving"); + } + + var content = CreateTaskRequest(); - if (proxy != null) + if (proxy is not null) + { + content.Task = new FunCaptchaTask { - content.Task = new GeeTestTask - { - WebsiteURL = siteUrl, - Gt = gt, - Challenge = challenge, - GeetestApiServerSubdomain = apiServer - }.SetProxy(proxy); - } - else + WebsitePublicKey = publicKey, + WebsiteURL = siteUrl, + FuncaptchaApiJSSubdomain = serviceUrl + }.SetProxy(proxy); + } + else + { + content.Task = new FunCaptchaTaskProxyless { - content.Task = new GeeTestTaskProxyless - { - WebsiteURL = siteUrl, - Gt = gt, - Challenge = challenge, - GeetestApiServerSubdomain = apiServer, - Version = 3 - }; - } + WebsitePublicKey = publicKey, + WebsiteURL = siteUrl, + FuncaptchaApiJSSubdomain = serviceUrl + }; + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.GeeTest, cancellationToken) - as GeeTestResponse; - } + return await GetResult( + response.Deserialize(), CaptchaType.FunCaptcha, + cancellationToken).ConfigureAwait(false); + } - /// - public async override Task SolveDataDomeAsync - (string siteUrl, string captchaUrl, Proxy proxy = null, CancellationToken cancellationToken = default) - { - var content = CreateTaskRequest(); + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); - if (proxy is null) + if (proxy is not null) + { + content.Task = new HCaptchaTask { - throw new NotSupportedException("A proxy is required to solve a DataDome captcha"); - } + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + }.SetProxy(proxy); + } + else + { + content.Task = new HCaptchaTaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + }; + } - content.Task = new DataDomeTask + var response = await _httpClient.PostJsonToStringAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response.Deserialize(), CaptchaType.HCaptcha, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveGeeTestAsync( + string gt, string challenge, string apiServer, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (proxy is not null) + { + content.Task = new GeeTestTask { WebsiteURL = siteUrl, - CaptchaURL = captchaUrl + Gt = gt, + Challenge = challenge, + GeetestApiServerSubdomain = apiServer }.SetProxy(proxy); + } + else + { + content.Task = new GeeTestTaskProxyless + { + WebsiteURL = siteUrl, + Gt = gt, + Challenge = challenge, + GeetestApiServerSubdomain = apiServer, + Version = 3 + }; + } - var response = await httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(false); - return await TryGetResult(response.Deserialize(), CaptchaType.DataDome, cancellationToken) - as StringResponse; + return await GetResult( + response.Deserialize(), CaptchaType.GeeTest, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveDataDomeAsync( + string siteUrl, string captchaUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (proxy is null) + { + throw new NotSupportedException("A proxy is required to solve a DataDome captcha"); } - #endregion - #region Getting the result - private async Task TryGetResult - (TaskCreationResponse response, CaptchaType type, CancellationToken cancellationToken = default) + content.Task = new DataDomeTask { - if (response.IsError) - throw new TaskCreationException($"{response.ErrorCode}: {response.ErrorDescription}"); + WebsiteURL = siteUrl, + CaptchaURL = captchaUrl + }.SetProxy(proxy); - var task = new CaptchaTask(response.TaskId, type); + var response = await _httpClient.PostJsonToStringAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await TryGetResult(task, cancellationToken).ConfigureAwait(false); - } + return await GetResult( + response.Deserialize(), CaptchaType.DataDome, + cancellationToken).ConfigureAwait(false); + } + #endregion - /// - protected async override Task CheckResult - (CaptchaTask task, CancellationToken cancellationToken = default) + #region Getting the result + private async Task GetResult( + TaskCreationResponse response, CaptchaType type, + CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (response.IsError) { - var response = await httpClient.PostJsonToStringAsync - ("getTaskResult", - new GetTaskResultRequest() { ClientKey = ApiKey, TaskId = task.IdString }, - cancellationToken: cancellationToken).ConfigureAwait(false); + throw new TaskCreationException($"{response.ErrorCode}: {response.ErrorDescription}"); + } - var result = response.Deserialize(); + var task = new CaptchaTask(response.TaskId, type); - if (!result.IsReady) - return default; + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } - task.Completed = true; + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + var response = await _httpClient.PostJsonToStringAsync( + "getTaskResult", + new GetTaskResultRequest { ClientKey = ApiKey, TaskId = task.IdString }, + cancellationToken: cancellationToken).ConfigureAwait(false); - if (result.IsError) - throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); + var result = response.Deserialize(); - var jObject = JObject.Parse(response); - var solution = jObject["solution"]; + if (!result.IsReady) + { + return null; + } - switch (task.Type) - { - case CaptchaType.ReCaptchaV2: - case CaptchaType.ReCaptchaV3: - case CaptchaType.HCaptcha: - result.Solution = solution.ToObject(); - break; - - case CaptchaType.FunCaptcha: - result.Solution = solution.ToObject(); - break; - - case CaptchaType.ImageCaptcha: - result.Solution = solution.ToObject(); - break; - - case CaptchaType.GeeTest: - result.Solution = solution.ToObject(); - break; - - case CaptchaType.DataDome: - result.Solution = solution.ToObject(); - break; - - default: - throw new NotSupportedException(); - } + task.Completed = true; - return result.Solution.ToCaptchaResponse(task.IdString); + if (result.IsError) + { + throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); } - #endregion - #region Private Methods - private CaptchaTaskRequest CreateTaskRequest() + var jObject = JObject.Parse(response); + var solution = jObject["solution"]; + + if (solution is null) { - return new CaptchaTaskRequest - { - ClientKey = ApiKey, - AppId = appId - }; + throw new TaskSolutionException("The solution is null"); } - #endregion - #region Capabilities - /// - public override CaptchaServiceCapabilities Capabilities => - CaptchaServiceCapabilities.None; - #endregion + result.Solution = task.Type switch + { + CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3 or CaptchaType.HCaptcha => + (Solution)solution.ToObject()!, + CaptchaType.FunCaptcha => solution.ToObject(), + CaptchaType.ImageCaptcha => solution.ToObject(), + CaptchaType.GeeTest => solution.ToObject(), + CaptchaType.DataDome => solution.ToObject(), + _ => throw new NotSupportedException($"The captcha type {task.Type} is not supported") + } ?? throw new TaskSolutionException("The solution is null"); + + return result.Solution.ToCaptchaResponse(task.IdString) as T; } + #endregion + + #region Private Methods + private CaptchaTaskRequest CreateTaskRequest() + { + return new CaptchaTaskRequest + { + ClientKey = ApiKey, + AppId = _appId + }; + } + #endregion + + #region Capabilities + /// + public override CaptchaServiceCapabilities Capabilities => + CaptchaServiceCapabilities.None; + #endregion } diff --git a/CaptchaSharp/Services/CustomAntiCaptchaService.cs b/CaptchaSharp/Services/CustomAntiCaptchaService.cs index 730de3a..a963194 100644 --- a/CaptchaSharp/Services/CustomAntiCaptchaService.cs +++ b/CaptchaSharp/Services/CustomAntiCaptchaService.cs @@ -2,37 +2,33 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services +namespace CaptchaSharp.Services; + +/// +/// The service provided by a service that implements the anti-captcha API. +/// +public class CustomAntiCaptchaService : AntiCaptchaService { - /// The service provided by a service that implements the anti-captcha API. - public class CustomAntiCaptchaService : AntiCaptchaService + /// + /// Initializes a . + /// + /// The API key to use. + /// The base URI of the service. + /// The to use for requests. If null, a default one will be created. + public CustomAntiCaptchaService(string apiKey, Uri baseUri, HttpClient? httpClient = null) + : base(apiKey, httpClient) { - /// Initializes a using the given , - /// and . - /// If is null, a default one will be created. - /// - public CustomAntiCaptchaService(string apiKey, Uri baseUri, HttpClient? httpClient = null) - : base(apiKey, httpClient) - { - SetupHttpClient(baseUri); - } - - /// Sets anti-captcha.com as host and as - /// for the requests. - protected void SetupHttpClient(Uri baseUri) - { - httpClient.BaseAddress = baseUri; - } - - #region Supported Types - /// The supported captcha types for this service. - public CaptchaType SupportedCaptchaTypes { get; set; } = - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3 | - CaptchaType.FunCaptcha | - CaptchaType.HCaptcha | - CaptchaType.GeeTest; - #endregion + httpClient.BaseAddress = baseUri; } + + #region Supported Types + /// The supported captcha types for this service. + public CaptchaType SupportedCaptchaTypes { get; set; } = + CaptchaType.ImageCaptcha | + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3 | + CaptchaType.FunCaptcha | + CaptchaType.HCaptcha | + CaptchaType.GeeTest; + #endregion } diff --git a/CaptchaSharp/Services/CustomTwoCaptchaService.cs b/CaptchaSharp/Services/CustomTwoCaptchaService.cs index 8a9875c..4eed1cc 100644 --- a/CaptchaSharp/Services/CustomTwoCaptchaService.cs +++ b/CaptchaSharp/Services/CustomTwoCaptchaService.cs @@ -40,7 +40,7 @@ private void SetupHttpClient(Uri baseUri, bool overrideHostHeader = true) #region Supported Types /// The supported captcha types for this service. - public CaptchaType SupportedCaptchaTypes { get; protected set; } = + public CaptchaType SupportedCaptchaTypes { get; set; } = CaptchaType.TextCaptcha | CaptchaType.ImageCaptcha | CaptchaType.ReCaptchaV2 | diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index f76ace5..4af361d 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -59,8 +59,7 @@ public TwoCaptchaService(string apiKey, HttpClient? httpClient = null) /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await HttpClient.GetStringAsync - ("res.php", + var response = await HttpClient.GetStringAsync("res.php", new StringPairCollection() .Add("key", ApiKey) .Add("action", "getbalance") @@ -93,17 +92,16 @@ public override async Task GetBalanceAsync(CancellationToken cancellati public override async Task SolveTextCaptchaAsync( string text, TextCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("textcaptcha", text) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertCapabilities(options)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("textcaptcha", text) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertCapabilities(options)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -119,18 +117,17 @@ public override async Task SolveTextCaptchaAsync( public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "base64") - .Add("body", base64) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertCapabilities(options)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "base64") + .Add("body", base64) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertCapabilities(options)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -147,21 +144,20 @@ public override async Task SolveRecaptchaV2Async( string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "userrecaptcha") - .Add("googlekey", siteKey) - .Add("pageurl", siteUrl) - .Add("data-s", dataS, !string.IsNullOrEmpty(dataS)) - .Add("enterprise", Convert.ToInt32(enterprise).ToString()) - .Add("invisible", Convert.ToInt32(invisible).ToString()) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "userrecaptcha") + .Add("googlekey", siteKey) + .Add("pageurl", siteUrl) + .Add("data-s", dataS, !string.IsNullOrEmpty(dataS)) + .Add("enterprise", Convert.ToInt32(enterprise).ToString()) + .Add("invisible", Convert.ToInt32(invisible).ToString()) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), cancellationToken) .ConfigureAwait(false); @@ -179,23 +175,22 @@ public override async Task SolveRecaptchaV3Async( string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "userrecaptcha") - .Add("version", "v3") - .Add("googlekey", siteKey) - .Add("pageurl", siteUrl) - .Add("action", action) - .Add("enterprise", Convert.ToInt32(enterprise).ToString()) - .Add("min_score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "userrecaptcha") + .Add("version", "v3") + .Add("googlekey", siteKey) + .Add("pageurl", siteUrl) + .Add("action", action) + .Add("enterprise", Convert.ToInt32(enterprise).ToString()) + .Add("min_score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -212,21 +207,20 @@ public override async Task SolveFuncaptchaAsync( string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "funcaptcha") - .Add("publickey", publicKey) - .Add("surl", serviceUrl) - .Add("pageurl", siteUrl) - .Add("nojs", Convert.ToInt32(noJs).ToString()) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "funcaptcha") + .Add("publickey", publicKey) + .Add("surl", serviceUrl) + .Add("pageurl", siteUrl) + .Add("nojs", Convert.ToInt32(noJs).ToString()) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -243,19 +237,18 @@ public override async Task SolveHCaptchaAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "hcaptcha") - .Add("sitekey", siteKey) - .Add("pageurl", siteUrl) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "hcaptcha") + .Add("sitekey", siteKey) + .Add("pageurl", siteUrl) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -272,22 +265,21 @@ public override async Task SolveKeyCaptchaAsync( string userId, string sessionId, string webServerSign1, string webServerSign2, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "keycaptcha") - .Add("s_s_c_user_id", userId) - .Add("s_s_c_session_id", sessionId) - .Add("s_s_c_web_server_sign", webServerSign1) - .Add("s_s_c_web_server_sign2", webServerSign2) - .Add("pageurl", siteUrl) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "keycaptcha") + .Add("s_s_c_user_id", userId) + .Add("s_s_c_session_id", sessionId) + .Add("s_s_c_web_server_sign", webServerSign1) + .Add("s_s_c_web_server_sign2", webServerSign2) + .Add("pageurl", siteUrl) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -304,21 +296,20 @@ public override async Task SolveGeeTestAsync( string gt, string challenge, string apiServer, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "geetest") - .Add("gt", gt) - .Add("challenge", challenge) - .Add("api_server", apiServer) - .Add("pageurl", siteUrl) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "geetest") + .Add("gt", gt) + .Add("challenge", challenge) + .Add("api_server", apiServer) + .Add("pageurl", siteUrl) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -335,19 +326,18 @@ public override async Task SolveCapyAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "capy") - .Add("captchakey", siteKey) - .Add("pageurl", siteUrl) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "capy") + .Add("captchakey", siteKey) + .Add("pageurl", siteUrl) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -370,19 +360,18 @@ public override async Task SolveDataDomeAsync( throw new ArgumentException("A proxy with a User-Agent is required for DataDome captchas."); } - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "datadome") - .Add("captcha_url", captchaUrl) - .Add("pageurl", siteUrl) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "datadome") + .Add("captcha_url", captchaUrl) + .Add("pageurl", siteUrl) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -406,22 +395,21 @@ public override async Task SolveCloudflareTurnstileAsync( throw new ArgumentException("A User-Agent is required for Cloudflare Turnstile captchas."); } - var response = await HttpClient.PostMultipartToStringAsync - ("in.php", - new StringPairCollection() - .Add("key", ApiKey) - .Add("method", "turnstile") - .Add("sitekey", siteKey) - .Add("pageurl", siteUrl) - .Add("action", action ?? string.Empty, !string.IsNullOrEmpty(action)) - .Add("data", data ?? string.Empty, !string.IsNullOrEmpty(data)) - .Add("pagedata", pageData ?? string.Empty, !string.IsNullOrEmpty(pageData)) - .Add("soft_id", _softId) - .Add("json", "1", UseJsonFlag) - .Add("header_acao", "1", AddAcaoHeader) - .Add(ConvertProxy(proxy)) - .ToMultipartFormDataContent(), - cancellationToken) + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "turnstile") + .Add("sitekey", siteKey) + .Add("pageurl", siteUrl) + .Add("action", action ?? string.Empty, !string.IsNullOrEmpty(action)) + .Add("data", data ?? string.Empty, !string.IsNullOrEmpty(data)) + .Add("pagedata", pageData ?? string.Empty, !string.IsNullOrEmpty(pageData)) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); return UseJsonFlag @@ -436,7 +424,7 @@ public override async Task SolveCloudflareTurnstileAsync( #endregion #region Getting the result - internal async Task GetResult( + private async Task GetResult( Response response, CaptchaType type, CancellationToken cancellationToken = default) where T : CaptchaResponse { @@ -465,8 +453,7 @@ internal async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var response = await HttpClient.GetStringAsync - ("res.php", + var response = await HttpClient.GetStringAsync("res.php", new StringPairCollection() .Add("key", ApiKey) .Add("action", "get") @@ -549,8 +536,7 @@ public override async Task ReportSolution( { var action = correct ? "reportgood" : "reportbad"; - var response = await HttpClient.GetStringAsync - ("res.php", + var response = await HttpClient.GetStringAsync("res.php", new StringPairCollection() .Add("key", ApiKey) .Add("action", action) From 4a648ad3fb5e0c744db479dfdcf4217db34387d4 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 03:59:38 +0200 Subject: [PATCH 07/67] Updated DeCaptcherService to new API spec from the docs Need a valid account to test --- CaptchaSharp.Tests/ConfigFixture.cs | 3 +- CaptchaSharp.Tests/DeCaptcherServiceTests.cs | 12 +- CaptchaSharp.Tests/ImageTyperzServiceTests.cs | 7 +- CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 3 +- CaptchaSharp/CaptchaService.cs | 24 ++- CaptchaSharp/Services/DeCaptcherService.cs | 197 ++++++++---------- 6 files changed, 119 insertions(+), 127 deletions(-) diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 8d5d750..8364b52 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -40,8 +40,7 @@ public class Credentials public bool CustomTwoCaptchaOverrideHostHeader { get; set; } = true; public string DeathByCaptchaUsername { get; set; } = string.Empty; public string DeathByCaptchaPassword { get; set; } = string.Empty; - public string DeCaptcherUsername { get; set; } = string.Empty; - public string DeCaptcherPassword { get; set; } = string.Empty; + public string DeCaptcherApiKey { get; set; } = string.Empty; public string ImageTyperzApiKey { get; set; } = string.Empty; public string CapMonsterHost { get; set; } = string.Empty; public int CapMonsterPort { get; set; } = 80; diff --git a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs index 891c7db..a141359 100644 --- a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs +++ b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs @@ -8,20 +8,16 @@ public class DeCaptcherFixture : ServiceFixture { public DeCaptcherFixture() { - Service = new DeCaptcherService( - Config.Credentials.DeCaptcherUsername, - Config.Credentials.DeCaptcherPassword); + Service = new DeCaptcherService(Config.Credentials.DeCaptcherApiKey); } } - public class DeCaptcherServiceTests : ServiceTests, IClassFixture + public class DeCaptcherServiceTests(DeCaptcherFixture fixture) + : ServiceTests(fixture), IClassFixture { - public DeCaptcherServiceTests(DeCaptcherFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveTextCaptchaAsync_ValidCaptcha_ValidSolution() => TextCaptchaTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); } -} \ No newline at end of file +} diff --git a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs index 9d00875..0b4323e 100644 --- a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs +++ b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs @@ -12,10 +12,9 @@ public ImageTyperzFixture() } } - public class ImageTyperzServiceTests : ServiceTests, IClassFixture + public class ImageTyperzServiceTests(ImageTyperzFixture fixture) + : ServiceTests(fixture), IClassFixture { - public ImageTyperzServiceTests(ImageTyperzFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); @@ -29,4 +28,4 @@ public ImageTyperzServiceTests(ImageTyperzFixture fixture) : base(fixture) { } [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); } -} \ No newline at end of file +} diff --git a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs index 941f65a..4c79dbb 100644 --- a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs @@ -12,7 +12,8 @@ public TwoCaptchaFixture() } } -public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture) : ServiceTests(fixture), IClassFixture +public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture) + : ServiceTests(fixture), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); diff --git a/CaptchaSharp/CaptchaService.cs b/CaptchaSharp/CaptchaService.cs index 16974ee..b329b72 100644 --- a/CaptchaSharp/CaptchaService.cs +++ b/CaptchaSharp/CaptchaService.cs @@ -388,11 +388,31 @@ public virtual Task SolveCloudflareTurnstileAsync( /// A token that can be used to cancel the async task. /// /// - public virtual Task ReportSolution - (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + public virtual Task ReportSolution( + long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } + + /// + /// Reports a captcha solution as good or bad to the service. + /// Mostly used for reporting bad solutions for image captchas and get the funds back. + /// Make sure to not abuse this system or the service might ban you from accessing it! + /// + /// + /// The string ID of the captcha that you got inside your . + /// The type of captcha you want to report. + /// + /// + /// If true, the captcha will be reported as correctly solved (this is not supported by most services). + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + public virtual Task ReportSolution( + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + => ReportSolution(long.Parse(id), type, correct, cancellationToken); /// protected async Task GetResult( diff --git a/CaptchaSharp/Services/DeCaptcherService.cs b/CaptchaSharp/Services/DeCaptcherService.cs index 725dfaf..224c6f5 100644 --- a/CaptchaSharp/Services/DeCaptcherService.cs +++ b/CaptchaSharp/Services/DeCaptcherService.cs @@ -17,14 +17,9 @@ namespace CaptchaSharp.Services; public class DeCaptcherService : CaptchaService { /// - /// Your DeCaptcher account name. + /// Your secret api key. /// - public string Username { get; set; } - - /// - /// Your DeCaptcher account password. - /// - public string Password { get; set; } + public string ApiKey { get; set; } /// The default used for requests. private readonly HttpClient _httpClient; @@ -33,14 +28,13 @@ public class DeCaptcherService : CaptchaService /// Initializes a . /// /// - public DeCaptcherService(string username, string password, HttpClient? httpClient = null) + public DeCaptcherService(string apiKey, HttpClient? httpClient = null) { - Username = username; - Password = password; + ApiKey = apiKey; this._httpClient = httpClient ?? new HttpClient(); // TODO: Use https instead of http if possible - this._httpClient.BaseAddress = new Uri("http://poster.de-captcher.com/"); + this._httpClient.BaseAddress = new Uri("http://api.captchacoder.com/"); // Since this service replies directly with the solution to the task creation request // we need to set a high timeout here, or it will not finish in time @@ -51,8 +45,12 @@ public DeCaptcherService(string username, string password, HttpClient? httpClien /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartToStringAsync("", - GetAuthPair().Add("function", "balance").ToMultipartFormDataContent(), cancellationToken) + var response = await _httpClient.PostMultipartToStringAsync( + "Imagepost.ashx", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", "balance") + .ToMultipartFormDataContent(), cancellationToken) .ConfigureAwait(false); if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) @@ -66,145 +64,124 @@ public override async Task GetBalanceAsync(CancellationToken cancellati #region Solve Methods /// - public override async Task SolveTextCaptchaAsync( - string text, TextCaptchaOptions? options = null, CancellationToken cancellationToken = default) + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartToStringAsync("", - GetAuthPair() - .Add("function", "picture2") - .Add("pict", text) - .Add("pict_type", 83) - .Add("lang", options?.CaptchaLanguage.ToIso6391Code() ?? string.Empty, options is not null) - .ToMultipartFormDataContent(), - cancellationToken) + var captchaId = Guid.NewGuid().ToString(); + + var response = await _httpClient.PostMultipartToStringAsync( + "Imagepost.ashx", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", "upload") + .Add("file", base64) + .Add("gen_task_id", captchaId) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); if (DeCaptcherResponse.TryParse(response, out var resp)) { - return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; + return new StringResponse { IdString = captchaId, Response = resp.Text }; } throw new TaskSolutionException(response); } /// - public override async Task SolveImageCaptchaAsync( - string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) { - var content = GetAuthPair() - .Add("function", "picture2") - .Add("pict_type", 0) - .Add("lang", options?.CaptchaLanguage.ToIso6391Code() ?? string.Empty, options is not null) - .ToMultipartFormDataContent(); - - var buffer = Convert.FromBase64String(base64); - content.Add(new ByteArrayContent(buffer), "pict"); - - var response = await _httpClient.PostMultipartToStringAsync("", - content, - cancellationToken) + if (proxy is not null) + { + throw new NotSupportedException("Proxies are not supported by this service."); + } + + var captchaId = Guid.NewGuid().ToString(); + + var response = await _httpClient.PostMultipartToStringAsync( + "Imagepost.ashx", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", "upload") + .Add("captchatype", 3) + .Add("gen_task_id", captchaId) + .Add("pageurl", siteUrl) + .Add("sitekey", siteKey) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); if (DeCaptcherResponse.TryParse(response, out var resp)) { - return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; + return new StringResponse { IdString = captchaId, Response = resp.Text }; } throw new TaskSolutionException(response); } /// - public override async Task SolveRecaptchaV2Async( - string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy? proxy = null, CancellationToken cancellationToken = default) + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { if (proxy is not null) { - if (proxy.RequiresAuthentication) - { - throw new NotSupportedException("Authenticated proxies are not supported"); - } - - if (proxy.Type != ProxyType.SOCKS4 && proxy.Type != ProxyType.SOCKS5) - { - throw new NotSupportedException("Only SOCKS proxies are supported"); - } - - if (siteUrl.StartsWith("https")) - { - throw new NotSupportedException("Only http sites are supported"); - } + throw new NotSupportedException("Proxies are not supported by this service."); } - - var pairs = GetAuthPair() - .Add("function", "proxy_url") - .Add("url", siteUrl) - .Add("key", siteKey); - - if (proxy != null) - { - pairs.Add("proxy", $"{proxy.Host}:{proxy.Port}"); - } - - var response = await _httpClient.PostMultipartToStringAsync("", - pairs.ToMultipartFormDataContent(), - cancellationToken) + + var captchaId = Guid.NewGuid().ToString(); + + var response = await _httpClient.PostMultipartToStringAsync( + "Imagepost.ashx", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", "upload") + .Add("captchatype", 3) + .Add("gen_task_id", captchaId) + .Add("pageurl", siteUrl) + .Add("sitekey", siteKey) + .Add("pageaction", action) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); - if (DeCaptcherResponse.TryParse(response, out DeCaptcherResponse resp)) + if (DeCaptcherResponse.TryParse(response, out var resp)) { - return new StringResponse { Id = GetCaptchaId(resp), Response = resp.Text }; + return new StringResponse { IdString = captchaId, Response = resp.Text }; } - + throw new TaskSolutionException(response); } + #endregion #region Reporting the solution /// - public override async Task ReportSolution( + public override Task ReportSolution( long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var (major, minor) = LongToInts(id); - - await _httpClient.PostMultipartToStringAsync("", - GetAuthPair() - .Add("function", "picture_bad2") - .Add("major_id", major) - .Add("minor_id", minor) - .ToMultipartFormDataContent(), cancellationToken) - .ConfigureAwait(false); - - // TODO: Find a way to check if the api accepted the report or not + throw new NotSupportedException("Use the string id overload instead."); } - #endregion - #region Private Methods - private StringPairCollection GetAuthPair() - { - return new StringPairCollection() - .Add("username", Username) - .Add("password", Password); - } - - private long GetCaptchaId(DeCaptcherResponse response) - { - return IntsToLong(response.MajorID, response.MinorID); - } - - // Encodes two 32-bit integers as a 64-bit long - private static long IntsToLong(int a, int b) + /// + public override async Task ReportSolution( + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - long l = b; - l <<= 32; - l |= (uint)a; - return l; - } + var response = await _httpClient.PostMultipartToStringAsync( + "Imagepost.ashx", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", "refund") + .Add("gen_task_id", id) + .ToMultipartFormDataContent(), cancellationToken) + .ConfigureAwait(false); - // Gets two 32-bit integers by splitting a 64-bit long - private static (int, int) LongToInts(long longId) - { - return ((int)(longId & uint.MaxValue), (int)(longId >> 32)); + if (!response.Contains("ok")) + { + throw new TaskReportException(response); + } } #endregion } From a98f7a9450a1b1989e88eb7c272080bea9d61d09 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 03:59:56 +0200 Subject: [PATCH 08/67] Updated TrueCaptchaService --- .../TrueCaptchaService.cs | 71 +++- CaptchaSharp.Tests/TrueCaptchaTests.cs | 36 +- CaptchaSharp/CaptchaSharp.xml | 401 ++++++++++-------- 3 files changed, 299 insertions(+), 209 deletions(-) diff --git a/CaptchaSharp.Services.More/TrueCaptchaService.cs b/CaptchaSharp.Services.More/TrueCaptchaService.cs index c42c9bc..66701ea 100644 --- a/CaptchaSharp.Services.More/TrueCaptchaService.cs +++ b/CaptchaSharp.Services.More/TrueCaptchaService.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using CaptchaSharp.Enums; using CaptchaSharp.Extensions; namespace CaptchaSharp.Services.More; @@ -49,12 +50,12 @@ public TrueCaptchaService(string userId, string apiKey, HttpClient? httpClient = /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("one/getbalance", - new StringPairCollection() - .Add("username", UserId) - .Add("apikey", ApiKey), - cancellationToken) + var response = await _httpClient.GetStringAsync( + "one/getbalance", + new StringPairCollection() + .Add("username", UserId) + .Add("apikey", ApiKey), + cancellationToken) .ConfigureAwait(false); if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out var balance)) @@ -69,16 +70,18 @@ public override async Task GetBalanceAsync(CancellationToken cancellati public override async Task SolveImageCaptchaAsync (string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var content = new JObject(); - content.Add("userid", UserId); - content.Add("apikey", ApiKey); - content.Add("data", base64); + var content = new JObject + { + { "userid", UserId }, + { "apikey", ApiKey }, + { "data", base64 } + }; - var response = await _httpClient.PostJsonToStringAsync - ("one/gettext", - content, - camelizeKeys: false, - cancellationToken: cancellationToken) + var response = await _httpClient.PostJsonToStringAsync( + "one/gettext", + content, + camelizeKeys: false, + cancellationToken: cancellationToken) .ConfigureAwait(false); var jObject = JObject.Parse(response); @@ -88,7 +91,43 @@ public override async Task SolveImageCaptchaAsync { throw new TaskSolutionException(response); } + + var requestId = jObject["requestId"]!.Value()!; - return new StringResponse { Id = 0, Response = result.ToString() }; + return new StringResponse { IdString = requestId, Response = result.ToString() }; + } + + /// + public override Task ReportSolution( + long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + throw new NotSupportedException("Use the string id overload instead."); + } + + /// + public override async Task ReportSolution( + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + var content = new JObject + { + { "userid", UserId }, + { "apikey", ApiKey }, + { "request_id", id } + }; + + var response = await _httpClient.PostJsonToStringAsync( + "one/report_error", + content, + camelizeKeys: false, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var jObject = JObject.Parse(response); + var result = jObject["result"]; + + if (result is null || result.Value() != "True") + { + throw new TaskReportException(response); + } } } diff --git a/CaptchaSharp.Tests/TrueCaptchaTests.cs b/CaptchaSharp.Tests/TrueCaptchaTests.cs index 0b0a83b..a643357 100644 --- a/CaptchaSharp.Tests/TrueCaptchaTests.cs +++ b/CaptchaSharp.Tests/TrueCaptchaTests.cs @@ -2,23 +2,27 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class TrueCaptchaFixture : ServiceFixture { - public class TrueCaptchaFixture : ServiceFixture + public TrueCaptchaFixture() { - public TrueCaptchaFixture() - { - Service = new TrueCaptchaService( - Config.Credentials.TrueCaptchaUsername, - Config.Credentials.TrueCaptchaApiKey); - } + Service = new TrueCaptchaService( + Config.Credentials.TrueCaptchaUsername, + Config.Credentials.TrueCaptchaApiKey); } +} - public class TrueCaptchaServiceTests : ServiceTests, IClassFixture - { - public TrueCaptchaServiceTests(TrueCaptchaFixture fixture) : base(fixture) { } - - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - } -} \ No newline at end of file +public class TrueCaptchaServiceTests(TrueCaptchaFixture fixture) + : ServiceTests(fixture), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + + // Do not overly use this test, or you will get banned. + [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); + + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 6837a3a..6a86504 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -50,7 +50,7 @@ The captcha image encoded as a base64 string. - Any additional options like whether the captcha is case sensitive. + Any additional options like whether the captcha is case-sensitive. If null they will be disregarded. @@ -66,31 +66,6 @@ - - Solves a Google ReCaptcha V2. - - The site key, can be found in the webpage source or by sniffing requests. - The URL where the captcha appears. - Whether the captcha is not in a clickable format on the page. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - Solves a Google ReCaptcha V2. @@ -118,32 +93,6 @@ - - Solves a Google ReCaptcha V3. - - The site key, can be found in the webpage source or by sniffing requests. - The URL where the captcha appears. - The action to execute. Can be found in the webpage source or in a js file. - The minimum human-to-robot score necessary to solve the challenge. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - Solves a Google ReCaptcha V3. @@ -184,7 +133,7 @@ The URL where the captcha appears. - + Whether to solve the challenge without JavaScript enabled. This is not supported by every service and it provides a partial token. @@ -378,10 +327,28 @@ - + + + Reports a captcha solution as good or bad to the service. + Mostly used for reporting bad solutions for image captchas and get the funds back. + Make sure to not abuse this system or the service might ban you from accessing it! + + + The string ID of the captcha that you got inside your . + The type of captcha you want to report. + + + If true, the captcha will be reported as correctly solved (this is not supported by most services). + + + A token that can be used to cancel the async task. + + + + - + @@ -717,57 +684,57 @@ - + + Extensions for a . + + + Converts a to an ISO-639-1 country code. + + Extensions for an . - + Automatically builds a GET query string from a and appends it to the provided URL. - + Automatically builds a GET query string from a and appends it to the provided URL. The content converted to a string. - + Automatically builds a POST query string from a using encoding and the provided Content-Type. - + Automatically builds a POST query string from a using encoding and the provided Content-Type. The content converted to a . - + Sends a POST request with the desired and reads the response as a . The content converted to a . - + Automatically builds a POST json string from a given object using encoding and application/json Content-Type. The content converted to a . - + Extensions for a . - + Deserializes a json string to a given type. - + Serializes an object to a json string and converts the property names to a camelCase based convention. - + Serializes an object to a json string and converts the property names to a lowercase based convention. - - Extensions for a . - - - Converts a to an ISO-639-1 country code. - A generic captcha response. @@ -838,7 +805,7 @@ Whether the captcha is made of multiple words. - Whether the captcha should be solved as case sensitive. + Whether the captcha should be solved as case-sensitive. The set of allowed characters. @@ -930,20 +897,31 @@ The language of the text. - The service provided by https://anti-captcha.com/ + + The service provided by https://anti-captcha.com/ + - Your secret api key. + + Your secret api key. + - - The default used for requests. + + + The default used for requests. + - - The ID of the software developer. + + + The ID of the software developer. + - Initializes a using the given and - . If is null, a default one will be created. + + Initializes a . + + Your secret api key. + The to use for requests. If null, a default one will be created. @@ -966,7 +944,7 @@ - + @@ -976,31 +954,45 @@ - The service provided by the CapMonster OCR application by ZennoLab. + + The service provided by the CapMonster OCR application by ZennoLab. + - Initializes a using the given , - and . - If is null, a default one will be created. + Initializes a . + The API key to use. + The base URI of the service. + The to use for requests. If null, a default one will be created. - The service provided by https://capsolver.com/ + + The service provided by https://capsolver.com/ + - Your secret api key. + + Your secret api key. + - - The default used for requests. + + + The default used for requests. + - - The ID of the app. + + + The ID of the app. + - Initializes a using the given and - . If is null, a default one will be created. + + Initializes a . + + Your secret api key. + The to use for requests. If null, a default one will be created. @@ -1026,59 +1018,72 @@ - + - The service provided by a service that implements the anti-captcha API. + + The service provided by a service that implements the anti-captcha API. + - Initializes a using the given , - and . - If is null, a default one will be created. + + Initializes a . - - - Sets anti-captcha.com as host and as - for the requests. + The API key to use. + The base URI of the service. + The to use for requests. If null, a default one will be created. The supported captcha types for this service. - The service provided by a service that implements the 2captcha API. + + The service provided by a service that implements the 2captcha API. + - Initializes a using the given , - and . - If is null, a default one will be created. - If is true, the Host header will be changed to 2captcha.com - - - Sets 2captcha.com as host and as - for the requests. + + Initializes a . + + The API key to use. + The base URI of the service. + The to use for requests. If null, a default one will be created. + Whether to override the Host header to 2captcha.com. The supported captcha types for this service. - The service provided by https://www.deathbycaptcha.com/ + + The service provided by https://www.deathbycaptcha.com/ + - Your DeathByCaptcha account name. + + Your DeathByCaptcha account name. + - Your DeathByCaptcha account password. + + Your DeathByCaptcha account password. + - - The default used for requests. + + + The default used for requests. + - Initializes a using the given account credentials and - . If is null, a default one will be created. + + Initializes a . + + Your DeathByCaptcha account name. + Your DeathByCaptcha account password. + The used for requests. If null, a default one will be created. @@ -1095,58 +1100,75 @@ - + - The service provided by https://de-captcher.com/ - - - Your DeCaptcher account name. + + The service provided by https://de-captcher.com/ + - - Your DeCaptcher account password. + + + Your secret api key. + - + The default used for requests. - - Initializes a using the given account credentials and - . If is null, a default one will be created. + + + Initializes a . + + - - - + + + + + + - The service provided by https://www.imagetyperz.com/ + + The service provided by https://www.imagetyperz.com/ + - Your secret api key. + + Your secret api key. + - - The default used for requests. + + + The default used for requests. + - - The ID of the software developer. + + + The ID of the software developer. + - Initializes a using the given and - . If is null, a default one will be created. + + Initializes a . + + Your secret api key. + The to use for requests. If null, a default one will be created. @@ -1169,7 +1191,7 @@ - + @@ -1178,77 +1200,100 @@ - - The service provided by https://www.9kw.eu/ + + + The service provided by https://www.9kw.eu/ + - - Your secret api key. + + + Your secret api key. + - - The default used for requests. + + + The default used for requests. + - - Initializes a using the given and - . If is null, a default one will be created. + + + Initializes a . + + Your secret api key. + The to use for requests. If null, a default one will be created. - + - + - + - + - + - + - + - + - + - + - - + + + The capabilities of the service. + - + - The service provided by https://2captcha.com/ + + The service provided by https://2captcha.com/ + - Your secret api key. + + Your secret api key. + - - The default used for requests. + + + The default used for requests. + - Set it to false if the service does not support json responses. + + Set it to false if the service does not support json responses. + - - Will include an Access-Control-Allow-Origin:* header in the response for - cross-domain AJAX requests in web applications. + + + Will include an Access-Control-Allow-Origin:* header in the response for + cross-domain AJAX requests in web applications. + - + The ID of the software developer. - Initializes a using the given and - . If is null, a default one will be created. + + Initializes a . + The API key to use. + The to use for requests. If null, a default one will be created. @@ -1286,7 +1331,7 @@ - + @@ -1302,7 +1347,9 @@ For non-json response. - + + The capabilities of the service. + From f0ec1a65fe6fa40760d2ec648203c2e790c3dc9f Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 15:23:15 +0200 Subject: [PATCH 09/67] Fixed DeCaptcher --- CaptchaSharp.Tests/DeCaptcherServiceTests.cs | 33 +++++++++++--------- CaptchaSharp.Tests/ServiceTests.cs | 14 +++++++-- CaptchaSharp/Models/CaptchaResponse.cs | 7 +++++ CaptchaSharp/Services/DeCaptcherService.cs | 27 ++++++++-------- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs index a141359..529808e 100644 --- a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs +++ b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs @@ -2,22 +2,27 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class DeCaptcherFixture : ServiceFixture { - public class DeCaptcherFixture : ServiceFixture + public DeCaptcherFixture() { - public DeCaptcherFixture() - { - Service = new DeCaptcherService(Config.Credentials.DeCaptcherApiKey); - } + Service = new DeCaptcherService(Config.Credentials.DeCaptcherApiKey); } +} - public class DeCaptcherServiceTests(DeCaptcherFixture fixture) - : ServiceTests(fixture), IClassFixture - { - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); - } +public class DeCaptcherServiceTests(DeCaptcherFixture fixture) + : ServiceTests(fixture), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + + // Do not overly use this test, or you will get banned. + [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); + + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); } diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index cd1584f..2e8a91f 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -49,9 +49,17 @@ protected async Task ReportSolutionTest() var solution = await Service.SolveImageCaptchaAsync( base64: _captchaImageBase64, options); - - await Service.ReportSolution( - solution.Id, CaptchaType.ImageCaptcha, correct: true); + + if (solution.IsLongId) + { + await Service.ReportSolution( + solution.Id, CaptchaType.ImageCaptcha, correct: true); + } + else + { + await Service.ReportSolution( + solution.IdString, CaptchaType.ImageCaptcha, correct: true); + } Assert.True(true); } diff --git a/CaptchaSharp/Models/CaptchaResponse.cs b/CaptchaSharp/Models/CaptchaResponse.cs index d08d65d..bb6ce6d 100644 --- a/CaptchaSharp/Models/CaptchaResponse.cs +++ b/CaptchaSharp/Models/CaptchaResponse.cs @@ -5,12 +5,19 @@ namespace CaptchaSharp.Models; /// A generic captcha response. public class CaptchaResponse { + // TODO: All captcha ids should be strings, then parse at need. Remove the long id. + /// The captcha id which is needed to report the solution as bad. public long Id { get => long.Parse(IdString); init => IdString = value.ToString(); } + + /// + /// Whether the captcha id is a long int or a string. + /// + public bool IsLongId => long.TryParse(IdString, out _); /// /// The captcha id which is needed to report the solution, if it's diff --git a/CaptchaSharp/Services/DeCaptcherService.cs b/CaptchaSharp/Services/DeCaptcherService.cs index 224c6f5..c0437f4 100644 --- a/CaptchaSharp/Services/DeCaptcherService.cs +++ b/CaptchaSharp/Services/DeCaptcherService.cs @@ -12,7 +12,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://de-captcher.com/ +/// The service provided by https://captchacoder.com/ /// public class DeCaptcherService : CaptchaService { @@ -33,7 +33,6 @@ public DeCaptcherService(string apiKey, HttpClient? httpClient = null) ApiKey = apiKey; this._httpClient = httpClient ?? new HttpClient(); - // TODO: Use https instead of http if possible this._httpClient.BaseAddress = new Uri("http://api.captchacoder.com/"); // Since this service replies directly with the solution to the task creation request @@ -79,13 +78,13 @@ public override async Task SolveImageCaptchaAsync( .ToMultipartFormDataContent(), cancellationToken) .ConfigureAwait(false); - - if (DeCaptcherResponse.TryParse(response, out var resp)) + + if (response.Contains("Error")) { - return new StringResponse { IdString = captchaId, Response = resp.Text }; + throw new TaskSolutionException(response); } - throw new TaskSolutionException(response); + return new StringResponse { IdString = captchaId, Response = response }; } /// @@ -113,12 +112,12 @@ public override async Task SolveRecaptchaV2Async( cancellationToken) .ConfigureAwait(false); - if (DeCaptcherResponse.TryParse(response, out var resp)) + if (response.Contains("Error")) { - return new StringResponse { IdString = captchaId, Response = resp.Text }; + throw new TaskSolutionException(response); } - - throw new TaskSolutionException(response); + + return new StringResponse { IdString = captchaId, Response = response }; } /// @@ -147,12 +146,12 @@ public override async Task SolveRecaptchaV3Async( cancellationToken) .ConfigureAwait(false); - if (DeCaptcherResponse.TryParse(response, out var resp)) + if (response.Contains("Error")) { - return new StringResponse { IdString = captchaId, Response = resp.Text }; + throw new TaskSolutionException(response); } - throw new TaskSolutionException(response); + return new StringResponse { IdString = captchaId, Response = response }; } #endregion @@ -178,7 +177,7 @@ public override async Task ReportSolution( .ToMultipartFormDataContent(), cancellationToken) .ConfigureAwait(false); - if (!response.Contains("ok")) + if (!response.Contains("ok", StringComparison.CurrentCultureIgnoreCase)) { throw new TaskReportException(response); } From 932e4cd64324ce24aa783033e336a99b8eb3fa43 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 15:23:41 +0200 Subject: [PATCH 10/67] Formatting --- CaptchaSharp.Tests/AntiCaptchaServiceTests.cs | 43 +++++---- CaptchaSharp.Tests/AnyCaptchaTests.cs | 33 ++++--- CaptchaSharp.Tests/CapSolverServiceTests.cs | 35 ++++--- CaptchaSharp.Tests/ConfigFixture.cs | 91 +++++++++---------- .../DeathByCaptchaServiceTests.cs | 42 ++++----- CaptchaSharp.Tests/ImageTyperzServiceTests.cs | 43 +++++---- CaptchaSharp.Tests/ServiceFixture.cs | 19 ++-- CaptchaSharp/CaptchaSharp.xml | 7 +- CaptchaSharp/Services/ImageTyperzService.cs | 1 - 9 files changed, 155 insertions(+), 159 deletions(-) diff --git a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs index 5a64de5..c56e52d 100644 --- a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs @@ -2,31 +2,30 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class AntiCaptchaFixture : ServiceFixture { - public class AntiCaptchaFixture : ServiceFixture + public AntiCaptchaFixture() { - public AntiCaptchaFixture() - { - Service = new AntiCaptchaService(Config.Credentials.AntiCaptchaApiKey); - } + Service = new AntiCaptchaService(Config.Credentials.AntiCaptchaApiKey); } +} - public class AntiCaptchaServiceTests : ServiceTests, IClassFixture - { - public AntiCaptchaServiceTests(AntiCaptchaFixture fixture) : base(fixture) { } +public class AntiCaptchaServiceTests : ServiceTests, IClassFixture +{ + public AntiCaptchaServiceTests(AntiCaptchaFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); - [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); - [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); - [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); - [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); - [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); - [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); - [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); - } + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/AnyCaptchaTests.cs b/CaptchaSharp.Tests/AnyCaptchaTests.cs index 4547ec3..732ba6e 100644 --- a/CaptchaSharp.Tests/AnyCaptchaTests.cs +++ b/CaptchaSharp.Tests/AnyCaptchaTests.cs @@ -2,25 +2,24 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class AnyCaptchaFixture : ServiceFixture { - public class AnyCaptchaFixture : ServiceFixture + public AnyCaptchaFixture() { - public AnyCaptchaFixture() - { - Service = new AnyCaptchaService(Config.Credentials.AnyCaptchaApiKey); - } + Service = new AnyCaptchaService(Config.Credentials.AnyCaptchaApiKey); } +} - public class AnyCaptchaServiceTests : ServiceTests, IClassFixture - { - public AnyCaptchaServiceTests(AnyCaptchaFixture fixture) : base(fixture) { } +public class AnyCaptchaServiceTests : ServiceTests, IClassFixture +{ + public AnyCaptchaServiceTests(AnyCaptchaFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); - [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); - } -} \ No newline at end of file + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); +} diff --git a/CaptchaSharp.Tests/CapSolverServiceTests.cs b/CaptchaSharp.Tests/CapSolverServiceTests.cs index 0fa09ed..4f6afa7 100644 --- a/CaptchaSharp.Tests/CapSolverServiceTests.cs +++ b/CaptchaSharp.Tests/CapSolverServiceTests.cs @@ -2,27 +2,26 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class CapSolverFixture : ServiceFixture { - public class CapSolverFixture : ServiceFixture + public CapSolverFixture() { - public CapSolverFixture() - { - Service = new CapSolverService(Config.Credentials.CapSolverApiKey); - } + Service = new CapSolverService(Config.Credentials.CapSolverApiKey); } +} - public class CapSolverServiceTests : ServiceTests, IClassFixture - { - public CapSolverServiceTests(CapSolverFixture fixture) : base(fixture) { } +public class CapSolverServiceTests : ServiceTests, IClassFixture +{ + public CapSolverServiceTests(CapSolverFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); - [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); - [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); - [Fact] public Task SolveDataDomeTestAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); - } + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveDataDomeTestAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 8364b52..0ebaa9f 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -2,57 +2,56 @@ using Newtonsoft.Json; using System.IO; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class ConfigFixture { - public class ConfigFixture - { - private readonly string credentialsFile = "config.json"; - public Config Config { get; set; } + private readonly string credentialsFile = "config.json"; + public Config Config { get; set; } - public ConfigFixture() + public ConfigFixture() + { + if (File.Exists(credentialsFile)) { - if (File.Exists(credentialsFile)) - { - Config = JsonConvert.DeserializeObject(File.ReadAllText(credentialsFile)); - } - else - { - // Write a blank structure if it doesn't exist so that we don't have to manually create it from scratch - Config = new Config(); - File.WriteAllText(credentialsFile, JsonConvert.SerializeObject(Config, Formatting.Indented)); - } + Config = JsonConvert.DeserializeObject(File.ReadAllText(credentialsFile)); + } + else + { + // Write a blank structure if it doesn't exist so that we don't have to manually create it from scratch + Config = new Config(); + File.WriteAllText(credentialsFile, JsonConvert.SerializeObject(Config, Formatting.Indented)); } } +} - public class Config - { - public Credentials Credentials { get; set; } = new(); - public Proxy Proxy { get; set; } = new(); - } +public class Config +{ + public Credentials Credentials { get; set; } = new(); + public Proxy Proxy { get; set; } = new(); +} - public class Credentials - { - public string TwoCaptchaApiKey { get; set; } = string.Empty; - public string AntiCaptchaApiKey { get; set; } = string.Empty; - public string CustomTwoCaptchaApiKey { get; set; } = string.Empty; - public string CustomTwoCaptchaHost { get; set; } = string.Empty; - public int CustomTwoCaptchaPort { get; set; } = 80; - public bool CustomTwoCaptchaOverrideHostHeader { get; set; } = true; - public string DeathByCaptchaUsername { get; set; } = string.Empty; - public string DeathByCaptchaPassword { get; set; } = string.Empty; - public string DeCaptcherApiKey { get; set; } = string.Empty; - public string ImageTyperzApiKey { get; set; } = string.Empty; - public string CapMonsterHost { get; set; } = string.Empty; - public int CapMonsterPort { get; set; } = 80; - public string AZCaptchaApiKey { get; set; } = string.Empty; - public string CaptchasIOApiKey { get; set; } = string.Empty; - public string RuCaptchaApiKey { get; set; } = string.Empty; - public string SolveCaptchaApiKey { get; set; } = string.Empty; - public string SolveRecaptchaApiKey { get; set; } = string.Empty; - public string TrueCaptchaApiKey { get; set; } = string.Empty; - public string TrueCaptchaUsername { get; set; } = string.Empty; - public string NineKWApiKey { get; set; } = string.Empty; - public string AnyCaptchaApiKey { get; set; } = string.Empty; - public string CapSolverApiKey { get; set; } = string.Empty; - } +public class Credentials +{ + public string TwoCaptchaApiKey { get; set; } = string.Empty; + public string AntiCaptchaApiKey { get; set; } = string.Empty; + public string CustomTwoCaptchaApiKey { get; set; } = string.Empty; + public string CustomTwoCaptchaHost { get; set; } = string.Empty; + public int CustomTwoCaptchaPort { get; set; } = 80; + public bool CustomTwoCaptchaOverrideHostHeader { get; set; } = true; + public string DeathByCaptchaUsername { get; set; } = string.Empty; + public string DeathByCaptchaPassword { get; set; } = string.Empty; + public string DeCaptcherApiKey { get; set; } = string.Empty; + public string ImageTyperzApiKey { get; set; } = string.Empty; + public string CapMonsterHost { get; set; } = string.Empty; + public int CapMonsterPort { get; set; } = 80; + public string AZCaptchaApiKey { get; set; } = string.Empty; + public string CaptchasIOApiKey { get; set; } = string.Empty; + public string RuCaptchaApiKey { get; set; } = string.Empty; + public string SolveCaptchaApiKey { get; set; } = string.Empty; + public string SolveRecaptchaApiKey { get; set; } = string.Empty; + public string TrueCaptchaApiKey { get; set; } = string.Empty; + public string TrueCaptchaUsername { get; set; } = string.Empty; + public string NineKWApiKey { get; set; } = string.Empty; + public string AnyCaptchaApiKey { get; set; } = string.Empty; + public string CapSolverApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs index 12aa037..ec26deb 100644 --- a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs @@ -2,29 +2,27 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class DeathByCaptchaFixture : ServiceFixture { - public class DeathByCaptchaFixture : ServiceFixture + public DeathByCaptchaFixture() { - public DeathByCaptchaFixture() - { - Service = new DeathByCaptchaService( - Config.Credentials.DeathByCaptchaUsername, - Config.Credentials.DeathByCaptchaPassword); - } + Service = new DeathByCaptchaService( + Config.Credentials.DeathByCaptchaUsername, + Config.Credentials.DeathByCaptchaPassword); } +} - public class DeathByCaptchaServiceTests : ServiceTests, IClassFixture - { - public DeathByCaptchaServiceTests(DeathByCaptchaFixture fixture) : base(fixture) { } - - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); - [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); - [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); - [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); - } -} \ No newline at end of file +public class DeathByCaptchaServiceTests(DeathByCaptchaFixture fixture) + : ServiceTests(fixture), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); +} diff --git a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs index 0b4323e..ee493e8 100644 --- a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs +++ b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs @@ -2,30 +2,29 @@ using System.Threading.Tasks; using Xunit; -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public class ImageTyperzFixture : ServiceFixture { - public class ImageTyperzFixture : ServiceFixture + public ImageTyperzFixture() { - public ImageTyperzFixture() - { - Service = new ImageTyperzService(Config.Credentials.ImageTyperzApiKey); - } + Service = new ImageTyperzService(Config.Credentials.ImageTyperzApiKey); } +} - public class ImageTyperzServiceTests(ImageTyperzFixture fixture) - : ServiceTests(fixture), IClassFixture - { - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); - [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); - [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); - [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); - [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); - [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); - [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); - [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); - } +public class ImageTyperzServiceTests(ImageTyperzFixture fixture) + : ServiceTests(fixture), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); + [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/ServiceFixture.cs b/CaptchaSharp.Tests/ServiceFixture.cs index 888ea6c..11dc466 100644 --- a/CaptchaSharp.Tests/ServiceFixture.cs +++ b/CaptchaSharp.Tests/ServiceFixture.cs @@ -1,14 +1,13 @@ -namespace CaptchaSharp.Tests +namespace CaptchaSharp.Tests; + +public abstract class ServiceFixture { - public abstract class ServiceFixture - { - private readonly ConfigFixture configFixture; - public Config Config => configFixture.Config; - public CaptchaService Service { get; set; } + private readonly ConfigFixture configFixture; + public Config Config => configFixture.Config; + public CaptchaService Service { get; set; } - public ServiceFixture() - { - configFixture = new ConfigFixture(); - } + public ServiceFixture() + { + configFixture = new ConfigFixture(); } } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 6a86504..85903ad 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -741,6 +741,11 @@ The captcha id which is needed to report the solution as bad. + + + Whether the captcha id is a long int or a string. + + The captcha id which is needed to report the solution, if it's @@ -1108,7 +1113,7 @@ - The service provided by https://de-captcher.com/ + The service provided by https://captchacoder.com/ diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index 07c501a..1623b5f 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -41,7 +41,6 @@ public ImageTyperzService(string apiKey, HttpClient? httpClient = null) ApiKey = apiKey; this._httpClient = httpClient ?? new HttpClient(); - // TODO: Use https instead of http if possible this._httpClient.BaseAddress = new Uri("http://captchatypers.com"); // Since this service replies directly with the solution to the task creation request (for image captchas) From 872352a2b86429ce13e90ebb1ec6cab9a05ba7b3 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 18:24:57 +0200 Subject: [PATCH 11/67] Fixed ImageTyperz, added new endpoints and other refactors --- CaptchaSharp.Tests/ImageTyperzServiceTests.cs | 10 + CaptchaSharp.Tests/ServiceTests.cs | 6 +- CaptchaSharp/CaptchaService.cs | 6 +- CaptchaSharp/CaptchaSharp.xml | 43 ++- CaptchaSharp/Models/CaptchaTask.cs | 2 +- CaptchaSharp/Services/AntiCaptchaService.cs | 4 +- CaptchaSharp/Services/CapSolverService.cs | 4 +- .../ImageTyperz/ImageTyperzResponse.cs | 27 ++ .../ImageTyperzTaskCreatedResponse.cs | 12 + CaptchaSharp/Services/ImageTyperzService.cs | 279 +++++++++++------- CaptchaSharp/Services/TwoCaptchaService.cs | 4 +- 11 files changed, 275 insertions(+), 122 deletions(-) create mode 100644 CaptchaSharp/Services/ImageTyperz/ImageTyperzResponse.cs create mode 100644 CaptchaSharp/Services/ImageTyperz/ImageTyperzTaskCreatedResponse.cs diff --git a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs index ee493e8..e0306eb 100644 --- a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs +++ b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs @@ -19,12 +19,22 @@ public class ImageTyperzServiceTests(ImageTyperzFixture fixture) [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index 2e8a91f..a6a75cb 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -250,14 +250,14 @@ private async Task GeeTestTest(Proxy proxy) var pageSource = await response.Content.ReadAsStringAsync(); var obj = JObject.Parse(pageSource); - var gt = obj.Value("gt"); - var challenge = obj.Value("challenge"); + var gt = obj.Value("gt")!; + var challenge = obj.Value("challenge")!; var solution = await Service.SolveGeeTestAsync( gt, challenge, - apiServer: "api.geetest.com", siteUrl, + apiServer: "api.geetest.com", proxy); Assert.NotEqual("", solution.Challenge); diff --git a/CaptchaSharp/CaptchaService.cs b/CaptchaSharp/CaptchaService.cs index b329b72..873c741 100644 --- a/CaptchaSharp/CaptchaService.cs +++ b/CaptchaSharp/CaptchaService.cs @@ -146,7 +146,7 @@ public virtual Task SolveRecaptchaV3Async( throw new NotSupportedException(); } - /// Solves a FunCaptcha. + /// Solves a FunCaptcha (Arkose Labs). /// /// /// Can be found inside data-pkey parameter of funcaptcha's div element or inside an input element @@ -275,8 +275,8 @@ public virtual Task SolveKeyCaptchaAsync( /// /// public virtual Task SolveGeeTestAsync( - string gt, string challenge, string apiServer, string siteUrl, Proxy? proxy = null, - CancellationToken cancellationToken = default) + string gt, string challenge, string siteUrl, string? apiServer = null, + Proxy? proxy = null, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 85903ad..c4f22c7 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -120,7 +120,7 @@ - Solves a FunCaptcha. + Solves a FunCaptcha (Arkose Labs). Can be found inside data-pkey parameter of funcaptcha's div element or inside an input element @@ -1187,6 +1187,9 @@ + + + @@ -1196,6 +1199,9 @@ + + + @@ -1205,6 +1211,41 @@ + + + The response for a captcha task. + + + + + The captcha id. + + + + + The response (if solved). + + + + + The status of the task. + + + + + The error message (if any). + + + + + The response for a captcha task after it's created. + + + + + The captcha id. + + The service provided by https://www.9kw.eu/ diff --git a/CaptchaSharp/Models/CaptchaTask.cs b/CaptchaSharp/Models/CaptchaTask.cs index 5806904..f941373 100644 --- a/CaptchaSharp/Models/CaptchaTask.cs +++ b/CaptchaSharp/Models/CaptchaTask.cs @@ -26,7 +26,7 @@ public CaptchaTask(string id, CaptchaType type) { IdString = id; - if (long.TryParse(id, out long parsed)) + if (long.TryParse(id, out var parsed)) { Id = parsed; } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index 032a987..141acc5 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -280,8 +280,8 @@ public override async Task SolveHCaptchaAsync( /// public override async Task SolveGeeTestAsync( - string gt, string challenge, string apiServer, string siteUrl, Proxy? proxy = null, - CancellationToken cancellationToken = default) + string gt, string challenge, string siteUrl, string? apiServer = null, + Proxy? proxy = null, CancellationToken cancellationToken = default) { var content = CreateTaskRequest(); diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index eefaa86..5c6b793 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -288,8 +288,8 @@ public override async Task SolveHCaptchaAsync( /// public override async Task SolveGeeTestAsync( - string gt, string challenge, string apiServer, string siteUrl, Proxy? proxy = null, - CancellationToken cancellationToken = default) + string gt, string challenge, string siteUrl, string? apiServer = null, + Proxy? proxy = null, CancellationToken cancellationToken = default) { var content = CreateTaskRequest(); diff --git a/CaptchaSharp/Services/ImageTyperz/ImageTyperzResponse.cs b/CaptchaSharp/Services/ImageTyperz/ImageTyperzResponse.cs new file mode 100644 index 0000000..32545ec --- /dev/null +++ b/CaptchaSharp/Services/ImageTyperz/ImageTyperzResponse.cs @@ -0,0 +1,27 @@ +namespace CaptchaSharp.Services.ImageTyperz; + +/// +/// The response for a captcha task. +/// +public class ImageTyperzResponse +{ + /// + /// The captcha id. + /// + public required long CaptchaId { get; set; } + + /// + /// The response (if solved). + /// + public required string Response { get; set; } + + /// + /// The status of the task. + /// + public required string Status { get; set; } + + /// + /// The error message (if any). + /// + public required string Error { get; set; } +} diff --git a/CaptchaSharp/Services/ImageTyperz/ImageTyperzTaskCreatedResponse.cs b/CaptchaSharp/Services/ImageTyperz/ImageTyperzTaskCreatedResponse.cs new file mode 100644 index 0000000..a483e44 --- /dev/null +++ b/CaptchaSharp/Services/ImageTyperz/ImageTyperzTaskCreatedResponse.cs @@ -0,0 +1,12 @@ +namespace CaptchaSharp.Services.ImageTyperz; + +/// +/// The response for a captcha task after it's created. +/// +public class ImageTyperzTaskCreatedResponse +{ + /// + /// The captcha id. + /// + public required long CaptchaId { get; set; } +} diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index 1623b5f..6fa2d0f 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -8,6 +8,8 @@ using System.Threading; using System.Threading.Tasks; using CaptchaSharp.Extensions; +using CaptchaSharp.Services.ImageTyperz; +using Newtonsoft.Json.Linq; namespace CaptchaSharp.Services; @@ -29,7 +31,7 @@ public class ImageTyperzService : CaptchaService /// /// The ID of the software developer. /// - private const int _affiliateId = 671869; + private const int _affiliateId = 109; /// /// Initializes a . @@ -52,15 +54,17 @@ public ImageTyperzService(string apiKey, HttpClient? httpClient = null) /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync - ("Forms/RequestBalanceToken.ashx", - GetAuthPair() - .Add("action", "REQUESTBALANCE"), - cancellationToken: cancellationToken) + var response = await _httpClient.PostToStringAsync( + "Forms/RequestBalanceToken.ashx", + GetAuthPair() + .Add("action", "REQUESTBALANCE"), + cancellationToken: cancellationToken) .ConfigureAwait(false); if (IsError(response)) + { throw new BadAuthenticationException(GetErrorMessage(response)); + } return decimal.Parse(response, CultureInfo.InvariantCulture); } @@ -71,17 +75,19 @@ public override async Task GetBalanceAsync(CancellationToken cancellati public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync - ("Forms/UploadFileAndGetTextNEWToken.ashx", - GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("file", base64) - .Add(ConvertCapabilities(options)), - cancellationToken: cancellationToken) + var response = await _httpClient.PostToStringAsync( + "Forms/UploadFileAndGetTextNEWToken.ashx", + GetAuthAffiliatePair() + .Add("action", "UPLOADCAPTCHA") + .Add("file", base64) + .Add(ConvertCapabilities(options)), + cancellationToken: cancellationToken) .ConfigureAwait(false); if (IsError(response)) + { throw new TaskSolutionException(GetErrorMessage(response)); + } var split = response.Split(['|'], 2); return new StringResponse { Id = long.Parse(split[0]), Response = split[1] }; @@ -92,17 +98,17 @@ public override async Task SolveRecaptchaV2Async( string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync - (enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", - GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", siteUrl) - .Add("googlekey", siteKey) - .Add("recaptchatype", invisible ? 2 : 1, !enterprise) - .Add("enterprise_type", "v2", enterprise) - .Add("data-s", dataS, !string.IsNullOrEmpty(dataS)) - .Add(GetProxyParams(proxy)), - cancellationToken: cancellationToken) + var response = await _httpClient.PostToStringAsync( + enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", + GetAuthAffiliatePair() + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", siteUrl) + .Add("googlekey", siteKey) + .Add("recaptchatype", invisible ? 2 : 1, !enterprise) + .Add("enterprise_type", "v2", enterprise) + .Add("data-s", dataS, !string.IsNullOrEmpty(dataS)) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult(response, CaptchaType.ReCaptchaV2, cancellationToken); @@ -113,37 +119,59 @@ public override async Task SolveRecaptchaV3Async (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync - (enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", - GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", siteUrl) - .Add("googlekey", siteKey) - .Add("captchaaction", action) - .Add("score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) - .Add("recaptchatype", 3, !enterprise) - .Add("enterprise_type", "v3", enterprise) - .Add(GetProxyParams(proxy)), - cancellationToken: cancellationToken) + var response = await _httpClient.PostToStringAsync( + enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", + GetAuthAffiliatePair() + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", siteUrl) + .Add("googlekey", siteKey) + .Add("captchaaction", action) + .Add("score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) + .Add("recaptchatype", 3, !enterprise) + .Add("enterprise_type", "v3", enterprise) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( response, CaptchaType.ReCaptchaV2, cancellationToken); } + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync( + "captchaapi/UploadFunCaptcha.ashx", + GetAuthAffiliatePair() + .Add("action", "UPLOADCAPTCHA") + .Add("captchatype", 13) + .Add("pageurl", siteUrl) + .Add("sitekey", publicKey) + .Add("s_url", serviceUrl) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.FunCaptcha, cancellationToken); + } + /// public override async Task SolveHCaptchaAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync - ("captchaapi/UploadRecaptchaToken.ashx", - GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", $"{siteUrl}--hcaptcha") - .Add("googlekey", $"{siteKey}--hcaptcha") - .Add(GetProxyParams(proxy)), - cancellationToken: cancellationToken) + var response = await _httpClient.PostToStringAsync( + "captchaapi/UploadHCaptchaUser.ashx", + GetAuthAffiliatePair() + .Add("captchatype", 11) + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", siteUrl) + .Add("sitekey", siteKey) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( @@ -152,17 +180,19 @@ public override async Task SolveHCaptchaAsync( /// public override async Task SolveGeeTestAsync( - string gt, string challenge, string apiServer, string siteUrl, + string gt, string challenge, string siteUrl, string? apiServer = null, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("captchaapi/UploadGeeTestToken.ashx", - GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("gt", gt) - .Add("challenge", challenge) - .Add("domain", siteUrl), - cancellationToken) + var response = await _httpClient.GetStringAsync( + "captchaapi/UploadGeeTestToken.ashx", + GetAuthAffiliatePair() + .Add("action", "UPLOADCAPTCHA") + .Add("gt", gt) + .Add("challenge", challenge) + .Add("api_server", apiServer!, !string.IsNullOrEmpty(apiServer)) + .Add("domain", siteUrl) + .Add(GetProxyParams(proxy)), + cancellationToken) .ConfigureAwait(false); return await GetResult( @@ -174,20 +204,42 @@ public override async Task SolveCapyAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync - ("captchaapi/UploadRecaptchaToken.ashx", - GetAuthAffiliatePair() - .Add("action", "UPLOADCAPTCHA") - .Add("pageurl", $"{siteUrl}--capy") - .Add("googlekey", $"{siteKey}--capy") - .Add(GetProxyParams(proxy)), - cancellationToken: cancellationToken) + var response = await _httpClient.PostToStringAsync( + "captchaapi/UploadCapyCaptchaUser.ashx", + GetAuthAffiliatePair() + .Add("action", "UPLOADCAPTCHA") + .Add("captchatype", 12) + .Add("pageurl", siteUrl) + .Add("sitekey", siteKey) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) .ConfigureAwait(false); - // TODO: Check this, the get result method is not implemented for Capy return await GetResult( response, CaptchaType.Capy, cancellationToken); } + + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostToStringAsync( + "captchaapi/Uploadturnstile.ashx", + GetAuthAffiliatePair() + .Add("action", "UPLOADCAPTCHA") + .Add("pageurl", siteUrl) + .Add("sitekey", siteKey) + .Add("taction", action!, !string.IsNullOrEmpty(action)) + .Add("data", data!, !string.IsNullOrEmpty(data)) + .Add(GetProxyParams(proxy)), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.CloudflareTurnstile, cancellationToken); + } + #endregion #region Getting the result @@ -196,7 +248,16 @@ private async Task GetResult( where T : CaptchaResponse { if (IsError(response)) + { throw new TaskCreationException(response); + } + + // If the response starts with a [, it's a JSON array + if (response.StartsWith('[')) + { + var responses = response.Deserialize(); + response = responses[0].CaptchaId.ToString(); + } var task = new CaptchaTask(response, type); @@ -208,39 +269,33 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - string response; - - if (task.Type == CaptchaType.GeeTest) - { - response = await _httpClient.GetStringAsync - ("captchaapi/getrecaptchatext.ashx", - GetAuthPair() - .Add("action", "GETTEXT") - .Add("captchaID", task.Id), - cancellationToken) - .ConfigureAwait(false); - } - else + var responseJson = await _httpClient.GetStringAsync( + "captchaapi/GetCaptchaResponseJson.ashx", + GetAuthPair() + .Add("action", "GETTEXT") + .Add("captchaid", task.Id), + cancellationToken) + .ConfigureAwait(false); + + if (string.IsNullOrEmpty(responseJson)) { - response = await _httpClient.PostToStringAsync - ("captchaapi/GetRecaptchaTextToken.ashx", - GetAuthPair() - .Add("action", "GETTEXT") - .Add("captchaID", task.Id), - cancellationToken: cancellationToken) - .ConfigureAwait(false); + throw new TaskSolutionException("Could not get the solution for the task"); } + + // For some reason, the response is an array with a single element + var responses = responseJson.Deserialize(); + var response = responses[0]; - if (response.Contains("NOT_DECODED")) + if (response.Status == "Pending") { - return default; + return null; } task.Completed = true; - if (IsError(response)) + if (response.Status != "Solved") { - throw new TaskSolutionException(response); + throw new TaskSolutionException(response.Error); } // GeeTestResponse needs GeeTest captcha type @@ -250,14 +305,15 @@ private async Task GetResult( { throw new TaskSolutionException("The task is not a GeeTest captcha"); } - - var split = response.Split([";;;"], 3, StringSplitOptions.None); + + var geeTestResponseJson = response.Response; + var geeTestResponse = JObject.Parse(geeTestResponseJson); return new GeeTestResponse { - Challenge = split[0], - Validate = split[1], - SecCode = split[2] + Challenge = geeTestResponse["geetest_challenge"]!.Value()!, + Validate = geeTestResponse["geetest_validate"]!.Value()!, + SecCode = geeTestResponse["geetest_seccode"]!.Value()! } as T; } @@ -267,7 +323,7 @@ private async Task GetResult( throw new NotSupportedException("Only StringResponse and GeeTestResponse are supported"); } - return new StringResponse { Id = task.Id, Response = response } as T; + return new StringResponse { Id = task.Id, Response = response.Response } as T; } #endregion @@ -276,12 +332,12 @@ private async Task GetResult( public override async Task ReportSolution (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync - ("Forms/SetBadImageToken.ashx", - GetAuthPair() - .Add("imageid", id) - .Add("action", "SETBADIMAGE"), - cancellationToken: cancellationToken) + var response = await _httpClient.PostToStringAsync( + "Forms/SetBadImageToken.ashx", + GetAuthPair() + .Add("imageid", id) + .Add("action", "SETBADIMAGE"), + cancellationToken: cancellationToken) .ConfigureAwait(false); if (response != "SUCCESS") @@ -296,7 +352,7 @@ private StringPairCollection GetAuthPair() private StringPairCollection GetAuthAffiliatePair() => GetAuthPair().Add("affiliateid", _affiliateId); - private static bool IsError(string response) + private static bool IsError(string response) => response.StartsWith("ERROR:"); private static string GetErrorMessage(string response) @@ -309,23 +365,30 @@ private static string GetErrorMessage(string response) return []; } - if (proxy.Type != ProxyType.HTTP && proxy.Type != ProxyType.HTTPS) + var proxyPairs = new List<(string, string)>(); + + if (proxy.UserAgent is not null) { - throw new NotSupportedException("The api only supports HTTP proxies"); + proxyPairs.Add(("useragent", proxy.UserAgent)); } - var proxyPairs = new List<(string, string)> + if (string.IsNullOrEmpty(proxy.Host)) { + return proxyPairs; + } + + if (proxy.Type != ProxyType.HTTP && proxy.Type != ProxyType.HTTPS) + { + throw new NotSupportedException("The api only supports HTTP proxies"); + } + + proxyPairs.AddRange( + [ ("proxytype", "HTTP"), proxy.RequiresAuthentication ? ("proxy", $"{proxy.Host}:{proxy.Port}:{proxy.Username}:{proxy.Password}") : ("proxy", $"{proxy.Host}:{proxy.Port}") - }; - - if (proxy.UserAgent is not null) - { - proxyPairs.Add(("useragent", proxy.UserAgent)); - } + ]); return proxyPairs; } diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 4af361d..b4ca879 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -293,7 +293,7 @@ public override async Task SolveKeyCaptchaAsync( /// public override async Task SolveGeeTestAsync( - string gt, string challenge, string apiServer, string siteUrl, + string gt, string challenge, string siteUrl, string? apiServer = null, Proxy? proxy = null, CancellationToken cancellationToken = default) { var response = await HttpClient.PostMultipartToStringAsync("in.php", @@ -302,7 +302,7 @@ public override async Task SolveGeeTestAsync( .Add("method", "geetest") .Add("gt", gt) .Add("challenge", challenge) - .Add("api_server", apiServer) + .Add("api_server", apiServer!, !string.IsNullOrEmpty(apiServer)) .Add("pageurl", siteUrl) .Add("soft_id", _softId) .Add("json", "1", UseJsonFlag) From 5ab08e6b63117226cc13a158f4df0cb9617c5371 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 18:29:57 +0200 Subject: [PATCH 12/67] Removed CaptchaSharp.Services.More project and moved services over --- .../CaptchaSharp.Services.More.csproj | 28 ------------------- CaptchaSharp.Tests/AnyCaptchaTests.cs | 6 ++-- CaptchaSharp.Tests/CaptchaSharp.Tests.csproj | 1 - CaptchaSharp.Tests/TrueCaptchaTests.cs | 2 +- CaptchaSharp.sln | 6 ---- .../Services}/AnyCaptchaService.cs | 2 +- .../Services}/AzCaptchaService.cs | 2 +- .../Services}/CaptchasIOService.cs | 2 +- CaptchaSharp/Services/ImageTyperzService.cs | 4 +++ .../Services}/RuCaptchaService.cs | 2 +- .../Services}/SolveCaptchaService.cs | 2 +- .../Services}/SolveRecaptchaService.cs | 2 +- .../Services}/TrueCaptchaService.cs | 2 +- 13 files changed, 14 insertions(+), 47 deletions(-) delete mode 100644 CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj rename {CaptchaSharp.Services.More => CaptchaSharp/Services}/AnyCaptchaService.cs (95%) rename {CaptchaSharp.Services.More => CaptchaSharp/Services}/AzCaptchaService.cs (95%) rename {CaptchaSharp.Services.More => CaptchaSharp/Services}/CaptchasIOService.cs (95%) rename {CaptchaSharp.Services.More => CaptchaSharp/Services}/RuCaptchaService.cs (94%) rename {CaptchaSharp.Services.More => CaptchaSharp/Services}/SolveCaptchaService.cs (95%) rename {CaptchaSharp.Services.More => CaptchaSharp/Services}/SolveRecaptchaService.cs (99%) rename {CaptchaSharp.Services.More => CaptchaSharp/Services}/TrueCaptchaService.cs (99%) diff --git a/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj b/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj deleted file mode 100644 index a26a420..0000000 --- a/CaptchaSharp.Services.More/CaptchaSharp.Services.More.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net8.0 - Adds additional services to CaptchaSharp. - MIT - Ruri - Ruri - https://github.com/openbullet/CaptchaSharp - Captcha, Solver, Service, API, 2Captcha, TwoCaptcha, AntiCaptcha, Anti-Captcha, DeathByCaptcha, DBC, DeCaptcher, ImageTyperz - https://github.com/openbullet/CaptchaSharp - 1.0.6 - True - enable - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/CaptchaSharp.Tests/AnyCaptchaTests.cs b/CaptchaSharp.Tests/AnyCaptchaTests.cs index 732ba6e..0ab410c 100644 --- a/CaptchaSharp.Tests/AnyCaptchaTests.cs +++ b/CaptchaSharp.Tests/AnyCaptchaTests.cs @@ -1,4 +1,4 @@ -using CaptchaSharp.Services.More; +using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; @@ -12,10 +12,8 @@ public AnyCaptchaFixture() } } -public class AnyCaptchaServiceTests : ServiceTests, IClassFixture +public class AnyCaptchaServiceTests(AnyCaptchaFixture fixture) : ServiceTests(fixture), IClassFixture { - public AnyCaptchaServiceTests(AnyCaptchaFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); diff --git a/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj b/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj index e641cb6..194ab36 100644 --- a/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj +++ b/CaptchaSharp.Tests/CaptchaSharp.Tests.csproj @@ -24,7 +24,6 @@ - diff --git a/CaptchaSharp.Tests/TrueCaptchaTests.cs b/CaptchaSharp.Tests/TrueCaptchaTests.cs index a643357..a9202db 100644 --- a/CaptchaSharp.Tests/TrueCaptchaTests.cs +++ b/CaptchaSharp.Tests/TrueCaptchaTests.cs @@ -1,4 +1,4 @@ -using CaptchaSharp.Services.More; +using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; diff --git a/CaptchaSharp.sln b/CaptchaSharp.sln index 9aac625..df28c26 100644 --- a/CaptchaSharp.sln +++ b/CaptchaSharp.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CaptchaSharp", "CaptchaSharp\CaptchaSharp.csproj", "{CAB6F51C-5D7F-40F3-AF14-BC2FD6FB40D0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CaptchaSharp.Services.More", "CaptchaSharp.Services.More\CaptchaSharp.Services.More.csproj", "{16E252FC-B2C6-40C9-9330-809BD84A2941}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CaptchaSharp.Tests", "CaptchaSharp.Tests\CaptchaSharp.Tests.csproj", "{235EEDF1-6B76-46A7-8B9E-AC850A02D017}" EndProject Global @@ -19,10 +17,6 @@ Global {CAB6F51C-5D7F-40F3-AF14-BC2FD6FB40D0}.Debug|Any CPU.Build.0 = Debug|Any CPU {CAB6F51C-5D7F-40F3-AF14-BC2FD6FB40D0}.Release|Any CPU.ActiveCfg = Release|Any CPU {CAB6F51C-5D7F-40F3-AF14-BC2FD6FB40D0}.Release|Any CPU.Build.0 = Release|Any CPU - {16E252FC-B2C6-40C9-9330-809BD84A2941}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {16E252FC-B2C6-40C9-9330-809BD84A2941}.Debug|Any CPU.Build.0 = Debug|Any CPU - {16E252FC-B2C6-40C9-9330-809BD84A2941}.Release|Any CPU.ActiveCfg = Release|Any CPU - {16E252FC-B2C6-40C9-9330-809BD84A2941}.Release|Any CPU.Build.0 = Release|Any CPU {235EEDF1-6B76-46A7-8B9E-AC850A02D017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {235EEDF1-6B76-46A7-8B9E-AC850A02D017}.Debug|Any CPU.Build.0 = Debug|Any CPU {235EEDF1-6B76-46A7-8B9E-AC850A02D017}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/CaptchaSharp.Services.More/AnyCaptchaService.cs b/CaptchaSharp/Services/AnyCaptchaService.cs similarity index 95% rename from CaptchaSharp.Services.More/AnyCaptchaService.cs rename to CaptchaSharp/Services/AnyCaptchaService.cs index 672a618..98eeef0 100644 --- a/CaptchaSharp.Services.More/AnyCaptchaService.cs +++ b/CaptchaSharp/Services/AnyCaptchaService.cs @@ -2,7 +2,7 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More; +namespace CaptchaSharp.Services; /// /// The service provided by https://anycaptcha.com/ diff --git a/CaptchaSharp.Services.More/AzCaptchaService.cs b/CaptchaSharp/Services/AzCaptchaService.cs similarity index 95% rename from CaptchaSharp.Services.More/AzCaptchaService.cs rename to CaptchaSharp/Services/AzCaptchaService.cs index 5f3e878..ec31042 100644 --- a/CaptchaSharp.Services.More/AzCaptchaService.cs +++ b/CaptchaSharp/Services/AzCaptchaService.cs @@ -2,7 +2,7 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More; +namespace CaptchaSharp.Services; /// /// The service provided by https://azcaptcha.com/ diff --git a/CaptchaSharp.Services.More/CaptchasIOService.cs b/CaptchaSharp/Services/CaptchasIOService.cs similarity index 95% rename from CaptchaSharp.Services.More/CaptchasIOService.cs rename to CaptchaSharp/Services/CaptchasIOService.cs index d70470a..c9b5489 100644 --- a/CaptchaSharp.Services.More/CaptchasIOService.cs +++ b/CaptchaSharp/Services/CaptchasIOService.cs @@ -2,7 +2,7 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More; +namespace CaptchaSharp.Services; /// /// The service provided by https://captchas.io/ diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index 6fa2d0f..ab2417e 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -316,6 +316,8 @@ private async Task GetResult( SecCode = geeTestResponse["geetest_seccode"]!.Value()! } as T; } + + // TODO: Handle Capy response // If it's not a StringResponse, throw if (typeof(T) != typeof(StringResponse)) @@ -341,7 +343,9 @@ public override async Task ReportSolution .ConfigureAwait(false); if (response != "SUCCESS") + { throw new TaskReportException(response); + } } #endregion diff --git a/CaptchaSharp.Services.More/RuCaptchaService.cs b/CaptchaSharp/Services/RuCaptchaService.cs similarity index 94% rename from CaptchaSharp.Services.More/RuCaptchaService.cs rename to CaptchaSharp/Services/RuCaptchaService.cs index f90e422..cd97d21 100644 --- a/CaptchaSharp.Services.More/RuCaptchaService.cs +++ b/CaptchaSharp/Services/RuCaptchaService.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; -namespace CaptchaSharp.Services.More; +namespace CaptchaSharp.Services; /// /// The service provided by https://rucaptcha.com/ diff --git a/CaptchaSharp.Services.More/SolveCaptchaService.cs b/CaptchaSharp/Services/SolveCaptchaService.cs similarity index 95% rename from CaptchaSharp.Services.More/SolveCaptchaService.cs rename to CaptchaSharp/Services/SolveCaptchaService.cs index 302a994..82e3ea3 100644 --- a/CaptchaSharp.Services.More/SolveCaptchaService.cs +++ b/CaptchaSharp/Services/SolveCaptchaService.cs @@ -2,7 +2,7 @@ using CaptchaSharp.Enums; using System.Net.Http; -namespace CaptchaSharp.Services.More; +namespace CaptchaSharp.Services; /// /// The service provided by https://solvecaptcha.com/ diff --git a/CaptchaSharp.Services.More/SolveRecaptchaService.cs b/CaptchaSharp/Services/SolveRecaptchaService.cs similarity index 99% rename from CaptchaSharp.Services.More/SolveRecaptchaService.cs rename to CaptchaSharp/Services/SolveRecaptchaService.cs index fd15940..1fac81f 100644 --- a/CaptchaSharp.Services.More/SolveRecaptchaService.cs +++ b/CaptchaSharp/Services/SolveRecaptchaService.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services.More; +namespace CaptchaSharp.Services; /// /// The service provided by https://solverecaptcha.com/ diff --git a/CaptchaSharp.Services.More/TrueCaptchaService.cs b/CaptchaSharp/Services/TrueCaptchaService.cs similarity index 99% rename from CaptchaSharp.Services.More/TrueCaptchaService.cs rename to CaptchaSharp/Services/TrueCaptchaService.cs index 66701ea..44de92b 100644 --- a/CaptchaSharp.Services.More/TrueCaptchaService.cs +++ b/CaptchaSharp/Services/TrueCaptchaService.cs @@ -9,7 +9,7 @@ using CaptchaSharp.Enums; using CaptchaSharp.Extensions; -namespace CaptchaSharp.Services.More; +namespace CaptchaSharp.Services; /// /// The service provided by https://apitruecaptcha.org/ From 77a8dba0a22cf0cfba54e69644e458fa708ab786 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 18:31:24 +0200 Subject: [PATCH 13/67] Removed AnyCaptcha (out of business) --- CaptchaSharp.Tests/AnyCaptchaTests.cs | 23 ---- CaptchaSharp/CaptchaSharp.xml | 124 +++++++++++++++++++++ CaptchaSharp/Services/AnyCaptchaService.cs | 27 ----- 3 files changed, 124 insertions(+), 50 deletions(-) delete mode 100644 CaptchaSharp.Tests/AnyCaptchaTests.cs delete mode 100644 CaptchaSharp/Services/AnyCaptchaService.cs diff --git a/CaptchaSharp.Tests/AnyCaptchaTests.cs b/CaptchaSharp.Tests/AnyCaptchaTests.cs deleted file mode 100644 index 0ab410c..0000000 --- a/CaptchaSharp.Tests/AnyCaptchaTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using CaptchaSharp.Services; -using System.Threading.Tasks; -using Xunit; - -namespace CaptchaSharp.Tests; - -public class AnyCaptchaFixture : ServiceFixture -{ - public AnyCaptchaFixture() - { - Service = new AnyCaptchaService(Config.Credentials.AnyCaptchaApiKey); - } -} - -public class AnyCaptchaServiceTests(AnyCaptchaFixture fixture) : ServiceTests(fixture), IClassFixture -{ - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); - [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); - [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); -} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index c4f22c7..0f332f9 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -958,6 +958,30 @@ + + + The service provided by https://anycaptcha.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + The service provided by https://azcaptcha.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + The service provided by the CapMonster OCR application by ZennoLab. @@ -1029,6 +1053,18 @@ + + + The service provided by https://captchas.io/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + The service provided by a service that implements the anti-captcha API. @@ -1306,6 +1342,94 @@ + + + The service provided by https://rucaptcha.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + The service provided by https://solvecaptcha.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + The service provided by https://solverecaptcha.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + + + + + + + + + + + + + The service provided by https://apitruecaptcha.org/ + + + + + Your user id. + + + + + Your secret api key. + + + + + The default used for requests. + + + + + Initializes a . + + Your user id. + Your secret api key. + The to use for requests. If null, a default one will be created. + + + + + + + + + + + + + The service provided by https://2captcha.com/ diff --git a/CaptchaSharp/Services/AnyCaptchaService.cs b/CaptchaSharp/Services/AnyCaptchaService.cs deleted file mode 100644 index 98eeef0..0000000 --- a/CaptchaSharp/Services/AnyCaptchaService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using CaptchaSharp.Enums; -using System; -using System.Net.Http; - -namespace CaptchaSharp.Services; - -/// -/// The service provided by https://anycaptcha.com/ -/// -public class AnyCaptchaService : CustomAntiCaptchaService -{ - /// - /// Initializes a . - /// - /// The API key to use. - /// The to use for requests. If null, a default one will be created. - public AnyCaptchaService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("https://api.anycaptcha.com"), httpClient) - { - SupportedCaptchaTypes = - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3 | - CaptchaType.FunCaptcha | - CaptchaType.HCaptcha; - } -} From dfc91f3410e8a672913fa2e796137d0dcd20f79f Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 18:32:00 +0200 Subject: [PATCH 14/67] Cleanup --- CaptchaSharp.Tests/ConfigFixture.cs | 1 - CaptchaSharp/CaptchaSharp.xml | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 0ebaa9f..1e504cd 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -52,6 +52,5 @@ public class Credentials public string TrueCaptchaApiKey { get; set; } = string.Empty; public string TrueCaptchaUsername { get; set; } = string.Empty; public string NineKWApiKey { get; set; } = string.Empty; - public string AnyCaptchaApiKey { get; set; } = string.Empty; public string CapSolverApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 0f332f9..ed486b5 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -958,18 +958,6 @@ - - - The service provided by https://anycaptcha.com/ - - - - - Initializes a . - - The API key to use. - The to use for requests. If null, a default one will be created. - The service provided by https://azcaptcha.com/ From 471748398d2b8b3a7ff3326fb25e276efba14a25 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 19:26:00 +0200 Subject: [PATCH 15/67] Added AzCaptcha tests, outputs and other refactors --- CaptchaSharp.Tests/AntiCaptchaServiceTests.cs | 6 +- CaptchaSharp.Tests/AzCaptchaServiceTests.cs | 32 ++++++ CaptchaSharp.Tests/CapSolverServiceTests.cs | 6 +- CaptchaSharp.Tests/ConfigFixture.cs | 10 +- CaptchaSharp.Tests/DeCaptcherServiceTests.cs | 5 +- .../DeathByCaptchaServiceTests.cs | 5 +- CaptchaSharp.Tests/ImageTyperzServiceTests.cs | 5 +- CaptchaSharp.Tests/ServiceFixture.cs | 10 +- CaptchaSharp.Tests/ServiceTests.cs | 104 ++++++++++++------ CaptchaSharp.Tests/TrueCaptchaTests.cs | 5 +- CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 5 +- CaptchaSharp/Services/AzCaptchaService.cs | 2 + .../Services/CustomTwoCaptchaService.cs | 5 +- 13 files changed, 140 insertions(+), 60 deletions(-) create mode 100644 CaptchaSharp.Tests/AzCaptchaServiceTests.cs diff --git a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs index c56e52d..3ebd72e 100644 --- a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs @@ -1,6 +1,7 @@ using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; @@ -12,10 +13,9 @@ public AntiCaptchaFixture() } } -public class AntiCaptchaServiceTests : ServiceTests, IClassFixture +public class AntiCaptchaServiceTests(AntiCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { - public AntiCaptchaServiceTests(AntiCaptchaFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); diff --git a/CaptchaSharp.Tests/AzCaptchaServiceTests.cs b/CaptchaSharp.Tests/AzCaptchaServiceTests.cs new file mode 100644 index 0000000..8567219 --- /dev/null +++ b/CaptchaSharp.Tests/AzCaptchaServiceTests.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class AzCaptchaFixture : ServiceFixture +{ + public AzCaptchaFixture() + { + Service = new AzCaptchaService( + Config.Credentials.AzCaptchaApiKey); + } +} + +public class AzCaptchaServiceTests(AzCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveFunCaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFunCaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); +} diff --git a/CaptchaSharp.Tests/CapSolverServiceTests.cs b/CaptchaSharp.Tests/CapSolverServiceTests.cs index 4f6afa7..50bf24d 100644 --- a/CaptchaSharp.Tests/CapSolverServiceTests.cs +++ b/CaptchaSharp.Tests/CapSolverServiceTests.cs @@ -1,6 +1,7 @@ using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; @@ -12,10 +13,9 @@ public CapSolverFixture() } } -public class CapSolverServiceTests : ServiceTests, IClassFixture +public class CapSolverServiceTests(CapSolverFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { - public CapSolverServiceTests(CapSolverFixture fixture) : base(fixture) { } - [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 1e504cd..76cbcb8 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -6,20 +6,20 @@ namespace CaptchaSharp.Tests; public class ConfigFixture { - private readonly string credentialsFile = "config.json"; + private const string _credentialsFile = "config.json"; public Config Config { get; set; } public ConfigFixture() { - if (File.Exists(credentialsFile)) + if (File.Exists(_credentialsFile)) { - Config = JsonConvert.DeserializeObject(File.ReadAllText(credentialsFile)); + Config = JsonConvert.DeserializeObject(File.ReadAllText(_credentialsFile))!; } else { // Write a blank structure if it doesn't exist so that we don't have to manually create it from scratch Config = new Config(); - File.WriteAllText(credentialsFile, JsonConvert.SerializeObject(Config, Formatting.Indented)); + File.WriteAllText(_credentialsFile, JsonConvert.SerializeObject(Config, Formatting.Indented)); } } } @@ -44,7 +44,7 @@ public class Credentials public string ImageTyperzApiKey { get; set; } = string.Empty; public string CapMonsterHost { get; set; } = string.Empty; public int CapMonsterPort { get; set; } = 80; - public string AZCaptchaApiKey { get; set; } = string.Empty; + public string AzCaptchaApiKey { get; set; } = string.Empty; public string CaptchasIOApiKey { get; set; } = string.Empty; public string RuCaptchaApiKey { get; set; } = string.Empty; public string SolveCaptchaApiKey { get; set; } = string.Empty; diff --git a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs index 529808e..947c28e 100644 --- a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs +++ b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs @@ -1,6 +1,7 @@ using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; @@ -12,8 +13,8 @@ public DeCaptcherFixture() } } -public class DeCaptcherServiceTests(DeCaptcherFixture fixture) - : ServiceTests(fixture), IClassFixture +public class DeCaptcherServiceTests(DeCaptcherFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); diff --git a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs index ec26deb..f0cef9c 100644 --- a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs @@ -1,6 +1,7 @@ using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; @@ -14,8 +15,8 @@ public DeathByCaptchaFixture() } } -public class DeathByCaptchaServiceTests(DeathByCaptchaFixture fixture) - : ServiceTests(fixture), IClassFixture +public class DeathByCaptchaServiceTests(DeathByCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); diff --git a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs index e0306eb..f971b92 100644 --- a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs +++ b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs @@ -1,6 +1,7 @@ using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; @@ -12,8 +13,8 @@ public ImageTyperzFixture() } } -public class ImageTyperzServiceTests(ImageTyperzFixture fixture) - : ServiceTests(fixture), IClassFixture +public class ImageTyperzServiceTests(ImageTyperzFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); diff --git a/CaptchaSharp.Tests/ServiceFixture.cs b/CaptchaSharp.Tests/ServiceFixture.cs index 11dc466..61ca4ea 100644 --- a/CaptchaSharp.Tests/ServiceFixture.cs +++ b/CaptchaSharp.Tests/ServiceFixture.cs @@ -2,12 +2,12 @@ public abstract class ServiceFixture { - private readonly ConfigFixture configFixture; - public Config Config => configFixture.Config; - public CaptchaService Service { get; set; } + private readonly ConfigFixture _configFixture; + public Config Config => _configFixture.Config; + public CaptchaService Service { get; init; } - public ServiceFixture() + protected ServiceFixture() { - configFixture = new ConfigFixture(); + _configFixture = new ConfigFixture(); } } diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index a6a75cb..61de3b9 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -7,20 +7,23 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; public class ServiceTests { - private readonly ServiceFixture fixture; + private readonly ServiceFixture _fixture; + private readonly ITestOutputHelper _output; - private readonly string _captchaImageBase64 = - "iVBORw0KGgoAAAANSUhEUgAAAGsAAAAgCAYAAAAVIIajAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAA5/SURBVGhD7Zr3k1VVEsdh8gAlOStLWiwyKLAqsErQMkIVSyqhFAa0VBBYVl1BESUskraUDGK5FpKHXCRByZlVgkOGIQ6TB/6C3vPp9/runTtvHqjgLhY/fOu9ubdvd5/+dvfpc9+UKLhZIPdxb+A+WfcQ7pP1GyD/Rn4hFNz47zX7Hum5IO46WebU7Tp0pxC0+b+yD3Lzc0PfCyIjryBPYfKR9IEiZPkfiPSg3VcEDEWS457nbEDmTsNs+P3KzctV+34/77YfwPPF4czZM7Ju3Trp07ePDBw4UD799FMZN26cvPf392T+/Ply5OgRycrKKhQnENRZiCwTsgBHWpz9fePGjRBu3igi45fLys6SH4/8qEFDn1/mTsLs5eTlhHxyhJ04dUK+3/a9HDx8UPLy3FrctVsljl0v7r4fpscP/73svGzZu3evVK5cWWJjYiU+Nl7iYuMkLsahZOgzpkSMPPDAA9KyRUvZtWuXl2h+XYaIZF26dEnOnz8vmVmZ3jW7jyICsnHjRnnuueekdevWcuTIkSIG7Ll33nlH4uLiZOvWreqI3b+TMFtgz949MnTYUGncuLEkJSZJyRIlNVCPP/64zJo9S86cORMxGH4d+KkIyARh8v49yJ7hkzjt3bdXCYotGauIKRnjQcmDOAfulS5dWkaNGiXXs65H9LEIWWTekKFDdIGdO3eW3NzCFaYyrkomTpwo8XHxUqpUKdm/f7/k5ReW0WfcggcMHCCJiYly6NAhdd5vKxr8fkWDyeP3gQMHpFy5chqIhLgELxAgPiZeSpQoIVWrVlXCrML8evA5IzND5s6dKz+l/aR/+235YTaPHz8ujz32mJw6dUrXZzr5pLIWLFigCUMFAWLRuEljeeihh5QcEjm2RIhII3PEiBERO1ERsgj64cOHlazWrVorWbkFoYXx8LWMa3Lh4gV55plnVDEGKOGr164WIkwNuYzr0aOHlC1bVs6ePVuoBXGfxXGN73zyN99Nxu9bcUCO5/Yf2C/ly5X3stTLWhcMgmXX+Xz22Wc1GP5KN7vTp01X+Z49ekatLvXR3Z8zZ47ExMQoKabD7mfnZsv6DetD1R0m4o033pDsnGy5fv26HDt+TLZs3SJdu3aVkiVDMoCES0tL05j4bUasrN17dqvixIREmT5jupLgD+LBgwelfPnyIQdctuDMsmXLJCc3lFkA+dEfjVYdKSkpcvnKZY8YkHE9Q/bt2yebN22WVatWyfr162XPnj1FWu+toHIFBfLRqI+0cixLqaC27dpqu546daqUSi7lZTdyu3fv1mCaHXxCz5gxY3Tt/fr3i0oW8jk5OVoh6GNQ8PvNJ0m0aNEiJUJtO72DBw/WOBELi8ely5dkyLAhUrpUafWdimNriUoWwMHjPx0PZaPrtW3bttVrKFVn3HccwLBlK87OmDFDK9DkMDR79mxJiE/QDTQ9PV2zmQDhyIMPPqjPBfHkk09q0IKOFgdsYXPsuLFeBb300kty8+ZNBffAihUrJCEh1BqRW7J0SaG2pX67tfXq3UuSE5OlSZMmUcnCv9OnT0tyUrLG4K1Bb+nAZfLmF0n5xBNPhBLbxWzGTBcnqtrdN+BHdna2NG3aVDvawAEDVb/pMhQhCyFaX4MGDbTvM0AUqiy3gNTUVCWhWtVqUqFCBXWCkZS2Z8FBz9dffy1VqlSRD0d96PVgBhecImjo7969u3Tq1Em6/aWb9OzVU1avWa3PIhv0LRLMp/HjxysJYPTo0XrNFqyf7m/2loTYBJXBTiSypk2bppVQu05trXLzQ+2EZfnkOpMu60Z+3rx53jbgl0cn/hAjsGTJEo8sv670i+mawJDFzECr5FlkDEXIMqdff/11Vc6GmHYiTRfGPZQ0a9ZMy3r48OGaUUkJSZqJyBAYPq9duya1a9fWjFqxcoWW/sWLF5VcHGrXrp2OtYzZVBJEG9SH8GJuBeSoIIKGLZKgQ8cOurcaGXxu2bJF7SIDWUyn/jZochs2bNAKp3WzVi9RXUyCYD1Dhw5VstasWeMlh+njuczMTN2z8Yt4sj/xrK0R0JGoLGKCj8nJydp98Me/1qJtMOw0+4dlKtMJBri+cuXKQtdTV6Rqr6VlcvDTCnILXLhooW7ukH323FnJz8/XFsDCatWqJRkZGRpkFqyEOdu2WEPQt0hAjoVPmDBBfdK9wW347dq2k8uXQ/skfk+cNFEDQcB69+6tyeRPCr6TKNOmT1OyaKXsd+Dbb7/V6RDwHL6fOn1KXu7zsq4HedqqDWLmV05+jg4RdCFrg0OGDAnt7WHCSBgq9M233vTkmAeuXL2iz/vXGpEsXYRT1qlzJx0QqCT+ZjG0G5zDyUOHD+lUU69ePXnqqae0lAkOGfnII49om3v00Ue1l/Ms1ce1+vXr6xmtVatWWmkp/VNk5MiR3rjsD+KtgBywkZv9UavH+ceAsWPHDk2e1157Tcli39q1c1cRG8hQ4Uy5TJIEFmg1OnBmA0xqTJ1kv953tojHzp07I1YqXQmfTF+vXr1CncQBkhZ8s0DbM3qM0GF/HaZxxEfzDxQhC2gAHDlT/zk1tHCXsQsXLtTrH3zwgToHCThDJU2aNEleeOEFzS6CdvLkyRCh7jlGVXSxD9IWaQcsnoBAHJ/IIf/www97U5A/kMVB/Qz7yplGB5earu87n9WO+2zUqJF88sknGmD+7tatm7YmbPh1KVkuoUhQJctVKJ0BUkDlKpW1nVWtVlXKlCnjHXRtupw8ZbI+b37ziY30S+lSsVJFlQU1a9aUxYsXy8cffyxt2rTRpEIHUyw+V6xYUXgpESkGxZIFq+wptDGCy3mJCuGNBMF99913dYG6aOfkiy++KHHxcdo2yDIlyzlCJULqtm3bVA+Z06RpE5kzd46sXbtWW8zkyZOlS5cuesjWgSYvpDfobBDm57lz53QktmEnKSlJ6tatq34SIIIPuE8nwJ+gbs1it47nn38+JB8fL1/M/0LbGNMxgxEHX6ZakuLLL7/UjoJ+1qqju0sa08sndk6eOik1atTwyNIKc21abbiDOtcgCeDv+HHjvWEs6GNEsgDBIgg6EDiFLVu21PZGGUMCAYYsWySVFRsbq4c8KovF8vd3330nmdmZeq6BjIoVKsrpM6e9tqrZ6L5zaKblVq5UWadN/8RUHLiPnwMGDNAAkO1t/tTGO0PhS58+fUL3XDBoY28Pflv3A/YXvy6rrI4dO+rA9Oqrr+o+iw3AOgvByc+YPkODD1kcDahu89meY220Onwwwqy1GsqULiMdOnTQBKAD2bN+/0CxZOEQAeOchTMtWrTQ9sHZiY2VN8nImEO8SYZENvrPPvtMnaJ9IJeVk6UHXxyuXr26XLnighWuHMB3qrZO3TqacTjPJo7+SL4ZuA8hllA1qteQY8eOeZkJYXwf/PZgb/OmtTEZEmx/QNSW20c4RrAOAmz++W0CrjEkLF261COL4cq/Z5kcsWEa9shy5DRq2EiTvn9Kf5k0eZKe1/RMGE7+SDZBsWTxAA9TXewlNWrW0LPEoEGDCp0pAMGGRJymmgCZ3LBhQ61GAvb55597LWD16tWeU/Y8ZHG2o/paNG8R1WnAPXwYMXKE2kI3ExzP+XXzncrW6nMyBKt9+/b61gC7pk+fc/oa/LGBtiNeI+kg4LNpQC8tDrIgFtAmg+1Vbbuz2tOdn1ai0Muhn1asxDjgg373+e235UdUsiyIvLHGGIulWr7611chxS5rLCBkKy91NXvCGTThHxN0weiBNF4BJcYnSsqAFH3WHARkKpMWbwTWrlurz0RznHvoYO+k9+Nbv379vLcofjmCyG9GtEEL2vLU5VoJfjl8rFSpkt4nIaORxbO0PojC9oULF4qQxRquZlzVvc0SqkfPHt6adQ1h+PUXh2LJAqrQBWTWrFneqxrIYsS2YAI17rKDvmtTEu1m46aNHinsB126uiHCtTmIfOWVV3Sf4nXMNwu/kebNm+uzderUCb08vgVZppNMNbLa/7m9BjwYCHSlnUxT31kDZHA+DJJFwvDbEwRs2rRJn/PbLCTriDn878PaTUhCDuHqk09O/XDr53WT+QhZuk87HX7Z20FUslCIQcqWg6xlBwv1ZxEykPX+iPdDU44DY679FMF9Dng//PiDBgwduje5EbhatWpKEmM818eOHes9E21BFghO/QQC3zhMclj12gt+OUAKv8qiH+DD0aNHdQ2mD1sQDVm0at5w+O/7YXHhRS5dZ+bMmWoz6K/azs729lRsQxZy0dZWHKKSBVg0p3Z7b1Wndh39CVqDFTZozvNan4Mu56kxY8eEJjoXUHMOmRMnTkjfvn29X08JMqTRKpYtXxaSDz8T9MUP1ecCNOrDUUq8vrlwoEJTl6fqXkulULm80ae9acU7exxDgjbwDX+pbMjavn17sWQB5Bmc6tWvF3qJ7eIU9FltuImSSdoSvXuP7re1vki4JVkoRTnTGRXFuQFHAeXsyTjoNfq8g10LgkVxn9+/aJObNm+S8+nndVE875cN+uKH6aKVkrkEw8igNdG2a/2hltfWCJS1Z852Zsv02d/80swkCNG6Hp9NP7hHu+YHWpIsUmUpKQ68ltOEcj7wksDflX4Obo8sB12MM2yLKuJY+BoBtCxTmTChQV0qE16MXTPd/meiAVn08HalXNlyuhdBlhFnIFC8IeD+8L8Nl+uZoZ/Ng7oUUdboh92HpOLk9brTN3feXCWKpOFne8gK2r8d3JIsgzkSdOiXwq/nl+rkOV20I5d/LWDYsD3RKkk/3VGiabOmMmXKFK1qC24kfZG+R0R4SEBXNH3c4xDOQZ/XTIz4xcnfCrdN1v8rWDRggKEt8Spo85bNsmrNKlm0eJF+375ju270VKEF6pcE6+fC7GCTamLQ+TX273mygD8oXmv1w+6F5UAkPXcDQVu/xv7vgqwg/MH4LYm52/hdkvV7xX2y7hkUyH8AeIrWJFR4fQAAAAAASUVORK5CYII="; - protected CaptchaService Service => fixture.Service; + private const string _captchaImageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAGsAAAAgCAYAAAAVIIajAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAA5/SURBVGhD7Zr3k1VVEsdh8gAlOStLWiwyKLAqsErQMkIVSyqhFAa0VBBYVl1BESUskraUDGK5FpKHXCRByZlVgkOGIQ6TB/6C3vPp9/runTtvHqjgLhY/fOu9ubdvd5/+dvfpc9+UKLhZIPdxb+A+WfcQ7pP1GyD/Rn4hFNz47zX7Hum5IO46WebU7Tp0pxC0+b+yD3Lzc0PfCyIjryBPYfKR9IEiZPkfiPSg3VcEDEWS457nbEDmTsNs+P3KzctV+34/77YfwPPF4czZM7Ju3Trp07ePDBw4UD799FMZN26cvPf392T+/Ply5OgRycrKKhQnENRZiCwTsgBHWpz9fePGjRBu3igi45fLys6SH4/8qEFDn1/mTsLs5eTlhHxyhJ04dUK+3/a9HDx8UPLy3FrctVsljl0v7r4fpscP/73svGzZu3evVK5cWWJjYiU+Nl7iYuMkLsahZOgzpkSMPPDAA9KyRUvZtWuXl2h+XYaIZF26dEnOnz8vmVmZ3jW7jyICsnHjRnnuueekdevWcuTIkSIG7Ll33nlH4uLiZOvWreqI3b+TMFtgz949MnTYUGncuLEkJSZJyRIlNVCPP/64zJo9S86cORMxGH4d+KkIyARh8v49yJ7hkzjt3bdXCYotGauIKRnjQcmDOAfulS5dWkaNGiXXs65H9LEIWWTekKFDdIGdO3eW3NzCFaYyrkomTpwo8XHxUqpUKdm/f7/k5ReW0WfcggcMHCCJiYly6NAhdd5vKxr8fkWDyeP3gQMHpFy5chqIhLgELxAgPiZeSpQoIVWrVlXCrML8evA5IzND5s6dKz+l/aR/+235YTaPHz8ujz32mJw6dUrXZzr5pLIWLFigCUMFAWLRuEljeeihh5QcEjm2RIhII3PEiBERO1ERsgj64cOHlazWrVorWbkFoYXx8LWMa3Lh4gV55plnVDEGKOGr164WIkwNuYzr0aOHlC1bVs6ePVuoBXGfxXGN73zyN99Nxu9bcUCO5/Yf2C/ly5X3stTLWhcMgmXX+Xz22Wc1GP5KN7vTp01X+Z49ekatLvXR3Z8zZ47ExMQoKabD7mfnZsv6DetD1R0m4o033pDsnGy5fv26HDt+TLZs3SJdu3aVkiVDMoCES0tL05j4bUasrN17dqvixIREmT5jupLgD+LBgwelfPnyIQdctuDMsmXLJCc3lFkA+dEfjVYdKSkpcvnKZY8YkHE9Q/bt2yebN22WVatWyfr162XPnj1FWu+toHIFBfLRqI+0cixLqaC27dpqu546daqUSi7lZTdyu3fv1mCaHXxCz5gxY3Tt/fr3i0oW8jk5OVoh6GNQ8PvNJ0m0aNEiJUJtO72DBw/WOBELi8ely5dkyLAhUrpUafWdimNriUoWwMHjPx0PZaPrtW3bttVrKFVn3HccwLBlK87OmDFDK9DkMDR79mxJiE/QDTQ9PV2zmQDhyIMPPqjPBfHkk09q0IKOFgdsYXPsuLFeBb300kty8+ZNBffAihUrJCEh1BqRW7J0SaG2pX67tfXq3UuSE5OlSZMmUcnCv9OnT0tyUrLG4K1Bb+nAZfLmF0n5xBNPhBLbxWzGTBcnqtrdN+BHdna2NG3aVDvawAEDVb/pMhQhCyFaX4MGDbTvM0AUqiy3gNTUVCWhWtVqUqFCBXWCkZS2Z8FBz9dffy1VqlSRD0d96PVgBhecImjo7969u3Tq1Em6/aWb9OzVU1avWa3PIhv0LRLMp/HjxysJYPTo0XrNFqyf7m/2loTYBJXBTiSypk2bppVQu05trXLzQ+2EZfnkOpMu60Z+3rx53jbgl0cn/hAjsGTJEo8sv670i+mawJDFzECr5FlkDEXIMqdff/11Vc6GmHYiTRfGPZQ0a9ZMy3r48OGaUUkJSZqJyBAYPq9duya1a9fWjFqxcoWW/sWLF5VcHGrXrp2OtYzZVBJEG9SH8GJuBeSoIIKGLZKgQ8cOurcaGXxu2bJF7SIDWUyn/jZochs2bNAKp3WzVi9RXUyCYD1Dhw5VstasWeMlh+njuczMTN2z8Yt4sj/xrK0R0JGoLGKCj8nJydp98Me/1qJtMOw0+4dlKtMJBri+cuXKQtdTV6Rqr6VlcvDTCnILXLhooW7ukH323FnJz8/XFsDCatWqJRkZGRpkFqyEOdu2WEPQt0hAjoVPmDBBfdK9wW347dq2k8uXQ/skfk+cNFEDQcB69+6tyeRPCr6TKNOmT1OyaKXsd+Dbb7/V6RDwHL6fOn1KXu7zsq4HedqqDWLmV05+jg4RdCFrg0OGDAnt7WHCSBgq9M233vTkmAeuXL2iz/vXGpEsXYRT1qlzJx0QqCT+ZjG0G5zDyUOHD+lUU69ePXnqqae0lAkOGfnII49om3v00Ue1l/Ms1ce1+vXr6xmtVatWWmkp/VNk5MiR3rjsD+KtgBywkZv9UavH+ceAsWPHDk2e1157Tcli39q1c1cRG8hQ4Uy5TJIEFmg1OnBmA0xqTJ1kv953tojHzp07I1YqXQmfTF+vXr1CncQBkhZ8s0DbM3qM0GF/HaZxxEfzDxQhC2gAHDlT/zk1tHCXsQsXLtTrH3zwgToHCThDJU2aNEleeOEFzS6CdvLkyRCh7jlGVXSxD9IWaQcsnoBAHJ/IIf/www97U5A/kMVB/Qz7yplGB5earu87n9WO+2zUqJF88sknGmD+7tatm7YmbPh1KVkuoUhQJctVKJ0BUkDlKpW1nVWtVlXKlCnjHXRtupw8ZbI+b37ziY30S+lSsVJFlQU1a9aUxYsXy8cffyxt2rTRpEIHUyw+V6xYUXgpESkGxZIFq+wptDGCy3mJCuGNBMF99913dYG6aOfkiy++KHHxcdo2yDIlyzlCJULqtm3bVA+Z06RpE5kzd46sXbtWW8zkyZOlS5cuesjWgSYvpDfobBDm57lz53QktmEnKSlJ6tatq34SIIIPuE8nwJ+gbs1it47nn38+JB8fL1/M/0LbGNMxgxEHX6ZakuLLL7/UjoJ+1qqju0sa08sndk6eOik1atTwyNIKc21abbiDOtcgCeDv+HHjvWEs6GNEsgDBIgg6EDiFLVu21PZGGUMCAYYsWySVFRsbq4c8KovF8vd3330nmdmZeq6BjIoVKsrpM6e9tqrZ6L5zaKblVq5UWadN/8RUHLiPnwMGDNAAkO1t/tTGO0PhS58+fUL3XDBoY28Pflv3A/YXvy6rrI4dO+rA9Oqrr+o+iw3AOgvByc+YPkODD1kcDahu89meY220Onwwwqy1GsqULiMdOnTQBKAD2bN+/0CxZOEQAeOchTMtWrTQ9sHZiY2VN8nImEO8SYZENvrPPvtMnaJ9IJeVk6UHXxyuXr26XLnighWuHMB3qrZO3TqacTjPJo7+SL4ZuA8hllA1qteQY8eOeZkJYXwf/PZgb/OmtTEZEmx/QNSW20c4RrAOAmz++W0CrjEkLF261COL4cq/Z5kcsWEa9shy5DRq2EiTvn9Kf5k0eZKe1/RMGE7+SDZBsWTxAA9TXewlNWrW0LPEoEGDCp0pAMGGRJymmgCZ3LBhQ61GAvb55597LWD16tWeU/Y8ZHG2o/paNG8R1WnAPXwYMXKE2kI3ExzP+XXzncrW6nMyBKt9+/b61gC7pk+fc/oa/LGBtiNeI+kg4LNpQC8tDrIgFtAmg+1Vbbuz2tOdn1ai0Muhn1asxDjgg373+e235UdUsiyIvLHGGIulWr7611chxS5rLCBkKy91NXvCGTThHxN0weiBNF4BJcYnSsqAFH3WHARkKpMWbwTWrlurz0RznHvoYO+k9+Nbv379vLcofjmCyG9GtEEL2vLU5VoJfjl8rFSpkt4nIaORxbO0PojC9oULF4qQxRquZlzVvc0SqkfPHt6adQ1h+PUXh2LJAqrQBWTWrFneqxrIYsS2YAI17rKDvmtTEu1m46aNHinsB126uiHCtTmIfOWVV3Sf4nXMNwu/kebNm+uzderUCb08vgVZppNMNbLa/7m9BjwYCHSlnUxT31kDZHA+DJJFwvDbEwRs2rRJn/PbLCTriDn878PaTUhCDuHqk09O/XDr53WT+QhZuk87HX7Z20FUslCIQcqWg6xlBwv1ZxEykPX+iPdDU44DY679FMF9Dng//PiDBgwduje5EbhatWpKEmM818eOHes9E21BFghO/QQC3zhMclj12gt+OUAKv8qiH+DD0aNHdQ2mD1sQDVm0at5w+O/7YXHhRS5dZ+bMmWoz6K/azs729lRsQxZy0dZWHKKSBVg0p3Z7b1Wndh39CVqDFTZozvNan4Mu56kxY8eEJjoXUHMOmRMnTkjfvn29X08JMqTRKpYtXxaSDz8T9MUP1ecCNOrDUUq8vrlwoEJTl6fqXkulULm80ae9acU7exxDgjbwDX+pbMjavn17sWQB5Bmc6tWvF3qJ7eIU9FltuImSSdoSvXuP7re1vki4JVkoRTnTGRXFuQFHAeXsyTjoNfq8g10LgkVxn9+/aJObNm+S8+nndVE875cN+uKH6aKVkrkEw8igNdG2a/2hltfWCJS1Z852Zsv02d/80swkCNG6Hp9NP7hHu+YHWpIsUmUpKQ68ltOEcj7wksDflX4Obo8sB12MM2yLKuJY+BoBtCxTmTChQV0qE16MXTPd/meiAVn08HalXNlyuhdBlhFnIFC8IeD+8L8Nl+uZoZ/Ng7oUUdboh92HpOLk9brTN3feXCWKpOFne8gK2r8d3JIsgzkSdOiXwq/nl+rkOV20I5d/LWDYsD3RKkk/3VGiabOmMmXKFK1qC24kfZG+R0R4SEBXNH3c4xDOQZ/XTIz4xcnfCrdN1v8rWDRggKEt8Spo85bNsmrNKlm0eJF+375ju270VKEF6pcE6+fC7GCTamLQ+TX273mygD8oXmv1w+6F5UAkPXcDQVu/xv7vgqwg/MH4LYm52/hdkvV7xX2y7hkUyH8AeIrWJFR4fQAAAAAASUVORK5CYII="; - public ServiceTests(ServiceFixture fixture) + private CaptchaService Service => _fixture.Service; + + protected ServiceTests(ServiceFixture fixture, ITestOutputHelper output) { - this.fixture = fixture; + this._fixture = fixture; + _output = output; } protected static Task ShouldNotBeSupported(Func method) => Assert.ThrowsAsync(method); @@ -29,6 +32,8 @@ protected async Task BalanceTest() { var balance = await Service.GetBalanceAsync(); Assert.True(balance > 0); + + _output.WriteLine($"Balance: {balance}"); } protected async Task ReportSolutionTest() @@ -101,7 +106,7 @@ protected async Task ImageCaptchaTest() Assert.Equal("w68hp", solution.Response.Replace(" ", "").ToLower()); } - private async Task RecaptchaV2Test(Proxy proxy) + private async Task RecaptchaV2Test(Proxy? proxy) { var solution = await Service.SolveRecaptchaV2Async( siteKey: "6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u", @@ -112,13 +117,15 @@ private async Task RecaptchaV2Test(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task RecaptchaV2Test_NoProxy() => RecaptchaV2Test(null); - protected Task RecaptchaV2Test_WithProxy() => RecaptchaV2Test(fixture.Config.Proxy); + protected Task RecaptchaV2Test_WithProxy() => RecaptchaV2Test(_fixture.Config.Proxy); - private async Task RecaptchaV2InvisibleTest(Proxy proxy) + private async Task RecaptchaV2InvisibleTest(Proxy? proxy) { var solution = await Service.SolveRecaptchaV2Async( siteKey: "6LdO5_IbAAAAAAeVBL9TClS19NUTt5wswEb3Q7C5", @@ -129,13 +136,15 @@ private async Task RecaptchaV2InvisibleTest(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task RecaptchaV2InvisibleTest_NoProxy() => RecaptchaV2InvisibleTest(null); - protected Task RecaptchaV2InvisibleTest_WithProxy() => RecaptchaV2InvisibleTest(fixture.Config.Proxy); + protected Task RecaptchaV2InvisibleTest_WithProxy() => RecaptchaV2InvisibleTest(_fixture.Config.Proxy); - private async Task RecaptchaV2EnterpriseTest(Proxy proxy) + private async Task RecaptchaV2EnterpriseTest(Proxy? proxy) { var solution = await Service.SolveRecaptchaV2Async( siteKey: "6Lf26sUnAAAAAIKLuWNYgRsFUfmI-3Lex3xT5N-s", @@ -146,13 +155,15 @@ private async Task RecaptchaV2EnterpriseTest(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task RecaptchaV2EnterpriseTest_NoProxy() => RecaptchaV2EnterpriseTest(null); - protected Task RecaptchaV2EnterpriseTest_WithProxy() => RecaptchaV2EnterpriseTest(fixture.Config.Proxy); + protected Task RecaptchaV2EnterpriseTest_WithProxy() => RecaptchaV2EnterpriseTest(_fixture.Config.Proxy); - private async Task RecaptchaV3Test(Proxy proxy) + private async Task RecaptchaV3Test(Proxy? proxy) { var solution = await Service.SolveRecaptchaV3Async( siteKey: "6LfB5_IbAAAAAMCtsjEHEHKqcB9iQocwwxTiihJu", @@ -163,12 +174,14 @@ private async Task RecaptchaV3Test(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task RecaptchaV3Test_NoProxy() => RecaptchaV3Test(null); - protected Task RecaptchaV3Test_WithProxy() => RecaptchaV3Test(fixture.Config.Proxy); + protected Task RecaptchaV3Test_WithProxy() => RecaptchaV3Test(_fixture.Config.Proxy); - private async Task RecaptchaV3EnterpriseTest(Proxy proxy) + private async Task RecaptchaV3EnterpriseTest(Proxy? proxy) { var solution = await Service.SolveRecaptchaV3Async( siteKey: "6Lel38UnAAAAAMRwKj9qLH2Ws4Tf2uTDQCyfgR6b", @@ -179,13 +192,15 @@ private async Task RecaptchaV3EnterpriseTest(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task RecaptchaV3EnterpriseTest_NoProxy() => RecaptchaV3EnterpriseTest(null); - protected Task RecaptchaV3EnterpriseTest_WithProxy() => RecaptchaV3EnterpriseTest(fixture.Config.Proxy); + protected Task RecaptchaV3EnterpriseTest_WithProxy() => RecaptchaV3EnterpriseTest(_fixture.Config.Proxy); - private async Task FunCaptchaTest(Proxy proxy) + private async Task FunCaptchaTest(Proxy? proxy) { var solution = await Service.SolveFuncaptchaAsync( publicKey: "3EE79F8D-13A6-474B-9278-448EA19F79B3", @@ -195,12 +210,14 @@ private async Task FunCaptchaTest(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task FunCaptchaTest_NoProxy() => FunCaptchaTest(null); - protected Task FunCaptchaTest_WithProxy() => FunCaptchaTest(fixture.Config.Proxy); + protected Task FunCaptchaTest_WithProxy() => FunCaptchaTest(_fixture.Config.Proxy); - private async Task HCaptchaTest(Proxy proxy) + private async Task HCaptchaTest(Proxy? proxy) { var solution = await Service.SolveHCaptchaAsync( siteKey: "f7de0da3-3303-44e8-ab48-fa32ff8ccc7b", @@ -208,15 +225,17 @@ private async Task HCaptchaTest(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task HCaptchaTest_NoProxy() => HCaptchaTest(null); - protected Task HCaptchaTest_WithProxy() => HCaptchaTest(fixture.Config.Proxy); + protected Task HCaptchaTest_WithProxy() => HCaptchaTest(_fixture.Config.Proxy); - private async Task KeyCaptchaTest(Proxy proxy) + private async Task KeyCaptchaTest(Proxy? proxy) { // Get the required parameters from the page since they are not static - var siteUrl = $"{"https"}://www.keycaptcha.com/contact-us/"; + const string siteUrl = "https://www.keycaptcha.com/contact-us/"; using var httpClient = new HttpClient(); using var response = await httpClient.GetAsync(siteUrl); var pageSource = await response.Content.ReadAsStringAsync(); @@ -235,16 +254,18 @@ private async Task KeyCaptchaTest(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task KeyCaptchaTest_NoProxy() => KeyCaptchaTest(null); - protected Task KeyCaptchaTest_WithProxy() => KeyCaptchaTest(fixture.Config.Proxy); + protected Task KeyCaptchaTest_WithProxy() => KeyCaptchaTest(_fixture.Config.Proxy); - private async Task GeeTestTest(Proxy proxy) + private async Task GeeTestTest(Proxy? proxy) { // Get the required parameters from the page since they are not static var unixTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - var siteUrl = $"{"https"}://www.geetest.com/demo/gt/register-enFullpage-official?t={unixTime}"; + var siteUrl = $"https://www.geetest.com/demo/gt/register-enFullpage-official?t={unixTime}"; using var httpClient = new HttpClient(); using var response = await httpClient.GetAsync(siteUrl); var pageSource = await response.Content.ReadAsStringAsync(); @@ -263,12 +284,16 @@ private async Task GeeTestTest(Proxy proxy) Assert.NotEqual("", solution.Challenge); Assert.NotEqual("", solution.SecCode); Assert.NotEqual("", solution.Validate); + + _output.WriteLine($"Challenge: {solution.Challenge}"); + _output.WriteLine($"SecCode: {solution.SecCode}"); + _output.WriteLine($"Validate: {solution.Validate}"); } protected Task GeeTestTest_NoProxy() => GeeTestTest(null); - protected Task GeeTestTest_WithProxy() => GeeTestTest(fixture.Config.Proxy); + protected Task GeeTestTest_WithProxy() => GeeTestTest(_fixture.Config.Proxy); - private async Task CapyTest(Proxy proxy) + private async Task CapyTest(Proxy? proxy) { var solution = await Service.SolveCapyAsync( siteKey: "PUZZLE_Cme4hZLjuZRMYC3uh14C52D3uNms5w", @@ -278,15 +303,19 @@ private async Task CapyTest(Proxy proxy) Assert.NotEqual(string.Empty, solution.ChallengeKey); Assert.NotEqual(string.Empty, solution.CaptchaKey); Assert.NotEqual(string.Empty, solution.Answer); + + _output.WriteLine($"ChallengeKey: {solution.ChallengeKey}"); + _output.WriteLine($"CaptchaKey: {solution.CaptchaKey}"); + _output.WriteLine($"Answer: {solution.Answer}"); } protected Task CapyTest_NoProxy() => CapyTest(null); - protected Task CapyTest_WithProxy() => CapyTest(fixture.Config.Proxy); + protected Task CapyTest_WithProxy() => CapyTest(_fixture.Config.Proxy); // Proxy and User-Agent required private async Task DataDomeTest(Proxy proxy) { - var site = "https://antoinevastel.com/bots/datadome"; + const string site = "https://antoinevastel.com/bots/datadome"; // If it doesn't work, try a few times until it triggers // the captcha @@ -295,6 +324,11 @@ private async Task DataDomeTest(Proxy proxy) httpClientHandler.UseCookies = true; httpClientHandler.CookieContainer = cookieContainer; using var httpClient = new HttpClient(httpClientHandler); + + if (string.IsNullOrEmpty(proxy.UserAgent)) + { + throw new ArgumentException("User-Agent is required"); + } // The User-Agent must be the same as the one used to get the page httpClient.DefaultRequestHeaders.Add("User-Agent", proxy.UserAgent); @@ -321,11 +355,13 @@ private async Task DataDomeTest(Proxy proxy) proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } - protected Task DataDomeTest_WithProxy() => DataDomeTest(fixture.Config.Proxy); + protected Task DataDomeTest_WithProxy() => DataDomeTest(_fixture.Config.Proxy); - private async Task CloudflareTurnstileTest(Proxy proxy) + private async Task CloudflareTurnstileTest(Proxy? proxy) { var solution = await Service.SolveCloudflareTurnstileAsync( siteKey: "0x4AAAAAAAVrOwQWPlm3Bnr5", @@ -333,13 +369,15 @@ private async Task CloudflareTurnstileTest(Proxy proxy) proxy: proxy); Assert.NotEqual(string.Empty, solution.Response); + + _output.WriteLine($"Response: {solution.Response}"); } protected Task CloudflareTurnstileTest_NoProxy() => CloudflareTurnstileTest(new Proxy { // User-Agent required - UserAgent = fixture.Config.Proxy.UserAgent + UserAgent = _fixture.Config.Proxy.UserAgent }); - protected Task CloudflareTurnstileTest_WithProxy() => CloudflareTurnstileTest(fixture.Config.Proxy); + protected Task CloudflareTurnstileTest_WithProxy() => CloudflareTurnstileTest(_fixture.Config.Proxy); } diff --git a/CaptchaSharp.Tests/TrueCaptchaTests.cs b/CaptchaSharp.Tests/TrueCaptchaTests.cs index a9202db..d176771 100644 --- a/CaptchaSharp.Tests/TrueCaptchaTests.cs +++ b/CaptchaSharp.Tests/TrueCaptchaTests.cs @@ -1,6 +1,7 @@ using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; @@ -14,8 +15,8 @@ public TrueCaptchaFixture() } } -public class TrueCaptchaServiceTests(TrueCaptchaFixture fixture) - : ServiceTests(fixture), IClassFixture +public class TrueCaptchaServiceTests(TrueCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); diff --git a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs index 4c79dbb..30834ed 100644 --- a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs @@ -1,6 +1,7 @@ using CaptchaSharp.Services; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace CaptchaSharp.Tests; @@ -12,8 +13,8 @@ public TwoCaptchaFixture() } } -public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture) - : ServiceTests(fixture), IClassFixture +public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); diff --git a/CaptchaSharp/Services/AzCaptchaService.cs b/CaptchaSharp/Services/AzCaptchaService.cs index ec31042..0777eec 100644 --- a/CaptchaSharp/Services/AzCaptchaService.cs +++ b/CaptchaSharp/Services/AzCaptchaService.cs @@ -1,6 +1,8 @@ using CaptchaSharp.Enums; using System; using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; namespace CaptchaSharp.Services; diff --git a/CaptchaSharp/Services/CustomTwoCaptchaService.cs b/CaptchaSharp/Services/CustomTwoCaptchaService.cs index 4eed1cc..da5b710 100644 --- a/CaptchaSharp/Services/CustomTwoCaptchaService.cs +++ b/CaptchaSharp/Services/CustomTwoCaptchaService.cs @@ -48,6 +48,9 @@ private void SetupHttpClient(Uri baseUri, bool overrideHostHeader = true) CaptchaType.FunCaptcha | CaptchaType.HCaptcha | CaptchaType.KeyCaptcha | - CaptchaType.GeeTest; + CaptchaType.GeeTest | + CaptchaType.Capy | + CaptchaType.DataDome | + CaptchaType.CloudflareTurnstile; #endregion } From a50ec840edff4dc12a034c08749081b78164a2ae Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 19:28:09 +0200 Subject: [PATCH 16/67] Deleted SolveRecaptcha service (out of business) --- CaptchaSharp.Tests/ConfigFixture.cs | 1 - CaptchaSharp/CaptchaSharp.xml | 24 ---- .../Services/SolveRecaptchaService.cs | 106 ------------------ 3 files changed, 131 deletions(-) delete mode 100644 CaptchaSharp/Services/SolveRecaptchaService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 76cbcb8..46028a4 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -48,7 +48,6 @@ public class Credentials public string CaptchasIOApiKey { get; set; } = string.Empty; public string RuCaptchaApiKey { get; set; } = string.Empty; public string SolveCaptchaApiKey { get; set; } = string.Empty; - public string SolveRecaptchaApiKey { get; set; } = string.Empty; public string TrueCaptchaApiKey { get; set; } = string.Empty; public string TrueCaptchaUsername { get; set; } = string.Empty; public string NineKWApiKey { get; set; } = string.Empty; diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index ed486b5..b2361e5 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1354,30 +1354,6 @@ The API key to use. The to use for requests. If null, a default one will be created. - - - The service provided by https://solverecaptcha.com/ - - - - - Initializes a . - - The API key to use. - The to use for requests. If null, a default one will be created. - - - - - - - - - - - - - The service provided by https://apitruecaptcha.org/ diff --git a/CaptchaSharp/Services/SolveRecaptchaService.cs b/CaptchaSharp/Services/SolveRecaptchaService.cs deleted file mode 100644 index 1fac81f..0000000 --- a/CaptchaSharp/Services/SolveRecaptchaService.cs +++ /dev/null @@ -1,106 +0,0 @@ -using CaptchaSharp.Enums; -using CaptchaSharp.Exceptions; -using CaptchaSharp.Models; -using System; -using System.Globalization; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using CaptchaSharp.Extensions; - -namespace CaptchaSharp.Services; - -/// -/// The service provided by https://solverecaptcha.com/ -/// -public class SolveRecaptchaService : CustomTwoCaptchaService -{ - /// - /// Initializes a . - /// - /// The API key to use. - /// The to use for requests. If null, a default one will be created. - public SolveRecaptchaService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("http://api.solverecaptcha.com"), httpClient) - { - this.HttpClient.DefaultRequestHeaders.Host = "api.solverecaptcha.com"; - this.HttpClient.Timeout = Timeout; - - SupportedCaptchaTypes = - CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3; - } - - /// - public override Task GetBalanceAsync(CancellationToken cancellationToken = default) - { - // There is no balance since this service has a monthly subscription - return Task.FromResult(999m); - } - - /// - public override async Task SolveRecaptchaV2Async - (string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, - Proxy? proxy = null, CancellationToken cancellationToken = default) - { - var response = await HttpClient.GetStringAsync - ("", - GetAuthPair() - .Add("sitekey", siteKey) - .Add("pageurl", siteUrl) - .Add("version", invisible ? "invisible" : "v2") - .Add("invisible", Convert.ToInt32(invisible).ToString()) - .Add(ConvertProxy(proxy)), - cancellationToken) - .ConfigureAwait(false); - - if (IsErrorCode(response)) - ThrowException(response); - - return new StringResponse { Id = 0, Response = TakeSecondSlice(response) }; - } - - /// - public override async Task SolveRecaptchaV3Async - (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4F, bool enterprise = false, - Proxy? proxy = null, CancellationToken cancellationToken = default) - { - var response = await HttpClient.GetStringAsync - ("", - GetAuthPair() - .Add("sitekey", siteKey) - .Add("pageurl", siteUrl) - .Add("action", action) - .Add("min_score", minScore.ToString("0.0", CultureInfo.InvariantCulture)) - .Add("version", "v3") - .Add(ConvertProxy(proxy)), - cancellationToken) - .ConfigureAwait(false); - - if (IsErrorCode(response)) - ThrowException(response); - - return new StringResponse { Id = 0, Response = TakeSecondSlice(response) }; - } - - /// - public override Task ReportSolution - (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - private StringPairCollection GetAuthPair() - => new StringPairCollection().Add("key", ApiKey); - - private void ThrowException(string response) - { - throw response switch - { - "ERROR_API_KEY_NOT_FOUND" or "ERROR_ACCESS_DENIED" => new BadAuthenticationException(response), - "ERROR_NO_AVAILABLE_THREADS" => new TaskCreationException(response), - "ERROR_CAPTCHA_UNSOLVABLE" => new TaskSolutionException(response), - _ => new Exception(response) - }; - } -} From bbf12e713c4cfe7c48294c7fd8a994e74d0a5224 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 13 Jul 2024 21:51:43 +0200 Subject: [PATCH 17/67] Added captchas.io tests --- CaptchaSharp.Tests/CaptchasIoServiceTests.cs | 41 +++++++++++++++++++ CaptchaSharp.Tests/ConfigFixture.cs | 2 +- CaptchaSharp.Tests/ServiceTests.cs | 12 ++++++ CaptchaSharp/CaptchaSharp.xml | 6 +-- CaptchaSharp/Services/AzCaptchaService.cs | 4 +- ...tchasIOService.cs => CaptchasIoService.cs} | 15 ++++--- 6 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 CaptchaSharp.Tests/CaptchasIoServiceTests.cs rename CaptchaSharp/Services/{CaptchasIOService.cs => CaptchasIoService.cs} (59%) diff --git a/CaptchaSharp.Tests/CaptchasIoServiceTests.cs b/CaptchaSharp.Tests/CaptchasIoServiceTests.cs new file mode 100644 index 0000000..eed4542 --- /dev/null +++ b/CaptchaSharp.Tests/CaptchasIoServiceTests.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class CaptchasIoFixture : ServiceFixture +{ + public CaptchasIoFixture() + { + Service = new CaptchasIoService( + Config.Credentials.CaptchasIoApiKey); + } +} + +public class CaptchasIoServiceTests(CaptchasIoFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveTextCaptchaAsync_ValidCaptcha_ValidSolution() => TextCaptchaTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); +} diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 46028a4..3d3b3e4 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -45,7 +45,7 @@ public class Credentials public string CapMonsterHost { get; set; } = string.Empty; public int CapMonsterPort { get; set; } = 80; public string AzCaptchaApiKey { get; set; } = string.Empty; - public string CaptchasIOApiKey { get; set; } = string.Empty; + public string CaptchasIoApiKey { get; set; } = string.Empty; public string RuCaptchaApiKey { get; set; } = string.Empty; public string SolveCaptchaApiKey { get; set; } = string.Empty; public string TrueCaptchaApiKey { get; set; } = string.Empty; diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index 61de3b9..2b7eb58 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -118,6 +118,7 @@ private async Task RecaptchaV2Test(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -137,6 +138,7 @@ private async Task RecaptchaV2InvisibleTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -156,6 +158,7 @@ private async Task RecaptchaV2EnterpriseTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -175,6 +178,7 @@ private async Task RecaptchaV3Test(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -193,6 +197,7 @@ private async Task RecaptchaV3EnterpriseTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -211,6 +216,7 @@ private async Task FunCaptchaTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -226,6 +232,7 @@ private async Task HCaptchaTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -255,6 +262,7 @@ private async Task KeyCaptchaTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -285,6 +293,7 @@ private async Task GeeTestTest(Proxy? proxy) Assert.NotEqual("", solution.SecCode); Assert.NotEqual("", solution.Validate); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Challenge: {solution.Challenge}"); _output.WriteLine($"SecCode: {solution.SecCode}"); _output.WriteLine($"Validate: {solution.Validate}"); @@ -304,6 +313,7 @@ private async Task CapyTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.CaptchaKey); Assert.NotEqual(string.Empty, solution.Answer); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"ChallengeKey: {solution.ChallengeKey}"); _output.WriteLine($"CaptchaKey: {solution.CaptchaKey}"); _output.WriteLine($"Answer: {solution.Answer}"); @@ -356,6 +366,7 @@ private async Task DataDomeTest(Proxy proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -370,6 +381,7 @@ private async Task CloudflareTurnstileTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); + _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index b2361e5..142093b 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1041,14 +1041,14 @@ - + The service provided by https://captchas.io/ - + - Initializes a . + Initializes a . The API key to use. The to use for requests. If null, a default one will be created. diff --git a/CaptchaSharp/Services/AzCaptchaService.cs b/CaptchaSharp/Services/AzCaptchaService.cs index 0777eec..914a50f 100644 --- a/CaptchaSharp/Services/AzCaptchaService.cs +++ b/CaptchaSharp/Services/AzCaptchaService.cs @@ -22,6 +22,8 @@ public AzCaptchaService(string apiKey, HttpClient? httpClient = null) SupportedCaptchaTypes = CaptchaType.ImageCaptcha | CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3; + CaptchaType.ReCaptchaV3 | + CaptchaType.FunCaptcha | + CaptchaType.HCaptcha; } } diff --git a/CaptchaSharp/Services/CaptchasIOService.cs b/CaptchaSharp/Services/CaptchasIoService.cs similarity index 59% rename from CaptchaSharp/Services/CaptchasIOService.cs rename to CaptchaSharp/Services/CaptchasIoService.cs index c9b5489..523c306 100644 --- a/CaptchaSharp/Services/CaptchasIOService.cs +++ b/CaptchaSharp/Services/CaptchasIoService.cs @@ -7,19 +7,24 @@ namespace CaptchaSharp.Services; /// /// The service provided by https://captchas.io/ /// -public class CaptchasIOService : CustomTwoCaptchaService +public class CaptchasIoService : CustomTwoCaptchaService { /// - /// Initializes a . + /// Initializes a . /// /// The API key to use. /// The to use for requests. If null, a default one will be created. - public CaptchasIOService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("https://api.captchas.io"), httpClient, false) + public CaptchasIoService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("https://api.captchas.io"), httpClient, false) { SupportedCaptchaTypes = + CaptchaType.TextCaptcha | CaptchaType.ImageCaptcha | CaptchaType.ReCaptchaV2 | - CaptchaType.ReCaptchaV3; + CaptchaType.ReCaptchaV3 | + CaptchaType.FunCaptcha | + CaptchaType.HCaptcha | + CaptchaType.GeeTest | + CaptchaType.CloudflareTurnstile; } } From a7373a93bd92ce45ea2d31dfe5f4a43b72f7e3a0 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 14 Jul 2024 02:52:45 +0200 Subject: [PATCH 18/67] Improved anti-captcha --- CaptchaSharp.Tests/AntiCaptchaServiceTests.cs | 9 +- CaptchaSharp.Tests/DeCaptcherServiceTests.cs | 2 +- CaptchaSharp.Tests/ServiceTests.cs | 30 +++- CaptchaSharp.Tests/TrueCaptchaTests.cs | 2 +- CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 2 +- CaptchaSharp/CaptchaService.cs | 2 +- CaptchaSharp/CaptchaSharp.xml | 24 ++++ .../Models/CloudflareTurnstileResponse.cs | 18 +++ .../Requests/Tasks/Proxied/AntiCaptchaTask.cs | 33 +++-- .../Requests/Tasks/Proxied/TurnstileTask.cs | 14 ++ .../Requests/Tasks/TurnstileTaskProxyless.cs | 14 ++ .../Responses/AntiCaptchaResponse.cs | 13 ++ .../GetBalanceAntiCaptchaResponse.cs | 6 + .../Responses/GetBalanceResponse.cs | 7 - .../GetTaskResultAntiCaptchaResponse.cs | 16 +++ .../Responses/GetTaskResultResponse.cs | 17 --- ...portIncorrectCaptchaAntiCaptchaResponse.cs | 15 ++ .../ReportIncorrectCaptchaResponse.cs | 16 --- .../AntiCaptcha/Responses/Response.cs | 14 -- .../Solutions/AntiCaptchaTaskSolution.cs | 12 ++ ...s => FuncaptchaAntiCaptchaTaskSolution.cs} | 2 +- ...n.cs => GeeTestAntiCaptchaTaskSolution.cs} | 2 +- ...=> ImageCaptchaAntiCaptchaTaskSolution.cs} | 2 +- ...cs => RecaptchaAntiCaptchaTaskSolution.cs} | 2 +- .../Responses/Solutions/Solution.cs | 13 -- .../TurnstileAntiCaptchaTaskSolution.cs | 20 +++ ....cs => TaskCreationAntiCaptchaResponse.cs} | 2 +- CaptchaSharp/Services/AntiCaptchaService.cs | 129 +++++++++++++----- CaptchaSharp/Services/ImageTyperzService.cs | 23 +++- .../Services/TwoCaptcha/CapyResponse.cs | 27 ---- .../Services/TwoCaptcha/GeeTestResponse.cs | 33 ----- CaptchaSharp/Services/TwoCaptcha/Response.cs | 17 --- .../TwoCaptcha/TwoCaptchaCapyResponse.cs | 26 ++++ .../TwoCaptchaCloudflareTurnstileResponse.cs | 23 ++++ .../TwoCaptcha/TwoCaptchaGeeTestResponse.cs | 32 +++++ .../Services/TwoCaptcha/TwoCaptchaResponse.cs | 19 +++ CaptchaSharp/Services/TwoCaptchaService.cs | 55 ++++---- 37 files changed, 464 insertions(+), 229 deletions(-) create mode 100644 CaptchaSharp/Models/CloudflareTurnstileResponse.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs delete mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceResponse.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs delete mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultResponse.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs delete mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaResponse.cs delete mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/Response.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs rename CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/{FuncaptchaSolution.cs => FuncaptchaAntiCaptchaTaskSolution.cs} (82%) rename CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/{GeeTestSolution.cs => GeeTestAntiCaptchaTaskSolution.cs} (87%) rename CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/{ImageCaptchaSolution.cs => ImageCaptchaAntiCaptchaTaskSolution.cs} (83%) rename CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/{RecaptchaSolution.cs => RecaptchaAntiCaptchaTaskSolution.cs} (83%) delete mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/Solution.cs create mode 100644 CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs rename CaptchaSharp/Services/AntiCaptcha/Responses/{TaskCreationResponse.cs => TaskCreationAntiCaptchaResponse.cs} (60%) delete mode 100644 CaptchaSharp/Services/TwoCaptcha/CapyResponse.cs delete mode 100644 CaptchaSharp/Services/TwoCaptcha/GeeTestResponse.cs delete mode 100644 CaptchaSharp/Services/TwoCaptcha/Response.cs create mode 100644 CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs create mode 100644 CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs create mode 100644 CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs create mode 100644 CaptchaSharp/Services/TwoCaptcha/TwoCaptchaResponse.cs diff --git a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs index 3ebd72e..29c8d3c 100644 --- a/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/AntiCaptchaServiceTests.cs @@ -17,15 +17,22 @@ public class AntiCaptchaServiceTests(AntiCaptchaFixture fixture, ITestOutputHelp : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolution_NoException() => ReportRecaptchaSolutionTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); - [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs index 947c28e..24c440e 100644 --- a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs +++ b/CaptchaSharp.Tests/DeCaptcherServiceTests.cs @@ -19,7 +19,7 @@ public class DeCaptcherServiceTests(DeCaptcherFixture fixture, ITestOutputHelper [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); // Do not overly use this test, or you will get banned. - [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index 2b7eb58..cd36a43 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -36,7 +36,7 @@ protected async Task BalanceTest() _output.WriteLine($"Balance: {balance}"); } - protected async Task ReportSolutionTest() + protected async Task ReportImageSolutionTest(bool correct = true) { var options = new ImageCaptchaOptions { @@ -58,12 +58,35 @@ protected async Task ReportSolutionTest() if (solution.IsLongId) { await Service.ReportSolution( - solution.Id, CaptchaType.ImageCaptcha, correct: true); + solution.Id, CaptchaType.ImageCaptcha, correct); } else { await Service.ReportSolution( - solution.IdString, CaptchaType.ImageCaptcha, correct: true); + solution.IdString, CaptchaType.ImageCaptcha, correct); + } + + Assert.True(true); + } + + protected async Task ReportRecaptchaSolutionTest(bool correct = true) + { + var solution = await Service.SolveRecaptchaV2Async( + siteKey: "6LfD3PIbAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u", + siteUrl: "https://2captcha.com/demo/recaptcha-v2", + dataS: "", + enterprise: false, + invisible: false); + + if (solution.IsLongId) + { + await Service.ReportSolution( + solution.Id, CaptchaType.ReCaptchaV2, correct); + } + else + { + await Service.ReportSolution( + solution.IdString, CaptchaType.ReCaptchaV2, correct); } Assert.True(true); @@ -383,6 +406,7 @@ private async Task CloudflareTurnstileTest(Proxy? proxy) _output.WriteLine($"Captcha ID: {solution.IdString}"); _output.WriteLine($"Response: {solution.Response}"); + _output.WriteLine($"User-Agent: {solution.UserAgent}"); } protected Task CloudflareTurnstileTest_NoProxy() => CloudflareTurnstileTest(new Proxy diff --git a/CaptchaSharp.Tests/TrueCaptchaTests.cs b/CaptchaSharp.Tests/TrueCaptchaTests.cs index d176771..6e63ea1 100644 --- a/CaptchaSharp.Tests/TrueCaptchaTests.cs +++ b/CaptchaSharp.Tests/TrueCaptchaTests.cs @@ -21,7 +21,7 @@ public class TrueCaptchaServiceTests(TrueCaptchaFixture fixture, ITestOutputHelp [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); // Do not overly use this test, or you will get banned. - [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); diff --git a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs index 30834ed..24c0e0e 100644 --- a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs @@ -17,7 +17,7 @@ public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture, ITestOutputHelper : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - [Fact] public Task ReportSolution_NoException() => ReportSolutionTest(); + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); [Fact] public Task SolveTextCaptchaAsync_ValidCaptcha_ValidSolution() => TextCaptchaTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); diff --git a/CaptchaSharp/CaptchaService.cs b/CaptchaSharp/CaptchaService.cs index 873c741..2112b33 100644 --- a/CaptchaSharp/CaptchaService.cs +++ b/CaptchaSharp/CaptchaService.cs @@ -365,7 +365,7 @@ public virtual Task SolveDataDomeAsync( /// /// /// - public virtual Task SolveCloudflareTurnstileAsync( + public virtual Task SolveCloudflareTurnstileAsync( string siteKey, string siteUrl, string? action = null, string? data = null, string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) { diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 142093b..1225c9c 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -791,6 +791,22 @@ + + + A captcha response for Cloudflare's Turnstile. + + + + + The response token. + + + + + The user agent used to solve the challenge. It must be also used + when submitting the response to the target website. + + The solution of a GeeTest captcha. @@ -949,6 +965,9 @@ + + + @@ -1491,5 +1510,10 @@ + + + The User-Agent used to solve the challenge. + + diff --git a/CaptchaSharp/Models/CloudflareTurnstileResponse.cs b/CaptchaSharp/Models/CloudflareTurnstileResponse.cs new file mode 100644 index 0000000..dd95410 --- /dev/null +++ b/CaptchaSharp/Models/CloudflareTurnstileResponse.cs @@ -0,0 +1,18 @@ +namespace CaptchaSharp.Models; + +/// +/// A captcha response for Cloudflare's Turnstile. +/// +public class CloudflareTurnstileResponse : CaptchaResponse +{ + /// + /// The response token. + /// + public required string Response { get; set; } + + /// + /// The user agent used to solve the challenge. It must be also used + /// when submitting the response to the target website. + /// + public required string UserAgent { get; set; } +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs index fb2f18d..5ce3f60 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs @@ -7,34 +7,47 @@ namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied { internal class AntiCaptchaTask : AntiCaptchaTaskProxyless { - public string ProxyType { get; set; } - public string ProxyAddress { get; set; } + public string? ProxyType { get; set; } + public string? ProxyAddress { get; set; } public int ProxyPort { get; set; } - public string ProxyLogin { get; set; } - public string ProxyPassword { get; set; } - public string UserAgent { get; set; } - public string Cookies { get; set; } // Format cookiename1=cookievalue1; cookiename2=cookievalue2 + public string? ProxyLogin { get; set; } + public string? ProxyPassword { get; set; } + public string? UserAgent { get; set; } + + // Format cookiename1=cookievalue1; cookiename2=cookievalue2 + public string? Cookies { get; set; } public AntiCaptchaTask SetProxy(Proxy proxy) { + UserAgent = proxy.UserAgent; + SetCookies(proxy.Cookies); + + if (string.IsNullOrEmpty(proxy.Host)) + { + return this; + } + if (!System.Net.IPAddress.TryParse(proxy.Host, out _)) - throw new NotSupportedException($"Only IP addresses are supported for the proxy host"); + { + throw new NotSupportedException( + "Only IP addresses are supported for the proxy host"); + } ProxyAddress = proxy.Host; ProxyPort = proxy.Port; ProxyType = proxy.Type.ToString().ToLower(); ProxyLogin = proxy.Username; ProxyPassword = proxy.Password; - UserAgent = proxy.UserAgent; - SetCookies(proxy.Cookies); return this; } - private void SetCookies(IEnumerable<(string, string)> cookies) + private void SetCookies(IEnumerable<(string, string)>? cookies) { if (cookies == null) + { return; + } Cookies = string.Join("; ", cookies.Select(c => $"{c.Item1}={c.Item2}")); } diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs new file mode 100644 index 0000000..b813893 --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs @@ -0,0 +1,14 @@ +namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied; + +internal class TurnstileTask : AntiCaptchaTask +{ + public string WebsiteKey { get; set; } + public string WebsiteURL { get; set; } + public string Action { get; set; } + public string TurnstileCData { get; set; } + + public TurnstileTask() + { + Type = "TurnstileTask"; + } +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs new file mode 100644 index 0000000..271f718 --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs @@ -0,0 +1,14 @@ +namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; + +internal class TurnstileTaskProxyless : AntiCaptchaTaskProxyless +{ + public string WebsiteKey { get; set; } + public string WebsiteURL { get; set; } + public string Action { get; set; } + public string TurnstileCData { get; set; } + + public TurnstileTaskProxyless() + { + Type = "TurnstileTaskProxyless"; + } +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs new file mode 100644 index 0000000..e713ccc --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.AntiCaptcha.Responses; + +internal class AntiCaptchaResponse +{ + public int ErrorId { get; set; } + public string? ErrorCode { get; set; } + public string? ErrorDescription { get; set; } + + [JsonIgnore] + public bool IsError => ErrorId > 0; +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs new file mode 100644 index 0000000..eb52cf7 --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs @@ -0,0 +1,6 @@ +namespace CaptchaSharp.Services.AntiCaptcha.Responses; + +internal class GetBalanceAntiCaptchaResponse : AntiCaptchaResponse +{ + public float Balance { get; set; } +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceResponse.cs deleted file mode 100644 index d9c2930..0000000 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Responses -{ - internal class GetBalanceResponse : Response - { - public float Balance { get; set; } - } -} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs new file mode 100644 index 0000000..5beac6c --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs @@ -0,0 +1,16 @@ +using CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; + +namespace CaptchaSharp.Services.AntiCaptcha.Responses; + +internal class GetTaskResultAntiCaptchaResponse : AntiCaptchaResponse +{ + public string Status { get; set; } + public double Cost { get; set; } + public AntiCaptchaTaskSolution AntiCaptchaTaskSolution { get; set; } + public string Ip { get; set; } + public double CreateTime { get; set; } + public double EndTime { get; set; } + public double? SolveCount { get; set; } + + public bool IsReady => Status != "processing"; +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultResponse.cs deleted file mode 100644 index a7c195e..0000000 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; - -namespace CaptchaSharp.Services.AntiCaptcha.Responses -{ - internal class GetTaskResultResponse : Response - { - public string Status { get; set; } - public double Cost { get; set; } - public Solution Solution { get; set; } - public string Ip { get; set; } - public double CreateTime { get; set; } - public double EndTime { get; set; } - public double? SolveCount { get; set; } - - public bool IsReady => Status != "processing"; - } -} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs new file mode 100644 index 0000000..ecaca53 --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.AntiCaptcha.Responses; + +internal class ReportIncorrectCaptchaAntiCaptchaResponse +{ + public int ErrorId { get; set; } + public required string Status { get; set; } + + [JsonIgnore] + public bool Success => ErrorId == 0; + + [JsonIgnore] + public bool NotFoundOrExpired => ErrorId == 16; +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaResponse.cs deleted file mode 100644 index 1784ef8..0000000 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Newtonsoft.Json; - -namespace CaptchaSharp.Services.AntiCaptcha.Responses -{ - internal class ReportIncorrectCaptchaResponse - { - public int ErrorId { get; set; } - public string Status { get; set; } - - [JsonIgnore] - public bool Success => ErrorId == 0; - - [JsonIgnore] - public bool NotFoundOrExpired => ErrorId == 16; - } -} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Response.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Response.cs deleted file mode 100644 index 490c89e..0000000 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Response.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Newtonsoft.Json; - -namespace CaptchaSharp.Services.AntiCaptcha.Responses -{ - internal class Response - { - public int ErrorId { get; set; } - public string ErrorCode { get; set; } - public string ErrorDescription { get; set; } - - [JsonIgnore] - public bool IsError => ErrorId > 0; - } -} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs new file mode 100644 index 0000000..a166bbd --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs @@ -0,0 +1,12 @@ +using CaptchaSharp.Models; +using System; + +namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; + +internal class AntiCaptchaTaskSolution +{ + public virtual CaptchaResponse ToCaptchaResponse(long id) + { + throw new NotImplementedException(); + } +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs similarity index 82% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaSolution.cs rename to CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs index 79ac1cb..7f8d9d8 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs @@ -2,7 +2,7 @@ namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions { - internal class FuncaptchaSolution : Solution + internal class FuncaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { public string Token { get; set; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs similarity index 87% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestSolution.cs rename to CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs index 4e5ee51..9a0e1d9 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs @@ -2,7 +2,7 @@ namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions { - internal class GeeTestSolution : Solution + internal class GeeTestAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { public string Challenge { get; set; } public string Validate { get; set; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs similarity index 83% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaSolution.cs rename to CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs index 26919b9..6c8c796 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs @@ -2,7 +2,7 @@ namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions { - internal class ImageCaptchaSolution : Solution + internal class ImageCaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { public string Text { get; set; } public string Url { get; set; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs similarity index 83% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaSolution.cs rename to CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs index e6b3eea..116057e 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs @@ -2,7 +2,7 @@ namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions { - internal class RecaptchaSolution : Solution + internal class RecaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { public string GRecaptchaResponse { get; set; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/Solution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/Solution.cs deleted file mode 100644 index 9878489..0000000 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/Solution.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CaptchaSharp.Models; -using System; - -namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions -{ - internal class Solution - { - public virtual CaptchaResponse ToCaptchaResponse(long id) - { - throw new NotImplementedException(); - } - } -} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs new file mode 100644 index 0000000..5865be3 --- /dev/null +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs @@ -0,0 +1,20 @@ +using CaptchaSharp.Models; + +namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; + +internal class TurnstileAntiCaptchaTaskSolution : AntiCaptchaTaskSolution +{ + public required string Token { get; set; } + + public required string UserAgent { get; set; } + + public override CaptchaResponse ToCaptchaResponse(long id) + { + return new CloudflareTurnstileResponse + { + Id = id, + Response = Token, + UserAgent = UserAgent + }; + } +} diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs similarity index 60% rename from CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationResponse.cs rename to CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs index 3e50b51..3ae71cf 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationResponse.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs @@ -1,6 +1,6 @@ namespace CaptchaSharp.Services.AntiCaptcha.Responses { - internal class TaskCreationResponse : Response + internal class TaskCreationAntiCaptchaResponse : AntiCaptchaResponse { public int TaskId { get; set; } } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index 141acc5..df251d4 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -56,7 +56,7 @@ public override async Task GetBalanceAsync(CancellationToken cancellati "getBalance", new Request { ClientKey = ApiKey }, cancellationToken: cancellationToken).ConfigureAwait(false); - var balanceResponse = response.Deserialize(); + var balanceResponse = response.Deserialize(); if (balanceResponse.IsError) { @@ -89,7 +89,7 @@ public override async Task SolveImageCaptchaAsync( .ConfigureAwait(false); return await GetResult( - response.Deserialize(), CaptchaType.ImageCaptcha, + response.Deserialize(), CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false); } @@ -102,7 +102,7 @@ public override async Task SolveRecaptchaV2Async( if (enterprise) { - if (proxy != null) + if (proxy is not null) { content.Task = new RecaptchaV2EnterpriseTask { @@ -133,7 +133,7 @@ public override async Task SolveRecaptchaV2Async( } else { - if (proxy != null) + if (proxy is not null) { content.Task = new RecaptchaV2Task { @@ -153,14 +153,14 @@ public override async Task SolveRecaptchaV2Async( } } - var response = await _httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV2, + response.Deserialize(), CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); } @@ -169,9 +169,9 @@ public override async Task SolveRecaptchaV3Async( string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - if (proxy != null) + if (proxy is not null) { - throw new NotSupportedException("Proxies are not supported"); + throw new NotSupportedException("Proxies are not supported for ReCaptchaV3"); } if (minScore != 0.3f && minScore != 0.7f && minScore != 0.9f) @@ -197,7 +197,7 @@ public override async Task SolveRecaptchaV3Async( .ConfigureAwait(false); return await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV3, + response.Deserialize(), CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false); } @@ -213,7 +213,7 @@ public override async Task SolveFuncaptchaAsync( var content = CreateTaskRequest(); - if (proxy != null) + if (proxy is not null) { content.Task = new FunCaptchaTask { @@ -239,7 +239,7 @@ public override async Task SolveFuncaptchaAsync( .ConfigureAwait(false); return await GetResult( - response.Deserialize(), CaptchaType.FunCaptcha, + response.Deserialize(), CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false); } @@ -250,7 +250,7 @@ public override async Task SolveHCaptchaAsync( { var content = CreateTaskRequest(); - if (proxy != null) + if (proxy is not null) { content.Task = new HCaptchaTask { @@ -274,7 +274,7 @@ public override async Task SolveHCaptchaAsync( .ConfigureAwait(false); return await GetResult( - response.Deserialize(), CaptchaType.HCaptcha, + response.Deserialize(), CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false); } @@ -285,7 +285,7 @@ public override async Task SolveGeeTestAsync( { var content = CreateTaskRequest(); - if (proxy != null) + if (proxy is not null) { content.Task = new GeeTestTask { @@ -306,30 +306,70 @@ public override async Task SolveGeeTestAsync( }; } - var response = await _httpClient.PostJsonToStringAsync - ("createTask", + var response = await _httpClient.PostJsonToStringAsync( + "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - response.Deserialize(), CaptchaType.GeeTest, + response.Deserialize(), CaptchaType.GeeTest, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (proxy?.Host is not null) + { + content.Task = new TurnstileTask + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + Action = action, + TurnstileCData = data + }.SetProxy(proxy); + } + else + { + content.Task = new TurnstileTaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + Action = action, + TurnstileCData = data + }; + } + + var response = await _httpClient.PostJsonToStringAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response.Deserialize(), CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false); } + #endregion #region Getting the result private async Task GetResult( - TaskCreationResponse response, CaptchaType type, + TaskCreationAntiCaptchaResponse antiCaptchaResponse, CaptchaType type, CancellationToken cancellationToken = default) where T : CaptchaResponse { - if (response.IsError) + if (antiCaptchaResponse.IsError) { - throw new TaskCreationException($"{response.ErrorCode}: {response.ErrorDescription}"); + throw new TaskCreationException($"{antiCaptchaResponse.ErrorCode}: {antiCaptchaResponse.ErrorDescription}"); } - var task = new CaptchaTask(response.TaskId, type); + var task = new CaptchaTask(antiCaptchaResponse.TaskId, type); return await GetResult(task, cancellationToken).ConfigureAwait(false); } @@ -344,7 +384,7 @@ private async Task GetResult( new GetTaskResultRequest { ClientKey = ApiKey, TaskId = (int)task.Id }, cancellationToken: cancellationToken).ConfigureAwait(false); - var result = response.Deserialize(); + var result = response.Deserialize(); if (!result.IsReady) { @@ -366,17 +406,18 @@ private async Task GetResult( throw new TaskSolutionException(response); } - result.Solution = task.Type switch + result.AntiCaptchaTaskSolution = task.Type switch { CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3 or CaptchaType.HCaptcha => - solution.ToObject()! as Solution, - CaptchaType.FunCaptcha => solution.ToObject()!, - CaptchaType.ImageCaptcha => solution.ToObject(), - CaptchaType.GeeTest => solution.ToObject(), + solution.ToObject()! as AntiCaptchaTaskSolution, + CaptchaType.FunCaptcha => solution.ToObject()!, + CaptchaType.ImageCaptcha => solution.ToObject(), + CaptchaType.GeeTest => solution.ToObject(), + CaptchaType.CloudflareTurnstile => solution.ToObject(), _ => throw new NotSupportedException($"The {task.Type} captcha type is not supported") } ?? throw new TaskSolutionException(response); - return result.Solution.ToCaptchaResponse(task.Id) as T; + return result.AntiCaptchaTaskSolution.ToCaptchaResponse(task.Id) as T; } #endregion @@ -388,11 +429,22 @@ public override async Task ReportSolution( { if (correct) { - throw new NotSupportedException("This service doesn't allow reporting of good solutions"); + if (type is not CaptchaType.ReCaptchaV2 && type is not CaptchaType.ReCaptchaV3) + { + throw new NotSupportedException( + "Reporting correct solutions is only supported for ReCaptchaV2 and ReCaptchaV3"); + } + + await _httpClient.PostJsonToStringAsync( + "reportCorrectRecaptcha", + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return; } string response; - ReportIncorrectCaptchaResponse incResponse; + ReportIncorrectCaptchaAntiCaptchaResponse incAntiCaptchaResponse; switch (type) { @@ -402,7 +454,7 @@ public override async Task ReportSolution( new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); - incResponse = response.Deserialize(); + incAntiCaptchaResponse = response.Deserialize(); break; case CaptchaType.ReCaptchaV2: @@ -412,14 +464,23 @@ public override async Task ReportSolution( new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); - incResponse = response.Deserialize(); + incAntiCaptchaResponse = response.Deserialize(); + break; + + case CaptchaType.HCaptcha: + response = await _httpClient.PostJsonToStringAsync( + "reportIncorrectHcaptcha", + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, + cancellationToken: cancellationToken).ConfigureAwait(false); + + incAntiCaptchaResponse = response.Deserialize(); break; default: throw new NotSupportedException("Reporting is not supported for this captcha type"); } - if (incResponse.NotFoundOrExpired) + if (incAntiCaptchaResponse.NotFoundOrExpired) { throw new TaskReportException("Captcha not found or expired"); } diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index ab2417e..25c0734 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -220,7 +220,7 @@ public override async Task SolveCapyAsync( } /// - public override async Task SolveCloudflareTurnstileAsync( + public override async Task SolveCloudflareTurnstileAsync( string siteKey, string siteUrl, string? action = null, string? data = null, string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) { @@ -236,7 +236,7 @@ public override async Task SolveCloudflareTurnstileAsync( cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( + return await GetResult( response, CaptchaType.CloudflareTurnstile, cancellationToken); } @@ -306,8 +306,7 @@ private async Task GetResult( throw new TaskSolutionException("The task is not a GeeTest captcha"); } - var geeTestResponseJson = response.Response; - var geeTestResponse = JObject.Parse(geeTestResponseJson); + var geeTestResponse = JObject.Parse(response.Response); return new GeeTestResponse { @@ -318,6 +317,22 @@ private async Task GetResult( } // TODO: Handle Capy response + + else if (typeof(T) == typeof(CloudflareTurnstileResponse)) + { + if (task.Type is not CaptchaType.CloudflareTurnstile) + { + throw new TaskSolutionException("The task is not a Cloudflare Turnstile captcha"); + } + + var cloudflareResponse = JObject.Parse(response.Response); + + return new CloudflareTurnstileResponse + { + Response = cloudflareResponse["Response"]!.Value()!, + UserAgent = cloudflareResponse["UserAgent"]!.Value()! + } as T; + } // If it's not a StringResponse, throw if (typeof(T) != typeof(StringResponse)) diff --git a/CaptchaSharp/Services/TwoCaptcha/CapyResponse.cs b/CaptchaSharp/Services/TwoCaptcha/CapyResponse.cs deleted file mode 100644 index 5657e29..0000000 --- a/CaptchaSharp/Services/TwoCaptcha/CapyResponse.cs +++ /dev/null @@ -1,27 +0,0 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Services.TwoCaptcha -{ - internal class TwoCaptchaCapyResponse : Response - { - public new CapySolution Request { get; set; } - } - - internal class CapySolution - { - public string CaptchaKey { get; set; } - public string ChallengeKey { get; set; } - public string Answer { get; set; } - - public CapyResponse ToCapyResponse(long id) - { - return new CapyResponse() - { - Id = id, - CaptchaKey = CaptchaKey, - ChallengeKey = ChallengeKey, - Answer = Answer - }; - } - } -} diff --git a/CaptchaSharp/Services/TwoCaptcha/GeeTestResponse.cs b/CaptchaSharp/Services/TwoCaptcha/GeeTestResponse.cs deleted file mode 100644 index b48547a..0000000 --- a/CaptchaSharp/Services/TwoCaptcha/GeeTestResponse.cs +++ /dev/null @@ -1,33 +0,0 @@ -using CaptchaSharp.Models; -using Newtonsoft.Json; - -namespace CaptchaSharp.Services.TwoCaptcha -{ - internal class TwoCaptchaGeeTestResponse : Response - { - public new GeeTestSolution Request { get; set; } - } - - internal class GeeTestSolution - { - [JsonProperty(PropertyName = "geetest_challenge")] - public string Challenge { get; set; } - - [JsonProperty(PropertyName = "geetest_validate")] - public string Validate { get; set; } - - [JsonProperty(PropertyName = "geetest_seccode")] - public string Seccode { get; set; } - - public GeeTestResponse ToGeeTestResponse(long id) - { - return new GeeTestResponse() - { - Id = id, - Challenge = Challenge, - Validate = Validate, - SecCode = Seccode - }; - } - } -} diff --git a/CaptchaSharp/Services/TwoCaptcha/Response.cs b/CaptchaSharp/Services/TwoCaptcha/Response.cs deleted file mode 100644 index e2c1f15..0000000 --- a/CaptchaSharp/Services/TwoCaptcha/Response.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Newtonsoft.Json; - -namespace CaptchaSharp.Services.TwoCaptcha -{ - internal class Response - { - public int Status { get; set; } - public string Request { get; set; } - public string Error_Text { get; set; } - - [JsonIgnore] - public bool Success => Status == 1; - - [JsonIgnore] - public bool IsErrorCode => Status == 0 && Request.Contains("ERROR"); - } -} diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs new file mode 100644 index 0000000..b8e230e --- /dev/null +++ b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs @@ -0,0 +1,26 @@ +using CaptchaSharp.Models; + +namespace CaptchaSharp.Services.TwoCaptcha; + +internal class TwoCaptchaCapyResponse : TwoCaptchaResponse +{ + public new CapySolution? Request { get; set; } +} + +internal class CapySolution +{ + public string? CaptchaKey { get; set; } + public string? ChallengeKey { get; set; } + public string? Answer { get; set; } + + public CapyResponse ToCapyResponse(long id) + { + return new CapyResponse() + { + Id = id, + CaptchaKey = CaptchaKey, + ChallengeKey = ChallengeKey, + Answer = Answer + }; + } +} diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs new file mode 100644 index 0000000..69a66a4 --- /dev/null +++ b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs @@ -0,0 +1,23 @@ +using CaptchaSharp.Models; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.TwoCaptcha; + +internal class TwoCaptchaCloudflareTurnstileResponse : TwoCaptchaResponse +{ + /// + /// The User-Agent used to solve the challenge. + /// + [JsonProperty("useragent")] + public string? UserAgent { get; set; } + + public CloudflareTurnstileResponse ToCloudflareTurnstileResponse(long id) + { + return new CloudflareTurnstileResponse() + { + Id = id, + Response = Request, + UserAgent = UserAgent + }; + } +} diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs new file mode 100644 index 0000000..cabdc8c --- /dev/null +++ b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs @@ -0,0 +1,32 @@ +using CaptchaSharp.Models; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.TwoCaptcha; + +internal class TwoCaptchaGeeTestResponse : TwoCaptchaResponse +{ + public new GeeTestSolution Request { get; set; } +} + +internal class GeeTestSolution +{ + [JsonProperty(PropertyName = "geetest_challenge")] + public string Challenge { get; set; } + + [JsonProperty(PropertyName = "geetest_validate")] + public string Validate { get; set; } + + [JsonProperty(PropertyName = "geetest_seccode")] + public string Seccode { get; set; } + + public GeeTestResponse ToGeeTestResponse(long id) + { + return new GeeTestResponse() + { + Id = id, + Challenge = Challenge, + Validate = Validate, + SecCode = Seccode + }; + } +} diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaResponse.cs b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaResponse.cs new file mode 100644 index 0000000..8c65266 --- /dev/null +++ b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.TwoCaptcha; + +internal class TwoCaptchaResponse +{ + public int Status { get; set; } + public string? Request { get; set; } + + [JsonProperty("Error_Text")] + public string? ErrorText { get; set; } + + [JsonIgnore] + public bool Success => Status == 1; + + [JsonIgnore] + public bool IsErrorCode => + Status == 0 && Request is not null && Request.Contains("ERROR"); +} diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index b4ca879..4971269 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -68,7 +68,7 @@ public override async Task GetBalanceAsync(CancellationToken cancellati if (UseJsonFlag) { - var tcResponse = response.Deserialize(); + var tcResponse = response.Deserialize(); if (tcResponse.IsErrorCode) { @@ -106,7 +106,7 @@ public override async Task SolveTextCaptchaAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.TextCaptcha, + response.Deserialize(), CaptchaType.TextCaptcha, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.TextCaptcha, @@ -132,7 +132,7 @@ public override async Task SolveImageCaptchaAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.ImageCaptcha, + response.Deserialize(), CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.ImageCaptcha, @@ -163,7 +163,7 @@ public override async Task SolveRecaptchaV2Async( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV2, + response.Deserialize(), CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.ReCaptchaV2, @@ -195,7 +195,7 @@ public override async Task SolveRecaptchaV3Async( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV3, + response.Deserialize(), CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.ReCaptchaV3, @@ -225,7 +225,7 @@ public override async Task SolveFuncaptchaAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.FunCaptcha, + response.Deserialize(), CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.FunCaptcha, @@ -253,7 +253,7 @@ public override async Task SolveHCaptchaAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.HCaptcha, + response.Deserialize(), CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.HCaptcha, @@ -284,7 +284,7 @@ public override async Task SolveKeyCaptchaAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.KeyCaptcha, + response.Deserialize(), CaptchaType.KeyCaptcha, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.KeyCaptcha, @@ -314,7 +314,7 @@ public override async Task SolveGeeTestAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.GeeTest, + response.Deserialize(), CaptchaType.GeeTest, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.GeeTest, @@ -342,7 +342,7 @@ public override async Task SolveCapyAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.Capy, + response.Deserialize(), CaptchaType.Capy, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.Capy, @@ -376,7 +376,7 @@ public override async Task SolveDataDomeAsync( return UseJsonFlag ? await GetResult( - response.Deserialize(), CaptchaType.DataDome, + response.Deserialize(), CaptchaType.DataDome, cancellationToken).ConfigureAwait(false) : await GetResult( response, CaptchaType.DataDome, @@ -384,7 +384,7 @@ public override async Task SolveDataDomeAsync( } /// - public override async Task SolveCloudflareTurnstileAsync( + public override async Task SolveCloudflareTurnstileAsync( string siteKey, string siteUrl, string? action = null, string? data = null, string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) @@ -413,10 +413,10 @@ public override async Task SolveCloudflareTurnstileAsync( .ConfigureAwait(false); return UseJsonFlag - ? await GetResult( - response.Deserialize(), CaptchaType.CloudflareTurnstile, + ? await GetResult( + response.Deserialize(), CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false) - : await GetResult( + : await GetResult( response, CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false); } @@ -425,13 +425,15 @@ public override async Task SolveCloudflareTurnstileAsync( #region Getting the result private async Task GetResult( - Response response, CaptchaType type, CancellationToken cancellationToken = default) + TwoCaptchaResponse twoCaptchaResponse, CaptchaType type, CancellationToken cancellationToken = default) where T : CaptchaResponse { - if (response.IsErrorCode) - throw new TaskCreationException(response.Request); + if (twoCaptchaResponse.IsErrorCode) + { + throw new TaskCreationException(twoCaptchaResponse.Request!); + } - var task = new CaptchaTask(response.Request, type); + var task = new CaptchaTask(twoCaptchaResponse.Request!, type); return await GetResult(task, cancellationToken).ConfigureAwait(false); } @@ -499,18 +501,23 @@ internal async Task GetResult( if (solution.Type == JTokenType.Object) { return response.Deserialize() - .Request.ToCapyResponse(task.Id) as T; + .Request!.ToCapyResponse(task.Id) as T; } } + else if (task.Type == CaptchaType.CloudflareTurnstile) + { + return response.Deserialize() + .ToCloudflareTurnstileResponse(task.Id) as T; + } - var tcResponse = response.Deserialize(); + var tcResponse = response.Deserialize(); if (tcResponse.IsErrorCode) { - throw new TaskSolutionException(tcResponse.Error_Text); + throw new TaskSolutionException(tcResponse.ErrorText!); } - return new StringResponse { Id = task.Id, Response = tcResponse.Request } as T; + return new StringResponse { Id = task.Id, Response = tcResponse.Request! } as T; } if (IsErrorCode(response)) @@ -546,7 +553,7 @@ public override async Task ReportSolution( if (UseJsonFlag) { - var tcResponse = response.Deserialize(); + var tcResponse = response.Deserialize(); if (tcResponse.IsErrorCode) throw new TaskReportException(tcResponse.Request); From 71c609430bacd65ddf9d89e0f3a8aa940340524f Mon Sep 17 00:00:00 2001 From: Ruri Date: Wed, 17 Jul 2024 23:05:36 +0200 Subject: [PATCH 19/67] Added HumanCoder, renamed DeCaptcher to CaptchaCoder and expanded DBC --- ...ceTests.cs => CaptchaCoderServiceTests.cs} | 14 +- CaptchaSharp.Tests/ConfigFixture.cs | 3 +- .../DeathByCaptchaServiceTests.cs | 18 ++ CaptchaSharp.Tests/HumanCoderServiceTests.cs | 29 ++ CaptchaSharp/CaptchaSharp.csproj | 2 +- CaptchaSharp/CaptchaSharp.xml | 88 ++++-- CaptchaSharp/Extensions/StringExtensions.cs | 18 +- .../Models/CloudflareTurnstileResponse.cs | 2 +- .../CaptchaCoder/CaptchaCoderResponse.cs | 41 +++ ...tcherService.cs => CaptchaCoderService.cs} | 28 +- .../Services/DeCaptcher/DeCaptcherResponse.cs | 42 --- .../Responses/CapyDbcResponse.cs | 15 + .../Responses/GeeTestDbcResponse.cs | 15 + .../Tasks/CapyDbcTaskProxyless.cs | 15 + .../DeathByCaptcha/Tasks/DBCTaskProxyless.cs | 10 +- .../Tasks/FunCaptchaDbcTaskProxyless.cs | 12 + .../Tasks/FuncaptchaTaskProxyless.cs | 8 - .../Tasks/GeeTestDbcTaskProxyless.cs | 15 + .../Tasks/HCaptchaDbcTaskProxyless.cs | 12 + .../Tasks/KeyCaptchaDbcTaskProxyless.cs | 22 ++ .../Tasks/Proxied/CapyDbcTask.cs | 15 + .../Proxied/CloudflareTurnstileDbcTask.cs | 15 + .../DeathByCaptcha/Tasks/Proxied/DBCTask.cs | 25 -- .../Tasks/Proxied/DataDomeDbcTask.cs | 12 + .../DeathByCaptcha/Tasks/Proxied/DbcTask.cs | 28 ++ .../Tasks/Proxied/FunCaptchaDbcTask.cs | 12 + .../Tasks/Proxied/FuncaptchaTask.cs | 8 - .../Tasks/Proxied/GeeTestDbcTask.cs | 15 + .../Tasks/Proxied/HCaptchaDbcTask.cs | 12 + .../Tasks/Proxied/KeyCaptchaDbcTask.cs | 22 ++ .../Tasks/Proxied/RecaptchaV2DbcTask.cs | 12 + .../Tasks/Proxied/RecaptchaV2Task.cs | 8 - .../Tasks/Proxied/RecaptchaV3DbcTask.cs | 18 ++ .../Tasks/Proxied/RecaptchaV3Task.cs | 10 - .../Tasks/RecaptchaV2DbcTaskProxyless.cs | 12 + .../Tasks/RecaptchaV2TaskProxyless.cs | 8 - .../Tasks/RecaptchaV3DbcTaskProxyless.cs | 18 ++ .../Tasks/RecaptchaV3TaskProxyless.cs | 10 - .../Services/DeathByCaptchaService.cs | 295 ++++++++++++++++-- CaptchaSharp/Services/HumanCoderService.cs | 18 ++ 40 files changed, 768 insertions(+), 214 deletions(-) rename CaptchaSharp.Tests/{DeCaptcherServiceTests.cs => CaptchaCoderServiceTests.cs} (73%) create mode 100644 CaptchaSharp.Tests/HumanCoderServiceTests.cs create mode 100644 CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs rename CaptchaSharp/Services/{DeCaptcherService.cs => CaptchaCoderService.cs} (86%) delete mode 100644 CaptchaSharp/Services/DeCaptcher/DeCaptcherResponse.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Responses/CapyDbcResponse.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Responses/GeeTestDbcResponse.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/FuncaptchaTaskProxyless.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DBCTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DbcTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FuncaptchaTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2Task.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3Task.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2TaskProxyless.cs create mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3TaskProxyless.cs create mode 100644 CaptchaSharp/Services/HumanCoderService.cs diff --git a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs b/CaptchaSharp.Tests/CaptchaCoderServiceTests.cs similarity index 73% rename from CaptchaSharp.Tests/DeCaptcherServiceTests.cs rename to CaptchaSharp.Tests/CaptchaCoderServiceTests.cs index 24c440e..c30a7fb 100644 --- a/CaptchaSharp.Tests/DeCaptcherServiceTests.cs +++ b/CaptchaSharp.Tests/CaptchaCoderServiceTests.cs @@ -5,22 +5,22 @@ namespace CaptchaSharp.Tests; -public class DeCaptcherFixture : ServiceFixture +public class CaptchaCoderFixture : ServiceFixture { - public DeCaptcherFixture() + public CaptchaCoderFixture() { - Service = new DeCaptcherService(Config.Credentials.DeCaptcherApiKey); + Service = new CaptchaCoderService(Config.Credentials.CaptchaCoderApiKey); } } -public class DeCaptcherServiceTests(DeCaptcherFixture fixture, ITestOutputHelper output) - : ServiceTests(fixture, output), IClassFixture +public class CaptchaCoderServiceTests(CaptchaCoderFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); - + // Do not overly use this test, or you will get banned. [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); - + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 3d3b3e4..3b590d8 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -40,7 +40,8 @@ public class Credentials public bool CustomTwoCaptchaOverrideHostHeader { get; set; } = true; public string DeathByCaptchaUsername { get; set; } = string.Empty; public string DeathByCaptchaPassword { get; set; } = string.Empty; - public string DeCaptcherApiKey { get; set; } = string.Empty; + public string CaptchaCoderApiKey { get; set; } = string.Empty; + public string HumanCoderApiKey { get; set; } = string.Empty; public string ImageTyperzApiKey { get; set; } = string.Empty; public string CapMonsterHost { get; set; } = string.Empty; public int CapMonsterPort { get; set; } = 80; diff --git a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs index f0cef9c..11fd476 100644 --- a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs @@ -19,11 +19,29 @@ public class DeathByCaptchaServiceTests(DeathByCaptchaFixture fixture, ITestOutp : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolutionAsync_ValidCaptcha_Reported() => ReportImageSolutionTest(correct: false); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveKeyCaptchaAsync_NoProxy_ValidSolution() => KeyCaptchaTest_NoProxy(); + [Fact] public Task SolveKeyCaptchaAsync_WithProxy_ValidSolution() => KeyCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); + [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); + [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/HumanCoderServiceTests.cs b/CaptchaSharp.Tests/HumanCoderServiceTests.cs new file mode 100644 index 0000000..03d1d3f --- /dev/null +++ b/CaptchaSharp.Tests/HumanCoderServiceTests.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class HumanCoderFixture : ServiceFixture +{ + public HumanCoderFixture() + { + Service = new HumanCoderService(Config.Credentials.HumanCoderApiKey); + } +} + +public class HumanCoderServiceTests(HumanCoderFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + + // Do not overly use this test, or you will get banned. + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); + + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.csproj b/CaptchaSharp/CaptchaSharp.csproj index cda56e8..ef58445 100644 --- a/CaptchaSharp/CaptchaSharp.csproj +++ b/CaptchaSharp/CaptchaSharp.csproj @@ -9,7 +9,7 @@ MIT https://github.com/openbullet/CaptchaSharp https://github.com/openbullet/CaptchaSharp - Captcha, Solver, Service, API, 2Captcha, TwoCaptcha, AntiCaptcha, Anti-Captcha, DeathByCaptcha, DBC, DeCaptcher, ImageTyperz + Captcha, Solver, Service, API, 2Captcha, TwoCaptcha, AntiCaptcha, Anti-Captcha, CaptchaCoder, DeathByCaptcha, DBC, ImageTyperz 1.0.13 Added capsolver.com and datadome captcha True diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 1225c9c..3dbd58c 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -727,14 +727,13 @@ Deserializes a json string to a given type. + + Serializes an object to a json string. + Serializes an object to a json string and converts the property names to a camelCase based convention. - - Serializes an object to a json string and converts the property names - to a lowercase based convention. - A generic captcha response. @@ -1060,6 +1059,42 @@ + + + The service provided by https://captchacoder.com/ + + + + + Your secret api key. + + + + The default used for requests. + + + + Initializes a . + + + + + + + + + + + + + + + + + + + + The service provided by https://captchas.io/ @@ -1148,49 +1183,40 @@ - + - + - - - The service provided by https://captchacoder.com/ - - - - - Your secret api key. - - - - The default used for requests. - - - - Initializes a . - - - - + - + - + - + - + - + + + + The service provided by https://humancoder.com/ + + + + + Initializes a . + + The service provided by https://www.imagetyperz.com/ diff --git a/CaptchaSharp/Extensions/StringExtensions.cs b/CaptchaSharp/Extensions/StringExtensions.cs index cd28693..ece57a5 100644 --- a/CaptchaSharp/Extensions/StringExtensions.cs +++ b/CaptchaSharp/Extensions/StringExtensions.cs @@ -1,4 +1,3 @@ -using CaptchaSharp.Helpers; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -13,6 +12,12 @@ public static T Deserialize(this string json) where T : notnull return JsonConvert.DeserializeObject(json) ?? throw new JsonSerializationException("Failed to deserialize json string."); } + + /// Serializes an object to a json string. + public static string Serialize(this T obj) + { + return JsonConvert.SerializeObject(obj); + } /// Serializes an object to a json string and converts the property names /// to a camelCase based convention. @@ -24,15 +29,4 @@ public static string SerializeCamelCase(this T obj) }; return JsonConvert.SerializeObject(obj, settings); } - - /// Serializes an object to a json string and converts the property names - /// to a lowercase based convention. - public static string SerializeLowerCase(this T obj) - { - var settings = new JsonSerializerSettings - { - ContractResolver = new LowercasePropertyNamesContractResolver() - }; - return JsonConvert.SerializeObject(obj, settings); - } } diff --git a/CaptchaSharp/Models/CloudflareTurnstileResponse.cs b/CaptchaSharp/Models/CloudflareTurnstileResponse.cs index dd95410..c3ec1ea 100644 --- a/CaptchaSharp/Models/CloudflareTurnstileResponse.cs +++ b/CaptchaSharp/Models/CloudflareTurnstileResponse.cs @@ -14,5 +14,5 @@ public class CloudflareTurnstileResponse : CaptchaResponse /// The user agent used to solve the challenge. It must be also used /// when submitting the response to the target website. /// - public required string UserAgent { get; set; } + public string? UserAgent { get; set; } } diff --git a/CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs b/CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs new file mode 100644 index 0000000..d47e524 --- /dev/null +++ b/CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs @@ -0,0 +1,41 @@ +namespace CaptchaSharp.Services.CaptchaCoder; + +internal class CaptchaCoderResponse +{ + public int ResultCode { get; set; } + public int MajorID { get; set; } + public int MinorID { get; set; } + public int Type { get; set; } + public int Timeout { get; set; } + public string Text { get; set; } + + public static CaptchaCoderResponse Parse(string str) + { + // ResultCode|MajorID|MinorID|Type|Timeout|Text + // 0|107|44685|0|0|n7hjks + var split = str.Split(['|'], 6); + return new CaptchaCoderResponse + { + ResultCode = int.Parse(split[0]), + MajorID = int.Parse(split[1]), + MinorID = int.Parse(split[2]), + Type = int.Parse(split[3]), + Timeout = int.Parse(split[4]), + Text = split[5] + }; + } + + public static bool TryParse(string str, out CaptchaCoderResponse? response) + { + try + { + response = Parse(str); + return true; + } + catch + { + response = null; + return false; + } + } +} diff --git a/CaptchaSharp/Services/DeCaptcherService.cs b/CaptchaSharp/Services/CaptchaCoderService.cs similarity index 86% rename from CaptchaSharp/Services/DeCaptcherService.cs rename to CaptchaSharp/Services/CaptchaCoderService.cs index c0437f4..82838a4 100644 --- a/CaptchaSharp/Services/DeCaptcherService.cs +++ b/CaptchaSharp/Services/CaptchaCoderService.cs @@ -1,7 +1,6 @@ using CaptchaSharp.Enums; using CaptchaSharp.Exceptions; using CaptchaSharp.Models; -using CaptchaSharp.Services.DeCaptcher; using System; using System.Globalization; using System.Net.Http; @@ -14,37 +13,36 @@ namespace CaptchaSharp.Services; /// /// The service provided by https://captchacoder.com/ /// -public class DeCaptcherService : CaptchaService +public class CaptchaCoderService : CaptchaService { /// /// Your secret api key. /// public string ApiKey { get; set; } - /// The default used for requests. - private readonly HttpClient _httpClient; + /// The default used for requests. + protected readonly HttpClient HttpClient; /// - /// Initializes a . + /// Initializes a . /// - /// - public DeCaptcherService(string apiKey, HttpClient? httpClient = null) + public CaptchaCoderService(string apiKey, HttpClient? httpClient = null) { ApiKey = apiKey; - this._httpClient = httpClient ?? new HttpClient(); + this.HttpClient = httpClient ?? new HttpClient(); - this._httpClient.BaseAddress = new Uri("http://api.captchacoder.com/"); + this.HttpClient.BaseAddress = new Uri("http://api.captchacoder.com/"); // Since this service replies directly with the solution to the task creation request // we need to set a high timeout here, or it will not finish in time - this._httpClient.Timeout = Timeout; + this.HttpClient.Timeout = Timeout; } #region Getting the Balance /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartToStringAsync( + var response = await HttpClient.PostMultipartToStringAsync( "Imagepost.ashx", new StringPairCollection() .Add("key", ApiKey) @@ -68,7 +66,7 @@ public override async Task SolveImageCaptchaAsync( { var captchaId = Guid.NewGuid().ToString(); - var response = await _httpClient.PostMultipartToStringAsync( + var response = await HttpClient.PostMultipartToStringAsync( "Imagepost.ashx", new StringPairCollection() .Add("key", ApiKey) @@ -99,7 +97,7 @@ public override async Task SolveRecaptchaV2Async( var captchaId = Guid.NewGuid().ToString(); - var response = await _httpClient.PostMultipartToStringAsync( + var response = await HttpClient.PostMultipartToStringAsync( "Imagepost.ashx", new StringPairCollection() .Add("key", ApiKey) @@ -132,7 +130,7 @@ public override async Task SolveRecaptchaV3Async( var captchaId = Guid.NewGuid().ToString(); - var response = await _httpClient.PostMultipartToStringAsync( + var response = await HttpClient.PostMultipartToStringAsync( "Imagepost.ashx", new StringPairCollection() .Add("key", ApiKey) @@ -168,7 +166,7 @@ public override Task ReportSolution( public override async Task ReportSolution( string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartToStringAsync( + var response = await HttpClient.PostMultipartToStringAsync( "Imagepost.ashx", new StringPairCollection() .Add("key", ApiKey) diff --git a/CaptchaSharp/Services/DeCaptcher/DeCaptcherResponse.cs b/CaptchaSharp/Services/DeCaptcher/DeCaptcherResponse.cs deleted file mode 100644 index cd5eafa..0000000 --- a/CaptchaSharp/Services/DeCaptcher/DeCaptcherResponse.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace CaptchaSharp.Services.DeCaptcher -{ - internal class DeCaptcherResponse - { - public int ResultCode { get; set; } - public int MajorID { get; set; } - public int MinorID { get; set; } - public int Type { get; set; } - public int Timeout { get; set; } - public string Text { get; set; } - - public static DeCaptcherResponse Parse(string str) - { - // ResultCode|MajorID|MinorID|Type|Timeout|Text - // 0|107|44685|0|0|n7hjks - var split = str.Split(new char[] { '|' }, 6); - return new DeCaptcherResponse - { - ResultCode = int.Parse(split[0]), - MajorID = int.Parse(split[1]), - MinorID = int.Parse(split[2]), - Type = int.Parse(split[3]), - Timeout = int.Parse(split[4]), - Text = split[5] - }; - } - - public static bool TryParse(string str, out DeCaptcherResponse response) - { - try - { - response = Parse(str); - return true; - } - catch - { - response = null; - return false; - } - } - } -} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Responses/CapyDbcResponse.cs b/CaptchaSharp/Services/DeathByCaptcha/Responses/CapyDbcResponse.cs new file mode 100644 index 0000000..c56e4d5 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Responses/CapyDbcResponse.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Responses; + +internal class CapyDbcResponse +{ + [JsonProperty("captchakey")] + public required string CaptchaKey { get; set; } + + [JsonProperty("challengekey")] + public required string ChallengeKey { get; set; } + + [JsonProperty("answer")] + public required string Answer { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Responses/GeeTestDbcResponse.cs b/CaptchaSharp/Services/DeathByCaptcha/Responses/GeeTestDbcResponse.cs new file mode 100644 index 0000000..8703aa3 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Responses/GeeTestDbcResponse.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Responses; + +internal class GeeTestDbcResponse +{ + [JsonProperty("challenge")] + public required string Challenge { get; set; } + + [JsonProperty("validate")] + public required string Validate { get; set; } + + [JsonProperty("seccode")] + public required string Seccode { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs new file mode 100644 index 0000000..4aa6e65 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; + +internal class CapyDbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("captchakey")] + public required string CaptchaKey { get; set; } + + [JsonProperty("api_server")] + public string ApiServer { get; set; } = "https://www.capy.me/"; + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs index 99418cc..d7f6347 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks +internal class DbcTaskProxyless { - internal class DBCTaskProxyless - { - } + } diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs new file mode 100644 index 0000000..855e3aa --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; + +internal class FunCaptchaDbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("publickey")] + public required string PublicKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/FuncaptchaTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/FuncaptchaTaskProxyless.cs deleted file mode 100644 index 3ffdda9..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/FuncaptchaTaskProxyless.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks -{ - internal class FuncaptchaTaskProxyless : DBCTaskProxyless - { - public string PublicKey { get; set; } - public string PageUrl { get; set; } - } -} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs new file mode 100644 index 0000000..533bad1 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; + +internal class GeeTestDbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("gt")] + public required string Gt { get; set; } + + [JsonProperty("challenge")] + public required string Challenge { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs new file mode 100644 index 0000000..efd6eaa --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; + +internal class HCaptchaDbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs new file mode 100644 index 0000000..eb37f12 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; + +internal class KeyCaptchaDbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("s_s_c_user_id")] + public required string UserId { get; set; } + + [JsonProperty("s_s_c_session_id")] + public required string SessionId { get; set; } + + [JsonProperty("s_s_c_web_server_sign")] + + public required string WebServerSign { get; set; } + + [JsonProperty("s_s_c_web_server_sign2")] + public required string WebServerSign2 { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs new file mode 100644 index 0000000..43ad95f --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class CapyDbcTask : DbcTask +{ + [JsonProperty("captchakey")] + public required string CaptchaKey { get; set; } + + [JsonProperty("api_server")] + public string ApiServer { get; set; } = "https://www.capy.me/"; + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs new file mode 100644 index 0000000..ff93a5f --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class CloudflareTurnstileDbcTask : DbcTask +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } + + [JsonProperty("action")] + public string? Action { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DBCTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DBCTask.cs deleted file mode 100644 index 8f0dc82..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DBCTask.cs +++ /dev/null @@ -1,25 +0,0 @@ -using CaptchaSharp.Enums; -using CaptchaSharp.Models; -using System; - -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied -{ - internal class DBCTask : DBCTaskProxyless - { - public string Proxy { get; set; } - public string ProxyType { get; set; } = "HTTP"; - - public DBCTask SetProxy(Proxy proxy) - { - if (proxy.Type != Enums.ProxyType.HTTP && proxy.Type != Enums.ProxyType.HTTPS) - throw new NotSupportedException($"DBC only supports HTTP proxies"); - - if (proxy.RequiresAuthentication) - Proxy = $"http://{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}"; - else - Proxy = $"http://{proxy.Host}:{proxy.Port}"; - - return this; - } - } -} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs new file mode 100644 index 0000000..7a1b2f8 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class DataDomeDbcTask : DbcTask +{ + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } + + [JsonProperty("captcha_url")] + public required string CaptchaUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DbcTask.cs new file mode 100644 index 0000000..48799d5 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DbcTask.cs @@ -0,0 +1,28 @@ +using CaptchaSharp.Models; +using System; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class DbcTask : DbcTaskProxyless +{ + [JsonProperty("proxy")] + public string? Proxy { get; set; } + + [JsonProperty("proxytype")] + public string ProxyType { get; set; } = "HTTP"; + + public DbcTask SetProxy(Proxy proxy) + { + if (proxy.Type is not Enums.ProxyType.HTTP && proxy.Type is not Enums.ProxyType.HTTPS) + { + throw new NotSupportedException("DBC only supports HTTP proxies"); + } + + Proxy = proxy.RequiresAuthentication + ? $"http://{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}" + : $"http://{proxy.Host}:{proxy.Port}"; + + return this; + } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs new file mode 100644 index 0000000..9e7c4b4 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class FunCaptchaDbcTask : DbcTask +{ + [JsonProperty("publickey")] + public required string PublicKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FuncaptchaTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FuncaptchaTask.cs deleted file mode 100644 index cc74a79..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FuncaptchaTask.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied -{ - internal class FuncaptchaTask : DBCTask - { - public string PublicKey { get; set; } - public string PageUrl { get; set; } - } -} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs new file mode 100644 index 0000000..1ed893d --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class GeeTestDbcTask : DbcTask +{ + [JsonProperty("gt")] + public required string Gt { get; set; } + + [JsonProperty("challenge")] + public required string Challenge { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs new file mode 100644 index 0000000..f6cb250 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class HCaptchaDbcTask : DbcTask +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs new file mode 100644 index 0000000..6a5f2e6 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class KeyCaptchaDbcTask : DbcTask +{ + [JsonProperty("s_s_c_user_id")] + public required string UserId { get; set; } + + [JsonProperty("s_s_c_session_id")] + public required string SessionId { get; set; } + + [JsonProperty("s_s_c_web_server_sign")] + + public required string WebServerSign { get; set; } + + [JsonProperty("s_s_c_web_server_sign2")] + public required string WebServerSign2 { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs new file mode 100644 index 0000000..1eea419 --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class RecaptchaV2DbcTask : DbcTask +{ + [JsonProperty("googlekey")] + public required string GoogleKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2Task.cs deleted file mode 100644 index 2ff0899..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2Task.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied -{ - internal class RecaptchaV2Task : DBCTask - { - public string GoogleKey { get; set; } - public string PageUrl { get; set; } - } -} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs new file mode 100644 index 0000000..90c4edf --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; + +internal class RecaptchaV3DbcTask : DbcTask +{ + [JsonProperty("googlekey")] + public required string GoogleKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } + + [JsonProperty("action")] + public string? Action { get; set; } + + [JsonProperty("min_score")] + public float MinScore { get; set; } = 0.3F; +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3Task.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3Task.cs deleted file mode 100644 index c43d3c7..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3Task.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied -{ - internal class RecaptchaV3Task : DBCTask - { - public string GoogleKey { get; set; } - public string PageUrl { get; set; } - public string Action { get; set; } - public float Min_Score { get; set; } = 0.3F; - } -} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs new file mode 100644 index 0000000..fc5b6ab --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; + +internal class RecaptchaV2DbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("googlekey")] + public required string GoogleKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2TaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2TaskProxyless.cs deleted file mode 100644 index 9738fc2..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2TaskProxyless.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks -{ - internal class RecaptchaV2TaskProxyless : DBCTaskProxyless - { - public string GoogleKey { get; set; } - public string PageUrl { get; set; } - } -} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs new file mode 100644 index 0000000..ecaaa2c --- /dev/null +++ b/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; + +internal class RecaptchaV3DbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("googlekey")] + public required string GoogleKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } + + [JsonProperty("action")] + public string? Action { get; set; } + + [JsonProperty("min_score")] + public float MinScore { get; set; } = 0.3f; +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3TaskProxyless.cs deleted file mode 100644 index 3efacb7..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3TaskProxyless.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks -{ - internal class RecaptchaV3TaskProxyless : DBCTaskProxyless - { - public string GoogleKey { get; set; } - public string PageUrl { get; set; } - public string Action { get; set; } - public float Min_Score { get; set; } = 0.3F; - } -} diff --git a/CaptchaSharp/Services/DeathByCaptchaService.cs b/CaptchaSharp/Services/DeathByCaptchaService.cs index 4c19e50..7aab941 100644 --- a/CaptchaSharp/Services/DeathByCaptchaService.cs +++ b/CaptchaSharp/Services/DeathByCaptchaService.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using System.Web; using CaptchaSharp.Extensions; +using CaptchaSharp.Services.DeathByCaptcha.Responses; namespace CaptchaSharp.Services; @@ -53,7 +54,6 @@ public DeathByCaptchaService(string username, string password, HttpClient? httpC Password = password; this._httpClient = httpClient ?? new HttpClient(); - // TODO: Use https instead of http if possible this._httpClient.BaseAddress = new Uri("http://api.dbcapi.me/api/"); } @@ -91,8 +91,8 @@ public override async Task GetBalanceAsync(CancellationToken cancellati public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostAsync - ("captcha", + var response = await _httpClient.PostAsync( + "captcha", GetAuthPair() .Add("captchafile", $"base64:{base64}") .ToMultipartFormDataContent(), @@ -107,11 +107,11 @@ public override async Task SolveRecaptchaV2Async( string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - DBCTaskProxyless task; + DbcTaskProxyless task; if (proxy is not null) { - task = new RecaptchaV2Task + task = new RecaptchaV2DbcTask { GoogleKey = siteKey, PageUrl = siteUrl @@ -119,7 +119,7 @@ public override async Task SolveRecaptchaV2Async( } else { - task = new RecaptchaV2TaskProxyless + task = new RecaptchaV2DbcTaskProxyless { GoogleKey = siteKey, PageUrl = siteUrl @@ -130,7 +130,7 @@ public override async Task SolveRecaptchaV2Async( "captcha", GetAuthPair() .Add("type", 4) - .Add("token_params", task.SerializeLowerCase()), + .Add("token_params", task.Serialize()), cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -141,29 +141,29 @@ public override async Task SolveRecaptchaV2Async( /// public override async Task SolveRecaptchaV3Async( - string siteKey, string siteUrl, string action, float minScore, bool enterprise = false, - Proxy? proxy = null, CancellationToken cancellationToken = default) + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - DBCTaskProxyless task; + DbcTaskProxyless task; if (proxy is not null) { - task = new RecaptchaV3Task + task = new RecaptchaV3DbcTask { GoogleKey = siteKey, PageUrl = siteUrl, Action = action, - Min_Score = minScore + MinScore = minScore }.SetProxy(proxy); } else { - task = new RecaptchaV3TaskProxyless + task = new RecaptchaV3DbcTaskProxyless { GoogleKey = siteKey, PageUrl = siteUrl, Action = action, - Min_Score = minScore + MinScore = minScore }; } @@ -171,7 +171,7 @@ public override async Task SolveRecaptchaV3Async( "captcha", GetAuthPair() .Add("type", 5) - .Add("token_params", task.SerializeLowerCase()), + .Add("token_params", task.Serialize()), cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -185,11 +185,11 @@ public override async Task SolveFuncaptchaAsync( string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - DBCTaskProxyless task; + DbcTaskProxyless task; if (proxy is not null) { - task = new FuncaptchaTask + task = new FunCaptchaDbcTask { PublicKey = publicKey, PageUrl = siteUrl @@ -197,7 +197,7 @@ public override async Task SolveFuncaptchaAsync( } else { - task = new FuncaptchaTaskProxyless + task = new FunCaptchaDbcTaskProxyless { PublicKey = publicKey, PageUrl = siteUrl @@ -208,7 +208,7 @@ public override async Task SolveFuncaptchaAsync( "captcha", GetAuthPair() .Add("type", 6) - .Add("funcaptcha_params", task.SerializeLowerCase()), + .Add("funcaptcha_params", task.Serialize()), cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -216,6 +216,227 @@ public override async Task SolveFuncaptchaAsync( HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), CaptchaType.FunCaptcha, cancellationToken); } + + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + DbcTaskProxyless task; + + if (proxy is not null) + { + task = new HCaptchaDbcTask + { + SiteKey = siteKey, + PageUrl = siteUrl + }.SetProxy(proxy); + } + else + { + task = new HCaptchaDbcTaskProxyless + { + SiteKey = siteKey, + PageUrl = siteUrl + }; + } + + var response = await _httpClient.PostAsync( + "captcha", + GetAuthPair() + .Add("type", 7) + .Add("hcaptcha_params", task.Serialize()), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.HCaptcha, cancellationToken); + } + + /// + public override async Task SolveKeyCaptchaAsync( + string userId, string sessionId, string webServerSign1, string webServerSign2, string siteUrl, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + DbcTaskProxyless task; + + if (proxy is not null) + { + task = new KeyCaptchaDbcTask + { + UserId = userId, + SessionId = sessionId, + WebServerSign = webServerSign1, + WebServerSign2 = webServerSign2, + PageUrl = siteUrl + }.SetProxy(proxy); + } + else + { + task = new KeyCaptchaDbcTaskProxyless + { + UserId = userId, + SessionId = sessionId, + WebServerSign = webServerSign1, + WebServerSign2 = webServerSign2, + PageUrl = siteUrl + }; + } + + var response = await _httpClient.PostAsync( + "captcha", + GetAuthPair() + .Add("type", 10) + .Add("keycaptcha_params", task.Serialize()), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.KeyCaptcha, cancellationToken); + } + + /// + public override async Task SolveGeeTestAsync( + string gt, string challenge, string siteUrl, string? apiServer = null, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + DbcTaskProxyless task; + + if (proxy is not null) + { + task = new GeeTestDbcTask + { + Gt = gt, + Challenge = challenge, + PageUrl = siteUrl, + }.SetProxy(proxy); + } + else + { + task = new GeeTestDbcTaskProxyless + { + Gt = gt, + Challenge = challenge, + PageUrl = siteUrl, + }; + } + + var response = await _httpClient.PostAsync( + "captcha", + GetAuthPair() + .Add("type", 8) + .Add("geetest_params", task.Serialize()), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.GeeTest, cancellationToken); + } + + /// + public override async Task SolveCapyAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + DbcTaskProxyless task; + + if (proxy is not null) + { + task = new CapyDbcTask + { + CaptchaKey = siteKey, + PageUrl = siteUrl + }.SetProxy(proxy); + } + else + { + task = new CapyDbcTaskProxyless + { + CaptchaKey = siteKey, + PageUrl = siteUrl + }; + } + + var response = await _httpClient.PostAsync( + "captcha", + GetAuthPair() + .Add("type", 15) + .Add("capy_params", task.Serialize()), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.Capy, cancellationToken); + } + + /// + public override async Task SolveDataDomeAsync( + string siteUrl, string captchaUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + if (proxy?.Host is null) + { + throw new ArgumentNullException( + nameof(proxy), "DataDome captchas require a proxy"); + } + + // The DBC API will use the User-Agent defined on this page + // to solve the captcha, so the same one MUST be used to submit + // the response: https://deathbycaptcha.com/api/datadome + + var task = new DataDomeDbcTask + { + PageUrl = siteUrl, + CaptchaUrl = captchaUrl + }.SetProxy(proxy); + + var response = await _httpClient.PostAsync( + "captcha", + GetAuthPair() + .Add("type", 21) + .Add("datadome_params", task.Serialize()), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.DataDome, cancellationToken); + } + + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + if (proxy?.Host is null) + { + throw new ArgumentNullException( + nameof(proxy), "Cloudflare Turnstile captchas require a proxy"); + } + + var task = new CloudflareTurnstileDbcTask + { + SiteKey = siteKey, + PageUrl = siteUrl, + Action = action, + }.SetProxy(proxy); + + var response = await _httpClient.PostAsync( + "captcha", + GetAuthPair() + .Add("type", 12) + .Add("turnstile_params", task.Serialize()), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.CloudflareTurnstile, cancellationToken); + } #endregion #region Getting the result @@ -261,8 +482,40 @@ private async Task GetResult( { throw new TaskSolutionException(GetErrorMessage(query)); } + + if (typeof(T) == typeof(GeeTestResponse)) + { + var geeTestResponse = text.Deserialize(); + return new GeeTestResponse + { + Id = task.Id, + Challenge = geeTestResponse.Challenge, + Validate = geeTestResponse.Validate, + SecCode = geeTestResponse.Seccode + } as T; + } + + if (typeof(T) == typeof(CapyResponse)) + { + var capyResponse = text.Deserialize(); + return new CapyResponse + { + Id = task.Id, + CaptchaKey = capyResponse.CaptchaKey, + ChallengeKey = capyResponse.ChallengeKey, + Answer = capyResponse.Answer + } as T; + } + + if (typeof(T) == typeof(CloudflareTurnstileResponse)) + { + return new CloudflareTurnstileResponse + { + Id = task.Id, + Response = text, + } as T; + } - // Only StringResponse is supported if (typeof(T) != typeof(StringResponse)) { throw new NotSupportedException(); @@ -278,7 +531,9 @@ public override async Task ReportSolution( long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { if (correct) + { throw new NotSupportedException("This service doesn't allow reporting of good solutions"); + } var response = await _httpClient.PostAsync( $"captcha/{id}/report", diff --git a/CaptchaSharp/Services/HumanCoderService.cs b/CaptchaSharp/Services/HumanCoderService.cs new file mode 100644 index 0000000..0bab864 --- /dev/null +++ b/CaptchaSharp/Services/HumanCoderService.cs @@ -0,0 +1,18 @@ +using System; +using System.Net.Http; + +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://humancoder.com/ +/// +public class HumanCoderService : CaptchaCoderService +{ + /// + /// Initializes a . + /// + public HumanCoderService(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) + { + this.HttpClient.BaseAddress = new Uri("http://fasttypers.org"); + } +} From 8c259b037c29b520c5c17e200fea0a7c61c02070 Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 19 Jul 2024 00:52:34 +0200 Subject: [PATCH 20/67] Improved NineKwService for 9kw.eu --- CaptchaSharp.Tests/NineKwServiceTests.cs | 39 +++ .../Services/NineKw/NineKwBalanceResponse.cs | 12 + .../Services/NineKw/NineKwCheckResponse.cs | 21 ++ .../Services/NineKw/NineKwResponse.cs | 12 + CaptchaSharp/Services/NineKw/NineKwStatus.cs | 12 + .../Services/NineKw/NineKwSubmitResponse.cs | 9 + .../{NineKWService.cs => NineKwService.cs} | 244 +++++++++++------- 7 files changed, 251 insertions(+), 98 deletions(-) create mode 100644 CaptchaSharp.Tests/NineKwServiceTests.cs create mode 100644 CaptchaSharp/Services/NineKw/NineKwBalanceResponse.cs create mode 100644 CaptchaSharp/Services/NineKw/NineKwCheckResponse.cs create mode 100644 CaptchaSharp/Services/NineKw/NineKwResponse.cs create mode 100644 CaptchaSharp/Services/NineKw/NineKwStatus.cs create mode 100644 CaptchaSharp/Services/NineKw/NineKwSubmitResponse.cs rename CaptchaSharp/Services/{NineKWService.cs => NineKwService.cs} (59%) diff --git a/CaptchaSharp.Tests/NineKwServiceTests.cs b/CaptchaSharp.Tests/NineKwServiceTests.cs new file mode 100644 index 0000000..266abc8 --- /dev/null +++ b/CaptchaSharp.Tests/NineKwServiceTests.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class NineKwFixture : ServiceFixture +{ + public NineKwFixture() + { + Service = new NineKwService( + Config.Credentials.NineKWApiKey); + } +} + +public class NineKwServiceTests(NineKwFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolutionAsync_ValidCaptcha_Reported() => ReportImageSolutionTest(); + [Fact] public Task ReportRecaptchaSolutionAsync_ValidCaptcha_Reported() => ReportRecaptchaSolutionTest(); + [Fact] public Task SolveTextCaptchaAsync_ValidCaptcha_ValidSolution() => TextCaptchaTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); +} diff --git a/CaptchaSharp/Services/NineKw/NineKwBalanceResponse.cs b/CaptchaSharp/Services/NineKw/NineKwBalanceResponse.cs new file mode 100644 index 0000000..ce6715f --- /dev/null +++ b/CaptchaSharp/Services/NineKw/NineKwBalanceResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.NineKw; + +internal class NineKwBalanceResponse : NineKwResponse +{ + [JsonProperty("message")] + public required string Message { get; set; } + + [JsonProperty("credits")] + public required long Credits { get; set; } +} diff --git a/CaptchaSharp/Services/NineKw/NineKwCheckResponse.cs b/CaptchaSharp/Services/NineKw/NineKwCheckResponse.cs new file mode 100644 index 0000000..c29aa07 --- /dev/null +++ b/CaptchaSharp/Services/NineKw/NineKwCheckResponse.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.NineKw; + +internal class NineKwCheckResponse : NineKwResponse +{ + [JsonProperty("answer")] + public required string Answer { get; set; } + + [JsonProperty("message")] + public required string Message { get; set; } + + [JsonProperty("try_again")] + public required int TryAgain { get; set; } + + [JsonProperty("timeout")] + public required long Timeout { get; set; } + + [JsonProperty("credits")] + public required long Credits { get; set; } +} diff --git a/CaptchaSharp/Services/NineKw/NineKwResponse.cs b/CaptchaSharp/Services/NineKw/NineKwResponse.cs new file mode 100644 index 0000000..6174d93 --- /dev/null +++ b/CaptchaSharp/Services/NineKw/NineKwResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.NineKw; + +internal class NineKwResponse +{ + [JsonProperty("status")] + public required NineKwStatus Status { get; set; } + + [JsonProperty("error")] + public string? Error { get; set; } +} diff --git a/CaptchaSharp/Services/NineKw/NineKwStatus.cs b/CaptchaSharp/Services/NineKw/NineKwStatus.cs new file mode 100644 index 0000000..22ff034 --- /dev/null +++ b/CaptchaSharp/Services/NineKw/NineKwStatus.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.NineKw; + +internal class NineKwStatus +{ + [JsonProperty("https")] + public required int Https { get; set; } + + [JsonProperty("success")] + public required bool Success { get; set; } +} diff --git a/CaptchaSharp/Services/NineKw/NineKwSubmitResponse.cs b/CaptchaSharp/Services/NineKw/NineKwSubmitResponse.cs new file mode 100644 index 0000000..d19ff2f --- /dev/null +++ b/CaptchaSharp/Services/NineKw/NineKwSubmitResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.NineKw; + +internal class NineKwSubmitResponse : NineKwResponse +{ + [JsonProperty("captchaid")] + public required string CaptchaId { get; set; } +} diff --git a/CaptchaSharp/Services/NineKWService.cs b/CaptchaSharp/Services/NineKwService.cs similarity index 59% rename from CaptchaSharp/Services/NineKWService.cs rename to CaptchaSharp/Services/NineKwService.cs index 8040e56..41a17f6 100644 --- a/CaptchaSharp/Services/NineKWService.cs +++ b/CaptchaSharp/Services/NineKwService.cs @@ -3,12 +3,11 @@ using CaptchaSharp.Models; using System; using System.Collections.Generic; -using System.Globalization; using System.Net.Http; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using CaptchaSharp.Extensions; +using CaptchaSharp.Services.NineKw; namespace CaptchaSharp.Services; @@ -47,55 +46,66 @@ public NineKwService(string apiKey, HttpClient? httpClient = null) /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchaguthaben"), - cancellationToken) + var json = await _httpClient.GetStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchaguthaben") + .Add("json", 1), + cancellationToken) .ConfigureAwait(false); + var response = json.Deserialize(); + if (IsError(response)) { throw new BadAuthenticationException(GetErrorMessage(response)); } - return decimal.Parse(response, CultureInfo.InvariantCulture); + return Convert.ToDecimal(response.Credits); } #endregion #region Solve Methods /// - public override async Task SolveTextCaptchaAsync - (string text, TextCaptchaOptions? options = default, CancellationToken cancellationToken = default) + public override async Task SolveTextCaptchaAsync( + string text, TextCaptchaOptions? options = default, + CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartToStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchaupload") - .Add("file-upload-01", text) - .Add("textonly", 1) - .ToMultipartFormDataContent(), - cancellationToken) + var json = await _httpClient.PostMultipartToStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchaupload") + .Add("file-upload-01", text) + .Add("textonly", 1) + .Add("json", 1) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); + var response = json.Deserialize(); + return await GetResult( response, CaptchaType.TextCaptcha, cancellationToken).ConfigureAwait(false); } /// - public override async Task SolveImageCaptchaAsync - (string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartToStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchaupload") - .Add("file-upload-01", base64) - .Add("base64", 1) - .Add(ConvertCapabilities(options)) - .ToMultipartFormDataContent(), - cancellationToken) + var json = await _httpClient.PostMultipartToStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchaupload") + .Add("file-upload-01", base64) + .Add("base64", 1) + .Add("json", 1) + .Add(ConvertCapabilities(options)) + .ToMultipartFormDataContent(), + cancellationToken) .ConfigureAwait(false); + + var response = json.Deserialize(); if (IsError(response)) { @@ -111,17 +121,20 @@ public override async Task SolveRecaptchaV2Async( string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchaupload") - .Add("interactive", 1) - .Add("oldsource", "recaptchav2") - .Add("file-upload-01", siteKey) - .Add("pageurl", siteUrl) - .Add(ConvertProxy(proxy)), - cancellationToken) + var json = await _httpClient.GetStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchaupload") + .Add("interactive", 1) + .Add("oldsource", "recaptchav2") + .Add("file-upload-01", siteKey) + .Add("pageurl", siteUrl) + .Add("json", 1) + .Add(ConvertProxy(proxy)), + cancellationToken) .ConfigureAwait(false); + + var response = json.Deserialize(); return await GetResult( response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); @@ -132,20 +145,23 @@ public override async Task SolveRecaptchaV3Async( string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchaupload") - .Add("interactive", 1) - .Add("oldsource", "recaptchav3") - .Add("file-upload-01", siteKey) - .Add("pageurl", siteUrl) - .Add(ConvertProxy(proxy)), - cancellationToken) + var json = await _httpClient.GetStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchaupload") + .Add("interactive", 1) + .Add("oldsource", "recaptchav3") + .Add("file-upload-01", siteKey) + .Add("pageurl", siteUrl) + .Add("json", 1) + .Add(ConvertProxy(proxy)), + cancellationToken) .ConfigureAwait(false); + var response = json.Deserialize(); + return await GetResult( - response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); + response, CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false); } /// @@ -153,54 +169,61 @@ public override async Task SolveFuncaptchaAsync( string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchaupload") - .Add("interactive", 1) - .Add("oldsource", "funcaptcha") - .Add("file-upload-01", publicKey) - .Add("pageurl", siteUrl) - .Add(ConvertProxy(proxy)), - cancellationToken) + var json = await _httpClient.GetStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchaupload") + .Add("interactive", 1) + .Add("oldsource", "funcaptcha") + .Add("file-upload-01", publicKey) + .Add("pageurl", siteUrl) + .Add("json", 1) + .Add(ConvertProxy(proxy)), + cancellationToken) .ConfigureAwait(false); + + var response = json.Deserialize(); return await GetResult( - response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); + response, CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false); } /// - public override async Task SolveHCaptchaAsync - (string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchaupload") - .Add("interactive", 1) - .Add("oldsource", "hcaptcha") - .Add("file-upload-01", siteKey) - .Add("pageurl", siteUrl) - .Add(ConvertProxy(proxy)), - cancellationToken) + var json = await _httpClient.GetStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchaupload") + .Add("interactive", 1) + .Add("oldsource", "hcaptcha") + .Add("file-upload-01", siteKey) + .Add("pageurl", siteUrl) + .Add("json", 1) + .Add(ConvertProxy(proxy)), + cancellationToken) .ConfigureAwait(false); + + var response = json.Deserialize(); return await GetResult( - response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); + response, CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false); } #endregion #region Getting the result private async Task GetResult( - string response, CaptchaType type, CancellationToken cancellationToken = default) + NineKwSubmitResponse response, CaptchaType type, CancellationToken cancellationToken = default) where T : CaptchaResponse { if (IsError(response)) { - throw new TaskCreationException(response); + throw new TaskCreationException(GetErrorMessage(response)); } - var task = new CaptchaTask(response, type); + var task = new CaptchaTask(response.CaptchaId, type); return await GetResult(task, cancellationToken).ConfigureAwait(false); } @@ -210,25 +233,33 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var response = await _httpClient.GetStringAsync - ("index.cgi", - GetAuthPair() - .Add("action", "usercaptchacorrectdata") - .Add("id", task.Id), - cancellationToken) + var json = await _httpClient.GetStringAsync( + "index.cgi", + GetAuthPair() + .Add("action", "usercaptchacorrectdata") + .Add("id", task.Id) + .Add("json", 1), + cancellationToken) .ConfigureAwait(false); - + + var response = json.Deserialize(); + // Not solved yet - if (string.IsNullOrEmpty(response) || response.Contains("CAPTCHA_NOT_READY")) + if (response.TryAgain is 1) { return null; } task.Completed = true; - if (IsError(response) || response.Contains("ERROR_NO_USER")) + if (response.Answer == "ERROR NO USER") + { + throw new TaskSolutionException("No workers available"); + } + + if (IsError(response)) { - throw new TaskSolutionException(response); + throw new TaskSolutionException(GetErrorMessage(response)); } // Only StringResponse is supported @@ -237,7 +268,7 @@ private async Task GetResult( throw new NotSupportedException(); } - return new StringResponse { Id = task.Id, Response = response } as T; + return new StringResponse { Id = task.Id, Response = response.Answer } as T; } #endregion @@ -246,16 +277,21 @@ private async Task GetResult( public override async Task ReportSolution( long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync - ("index.cgi", + var json = await _httpClient.GetStringAsync( + "index.cgi", GetAuthPair() .Add("action", "usercaptchacorrectback") .Add("id", id.ToString()) - .Add("correct", correct ? 1 : 2), + .Add("correct", correct ? 1 : 2) + .Add("json", 1), cancellationToken); + + var response = json.Deserialize(); if (IsError(response)) - throw new TaskReportException(response); + { + throw new TaskReportException(GetErrorMessage(response)); + } } #endregion @@ -263,11 +299,11 @@ public override async Task ReportSolution( private StringPairCollection GetAuthPair() => new StringPairCollection().Add("apikey", ApiKey); - private static bool IsError(string response) - => Regex.IsMatch(response, @"^\d{4} "); + private static bool IsError(NineKwResponse response) + => !string.IsNullOrEmpty(response.Error); - private static string GetErrorMessage(string response) - => Regex.Replace(response, @"^\d{4} ", ""); + private static string GetErrorMessage(NineKwResponse response) + => response.Error ?? "Unknown error"; #endregion #region Proxies @@ -290,8 +326,20 @@ private static string GetErrorMessage(string response) { proxyParams.Add(("cookies", proxy.GetCookieString())); } - - // TODO: Check if credentials are supported + + if (!string.IsNullOrEmpty(proxy.Username)) + { + throw new NotSupportedException( + "9kw.eu does not support proxies with authentication."); + } + + if (proxy.Type is not ProxyType.HTTP && + proxy.Type is not ProxyType.HTTPS && + proxy.Type is not ProxyType.SOCKS5) + { + throw new NotSupportedException( + "9kw.eu only supports HTTP, HTTPS and SOCKS5 proxies."); + } if (!string.IsNullOrEmpty(proxy.Host)) { From 73bd05749a8bd5db2fe40d8d88234695122697d2 Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 19 Jul 2024 00:53:43 +0200 Subject: [PATCH 21/67] Removed SolveCaptchaService (inactive) --- CaptchaSharp/Services/SolveCaptchaService.cs | 27 -------------------- 1 file changed, 27 deletions(-) delete mode 100644 CaptchaSharp/Services/SolveCaptchaService.cs diff --git a/CaptchaSharp/Services/SolveCaptchaService.cs b/CaptchaSharp/Services/SolveCaptchaService.cs deleted file mode 100644 index 82e3ea3..0000000 --- a/CaptchaSharp/Services/SolveCaptchaService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using CaptchaSharp.Enums; -using System.Net.Http; - -namespace CaptchaSharp.Services; - -/// -/// The service provided by https://solvecaptcha.com/ -/// -public class SolveCaptchaService : CustomTwoCaptchaService -{ - /// - /// Initializes a . - /// - /// The API key to use. - /// The to use for requests. If null, a default one will be created. - public SolveCaptchaService(string apiKey, HttpClient? httpClient = null) - : base(apiKey, new Uri("http://api.solvecaptcha.com"), httpClient, false) - { - SupportedCaptchaTypes = - CaptchaType.TextCaptcha | - CaptchaType.ImageCaptcha | - CaptchaType.ReCaptchaV2 | - CaptchaType.FunCaptcha | - CaptchaType.KeyCaptcha; - } -} From a19a55d9ad4bbcccf01b0387a7432dc717da3c26 Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 19 Jul 2024 13:25:14 +0200 Subject: [PATCH 22/67] Added CapmonsterCloudService --- .../CapMonsterCloudServiceTests.cs | 41 ++++++++ CaptchaSharp.Tests/ConfigFixture.cs | 1 + CaptchaSharp.Tests/ServiceTests.cs | 1 + CaptchaSharp/CaptchaSharp.xml | 47 ++++++--- .../Requests/CaptchaTaskRequest.cs | 13 ++- .../Services/AntiCaptcha/Requests/Request.cs | 9 +- .../Tasks/AntiCaptchaTaskProxyless.cs | 9 +- .../Responses/AntiCaptchaResponse.cs | 2 +- .../TaskCreationAntiCaptchaResponse.cs | 9 +- CaptchaSharp/Services/AntiCaptchaService.cs | 54 +++++++---- .../Requests/Tasks/CustomTaskProxyless.cs | 15 +++ .../Requests/Tasks/DataDomeTaskProxyless.cs | 30 ++++++ .../Services/CapMonsterCloudService.cs | 97 +++++++++++++++++++ .../Services/CustomAntiCaptchaService.cs | 2 +- 14 files changed, 273 insertions(+), 57 deletions(-) create mode 100644 CaptchaSharp.Tests/CapMonsterCloudServiceTests.cs create mode 100644 CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs create mode 100644 CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs create mode 100644 CaptchaSharp/Services/CapMonsterCloudService.cs diff --git a/CaptchaSharp.Tests/CapMonsterCloudServiceTests.cs b/CaptchaSharp.Tests/CapMonsterCloudServiceTests.cs new file mode 100644 index 0000000..a69107f --- /dev/null +++ b/CaptchaSharp.Tests/CapMonsterCloudServiceTests.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class CapMonsterCloudFixture : ServiceFixture +{ + public CapMonsterCloudFixture() + { + Service = new CapMonsterCloudService( + Config.Credentials.CapMonsterCloudApiKey); + } +} + +public class CapMonsterCloudServiceTests(CapMonsterCloudFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + + // Do not overuse this method, as it will get your account banned. + [Fact] public Task ReportSolutionAsync_ValidCaptcha_Reported() => ReportImageSolutionTest(correct: false); + + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); +} diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 3b590d8..0c23f99 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -53,4 +53,5 @@ public class Credentials public string TrueCaptchaUsername { get; set; } = string.Empty; public string NineKWApiKey { get; set; } = string.Empty; public string CapSolverApiKey { get; set; } = string.Empty; + public string CapMonsterCloudApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index cd36a43..e17fe26 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -378,6 +378,7 @@ private async Task DataDomeTest(Proxy proxy) // Get cid from "datadome" cookie var cid = cookieContainer.GetCookies(new Uri(site))["datadome"]?.Value; + proxy.Cookies = [("datadome", cid!)]; var captchaUrl = $"https://{host}/captcha/?initialCid={WebUtility.UrlEncode(initialCid)}&hash={hsh}&cid={cid}&t={t}&referer={WebUtility.UrlEncode(site)}&s={s}&e={e}&dm=cd"; diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 3dbd58c..d79ec4f 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -926,7 +926,7 @@ Your secret api key. - + The default used for requests. @@ -967,12 +967,27 @@ + + + Gets the result of a task. + + + + + Parses the solution of a DataDome captcha. + + + + + Creates a new . + + @@ -988,6 +1003,24 @@ The API key to use. The to use for requests. If null, a default one will be created. + + + The service provided by https://capmonster.cloud/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + + + + The service provided by the CapMonster OCR application by ZennoLab. @@ -1387,18 +1420,6 @@ The API key to use. The to use for requests. If null, a default one will be created. - - - The service provided by https://solvecaptcha.com/ - - - - - Initializes a . - - The API key to use. - The to use for requests. If null, a default one will be created. - The service provided by https://apitruecaptcha.org/ diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs index f924bc6..ae28797 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs @@ -1,11 +1,10 @@ using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; -namespace CaptchaSharp.Services.AntiCaptcha.Requests +namespace CaptchaSharp.Services.AntiCaptcha.Requests; + +public class CaptchaTaskRequest : Request { - internal class CaptchaTaskRequest : Request - { - public AntiCaptchaTaskProxyless Task { get; set; } - public int SoftId { get; set; } = 0; - public string LanguagePool { get; set; } = "en"; - } + public AntiCaptchaTaskProxyless Task { get; set; } + public int SoftId { get; set; } = 0; + public string LanguagePool { get; set; } = "en"; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Request.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/Request.cs index fb70bb0..96cf0d3 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Request.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/Request.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests +namespace CaptchaSharp.Services.AntiCaptcha.Requests; + +public class Request { - internal class Request - { - public string ClientKey { get; set; } = ""; - } + public string ClientKey { get; set; } = ""; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs index e8676a3..e653b01 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; + +public class AntiCaptchaTaskProxyless { - internal class AntiCaptchaTaskProxyless - { - public string Type { get; set; } - } + public string Type { get; set; } } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs index e713ccc..0657829 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs @@ -2,7 +2,7 @@ namespace CaptchaSharp.Services.AntiCaptcha.Responses; -internal class AntiCaptchaResponse +public class AntiCaptchaResponse { public int ErrorId { get; set; } public string? ErrorCode { get; set; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs index 3ae71cf..6de43b1 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Responses +namespace CaptchaSharp.Services.AntiCaptcha.Responses; + +public class TaskCreationAntiCaptchaResponse : AntiCaptchaResponse { - internal class TaskCreationAntiCaptchaResponse : AntiCaptchaResponse - { - public int TaskId { get; set; } - } + public int TaskId { get; set; } } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index df251d4..cb215c0 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -27,9 +27,9 @@ public class AntiCaptchaService : CaptchaService public string ApiKey { get; set; } /// - /// The default used for requests. + /// The default used for requests. /// - private readonly HttpClient _httpClient; + protected readonly HttpClient HttpClient; /// /// The ID of the software developer. @@ -40,19 +40,19 @@ public class AntiCaptchaService : CaptchaService /// Initializes a . /// /// Your secret api key. - /// The to use for requests. If null, a default one will be created. + /// The to use for requests. If null, a default one will be created. public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) { ApiKey = apiKey; - this._httpClient = httpClient ?? new HttpClient(); - this._httpClient.BaseAddress = new Uri("https://api.anti-captcha.com"); + this.HttpClient = httpClient ?? new HttpClient(); + this.HttpClient.BaseAddress = new Uri("https://api.anti-captcha.com"); } #region Getting the Balance /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "getBalance", new Request { ClientKey = ApiKey }, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -73,7 +73,7 @@ public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", AddImageCapabilities( new CaptchaTaskRequest @@ -153,7 +153,7 @@ public override async Task SolveRecaptchaV2Async( } } - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", content, cancellationToken: cancellationToken) @@ -190,7 +190,7 @@ public override async Task SolveRecaptchaV3Async( IsEnterprise = enterprise }; - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", content, cancellationToken: cancellationToken) @@ -232,7 +232,7 @@ public override async Task SolveFuncaptchaAsync( }; } - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", content, cancellationToken: cancellationToken) @@ -267,7 +267,7 @@ public override async Task SolveHCaptchaAsync( }; } - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", content, cancellationToken: cancellationToken) @@ -306,7 +306,7 @@ public override async Task SolveGeeTestAsync( }; } - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", content, cancellationToken: cancellationToken) @@ -345,7 +345,7 @@ public override async Task SolveCloudflareTurnstile }; } - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", content, cancellationToken: cancellationToken) @@ -359,7 +359,10 @@ public override async Task SolveCloudflareTurnstile #endregion #region Getting the result - private async Task GetResult( + /// + /// Gets the result of a task. + /// + protected async Task GetResult( TaskCreationAntiCaptchaResponse antiCaptchaResponse, CaptchaType type, CancellationToken cancellationToken = default) where T : CaptchaResponse @@ -379,7 +382,7 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "getTaskResult", new GetTaskResultRequest { ClientKey = ApiKey, TaskId = (int)task.Id }, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -419,6 +422,14 @@ private async Task GetResult( return result.AntiCaptchaTaskSolution.ToCaptchaResponse(task.Id) as T; } + + /// + /// Parses the solution of a DataDome captcha. + /// + protected virtual StringResponse ParseDataDomeSolution(JToken? solution) + { + throw new NotImplementedException(); + } #endregion #region Reporting the solution @@ -435,7 +446,7 @@ public override async Task ReportSolution( "Reporting correct solutions is only supported for ReCaptchaV2 and ReCaptchaV3"); } - await _httpClient.PostJsonToStringAsync( + await HttpClient.PostJsonToStringAsync( "reportCorrectRecaptcha", new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -449,7 +460,7 @@ await _httpClient.PostJsonToStringAsync( switch (type) { case CaptchaType.ImageCaptcha: - response = await _httpClient.PostJsonToStringAsync( + response = await HttpClient.PostJsonToStringAsync( "reportIncorrectImageCaptcha", new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -459,7 +470,7 @@ await _httpClient.PostJsonToStringAsync( case CaptchaType.ReCaptchaV2: case CaptchaType.ReCaptchaV3: - response = await _httpClient.PostJsonToStringAsync( + response = await HttpClient.PostJsonToStringAsync( "reportIncorrectRecaptcha", new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -468,7 +479,7 @@ await _httpClient.PostJsonToStringAsync( break; case CaptchaType.HCaptcha: - response = await _httpClient.PostJsonToStringAsync( + response = await HttpClient.PostJsonToStringAsync( "reportIncorrectHcaptcha", new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -488,7 +499,10 @@ await _httpClient.PostJsonToStringAsync( #endregion #region Private Methods - private CaptchaTaskRequest CreateTaskRequest() + /// + /// Creates a new . + /// + protected CaptchaTaskRequest CreateTaskRequest() { return new CaptchaTaskRequest { diff --git a/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs b/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs new file mode 100644 index 0000000..060c099 --- /dev/null +++ b/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs @@ -0,0 +1,15 @@ +using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.CapMonsterCloud.Requests.Tasks; + +internal abstract class CustomTaskProxyless : AntiCaptchaTaskProxyless +{ + [JsonProperty("class")] + public string Class { get; set; } + + protected CustomTaskProxyless() + { + Type = "CustomTask"; + } +} diff --git a/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs b/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs new file mode 100644 index 0000000..7c19654 --- /dev/null +++ b/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs @@ -0,0 +1,30 @@ +using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.CapMonsterCloud.Requests.Tasks; + +internal class DataDomeTaskProxyless : CustomTaskProxyless +{ + [JsonProperty("websiteURL")] + public required string WebsiteURL { get; set; } + + [JsonProperty("metadata")] + public required DataDomeMetadata Metadata { get; set; } + + public DataDomeTaskProxyless() + { + Class = "DataDome"; + } +} + +internal class DataDomeMetadata +{ + [JsonProperty("htmlPageBase64", NullValueHandling=NullValueHandling.Ignore)] + public string? HtmlPageBase64 { get; set; } + + [JsonProperty("captchaUrl", NullValueHandling=NullValueHandling.Ignore)] + public string? CaptchaUrl { get; set; } + + [JsonProperty("datadomeCookie")] + public required string DataDomeCookie { get; set; } +} diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs new file mode 100644 index 0000000..620ea81 --- /dev/null +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -0,0 +1,97 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; +using CaptchaSharp.Services.AntiCaptcha.Responses; +using CaptchaSharp.Services.CapMonsterCloud.Requests.Tasks; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://capmonster.cloud/ +/// +public class CapMonsterCloudService : CustomAntiCaptchaService +{ + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public CapMonsterCloudService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("https://api.capmonster.cloud"), httpClient) + { + SupportedCaptchaTypes = + Enums.CaptchaType.ImageCaptcha | + Enums.CaptchaType.ReCaptchaV2 | + Enums.CaptchaType.ReCaptchaV3 | + Enums.CaptchaType.HCaptcha | + Enums.CaptchaType.GeeTest | + Enums.CaptchaType.CloudflareTurnstile | + Enums.CaptchaType.DataDome; + } + + /// + public override async Task SolveDataDomeAsync( + string siteUrl, string captchaUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + // CapMonsterCloud will always use the current Windows OS User-Agent + // to solve captchas. + + if (proxy?.Cookies is null) + { + throw new ArgumentNullException( + nameof(proxy), "DataDome requires cookies"); + } + + // The cookie must contain datadome=... and nothing else + var datadomeCookie = Array.Find(proxy.Cookies, c => c.Item1 == "datadome").Item2; + + if (string.IsNullOrEmpty(datadomeCookie) || proxy.Cookies.Length > 1) + { + throw new ArgumentException( + "The cookie must contain a single datadome cookie", nameof(proxy)); + } + + var content = CreateTaskRequest(); + + content.Task = new DataDomeTaskProxyless + { + WebsiteURL = siteUrl, + Metadata = new DataDomeMetadata + { + CaptchaUrl = captchaUrl, + DataDomeCookie = $"datadome={datadomeCookie}" + } + }; + + var response = await HttpClient.PostJsonToStringAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response.Deserialize(), CaptchaType.DataDome, + cancellationToken).ConfigureAwait(false); + } + + /// + protected override StringResponse ParseDataDomeSolution(JToken? solution) + { + // The solution is like { "domains": { "site.com": { "cookies": { "datadome": "..." } } } } + // We want to return the datadome cookie. Since site.com varies, we need to + // take the first domain. + var cookie = solution + ?.SelectToken("domains") + ?.First?.First + ?.SelectToken("cookies.datadome") + ?.Value() ?? ""; + + return new StringResponse { Response = cookie }; + } +} diff --git a/CaptchaSharp/Services/CustomAntiCaptchaService.cs b/CaptchaSharp/Services/CustomAntiCaptchaService.cs index a963194..3da6e49 100644 --- a/CaptchaSharp/Services/CustomAntiCaptchaService.cs +++ b/CaptchaSharp/Services/CustomAntiCaptchaService.cs @@ -18,7 +18,7 @@ public class CustomAntiCaptchaService : AntiCaptchaService public CustomAntiCaptchaService(string apiKey, Uri baseUri, HttpClient? httpClient = null) : base(apiKey, httpClient) { - httpClient.BaseAddress = baseUri; + HttpClient.BaseAddress = baseUri; } #region Supported Types From 73ed5cc9ac0b7b50ab324c68748bb9e4b8097031 Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 19 Jul 2024 17:36:28 +0200 Subject: [PATCH 23/67] Added MetaBypassTechService --- CaptchaSharp.Tests/ConfigFixture.cs | 4 + .../MetaBypassTechServiceTests.cs | 34 ++ CaptchaSharp/CaptchaSharp.xml | 65 ++++ .../Extensions/HttpClientExtensions.cs | 16 + .../Services/CapMonsterCloudService.cs | 14 +- .../MetaBypassTechAccessTokenRequest.cs | 21 ++ .../MetaBypassTechAccessTokenResponse.cs | 27 ++ ...MetaBypassTechRefreshAccessTokenRequest.cs | 18 + .../MetaBypassTech/MetaBypassTechResponse.cs | 19 + .../MetaBypassTechSolveImageCaptchaRequest.cs | 18 + .../MetaBypassTechSolveRecaptchaRequest.cs | 15 + .../Services/MetaBypassTechService.cs | 349 ++++++++++++++++++ CaptchaSharp/Services/NineKwService.cs | 6 +- CaptchaSharp/Services/TwoCaptchaService.cs | 2 +- 14 files changed, 597 insertions(+), 11 deletions(-) create mode 100644 CaptchaSharp.Tests/MetaBypassTechServiceTests.cs create mode 100644 CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs create mode 100644 CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs create mode 100644 CaptchaSharp/Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs create mode 100644 CaptchaSharp/Services/MetaBypassTech/MetaBypassTechResponse.cs create mode 100644 CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs create mode 100644 CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs create mode 100644 CaptchaSharp/Services/MetaBypassTechService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 0c23f99..19cefdd 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -54,4 +54,8 @@ public class Credentials public string NineKWApiKey { get; set; } = string.Empty; public string CapSolverApiKey { get; set; } = string.Empty; public string CapMonsterCloudApiKey { get; set; } = string.Empty; + public string MetaBypassTechClientId { get; set; } = string.Empty; + public string MetaBypassTechClientSecret { get; set; } = string.Empty; + public string MetaBypassTechUsername { get; set; } = string.Empty; + public string MetaBypassTechPassword { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/MetaBypassTechServiceTests.cs b/CaptchaSharp.Tests/MetaBypassTechServiceTests.cs new file mode 100644 index 0000000..c9e04f9 --- /dev/null +++ b/CaptchaSharp.Tests/MetaBypassTechServiceTests.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class MetaBypassTechFixture : ServiceFixture +{ + public MetaBypassTechFixture() + { + Service = new MetaBypassTechService( + Config.Credentials.MetaBypassTechClientId, + Config.Credentials.MetaBypassTechClientSecret, + Config.Credentials.MetaBypassTechUsername, + Config.Credentials.MetaBypassTechPassword) + { + Timeout = TimeSpan.FromMinutes(5) + }; + } +} + +public class MetaBypassTechServiceTests(MetaBypassTechFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_InvalidImage_ThrowsException() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index d79ec4f..5c8fd26 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -716,6 +716,11 @@ response as a . The content converted to a . + + + Automatically builds a POST json string from a given object using encoding + + Automatically builds a POST json string from a given object using encoding and application/json Content-Type. @@ -1348,6 +1353,66 @@ The captcha id. + + + The service provided by https://metabypass.tech/. + + + + + The client ID to use. + + + + + The client secret to use. + + + + + The username. + + + + + The password. + + + + + The default used for requests. + + + + + The current access token. + + + + + Initializes a . + + The client ID to use. + The client secret to use. + The username. + The password. + The to use for requests. If null, a default one will be created. + + + + + + + + + + + + + + + + The service provided by https://www.9kw.eu/ diff --git a/CaptchaSharp/Extensions/HttpClientExtensions.cs b/CaptchaSharp/Extensions/HttpClientExtensions.cs index 68f4a22..7bb48d5 100644 --- a/CaptchaSharp/Extensions/HttpClientExtensions.cs +++ b/CaptchaSharp/Extensions/HttpClientExtensions.cs @@ -72,6 +72,22 @@ public static async Task PostMultipartToStringAsync( var response = await httpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false); return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); } + + /// + /// Automatically builds a POST json string from a given object using encoding + /// + public static async Task PostJsonAsync( + this HttpClient httpClient, string url, T content, bool camelizeKeys = true, + CancellationToken cancellationToken = default) where T : class + { + var json = camelizeKeys + ? content.SerializeCamelCase() + : JsonConvert.SerializeObject(content); + + return await httpClient.PostAsync(url, + new StringContent(json, Encoding.UTF8, "application/json"), + cancellationToken); + } /// Automatically builds a POST json string from a given object using encoding /// and application/json Content-Type. diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index 620ea81..97b8169 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -25,13 +25,13 @@ public CapMonsterCloudService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("https://api.capmonster.cloud"), httpClient) { SupportedCaptchaTypes = - Enums.CaptchaType.ImageCaptcha | - Enums.CaptchaType.ReCaptchaV2 | - Enums.CaptchaType.ReCaptchaV3 | - Enums.CaptchaType.HCaptcha | - Enums.CaptchaType.GeeTest | - Enums.CaptchaType.CloudflareTurnstile | - Enums.CaptchaType.DataDome; + CaptchaType.ImageCaptcha | + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3 | + CaptchaType.HCaptcha | + CaptchaType.GeeTest | + CaptchaType.CloudflareTurnstile | + CaptchaType.DataDome; } /// diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs new file mode 100644 index 0000000..0130812 --- /dev/null +++ b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.MetaBypassTech; + +internal class MetaBypassTechAccessTokenRequest +{ + [JsonProperty("grant_type")] + public required string GrantType { get; set; } + + [JsonProperty("client_id")] + public required string ClientId { get; set; } + + [JsonProperty("client_secret")] + public required string ClientSecret { get; set; } + + [JsonProperty("username")] + public required string Username { get; set; } + + [JsonProperty("password")] + public required string Password { get; set; } +} diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs new file mode 100644 index 0000000..eb22ed1 --- /dev/null +++ b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs @@ -0,0 +1,27 @@ +using System; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.MetaBypassTech; + +internal class MetaBypassTechAccessTokenResponse : MetaBypassTechResponse +{ + [JsonProperty("token_type")] + public required string TokenType { get; set; } + + [JsonProperty("expires_in")] + public required long ExpiresInSeconds { get; set; } + + [JsonProperty("access_token")] + public required string AccessToken { get; set; } + + [JsonProperty("refresh_token")] + public required string RefreshToken { get; set; } + + public DateTime ExpirationDate { get; set; } + + public MetaBypassTechAccessTokenResponse() + { + // Subtract 1 minute to avoid expiration issues + ExpirationDate = DateTime.Now.AddSeconds(ExpiresInSeconds) - TimeSpan.FromMinutes(1); + } +} diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs new file mode 100644 index 0000000..b08f00a --- /dev/null +++ b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.MetaBypassTech; + +internal class MetaBypassTechRefreshAccessTokenRequest +{ + [JsonProperty("grant_type")] + public required string GrantType { get; set; } + + [JsonProperty("client_id")] + public required string ClientId { get; set; } + + [JsonProperty("client_secret")] + public required string ClientSecret { get; set; } + + [JsonProperty("refresh_token")] + public required string RefreshToken { get; set; } +} diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechResponse.cs b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechResponse.cs new file mode 100644 index 0000000..b013928 --- /dev/null +++ b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services.MetaBypassTech; + +internal class MetaBypassTechResponse +{ + [JsonProperty("ok")] + public required bool Ok { get; set; } + + [JsonProperty("data")] + public JToken? Data { get; set; } + + [JsonProperty("status_code")] + public int StatusCode { get; set; } + + [JsonProperty("message")] + public string? Message { get; set; } +} diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs new file mode 100644 index 0000000..f86b7da --- /dev/null +++ b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.MetaBypassTech; + +internal class MetaBypassTechSolveImageCaptchaRequest +{ + [JsonProperty("image")] + public required string Base64Image { get; set; } + + [JsonProperty("numeric")] + public int Numeric { get; set; } + + [JsonProperty("min_len")] + public int MinLength { get; set; } + + [JsonProperty("max_len")] + public int MaxLength { get; set; } +} diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs new file mode 100644 index 0000000..93467fa --- /dev/null +++ b/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.MetaBypassTech; + +internal class MetaBypassTechSolveRecaptchaRequest +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("url")] + public required string Url { get; set; } + + [JsonProperty("version")] + public required string Version { get; set; } +} diff --git a/CaptchaSharp/Services/MetaBypassTechService.cs b/CaptchaSharp/Services/MetaBypassTechService.cs new file mode 100644 index 0000000..4593e31 --- /dev/null +++ b/CaptchaSharp/Services/MetaBypassTechService.cs @@ -0,0 +1,349 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Exceptions; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; +using CaptchaSharp.Services.MetaBypassTech; + +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://metabypass.tech/. +/// +public class MetaBypassTechService : CaptchaService +{ + /// + /// The client ID to use. + /// + public string ClientId { get; set; } + + /// + /// The client secret to use. + /// + public string ClientSecret { get; set; } + + /// + /// The username. + /// + public string Username { get; set; } + + /// + /// The password. + /// + public string Password { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /// + /// The current access token. + /// + private MetaBypassTechAccessTokenResponse? _accessToken; + + /// + /// Initializes a . + /// + /// The client ID to use. + /// The client secret to use. + /// The username. + /// The password. + /// The to use for requests. If null, a default one will be created. + public MetaBypassTechService(string clientId, string clientSecret, + string username, string password, HttpClient? httpClient = null) + { + ClientId = clientId; + ClientSecret = clientSecret; + Username = username; + Password = password; + + _httpClient = httpClient ?? new HttpClient(); + _httpClient.BaseAddress = new Uri("https://app.metabypass.tech/CaptchaSolver/"); + + // Since some captchas are returned directly in the response body, + // we need to set a high timeout to account for those requests + _httpClient.Timeout = Timeout; + } + + #region Getting the Balance + /// + public override async Task GetBalanceAsync( + CancellationToken cancellationToken = default) + { + await EnsureAccessTokenAsync().ConfigureAwait(false); + + var json = await _httpClient.GetStringAsync( + "api/v1/me", + cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Ok) + { + throw new BadAuthenticationException( + response.Message ?? "Unknown error"); + } + + return decimal.Parse(response.Data!["total_balance"]!.ToString()); + } + #endregion + + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + await EnsureAccessTokenAsync().ConfigureAwait(false); + + var numeric = 0; + + if (options is not null) + { + numeric = options.CharacterSet switch + { + CharacterSet.OnlyNumbers => 1, + CharacterSet.OnlyLetters => 2, + CharacterSet.OnlyNumbersOrOnlyLetters => 3, + CharacterSet.BothNumbersAndLetters => 4, + _ => 0 + }; + } + + var payload = new MetaBypassTechSolveImageCaptchaRequest + { + Base64Image = base64, + Numeric = numeric, + MinLength = options?.MinLength ?? 0, + MaxLength = options?.MaxLength ?? 0 + }; + + var json = await _httpClient.PostJsonToStringAsync( + "api/v1/services/captchaSolver", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Ok) + { + throw new TaskSolutionException( + response.Message ?? "Unknown error"); + } + + return new StringResponse + { + Id = 0, + Response = response.Data!["result"]!.ToString() + }; + } + + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, + bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + if (proxy?.Host is not null) + { + throw new NotSupportedException("Proxies are not supported by metabypass.tech"); + } + + await EnsureAccessTokenAsync().ConfigureAwait(false); + + // When using version "invisible", we get "Service Failed" as a response + // so we're just going to ignore it and use version "2" instead + + var payload = new MetaBypassTechSolveRecaptchaRequest + { + SiteKey = siteKey, + Url = siteUrl, + Version = "2" + }; + + var json = await _httpClient.PostJsonToStringAsync( + "api/v1/services/bypassReCaptcha", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Ok) + { + throw new TaskSolutionException( + response.Message ?? "Unknown error"); + } + + var captchaId = response.Data!["RecaptchaId"]!.ToString(); + + return await GetResult( + new CaptchaTask(captchaId, CaptchaType.ReCaptchaV2), + cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + if (proxy?.Host is not null) + { + throw new NotSupportedException("Proxies are not supported by metabypass.tech"); + } + + await EnsureAccessTokenAsync().ConfigureAwait(false); + + var payload = new MetaBypassTechSolveRecaptchaRequest + { + SiteKey = siteKey, + Url = siteUrl, + Version = "3" + }; + + var json = await _httpClient.PostJsonToStringAsync( + "api/v1/services/bypassReCaptcha", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Ok) + { + throw new TaskSolutionException( + response.Message ?? "Unknown error"); + } + + return new StringResponse + { + Id = 0, + Response = response.Data!["RecaptchaResponse"]!.ToString() + }; + } + + #endregion + + #region Getting the result + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) where T : class + { + if (task.Type is not CaptchaType.ReCaptchaV2) + { + throw new NotSupportedException( + "The getCaptchaResult method is only supported for ReCaptchaV2 tasks"); + } + + var json = await _httpClient.GetStringAsync( + "api/v1/services/getCaptchaResult", + new StringPairCollection() + .Add("recaptcha_id", task.Id), + cancellationToken).ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Ok) + { + throw new TaskSolutionException( + response.Message ?? "Unknown error"); + } + + if (response.Data!["step"]!.ToString() == "pending") + { + return null; + } + + return new StringResponse + { + IdString = task.IdString, + Response = response.Data!["RecaptchaResponse"]!.ToString() + } as T; + } + #endregion + + #region Private Methods + private async ValueTask EnsureAccessTokenAsync() + { + if (_accessToken is null) + { + await GetAccessTokenAsync().ConfigureAwait(false); + return; + } + + if (_accessToken.ExpirationDate < DateTime.Now) + { + await RefreshAccessTokenAsync(_accessToken).ConfigureAwait(false); + } + } + + private async Task GetAccessTokenAsync() + { + var payload = new MetaBypassTechAccessTokenRequest + { + GrantType = "password", + ClientId = ClientId, + ClientSecret = ClientSecret, + Username = Username, + Password = Password + }; + + using var response = await _httpClient.PostJsonAsync( + "oauth/token", + payload, + cancellationToken: default) + .ConfigureAwait(false); + + var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + if (!response.IsSuccessStatusCode) + { + var serviceResponse = json.Deserialize(); + + throw new BadAuthenticationException( + serviceResponse.Message ?? "Unknown error"); + } + + _accessToken = json.Deserialize(); + _httpClient.DefaultRequestHeaders.Add("Authorization", + $"{_accessToken.TokenType} {_accessToken.AccessToken}"); + } + + private async Task RefreshAccessTokenAsync(MetaBypassTechAccessTokenResponse tokenResponse) + { + var payload = new MetaBypassTechRefreshAccessTokenRequest + { + GrantType = "refresh_token", + ClientId = ClientId, + ClientSecret = ClientSecret, + RefreshToken = tokenResponse.RefreshToken + }; + + using var response = await _httpClient.PostJsonAsync( + "oauth/token", + payload, + cancellationToken: default) + .ConfigureAwait(false); + + var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + if (!response.IsSuccessStatusCode) + { + var serviceResponse = json.Deserialize(); + + throw new BadAuthenticationException( + serviceResponse.Message ?? "Unknown error"); + } + + _accessToken = json.Deserialize(); + _httpClient.DefaultRequestHeaders.Add("Authorization", + $"{_accessToken.TokenType} {_accessToken.AccessToken}"); + } + #endregion +} diff --git a/CaptchaSharp/Services/NineKwService.cs b/CaptchaSharp/Services/NineKwService.cs index 41a17f6..c34a07a 100644 --- a/CaptchaSharp/Services/NineKwService.cs +++ b/CaptchaSharp/Services/NineKwService.cs @@ -34,12 +34,12 @@ public class NineKwService : CaptchaService public NineKwService(string apiKey, HttpClient? httpClient = null) { ApiKey = apiKey; - this._httpClient = httpClient ?? new HttpClient(); - this._httpClient.BaseAddress = new Uri("https://www.9kw.eu/"); + _httpClient = httpClient ?? new HttpClient(); + _httpClient.BaseAddress = new Uri("https://www.9kw.eu/"); // Since this service replies directly with the solution to the task creation request (for image captchas) // we need to set a high timeout here, or it will not finish in time - this._httpClient.Timeout = Timeout; + _httpClient.Timeout = Timeout; } #region Getting the Balance diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 4971269..88062e4 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -461,7 +461,7 @@ internal async Task GetResult( .Add("action", "get") .Add("id", task.Id.ToString()) .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), - cancellationToken); + cancellationToken).ConfigureAwait(false); if (response.Contains("CAPCHA_NOT_READY")) { From 7f7a7a17a73484e68d5a5bb7533236ecabe59c9e Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 19 Jul 2024 22:00:28 +0200 Subject: [PATCH 24/67] Added NextCaptchaService --- CaptchaSharp.Tests/ConfigFixture.cs | 1 + CaptchaSharp.Tests/NextCaptchaServiceTests.cs | 32 +++++++++++++++++ CaptchaSharp/CaptchaSharp.xml | 34 ++++++++++++++++++- .../Requests/CaptchaTaskRequest.cs | 23 +++++++++++-- CaptchaSharp/Services/AntiCaptchaService.cs | 11 ++---- .../Services/CapMonsterCloudService.cs | 4 ++- CaptchaSharp/Services/NextCaptchaService.cs | 28 +++++++++++++++ 7 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 CaptchaSharp.Tests/NextCaptchaServiceTests.cs create mode 100644 CaptchaSharp/Services/NextCaptchaService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 19cefdd..9e75e39 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -58,4 +58,5 @@ public class Credentials public string MetaBypassTechClientSecret { get; set; } = string.Empty; public string MetaBypassTechUsername { get; set; } = string.Empty; public string MetaBypassTechPassword { get; set; } = string.Empty; + public string NextCaptchaApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/NextCaptchaServiceTests.cs b/CaptchaSharp.Tests/NextCaptchaServiceTests.cs new file mode 100644 index 0000000..dbb9aa9 --- /dev/null +++ b/CaptchaSharp.Tests/NextCaptchaServiceTests.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class NextCaptchaFixture : ServiceFixture +{ + public NextCaptchaFixture() + { + Service = new NextCaptchaService( + Config.Credentials.NextCaptchaApiKey); + } +} + +public class NextCaptchaServiceTests(NextCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 5c8fd26..5df2bab 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -936,7 +936,7 @@ The default used for requests. - + The ID of the software developer. @@ -996,6 +996,26 @@ + + + A request to solve a captcha task. + + + + + The task to solve. + + + + + The soft ID to use. Default is 0. + + + + + The language pool to use. Default is "en". + + The service provided by https://azcaptcha.com/ @@ -1413,6 +1433,18 @@ + + + The service offered by https://nextcaptcha.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + The service provided by https://www.9kw.eu/ diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs index ae28797..af7f6fa 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs @@ -1,10 +1,27 @@ using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +using Newtonsoft.Json; namespace CaptchaSharp.Services.AntiCaptcha.Requests; +/// +/// A request to solve a captcha task. +/// public class CaptchaTaskRequest : Request { - public AntiCaptchaTaskProxyless Task { get; set; } - public int SoftId { get; set; } = 0; - public string LanguagePool { get; set; } = "en"; + /// + /// The task to solve. + /// + public AntiCaptchaTaskProxyless Task { get; set; } = null!; + + /// + /// The soft ID to use. Default is 0. + /// + [JsonProperty("softId", DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? SoftId { get; set; } + + /// + /// The language pool to use. Default is "en". + /// + [JsonProperty("languagePool", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LanguagePool { get; set; } } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index cb215c0..d77507d 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -34,7 +34,7 @@ public class AntiCaptchaService : CaptchaService /// /// The ID of the software developer. /// - private const int _softId = 934; + protected int? SoftId = 934; /// /// Initializes a . @@ -79,7 +79,7 @@ public override async Task SolveImageCaptchaAsync( new CaptchaTaskRequest { ClientKey = ApiKey, - SoftId = _softId, + SoftId = SoftId, Task = new ImageCaptchaTask { Body = base64 @@ -169,11 +169,6 @@ public override async Task SolveRecaptchaV3Async( string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - if (proxy is not null) - { - throw new NotSupportedException("Proxies are not supported for ReCaptchaV3"); - } - if (minScore != 0.3f && minScore != 0.7f && minScore != 0.9f) { throw new NotSupportedException("Only min scores of 0.3, 0.7 and 0.9 are supported"); @@ -507,7 +502,7 @@ protected CaptchaTaskRequest CreateTaskRequest() return new CaptchaTaskRequest { ClientKey = ApiKey, - SoftId = _softId + SoftId = SoftId }; } #endregion diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index 97b8169..5aa04f9 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -24,7 +24,7 @@ public class CapMonsterCloudService : CustomAntiCaptchaService public CapMonsterCloudService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("https://api.capmonster.cloud"), httpClient) { - SupportedCaptchaTypes = + SupportedCaptchaTypes = CaptchaType.ImageCaptcha | CaptchaType.ReCaptchaV2 | CaptchaType.ReCaptchaV3 | @@ -32,6 +32,8 @@ public CapMonsterCloudService(string apiKey, HttpClient? httpClient = null) CaptchaType.GeeTest | CaptchaType.CloudflareTurnstile | CaptchaType.DataDome; + + SoftId = 80; } /// diff --git a/CaptchaSharp/Services/NextCaptchaService.cs b/CaptchaSharp/Services/NextCaptchaService.cs new file mode 100644 index 0000000..09bbed6 --- /dev/null +++ b/CaptchaSharp/Services/NextCaptchaService.cs @@ -0,0 +1,28 @@ +using System; +using System.Net.Http; +using CaptchaSharp.Enums; + +namespace CaptchaSharp.Services; + +/// +/// The service offered by https://nextcaptcha.com/ +/// +public class NextCaptchaService : CustomAntiCaptchaService +{ + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public NextCaptchaService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("https://api.nextcaptcha.com"), httpClient) + { + SupportedCaptchaTypes = + CaptchaType.ReCaptchaV2 | + CaptchaType.ReCaptchaV3 | + CaptchaType.FunCaptcha | + CaptchaType.HCaptcha; + + SoftId = null; + } +} From b7867687460d3736cae3b024d3757602c5185bf2 Mon Sep 17 00:00:00 2001 From: Ruri Date: Fri, 19 Jul 2024 23:02:29 +0200 Subject: [PATCH 25/67] Added partially tested NoCaptchaAiService --- CaptchaSharp.Tests/ConfigFixture.cs | 1 + CaptchaSharp.Tests/NoCaptchaAiServiceTests.cs | 24 ++++++++++++ CaptchaSharp/CaptchaSharp.xml | 22 +++++++++++ CaptchaSharp/Services/NoCaptchaAiService.cs | 39 +++++++++++++++++++ CaptchaSharp/Services/TwoCaptchaService.cs | 4 +- 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 CaptchaSharp.Tests/NoCaptchaAiServiceTests.cs create mode 100644 CaptchaSharp/Services/NoCaptchaAiService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 9e75e39..65250bf 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -59,4 +59,5 @@ public class Credentials public string MetaBypassTechUsername { get; set; } = string.Empty; public string MetaBypassTechPassword { get; set; } = string.Empty; public string NextCaptchaApiKey { get; set; } = string.Empty; + public string NoCaptchaAiApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/NoCaptchaAiServiceTests.cs b/CaptchaSharp.Tests/NoCaptchaAiServiceTests.cs new file mode 100644 index 0000000..6d3c413 --- /dev/null +++ b/CaptchaSharp.Tests/NoCaptchaAiServiceTests.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class NoCaptchaAiFixture : ServiceFixture +{ + public NoCaptchaAiFixture() + { + Service = new NoCaptchaAiService( + Config.Credentials.NoCaptchaAiApiKey); + } +} + +public class NoCaptchaAiServiceTests(NoCaptchaAiFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 5df2bab..d3c99e0 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1505,6 +1505,28 @@ + + + The service provided by https://nocaptchaai.com/ + + + + + Your secret api key. + + + + + The default used for requests. + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + The service provided by https://rucaptcha.com/ diff --git a/CaptchaSharp/Services/NoCaptchaAiService.cs b/CaptchaSharp/Services/NoCaptchaAiService.cs new file mode 100644 index 0000000..655b4b9 --- /dev/null +++ b/CaptchaSharp/Services/NoCaptchaAiService.cs @@ -0,0 +1,39 @@ +using System; +using System.Net.Http; +using CaptchaSharp.Enums; + +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://nocaptchaai.com/ +/// +public class NoCaptchaAiService : CustomTwoCaptchaService +{ + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public NoCaptchaAiService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("https://token.nocaptchaai.com/"), httpClient, false) + { + ApiKey = apiKey; + _httpClient = httpClient ?? new HttpClient(); + + _httpClient.BaseAddress = new Uri("https://free.nocaptchaai.com"); + + SupportedCaptchaTypes = + CaptchaType.ImageCaptcha | + CaptchaType.HCaptcha; + } +} diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 88062e4..09c6102 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -49,10 +49,10 @@ public class TwoCaptchaService : CaptchaService public TwoCaptchaService(string apiKey, HttpClient? httpClient = null) { ApiKey = apiKey; - this.HttpClient = httpClient ?? new HttpClient(); + HttpClient = httpClient ?? new HttpClient(); // TODO: Use https instead of http if possible - this.HttpClient.BaseAddress = new Uri("http://2captcha.com"); + HttpClient.BaseAddress = new Uri("http://2captcha.com"); } #region Getting the Balance From f6a958ae927ee63b38626326eab02e8decf6b32c Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 20 Jul 2024 00:37:40 +0200 Subject: [PATCH 26/67] Added NopechaService --- CaptchaSharp.Tests/ConfigFixture.cs | 1 + CaptchaSharp.Tests/NopechaServiceTests.cs | 32 ++ CaptchaSharp/CaptchaSharp.xml | 48 +++ .../Services/Nopecha/NopechaCookie.cs | 36 +++ .../Services/Nopecha/NopechaDataResponse.cs | 10 + CaptchaSharp/Services/Nopecha/NopechaProxy.cs | 21 ++ .../Services/Nopecha/NopechaRequest.cs | 9 + .../Services/Nopecha/NopechaResponse.cs | 15 + .../NopechaSolveCloudflareTurnstileRequest.cs | 21 ++ .../Nopecha/NopechaSolveHCaptchaRequest.cs | 17 ++ .../Nopecha/NopechaSolveImageRequest.cs | 12 + .../Nopecha/NopechaSolveRecaptchaV2Request.cs | 24 ++ .../Nopecha/NopechaSolveRecaptchaV3Request.cs | 24 ++ .../Services/Nopecha/NopechaSolveRequest.cs | 9 + .../Nopecha/NopechaSolveTokenRequest.cs | 63 ++++ .../Services/Nopecha/NopechaStatusResponse.cs | 21 ++ CaptchaSharp/Services/NopechaService.cs | 283 ++++++++++++++++++ 17 files changed, 646 insertions(+) create mode 100644 CaptchaSharp.Tests/NopechaServiceTests.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaCookie.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaDataResponse.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaProxy.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaRequest.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaResponse.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaSolveHCaptchaRequest.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaSolveImageRequest.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV2Request.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV3Request.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaSolveRequest.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs create mode 100644 CaptchaSharp/Services/Nopecha/NopechaStatusResponse.cs create mode 100644 CaptchaSharp/Services/NopechaService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 65250bf..fb46635 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -60,4 +60,5 @@ public class Credentials public string MetaBypassTechPassword { get; set; } = string.Empty; public string NextCaptchaApiKey { get; set; } = string.Empty; public string NoCaptchaAiApiKey { get; set; } = string.Empty; + public string NopechaApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/NopechaServiceTests.cs b/CaptchaSharp.Tests/NopechaServiceTests.cs new file mode 100644 index 0000000..3905a7b --- /dev/null +++ b/CaptchaSharp.Tests/NopechaServiceTests.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class NopechaFixture : ServiceFixture +{ + public NopechaFixture() + { + Service = new NopechaService(Config.Credentials.NopechaApiKey); + } +} + +public class NopechaServiceTests(NopechaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index d3c99e0..71912a9 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1527,6 +1527,54 @@ The API key to use. The to use for requests. If null, a default one will be created. + + + The service provided by https://nopecha.com/ + + + + + Your secret api key. + + + + + The default used for requests. + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + + + + + + + + + + + + + + + + + + + + + + Seconds since UNIX epoch. + + The service provided by https://rucaptcha.com/ diff --git a/CaptchaSharp/Services/Nopecha/NopechaCookie.cs b/CaptchaSharp/Services/Nopecha/NopechaCookie.cs new file mode 100644 index 0000000..976b22f --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaCookie.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaCookie +{ + [JsonProperty("name")] + public required string Name { get; set; } + + [JsonProperty("value")] + public required string Value { get; set; } + + [JsonProperty("domain")] + public required string Domain { get; set; } + + [JsonProperty("path")] + public required string Path { get; set; } + + [JsonProperty("hostOnly")] + public required bool HostOnly { get; set; } + + [JsonProperty("httpOnly")] + public required bool HttpOnly { get; set; } + + [JsonProperty("secure")] + public required bool Secure { get; set; } + + [JsonProperty("session")] + public required bool Session { get; set; } + + /// + /// Seconds since UNIX epoch. + /// + [JsonProperty("expirationDate")] + public required long ExpirationDate { get; set; } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaDataResponse.cs b/CaptchaSharp/Services/Nopecha/NopechaDataResponse.cs new file mode 100644 index 0000000..0b39350 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaDataResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaDataResponse : NopechaResponse +{ + [JsonProperty("data")] + public JToken? Data { get; set; } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaProxy.cs b/CaptchaSharp/Services/Nopecha/NopechaProxy.cs new file mode 100644 index 0000000..0ca9bde --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaProxy.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaProxy +{ + [JsonProperty("scheme")] + public required string Scheme { get; set; } + + [JsonProperty("host")] + public required string Host { get; set; } + + [JsonProperty("port")] + public int Port { get; set; } + + [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)] + public string? Username { get; set; } + + [JsonProperty("password", NullValueHandling = NullValueHandling.Ignore)] + public string? Password { get; set; } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaRequest.cs b/CaptchaSharp/Services/Nopecha/NopechaRequest.cs new file mode 100644 index 0000000..ee08025 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaRequest.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaRequest +{ + [JsonProperty("key")] + public required string ApiKey { get; set; } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaResponse.cs b/CaptchaSharp/Services/Nopecha/NopechaResponse.cs new file mode 100644 index 0000000..8608576 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaResponse.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaResponse +{ + [JsonProperty("error")] + public int? Error { get; set; } + + [JsonProperty("message")] + public string? Message { get; set; } + + [JsonIgnore] + public bool IsSuccess => Error is null; +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs new file mode 100644 index 0000000..5f6e970 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaSolveCloudflareTurnstileRequest : NopechaSolveTokenRequest +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("url")] + public required string Url { get; set; } + + [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)] + public Dictionary? Data { get; set; } = new(); + + public NopechaSolveCloudflareTurnstileRequest() + { + Type = "turnstile"; + } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveHCaptchaRequest.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveHCaptchaRequest.cs new file mode 100644 index 0000000..62795c2 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveHCaptchaRequest.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaSolveHCaptchaRequest : NopechaSolveTokenRequest +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("url")] + public required string Url { get; set; } + + public NopechaSolveHCaptchaRequest() + { + Type = "hcaptcha"; + } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveImageRequest.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveImageRequest.cs new file mode 100644 index 0000000..b512242 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveImageRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaSolveImageRequest : NopechaRequest +{ + [JsonProperty("type")] + public string Type => "textcaptcha"; + + [JsonProperty("image_data")] + public string[] ImageData { get; set; } = []; +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV2Request.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV2Request.cs new file mode 100644 index 0000000..4f982db --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV2Request.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaSolveRecaptchaV2Request : NopechaSolveTokenRequest +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("url")] + public required string Url { get; set; } + + [JsonProperty("enterprise")] + public bool Enterprise { get; set; } + + [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)] + public Dictionary? DataS { get; set; } = new(); + + public NopechaSolveRecaptchaV2Request() + { + Type = "recaptcha2"; + } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV3Request.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV3Request.cs new file mode 100644 index 0000000..428d9ee --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV3Request.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaSolveRecaptchaV3Request : NopechaSolveTokenRequest +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("url")] + public required string Url { get; set; } + + [JsonProperty("enterprise")] + public bool Enterprise { get; set; } + + [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)] + public Dictionary? DataS { get; set; } = new(); + + public NopechaSolveRecaptchaV3Request() + { + Type = "recaptcha3"; + } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveRequest.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveRequest.cs new file mode 100644 index 0000000..bd36728 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveRequest.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaSolveRequest : NopechaRequest +{ + [JsonProperty("type")] + public string Type { get; protected set; } = ""; +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs new file mode 100644 index 0000000..9bd2869 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using CaptchaSharp.Models; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaSolveTokenRequest : NopechaSolveRequest +{ + [JsonProperty("proxy", NullValueHandling = NullValueHandling.Ignore)] + public NopechaProxy? Proxy { get; set; } + + [JsonProperty("cookie", NullValueHandling = NullValueHandling.Ignore)] + public NopechaCookie[]? Cookies { get; set; } + + [JsonProperty("useragent", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + public void SetProxy(Proxy? proxy, string url) + { + if (proxy is null) + { + return; + } + + if (!string.IsNullOrEmpty(proxy.UserAgent)) + { + UserAgent = proxy.UserAgent; + } + + if (!string.IsNullOrEmpty(proxy.Host)) + { + Proxy = new NopechaProxy + { + Scheme = proxy.Type.ToString().ToLower(), + Host = proxy.Host, + Port = proxy.Port, + Username = proxy.Username, + Password = proxy.Password + }; + } + + if (proxy.Cookies is not null) + { + var uri = new Uri(url); + + Cookies = proxy.Cookies.Select(c => new NopechaCookie + { + Name = c.Item1, + Value = c.Item2, + Domain = uri.Host, + Path = uri.AbsolutePath, + Secure = uri.Scheme == "https", + + // Hardcoded since we don't have access to these values + HostOnly = true, + HttpOnly = true, + Session = false, + ExpirationDate = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 3600 + }).ToArray(); + } + } +} diff --git a/CaptchaSharp/Services/Nopecha/NopechaStatusResponse.cs b/CaptchaSharp/Services/Nopecha/NopechaStatusResponse.cs new file mode 100644 index 0000000..76eb3d4 --- /dev/null +++ b/CaptchaSharp/Services/Nopecha/NopechaStatusResponse.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.Nopecha; + +internal class NopechaStatusResponse : NopechaResponse +{ + [JsonProperty("plan")] + public required string Plan { get; set; } + + [JsonProperty("credit")] + public required double Credit { get; set; } + + [JsonProperty("quota")] + public required double Quota { get; set; } + + [JsonProperty("duration")] + public required long Duration { get; set; } + + [JsonProperty("lastreset")] + public required long LastReset { get; set; } +} diff --git a/CaptchaSharp/Services/NopechaService.cs b/CaptchaSharp/Services/NopechaService.cs new file mode 100644 index 0000000..53759bd --- /dev/null +++ b/CaptchaSharp/Services/NopechaService.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Exceptions; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; +using CaptchaSharp.Services.Nopecha; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://nopecha.com/ +/// +public class NopechaService : CaptchaService +{ + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public NopechaService(string apiKey, HttpClient? httpClient = null) + { + ApiKey = apiKey; + _httpClient = httpClient ?? new HttpClient(); + + _httpClient.BaseAddress = new Uri("https://api.nopecha.com"); + } + + #region Getting the Balance + /// + public override async Task GetBalanceAsync( + CancellationToken cancellationToken = default) + { + var json = await _httpClient.GetStringAsync( + "status", + new StringPairCollection() + .Add("key", ApiKey), + cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.IsSuccess) + { + throw new BadAuthenticationException(response.Message!); + } + + return Convert.ToDecimal(response.Credit); + } + #endregion + + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + var payload = new NopechaSolveImageRequest + { + ApiKey = ApiKey, + ImageData = [base64] + }; + + var json = await _httpClient.PostJsonToStringAsync( + "", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + json, CaptchaType.ImageCaptcha, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, + bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var payload = new NopechaSolveRecaptchaV2Request + { + ApiKey = ApiKey, + SiteKey = siteKey, + Url = siteUrl, + DataS = string.IsNullOrEmpty(dataS) + ? null + : dataS.Deserialize>(), + Enterprise = enterprise + }; + + payload.SetProxy(proxy, siteUrl); + + var json = await _httpClient.PostJsonToStringAsync( + "token", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + json, CaptchaType.ReCaptchaV2, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var payload = new NopechaSolveRecaptchaV3Request + { + ApiKey = ApiKey, + SiteKey = siteKey, + Url = siteUrl, + DataS = new Dictionary + { + ["action"] = action, + }, + Enterprise = enterprise + }; + + payload.SetProxy(proxy, siteUrl); + + var json = await _httpClient.PostJsonToStringAsync( + "token", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + json, CaptchaType.ReCaptchaV3, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var payload = new NopechaSolveHCaptchaRequest + { + ApiKey = ApiKey, + SiteKey = siteKey, + Url = siteUrl + }; + + payload.SetProxy(proxy, siteUrl); + + var json = await _httpClient.PostJsonToStringAsync( + "token", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + json, CaptchaType.HCaptcha, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var dataDict = new Dictionary {}; + + if (!string.IsNullOrEmpty(action)) + { + dataDict["action"] = action; + } + + if (!string.IsNullOrEmpty(data)) + { + dataDict["cdata"] = data; + } + + var payload = new NopechaSolveCloudflareTurnstileRequest + { + ApiKey = ApiKey, + SiteKey = siteKey, + Url = siteUrl, + Data = dataDict, + }; + + payload.SetProxy(proxy, siteUrl); + + var json = await _httpClient.PostJsonToStringAsync( + "token", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + json, CaptchaType.CloudflareTurnstile, cancellationToken) + .ConfigureAwait(false); + } + #endregion + + #region Getting the result + private async Task GetResult( + string json, CaptchaType captchaType, CancellationToken cancellationToken) + where T : CaptchaResponse + { + var response = json.Deserialize(); + + if (!response.IsSuccess) + { + throw new TaskCreationException(response.Message!); + } + + var task = new CaptchaTask(response.Data!.ToString(), captchaType); + + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } + + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) where T : class + { + var json = await _httpClient.GetStringAsync( + "", + new StringPairCollection() + .Add("key", ApiKey) + .Add("id", task.IdString), + cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.IsSuccess) + { + // Incomplete job + if (response.Error is 14) + { + return null; + } + + throw new TaskSolutionException(response.Message!); + } + + if (typeof(T) == typeof(CloudflareTurnstileResponse)) + { + return new CloudflareTurnstileResponse + { + IdString = task.IdString, + Response = response.Data!.ToString() + } as T; + } + + if (typeof(T) != typeof(StringResponse)) + { + throw new NotSupportedException($"Type {typeof(T).Name} is not supported."); + } + + task.Completed = true; + + // response.Data can be either a string or an array of strings (with 1 value) + var result = response.Data!.Type == JTokenType.Array + ? response.Data!.First!.ToString() + : response.Data!.ToString(); + + return new StringResponse + { + IdString = task.IdString, + Response = result + } as T; + } + + #endregion +} From 86a99a3e681cb6c6fe23659cb8c9c19dc6c5e10c Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 20 Jul 2024 14:30:55 +0200 Subject: [PATCH 27/67] Improved CapSolverService --- CaptchaSharp.Tests/CapSolverServiceTests.cs | 12 ++++ CaptchaSharp.Tests/ConfigFixture.cs | 1 - CaptchaSharp/CaptchaSharp.xml | 6 ++ .../Requests/CaptchaTaskFeedbackRequest.cs | 23 ++++++ .../CapSolver/Requests/CaptchaTaskRequest.cs | 16 +++-- .../Requests/GetTaskResultRequest.cs | 9 ++- .../Requests/RecaptchaV2CaptchaRequest.cs | 7 -- .../Requests/ReportIncorrectCaptchaRequest.cs | 7 -- .../Services/CapSolver/Requests/Request.cs | 9 ++- .../Tasks/AntiTurnstileTaskProxyless.cs | 24 +++++++ .../Requests/Tasks/CapSolverTaskProxyless.cs | 9 ++- .../Requests/Tasks/GeeTestTaskProxyless.cs | 2 +- .../Requests/Tasks/Proxied/CapSolverTask.cs | 71 +++++++++++-------- .../Requests/Tasks/Proxied/GeeTestTask.cs | 2 +- .../Proxied/RecaptchaV2EnterpriseTask.cs | 24 ++++--- .../RecaptchaV2EnterpriseTaskProxyless.cs | 24 ++++--- .../Responses/CaptchaTaskFeedbackResponse.cs | 6 ++ .../CapSolver/Responses/GetBalanceResponse.cs | 9 ++- .../Responses/GetTaskResultResponse.cs | 23 +++--- .../ReportIncorrectCaptchaResponse.cs | 16 ----- .../Services/CapSolver/Responses/Response.cs | 4 +- .../Solutions/CloudflareTurnstileSolution.cs | 20 ++++++ .../Responses/Solutions/DataDomeSolution.cs | 21 +++--- .../Responses/TaskCreationResponse.cs | 9 ++- CaptchaSharp/Services/CapSolverService.cs | 71 +++++++++++++++++-- 25 files changed, 279 insertions(+), 146 deletions(-) create mode 100644 CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs delete mode 100644 CaptchaSharp/Services/CapSolver/Requests/RecaptchaV2CaptchaRequest.cs delete mode 100644 CaptchaSharp/Services/CapSolver/Requests/ReportIncorrectCaptchaRequest.cs create mode 100644 CaptchaSharp/Services/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs create mode 100644 CaptchaSharp/Services/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs delete mode 100644 CaptchaSharp/Services/CapSolver/Responses/ReportIncorrectCaptchaResponse.cs create mode 100644 CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs diff --git a/CaptchaSharp.Tests/CapSolverServiceTests.cs b/CaptchaSharp.Tests/CapSolverServiceTests.cs index 50bf24d..2e08820 100644 --- a/CaptchaSharp.Tests/CapSolverServiceTests.cs +++ b/CaptchaSharp.Tests/CapSolverServiceTests.cs @@ -17,11 +17,23 @@ public class CapSolverServiceTests(CapSolverFixture fixture, ITestOutputHelper o : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); [Fact] public Task SolveDataDomeTestAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); } diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index fb46635..20bc2b1 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -48,7 +48,6 @@ public class Credentials public string AzCaptchaApiKey { get; set; } = string.Empty; public string CaptchasIoApiKey { get; set; } = string.Empty; public string RuCaptchaApiKey { get; set; } = string.Empty; - public string SolveCaptchaApiKey { get; set; } = string.Empty; public string TrueCaptchaApiKey { get; set; } = string.Empty; public string TrueCaptchaUsername { get; set; } = string.Empty; public string NineKWApiKey { get; set; } = string.Empty; diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 71912a9..65ee046 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1111,9 +1111,15 @@ + + + + + + diff --git a/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs b/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs new file mode 100644 index 0000000..687da79 --- /dev/null +++ b/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.CapSolver.Requests; + +internal class CaptchaTaskFeedbackRequest : Request +{ + public required string AppId { get; set; } + + public required string TaskId { get; set; } + + public required TaskResultFeedback Result { get; set; } +} + +internal class TaskResultFeedback +{ + public required bool Invalid { get; set; } + + [JsonProperty("code", NullValueHandling = NullValueHandling.Ignore)] + public int? Code { get; set; } + + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] + public string? Message { get; set; } +} diff --git a/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskRequest.cs b/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskRequest.cs index 7598424..4a67084 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskRequest.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskRequest.cs @@ -1,11 +1,13 @@ using CaptchaSharp.Services.CapSolver.Requests.Tasks; +using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapSolver.Requests +namespace CaptchaSharp.Services.CapSolver.Requests; + +internal class CaptchaTaskRequest : Request { - internal class CaptchaTaskRequest : Request - { - public CapSolverTaskProxyless Task { get; set; } - public string AppId { get; set; } = ""; - public string LanguagePool { get; set; } = "en"; - } + public CapSolverTaskProxyless? Task { get; set; } + public required string AppId { get; set; } + + [JsonProperty("languagePool", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LanguagePool { get; set; } } diff --git a/CaptchaSharp/Services/CapSolver/Requests/GetTaskResultRequest.cs b/CaptchaSharp/Services/CapSolver/Requests/GetTaskResultRequest.cs index 81b314c..55b7246 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/GetTaskResultRequest.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/GetTaskResultRequest.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.CapSolver.Requests +namespace CaptchaSharp.Services.CapSolver.Requests; + +internal class GetTaskResultRequest : Request { - internal class GetTaskResultRequest : Request - { - public string TaskId { get; set; } - } + public required string TaskId { get; set; } } diff --git a/CaptchaSharp/Services/CapSolver/Requests/RecaptchaV2CaptchaRequest.cs b/CaptchaSharp/Services/CapSolver/Requests/RecaptchaV2CaptchaRequest.cs deleted file mode 100644 index 46e1560..0000000 --- a/CaptchaSharp/Services/CapSolver/Requests/RecaptchaV2CaptchaRequest.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CaptchaSharp.Services.CapSolver.Requests -{ - internal class RecaptchaV2CaptchaRequest : Request - { - - } -} diff --git a/CaptchaSharp/Services/CapSolver/Requests/ReportIncorrectCaptchaRequest.cs b/CaptchaSharp/Services/CapSolver/Requests/ReportIncorrectCaptchaRequest.cs deleted file mode 100644 index a393d10..0000000 --- a/CaptchaSharp/Services/CapSolver/Requests/ReportIncorrectCaptchaRequest.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CaptchaSharp.Services.CapSolver.Requests -{ - internal class ReportIncorrectCaptchaRequest : Request - { - public string TaskId { get; set; } - } -} diff --git a/CaptchaSharp/Services/CapSolver/Requests/Request.cs b/CaptchaSharp/Services/CapSolver/Requests/Request.cs index ea32cbd..7bc808b 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Request.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Request.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.CapSolver.Requests +namespace CaptchaSharp.Services.CapSolver.Requests; + +internal class Request { - internal class Request - { - public string ClientKey { get; set; } = ""; - } + public required string ClientKey { get; set; } } diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs new file mode 100644 index 0000000..7bc1360 --- /dev/null +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.CapSolver.Requests.Tasks; + +internal class AntiTurnstileTaskProxyless : CapSolverTaskProxyless +{ + public required string WebsiteKey { get; set; } + public required string WebsiteURL { get; set; } + public TurnstileMetadata? Metadata { get; set; } + + public AntiTurnstileTaskProxyless() + { + Type = "AntiTurnstileTaskProxyLess"; + } +} + +internal class TurnstileMetadata +{ + [JsonProperty("action", NullValueHandling = NullValueHandling.Ignore)] + public string? Action { get; set; } + + [JsonProperty("cdata", NullValueHandling = NullValueHandling.Ignore)] + public string? CData { get; set; } +} diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs index b0c76ca..a3f2f29 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +namespace CaptchaSharp.Services.CapSolver.Requests.Tasks; + +internal class CapSolverTaskProxyless { - internal class CapSolverTaskProxyless - { - public string Type { get; set; } - } + public string Type { get; set; } } diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs index 663ed28..73b69cc 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs @@ -5,7 +5,7 @@ internal class GeeTestTaskProxyless : CapSolverTaskProxyless public string WebsiteURL { get; set; } public string Gt { get; set; } public string Challenge { get; set; } - public string GeetestApiServerSubdomain { get; set; } + public string? GeetestApiServerSubdomain { get; set; } public int Version { get; set; } public GeeTestTaskProxyless() diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs index 84b6180..6c84776 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs @@ -1,42 +1,51 @@ using CaptchaSharp.Models; -using System; -using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied; + +internal class CapSolverTask : CapSolverTaskProxyless { - internal class CapSolverTask : CapSolverTaskProxyless + public string? ProxyType { get; set; } + public string? ProxyAddress { get; set; } + 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; } + + [JsonProperty("userAgent", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + [JsonProperty("cookies", NullValueHandling = NullValueHandling.Ignore)] + public CapSolverCookie[]? Cookies { get; set; } + + public CapSolverTask SetProxy(Proxy proxy) { - public string ProxyType { get; set; } - public string ProxyAddress { get; set; } - public int ProxyPort { get; set; } - public string ProxyLogin { get; set; } - public string ProxyPassword { get; set; } - public string UserAgent { get; set; } - public string Cookies { get; set; } // Format cookiename1=cookievalue1; cookiename2=cookievalue2 + ProxyAddress = proxy.Host; + ProxyPort = proxy.Port; + ProxyType = proxy.Type.ToString().ToLower(); + ProxyLogin = proxy.Username; + ProxyPassword = proxy.Password; + UserAgent = proxy.UserAgent; - public CapSolverTask SetProxy(Proxy proxy) + if (proxy.Cookies is not null) { - if (!System.Net.IPAddress.TryParse(proxy.Host, out _)) - throw new NotSupportedException($"Only IP addresses are supported for the proxy host"); - - ProxyAddress = proxy.Host; - ProxyPort = proxy.Port; - ProxyType = proxy.Type.ToString().ToLower(); - ProxyLogin = proxy.Username; - ProxyPassword = proxy.Password; - UserAgent = proxy.UserAgent; - SetCookies(proxy.Cookies); - - return this; + Cookies = proxy.Cookies.Select(c => new CapSolverCookie + { + Name = c.Item1, + Value = c.Item2 + }).ToArray(); } - private void SetCookies(IEnumerable<(string, string)> cookies) - { - if (cookies == null) - return; - - Cookies = string.Join("; ", cookies.Select(c => $"{c.Item1}={c.Item2}")); - } + return this; } } + +internal class CapSolverCookie +{ + public required string Name { get; set; } + public required string Value { get; set; } +} diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs index 590f0d6..154bc8c 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs @@ -5,7 +5,7 @@ internal class GeeTestTask : CapSolverTask public string WebsiteURL { get; set; } public string Gt { get; set; } public string Challenge { get; set; } - public string GeetestApiServerSubdomain { get; set; } + public string? GeetestApiServerSubdomain { get; set; } public GeeTestTask() { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs index 0487e43..8c9e1c2 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs @@ -1,14 +1,18 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied; + +internal class RecaptchaV2EnterpriseTask : CapSolverTask { - internal class RecaptchaV2EnterpriseTask : CapSolverTask - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string EnterprisePayload { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + + [JsonProperty("enterprisePayload", NullValueHandling = NullValueHandling.Ignore)] + public JObject? EnterprisePayload { get; set; } - public RecaptchaV2EnterpriseTask() - { - Type = "RecaptchaV2EnterpriseTask"; - } + public RecaptchaV2EnterpriseTask() + { + Type = "RecaptchaV2EnterpriseTask"; } } diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs index bfae5cd..164cc58 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs @@ -1,14 +1,18 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services.CapSolver.Requests.Tasks; + +internal class RecaptchaV2EnterpriseTaskProxyless : CapSolverTaskProxyless { - internal class RecaptchaV2EnterpriseTaskProxyless : CapSolverTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string EnterprisePayload { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + + [JsonProperty("enterprisePayload", NullValueHandling = NullValueHandling.Ignore)] + public JObject? EnterprisePayload { get; set; } - public RecaptchaV2EnterpriseTaskProxyless() - { - Type = "RecaptchaV2EnterpriseTaskProxyless"; - } + public RecaptchaV2EnterpriseTaskProxyless() + { + Type = "RecaptchaV2EnterpriseTaskProxyless"; } } diff --git a/CaptchaSharp/Services/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs b/CaptchaSharp/Services/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs new file mode 100644 index 0000000..9155a48 --- /dev/null +++ b/CaptchaSharp/Services/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs @@ -0,0 +1,6 @@ +namespace CaptchaSharp.Services.CapSolver.Responses; + +internal class CaptchaTaskFeedbackResponse : Response +{ + public string? Message { get; set; } +} diff --git a/CaptchaSharp/Services/CapSolver/Responses/GetBalanceResponse.cs b/CaptchaSharp/Services/CapSolver/Responses/GetBalanceResponse.cs index 1d85e4f..d8d97db 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/GetBalanceResponse.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/GetBalanceResponse.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.CapSolver.Responses +namespace CaptchaSharp.Services.CapSolver.Responses; + +internal class GetBalanceResponse : Response { - internal class GetBalanceResponse : Response - { - public float Balance { get; set; } - } + public float Balance { get; set; } } diff --git a/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs b/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs index cdb5050..181c249 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs @@ -1,17 +1,16 @@ using CaptchaSharp.Services.CapSolver.Responses.Solutions; -namespace CaptchaSharp.Services.CapSolver.Responses +namespace CaptchaSharp.Services.CapSolver.Responses; + +internal class GetTaskResultResponse : Response { - internal class GetTaskResultResponse : Response - { - public string Status { get; set; } - public double Cost { get; set; } - public required Solution Solution { get; set; } - public string Ip { get; set; } - public double CreateTime { get; set; } - public double EndTime { get; set; } - public double? SolveCount { get; set; } + public string? Status { get; set; } + public double Cost { get; set; } + public required Solution Solution { get; set; } + public string? Ip { get; set; } + public double CreateTime { get; set; } + public double EndTime { get; set; } + public double? SolveCount { get; set; } - public bool IsReady => Status != "processing"; - } + public bool IsReady => Status != "processing"; } diff --git a/CaptchaSharp/Services/CapSolver/Responses/ReportIncorrectCaptchaResponse.cs b/CaptchaSharp/Services/CapSolver/Responses/ReportIncorrectCaptchaResponse.cs deleted file mode 100644 index cffe0ee..0000000 --- a/CaptchaSharp/Services/CapSolver/Responses/ReportIncorrectCaptchaResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Newtonsoft.Json; - -namespace CaptchaSharp.Services.CapSolver.Responses -{ - internal class ReportIncorrectCaptchaResponse - { - public int ErrorId { get; set; } - public string Status { get; set; } - - [JsonIgnore] - public bool Success => ErrorId == 0; - - [JsonIgnore] - public bool NotFoundOrExpired => ErrorId == 16; - } -} diff --git a/CaptchaSharp/Services/CapSolver/Responses/Response.cs b/CaptchaSharp/Services/CapSolver/Responses/Response.cs index 1238a57..5b9e577 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Response.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Response.cs @@ -5,8 +5,8 @@ namespace CaptchaSharp.Services.CapSolver.Responses internal class Response { public int ErrorId { get; set; } - public string ErrorCode { get; set; } - public string ErrorDescription { get; set; } + public string? ErrorCode { get; set; } + public string? ErrorDescription { get; set; } [JsonIgnore] public bool IsError => ErrorId > 0; diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs new file mode 100644 index 0000000..64cfdfb --- /dev/null +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs @@ -0,0 +1,20 @@ +using CaptchaSharp.Models; + +namespace CaptchaSharp.Services.CapSolver.Responses.Solutions; + +internal class CloudflareTurnstileSolution : Solution +{ + public required string Token { get; set; } + + public required string UserAgent { get; set; } + + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new CloudflareTurnstileResponse + { + IdString = id, + Response = Token, + UserAgent = UserAgent + }; + } +} diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs index 7f27f44..afef88e 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs @@ -1,18 +1,17 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions +namespace CaptchaSharp.Services.CapSolver.Responses.Solutions; + +internal class DataDomeSolution : Solution { - internal class DataDomeSolution : Solution - { - public string Cookie { get; set; } + public string Cookie { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse { - return new StringResponse - { - IdString = id, - Response = Cookie - }; - } + IdString = id, + Response = Cookie + }; } } diff --git a/CaptchaSharp/Services/CapSolver/Responses/TaskCreationResponse.cs b/CaptchaSharp/Services/CapSolver/Responses/TaskCreationResponse.cs index ade2849..1c757cd 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/TaskCreationResponse.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/TaskCreationResponse.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Services.CapSolver.Responses +namespace CaptchaSharp.Services.CapSolver.Responses; + +internal class TaskCreationResponse : Response { - internal class TaskCreationResponse : Response - { - public string TaskId { get; set; } - } + public required string TaskId { get; set; } } diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index 5c6b793..4392ec6 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -111,7 +111,12 @@ public override async Task SolveRecaptchaV2Async( Proxy? proxy = null, CancellationToken cancellationToken = default) { var content = CreateTaskRequest(); - + + // If dataS is not null or empty, the enterprise payload is { "s": dataS } + var enterprisePayload = string.IsNullOrEmpty(dataS) + ? null + : JObject.Parse($"{{ \"s\": \"{dataS}\" }}"); + if (enterprise) { if (proxy is not null) @@ -120,7 +125,7 @@ public override async Task SolveRecaptchaV2Async( { WebsiteKey = siteKey, WebsiteURL = siteUrl, - EnterprisePayload = dataS + EnterprisePayload = enterprisePayload }.SetProxy(proxy); } else @@ -129,7 +134,7 @@ public override async Task SolveRecaptchaV2Async( { WebsiteKey = siteKey, WebsiteURL = siteUrl, - EnterprisePayload = dataS + EnterprisePayload = enterprisePayload }; } } @@ -142,7 +147,6 @@ public override async Task SolveRecaptchaV2Async( WebsiteKey = siteKey, WebsiteURL = siteUrl, IsInvisible = invisible, - RecaptchaDataSValue = dataS }.SetProxy(proxy); } else @@ -152,7 +156,6 @@ public override async Task SolveRecaptchaV2Async( WebsiteKey = siteKey, WebsiteURL = siteUrl, IsInvisible = invisible, - RecaptchaDataSValue = dataS }; } } @@ -354,6 +357,35 @@ public override async Task SolveDataDomeAsync( response.Deserialize(), CaptchaType.DataDome, cancellationToken).ConfigureAwait(false); } + + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + content.Task = new AntiTurnstileTaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + Metadata = new TurnstileMetadata + { + Action = string.IsNullOrEmpty(action) ? null : action, + CData = string.IsNullOrEmpty(data) ? null : data + } + }; + + var response = await _httpClient.PostJsonToStringAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response.Deserialize(), CaptchaType.CloudflareTurnstile, + cancellationToken).ConfigureAwait(false); + } #endregion #region Getting the result @@ -412,12 +444,41 @@ private async Task GetResult( CaptchaType.ImageCaptcha => solution.ToObject(), CaptchaType.GeeTest => solution.ToObject(), CaptchaType.DataDome => solution.ToObject(), + CaptchaType.CloudflareTurnstile => solution.ToObject(), _ => throw new NotSupportedException($"The captcha type {task.Type} is not supported") } ?? throw new TaskSolutionException("The solution is null"); return result.Solution.ToCaptchaResponse(task.IdString) as T; } #endregion + + #region Reporting the solution + /// + public override async Task ReportSolution(string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + var response = await _httpClient.PostJsonToStringAsync( + "feedbackTask", + new CaptchaTaskFeedbackRequest + { + ClientKey = ApiKey, + AppId = _appId, + TaskId = id, + Result = new TaskResultFeedback + { + Invalid = !correct + } + }, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var result = response.Deserialize(); + + if (result.IsError) + { + throw new TaskReportException($"{result.ErrorCode}: {result.ErrorDescription}"); + } + } + #endregion #region Private Methods private CaptchaTaskRequest CreateTaskRequest() From 8759f7bb7d89d6665d1a158cf9bdd8ab4bfefad4 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sat, 20 Jul 2024 18:52:34 +0200 Subject: [PATCH 28/67] Added BestCaptchaSolverService --- .../BestCaptchaSolverServiceTests.cs | 46 ++ CaptchaSharp.Tests/ConfigFixture.cs | 1 + CaptchaSharp/CaptchaSharp.xml | 52 ++ CaptchaSharp/Models/Proxy.cs | 4 +- .../Requests/Tasks/Proxied/AntiCaptchaTask.cs | 4 +- .../BestCaptchaSolver/Requests/BcsRequest.cs | 12 + .../Requests/BcsSolveCapyRequest.cs | 12 + .../BcsSolveCloudflareTurnstileRequest.cs | 18 + .../Requests/BcsSolveFuncaptchaRequest.cs | 15 + .../Requests/BcsSolveGeeTestRequest.cs | 18 + .../Requests/BcsSolveHCaptchaRequest.cs | 12 + .../Requests/BcsSolveImageRequest.cs | 27 + .../Requests/BcsSolveRecaptchaV2Request.cs | 18 + .../Requests/BcsSolveRecaptchaV3Request.cs | 21 + .../Requests/BcsSolveRequest.cs | 57 +++ .../Responses/BcsBalanceResponse.cs | 9 + .../Responses/BcsResponse.cs | 15 + .../Responses/BcsSolveCapyResponse.cs | 9 + .../BcsSolveCloudflareTurnstileResponse.cs | 12 + .../Responses/BcsSolveFuncaptchaResponse.cs | 9 + .../Responses/BcsSolveGeeTestResponse.cs | 21 + .../Responses/BcsSolveHCaptchaResponse.cs | 9 + .../Responses/BcsSolveImageResponse.cs | 9 + .../Responses/BcsSolveRecaptchaResponse.cs | 10 + .../Responses/BcsTaskCreatedResponse.cs | 9 + .../Services/BestCaptchaSolverService.cs | 467 ++++++++++++++++++ .../Services/CapMonsterCloudService.cs | 2 +- .../Requests/Tasks/Proxied/CapSolverTask.cs | 4 +- .../Nopecha/NopechaSolveTokenRequest.cs | 4 +- CaptchaSharp/Services/TwoCaptchaService.cs | 7 +- 30 files changed, 901 insertions(+), 12 deletions(-) create mode 100644 CaptchaSharp.Tests/BestCaptchaSolverServiceTests.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRequest.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsBalanceResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs create mode 100644 CaptchaSharp/Services/BestCaptchaSolverService.cs diff --git a/CaptchaSharp.Tests/BestCaptchaSolverServiceTests.cs b/CaptchaSharp.Tests/BestCaptchaSolverServiceTests.cs new file mode 100644 index 0000000..63ab368 --- /dev/null +++ b/CaptchaSharp.Tests/BestCaptchaSolverServiceTests.cs @@ -0,0 +1,46 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class BestCaptchaSolverFixture : ServiceFixture +{ + public BestCaptchaSolverFixture() + { + Service = new BestCaptchaSolverService( + Config.Credentials.BestCaptchaSolverApiKey); + } +} + +public class BestCaptchaSolverServiceTests(BestCaptchaSolverFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + + // Do not abuse this method or you will be banned + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(correct: false); + + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveCapyAsync_NoProxy_ValidSolution() => CapyTest_NoProxy(); + [Fact] public Task SolveCapyAsync_WithProxy_ValidSolution() => CapyTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); +} diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 20bc2b1..0b40ba0 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -60,4 +60,5 @@ public class Credentials public string NextCaptchaApiKey { get; set; } = string.Empty; public string NoCaptchaAiApiKey { get; set; } = string.Empty; public string NopechaApiKey { get; set; } = string.Empty; + public string BestCaptchaSolverApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 65ee046..1ec9191 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1028,6 +1028,58 @@ The API key to use. The to use for requests. If null, a default one will be created. + + + The service offered by https://bestcaptchasolver.com/ + + + + + Your secret api key. + + + + + The default used for requests. + + + + + Initializes a new . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The service provided by https://capmonster.cloud/ diff --git a/CaptchaSharp/Models/Proxy.cs b/CaptchaSharp/Models/Proxy.cs index 22af499..21d26c7 100644 --- a/CaptchaSharp/Models/Proxy.cs +++ b/CaptchaSharp/Models/Proxy.cs @@ -28,7 +28,7 @@ public class Proxy public string? UserAgent { get; set; } /// The cookies needed to get to the page where the captcha is shown. - public (string, string)[]? Cookies { get; set; } + public (string Name, string Value)[]? Cookies { get; set; } /// Whether the proxy requires authentication. [JsonIgnore] @@ -48,5 +48,5 @@ public Proxy(string host, int port, ProxyType type = ProxyType.HTTP, string user } internal string GetCookieString() - => Cookies != null ? string.Join("; ", Cookies.Select(c => $"{c.Item1}={c.Item2}")) : string.Empty; + => Cookies != null ? string.Join("; ", Cookies.Select(c => $"{c.Name}={c.Value}")) : string.Empty; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs index 5ce3f60..e7278c9 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs @@ -42,14 +42,14 @@ public AntiCaptchaTask SetProxy(Proxy proxy) return this; } - private void SetCookies(IEnumerable<(string, string)>? cookies) + private void SetCookies(IEnumerable<(string Name, string Value)>? cookies) { if (cookies == null) { return; } - Cookies = string.Join("; ", cookies.Select(c => $"{c.Item1}={c.Item2}")); + Cookies = string.Join("; ", cookies.Select(c => $"{c.Name}={c.Value}")); } } } diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsRequest.cs new file mode 100644 index 0000000..a6e421c --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsRequest +{ + [JsonProperty("access_token")] + public required string AccessToken { get; set; } + + [JsonProperty("affiliate_id", NullValueHandling = NullValueHandling.Ignore)] + public string? AffiliateId { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs new file mode 100644 index 0000000..e42b40d --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveCapyRequest : BcsSolveRequest +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs new file mode 100644 index 0000000..d84bf3f --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveCloudflareTurnstileRequest : BcsSolveRequest +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("action", NullValueHandling = NullValueHandling.Ignore)] + public string? Action { get; set; } + + [JsonProperty("cdata", NullValueHandling = NullValueHandling.Ignore)] + public string? CData { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs new file mode 100644 index 0000000..cd4905b --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveFuncaptchaRequest : BcsSolveRequest +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("s_url")] + public required string SUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs new file mode 100644 index 0000000..b62c58e --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveGeeTestRequest : BcsSolveRequest +{ + [JsonProperty("domain")] + public required string Domain { get; set; } + + [JsonProperty("gt")] + public required string Gt { get; set; } + + [JsonProperty("challenge")] + public required string Challenge { get; set; } + + [JsonProperty("api_server", NullValueHandling = NullValueHandling.Ignore)] + public string? ApiServer { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs new file mode 100644 index 0000000..094694f --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveHCaptchaRequest : BcsSolveRequest +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs new file mode 100644 index 0000000..190f293 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveImageRequest : BcsRequest +{ + [JsonProperty("b64image")] + public required string Base64Image { get; set; } + + [JsonProperty("is_case", NullValueHandling = NullValueHandling.Ignore)] + public bool? CaseSensitive { get; set; } + + [JsonProperty("is_phrase", NullValueHandling = NullValueHandling.Ignore)] + public bool? IsPhrase { get; set; } + + [JsonProperty("is_math", NullValueHandling = NullValueHandling.Ignore)] + public bool? IsMath { get; set; } + + [JsonProperty("alphanumeric", NullValueHandling = NullValueHandling.Ignore)] + public int? Alphanumeric { get; set; } + + [JsonProperty("minlength", NullValueHandling = NullValueHandling.Ignore)] + public int? MinLength { get; set; } + + [JsonProperty("maxlength", NullValueHandling = NullValueHandling.Ignore)] + public int? MaxLength { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs new file mode 100644 index 0000000..8064366 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveRecaptchaV2Request : BcsSolveRequest +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("type")] + public int Type { get; set; } + + [JsonProperty("data_s")] + public string? DataS { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs new file mode 100644 index 0000000..4f0004f --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveRecaptchaV3Request : BcsSolveRequest +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("type")] + public int Type { get; set; } + + [JsonProperty("v3_action", NullValueHandling = NullValueHandling.Ignore)] + public string? Action { get; set; } + + [JsonProperty("v3_min_score", NullValueHandling = NullValueHandling.Ignore)] + public double? MinScore { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRequest.cs b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRequest.cs new file mode 100644 index 0000000..fb32cb6 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRequest.cs @@ -0,0 +1,57 @@ +using System; +using System.Linq; +using CaptchaSharp.Models; +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; + +internal class BcsSolveRequest : BcsRequest +{ + [JsonProperty("cookie_input", NullValueHandling = NullValueHandling.Ignore)] + public string? CookieInput { get; set; } + + [JsonProperty("user_agent", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + [JsonProperty("proxy", NullValueHandling = NullValueHandling.Ignore)] + public string? Proxy { get; set; } + + [JsonProperty("proxy_type", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyType { get; set; } + + public void SetProxy(Proxy? proxy) + { + if (proxy == null) + { + return; + } + + if (!string.IsNullOrEmpty(proxy.UserAgent)) + { + UserAgent = proxy.UserAgent; + } + + if (proxy.Cookies is not null) + { + CookieInput = string.Join("; ", proxy.Cookies + .Select(c => $"{c.Name}={c.Value}")); + } + + // Only http(s) proxies are supported + if (proxy.Type is not Enums.ProxyType.HTTP && proxy.Type is not Enums.ProxyType.HTTPS) + { + throw new NotSupportedException("Only HTTP and HTTPS proxies are supported"); + } + + if (string.IsNullOrEmpty(proxy.Host)) + { + return; + } + + Proxy = proxy.RequiresAuthentication + ? $"{proxy.Username}:{proxy.Password}@{proxy.Host}:{proxy.Port}" + : $"{proxy.Host}:{proxy.Port}"; + + ProxyType = "HTTP"; + } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsBalanceResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsBalanceResponse.cs new file mode 100644 index 0000000..36d9b6f --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsBalanceResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsBalanceResponse : BcsResponse +{ + [JsonProperty("balance")] + public string? Balance { get; set; } // This should be decimal, but the API returns a string +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsResponse.cs new file mode 100644 index 0000000..b460004 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsResponse.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsResponse +{ + [JsonProperty("status")] + public required string Status { get; set; } + + [JsonProperty("error")] + public string? Error { get; set; } + + [JsonIgnore] + public bool Success => Status != "error"; +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs new file mode 100644 index 0000000..22b1f4e --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsSolveCapyResponse : BcsResponse +{ + [JsonProperty("solution")] + public string? Solution { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs new file mode 100644 index 0000000..cb3ed6d --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsSolveCloudflareTurnstileResponse : BcsResponse +{ + [JsonProperty("solution")] + public string? Solution { get; set; } + + [JsonProperty("user_agent")] + public string? UserAgent { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs new file mode 100644 index 0000000..5212b95 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsSolveFuncaptchaResponse : BcsResponse +{ + [JsonProperty("solution")] + public string? Solution { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs new file mode 100644 index 0000000..b520a45 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsSolveGeeTestResponse +{ + [JsonProperty("solution")] + public GeeTestSolution? Solution { get; set; } +} + +internal class GeeTestSolution +{ + [JsonProperty("challenge")] + public required string Challenge { get; set; } + + [JsonProperty("validate")] + public required string Validate { get; set; } + + [JsonProperty("seccode")] + public required string SecCode { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs new file mode 100644 index 0000000..65c2e8f --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsSolveHCaptchaResponse : BcsResponse +{ + [JsonProperty("solution")] + public string? Solution { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs new file mode 100644 index 0000000..86f0522 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsSolveImageResponse : BcsResponse +{ + [JsonProperty("text")] + public string? Text { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs new file mode 100644 index 0000000..d5bfd66 --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsSolveRecaptchaResponse : BcsResponse +{ + [JsonProperty("gresponse")] + public string? GResponse { get; set; } +} + diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs new file mode 100644 index 0000000..64e6bef --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; + +internal class BcsTaskCreatedResponse : BcsResponse +{ + [JsonProperty("id")] + public long Id { get; set; } +} diff --git a/CaptchaSharp/Services/BestCaptchaSolverService.cs b/CaptchaSharp/Services/BestCaptchaSolverService.cs new file mode 100644 index 0000000..92afc2e --- /dev/null +++ b/CaptchaSharp/Services/BestCaptchaSolverService.cs @@ -0,0 +1,467 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Exceptions; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; +using CaptchaSharp.Services.BestCaptchaSolver.Requests; +using CaptchaSharp.Services.BestCaptchaSolver.Responses; + +namespace CaptchaSharp.Services; + +/// +/// The service offered by https://bestcaptchasolver.com/ +/// +public class BestCaptchaSolverService : CaptchaService +{ + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The default used for requests. + /// + private readonly HttpClient _httpClient; + + private const string _affiliateId = "5e95fff9fe5f8247ff965ac3"; + + /// + /// Initializes a new . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public BestCaptchaSolverService(string apiKey, HttpClient? httpClient = null) + { + ApiKey = apiKey; + _httpClient = httpClient ?? new HttpClient(); + _httpClient.BaseAddress = new Uri("https://bcsapi.xyz/api/"); + } + + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var json = await _httpClient.GetStringAsync( + "user/balance", + new StringPairCollection() + .Add("access_token", ApiKey), + cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Success) + { + throw new BadAuthenticationException(response.Error!); + } + + return decimal.Parse(response.Balance!); + } + #endregion + + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + var payload = new BcsSolveImageRequest + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + Base64Image = base64, + CaseSensitive = options?.CaseSensitive, + IsPhrase = options?.IsPhrase, + IsMath = options?.RequiresCalculation, + Alphanumeric = options?.CharacterSet switch + { + CharacterSet.OnlyNumbers => 1, + CharacterSet.OnlyLetters => 2, + _ => null + }, + MinLength = options?.MinLength, + MaxLength = options?.MaxLength + }; + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/image", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.ImageCaptcha, + cancellationToken: cancellationToken); + } + + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, + bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var type = invisible ? 2 : 1; + + if (enterprise) + { + type = 4; + } + + var payload = new BcsSolveRecaptchaV2Request + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + SiteKey = siteKey, + PageUrl = siteUrl, + Type = type, + DataS = dataS + }; + + payload.SetProxy(proxy); + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/recaptcha", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.ReCaptchaV2, + cancellationToken: cancellationToken); + } + + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var payload = new BcsSolveRecaptchaV3Request + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + SiteKey = siteKey, + PageUrl = siteUrl, + Type = enterprise ? 5 : 3, + Action = action, + MinScore = minScore + }; + + payload.SetProxy(proxy); + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/recaptcha", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.ReCaptchaV3, + cancellationToken: cancellationToken); + } + + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var payload = new BcsSolveFuncaptchaRequest + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + SiteKey = publicKey, + PageUrl = siteUrl, + SUrl = serviceUrl + }; + + payload.SetProxy(proxy); + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/funcaptcha", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.FunCaptcha, + cancellationToken: cancellationToken); + } + + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var payload = new BcsSolveHCaptchaRequest + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + SiteKey = siteKey, + PageUrl = siteUrl + }; + + payload.SetProxy(proxy); + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/hcaptcha", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.HCaptcha, + cancellationToken: cancellationToken); + } + + public override async Task SolveGeeTestAsync( + string gt, string challenge, string siteUrl, string? apiServer = null, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var payload = new BcsSolveGeeTestRequest + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + Gt = gt, + Challenge = challenge, + Domain = siteUrl, + ApiServer = string.IsNullOrEmpty(apiServer) ? null : apiServer + }; + + payload.SetProxy(proxy); + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/geetest", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.GeeTest, + cancellationToken: cancellationToken); + } + + /// + public override async Task SolveCapyAsync( + string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var payload = new BcsSolveCapyRequest + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + SiteKey = siteKey, + PageUrl = siteUrl + }; + + payload.SetProxy(proxy); + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/capy", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.Capy, + cancellationToken: cancellationToken); + } + + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var payload = new BcsSolveCloudflareTurnstileRequest + { + AccessToken = ApiKey, + AffiliateId = _affiliateId, + SiteKey = siteKey, + PageUrl = siteUrl, + Action = action, + CData = data + }; + + payload.SetProxy(proxy); + + var json = await _httpClient.PostJsonToStringAsync( + "captcha/turnstile", + payload, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + return await GetResult( + response, CaptchaType.CloudflareTurnstile, + cancellationToken: cancellationToken); + } + + #endregion + + #region Getting the result + private async Task GetResult( + BcsTaskCreatedResponse response, + CaptchaType type, CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (!response.Success) + { + throw new TaskCreationException(response.Error!); + } + + var task = new CaptchaTask(response.Id, type); + + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } + + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) where T : class + { + var json = await _httpClient.GetStringAsync( + $"captcha/{task.Id}", + new StringPairCollection() + .Add("access_token", ApiKey), + cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Success) + { + throw new TaskSolutionException(response.Error!); + } + + if (response.Status is not "completed") + { + return null; + } + + task.Completed = true; + + if (task.Type is CaptchaType.ImageCaptcha) + { + var imageResponse = json.Deserialize(); + return new StringResponse + { + Id = task.Id, + Response = imageResponse.Text! + } as T; + } + + if (task.Type is CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3) + { + var recaptchaResponse = json.Deserialize(); + return new StringResponse + { + Id = task.Id, + Response = recaptchaResponse.GResponse! + } as T; + } + + if (task.Type is CaptchaType.FunCaptcha) + { + var funcaptchaResponse = json.Deserialize(); + return new StringResponse + { + Id = task.Id, + Response = funcaptchaResponse.Solution! + } as T; + } + + if (task.Type is CaptchaType.HCaptcha) + { + var hCaptchaResponse = json.Deserialize(); + return new StringResponse + { + Id = task.Id, + Response = hCaptchaResponse.Solution! + } as T; + } + + if (task.Type is CaptchaType.GeeTest) + { + var geeTestResponse = json.Deserialize(); + return new GeeTestResponse + { + Id = task.Id, + Challenge = geeTestResponse.Solution!.Challenge, + Validate = geeTestResponse.Solution!.Validate, + SecCode = geeTestResponse.Solution!.SecCode + } as T; + } + + if (task.Type is CaptchaType.Capy) + { + var capyResponse = json.Deserialize(); + return new StringResponse + { + Id = task.Id, + Response = capyResponse.Solution! + } as T; + } + + if (task.Type is CaptchaType.CloudflareTurnstile) + { + var cloudflareResponse = json.Deserialize(); + return new CloudflareTurnstileResponse + { + Id = task.Id, + Response = cloudflareResponse.Solution!, + UserAgent = cloudflareResponse.UserAgent! + } as T; + } + + throw new NotImplementedException(); + } + #endregion + + #region Reporting the solution + /// + public override async Task ReportSolution( + long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + if (correct) + { + throw new ArgumentException( + "BestCaptchaSolver does not support reporting correct solutions."); + } + + var json = await _httpClient.PostJsonToStringAsync( + $"captcha/bad/{id}", + new BcsRequest + { + AccessToken = ApiKey, + }, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var response = json.Deserialize(); + + if (!response.Success) + { + throw new TaskReportException(response.Error!); + } + + if (response.Status != "updated") + { + throw new TaskReportException(response.Status); + } + } + + #endregion +} diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index 5aa04f9..481212c 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -51,7 +51,7 @@ public override async Task SolveDataDomeAsync( } // The cookie must contain datadome=... and nothing else - var datadomeCookie = Array.Find(proxy.Cookies, c => c.Item1 == "datadome").Item2; + var datadomeCookie = Array.Find(proxy.Cookies, c => c.Name == "datadome").Value; if (string.IsNullOrEmpty(datadomeCookie) || proxy.Cookies.Length > 1) { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs index 6c84776..8f45020 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs +++ b/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs @@ -35,8 +35,8 @@ public CapSolverTask SetProxy(Proxy proxy) { Cookies = proxy.Cookies.Select(c => new CapSolverCookie { - Name = c.Item1, - Value = c.Item2 + Name = c.Name, + Value = c.Value }).ToArray(); } diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs b/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs index 9bd2869..5c69cea 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs +++ b/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs @@ -46,8 +46,8 @@ public void SetProxy(Proxy? proxy, string url) Cookies = proxy.Cookies.Select(c => new NopechaCookie { - Name = c.Item1, - Value = c.Item2, + Name = c.Name, + Value = c.Value, Domain = uri.Host, Path = uri.AbsolutePath, Secure = uri.Scheme == "https", diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 09c6102..609b523 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -64,7 +64,8 @@ public override async Task GetBalanceAsync(CancellationToken cancellati .Add("key", ApiKey) .Add("action", "getbalance") .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), - cancellationToken); + cancellationToken) + .ConfigureAwait(false); if (UseJsonFlag) { @@ -72,10 +73,10 @@ public override async Task GetBalanceAsync(CancellationToken cancellati if (tcResponse.IsErrorCode) { - throw new BadAuthenticationException(tcResponse.Request); + throw new BadAuthenticationException(tcResponse.Request!); } - return decimal.Parse(tcResponse.Request, CultureInfo.InvariantCulture); + return decimal.Parse(tcResponse.Request!, CultureInfo.InvariantCulture); } if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) From 8f4366d87aae0491f02e7fbf1e0aeea8f0428efd Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 12:02:43 +0200 Subject: [PATCH 29/67] Changed all captcha task ids to strings --- CaptchaSharp.Tests/ServiceTests.cs | 48 ++++------- CaptchaSharp/CaptchaService.cs | 52 ++++-------- CaptchaSharp/CaptchaSharp.xml | 85 +++++-------------- CaptchaSharp/Models/CaptchaResponse.cs | 21 +---- CaptchaSharp/Models/CaptchaTask.cs | 22 +---- .../Solutions/AntiCaptchaTaskSolution.cs | 2 +- .../FuncaptchaAntiCaptchaTaskSolution.cs | 2 +- .../GeeTestAntiCaptchaTaskSolution.cs | 2 +- .../ImageCaptchaAntiCaptchaTaskSolution.cs | 2 +- .../RecaptchaAntiCaptchaTaskSolution.cs | 2 +- .../TurnstileAntiCaptchaTaskSolution.cs | 2 +- CaptchaSharp/Services/AntiCaptchaService.cs | 23 +++-- .../Services/BestCaptchaSolverService.cs | 4 +- .../Services/CapMonsterCloudService.cs | 4 +- .../Solutions/CloudflareTurnstileSolution.cs | 2 +- .../Responses/Solutions/DataDomeSolution.cs | 2 +- .../Responses/Solutions/FuncaptchaSolution.cs | 2 +- .../Responses/Solutions/GeeTestSolution.cs | 2 +- .../Solutions/ImageCaptchaSolution.cs | 2 +- .../Responses/Solutions/RecaptchaSolution.cs | 2 +- CaptchaSharp/Services/CapSolverService.cs | 4 +- CaptchaSharp/Services/CaptchaCoderService.cs | 13 +-- .../Services/DeathByCaptchaService.cs | 2 +- CaptchaSharp/Services/ImageTyperzService.cs | 8 +- .../Services/MetaBypassTechService.cs | 6 +- CaptchaSharp/Services/NineKwService.cs | 4 +- CaptchaSharp/Services/NopechaService.cs | 6 +- CaptchaSharp/Services/TrueCaptchaService.cs | 11 +-- .../TwoCaptcha/TwoCaptchaCapyResponse.cs | 2 +- .../TwoCaptchaCloudflareTurnstileResponse.cs | 2 +- .../TwoCaptcha/TwoCaptchaGeeTestResponse.cs | 2 +- CaptchaSharp/Services/TwoCaptchaService.cs | 6 +- 32 files changed, 115 insertions(+), 234 deletions(-) diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index e17fe26..5c4084e 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -55,16 +55,8 @@ protected async Task ReportImageSolutionTest(bool correct = true) base64: _captchaImageBase64, options); - if (solution.IsLongId) - { - await Service.ReportSolution( - solution.Id, CaptchaType.ImageCaptcha, correct); - } - else - { - await Service.ReportSolution( - solution.IdString, CaptchaType.ImageCaptcha, correct); - } + await Service.ReportSolution( + solution.Id, CaptchaType.ImageCaptcha, correct); Assert.True(true); } @@ -78,16 +70,8 @@ protected async Task ReportRecaptchaSolutionTest(bool correct = true) enterprise: false, invisible: false); - if (solution.IsLongId) - { - await Service.ReportSolution( - solution.Id, CaptchaType.ReCaptchaV2, correct); - } - else - { - await Service.ReportSolution( - solution.IdString, CaptchaType.ReCaptchaV2, correct); - } + await Service.ReportSolution( + solution.Id, CaptchaType.ReCaptchaV2, correct); Assert.True(true); } @@ -141,7 +125,7 @@ private async Task RecaptchaV2Test(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -161,7 +145,7 @@ private async Task RecaptchaV2InvisibleTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -181,7 +165,7 @@ private async Task RecaptchaV2EnterpriseTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -201,7 +185,7 @@ private async Task RecaptchaV3Test(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -220,7 +204,7 @@ private async Task RecaptchaV3EnterpriseTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -239,7 +223,7 @@ private async Task FunCaptchaTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -255,7 +239,7 @@ private async Task HCaptchaTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -285,7 +269,7 @@ private async Task KeyCaptchaTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -316,7 +300,7 @@ private async Task GeeTestTest(Proxy? proxy) Assert.NotEqual("", solution.SecCode); Assert.NotEqual("", solution.Validate); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Challenge: {solution.Challenge}"); _output.WriteLine($"SecCode: {solution.SecCode}"); _output.WriteLine($"Validate: {solution.Validate}"); @@ -336,7 +320,7 @@ private async Task CapyTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.CaptchaKey); Assert.NotEqual(string.Empty, solution.Answer); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"ChallengeKey: {solution.ChallengeKey}"); _output.WriteLine($"CaptchaKey: {solution.CaptchaKey}"); _output.WriteLine($"Answer: {solution.Answer}"); @@ -390,7 +374,7 @@ private async Task DataDomeTest(Proxy proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); } @@ -405,7 +389,7 @@ private async Task CloudflareTurnstileTest(Proxy? proxy) Assert.NotEqual(string.Empty, solution.Response); - _output.WriteLine($"Captcha ID: {solution.IdString}"); + _output.WriteLine($"Captcha ID: {solution.Id}"); _output.WriteLine($"Response: {solution.Response}"); _output.WriteLine($"User-Agent: {solution.UserAgent}"); } diff --git a/CaptchaSharp/CaptchaService.cs b/CaptchaSharp/CaptchaService.cs index 2112b33..8bea582 100644 --- a/CaptchaSharp/CaptchaService.cs +++ b/CaptchaSharp/CaptchaService.cs @@ -42,7 +42,7 @@ public virtual Task GetBalanceAsync( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -69,7 +69,7 @@ public virtual Task SolveTextCaptchaAsync( /// /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -101,7 +101,7 @@ public virtual Task SolveImageCaptchaAsync( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -132,7 +132,7 @@ public virtual Task SolveRecaptchaV2Async( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -174,7 +174,7 @@ public virtual Task SolveRecaptchaV3Async( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -204,7 +204,7 @@ public virtual Task SolveFuncaptchaAsync( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -236,7 +236,7 @@ public virtual Task SolveHCaptchaAsync( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -267,7 +267,7 @@ public virtual Task SolveKeyCaptchaAsync( /// /// /// A containing the captcha id to be used with - /// and three solution parameters + /// and three solution parameters /// (Challenge, Validate and SecCode) that you will need to provide when you submit the form. /// /// @@ -296,7 +296,7 @@ public virtual Task SolveGeeTestAsync( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -326,7 +326,7 @@ public virtual Task SolveCapyAsync( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext (a.k.a. a valid datadome session cookie). /// /// @@ -358,7 +358,7 @@ public virtual Task SolveDataDomeAsync( /// /// /// A containing the captcha id to be used with - /// and the + /// and the /// captcha solution as plaintext. /// /// @@ -371,40 +371,18 @@ public virtual Task SolveCloudflareTurnstileAsync( { throw new NotSupportedException(); } - - /// - /// Reports a captcha solution as good or bad to the service. - /// Mostly used for reporting bad solutions for image captchas and get the funds back. - /// Make sure to not abuse this system or the service might ban you from accessing it! - /// - /// - /// The ID of the captcha that you got inside your . - /// The type of captcha you want to report. - /// - /// - /// If true, the captcha will be reported as correctly solved (this is not supported by most services). - /// - /// - /// A token that can be used to cancel the async task. - /// - /// - public virtual Task ReportSolution( - long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } /// /// Reports a captcha solution as good or bad to the service. /// Mostly used for reporting bad solutions for image captchas and get the funds back. - /// Make sure to not abuse this system or the service might ban you from accessing it! + /// Make sure to not abuse this system or the service might ban your account! /// /// /// The string ID of the captcha that you got inside your . /// The type of captcha you want to report. /// /// - /// If true, the captcha will be reported as correctly solved (this is not supported by most services). + /// If true, the captcha will be reported as correctly solved (this is not supported by some services). /// /// /// A token that can be used to cancel the async task. @@ -412,7 +390,9 @@ public virtual Task ReportSolution( /// public virtual Task ReportSolution( string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - => ReportSolution(long.Parse(id), type, correct, cancellationToken); + { + throw new NotSupportedException(); + } /// protected async Task GetResult( diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 1ec9191..9eaf73e 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -36,7 +36,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -58,7 +58,7 @@ /// A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -85,7 +85,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -111,7 +111,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -148,7 +148,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -172,7 +172,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -199,7 +199,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -225,7 +225,7 @@ A containing the captcha id to be used with - and three solution parameters + and three solution parameters (Challenge, Validate and SecCode) that you will need to provide when you submit the form. @@ -249,7 +249,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -274,7 +274,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext (a.k.a. a valid datadome session cookie). @@ -301,7 +301,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -309,36 +309,18 @@ - - - Reports a captcha solution as good or bad to the service. - Mostly used for reporting bad solutions for image captchas and get the funds back. - Make sure to not abuse this system or the service might ban you from accessing it! - - - The ID of the captcha that you got inside your . - The type of captcha you want to report. - - - If true, the captcha will be reported as correctly solved (this is not supported by most services). - - - A token that can be used to cancel the async task. - - - Reports a captcha solution as good or bad to the service. Mostly used for reporting bad solutions for image captchas and get the funds back. - Make sure to not abuse this system or the service might ban you from accessing it! + Make sure to not abuse this system or the service might ban your account! The string ID of the captcha that you got inside your . The type of captcha you want to report. - If true, the captcha will be reported as correctly solved (this is not supported by most services). + If true, the captcha will be reported as correctly solved (this is not supported by some services). A token that can be used to cancel the async task. @@ -745,18 +727,7 @@ The captcha id which is needed to report the solution as bad. - - - Whether the captcha id is a long int or a string. - - - - - The captcha id which is needed to report the solution, if it's - a string instead of a long int. - - - + The time when the solution was received. @@ -771,18 +742,12 @@ The id of the task - - The id of the task as a string - Whether the task is completed Creates a from a string id - - Creates a from a long id - The solution of a Capy captcha. @@ -980,12 +945,12 @@ - + Parses the solution of a DataDome captcha. - + @@ -1077,7 +1042,7 @@ - + @@ -1095,7 +1060,7 @@ - + @@ -1205,9 +1170,6 @@ - - - @@ -1320,7 +1282,7 @@ - + @@ -1390,7 +1352,7 @@ - + @@ -1549,7 +1511,7 @@ - + @@ -1679,9 +1641,6 @@ - - - @@ -1759,7 +1718,7 @@ - + diff --git a/CaptchaSharp/Models/CaptchaResponse.cs b/CaptchaSharp/Models/CaptchaResponse.cs index bb6ce6d..c9b19c4 100644 --- a/CaptchaSharp/Models/CaptchaResponse.cs +++ b/CaptchaSharp/Models/CaptchaResponse.cs @@ -5,26 +5,9 @@ namespace CaptchaSharp.Models; /// A generic captcha response. public class CaptchaResponse { - // TODO: All captcha ids should be strings, then parse at need. Remove the long id. - /// The captcha id which is needed to report the solution as bad. - public long Id - { - get => long.Parse(IdString); - init => IdString = value.ToString(); - } - - /// - /// Whether the captcha id is a long int or a string. - /// - public bool IsLongId => long.TryParse(IdString, out _); - - /// - /// The captcha id which is needed to report the solution, if it's - /// a string instead of a long int. - /// - public string IdString { get; set; } = "0"; + public required string Id { get; set; } /// The time when the solution was received. - public DateTime Time { get; set; } = DateTime.Now; + public DateTime CompletedAt { get; set; } = DateTime.Now; } diff --git a/CaptchaSharp/Models/CaptchaTask.cs b/CaptchaSharp/Models/CaptchaTask.cs index f941373..74cbbb7 100644 --- a/CaptchaSharp/Models/CaptchaTask.cs +++ b/CaptchaSharp/Models/CaptchaTask.cs @@ -13,33 +13,15 @@ public class CaptchaTask public CaptchaType Type { get; set; } /// The id of the task - public long Id { get; } - - /// The id of the task as a string - public string IdString { get; } + public string Id { get; } /// Whether the task is completed - public bool Completed { get; set; } = false; + public bool Completed { get; set; } /// Creates a from a string id public CaptchaTask(string id, CaptchaType type) - { - IdString = id; - - if (long.TryParse(id, out var parsed)) - { - Id = parsed; - } - - Type = type; - CreationDate = DateTime.Now; - } - - /// Creates a from a long id - public CaptchaTask(long id, CaptchaType type) { Id = id; - IdString = id.ToString(); Type = type; CreationDate = DateTime.Now; } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs index a166bbd..47f62b0 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs @@ -5,7 +5,7 @@ namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; internal class AntiCaptchaTaskSolution { - public virtual CaptchaResponse ToCaptchaResponse(long id) + public virtual CaptchaResponse ToCaptchaResponse(string id) { throw new NotImplementedException(); } diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs index 7f8d9d8..b8e60a0 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs @@ -6,7 +6,7 @@ internal class FuncaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { public string Token { get; set; } - public override CaptchaResponse ToCaptchaResponse(long id) + public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs index 9a0e1d9..8c9f199 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs @@ -8,7 +8,7 @@ internal class GeeTestAntiCaptchaTaskSolution : AntiCaptchaTaskSolution public string Validate { get; set; } public string SecCode { get; set; } - public override CaptchaResponse ToCaptchaResponse(long id) + public override CaptchaResponse ToCaptchaResponse(string id) { return new GeeTestResponse() { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs index 6c8c796..b6124a5 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs @@ -7,7 +7,7 @@ internal class ImageCaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution public string Text { get; set; } public string Url { get; set; } - public override CaptchaResponse ToCaptchaResponse(long id) + public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs index 116057e..ffbaacc 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs @@ -6,7 +6,7 @@ internal class RecaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { public string GRecaptchaResponse { get; set; } - public override CaptchaResponse ToCaptchaResponse(long id) + public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs index 5865be3..f1fa5c3 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs @@ -8,7 +8,7 @@ internal class TurnstileAntiCaptchaTaskSolution : AntiCaptchaTaskSolution public required string UserAgent { get; set; } - public override CaptchaResponse ToCaptchaResponse(long id) + public override CaptchaResponse ToCaptchaResponse(string id) { return new CloudflareTurnstileResponse { diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index d77507d..167cfbd 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -367,7 +367,7 @@ protected async Task GetResult( throw new TaskCreationException($"{antiCaptchaResponse.ErrorCode}: {antiCaptchaResponse.ErrorDescription}"); } - var task = new CaptchaTask(antiCaptchaResponse.TaskId, type); + var task = new CaptchaTask(antiCaptchaResponse.TaskId.ToString(), type); return await GetResult(task, cancellationToken).ConfigureAwait(false); } @@ -379,7 +379,7 @@ protected async Task GetResult( { var response = await HttpClient.PostJsonToStringAsync( "getTaskResult", - new GetTaskResultRequest { ClientKey = ApiKey, TaskId = (int)task.Id }, + new GetTaskResultRequest { ClientKey = ApiKey, TaskId = int.Parse(task.Id) }, cancellationToken: cancellationToken).ConfigureAwait(false); var result = response.Deserialize(); @@ -403,6 +403,11 @@ protected async Task GetResult( { throw new TaskSolutionException(response); } + + if (task.Type == CaptchaType.DataDome) + { + return ParseDataDomeSolution(task.Id, solution) as T; + } result.AntiCaptchaTaskSolution = task.Type switch { @@ -421,16 +426,16 @@ protected async Task GetResult( /// /// Parses the solution of a DataDome captcha. /// - protected virtual StringResponse ParseDataDomeSolution(JToken? solution) + protected virtual StringResponse ParseDataDomeSolution(string taskId, JToken? solution) { - throw new NotImplementedException(); + throw new NotImplementedException("DataDome captcha solving is not supported"); } #endregion #region Reporting the solution /// public override async Task ReportSolution( - long id, CaptchaType type, bool correct = false, + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { if (correct) @@ -443,7 +448,7 @@ public override async Task ReportSolution( await HttpClient.PostJsonToStringAsync( "reportCorrectRecaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken).ConfigureAwait(false); return; @@ -457,7 +462,7 @@ await HttpClient.PostJsonToStringAsync( case CaptchaType.ImageCaptcha: response = await HttpClient.PostJsonToStringAsync( "reportIncorrectImageCaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken).ConfigureAwait(false); incAntiCaptchaResponse = response.Deserialize(); @@ -467,7 +472,7 @@ await HttpClient.PostJsonToStringAsync( case CaptchaType.ReCaptchaV3: response = await HttpClient.PostJsonToStringAsync( "reportIncorrectRecaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken).ConfigureAwait(false); incAntiCaptchaResponse = response.Deserialize(); @@ -476,7 +481,7 @@ await HttpClient.PostJsonToStringAsync( case CaptchaType.HCaptcha: response = await HttpClient.PostJsonToStringAsync( "reportIncorrectHcaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = id }, + new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken).ConfigureAwait(false); incAntiCaptchaResponse = response.Deserialize(); diff --git a/CaptchaSharp/Services/BestCaptchaSolverService.cs b/CaptchaSharp/Services/BestCaptchaSolverService.cs index 92afc2e..7262ad7 100644 --- a/CaptchaSharp/Services/BestCaptchaSolverService.cs +++ b/CaptchaSharp/Services/BestCaptchaSolverService.cs @@ -323,7 +323,7 @@ private async Task GetResult( throw new TaskCreationException(response.Error!); } - var task = new CaptchaTask(response.Id, type); + var task = new CaptchaTask(response.Id.ToString(), type); return await GetResult(task, cancellationToken).ConfigureAwait(false); } @@ -433,7 +433,7 @@ private async Task GetResult( #region Reporting the solution /// public override async Task ReportSolution( - long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { if (correct) { diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index 481212c..f7d52c8 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -83,7 +83,7 @@ public override async Task SolveDataDomeAsync( } /// - protected override StringResponse ParseDataDomeSolution(JToken? solution) + protected override StringResponse ParseDataDomeSolution(string taskId, JToken? solution) { // The solution is like { "domains": { "site.com": { "cookies": { "datadome": "..." } } } } // We want to return the datadome cookie. Since site.com varies, we need to @@ -94,6 +94,6 @@ protected override StringResponse ParseDataDomeSolution(JToken? solution) ?.SelectToken("cookies.datadome") ?.Value() ?? ""; - return new StringResponse { Response = cookie }; + return new StringResponse { Id = taskId, Response = cookie }; } } diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs index 64cfdfb..9a8eba8 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs @@ -12,7 +12,7 @@ public override CaptchaResponse ToCaptchaResponse(string id) { return new CloudflareTurnstileResponse { - IdString = id, + Id = id, Response = Token, UserAgent = UserAgent }; diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs index afef88e..c08b79e 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs @@ -10,7 +10,7 @@ public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { - IdString = id, + Id = id, Response = Cookie }; } diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/FuncaptchaSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/FuncaptchaSolution.cs index 93fc33f..44e4bdd 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/FuncaptchaSolution.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/FuncaptchaSolution.cs @@ -10,7 +10,7 @@ public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { - IdString = id, + Id = id, Response = Token }; } diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/GeeTestSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/GeeTestSolution.cs index b124707..8b8cccc 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/GeeTestSolution.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/GeeTestSolution.cs @@ -12,7 +12,7 @@ public override CaptchaResponse ToCaptchaResponse(string id) { return new GeeTestResponse() { - IdString = id, + Id = id, Challenge = Challenge, Validate = Validate, SecCode = SecCode diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs index e9408e2..c08ef5c 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs @@ -11,7 +11,7 @@ public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { - IdString = id, + Id = id, Response = Text }; } diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/RecaptchaSolution.cs b/CaptchaSharp/Services/CapSolver/Responses/Solutions/RecaptchaSolution.cs index 3a176c4..09c0ee1 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/RecaptchaSolution.cs +++ b/CaptchaSharp/Services/CapSolver/Responses/Solutions/RecaptchaSolution.cs @@ -10,7 +10,7 @@ public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { - IdString = id, + Id = id, Response = GRecaptchaResponse }; } diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index 4392ec6..71782a9 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -411,7 +411,7 @@ private async Task GetResult( { var response = await _httpClient.PostJsonToStringAsync( "getTaskResult", - new GetTaskResultRequest { ClientKey = ApiKey, TaskId = task.IdString }, + new GetTaskResultRequest { ClientKey = ApiKey, TaskId = task.Id }, cancellationToken: cancellationToken).ConfigureAwait(false); var result = response.Deserialize(); @@ -448,7 +448,7 @@ private async Task GetResult( _ => throw new NotSupportedException($"The captcha type {task.Type} is not supported") } ?? throw new TaskSolutionException("The solution is null"); - return result.Solution.ToCaptchaResponse(task.IdString) as T; + return result.Solution.ToCaptchaResponse(task.Id) as T; } #endregion diff --git a/CaptchaSharp/Services/CaptchaCoderService.cs b/CaptchaSharp/Services/CaptchaCoderService.cs index 82838a4..4324ab5 100644 --- a/CaptchaSharp/Services/CaptchaCoderService.cs +++ b/CaptchaSharp/Services/CaptchaCoderService.cs @@ -82,7 +82,7 @@ public override async Task SolveImageCaptchaAsync( throw new TaskSolutionException(response); } - return new StringResponse { IdString = captchaId, Response = response }; + return new StringResponse { Id = captchaId, Response = response }; } /// @@ -115,7 +115,7 @@ public override async Task SolveRecaptchaV2Async( throw new TaskSolutionException(response); } - return new StringResponse { IdString = captchaId, Response = response }; + return new StringResponse { Id = captchaId, Response = response }; } /// @@ -149,19 +149,12 @@ public override async Task SolveRecaptchaV3Async( throw new TaskSolutionException(response); } - return new StringResponse { IdString = captchaId, Response = response }; + return new StringResponse { Id = captchaId, Response = response }; } #endregion #region Reporting the solution - /// - public override Task ReportSolution( - long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - { - throw new NotSupportedException("Use the string id overload instead."); - } - /// public override async Task ReportSolution( string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) diff --git a/CaptchaSharp/Services/DeathByCaptchaService.cs b/CaptchaSharp/Services/DeathByCaptchaService.cs index 7aab941..d4aced6 100644 --- a/CaptchaSharp/Services/DeathByCaptchaService.cs +++ b/CaptchaSharp/Services/DeathByCaptchaService.cs @@ -528,7 +528,7 @@ private async Task GetResult( #region Reporting the solution /// public override async Task ReportSolution( - long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { if (correct) { diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index 25c0734..a182644 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -90,7 +90,7 @@ public override async Task SolveImageCaptchaAsync( } var split = response.Split(['|'], 2); - return new StringResponse { Id = long.Parse(split[0]), Response = split[1] }; + return new StringResponse { Id = split[0], Response = split[1] }; } /// @@ -310,6 +310,7 @@ private async Task GetResult( return new GeeTestResponse { + Id = task.Id, Challenge = geeTestResponse["geetest_challenge"]!.Value()!, Validate = geeTestResponse["geetest_validate"]!.Value()!, SecCode = geeTestResponse["geetest_seccode"]!.Value()! @@ -329,6 +330,7 @@ private async Task GetResult( return new CloudflareTurnstileResponse { + Id = task.Id, Response = cloudflareResponse["Response"]!.Value()!, UserAgent = cloudflareResponse["UserAgent"]!.Value()! } as T; @@ -346,8 +348,8 @@ private async Task GetResult( #region Reporting the solution /// - public override async Task ReportSolution - (long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + public override async Task ReportSolution( + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { var response = await _httpClient.PostToStringAsync( "Forms/SetBadImageToken.ashx", diff --git a/CaptchaSharp/Services/MetaBypassTechService.cs b/CaptchaSharp/Services/MetaBypassTechService.cs index 4593e31..a1397e2 100644 --- a/CaptchaSharp/Services/MetaBypassTechService.cs +++ b/CaptchaSharp/Services/MetaBypassTechService.cs @@ -139,7 +139,7 @@ public override async Task SolveImageCaptchaAsync( return new StringResponse { - Id = 0, + Id = "0", Response = response.Data!["result"]!.ToString() }; } @@ -223,7 +223,7 @@ public override async Task SolveRecaptchaV3Async( return new StringResponse { - Id = 0, + Id = "0", Response = response.Data!["RecaptchaResponse"]!.ToString() }; } @@ -262,7 +262,7 @@ public override async Task SolveRecaptchaV3Async( return new StringResponse { - IdString = task.IdString, + Id = task.Id, Response = response.Data!["RecaptchaResponse"]!.ToString() } as T; } diff --git a/CaptchaSharp/Services/NineKwService.cs b/CaptchaSharp/Services/NineKwService.cs index c34a07a..1b73282 100644 --- a/CaptchaSharp/Services/NineKwService.cs +++ b/CaptchaSharp/Services/NineKwService.cs @@ -275,13 +275,13 @@ private async Task GetResult( #region Reporting the solution /// public override async Task ReportSolution( - long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { var json = await _httpClient.GetStringAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchacorrectback") - .Add("id", id.ToString()) + .Add("id", id) .Add("correct", correct ? 1 : 2) .Add("json", 1), cancellationToken); diff --git a/CaptchaSharp/Services/NopechaService.cs b/CaptchaSharp/Services/NopechaService.cs index 53759bd..56c6fa6 100644 --- a/CaptchaSharp/Services/NopechaService.cs +++ b/CaptchaSharp/Services/NopechaService.cs @@ -234,7 +234,7 @@ private async Task GetResult( "", new StringPairCollection() .Add("key", ApiKey) - .Add("id", task.IdString), + .Add("id", task.Id), cancellationToken) .ConfigureAwait(false); @@ -255,7 +255,7 @@ private async Task GetResult( { return new CloudflareTurnstileResponse { - IdString = task.IdString, + Id = task.Id, Response = response.Data!.ToString() } as T; } @@ -274,7 +274,7 @@ private async Task GetResult( return new StringResponse { - IdString = task.IdString, + Id = task.Id, Response = result } as T; } diff --git a/CaptchaSharp/Services/TrueCaptchaService.cs b/CaptchaSharp/Services/TrueCaptchaService.cs index 44de92b..7b07c4a 100644 --- a/CaptchaSharp/Services/TrueCaptchaService.cs +++ b/CaptchaSharp/Services/TrueCaptchaService.cs @@ -94,16 +94,9 @@ public override async Task SolveImageCaptchaAsync var requestId = jObject["requestId"]!.Value()!; - return new StringResponse { IdString = requestId, Response = result.ToString() }; + return new StringResponse { Id = requestId, Response = result.ToString() }; } - - /// - public override Task ReportSolution( - long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) - { - throw new NotSupportedException("Use the string id overload instead."); - } - + /// public override async Task ReportSolution( string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs index b8e230e..65b45e3 100644 --- a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs +++ b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs @@ -13,7 +13,7 @@ internal class CapySolution public string? ChallengeKey { get; set; } public string? Answer { get; set; } - public CapyResponse ToCapyResponse(long id) + public CapyResponse ToCapyResponse(string id) { return new CapyResponse() { diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs index 69a66a4..b63111b 100644 --- a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs +++ b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs @@ -11,7 +11,7 @@ internal class TwoCaptchaCloudflareTurnstileResponse : TwoCaptchaResponse [JsonProperty("useragent")] public string? UserAgent { get; set; } - public CloudflareTurnstileResponse ToCloudflareTurnstileResponse(long id) + public CloudflareTurnstileResponse ToCloudflareTurnstileResponse(string id) { return new CloudflareTurnstileResponse() { diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs index cabdc8c..2d4a247 100644 --- a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs +++ b/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs @@ -19,7 +19,7 @@ internal class GeeTestSolution [JsonProperty(PropertyName = "geetest_seccode")] public string Seccode { get; set; } - public GeeTestResponse ToGeeTestResponse(long id) + public GeeTestResponse ToGeeTestResponse(string id) { return new GeeTestResponse() { diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 609b523..83f0699 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -540,7 +540,7 @@ internal async Task GetResult( #region Reporting the solution /// public override async Task ReportSolution( - long id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { var action = correct ? "reportgood" : "reportbad"; @@ -548,7 +548,7 @@ public override async Task ReportSolution( new StringPairCollection() .Add("key", ApiKey) .Add("action", action) - .Add("id", id.ToString()) + .Add("id", id) .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), cancellationToken); @@ -557,7 +557,7 @@ public override async Task ReportSolution( var tcResponse = response.Deserialize(); if (tcResponse.IsErrorCode) - throw new TaskReportException(tcResponse.Request); + throw new TaskReportException(tcResponse.Request!); } else { From 11ad7b791a8ecfe0542d39d440abc333b1afbfd1 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 12:34:05 +0200 Subject: [PATCH 30/67] Added CaptchaAiService --- CaptchaSharp.Tests/CaptchaAiServiceTests.cs | 35 +++++++++++++++ CaptchaSharp.Tests/ConfigFixture.cs | 1 + CaptchaSharp/CaptchaSharp.xml | 37 +++++++++++----- CaptchaSharp/Services/CaptchaAiService.cs | 48 +++++++++++++++++++++ CaptchaSharp/Services/TwoCaptchaService.cs | 4 ++ 5 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 CaptchaSharp.Tests/CaptchaAiServiceTests.cs create mode 100644 CaptchaSharp/Services/CaptchaAiService.cs diff --git a/CaptchaSharp.Tests/CaptchaAiServiceTests.cs b/CaptchaSharp.Tests/CaptchaAiServiceTests.cs new file mode 100644 index 0000000..42f1613 --- /dev/null +++ b/CaptchaSharp.Tests/CaptchaAiServiceTests.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class CaptchaAiFixture : ServiceFixture +{ + public CaptchaAiFixture() + { + Service = new CaptchaAiService( + Config.Credentials.CaptchaAiApiKey); + } +} + +public class CaptchaAiServiceTests(CaptchaAiFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV3EnterpriseTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); +} diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 0b40ba0..def3a47 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -61,4 +61,5 @@ public class Credentials public string NoCaptchaAiApiKey { get; set; } = string.Empty; public string NopechaApiKey { get; set; } = string.Empty; public string BestCaptchaSolverApiKey { get; set; } = string.Empty; + public string CaptchaAiApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 9eaf73e..9b1cb05 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -36,7 +36,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -58,7 +58,7 @@ /// A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -85,7 +85,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -111,7 +111,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -148,7 +148,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -172,7 +172,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -199,7 +199,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -225,7 +225,7 @@ A containing the captcha id to be used with - and three solution parameters + and three solution parameters (Challenge, Validate and SecCode) that you will need to provide when you submit the form. @@ -249,7 +249,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -274,7 +274,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext (a.k.a. a valid datadome session cookie). @@ -301,7 +301,7 @@ A containing the captcha id to be used with - and the + and the captcha solution as plaintext. @@ -1140,6 +1140,21 @@ + + + The service provided by https://captchaai.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + The service provided by https://captchacoder.com/ diff --git a/CaptchaSharp/Services/CaptchaAiService.cs b/CaptchaSharp/Services/CaptchaAiService.cs new file mode 100644 index 0000000..18b67d3 --- /dev/null +++ b/CaptchaSharp/Services/CaptchaAiService.cs @@ -0,0 +1,48 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Exceptions; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; + +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://captchaai.com/ +/// +public class CaptchaAiService : CustomTwoCaptchaService +{ + /// + /// Initializes a . + /// + /// The API key to use. + /// The to use for requests. If null, a default one will be created. + public CaptchaAiService(string apiKey, HttpClient? httpClient = null) + : base(apiKey, new Uri("http://ocr.captchaai.com"), httpClient) { } + + #region Reporting the solution + /// + public override async Task ReportSolution( + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + var action = correct ? "reportgood" : "reportbad"; + + // This service just replies with 200 OK without body + // when reporting a solution, so we don't need to check the response + var response = await HttpClient.GetAsync("res.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("action", action) + .Add("id", id) + .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), + cancellationToken); + + if (!response.IsSuccessStatusCode) + { + throw new TaskReportException("Failed to report the solution."); + } + } + #endregion +} diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 83f0699..1d33f12 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -557,12 +557,16 @@ public override async Task ReportSolution( var tcResponse = response.Deserialize(); if (tcResponse.IsErrorCode) + { throw new TaskReportException(tcResponse.Request!); + } } else { if (IsErrorCode(response)) + { throw new TaskReportException(response); + } } } #endregion From 640546cd3f3df1492cf3e617d646f35c562d0afd Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 13:49:20 +0200 Subject: [PATCH 31/67] Reorganized models in CaptchaSharp.Models namespace --- CaptchaSharp.Tests/ServiceFixture.cs | 4 +- CaptchaSharp.Tests/ServiceTests.cs | 1 + .../Requests/CaptchaTaskRequest.cs | 4 +- .../Requests/GetTaskResultRequest.cs | 2 +- .../Requests/RecaptchaV2CaptchaRequest.cs | 2 +- .../Requests/ReportIncorrectCaptchaRequest.cs | 2 +- .../AntiCaptcha/Requests/Request.cs | 2 +- .../Tasks/AntiCaptchaTaskProxyless.cs | 2 +- .../Requests/Tasks/FunCaptchaTaskProxyless.cs | 2 +- .../Requests/Tasks/GeeTestTaskProxyless.cs | 2 +- .../Requests/Tasks/HCaptchaTaskProxyless.cs | 2 +- .../Tasks/ImageCaptchaTaskProxyless.cs | 2 +- .../Requests/Tasks/Proxied/AntiCaptchaTask.cs | 2 +- .../Requests/Tasks/Proxied/FunCaptchaTask.cs | 2 +- .../Requests/Tasks/Proxied/GeeTestTask.cs | 2 +- .../Requests/Tasks/Proxied/HCaptchaTask.cs | 2 +- .../Proxied/RecaptchaV2EnterpriseTask.cs | 2 +- .../Requests/Tasks/Proxied/RecaptchaV2Task.cs | 2 +- .../Requests/Tasks/Proxied/TurnstileTask.cs | 2 +- .../RecaptchaV2EnterpriseTaskProxyless.cs | 2 +- .../Tasks/RecaptchaV2TaskProxyless.cs | 2 +- .../Tasks/RecaptchaV3TaskProxyless.cs | 2 +- .../Requests/Tasks/TurnstileTaskProxyless.cs | 2 +- .../Responses/AntiCaptchaResponse.cs | 2 +- .../GetBalanceAntiCaptchaResponse.cs | 2 +- .../GetTaskResultAntiCaptchaResponse.cs | 4 +- ...portIncorrectCaptchaAntiCaptchaResponse.cs | 2 +- .../Solutions/AntiCaptchaTaskSolution.cs | 2 +- .../FuncaptchaAntiCaptchaTaskSolution.cs | 2 +- .../GeeTestAntiCaptchaTaskSolution.cs | 2 +- .../ImageCaptchaAntiCaptchaTaskSolution.cs | 2 +- .../RecaptchaAntiCaptchaTaskSolution.cs | 2 +- .../TurnstileAntiCaptchaTaskSolution.cs | 2 +- .../TaskCreationAntiCaptchaResponse.cs | 2 +- .../BestCaptchaSolver/Requests/BcsRequest.cs | 2 +- .../Requests/BcsSolveCapyRequest.cs | 2 +- .../BcsSolveCloudflareTurnstileRequest.cs | 2 +- .../Requests/BcsSolveFuncaptchaRequest.cs | 2 +- .../Requests/BcsSolveGeeTestRequest.cs | 2 +- .../Requests/BcsSolveHCaptchaRequest.cs | 2 +- .../Requests/BcsSolveImageRequest.cs | 2 +- .../Requests/BcsSolveRecaptchaV2Request.cs | 2 +- .../Requests/BcsSolveRecaptchaV3Request.cs | 2 +- .../Requests/BcsSolveRequest.cs | 2 +- .../Responses/BcsBalanceResponse.cs | 2 +- .../Responses/BcsResponse.cs | 2 +- .../Responses/BcsSolveCapyResponse.cs | 2 +- .../BcsSolveCloudflareTurnstileResponse.cs | 2 +- .../Responses/BcsSolveFuncaptchaResponse.cs | 2 +- .../Responses/BcsSolveGeeTestResponse.cs | 2 +- .../Responses/BcsSolveHCaptchaResponse.cs | 2 +- .../Responses/BcsSolveImageResponse.cs | 2 +- .../Responses/BcsSolveRecaptchaResponse.cs | 2 +- .../Responses/BcsTaskCreatedResponse.cs | 2 +- .../Requests/Tasks/CustomTaskProxyless.cs | 4 +- .../Requests/Tasks/DataDomeTaskProxyless.cs | 4 +- .../Requests/CaptchaTaskFeedbackRequest.cs | 2 +- .../CapSolver/Requests/CaptchaTaskRequest.cs | 4 +- .../Requests/GetTaskResultRequest.cs | 2 +- .../CapSolver/Requests/Request.cs | 2 +- .../Tasks/AntiTurnstileTaskProxyless.cs | 2 +- .../Requests/Tasks/CapSolverTaskProxyless.cs | 2 +- .../Requests/Tasks/FunCaptchaTaskProxyless.cs | 2 +- .../Requests/Tasks/GeeTestTaskProxyless.cs | 2 +- .../Requests/Tasks/HCaptchaTaskProxyless.cs | 2 +- .../Tasks/ImageCaptchaTaskProxyless.cs | 2 +- .../Requests/Tasks/Proxied/CapSolverTask.cs | 2 +- .../Requests/Tasks/Proxied/DataDomeTask.cs | 2 +- .../Requests/Tasks/Proxied/FunCaptchaTask.cs | 2 +- .../Requests/Tasks/Proxied/GeeTestTask.cs | 2 +- .../Requests/Tasks/Proxied/HCaptchaTask.cs | 2 +- .../Proxied/RecaptchaV2EnterpriseTask.cs | 2 +- .../Requests/Tasks/Proxied/RecaptchaV2Task.cs | 2 +- .../Requests/Tasks/Proxied/RecaptchaV3Task.cs | 2 +- .../RecaptchaV2EnterpriseTaskProxyless.cs | 2 +- .../Tasks/RecaptchaV2TaskProxyless.cs | 2 +- .../Tasks/RecaptchaV3TaskProxyless.cs | 2 +- .../Responses/CaptchaTaskFeedbackResponse.cs | 2 +- .../CapSolver/Responses/GetBalanceResponse.cs | 2 +- .../Responses/GetTaskResultResponse.cs | 4 +- .../CapSolver/Responses/Response.cs | 2 +- .../Solutions/CloudflareTurnstileSolution.cs | 2 +- .../Responses/Solutions/DataDomeSolution.cs | 2 +- .../Responses/Solutions/FuncaptchaSolution.cs | 2 +- .../Responses/Solutions/GeeTestSolution.cs | 2 +- .../Solutions/ImageCaptchaSolution.cs | 2 +- .../Responses/Solutions/RecaptchaSolution.cs | 2 +- .../CapSolver/Responses/Solutions/Solution.cs | 2 +- .../Responses/TaskCreationResponse.cs | 2 +- .../Responses/CapyDbcResponse.cs | 2 +- .../Responses/GeeTestDbcResponse.cs | 2 +- .../Tasks/CapyDbcTaskProxyless.cs | 2 +- .../DeathByCaptcha/Tasks/DBCTaskProxyless.cs | 6 +++ .../Tasks/FunCaptchaDbcTaskProxyless.cs | 2 +- .../Tasks/GeeTestDbcTaskProxyless.cs | 2 +- .../Tasks/HCaptchaDbcTaskProxyless.cs | 2 +- .../Tasks/KeyCaptchaDbcTaskProxyless.cs | 2 +- .../Tasks/Proxied/CapyDbcTask.cs | 2 +- .../Proxied/CloudflareTurnstileDbcTask.cs | 2 +- .../Tasks/Proxied/DataDomeDbcTask.cs | 2 +- .../DeathByCaptcha/Tasks/Proxied/DbcTask.cs | 2 +- .../Tasks/Proxied/FunCaptchaDbcTask.cs | 2 +- .../Tasks/Proxied/GeeTestDbcTask.cs | 2 +- .../Tasks/Proxied/HCaptchaDbcTask.cs | 2 +- .../Tasks/Proxied/KeyCaptchaDbcTask.cs | 2 +- .../Tasks/Proxied/RecaptchaV2DbcTask.cs | 2 +- .../Tasks/Proxied/RecaptchaV3DbcTask.cs | 2 +- .../Tasks/RecaptchaV2DbcTaskProxyless.cs | 2 +- .../Tasks/RecaptchaV3DbcTaskProxyless.cs | 2 +- .../ImageTyperz/ImageTyperzResponse.cs | 2 +- .../ImageTyperzTaskCreatedResponse.cs | 2 +- .../MetaBypassTech/MbtAccessTokenRequest.cs} | 4 +- .../MetaBypassTech/MbtAccessTokenResponse.cs} | 6 +-- .../MbtRefreshAccessTokenRequest.cs} | 4 +- .../MetaBypassTech/MbtResponse.cs} | 4 +- .../MbtSolveImageCaptchaRequest.cs} | 4 +- .../MbtSolveRecaptchaRequest.cs} | 4 +- .../NineKw/NineKwBalanceResponse.cs | 2 +- .../NineKw/NineKwCheckResponse.cs | 2 +- .../NineKw/NineKwResponse.cs | 2 +- .../NineKw/NineKwStatus.cs | 2 +- .../NineKw/NineKwSubmitResponse.cs | 2 +- .../Nopecha/NopechaCookie.cs | 2 +- .../Nopecha/NopechaDataResponse.cs | 2 +- .../Nopecha/NopechaProxy.cs | 2 +- .../Nopecha/NopechaRequest.cs | 2 +- .../Nopecha/NopechaResponse.cs | 2 +- .../NopechaSolveCloudflareTurnstileRequest.cs | 2 +- .../Nopecha/NopechaSolveHCaptchaRequest.cs | 2 +- .../Nopecha/NopechaSolveImageRequest.cs | 2 +- .../Nopecha/NopechaSolveRecaptchaV2Request.cs | 2 +- .../Nopecha/NopechaSolveRecaptchaV3Request.cs | 2 +- .../Nopecha/NopechaSolveRequest.cs | 2 +- .../Nopecha/NopechaSolveTokenRequest.cs | 2 +- .../Nopecha/NopechaStatusResponse.cs | 2 +- .../TwoCaptcha/TwoCaptchaCapyResponse.cs | 2 +- .../TwoCaptchaCloudflareTurnstileResponse.cs | 2 +- .../TwoCaptcha/TwoCaptchaGeeTestResponse.cs | 2 +- .../TwoCaptcha/TwoCaptchaResponse.cs | 2 +- CaptchaSharp/Services/AntiCaptchaService.cs | 10 ++--- .../Services/BestCaptchaSolverService.cs | 4 +- .../Services/CapMonsterCloudService.cs | 4 +- CaptchaSharp/Services/CapSolverService.cs | 10 ++--- .../CaptchaCoder/CaptchaCoderResponse.cs | 41 ------------------- CaptchaSharp/{ => Services}/CaptchaService.cs | 2 +- .../DeathByCaptcha/Tasks/DBCTaskProxyless.cs | 6 --- .../Services/DeathByCaptchaService.cs | 6 +-- CaptchaSharp/Services/ImageTyperzService.cs | 2 +- .../Services/MetaBypassTechService.cs | 34 +++++++-------- CaptchaSharp/Services/NineKwService.cs | 2 +- CaptchaSharp/Services/NopechaService.cs | 2 +- CaptchaSharp/Services/TwoCaptchaService.cs | 2 +- 152 files changed, 198 insertions(+), 236 deletions(-) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/CaptchaTaskRequest.cs (85%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/GetTaskResultRequest.cs (65%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs (56%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs (67%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Request.cs (56%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs (55%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs (85%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs (85%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs (81%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs (89%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs (95%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs (82%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs (83%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs (77%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs (85%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs (81%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs (81%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs (88%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs (84%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs (87%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs (84%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/AntiCaptchaResponse.cs (82%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs (65%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs (78%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs (84%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs (76%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs (85%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs (89%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs (86%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs (86%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs (87%) rename CaptchaSharp/{Services => Models}/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs (65%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsRequest.cs (82%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs (80%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs (89%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs (84%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs (88%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs (80%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs (93%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs (86%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs (90%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Requests/BcsSolveRequest.cs (96%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsBalanceResponse.cs (77%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsResponse.cs (81%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs (71%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs (80%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs (72%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs (87%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs (71%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs (70%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs (72%) rename CaptchaSharp/{Services => Models}/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs (69%) rename CaptchaSharp/{Services => Models}/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs (67%) rename CaptchaSharp/{Services => Models}/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs (86%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs (91%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/CaptchaTaskRequest.cs (74%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/GetTaskResultRequest.cs (64%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Request.cs (58%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs (91%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs (56%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs (85%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs (87%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs (82%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs (90%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs (95%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs (78%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs (82%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs (83%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs (78%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs (87%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs (84%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs (85%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs (89%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs (87%) rename CaptchaSharp/{Services => Models}/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs (87%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs (65%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/GetBalanceResponse.cs (61%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/GetTaskResultResponse.cs (77%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Response.cs (84%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs (86%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Solutions/DataDomeSolution.cs (82%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Solutions/FuncaptchaSolution.cs (85%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Solutions/GeeTestSolution.cs (89%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs (86%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Solutions/RecaptchaSolution.cs (85%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/Solutions/Solution.cs (78%) rename CaptchaSharp/{Services => Models}/CapSolver/Responses/TaskCreationResponse.cs (64%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Responses/CapyDbcResponse.cs (84%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Responses/GeeTestDbcResponse.cs (84%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs (86%) create mode 100644 CaptchaSharp/Models/DeathByCaptcha/Tasks/DBCTaskProxyless.cs rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs (83%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs (85%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs (82%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs (91%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs (84%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs (83%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs (79%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/DbcTask.cs (92%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs (79%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs (83%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs (78%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs (89%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs (79%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs (86%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs (82%) rename CaptchaSharp/{Services => Models}/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs (88%) rename CaptchaSharp/{Services => Models}/ImageTyperz/ImageTyperzResponse.cs (92%) rename CaptchaSharp/{Services => Models}/ImageTyperz/ImageTyperzTaskCreatedResponse.cs (84%) rename CaptchaSharp/{Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs => Models/MetaBypassTech/MbtAccessTokenRequest.cs} (82%) rename CaptchaSharp/{Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs => Models/MetaBypassTech/MbtAccessTokenResponse.cs} (78%) rename CaptchaSharp/{Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs => Models/MetaBypassTech/MbtRefreshAccessTokenRequest.cs} (79%) rename CaptchaSharp/{Services/MetaBypassTech/MetaBypassTechResponse.cs => Models/MetaBypassTech/MbtResponse.cs} (80%) rename CaptchaSharp/{Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs => Models/MetaBypassTech/MbtSolveImageCaptchaRequest.cs} (76%) rename CaptchaSharp/{Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs => Models/MetaBypassTech/MbtSolveRecaptchaRequest.cs} (73%) rename CaptchaSharp/{Services => Models}/NineKw/NineKwBalanceResponse.cs (86%) rename CaptchaSharp/{Services => Models}/NineKw/NineKwCheckResponse.cs (92%) rename CaptchaSharp/{Services => Models}/NineKw/NineKwResponse.cs (84%) rename CaptchaSharp/{Services => Models}/NineKw/NineKwStatus.cs (84%) rename CaptchaSharp/{Services => Models}/NineKw/NineKwSubmitResponse.cs (80%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaCookie.cs (95%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaDataResponse.cs (81%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaProxy.cs (92%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaRequest.cs (76%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaResponse.cs (86%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs (92%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaSolveHCaptchaRequest.cs (89%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaSolveImageRequest.cs (85%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaSolveRecaptchaV2Request.cs (93%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaSolveRecaptchaV3Request.cs (93%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaSolveRequest.cs (79%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaSolveTokenRequest.cs (97%) rename CaptchaSharp/{Services => Models}/Nopecha/NopechaStatusResponse.cs (92%) rename CaptchaSharp/{Services => Models}/TwoCaptcha/TwoCaptchaCapyResponse.cs (92%) rename CaptchaSharp/{Services => Models}/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs (92%) rename CaptchaSharp/{Services => Models}/TwoCaptcha/TwoCaptchaGeeTestResponse.cs (94%) rename CaptchaSharp/{Services => Models}/TwoCaptcha/TwoCaptchaResponse.cs (90%) delete mode 100644 CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs rename CaptchaSharp/{ => Services}/CaptchaService.cs (99%) delete mode 100644 CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs diff --git a/CaptchaSharp.Tests/ServiceFixture.cs b/CaptchaSharp.Tests/ServiceFixture.cs index 61ca4ea..1923261 100644 --- a/CaptchaSharp.Tests/ServiceFixture.cs +++ b/CaptchaSharp.Tests/ServiceFixture.cs @@ -1,4 +1,6 @@ -namespace CaptchaSharp.Tests; +using CaptchaSharp.Services; + +namespace CaptchaSharp.Tests; public abstract class ServiceFixture { diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index 5c4084e..34b6c33 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; +using CaptchaSharp.Services; using Xunit; using Xunit.Abstractions; diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskRequest.cs similarity index 85% rename from CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskRequest.cs index af7f6fa..3b50c63 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/CaptchaTaskRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskRequest.cs @@ -1,7 +1,7 @@ -using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +using CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; using Newtonsoft.Json; -namespace CaptchaSharp.Services.AntiCaptcha.Requests; +namespace CaptchaSharp.Models.AntiCaptcha.Requests; /// /// A request to solve a captcha task. diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/GetTaskResultRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs similarity index 65% rename from CaptchaSharp/Services/AntiCaptcha/Requests/GetTaskResultRequest.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs index 912beac..d2966a1 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/GetTaskResultRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests +namespace CaptchaSharp.Models.AntiCaptcha.Requests { internal class GetTaskResultRequest : Request { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs similarity index 56% rename from CaptchaSharp/Services/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs index 58eb506..ebc5671 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests +namespace CaptchaSharp.Models.AntiCaptcha.Requests { internal class RecaptchaV2CaptchaRequest : Request { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs similarity index 67% rename from CaptchaSharp/Services/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs index 5cbf0ba..6ecbcc5 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests +namespace CaptchaSharp.Models.AntiCaptcha.Requests { internal class ReportIncorrectCaptchaRequest : Request { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Request.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs similarity index 56% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Request.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs index 96cf0d3..2bd9158 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Request.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests; +namespace CaptchaSharp.Models.AntiCaptcha.Requests; public class Request { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs similarity index 55% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs index e653b01..147fea5 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; public class AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs similarity index 85% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs index 2375079..78756fa 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks { internal class FunCaptchaTaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs similarity index 85% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs index 4818150..d5283f7 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks { internal class GeeTestTaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs similarity index 81% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs index 4b4d668..5b53e88 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks { internal class HCaptchaTaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs similarity index 89% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs index 0bc2fa8..ba8cd7f 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks { internal class ImageCaptchaTask : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs similarity index 95% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs index e7278c9..0c17a71 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied { internal class AntiCaptchaTask : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs similarity index 82% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs index 91f2060..b671bfb 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied { internal class FunCaptchaTask : AntiCaptchaTask { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs similarity index 83% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs index 7fc9258..0ce909f 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied { internal class GeeTestTask : AntiCaptchaTask { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs similarity index 77% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs index 6417334..f7ab4af 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied { internal class HCaptchaTask : AntiCaptchaTask { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs similarity index 85% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs index f46c95b..1cd9701 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied { internal class RecaptchaV2EnterpriseTask : AntiCaptchaTask { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs similarity index 81% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs index c68938b..236b5e3 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied { internal class RecaptchaV2Task : AntiCaptchaTask { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs similarity index 81% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs index b813893..325aa2d 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied; +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; internal class TurnstileTask : AntiCaptchaTask { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs similarity index 88% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs index ae9661a..46a0b12 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks { internal class RecaptchaV2EnterpriseTaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs similarity index 84% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs index d1d66eb..1e0133e 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks { internal class RecaptchaV2TaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs similarity index 87% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs index c7422f9..6ba31f7 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks { internal class RecaptchaV3TaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs similarity index 84% rename from CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs index 271f718..28f5db8 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; internal class TurnstileTaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/AntiCaptchaResponse.cs similarity index 82% rename from CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/AntiCaptchaResponse.cs index 0657829..21e6088 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/AntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/AntiCaptchaResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.AntiCaptcha.Responses; +namespace CaptchaSharp.Models.AntiCaptcha.Responses; public class AntiCaptchaResponse { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs similarity index 65% rename from CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs index eb52cf7..bd95a69 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Responses; +namespace CaptchaSharp.Models.AntiCaptcha.Responses; internal class GetBalanceAntiCaptchaResponse : AntiCaptchaResponse { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs similarity index 78% rename from CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs index 5beac6c..575159e 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs @@ -1,6 +1,6 @@ -using CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; +using CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; -namespace CaptchaSharp.Services.AntiCaptcha.Responses; +namespace CaptchaSharp.Models.AntiCaptcha.Responses; internal class GetTaskResultAntiCaptchaResponse : AntiCaptchaResponse { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs similarity index 84% rename from CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs index ecaca53..3631450 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/ReportIncorrectCaptchaAntiCaptchaResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.AntiCaptcha.Responses; +namespace CaptchaSharp.Models.AntiCaptcha.Responses; internal class ReportIncorrectCaptchaAntiCaptchaResponse { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs similarity index 76% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs index 47f62b0..807a0f6 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs @@ -1,7 +1,7 @@ using CaptchaSharp.Models; using System; -namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; internal class AntiCaptchaTaskSolution { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs similarity index 85% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs index b8e60a0..20dc489 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions { internal class FuncaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs similarity index 89% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs index 8c9f199..3bd9f28 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions { internal class GeeTestAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs similarity index 86% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs index b6124a5..8ca583b 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions { internal class ImageCaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs similarity index 86% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs index ffbaacc..375dfc5 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions { internal class RecaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs similarity index 87% rename from CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs index f1fa5c3..91ad8ae 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/TurnstileAntiCaptchaTaskSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; internal class TurnstileAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { diff --git a/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs similarity index 65% rename from CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs rename to CaptchaSharp/Models/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs index 6de43b1..2ed0e5f 100644 --- a/CaptchaSharp/Services/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.AntiCaptcha.Responses; +namespace CaptchaSharp.Models.AntiCaptcha.Responses; public class TaskCreationAntiCaptchaResponse : AntiCaptchaResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsRequest.cs similarity index 82% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsRequest.cs index a6e421c..44432ad 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs similarity index 80% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs index e42b40d..a7daa32 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveCapyRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveCapyRequest : BcsSolveRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs similarity index 89% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs index d84bf3f..8f5c5ec 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveCloudflareTurnstileRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveCloudflareTurnstileRequest : BcsSolveRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs similarity index 84% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs index cd4905b..f55c241 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveFuncaptchaRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveFuncaptchaRequest : BcsSolveRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs similarity index 88% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs index b62c58e..708b31a 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveGeeTestRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveGeeTestRequest : BcsSolveRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs similarity index 80% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs index 094694f..70f1d41 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveHCaptchaRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveHCaptchaRequest : BcsSolveRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs similarity index 93% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs index 190f293..c6e58c6 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveImageRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveImageRequest : BcsRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs similarity index 86% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs index 8064366..80a45d1 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRecaptchaV2Request.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveRecaptchaV2Request : BcsSolveRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs similarity index 90% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs index 4f0004f..984748b 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRecaptchaV3Request.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveRecaptchaV3Request : BcsSolveRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRequest.cs b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRequest.cs similarity index 96% rename from CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRequest.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRequest.cs index fb32cb6..42cc88e 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Requests/BcsSolveRequest.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Requests/BcsSolveRequest.cs @@ -3,7 +3,7 @@ using CaptchaSharp.Models; using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Requests; +namespace CaptchaSharp.Models.BestCaptchaSolver.Requests; internal class BcsSolveRequest : BcsRequest { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsBalanceResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsBalanceResponse.cs similarity index 77% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsBalanceResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsBalanceResponse.cs index 36d9b6f..898d20b 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsBalanceResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsBalanceResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsBalanceResponse : BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsResponse.cs similarity index 81% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsResponse.cs index b460004..0461915 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs similarity index 71% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs index 22b1f4e..e0e3d55 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveCapyResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsSolveCapyResponse : BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs similarity index 80% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs index cb3ed6d..14bce17 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveCloudflareTurnstileResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsSolveCloudflareTurnstileResponse : BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs similarity index 72% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs index 5212b95..2ab5fb1 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveFuncaptchaResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsSolveFuncaptchaResponse : BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs similarity index 87% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs index b520a45..906a19f 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveGeeTestResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsSolveGeeTestResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs similarity index 71% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs index 65c2e8f..65d1e59 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveHCaptchaResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsSolveHCaptchaResponse : BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs similarity index 70% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs index 86f0522..e38b669 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveImageResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsSolveImageResponse : BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs similarity index 72% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs index d5bfd66..9e8581f 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsSolveRecaptchaResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsSolveRecaptchaResponse : BcsResponse { diff --git a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs similarity index 69% rename from CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs rename to CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs index 64e6bef..ca6c4b2 100644 --- a/CaptchaSharp/Services/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs +++ b/CaptchaSharp/Models/BestCaptchaSolver/Responses/BcsTaskCreatedResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.BestCaptchaSolver.Responses; +namespace CaptchaSharp.Models.BestCaptchaSolver.Responses; internal class BcsTaskCreatedResponse : BcsResponse { diff --git a/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs similarity index 67% rename from CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs rename to CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs index 060c099..26148d5 100644 --- a/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs +++ b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs @@ -1,7 +1,7 @@ -using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +using CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapMonsterCloud.Requests.Tasks; +namespace CaptchaSharp.Models.CapMonsterCloud.Requests.Tasks; internal abstract class CustomTaskProxyless : AntiCaptchaTaskProxyless { diff --git a/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs similarity index 86% rename from CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs rename to CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs index 7c19654..f92cd76 100644 --- a/CaptchaSharp/Services/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs +++ b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs @@ -1,7 +1,7 @@ -using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; +using CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapMonsterCloud.Requests.Tasks; +namespace CaptchaSharp.Models.CapMonsterCloud.Requests.Tasks; internal class DataDomeTaskProxyless : CustomTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs b/CaptchaSharp/Models/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs similarity index 91% rename from CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs rename to CaptchaSharp/Models/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs index 687da79..b5d7533 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/CaptchaTaskFeedbackRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapSolver.Requests; +namespace CaptchaSharp.Models.CapSolver.Requests; internal class CaptchaTaskFeedbackRequest : Request { diff --git a/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskRequest.cs b/CaptchaSharp/Models/CapSolver/Requests/CaptchaTaskRequest.cs similarity index 74% rename from CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskRequest.cs rename to CaptchaSharp/Models/CapSolver/Requests/CaptchaTaskRequest.cs index 4a67084..128bafb 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/CaptchaTaskRequest.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/CaptchaTaskRequest.cs @@ -1,7 +1,7 @@ -using CaptchaSharp.Services.CapSolver.Requests.Tasks; +using CaptchaSharp.Models.CapSolver.Requests.Tasks; using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapSolver.Requests; +namespace CaptchaSharp.Models.CapSolver.Requests; internal class CaptchaTaskRequest : Request { diff --git a/CaptchaSharp/Services/CapSolver/Requests/GetTaskResultRequest.cs b/CaptchaSharp/Models/CapSolver/Requests/GetTaskResultRequest.cs similarity index 64% rename from CaptchaSharp/Services/CapSolver/Requests/GetTaskResultRequest.cs rename to CaptchaSharp/Models/CapSolver/Requests/GetTaskResultRequest.cs index 55b7246..3ad2e1e 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/GetTaskResultRequest.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/GetTaskResultRequest.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests; +namespace CaptchaSharp.Models.CapSolver.Requests; internal class GetTaskResultRequest : Request { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Request.cs b/CaptchaSharp/Models/CapSolver/Requests/Request.cs similarity index 58% rename from CaptchaSharp/Services/CapSolver/Requests/Request.cs rename to CaptchaSharp/Models/CapSolver/Requests/Request.cs index 7bc808b..85f1cc3 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Request.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Request.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests; +namespace CaptchaSharp.Models.CapSolver.Requests; internal class Request { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs similarity index 91% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs index 7bc1360..ed342a5 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/AntiTurnstileTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks; +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; internal class AntiTurnstileTaskProxyless : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs similarity index 56% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs index a3f2f29..0e139df 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks; +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; internal class CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs similarity index 85% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs index 7424f6d..6af156a 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks { internal class FunCaptchaTaskProxyless : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs similarity index 87% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs index 73b69cc..0274fe3 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks { internal class GeeTestTaskProxyless : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs similarity index 82% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs index a7c38cf..9f3ea37 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks { internal class HCaptchaTaskProxyless : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs similarity index 90% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs index eecaa2e..0259983 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks { internal class ImageCaptchaTask : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs similarity index 95% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs index 8f45020..0aec678 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/CapSolverTask.cs @@ -2,7 +2,7 @@ using System.Linq; using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied; +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; internal class CapSolverTask : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs similarity index 78% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs index 24fa5d2..b919533 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied { internal class DataDomeTask : CapSolverTask { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs similarity index 82% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs index 3e49026..e74987f 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied { internal class FunCaptchaTask : CapSolverTask { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs similarity index 83% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs index 154bc8c..2b0751a 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied { internal class GeeTestTask : CapSolverTask { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs similarity index 78% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs index 358ee0e..3094fad 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied { internal class HCaptchaTask : CapSolverTask { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs similarity index 87% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs index 8c9e1c2..93a595c 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied; +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; internal class RecaptchaV2EnterpriseTask : CapSolverTask { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs similarity index 84% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs index 2781c04..bd9db79 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied { internal class RecaptchaV2Task : CapSolverTask { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs similarity index 85% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs index 98a03de..f923209 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied { internal class RecaptchaV3Task : CapSolverTask { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs similarity index 89% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs index 164cc58..200fad0 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks; +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; internal class RecaptchaV2EnterpriseTaskProxyless : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs similarity index 87% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs index 1831454..7bf2228 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks { internal class RecaptchaV2TaskProxyless : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs similarity index 87% rename from CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs rename to CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs index 27bea90..cb0b11a 100644 --- a/CaptchaSharp/Services/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks { internal class RecaptchaV3TaskProxyless : CapSolverTaskProxyless { diff --git a/CaptchaSharp/Services/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs b/CaptchaSharp/Models/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs similarity index 65% rename from CaptchaSharp/Services/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs rename to CaptchaSharp/Models/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs index 9155a48..37e5f48 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/CaptchaTaskFeedbackResponse.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Responses; +namespace CaptchaSharp.Models.CapSolver.Responses; internal class CaptchaTaskFeedbackResponse : Response { diff --git a/CaptchaSharp/Services/CapSolver/Responses/GetBalanceResponse.cs b/CaptchaSharp/Models/CapSolver/Responses/GetBalanceResponse.cs similarity index 61% rename from CaptchaSharp/Services/CapSolver/Responses/GetBalanceResponse.cs rename to CaptchaSharp/Models/CapSolver/Responses/GetBalanceResponse.cs index d8d97db..4cdcd1a 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/GetBalanceResponse.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/GetBalanceResponse.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Responses; +namespace CaptchaSharp.Models.CapSolver.Responses; internal class GetBalanceResponse : Response { diff --git a/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs b/CaptchaSharp/Models/CapSolver/Responses/GetTaskResultResponse.cs similarity index 77% rename from CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs rename to CaptchaSharp/Models/CapSolver/Responses/GetTaskResultResponse.cs index 181c249..aa22149 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/GetTaskResultResponse.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/GetTaskResultResponse.cs @@ -1,6 +1,6 @@ -using CaptchaSharp.Services.CapSolver.Responses.Solutions; +using CaptchaSharp.Models.CapSolver.Responses.Solutions; -namespace CaptchaSharp.Services.CapSolver.Responses; +namespace CaptchaSharp.Models.CapSolver.Responses; internal class GetTaskResultResponse : Response { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Response.cs b/CaptchaSharp/Models/CapSolver/Responses/Response.cs similarity index 84% rename from CaptchaSharp/Services/CapSolver/Responses/Response.cs rename to CaptchaSharp/Models/CapSolver/Responses/Response.cs index 5b9e577..1c56e76 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Response.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Response.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.CapSolver.Responses +namespace CaptchaSharp.Models.CapSolver.Responses { internal class Response { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs similarity index 86% rename from CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs rename to CaptchaSharp/Models/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs index 9a8eba8..056b302 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/CloudflareTurnstileSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions; +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; internal class CloudflareTurnstileSolution : Solution { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/DataDomeSolution.cs similarity index 82% rename from CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs rename to CaptchaSharp/Models/CapSolver/Responses/Solutions/DataDomeSolution.cs index c08b79e..69f0198 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/DataDomeSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/DataDomeSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions; +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; internal class DataDomeSolution : Solution { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/FuncaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs similarity index 85% rename from CaptchaSharp/Services/CapSolver/Responses/Solutions/FuncaptchaSolution.cs rename to CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs index 44e4bdd..708c67f 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/FuncaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions { internal class FuncaptchaSolution : Solution { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/GeeTestSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs similarity index 89% rename from CaptchaSharp/Services/CapSolver/Responses/Solutions/GeeTestSolution.cs rename to CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs index 8b8cccc..af57670 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/GeeTestSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions { internal class GeeTestSolution : Solution { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs similarity index 86% rename from CaptchaSharp/Services/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs rename to CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs index c08ef5c..db2f4cc 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions { internal class ImageCaptchaSolution : Solution { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/RecaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs similarity index 85% rename from CaptchaSharp/Services/CapSolver/Responses/Solutions/RecaptchaSolution.cs rename to CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs index 09c0ee1..522d7bc 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/RecaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions { internal class RecaptchaSolution : Solution { diff --git a/CaptchaSharp/Services/CapSolver/Responses/Solutions/Solution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/Solution.cs similarity index 78% rename from CaptchaSharp/Services/CapSolver/Responses/Solutions/Solution.cs rename to CaptchaSharp/Models/CapSolver/Responses/Solutions/Solution.cs index e848907..7e34be5 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/Solutions/Solution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/Solution.cs @@ -1,7 +1,7 @@ using CaptchaSharp.Models; using System; -namespace CaptchaSharp.Services.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions { internal class Solution { diff --git a/CaptchaSharp/Services/CapSolver/Responses/TaskCreationResponse.cs b/CaptchaSharp/Models/CapSolver/Responses/TaskCreationResponse.cs similarity index 64% rename from CaptchaSharp/Services/CapSolver/Responses/TaskCreationResponse.cs rename to CaptchaSharp/Models/CapSolver/Responses/TaskCreationResponse.cs index 1c757cd..8e1b7b7 100644 --- a/CaptchaSharp/Services/CapSolver/Responses/TaskCreationResponse.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/TaskCreationResponse.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.CapSolver.Responses; +namespace CaptchaSharp.Models.CapSolver.Responses; internal class TaskCreationResponse : Response { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Responses/CapyDbcResponse.cs b/CaptchaSharp/Models/DeathByCaptcha/Responses/CapyDbcResponse.cs similarity index 84% rename from CaptchaSharp/Services/DeathByCaptcha/Responses/CapyDbcResponse.cs rename to CaptchaSharp/Models/DeathByCaptcha/Responses/CapyDbcResponse.cs index c56e4d5..4837615 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Responses/CapyDbcResponse.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Responses/CapyDbcResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Responses; +namespace CaptchaSharp.Models.DeathByCaptcha.Responses; internal class CapyDbcResponse { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Responses/GeeTestDbcResponse.cs b/CaptchaSharp/Models/DeathByCaptcha/Responses/GeeTestDbcResponse.cs similarity index 84% rename from CaptchaSharp/Services/DeathByCaptcha/Responses/GeeTestDbcResponse.cs rename to CaptchaSharp/Models/DeathByCaptcha/Responses/GeeTestDbcResponse.cs index 8703aa3..0b61503 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Responses/GeeTestDbcResponse.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Responses/GeeTestDbcResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Responses; +namespace CaptchaSharp.Models.DeathByCaptcha.Responses; internal class GeeTestDbcResponse { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs similarity index 86% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs index 4aa6e65..7d3fac2 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/CapyDbcTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; internal class CapyDbcTaskProxyless : DbcTaskProxyless { diff --git a/CaptchaSharp/Models/DeathByCaptcha/Tasks/DBCTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/DBCTaskProxyless.cs new file mode 100644 index 0000000..0ea9a96 --- /dev/null +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/DBCTaskProxyless.cs @@ -0,0 +1,6 @@ +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; + +internal class DbcTaskProxyless +{ + +} diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs similarity index 83% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs index 855e3aa..5596c2a 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/FunCaptchaDbcTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; internal class FunCaptchaDbcTaskProxyless : DbcTaskProxyless { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs similarity index 85% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs index 533bad1..8fa73a7 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/GeeTestDbcTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; internal class GeeTestDbcTaskProxyless : DbcTaskProxyless { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs similarity index 82% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs index efd6eaa..ebde342 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/HCaptchaDbcTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; internal class HCaptchaDbcTaskProxyless : DbcTaskProxyless { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs similarity index 91% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs index eb37f12..a7ae615 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/KeyCaptchaDbcTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; internal class KeyCaptchaDbcTaskProxyless : DbcTaskProxyless { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs similarity index 84% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs index 43ad95f..650e308 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/CapyDbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class CapyDbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs similarity index 83% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs index ff93a5f..276f6b5 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/CloudflareTurnstileDbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class CloudflareTurnstileDbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs similarity index 79% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs index 7a1b2f8..b25e153 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/DataDomeDbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class DataDomeDbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/DbcTask.cs similarity index 92% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/DbcTask.cs index 48799d5..a4c91fb 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/DbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/DbcTask.cs @@ -2,7 +2,7 @@ using System; using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class DbcTask : DbcTaskProxyless { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs similarity index 79% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs index 9e7c4b4..36808e2 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/FunCaptchaDbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class FunCaptchaDbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs similarity index 83% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs index 1ed893d..b1e16e6 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/GeeTestDbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class GeeTestDbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs similarity index 78% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs index f6cb250..486b6b5 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/HCaptchaDbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class HCaptchaDbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs similarity index 89% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs index 6a5f2e6..9c42a6e 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/KeyCaptchaDbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class KeyCaptchaDbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs similarity index 79% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs index 1eea419..a33bb0e 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/RecaptchaV2DbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class RecaptchaV2DbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs similarity index 86% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs index 90c4edf..a19eb54 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/RecaptchaV3DbcTask.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; internal class RecaptchaV3DbcTask : DbcTask { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs similarity index 82% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs index fc5b6ab..b46abb3 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/RecaptchaV2DbcTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; internal class RecaptchaV2DbcTaskProxyless : DbcTaskProxyless { diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs similarity index 88% rename from CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs rename to CaptchaSharp/Models/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs index ecaaa2c..2eaaf44 100644 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/RecaptchaV3DbcTaskProxyless.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; internal class RecaptchaV3DbcTaskProxyless : DbcTaskProxyless { diff --git a/CaptchaSharp/Services/ImageTyperz/ImageTyperzResponse.cs b/CaptchaSharp/Models/ImageTyperz/ImageTyperzResponse.cs similarity index 92% rename from CaptchaSharp/Services/ImageTyperz/ImageTyperzResponse.cs rename to CaptchaSharp/Models/ImageTyperz/ImageTyperzResponse.cs index 32545ec..c6f99ef 100644 --- a/CaptchaSharp/Services/ImageTyperz/ImageTyperzResponse.cs +++ b/CaptchaSharp/Models/ImageTyperz/ImageTyperzResponse.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.ImageTyperz; +namespace CaptchaSharp.Models.ImageTyperz; /// /// The response for a captcha task. diff --git a/CaptchaSharp/Services/ImageTyperz/ImageTyperzTaskCreatedResponse.cs b/CaptchaSharp/Models/ImageTyperz/ImageTyperzTaskCreatedResponse.cs similarity index 84% rename from CaptchaSharp/Services/ImageTyperz/ImageTyperzTaskCreatedResponse.cs rename to CaptchaSharp/Models/ImageTyperz/ImageTyperzTaskCreatedResponse.cs index a483e44..9c2a08e 100644 --- a/CaptchaSharp/Services/ImageTyperz/ImageTyperzTaskCreatedResponse.cs +++ b/CaptchaSharp/Models/ImageTyperz/ImageTyperzTaskCreatedResponse.cs @@ -1,4 +1,4 @@ -namespace CaptchaSharp.Services.ImageTyperz; +namespace CaptchaSharp.Models.ImageTyperz; /// /// The response for a captcha task after it's created. diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs b/CaptchaSharp/Models/MetaBypassTech/MbtAccessTokenRequest.cs similarity index 82% rename from CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs rename to CaptchaSharp/Models/MetaBypassTech/MbtAccessTokenRequest.cs index 0130812..1630a2c 100644 --- a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenRequest.cs +++ b/CaptchaSharp/Models/MetaBypassTech/MbtAccessTokenRequest.cs @@ -1,8 +1,8 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.MetaBypassTech; +namespace CaptchaSharp.Models.MetaBypassTech; -internal class MetaBypassTechAccessTokenRequest +internal class MbtAccessTokenRequest { [JsonProperty("grant_type")] public required string GrantType { get; set; } diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs b/CaptchaSharp/Models/MetaBypassTech/MbtAccessTokenResponse.cs similarity index 78% rename from CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs rename to CaptchaSharp/Models/MetaBypassTech/MbtAccessTokenResponse.cs index eb22ed1..0ada055 100644 --- a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechAccessTokenResponse.cs +++ b/CaptchaSharp/Models/MetaBypassTech/MbtAccessTokenResponse.cs @@ -1,9 +1,9 @@ using System; using Newtonsoft.Json; -namespace CaptchaSharp.Services.MetaBypassTech; +namespace CaptchaSharp.Models.MetaBypassTech; -internal class MetaBypassTechAccessTokenResponse : MetaBypassTechResponse +internal class MbtAccessTokenResponse : MbtResponse { [JsonProperty("token_type")] public required string TokenType { get; set; } @@ -19,7 +19,7 @@ internal class MetaBypassTechAccessTokenResponse : MetaBypassTechResponse public DateTime ExpirationDate { get; set; } - public MetaBypassTechAccessTokenResponse() + public MbtAccessTokenResponse() { // Subtract 1 minute to avoid expiration issues ExpirationDate = DateTime.Now.AddSeconds(ExpiresInSeconds) - TimeSpan.FromMinutes(1); diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs b/CaptchaSharp/Models/MetaBypassTech/MbtRefreshAccessTokenRequest.cs similarity index 79% rename from CaptchaSharp/Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs rename to CaptchaSharp/Models/MetaBypassTech/MbtRefreshAccessTokenRequest.cs index b08f00a..4ab571e 100644 --- a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechRefreshAccessTokenRequest.cs +++ b/CaptchaSharp/Models/MetaBypassTech/MbtRefreshAccessTokenRequest.cs @@ -1,8 +1,8 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.MetaBypassTech; +namespace CaptchaSharp.Models.MetaBypassTech; -internal class MetaBypassTechRefreshAccessTokenRequest +internal class MbtRefreshAccessTokenRequest { [JsonProperty("grant_type")] public required string GrantType { get; set; } diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechResponse.cs b/CaptchaSharp/Models/MetaBypassTech/MbtResponse.cs similarity index 80% rename from CaptchaSharp/Services/MetaBypassTech/MetaBypassTechResponse.cs rename to CaptchaSharp/Models/MetaBypassTech/MbtResponse.cs index b013928..489b946 100644 --- a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechResponse.cs +++ b/CaptchaSharp/Models/MetaBypassTech/MbtResponse.cs @@ -1,9 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace CaptchaSharp.Services.MetaBypassTech; +namespace CaptchaSharp.Models.MetaBypassTech; -internal class MetaBypassTechResponse +internal class MbtResponse { [JsonProperty("ok")] public required bool Ok { get; set; } diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs b/CaptchaSharp/Models/MetaBypassTech/MbtSolveImageCaptchaRequest.cs similarity index 76% rename from CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs rename to CaptchaSharp/Models/MetaBypassTech/MbtSolveImageCaptchaRequest.cs index f86b7da..54daf9f 100644 --- a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveImageCaptchaRequest.cs +++ b/CaptchaSharp/Models/MetaBypassTech/MbtSolveImageCaptchaRequest.cs @@ -1,8 +1,8 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.MetaBypassTech; +namespace CaptchaSharp.Models.MetaBypassTech; -internal class MetaBypassTechSolveImageCaptchaRequest +internal class MbtSolveImageCaptchaRequest { [JsonProperty("image")] public required string Base64Image { get; set; } diff --git a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs b/CaptchaSharp/Models/MetaBypassTech/MbtSolveRecaptchaRequest.cs similarity index 73% rename from CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs rename to CaptchaSharp/Models/MetaBypassTech/MbtSolveRecaptchaRequest.cs index 93467fa..1844cfd 100644 --- a/CaptchaSharp/Services/MetaBypassTech/MetaBypassTechSolveRecaptchaRequest.cs +++ b/CaptchaSharp/Models/MetaBypassTech/MbtSolveRecaptchaRequest.cs @@ -1,8 +1,8 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.MetaBypassTech; +namespace CaptchaSharp.Models.MetaBypassTech; -internal class MetaBypassTechSolveRecaptchaRequest +internal class MbtSolveRecaptchaRequest { [JsonProperty("sitekey")] public required string SiteKey { get; set; } diff --git a/CaptchaSharp/Services/NineKw/NineKwBalanceResponse.cs b/CaptchaSharp/Models/NineKw/NineKwBalanceResponse.cs similarity index 86% rename from CaptchaSharp/Services/NineKw/NineKwBalanceResponse.cs rename to CaptchaSharp/Models/NineKw/NineKwBalanceResponse.cs index ce6715f..692b2d5 100644 --- a/CaptchaSharp/Services/NineKw/NineKwBalanceResponse.cs +++ b/CaptchaSharp/Models/NineKw/NineKwBalanceResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.NineKw; +namespace CaptchaSharp.Models.NineKw; internal class NineKwBalanceResponse : NineKwResponse { diff --git a/CaptchaSharp/Services/NineKw/NineKwCheckResponse.cs b/CaptchaSharp/Models/NineKw/NineKwCheckResponse.cs similarity index 92% rename from CaptchaSharp/Services/NineKw/NineKwCheckResponse.cs rename to CaptchaSharp/Models/NineKw/NineKwCheckResponse.cs index c29aa07..b7e1fb8 100644 --- a/CaptchaSharp/Services/NineKw/NineKwCheckResponse.cs +++ b/CaptchaSharp/Models/NineKw/NineKwCheckResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.NineKw; +namespace CaptchaSharp.Models.NineKw; internal class NineKwCheckResponse : NineKwResponse { diff --git a/CaptchaSharp/Services/NineKw/NineKwResponse.cs b/CaptchaSharp/Models/NineKw/NineKwResponse.cs similarity index 84% rename from CaptchaSharp/Services/NineKw/NineKwResponse.cs rename to CaptchaSharp/Models/NineKw/NineKwResponse.cs index 6174d93..993576a 100644 --- a/CaptchaSharp/Services/NineKw/NineKwResponse.cs +++ b/CaptchaSharp/Models/NineKw/NineKwResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.NineKw; +namespace CaptchaSharp.Models.NineKw; internal class NineKwResponse { diff --git a/CaptchaSharp/Services/NineKw/NineKwStatus.cs b/CaptchaSharp/Models/NineKw/NineKwStatus.cs similarity index 84% rename from CaptchaSharp/Services/NineKw/NineKwStatus.cs rename to CaptchaSharp/Models/NineKw/NineKwStatus.cs index 22ff034..29859f0 100644 --- a/CaptchaSharp/Services/NineKw/NineKwStatus.cs +++ b/CaptchaSharp/Models/NineKw/NineKwStatus.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.NineKw; +namespace CaptchaSharp.Models.NineKw; internal class NineKwStatus { diff --git a/CaptchaSharp/Services/NineKw/NineKwSubmitResponse.cs b/CaptchaSharp/Models/NineKw/NineKwSubmitResponse.cs similarity index 80% rename from CaptchaSharp/Services/NineKw/NineKwSubmitResponse.cs rename to CaptchaSharp/Models/NineKw/NineKwSubmitResponse.cs index d19ff2f..dc5bbab 100644 --- a/CaptchaSharp/Services/NineKw/NineKwSubmitResponse.cs +++ b/CaptchaSharp/Models/NineKw/NineKwSubmitResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.NineKw; +namespace CaptchaSharp.Models.NineKw; internal class NineKwSubmitResponse : NineKwResponse { diff --git a/CaptchaSharp/Services/Nopecha/NopechaCookie.cs b/CaptchaSharp/Models/Nopecha/NopechaCookie.cs similarity index 95% rename from CaptchaSharp/Services/Nopecha/NopechaCookie.cs rename to CaptchaSharp/Models/Nopecha/NopechaCookie.cs index 976b22f..bdae078 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaCookie.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaCookie.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaCookie { diff --git a/CaptchaSharp/Services/Nopecha/NopechaDataResponse.cs b/CaptchaSharp/Models/Nopecha/NopechaDataResponse.cs similarity index 81% rename from CaptchaSharp/Services/Nopecha/NopechaDataResponse.cs rename to CaptchaSharp/Models/Nopecha/NopechaDataResponse.cs index 0b39350..f226111 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaDataResponse.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaDataResponse.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaDataResponse : NopechaResponse { diff --git a/CaptchaSharp/Services/Nopecha/NopechaProxy.cs b/CaptchaSharp/Models/Nopecha/NopechaProxy.cs similarity index 92% rename from CaptchaSharp/Services/Nopecha/NopechaProxy.cs rename to CaptchaSharp/Models/Nopecha/NopechaProxy.cs index 0ca9bde..56a1fdb 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaProxy.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaProxy.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaProxy { diff --git a/CaptchaSharp/Services/Nopecha/NopechaRequest.cs b/CaptchaSharp/Models/Nopecha/NopechaRequest.cs similarity index 76% rename from CaptchaSharp/Services/Nopecha/NopechaRequest.cs rename to CaptchaSharp/Models/Nopecha/NopechaRequest.cs index ee08025..8dd6ddc 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaRequest.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaResponse.cs b/CaptchaSharp/Models/Nopecha/NopechaResponse.cs similarity index 86% rename from CaptchaSharp/Services/Nopecha/NopechaResponse.cs rename to CaptchaSharp/Models/Nopecha/NopechaResponse.cs index 8608576..620f763 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaResponse.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaResponse { diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs b/CaptchaSharp/Models/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs similarity index 92% rename from CaptchaSharp/Services/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs rename to CaptchaSharp/Models/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs index 5f6e970..62db222 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaSolveCloudflareTurnstileRequest.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaSolveCloudflareTurnstileRequest : NopechaSolveTokenRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveHCaptchaRequest.cs b/CaptchaSharp/Models/Nopecha/NopechaSolveHCaptchaRequest.cs similarity index 89% rename from CaptchaSharp/Services/Nopecha/NopechaSolveHCaptchaRequest.cs rename to CaptchaSharp/Models/Nopecha/NopechaSolveHCaptchaRequest.cs index 62795c2..36c7ea7 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveHCaptchaRequest.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaSolveHCaptchaRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaSolveHCaptchaRequest : NopechaSolveTokenRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveImageRequest.cs b/CaptchaSharp/Models/Nopecha/NopechaSolveImageRequest.cs similarity index 85% rename from CaptchaSharp/Services/Nopecha/NopechaSolveImageRequest.cs rename to CaptchaSharp/Models/Nopecha/NopechaSolveImageRequest.cs index b512242..eca2ac8 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveImageRequest.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaSolveImageRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaSolveImageRequest : NopechaRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV2Request.cs b/CaptchaSharp/Models/Nopecha/NopechaSolveRecaptchaV2Request.cs similarity index 93% rename from CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV2Request.cs rename to CaptchaSharp/Models/Nopecha/NopechaSolveRecaptchaV2Request.cs index 4f982db..dd339a3 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV2Request.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaSolveRecaptchaV2Request.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaSolveRecaptchaV2Request : NopechaSolveTokenRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV3Request.cs b/CaptchaSharp/Models/Nopecha/NopechaSolveRecaptchaV3Request.cs similarity index 93% rename from CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV3Request.cs rename to CaptchaSharp/Models/Nopecha/NopechaSolveRecaptchaV3Request.cs index 428d9ee..b9f891b 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveRecaptchaV3Request.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaSolveRecaptchaV3Request.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaSolveRecaptchaV3Request : NopechaSolveTokenRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveRequest.cs b/CaptchaSharp/Models/Nopecha/NopechaSolveRequest.cs similarity index 79% rename from CaptchaSharp/Services/Nopecha/NopechaSolveRequest.cs rename to CaptchaSharp/Models/Nopecha/NopechaSolveRequest.cs index bd36728..e466d61 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveRequest.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaSolveRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaSolveRequest : NopechaRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs b/CaptchaSharp/Models/Nopecha/NopechaSolveTokenRequest.cs similarity index 97% rename from CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs rename to CaptchaSharp/Models/Nopecha/NopechaSolveTokenRequest.cs index 5c69cea..6909e2a 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaSolveTokenRequest.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaSolveTokenRequest.cs @@ -3,7 +3,7 @@ using CaptchaSharp.Models; using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaSolveTokenRequest : NopechaSolveRequest { diff --git a/CaptchaSharp/Services/Nopecha/NopechaStatusResponse.cs b/CaptchaSharp/Models/Nopecha/NopechaStatusResponse.cs similarity index 92% rename from CaptchaSharp/Services/Nopecha/NopechaStatusResponse.cs rename to CaptchaSharp/Models/Nopecha/NopechaStatusResponse.cs index 76eb3d4..2ede17f 100644 --- a/CaptchaSharp/Services/Nopecha/NopechaStatusResponse.cs +++ b/CaptchaSharp/Models/Nopecha/NopechaStatusResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.Nopecha; +namespace CaptchaSharp.Models.Nopecha; internal class NopechaStatusResponse : NopechaResponse { diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs similarity index 92% rename from CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs rename to CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs index 65b45e3..4165b50 100644 --- a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCapyResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs @@ -1,6 +1,6 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Services.TwoCaptcha; +namespace CaptchaSharp.Models.TwoCaptcha; internal class TwoCaptchaCapyResponse : TwoCaptchaResponse { diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs similarity index 92% rename from CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs rename to CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs index b63111b..7d960f2 100644 --- a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs @@ -1,7 +1,7 @@ using CaptchaSharp.Models; using Newtonsoft.Json; -namespace CaptchaSharp.Services.TwoCaptcha; +namespace CaptchaSharp.Models.TwoCaptcha; internal class TwoCaptchaCloudflareTurnstileResponse : TwoCaptchaResponse { diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs similarity index 94% rename from CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs rename to CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs index 2d4a247..9040201 100644 --- a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaGeeTestResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs @@ -1,7 +1,7 @@ using CaptchaSharp.Models; using Newtonsoft.Json; -namespace CaptchaSharp.Services.TwoCaptcha; +namespace CaptchaSharp.Models.TwoCaptcha; internal class TwoCaptchaGeeTestResponse : TwoCaptchaResponse { diff --git a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaResponse.cs similarity index 90% rename from CaptchaSharp/Services/TwoCaptcha/TwoCaptchaResponse.cs rename to CaptchaSharp/Models/TwoCaptcha/TwoCaptchaResponse.cs index 8c65266..2a4081d 100644 --- a/CaptchaSharp/Services/TwoCaptcha/TwoCaptchaResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaResponse.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Services.TwoCaptcha; +namespace CaptchaSharp.Models.TwoCaptcha; internal class TwoCaptchaResponse { diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index 167cfbd..0a4de08 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -1,11 +1,11 @@ using CaptchaSharp.Enums; using CaptchaSharp.Exceptions; using CaptchaSharp.Models; -using CaptchaSharp.Services.AntiCaptcha.Requests; -using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks; -using CaptchaSharp.Services.AntiCaptcha.Requests.Tasks.Proxied; -using CaptchaSharp.Services.AntiCaptcha.Responses; -using CaptchaSharp.Services.AntiCaptcha.Responses.Solutions; +using CaptchaSharp.Models.AntiCaptcha.Requests; +using CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; +using CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; +using CaptchaSharp.Models.AntiCaptcha.Responses; +using CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; diff --git a/CaptchaSharp/Services/BestCaptchaSolverService.cs b/CaptchaSharp/Services/BestCaptchaSolverService.cs index 7262ad7..57cd1de 100644 --- a/CaptchaSharp/Services/BestCaptchaSolverService.cs +++ b/CaptchaSharp/Services/BestCaptchaSolverService.cs @@ -6,8 +6,8 @@ using CaptchaSharp.Exceptions; using CaptchaSharp.Extensions; using CaptchaSharp.Models; -using CaptchaSharp.Services.BestCaptchaSolver.Requests; -using CaptchaSharp.Services.BestCaptchaSolver.Responses; +using CaptchaSharp.Models.BestCaptchaSolver.Requests; +using CaptchaSharp.Models.BestCaptchaSolver.Responses; namespace CaptchaSharp.Services; diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index f7d52c8..c31b407 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -5,8 +5,8 @@ using CaptchaSharp.Enums; using CaptchaSharp.Extensions; using CaptchaSharp.Models; -using CaptchaSharp.Services.AntiCaptcha.Responses; -using CaptchaSharp.Services.CapMonsterCloud.Requests.Tasks; +using CaptchaSharp.Models.AntiCaptcha.Responses; +using CaptchaSharp.Models.CapMonsterCloud.Requests.Tasks; using Newtonsoft.Json.Linq; namespace CaptchaSharp.Services; diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index 71782a9..8d84818 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -1,11 +1,11 @@ using CaptchaSharp.Enums; using CaptchaSharp.Exceptions; using CaptchaSharp.Models; -using CaptchaSharp.Services.CapSolver.Requests; -using CaptchaSharp.Services.CapSolver.Requests.Tasks; -using CaptchaSharp.Services.CapSolver.Requests.Tasks.Proxied; -using CaptchaSharp.Services.CapSolver.Responses; -using CaptchaSharp.Services.CapSolver.Responses.Solutions; +using CaptchaSharp.Models.CapSolver.Requests; +using CaptchaSharp.Models.CapSolver.Requests.Tasks; +using CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; +using CaptchaSharp.Models.CapSolver.Responses; +using CaptchaSharp.Models.CapSolver.Responses.Solutions; using Newtonsoft.Json.Linq; using System; using System.Net.Http; diff --git a/CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs b/CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs deleted file mode 100644 index d47e524..0000000 --- a/CaptchaSharp/Services/CaptchaCoder/CaptchaCoderResponse.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace CaptchaSharp.Services.CaptchaCoder; - -internal class CaptchaCoderResponse -{ - public int ResultCode { get; set; } - public int MajorID { get; set; } - public int MinorID { get; set; } - public int Type { get; set; } - public int Timeout { get; set; } - public string Text { get; set; } - - public static CaptchaCoderResponse Parse(string str) - { - // ResultCode|MajorID|MinorID|Type|Timeout|Text - // 0|107|44685|0|0|n7hjks - var split = str.Split(['|'], 6); - return new CaptchaCoderResponse - { - ResultCode = int.Parse(split[0]), - MajorID = int.Parse(split[1]), - MinorID = int.Parse(split[2]), - Type = int.Parse(split[3]), - Timeout = int.Parse(split[4]), - Text = split[5] - }; - } - - public static bool TryParse(string str, out CaptchaCoderResponse? response) - { - try - { - response = Parse(str); - return true; - } - catch - { - response = null; - return false; - } - } -} diff --git a/CaptchaSharp/CaptchaService.cs b/CaptchaSharp/Services/CaptchaService.cs similarity index 99% rename from CaptchaSharp/CaptchaService.cs rename to CaptchaSharp/Services/CaptchaService.cs index 8bea582..af53d08 100644 --- a/CaptchaSharp/CaptchaService.cs +++ b/CaptchaSharp/Services/CaptchaService.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; -namespace CaptchaSharp; +namespace CaptchaSharp.Services; /// Abstract class for a generic captcha solving service. public abstract class CaptchaService diff --git a/CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs b/CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs deleted file mode 100644 index d7f6347..0000000 --- a/CaptchaSharp/Services/DeathByCaptcha/Tasks/DBCTaskProxyless.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace CaptchaSharp.Services.DeathByCaptcha.Tasks; - -internal class DbcTaskProxyless -{ - -} diff --git a/CaptchaSharp/Services/DeathByCaptchaService.cs b/CaptchaSharp/Services/DeathByCaptchaService.cs index d4aced6..d0d9176 100644 --- a/CaptchaSharp/Services/DeathByCaptchaService.cs +++ b/CaptchaSharp/Services/DeathByCaptchaService.cs @@ -1,8 +1,8 @@ using CaptchaSharp.Enums; using CaptchaSharp.Exceptions; using CaptchaSharp.Models; -using CaptchaSharp.Services.DeathByCaptcha.Tasks; -using CaptchaSharp.Services.DeathByCaptcha.Tasks.Proxied; +using CaptchaSharp.Models.DeathByCaptcha.Tasks; +using CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; using System; using System.Collections.Specialized; using System.Globalization; @@ -13,7 +13,7 @@ using System.Threading.Tasks; using System.Web; using CaptchaSharp.Extensions; -using CaptchaSharp.Services.DeathByCaptcha.Responses; +using CaptchaSharp.Models.DeathByCaptcha.Responses; namespace CaptchaSharp.Services; diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index a182644..db3a69a 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -8,7 +8,7 @@ using System.Threading; using System.Threading.Tasks; using CaptchaSharp.Extensions; -using CaptchaSharp.Services.ImageTyperz; +using CaptchaSharp.Models.ImageTyperz; using Newtonsoft.Json.Linq; namespace CaptchaSharp.Services; diff --git a/CaptchaSharp/Services/MetaBypassTechService.cs b/CaptchaSharp/Services/MetaBypassTechService.cs index a1397e2..8e8db5a 100644 --- a/CaptchaSharp/Services/MetaBypassTechService.cs +++ b/CaptchaSharp/Services/MetaBypassTechService.cs @@ -6,7 +6,7 @@ using CaptchaSharp.Exceptions; using CaptchaSharp.Extensions; using CaptchaSharp.Models; -using CaptchaSharp.Services.MetaBypassTech; +using CaptchaSharp.Models.MetaBypassTech; namespace CaptchaSharp.Services; @@ -43,7 +43,7 @@ public class MetaBypassTechService : CaptchaService /// /// The current access token. /// - private MetaBypassTechAccessTokenResponse? _accessToken; + private MbtAccessTokenResponse? _accessToken; /// /// Initializes a . @@ -81,7 +81,7 @@ public override async Task GetBalanceAsync( cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); + var response = json.Deserialize(); if (!response.Ok) { @@ -115,7 +115,7 @@ public override async Task SolveImageCaptchaAsync( }; } - var payload = new MetaBypassTechSolveImageCaptchaRequest + var payload = new MbtSolveImageCaptchaRequest { Base64Image = base64, Numeric = numeric, @@ -129,7 +129,7 @@ public override async Task SolveImageCaptchaAsync( cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); + var response = json.Deserialize(); if (!response.Ok) { @@ -159,7 +159,7 @@ public override async Task SolveRecaptchaV2Async( // When using version "invisible", we get "Service Failed" as a response // so we're just going to ignore it and use version "2" instead - var payload = new MetaBypassTechSolveRecaptchaRequest + var payload = new MbtSolveRecaptchaRequest { SiteKey = siteKey, Url = siteUrl, @@ -172,7 +172,7 @@ public override async Task SolveRecaptchaV2Async( cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); + var response = json.Deserialize(); if (!response.Ok) { @@ -200,7 +200,7 @@ public override async Task SolveRecaptchaV3Async( await EnsureAccessTokenAsync().ConfigureAwait(false); - var payload = new MetaBypassTechSolveRecaptchaRequest + var payload = new MbtSolveRecaptchaRequest { SiteKey = siteKey, Url = siteUrl, @@ -213,7 +213,7 @@ public override async Task SolveRecaptchaV3Async( cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); + var response = json.Deserialize(); if (!response.Ok) { @@ -247,7 +247,7 @@ public override async Task SolveRecaptchaV3Async( .Add("recaptcha_id", task.Id), cancellationToken).ConfigureAwait(false); - var response = json.Deserialize(); + var response = json.Deserialize(); if (!response.Ok) { @@ -285,7 +285,7 @@ private async ValueTask EnsureAccessTokenAsync() private async Task GetAccessTokenAsync() { - var payload = new MetaBypassTechAccessTokenRequest + var payload = new MbtAccessTokenRequest { GrantType = "password", ClientId = ClientId, @@ -304,20 +304,20 @@ private async Task GetAccessTokenAsync() if (!response.IsSuccessStatusCode) { - var serviceResponse = json.Deserialize(); + var serviceResponse = json.Deserialize(); throw new BadAuthenticationException( serviceResponse.Message ?? "Unknown error"); } - _accessToken = json.Deserialize(); + _accessToken = json.Deserialize(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"{_accessToken.TokenType} {_accessToken.AccessToken}"); } - private async Task RefreshAccessTokenAsync(MetaBypassTechAccessTokenResponse tokenResponse) + private async Task RefreshAccessTokenAsync(MbtAccessTokenResponse tokenResponse) { - var payload = new MetaBypassTechRefreshAccessTokenRequest + var payload = new MbtRefreshAccessTokenRequest { GrantType = "refresh_token", ClientId = ClientId, @@ -335,13 +335,13 @@ private async Task RefreshAccessTokenAsync(MetaBypassTechAccessTokenResponse tok if (!response.IsSuccessStatusCode) { - var serviceResponse = json.Deserialize(); + var serviceResponse = json.Deserialize(); throw new BadAuthenticationException( serviceResponse.Message ?? "Unknown error"); } - _accessToken = json.Deserialize(); + _accessToken = json.Deserialize(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"{_accessToken.TokenType} {_accessToken.AccessToken}"); } diff --git a/CaptchaSharp/Services/NineKwService.cs b/CaptchaSharp/Services/NineKwService.cs index 1b73282..4184dcb 100644 --- a/CaptchaSharp/Services/NineKwService.cs +++ b/CaptchaSharp/Services/NineKwService.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using CaptchaSharp.Extensions; -using CaptchaSharp.Services.NineKw; +using CaptchaSharp.Models.NineKw; namespace CaptchaSharp.Services; diff --git a/CaptchaSharp/Services/NopechaService.cs b/CaptchaSharp/Services/NopechaService.cs index 56c6fa6..35ca798 100644 --- a/CaptchaSharp/Services/NopechaService.cs +++ b/CaptchaSharp/Services/NopechaService.cs @@ -7,7 +7,7 @@ using CaptchaSharp.Exceptions; using CaptchaSharp.Extensions; using CaptchaSharp.Models; -using CaptchaSharp.Services.Nopecha; +using CaptchaSharp.Models.Nopecha; using Newtonsoft.Json.Linq; namespace CaptchaSharp.Services; diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 1d33f12..0e12338 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -5,7 +5,7 @@ using CaptchaSharp.Enums; using CaptchaSharp.Exceptions; using CaptchaSharp.Models; -using CaptchaSharp.Services.TwoCaptcha; +using CaptchaSharp.Models.TwoCaptcha; using System.Collections.Generic; using System; using CaptchaSharp.Extensions; From 786477f36ca01597e4bf5b0debc1e5d0f57b13ed Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 15:15:17 +0200 Subject: [PATCH 32/67] Removed some Deserialize calls, fixed some linting errors --- CaptchaSharp/CaptchaSharp.xml | 965 +++++++++--------- .../Extensions/HttpClientExtensions.cs | 120 ++- .../Models/AntiCaptcha/Requests/Request.cs | 6 + .../Tasks/AntiCaptchaTaskProxyless.cs | 8 +- .../Tasks/RecaptchaV2TaskProxyless.cs | 19 +- .../Requests/Tasks/TurnstileTaskProxyless.cs | 8 +- .../GetTaskResultAntiCaptchaResponse.cs | 6 +- .../ImageCaptchaAntiCaptchaTaskSolution.cs | 23 +- .../RecaptchaAntiCaptchaTaskSolution.cs | 21 +- CaptchaSharp/Services/AntiCaptchaService.cs | 88 +- .../Services/BestCaptchaSolverService.cs | 41 +- .../Services/CapMonsterCloudService.cs | 5 +- CaptchaSharp/Services/CapSolverService.cs | 52 +- .../Services/MetaBypassTechService.cs | 21 +- CaptchaSharp/Services/NineKwService.cs | 38 +- CaptchaSharp/Services/NoCaptchaAiService.cs | 14 +- CaptchaSharp/Services/NopechaService.cs | 32 +- CaptchaSharp/Services/TwoCaptchaService.cs | 7 +- 18 files changed, 737 insertions(+), 737 deletions(-) diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 9b1cb05..2ad21a6 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -4,335 +4,6 @@ CaptchaSharp - - Abstract class for a generic captcha solving service. - - - The maximum allowed time for captcha completion. - If this is exceeded, a is thrown. - - - The interval at which the service will be polled for a solution. - - - Returns a list of flags that denote the capabilities of the service in terms of additional - parameters to provide when solving text or image based captchas. - - - Retrieves the remaining balance in USD as a . - Thrown when the provided credentials are invalid. - - - Solves a text based captcha. - - The captcha question. - - - Any additional options like the language of the question. - If null they will be disregarded. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves an image based captcha. - - The captcha image encoded as a base64 string. - - - Any additional options like whether the captcha is case-sensitive. - If null they will be disregarded. - - - A token that can be used to cancel the async task. - - /// - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves a Google ReCaptcha V2. - - The site key, can be found in the webpage source or by sniffing requests. - The URL where the captcha appears. - The value of the 's' or 'data-s' field (currently only for Google services). - Whether this is an Enterprise ReCaptcha V2. - Whether the captcha is not in a clickable format on the page. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves a Google ReCaptcha V3. - - The site key, can be found in the webpage source or by sniffing requests. - The URL where the captcha appears. - The action to execute. Can be found in the webpage source or in a js file. - The minimum human-to-robot score necessary to solve the challenge. - Whether this is an Enterprise ReCaptcha V3. - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves a FunCaptcha (Arkose Labs). - - - Can be found inside data-pkey parameter of funcaptcha's div element or inside an input element - with name fc-token. Just extract the key indicated after pk from the value of this element. - - - - Can be found in the fc-token, that is a value of the surl parameter. - - - The URL where the captcha appears. - - - Whether to solve the challenge without JavaScript enabled. This is not supported by every service and - it provides a partial token. - - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves a HCaptcha. - - The site key, can be found in the webpage source or by sniffing requests. - The URL where the captcha appears. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves a KeyCaptcha. - - s_s_c_user_id parameter in the webpage source code. - s_s_c_session_id parameter in the webpage source code. - s_s_c_web_server_sign parameter in the webpage source code. - s_s_c_web_server_sign2 parameter in the webpage source code. - The URL where the captcha appears. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves a GeeTest captcha. - - The static public key assigned to the website found in the webpage source code. - The dynamic challenge key found in the webpage source code. - The api_server parameter found in the webpage source code. - The URL where the captcha appears. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and three solution parameters - (Challenge, Validate and SecCode) that you will need to provide when you submit the form. - - - - - - - - Solves a Capy captcha. - - The site key, can be found in the webpage source or by sniffing requests. - The URL where the captcha appears. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - Solves a DataDome captcha. - - The URL where the captcha appears. - The URL of the captcha. It is obtained from the 'dd' object in a script - inside the HTML and the 'datadome' cookie - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext (a.k.a. a valid datadome session cookie). - - - - - - - - Solves a Cloudflare Turnstile captcha. - - The site key, can be found in the webpage source or by sniffing requests. - The URL where the captcha appears. - Value of optional action parameter you found on page, can be defined in data-action attribute or passed to turnstile.render call. - The value of cData passed to turnstile.render call. Also can be defined in data-cdata attribute. - The value of chlPageData passed to turnstile.render call. - - - A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are - going to send it from when you submit the form. It can help bypass some blocks. If null, the service will - fetch the captcha without using a proxy. - - - A token that can be used to cancel the async task. - - - A containing the captcha id to be used with - and the - captcha solution as plaintext. - - - - - - - - - Reports a captcha solution as good or bad to the service. - Mostly used for reporting bad solutions for image captchas and get the funds back. - Make sure to not abuse this system or the service might ban your account! - - - The string ID of the captcha that you got inside your . - The type of captcha you want to report. - - - If true, the captcha will be reported as correctly solved (this is not supported by some services). - - - A token that can be used to cancel the async task. - - - - - - - - - @@ -673,40 +344,68 @@ Converts a to an ISO-639-1 country code. - Extensions for an . + + Extensions for an . + - Automatically builds a GET query string from a - and appends it to the provided URL. + + Automatically builds a GET query string from a + and appends it to the provided URL. + + + + + Automatically builds a GET query string from a + and appends it to the provided URL. The response is then deserialized to the provided type. + - Automatically builds a GET query string from a - and appends it to the provided URL. - The content converted to a string. + + Automatically builds a GET query string from a + and appends it to the provided URL. + - Automatically builds a POST query string from a - using encoding and the provided Content-Type. + + Automatically builds a POST query string from a + using encoding and the provided Content-Type. + - Automatically builds a POST query string from a - using encoding and the provided Content-Type. - The content converted to a . + + Automatically builds a POST query string from a + using encoding and the provided Content-Type. + - Sends a POST request with the desired and reads the - response as a . - The content converted to a . + + Sends a POST request with the desired and reads the + response as a . + + + + + Sends a POST request with the desired and reads the + response as a . The response is then deserialized to the provided type. + + + + + Automatically builds a POST json string from a given object using encoding + and application/json Content-Type. + - + Automatically builds a POST json string from a given object using encoding - - Automatically builds a POST json string from a given object using encoding - and application/json Content-Type. - The content converted to a . + + + Automatically builds a POST json string from a given object using encoding + and application/json Content-Type. The response is then deserialized to the provided type. + Extensions for a . @@ -721,6 +420,46 @@ Serializes an object to a json string and converts the property names to a camelCase based convention. + + + A request to solve a captcha task. + + + + + The task to solve. + + + + + The soft ID to use. Default is 0. + + + + + The language pool to use. Default is "en". + + + + + Represents a request to the AntiCaptcha API. + + + + + Your AntiCaptcha API key. + + + + + A task that does not require a proxy. + + + + + The type of the task. + + A generic captcha response. @@ -818,6 +557,46 @@ Any additional text instruction (e.g. type the characters in red). + + + The response for a captcha task. + + + + + The captcha id. + + + + + The response (if solved). + + + + + The status of the task. + + + + + The error message (if any). + + + + + The response for a captcha task after it's created. + + + + + The captcha id. + + + + + Seconds since UNIX epoch. + + A generic proxy class. @@ -886,6 +665,11 @@ The language of the text. + + + The User-Agent used to solve the challenge. + + The service provided by https://anti-captcha.com/ @@ -937,7 +721,7 @@ - + Gets the result of a task. @@ -955,32 +739,12 @@ - Creates a new . + Creates a new . - - - A request to solve a captcha task. - - - - - The task to solve. - - - - - The soft ID to use. Default is 0. - - - - - The language pool to use. Default is "en". - - The service provided by https://azcaptcha.com/ @@ -1033,6 +797,9 @@ + + + @@ -1140,53 +907,382 @@ - - - The service provided by https://captchaai.com/ - + + + The service provided by https://captchaai.com/ + + + + + Initializes a . + + The API key to use. + The to use for requests. If null, a default one will be created. + + + + + + + The service provided by https://captchacoder.com/ + + + + + Your secret api key. + + + + The default used for requests. + + + + Initializes a . + + + + + + + + + + + + + + + + + + + Abstract class for a generic captcha solving service. + + + The maximum allowed time for captcha completion. + If this is exceeded, a is thrown. + + + The interval at which the service will be polled for a solution. + + + Returns a list of flags that denote the capabilities of the service in terms of additional + parameters to provide when solving text or image based captchas. + + + Retrieves the remaining balance in USD as a . + Thrown when the provided credentials are invalid. + + + Solves a text based captcha. + + The captcha question. + + + Any additional options like the language of the question. + If null they will be disregarded. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + + + + Solves an image based captcha. + + The captcha image encoded as a base64 string. + + + Any additional options like whether the captcha is case-sensitive. + If null they will be disregarded. + + + A token that can be used to cancel the async task. + + /// + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + + + + Solves a Google ReCaptcha V2. + + The site key, can be found in the webpage source or by sniffing requests. + The URL where the captcha appears. + The value of the 's' or 'data-s' field (currently only for Google services). + Whether this is an Enterprise ReCaptcha V2. + Whether the captcha is not in a clickable format on the page. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + + + + Solves a Google ReCaptcha V3. + + The site key, can be found in the webpage source or by sniffing requests. + The URL where the captcha appears. + The action to execute. Can be found in the webpage source or in a js file. + The minimum human-to-robot score necessary to solve the challenge. + Whether this is an Enterprise ReCaptcha V3. + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + + + + Solves a FunCaptcha (Arkose Labs). + + + Can be found inside data-pkey parameter of funcaptcha's div element or inside an input element + with name fc-token. Just extract the key indicated after pk from the value of this element. + + + + Can be found in the fc-token, that is a value of the surl parameter. + + + The URL where the captcha appears. + + + Whether to solve the challenge without JavaScript enabled. This is not supported by every service and + it provides a partial token. + + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + + + + Solves a HCaptcha. + + The site key, can be found in the webpage source or by sniffing requests. + The URL where the captcha appears. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + - - - Initializes a . - - The API key to use. - The to use for requests. If null, a default one will be created. + + Solves a KeyCaptcha. + + s_s_c_user_id parameter in the webpage source code. + s_s_c_session_id parameter in the webpage source code. + s_s_c_web_server_sign parameter in the webpage source code. + s_s_c_web_server_sign2 parameter in the webpage source code. + The URL where the captcha appears. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + - - + + Solves a GeeTest captcha. + + The static public key assigned to the website found in the webpage source code. + The dynamic challenge key found in the webpage source code. + The api_server parameter found in the webpage source code. + The URL where the captcha appears. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and three solution parameters + (Challenge, Validate and SecCode) that you will need to provide when you submit the form. + + + + + - - - The service provided by https://captchacoder.com/ - + + Solves a Capy captcha. + + The site key, can be found in the webpage source or by sniffing requests. + The URL where the captcha appears. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + - - - Your secret api key. - + + Solves a DataDome captcha. + + The URL where the captcha appears. + The URL of the captcha. It is obtained from the 'dd' object in a script + inside the HTML and the 'datadome' cookie + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext (a.k.a. a valid datadome session cookie). + + + + + - - The default used for requests. + + Solves a Cloudflare Turnstile captcha. + + The site key, can be found in the webpage source or by sniffing requests. + The URL where the captcha appears. + Value of optional action parameter you found on page, can be defined in data-action attribute or passed to turnstile.render call. + The value of cData passed to turnstile.render call. Also can be defined in data-cdata attribute. + The value of chlPageData passed to turnstile.render call. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + - + - Initializes a . + Reports a captcha solution as good or bad to the service. + Mostly used for reporting bad solutions for image captchas and get the funds back. + Make sure to not abuse this system or the service might ban your account! + + The string ID of the captcha that you got inside your . + The type of captcha you want to report. + + + If true, the captcha will be reported as correctly solved (this is not supported by some services). + + + A token that can be used to cancel the async task. + + - - - - - - - - - - - + + - - + + @@ -1373,41 +1469,6 @@ - - - The response for a captcha task. - - - - - The captcha id. - - - - - The response (if solved). - - - - - The status of the task. - - - - - The error message (if any). - - - - - The response for a captcha task after it's created. - - - - - The captcha id. - - The service provided by https://metabypass.tech/. @@ -1545,16 +1606,6 @@ The service provided by https://nocaptchaai.com/ - - - Your secret api key. - - - - - The default used for requests. - - Initializes a . @@ -1605,11 +1656,6 @@ - - - Seconds since UNIX epoch. - - The service provided by https://rucaptcha.com/ @@ -1756,10 +1802,5 @@ - - - The User-Agent used to solve the challenge. - - diff --git a/CaptchaSharp/Extensions/HttpClientExtensions.cs b/CaptchaSharp/Extensions/HttpClientExtensions.cs index 7bb48d5..f9a58ef 100644 --- a/CaptchaSharp/Extensions/HttpClientExtensions.cs +++ b/CaptchaSharp/Extensions/HttpClientExtensions.cs @@ -7,30 +7,48 @@ namespace CaptchaSharp.Extensions; -/// Extensions for an . +/// +/// Extensions for an . +/// public static class HttpClientExtensions { /* * GET METHODS */ - /// Automatically builds a GET query string from a - /// and appends it to the provided URL. + /// + /// Automatically builds a GET query string from a + /// and appends it to the provided URL. + /// public static async Task GetAsync( this HttpClient httpClient, string url, StringPairCollection pairs, CancellationToken cancellationToken = default) { return await httpClient.GetAsync($"{url}?{pairs.ToHttpQueryString()}", cancellationToken); } + + /// + /// Automatically builds a GET query string from a + /// and appends it to the provided URL. The response is then deserialized to the provided type. + /// + public static async Task GetJsonAsync( + this HttpClient httpClient, string url, StringPairCollection pairs, + CancellationToken cancellationToken = default) where T : notnull + { + var response = await httpClient.GetAsync(url, pairs, cancellationToken).ConfigureAwait(false); + var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return json.Deserialize(); + } - /// Automatically builds a GET query string from a - /// and appends it to the provided URL. - /// The content converted to a string. + /// + /// Automatically builds a GET query string from a + /// and appends it to the provided URL. + /// public static async Task GetStringAsync( this HttpClient httpClient, string url, StringPairCollection pairs, CancellationToken cancellationToken = default) { - var response = await httpClient.GetAsync(url, pairs, cancellationToken); + var response = await httpClient.GetAsync(url, pairs, cancellationToken).ConfigureAwait(false); return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); } @@ -38,8 +56,10 @@ public static async Task GetStringAsync( * POST METHODS */ - /// Automatically builds a POST query string from a - /// using encoding and the provided Content-Type. + /// + /// Automatically builds a POST query string from a + /// using encoding and the provided Content-Type. + /// public static async Task PostAsync( this HttpClient httpClient, string url, StringPairCollection pairs, string mediaType = "application/x-www-form-urlencoded", @@ -50,21 +70,23 @@ public static async Task PostAsync( cancellationToken).ConfigureAwait(false); } - /// Automatically builds a POST query string from a - /// using encoding and the provided Content-Type. - /// The content converted to a . + /// + /// Automatically builds a POST query string from a + /// using encoding and the provided Content-Type. + /// public static async Task PostToStringAsync( this HttpClient httpClient, string url, StringPairCollection pairs, string mediaType = "application/x-www-form-urlencoded", CancellationToken cancellationToken = default) { - var response = await httpClient.PostAsync(url, pairs, mediaType, cancellationToken); + var response = await httpClient.PostAsync(url, pairs, mediaType, cancellationToken).ConfigureAwait(false); return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); } - /// Sends a POST request with the desired and reads the - /// response as a . - /// The content converted to a . + /// + /// Sends a POST request with the desired and reads the + /// response as a . + /// public static async Task PostMultipartToStringAsync( this HttpClient httpClient, string url, MultipartFormDataContent content, CancellationToken cancellationToken = default) @@ -74,36 +96,70 @@ public static async Task PostMultipartToStringAsync( } /// - /// Automatically builds a POST json string from a given object using encoding + /// Sends a POST request with the desired and reads the + /// response as a . The response is then deserialized to the provided type. + /// + public static async Task PostMultipartAsync( + this HttpClient httpClient, string url, MultipartFormDataContent content, + CancellationToken cancellationToken = default) where T : notnull + { + var response = await httpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false); + var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return json.Deserialize(); + } + + /// + /// Automatically builds a POST json string from a given object using encoding + /// and application/json Content-Type. /// - public static async Task PostJsonAsync( - this HttpClient httpClient, string url, T content, bool camelizeKeys = true, - CancellationToken cancellationToken = default) where T : class + public static async Task PostJsonToStringAsync( + this HttpClient httpClient, string url, object content, bool camelizeKeys = true, + CancellationToken cancellationToken = default) { var json = camelizeKeys ? content.SerializeCamelCase() : JsonConvert.SerializeObject(content); - return await httpClient.PostAsync(url, + var response = await httpClient.PostAsync(url, new StringContent(json, Encoding.UTF8, "application/json"), - cancellationToken); + cancellationToken).ConfigureAwait(false); + + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); } + + /// + /// Automatically builds a POST json string from a given object using encoding + /// + public static async Task PostJsonAsync( + this HttpClient httpClient, string url, object content, bool camelizeKeys = true, + CancellationToken cancellationToken = default) + { + var json = camelizeKeys + ? content.SerializeCamelCase() + : JsonConvert.SerializeObject(content); - /// Automatically builds a POST json string from a given object using encoding - /// and application/json Content-Type. - /// The content converted to a . - public static async Task PostJsonToStringAsync( - this HttpClient httpClient, string url, T content, bool camelizeKeys = true, - CancellationToken cancellationToken = default) where T : class + return await httpClient.PostAsync(url, + new StringContent(json, Encoding.UTF8, "application/json"), + cancellationToken).ConfigureAwait(false); + } + + /// + /// Automatically builds a POST json string from a given object using encoding + /// and application/json Content-Type. The response is then deserialized to the provided type. + /// + public static async Task PostJsonAsync( + this HttpClient httpClient, string url, object content, bool camelizeKeys = true, + CancellationToken cancellationToken = default) where T : notnull { - var json = camelizeKeys + var json = camelizeKeys ? content.SerializeCamelCase() : JsonConvert.SerializeObject(content); var response = await httpClient.PostAsync(url, new StringContent(json, Encoding.UTF8, "application/json"), - cancellationToken); - - return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); + + var responseJson = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return responseJson.Deserialize(); } } diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs index 2bd9158..b8f0ddd 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs @@ -1,6 +1,12 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests; +/// +/// Represents a request to the AntiCaptcha API. +/// public class Request { + /// + /// Your AntiCaptcha API key. + /// public string ClientKey { get; set; } = ""; } diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs index 147fea5..674901c 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/AntiCaptchaTaskProxyless.cs @@ -1,6 +1,12 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; +/// +/// A task that does not require a proxy. +/// public class AntiCaptchaTaskProxyless { - public string Type { get; set; } + /// + /// The type of the task. + /// + public string? Type { get; set; } } diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs index 1e0133e..0180864 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs @@ -1,14 +1,13 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; + +internal class RecaptchaV2TaskProxyless : AntiCaptchaTaskProxyless { - internal class RecaptchaV2TaskProxyless : AntiCaptchaTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public bool IsInvisible { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public bool IsInvisible { get; set; } - public RecaptchaV2TaskProxyless() - { - Type = "RecaptchaV2TaskProxyless"; - } + public RecaptchaV2TaskProxyless() + { + Type = "RecaptchaV2TaskProxyless"; } } diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs index 28f5db8..96d9299 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs @@ -2,10 +2,10 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; internal class TurnstileTaskProxyless : AntiCaptchaTaskProxyless { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } - public string Action { get; set; } - public string TurnstileCData { get; set; } + public string? WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + public string? Action { get; set; } + public string? TurnstileCData { get; set; } public TurnstileTaskProxyless() { diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs index 575159e..6f0c9b1 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/GetTaskResultAntiCaptchaResponse.cs @@ -4,10 +4,10 @@ namespace CaptchaSharp.Models.AntiCaptcha.Responses; internal class GetTaskResultAntiCaptchaResponse : AntiCaptchaResponse { - public string Status { get; set; } + public string? Status { get; set; } public double Cost { get; set; } - public AntiCaptchaTaskSolution AntiCaptchaTaskSolution { get; set; } - public string Ip { get; set; } + public AntiCaptchaTaskSolution? AntiCaptchaTaskSolution { get; set; } + public string? Ip { get; set; } public double CreateTime { get; set; } public double EndTime { get; set; } public double? SolveCount { get; set; } diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs index 8ca583b..31ec195 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/ImageCaptchaAntiCaptchaTaskSolution.cs @@ -1,19 +1,16 @@ -using CaptchaSharp.Models; +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; -namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions +internal class ImageCaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { - internal class ImageCaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution - { - public string Text { get; set; } - public string Url { get; set; } + public string? Text { get; set; } + public string? Url { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse { - return new StringResponse - { - Id = id, - Response = Text - }; - } + Id = id, + Response = Text! + }; } } diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs index 375dfc5..52e8d2d 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/RecaptchaAntiCaptchaTaskSolution.cs @@ -1,18 +1,15 @@ -using CaptchaSharp.Models; +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; -namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions +internal class RecaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { - internal class RecaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution - { - public string GRecaptchaResponse { get; set; } + public string? GRecaptchaResponse { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse { - return new StringResponse - { - Id = id, - Response = GRecaptchaResponse - }; - } + Id = id, + Response = GRecaptchaResponse! + }; } } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index 0a4de08..7cc2b13 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -52,18 +52,16 @@ public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "getBalance", new Request { ClientKey = ApiKey }, cancellationToken: cancellationToken).ConfigureAwait(false); - var balanceResponse = response.Deserialize(); - - if (balanceResponse.IsError) + if (response.IsError) { - throw new BadAuthenticationException($"{balanceResponse.ErrorCode}: {balanceResponse.ErrorDescription}"); + throw new BadAuthenticationException($"{response.ErrorCode}: {response.ErrorDescription}"); } - return new decimal(balanceResponse.Balance); + return new decimal(response.Balance); } #endregion @@ -73,7 +71,7 @@ public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", AddImageCapabilities( new CaptchaTaskRequest @@ -88,8 +86,7 @@ public override async Task SolveImageCaptchaAsync( cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.ImageCaptcha, + return await GetResult(response, CaptchaType.ImageCaptcha, cancellationToken).ConfigureAwait(false); } @@ -153,14 +150,13 @@ public override async Task SolveRecaptchaV2Async( } } - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV2, + return await GetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); } @@ -185,14 +181,13 @@ public override async Task SolveRecaptchaV3Async( IsEnterprise = enterprise }; - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV3, + return await GetResult(response, CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false); } @@ -227,14 +222,13 @@ public override async Task SolveFuncaptchaAsync( }; } - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.FunCaptcha, + return await GetResult(response, CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false); } @@ -262,14 +256,13 @@ public override async Task SolveHCaptchaAsync( }; } - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.HCaptcha, + return await GetResult(response, CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false); } @@ -301,14 +294,13 @@ public override async Task SolveGeeTestAsync( }; } - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.GeeTest, + return await GetResult(response, CaptchaType.GeeTest, cancellationToken).ConfigureAwait(false); } @@ -340,14 +332,13 @@ public override async Task SolveCloudflareTurnstile }; } - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.CloudflareTurnstile, + return await GetResult(response, CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false); } @@ -454,42 +445,25 @@ await HttpClient.PostJsonToStringAsync( return; } - string response; - ReportIncorrectCaptchaAntiCaptchaResponse incAntiCaptchaResponse; - - switch (type) + var incAntiCaptchaResponse = type switch { - case CaptchaType.ImageCaptcha: - response = await HttpClient.PostJsonToStringAsync( + CaptchaType.ImageCaptcha => await HttpClient.PostJsonAsync( "reportIncorrectImageCaptcha", new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, - cancellationToken: cancellationToken).ConfigureAwait(false); - - incAntiCaptchaResponse = response.Deserialize(); - break; - - case CaptchaType.ReCaptchaV2: - case CaptchaType.ReCaptchaV3: - response = await HttpClient.PostJsonToStringAsync( - "reportIncorrectRecaptcha", + cancellationToken: cancellationToken) + .ConfigureAwait(false), + CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3 => await HttpClient + .PostJsonAsync("reportIncorrectRecaptcha", new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, - cancellationToken: cancellationToken).ConfigureAwait(false); - - incAntiCaptchaResponse = response.Deserialize(); - break; - - case CaptchaType.HCaptcha: - response = await HttpClient.PostJsonToStringAsync( + cancellationToken: cancellationToken) + .ConfigureAwait(false), + CaptchaType.HCaptcha => await HttpClient.PostJsonAsync( "reportIncorrectHcaptcha", new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, - cancellationToken: cancellationToken).ConfigureAwait(false); - - incAntiCaptchaResponse = response.Deserialize(); - break; - - default: - throw new NotSupportedException("Reporting is not supported for this captcha type"); - } + cancellationToken: cancellationToken) + .ConfigureAwait(false), + _ => throw new NotSupportedException("Reporting is not supported for this captcha type") + }; if (incAntiCaptchaResponse.NotFoundOrExpired) { diff --git a/CaptchaSharp/Services/BestCaptchaSolverService.cs b/CaptchaSharp/Services/BestCaptchaSolverService.cs index 57cd1de..2302b9c 100644 --- a/CaptchaSharp/Services/BestCaptchaSolverService.cs +++ b/CaptchaSharp/Services/BestCaptchaSolverService.cs @@ -44,15 +44,13 @@ public BestCaptchaSolverService(string apiKey, HttpClient? httpClient = null) /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "user/balance", new StringPairCollection() .Add("access_token", ApiKey), cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.Success) { throw new BadAuthenticationException(response.Error!); @@ -86,14 +84,12 @@ public override async Task SolveImageCaptchaAsync( MaxLength = options?.MaxLength }; - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/image", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.ImageCaptcha, cancellationToken: cancellationToken); @@ -123,14 +119,12 @@ public override async Task SolveRecaptchaV2Async( payload.SetProxy(proxy); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/recaptcha", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.ReCaptchaV2, cancellationToken: cancellationToken); @@ -154,14 +148,12 @@ public override async Task SolveRecaptchaV3Async( payload.SetProxy(proxy); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/recaptcha", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.ReCaptchaV3, cancellationToken: cancellationToken); @@ -183,14 +175,12 @@ public override async Task SolveFuncaptchaAsync( payload.SetProxy(proxy); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/funcaptcha", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.FunCaptcha, cancellationToken: cancellationToken); @@ -211,19 +201,18 @@ public override async Task SolveHCaptchaAsync( payload.SetProxy(proxy); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/hcaptcha", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.HCaptcha, cancellationToken: cancellationToken); } + /// public override async Task SolveGeeTestAsync( string gt, string challenge, string siteUrl, string? apiServer = null, Proxy? proxy = null, CancellationToken cancellationToken = default) @@ -240,14 +229,12 @@ public override async Task SolveGeeTestAsync( payload.SetProxy(proxy); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/geetest", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.GeeTest, cancellationToken: cancellationToken); @@ -267,14 +254,12 @@ public override async Task SolveCapyAsync( payload.SetProxy(proxy); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/capy", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.Capy, cancellationToken: cancellationToken); @@ -297,14 +282,12 @@ public override async Task SolveCloudflareTurnstile payload.SetProxy(proxy); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "captcha/turnstile", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.CloudflareTurnstile, cancellationToken: cancellationToken); @@ -441,7 +424,7 @@ public override async Task ReportSolution( "BestCaptchaSolver does not support reporting correct solutions."); } - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( $"captcha/bad/{id}", new BcsRequest { @@ -450,8 +433,6 @@ public override async Task ReportSolution( cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.Success) { throw new TaskReportException(response.Error!); diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index c31b407..c072269 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -71,14 +71,13 @@ public override async Task SolveDataDomeAsync( } }; - var response = await HttpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.DataDome, + return await GetResult(response, CaptchaType.DataDome, cancellationToken).ConfigureAwait(false); } diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index 8d84818..8bab5d6 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -51,20 +51,18 @@ public CapSolverService(string apiKey, HttpClient? httpClient = null) /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "getBalance", new Request { ClientKey = ApiKey }, cancellationToken: cancellationToken) .ConfigureAwait(false); - var balanceResponse = response.Deserialize(); - - if (balanceResponse.IsError) + if (response.IsError) { - throw new BadAuthenticationException($"{balanceResponse.ErrorCode}: {balanceResponse.ErrorDescription}"); + throw new BadAuthenticationException($"{response.ErrorCode}: {response.ErrorDescription}"); } - return new decimal(balanceResponse.Balance); + return new decimal(response.Balance); } #endregion @@ -160,14 +158,13 @@ public override async Task SolveRecaptchaV2Async( } } - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV2, + return await GetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); } @@ -201,14 +198,13 @@ public override async Task SolveRecaptchaV3Async( }.SetProxy(proxy); } - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.ReCaptchaV3, + return await GetResult(response, CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false); } @@ -243,14 +239,13 @@ public override async Task SolveFuncaptchaAsync( }; } - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.FunCaptcha, + return await GetResult(response, CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false); } @@ -278,14 +273,13 @@ public override async Task SolveHCaptchaAsync( }; } - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.HCaptcha, + return await GetResult(response, CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false); } @@ -318,14 +312,13 @@ public override async Task SolveGeeTestAsync( }; } - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.GeeTest, + return await GetResult(response, CaptchaType.GeeTest, cancellationToken).ConfigureAwait(false); } @@ -347,14 +340,13 @@ public override async Task SolveDataDomeAsync( CaptchaURL = captchaUrl }.SetProxy(proxy); - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await GetResult( - response.Deserialize(), CaptchaType.DataDome, + return await GetResult(response, CaptchaType.DataDome, cancellationToken).ConfigureAwait(false); } @@ -376,14 +368,14 @@ public override async Task SolveCloudflareTurnstile } }; - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - response.Deserialize(), CaptchaType.CloudflareTurnstile, + response, CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false); } #endregion @@ -456,7 +448,7 @@ private async Task GetResult( /// public override async Task ReportSolution(string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "feedbackTask", new CaptchaTaskFeedbackRequest { @@ -471,11 +463,9 @@ public override async Task ReportSolution(string id, CaptchaType type, bool corr cancellationToken: cancellationToken) .ConfigureAwait(false); - var result = response.Deserialize(); - - if (result.IsError) + if (response.IsError) { - throw new TaskReportException($"{result.ErrorCode}: {result.ErrorDescription}"); + throw new TaskReportException($"{response.ErrorCode}: {response.ErrorDescription}"); } } #endregion diff --git a/CaptchaSharp/Services/MetaBypassTechService.cs b/CaptchaSharp/Services/MetaBypassTechService.cs index 8e8db5a..4254ea8 100644 --- a/CaptchaSharp/Services/MetaBypassTechService.cs +++ b/CaptchaSharp/Services/MetaBypassTechService.cs @@ -76,13 +76,12 @@ public override async Task GetBalanceAsync( { await EnsureAccessTokenAsync().ConfigureAwait(false); - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "api/v1/me", + new StringPairCollection(), cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.Ok) { throw new BadAuthenticationException( @@ -123,14 +122,12 @@ public override async Task SolveImageCaptchaAsync( MaxLength = options?.MaxLength ?? 0 }; - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "api/v1/services/captchaSolver", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.Ok) { throw new TaskSolutionException( @@ -166,14 +163,12 @@ public override async Task SolveRecaptchaV2Async( Version = "2" }; - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "api/v1/services/bypassReCaptcha", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.Ok) { throw new TaskSolutionException( @@ -207,14 +202,12 @@ public override async Task SolveRecaptchaV3Async( Version = "3" }; - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "api/v1/services/bypassReCaptcha", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.Ok) { throw new TaskSolutionException( @@ -241,14 +234,12 @@ public override async Task SolveRecaptchaV3Async( "The getCaptchaResult method is only supported for ReCaptchaV2 tasks"); } - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "api/v1/services/getCaptchaResult", new StringPairCollection() .Add("recaptcha_id", task.Id), cancellationToken).ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.Ok) { throw new TaskSolutionException( diff --git a/CaptchaSharp/Services/NineKwService.cs b/CaptchaSharp/Services/NineKwService.cs index 4184dcb..c194b2e 100644 --- a/CaptchaSharp/Services/NineKwService.cs +++ b/CaptchaSharp/Services/NineKwService.cs @@ -46,7 +46,7 @@ public NineKwService(string apiKey, HttpClient? httpClient = null) /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaguthaben") @@ -54,8 +54,6 @@ public override async Task GetBalanceAsync(CancellationToken cancellati cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (IsError(response)) { throw new BadAuthenticationException(GetErrorMessage(response)); @@ -71,7 +69,7 @@ public override async Task SolveTextCaptchaAsync( string text, TextCaptchaOptions? options = default, CancellationToken cancellationToken = default) { - var json = await _httpClient.PostMultipartToStringAsync( + var response = await _httpClient.PostMultipartAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -82,8 +80,6 @@ public override async Task SolveTextCaptchaAsync( cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - return await GetResult( response, CaptchaType.TextCaptcha, cancellationToken).ConfigureAwait(false); } @@ -93,7 +89,7 @@ public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var json = await _httpClient.PostMultipartToStringAsync( + var response = await _httpClient.PostMultipartAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -104,8 +100,6 @@ public override async Task SolveImageCaptchaAsync( .ToMultipartFormDataContent(), cancellationToken) .ConfigureAwait(false); - - var response = json.Deserialize(); if (IsError(response)) { @@ -121,7 +115,7 @@ public override async Task SolveRecaptchaV2Async( string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -133,8 +127,6 @@ public override async Task SolveRecaptchaV2Async( .Add(ConvertProxy(proxy)), cancellationToken) .ConfigureAwait(false); - - var response = json.Deserialize(); return await GetResult( response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); @@ -145,7 +137,7 @@ public override async Task SolveRecaptchaV3Async( string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -157,8 +149,6 @@ public override async Task SolveRecaptchaV3Async( .Add(ConvertProxy(proxy)), cancellationToken) .ConfigureAwait(false); - - var response = json.Deserialize(); return await GetResult( response, CaptchaType.ReCaptchaV3, cancellationToken).ConfigureAwait(false); @@ -169,7 +159,7 @@ public override async Task SolveFuncaptchaAsync( string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -181,8 +171,6 @@ public override async Task SolveFuncaptchaAsync( .Add(ConvertProxy(proxy)), cancellationToken) .ConfigureAwait(false); - - var response = json.Deserialize(); return await GetResult( response, CaptchaType.FunCaptcha, cancellationToken).ConfigureAwait(false); @@ -193,7 +181,7 @@ public override async Task SolveHCaptchaAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -205,8 +193,6 @@ public override async Task SolveHCaptchaAsync( .Add(ConvertProxy(proxy)), cancellationToken) .ConfigureAwait(false); - - var response = json.Deserialize(); return await GetResult( response, CaptchaType.HCaptcha, cancellationToken).ConfigureAwait(false); @@ -233,7 +219,7 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchacorrectdata") @@ -242,8 +228,6 @@ private async Task GetResult( cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - // Not solved yet if (response.TryAgain is 1) { @@ -277,17 +261,15 @@ private async Task GetResult( public override async Task ReportSolution( string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchacorrectback") .Add("id", id) .Add("correct", correct ? 1 : 2) .Add("json", 1), - cancellationToken); + cancellationToken).ConfigureAwait(false); - var response = json.Deserialize(); - if (IsError(response)) { throw new TaskReportException(GetErrorMessage(response)); diff --git a/CaptchaSharp/Services/NoCaptchaAiService.cs b/CaptchaSharp/Services/NoCaptchaAiService.cs index 655b4b9..80c094a 100644 --- a/CaptchaSharp/Services/NoCaptchaAiService.cs +++ b/CaptchaSharp/Services/NoCaptchaAiService.cs @@ -9,16 +9,6 @@ namespace CaptchaSharp.Services; /// public class NoCaptchaAiService : CustomTwoCaptchaService { - /// - /// Your secret api key. - /// - public string ApiKey { get; set; } - - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - /// /// Initializes a . /// @@ -28,9 +18,7 @@ public NoCaptchaAiService(string apiKey, HttpClient? httpClient = null) : base(apiKey, new Uri("https://token.nocaptchaai.com/"), httpClient, false) { ApiKey = apiKey; - _httpClient = httpClient ?? new HttpClient(); - - _httpClient.BaseAddress = new Uri("https://free.nocaptchaai.com"); + HttpClient.BaseAddress = new Uri("https://free.nocaptchaai.com"); SupportedCaptchaTypes = CaptchaType.ImageCaptcha | diff --git a/CaptchaSharp/Services/NopechaService.cs b/CaptchaSharp/Services/NopechaService.cs index 35ca798..6df2b84 100644 --- a/CaptchaSharp/Services/NopechaService.cs +++ b/CaptchaSharp/Services/NopechaService.cs @@ -45,15 +45,13 @@ public NopechaService(string apiKey, HttpClient? httpClient = null) public override async Task GetBalanceAsync( CancellationToken cancellationToken = default) { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "status", new StringPairCollection() .Add("key", ApiKey), cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.IsSuccess) { throw new BadAuthenticationException(response.Message!); @@ -75,14 +73,14 @@ public override async Task SolveImageCaptchaAsync( ImageData = [base64] }; - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - json, CaptchaType.ImageCaptcha, cancellationToken) + response, CaptchaType.ImageCaptcha, cancellationToken) .ConfigureAwait(false); } @@ -104,14 +102,14 @@ public override async Task SolveRecaptchaV2Async( payload.SetProxy(proxy, siteUrl); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - json, CaptchaType.ReCaptchaV2, cancellationToken) + response, CaptchaType.ReCaptchaV2, cancellationToken) .ConfigureAwait(false); } @@ -134,14 +132,14 @@ public override async Task SolveRecaptchaV3Async( payload.SetProxy(proxy, siteUrl); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - json, CaptchaType.ReCaptchaV3, cancellationToken) + response, CaptchaType.ReCaptchaV3, cancellationToken) .ConfigureAwait(false); } @@ -159,14 +157,14 @@ public override async Task SolveHCaptchaAsync( payload.SetProxy(proxy, siteUrl); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - json, CaptchaType.HCaptcha, cancellationToken) + response, CaptchaType.HCaptcha, cancellationToken) .ConfigureAwait(false); } @@ -197,25 +195,23 @@ public override async Task SolveCloudflareTurnstile payload.SetProxy(proxy, siteUrl); - var json = await _httpClient.PostJsonToStringAsync( + var response = await _httpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) .ConfigureAwait(false); return await GetResult( - json, CaptchaType.CloudflareTurnstile, cancellationToken) + response, CaptchaType.CloudflareTurnstile, cancellationToken) .ConfigureAwait(false); } #endregion #region Getting the result private async Task GetResult( - string json, CaptchaType captchaType, CancellationToken cancellationToken) + NopechaDataResponse response, CaptchaType captchaType, CancellationToken cancellationToken) where T : CaptchaResponse { - var response = json.Deserialize(); - if (!response.IsSuccess) { throw new TaskCreationException(response.Message!); @@ -230,7 +226,7 @@ private async Task GetResult( protected override async Task CheckResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var json = await _httpClient.GetStringAsync( + var response = await _httpClient.GetJsonAsync( "", new StringPairCollection() .Add("key", ApiKey) @@ -238,8 +234,6 @@ private async Task GetResult( cancellationToken) .ConfigureAwait(false); - var response = json.Deserialize(); - if (!response.IsSuccess) { // Incomplete job diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 0e12338..b56f035 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -79,7 +79,7 @@ public override async Task GetBalanceAsync(CancellationToken cancellati return decimal.Parse(tcResponse.Request!, CultureInfo.InvariantCulture); } - if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal balance)) + if (decimal.TryParse(response, NumberStyles.Any, CultureInfo.InvariantCulture, out var balance)) { return balance; } @@ -421,7 +421,6 @@ public override async Task SolveCloudflareTurnstile response, CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false); } - #endregion #region Getting the result @@ -460,7 +459,7 @@ internal async Task GetResult( new StringPairCollection() .Add("key", ApiKey) .Add("action", "get") - .Add("id", task.Id.ToString()) + .Add("id", task.Id) .Add("json", Convert.ToInt32(UseJsonFlag).ToString()), cancellationToken).ConfigureAwait(false); @@ -614,7 +613,7 @@ protected bool IsErrorCode(string response) /// For non-json response. protected static string TakeSecondSlice(string str) { - return str.Split('|')[1]; + return str.Split('|')[1].Replace("\r\n", "").Trim(); } #endregion From f2493624ca584ab87c7d7dc5d3ee6e81bb4c799a Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 15:22:25 +0200 Subject: [PATCH 33/67] Converted namespaces to block-scoped --- CaptchaSharp.Tests/ServiceFixture.cs | 9 +-- .../Requests/GetTaskResultRequest.cs | 11 ++- .../Requests/RecaptchaV2CaptchaRequest.cs | 9 +-- .../Requests/ReportIncorrectCaptchaRequest.cs | 11 ++- .../Requests/Tasks/FunCaptchaTaskProxyless.cs | 21 +++-- .../Requests/Tasks/GeeTestTaskProxyless.cs | 23 +++--- .../Requests/Tasks/HCaptchaTaskProxyless.cs | 19 +++-- .../Tasks/ImageCaptchaTaskProxyless.cs | 31 ++++---- .../Requests/Tasks/Proxied/AntiCaptchaTask.cs | 79 +++++++++---------- .../Requests/Tasks/Proxied/FunCaptchaTask.cs | 21 +++-- .../Requests/Tasks/Proxied/GeeTestTask.cs | 23 +++--- .../Requests/Tasks/Proxied/HCaptchaTask.cs | 19 +++-- .../Proxied/RecaptchaV2EnterpriseTask.cs | 21 +++-- .../Requests/Tasks/Proxied/RecaptchaV2Task.cs | 21 +++-- .../RecaptchaV2EnterpriseTaskProxyless.cs | 21 +++-- .../Tasks/RecaptchaV3TaskProxyless.cs | 25 +++--- .../FuncaptchaAntiCaptchaTaskSolution.cs | 23 +++--- .../GeeTestAntiCaptchaTaskSolution.cs | 31 ++++---- .../Requests/Tasks/FunCaptchaTaskProxyless.cs | 21 +++-- .../Requests/Tasks/GeeTestTaskProxyless.cs | 25 +++--- .../Requests/Tasks/HCaptchaTaskProxyless.cs | 19 +++-- .../Tasks/ImageCaptchaTaskProxyless.cs | 31 ++++---- .../Requests/Tasks/Proxied/DataDomeTask.cs | 19 +++-- .../Requests/Tasks/Proxied/FunCaptchaTask.cs | 21 +++-- .../Requests/Tasks/Proxied/GeeTestTask.cs | 23 +++--- .../Requests/Tasks/Proxied/HCaptchaTask.cs | 19 +++-- .../Requests/Tasks/Proxied/RecaptchaV2Task.cs | 23 +++--- .../Requests/Tasks/Proxied/RecaptchaV3Task.cs | 25 +++--- .../Tasks/RecaptchaV2TaskProxyless.cs | 23 +++--- .../Tasks/RecaptchaV3TaskProxyless.cs | 25 +++--- .../Models/CapSolver/Responses/Response.cs | 19 +++-- .../Responses/Solutions/FuncaptchaSolution.cs | 23 +++--- .../Responses/Solutions/GeeTestSolution.cs | 31 ++++---- .../Solutions/ImageCaptchaSolution.cs | 25 +++--- .../Responses/Solutions/RecaptchaSolution.cs | 23 +++--- .../CapSolver/Responses/Solutions/Solution.cs | 13 ++- 36 files changed, 393 insertions(+), 433 deletions(-) diff --git a/CaptchaSharp.Tests/ServiceFixture.cs b/CaptchaSharp.Tests/ServiceFixture.cs index 1923261..2c960a9 100644 --- a/CaptchaSharp.Tests/ServiceFixture.cs +++ b/CaptchaSharp.Tests/ServiceFixture.cs @@ -4,12 +4,7 @@ namespace CaptchaSharp.Tests; public abstract class ServiceFixture { - private readonly ConfigFixture _configFixture; + private readonly ConfigFixture _configFixture = new(); public Config Config => _configFixture.Config; - public CaptchaService Service { get; init; } - - protected ServiceFixture() - { - _configFixture = new ConfigFixture(); - } + public required CaptchaService Service { get; init; } } diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs index d2966a1..6bf63ad 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests +namespace CaptchaSharp.Models.AntiCaptcha.Requests; + +internal class GetTaskResultRequest : Request { - internal class GetTaskResultRequest : Request - { - public int TaskId { get; set; } - } -} + public int TaskId { get; set; } +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs index ebc5671..8afc527 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests +namespace CaptchaSharp.Models.AntiCaptcha.Requests; + +internal class RecaptchaV2CaptchaRequest : Request { - internal class RecaptchaV2CaptchaRequest : Request - { - } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs index 6ecbcc5..3dc880f 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs @@ -1,7 +1,6 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests +namespace CaptchaSharp.Models.AntiCaptcha.Requests; + +internal class ReportIncorrectCaptchaRequest : Request { - internal class ReportIncorrectCaptchaRequest : Request - { - public long TaskId { get; set; } - } -} + public long TaskId { get; set; } +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs index 78756fa..07c6bc1 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs @@ -1,14 +1,13 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; + +internal class FunCaptchaTaskProxyless : AntiCaptchaTaskProxyless { - internal class FunCaptchaTaskProxyless : AntiCaptchaTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string WebsiteURL { get; set; } + public string WebsitePublicKey { get; set; } + public string FuncaptchaApiJSSubdomain { get; set; } - public FunCaptchaTaskProxyless() - { - Type = "FunCaptchaTaskProxyless"; - } + public FunCaptchaTaskProxyless() + { + Type = "FunCaptchaTaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs index d5283f7..824eb08 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; + +internal class GeeTestTaskProxyless : AntiCaptchaTaskProxyless { - internal class GeeTestTaskProxyless : AntiCaptchaTaskProxyless - { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } - public string GeetestApiServerSubdomain { get; set; } + public string WebsiteURL { get; set; } + public string Gt { get; set; } + public string Challenge { get; set; } + public string GeetestApiServerSubdomain { get; set; } - public GeeTestTaskProxyless() - { - Type = "GeeTestTaskProxyless"; - } + public GeeTestTaskProxyless() + { + Type = "GeeTestTaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs index 5b53e88..652c200 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs @@ -1,13 +1,12 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; + +internal class HCaptchaTaskProxyless : AntiCaptchaTaskProxyless { - internal class HCaptchaTaskProxyless : AntiCaptchaTaskProxyless - { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public string WebsiteURL { get; set; } - public HCaptchaTaskProxyless() - { - Type = "HCaptchaTaskProxyless"; - } + public HCaptchaTaskProxyless() + { + Type = "HCaptchaTaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs index ba8cd7f..7b65fb9 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/ImageCaptchaTaskProxyless.cs @@ -1,19 +1,18 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; + +internal class ImageCaptchaTask : AntiCaptchaTaskProxyless { - internal class ImageCaptchaTask : AntiCaptchaTaskProxyless - { - public string Body { get; set; } = ""; - public bool Phrase { get; set; } = false; - public bool Case { get; set; } = false; - public int Numeric { get; set; } = 0; - public bool Math { get; set; } = false; - public int MinLength { get; set; } = 0; - public int MaxLength { get; set; } = 0; - public string Comment { get; set; } = ""; + public string Body { get; set; } = ""; + public bool Phrase { get; set; } = false; + public bool Case { get; set; } = false; + public int Numeric { get; set; } = 0; + public bool Math { get; set; } = false; + public int MinLength { get; set; } = 0; + public int MaxLength { get; set; } = 0; + public string Comment { get; set; } = ""; - public ImageCaptchaTask() - { - Type = "ImageToTextTask"; - } + public ImageCaptchaTask() + { + Type = "ImageToTextTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs index 0c17a71..3f4a5d6 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/AntiCaptchaTask.cs @@ -3,53 +3,52 @@ using System.Collections.Generic; using System.Linq; -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; + +internal class AntiCaptchaTask : AntiCaptchaTaskProxyless { - internal class AntiCaptchaTask : AntiCaptchaTaskProxyless - { - public string? ProxyType { get; set; } - public string? ProxyAddress { get; set; } - public int ProxyPort { get; set; } - public string? ProxyLogin { get; set; } - public string? ProxyPassword { get; set; } - public string? UserAgent { get; set; } + public string? ProxyType { get; set; } + public string? ProxyAddress { get; set; } + public int ProxyPort { get; set; } + public string? ProxyLogin { get; set; } + public string? ProxyPassword { get; set; } + public string? UserAgent { get; set; } - // Format cookiename1=cookievalue1; cookiename2=cookievalue2 - public string? Cookies { get; set; } - - public AntiCaptchaTask SetProxy(Proxy proxy) - { - UserAgent = proxy.UserAgent; - SetCookies(proxy.Cookies); + // Format cookiename1=cookievalue1; cookiename2=cookievalue2 + public string? Cookies { get; set; } - if (string.IsNullOrEmpty(proxy.Host)) - { - return this; - } - - if (!System.Net.IPAddress.TryParse(proxy.Host, out _)) - { - throw new NotSupportedException( - "Only IP addresses are supported for the proxy host"); - } - - ProxyAddress = proxy.Host; - ProxyPort = proxy.Port; - ProxyType = proxy.Type.ToString().ToLower(); - ProxyLogin = proxy.Username; - ProxyPassword = proxy.Password; + public AntiCaptchaTask SetProxy(Proxy proxy) + { + UserAgent = proxy.UserAgent; + SetCookies(proxy.Cookies); + if (string.IsNullOrEmpty(proxy.Host)) + { return this; } - - private void SetCookies(IEnumerable<(string Name, string Value)>? cookies) + + if (!System.Net.IPAddress.TryParse(proxy.Host, out _)) { - if (cookies == null) - { - return; - } + throw new NotSupportedException( + "Only IP addresses are supported for the proxy host"); + } + + ProxyAddress = proxy.Host; + ProxyPort = proxy.Port; + ProxyType = proxy.Type.ToString().ToLower(); + ProxyLogin = proxy.Username; + ProxyPassword = proxy.Password; + + return this; + } - Cookies = string.Join("; ", cookies.Select(c => $"{c.Name}={c.Value}")); + private void SetCookies(IEnumerable<(string Name, string Value)>? cookies) + { + if (cookies == null) + { + return; } + + Cookies = string.Join("; ", cookies.Select(c => $"{c.Name}={c.Value}")); } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs index b671bfb..00ccec7 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs @@ -1,14 +1,13 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; + +internal class FunCaptchaTask : AntiCaptchaTask { - internal class FunCaptchaTask : AntiCaptchaTask - { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string WebsiteURL { get; set; } + public string WebsitePublicKey { get; set; } + public string FuncaptchaApiJSSubdomain { get; set; } - public FunCaptchaTask() - { - Type = "FunCaptchaTask"; - } + public FunCaptchaTask() + { + Type = "FunCaptchaTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs index 0ce909f..4795fa8 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; + +internal class GeeTestTask : AntiCaptchaTask { - internal class GeeTestTask : AntiCaptchaTask - { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } - public string GeetestApiServerSubdomain { get; set; } + public string WebsiteURL { get; set; } + public string Gt { get; set; } + public string Challenge { get; set; } + public string GeetestApiServerSubdomain { get; set; } - public GeeTestTask() - { - Type = "GeeTestTask"; - } + public GeeTestTask() + { + Type = "GeeTestTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs index f7ab4af..1938973 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs @@ -1,13 +1,12 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; + +internal class HCaptchaTask : AntiCaptchaTask { - internal class HCaptchaTask : AntiCaptchaTask - { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public string WebsiteURL { get; set; } - public HCaptchaTask() - { - Type = "HCaptchaTask"; - } + public HCaptchaTask() + { + Type = "HCaptchaTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs index 1cd9701..8db8cd4 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; + +internal class RecaptchaV2EnterpriseTask : AntiCaptchaTask { - internal class RecaptchaV2EnterpriseTask : AntiCaptchaTask - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public Dictionary EnterprisePayload { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public Dictionary EnterprisePayload { get; set; } - public RecaptchaV2EnterpriseTask() - { - Type = "RecaptchaV2EnterpriseTask"; - } + public RecaptchaV2EnterpriseTask() + { + Type = "RecaptchaV2EnterpriseTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs index 236b5e3..e2c1622 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs @@ -1,14 +1,13 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; + +internal class RecaptchaV2Task : AntiCaptchaTask { - internal class RecaptchaV2Task : AntiCaptchaTask - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public bool IsInvisible { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public bool IsInvisible { get; set; } - public RecaptchaV2Task() - { - Type = "RecaptchaV2Task"; - } + public RecaptchaV2Task() + { + Type = "RecaptchaV2Task"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs index 46a0b12..03e8048 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; + +internal class RecaptchaV2EnterpriseTaskProxyless : AntiCaptchaTaskProxyless { - internal class RecaptchaV2EnterpriseTaskProxyless : AntiCaptchaTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public Dictionary EnterprisePayload { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public Dictionary EnterprisePayload { get; set; } - public RecaptchaV2EnterpriseTaskProxyless() - { - Type = "RecaptchaV2EnterpriseTaskProxyless"; - } + public RecaptchaV2EnterpriseTaskProxyless() + { + Type = "RecaptchaV2EnterpriseTaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs index 6ba31f7..cb5e9c0 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs @@ -1,16 +1,15 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks +namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; + +internal class RecaptchaV3TaskProxyless : AntiCaptchaTaskProxyless { - internal class RecaptchaV3TaskProxyless : AntiCaptchaTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string PageAction { get; set; } - public float MinScore { get; set; } - public bool IsEnterprise { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public string PageAction { get; set; } + public float MinScore { get; set; } + public bool IsEnterprise { get; set; } - public RecaptchaV3TaskProxyless() - { - Type = "RecaptchaV3TaskProxyless"; - } + public RecaptchaV3TaskProxyless() + { + Type = "RecaptchaV3TaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs index 20dc489..33ad31e 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs @@ -1,18 +1,17 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; + +internal class FuncaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { - internal class FuncaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution - { - public string Token { get; set; } + public string Token { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse { - return new StringResponse - { - Id = id, - Response = Token - }; - } + Id = id, + Response = Token + }; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs index 3bd9f28..308e504 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs @@ -1,22 +1,21 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; + +internal class GeeTestAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { - internal class GeeTestAntiCaptchaTaskSolution : AntiCaptchaTaskSolution - { - public string Challenge { get; set; } - public string Validate { get; set; } - public string SecCode { get; set; } + public string Challenge { get; set; } + public string Validate { get; set; } + public string SecCode { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new GeeTestResponse() { - return new GeeTestResponse() - { - Id = id, - Challenge = Challenge, - Validate = Validate, - SecCode = SecCode - }; - } + Id = id, + Challenge = Challenge, + Validate = Validate, + SecCode = SecCode + }; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs index 6af156a..9236743 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs @@ -1,14 +1,13 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; + +internal class FunCaptchaTaskProxyless : CapSolverTaskProxyless { - internal class FunCaptchaTaskProxyless : CapSolverTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string WebsiteURL { get; set; } + public string WebsitePublicKey { get; set; } + public string FuncaptchaApiJSSubdomain { get; set; } - public FunCaptchaTaskProxyless() - { - Type = "FunCaptchaTaskProxyless"; - } + public FunCaptchaTaskProxyless() + { + Type = "FunCaptchaTaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs index 0274fe3..d83b9ad 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs @@ -1,16 +1,15 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; + +internal class GeeTestTaskProxyless : CapSolverTaskProxyless { - internal class GeeTestTaskProxyless : CapSolverTaskProxyless - { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } - public string? GeetestApiServerSubdomain { get; set; } - public int Version { get; set; } + public string WebsiteURL { get; set; } + public string Gt { get; set; } + public string Challenge { get; set; } + public string? GeetestApiServerSubdomain { get; set; } + public int Version { get; set; } - public GeeTestTaskProxyless() - { - Type = "GeeTestTaskProxyless"; - } + public GeeTestTaskProxyless() + { + Type = "GeeTestTaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs index 9f3ea37..7e67d71 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs @@ -1,13 +1,12 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; + +internal class HCaptchaTaskProxyless : CapSolverTaskProxyless { - internal class HCaptchaTaskProxyless : CapSolverTaskProxyless - { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public string WebsiteURL { get; set; } - public HCaptchaTaskProxyless() - { - Type = "HCaptchaTaskProxyless"; - } + public HCaptchaTaskProxyless() + { + Type = "HCaptchaTaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs index 0259983..f9cf3aa 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/ImageCaptchaTaskProxyless.cs @@ -1,19 +1,18 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; + +internal class ImageCaptchaTask : CapSolverTaskProxyless { - internal class ImageCaptchaTask : CapSolverTaskProxyless - { - public string Body { get; set; } = ""; - public bool Phrase { get; set; } = false; - public bool Case { get; set; } = false; - public int Numeric { get; set; } = 0; - public bool Math { get; set; } = false; - public int MinLength { get; set; } = 0; - public int MaxLength { get; set; } = 0; - public string Comment { get; set; } = ""; + public string Body { get; set; } = ""; + public bool Phrase { get; set; } = false; + public bool Case { get; set; } = false; + public int Numeric { get; set; } = 0; + public bool Math { get; set; } = false; + public int MinLength { get; set; } = 0; + public int MaxLength { get; set; } = 0; + public string Comment { get; set; } = ""; - public ImageCaptchaTask() - { - Type = "ImageToTextTask"; - } + public ImageCaptchaTask() + { + Type = "ImageToTextTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs index b919533..caf6243 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs @@ -1,13 +1,12 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; + +internal class DataDomeTask : CapSolverTask { - internal class DataDomeTask : CapSolverTask - { - public string WebsiteURL { get; set; } - public string CaptchaURL { get; set; } + public string WebsiteURL { get; set; } + public string CaptchaURL { get; set; } - public DataDomeTask() - { - Type = "DatadomeSliderTask"; - } + public DataDomeTask() + { + Type = "DatadomeSliderTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs index e74987f..9e2d355 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs @@ -1,14 +1,13 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; + +internal class FunCaptchaTask : CapSolverTask { - internal class FunCaptchaTask : CapSolverTask - { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string WebsiteURL { get; set; } + public string WebsitePublicKey { get; set; } + public string FuncaptchaApiJSSubdomain { get; set; } - public FunCaptchaTask() - { - Type = "FunCaptchaTask"; - } + public FunCaptchaTask() + { + Type = "FunCaptchaTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs index 2b0751a..99585db 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; + +internal class GeeTestTask : CapSolverTask { - internal class GeeTestTask : CapSolverTask - { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } - public string? GeetestApiServerSubdomain { get; set; } + public string WebsiteURL { get; set; } + public string Gt { get; set; } + public string Challenge { get; set; } + public string? GeetestApiServerSubdomain { get; set; } - public GeeTestTask() - { - Type = "GeeTestTask"; - } + public GeeTestTask() + { + Type = "GeeTestTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs index 3094fad..d51f8ef 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs @@ -1,13 +1,12 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; + +internal class HCaptchaTask : CapSolverTask { - internal class HCaptchaTask : CapSolverTask - { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public string WebsiteURL { get; set; } - public HCaptchaTask() - { - Type = "HCaptchaTask"; - } + public HCaptchaTask() + { + Type = "HCaptchaTask"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs index bd9db79..df688bc 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; + +internal class RecaptchaV2Task : CapSolverTask { - internal class RecaptchaV2Task : CapSolverTask - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public bool IsInvisible { get; set; } - public string RecaptchaDataSValue { get; set; } = string.Empty; + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public bool IsInvisible { get; set; } + public string RecaptchaDataSValue { get; set; } = string.Empty; - public RecaptchaV2Task() - { - Type = "RecaptchaV2Task"; - } + public RecaptchaV2Task() + { + Type = "RecaptchaV2Task"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs index f923209..7e6a244 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs @@ -1,16 +1,15 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; + +internal class RecaptchaV3Task : CapSolverTask { - internal class RecaptchaV3Task : CapSolverTask - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string PageAction { get; set; } - public float MinScore { get; set; } - public bool IsEnterprise { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public string PageAction { get; set; } + public float MinScore { get; set; } + public bool IsEnterprise { get; set; } - public RecaptchaV3Task() - { - Type = "RecaptchaV3Task"; - } + public RecaptchaV3Task() + { + Type = "RecaptchaV3Task"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs index 7bf2228..47be30a 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs @@ -1,15 +1,14 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; + +internal class RecaptchaV2TaskProxyless : CapSolverTaskProxyless { - internal class RecaptchaV2TaskProxyless : CapSolverTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public bool IsInvisible { get; set; } - public string RecaptchaDataSValue { get; set; } = string.Empty; + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public bool IsInvisible { get; set; } + public string RecaptchaDataSValue { get; set; } = string.Empty; - public RecaptchaV2TaskProxyless() - { - Type = "RecaptchaV2TaskProxyless"; - } + public RecaptchaV2TaskProxyless() + { + Type = "RecaptchaV2TaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs index cb0b11a..ea7aa78 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs @@ -1,16 +1,15 @@ -namespace CaptchaSharp.Models.CapSolver.Requests.Tasks +namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; + +internal class RecaptchaV3TaskProxyless : CapSolverTaskProxyless { - internal class RecaptchaV3TaskProxyless : CapSolverTaskProxyless - { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string PageAction { get; set; } - public float MinScore { get; set; } - public bool IsEnterprise { get; set; } + public string WebsiteURL { get; set; } + public string WebsiteKey { get; set; } + public string PageAction { get; set; } + public float MinScore { get; set; } + public bool IsEnterprise { get; set; } - public RecaptchaV3TaskProxyless() - { - Type = "RecaptchaV3TaskProxyless"; - } + public RecaptchaV3TaskProxyless() + { + Type = "RecaptchaV3TaskProxyless"; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Responses/Response.cs b/CaptchaSharp/Models/CapSolver/Responses/Response.cs index 1c56e76..3e54030 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Response.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Response.cs @@ -1,14 +1,13 @@ using Newtonsoft.Json; -namespace CaptchaSharp.Models.CapSolver.Responses +namespace CaptchaSharp.Models.CapSolver.Responses; + +internal class Response { - internal class Response - { - public int ErrorId { get; set; } - public string? ErrorCode { get; set; } - public string? ErrorDescription { get; set; } + public int ErrorId { get; set; } + public string? ErrorCode { get; set; } + public string? ErrorDescription { get; set; } - [JsonIgnore] - public bool IsError => ErrorId > 0; - } -} + [JsonIgnore] + public bool IsError => ErrorId > 0; +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs index 708c67f..d237409 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs @@ -1,18 +1,17 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; + +internal class FuncaptchaSolution : Solution { - internal class FuncaptchaSolution : Solution - { - public string Token { get; set; } + public string Token { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse { - return new StringResponse - { - Id = id, - Response = Token - }; - } + Id = id, + Response = Token + }; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs index af57670..b6f6029 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs @@ -1,22 +1,21 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; + +internal class GeeTestSolution : Solution { - internal class GeeTestSolution : Solution - { - public string Challenge { get; set; } - public string Validate { get; set; } - public string SecCode { get; set; } + public string Challenge { get; set; } + public string Validate { get; set; } + public string SecCode { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new GeeTestResponse() { - return new GeeTestResponse() - { - Id = id, - Challenge = Challenge, - Validate = Validate, - SecCode = SecCode - }; - } + Id = id, + Challenge = Challenge, + Validate = Validate, + SecCode = SecCode + }; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs index db2f4cc..81b9bbd 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs @@ -1,19 +1,18 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; + +internal class ImageCaptchaSolution : Solution { - internal class ImageCaptchaSolution : Solution - { - public string Text { get; set; } - public string Url { get; set; } + public string Text { get; set; } + public string Url { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse { - return new StringResponse - { - Id = id, - Response = Text - }; - } + Id = id, + Response = Text + }; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs index 522d7bc..7bae839 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs @@ -1,18 +1,17 @@ using CaptchaSharp.Models; -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; + +internal class RecaptchaSolution : Solution { - internal class RecaptchaSolution : Solution - { - public string GRecaptchaResponse { get; set; } + public string GRecaptchaResponse { get; set; } - public override CaptchaResponse ToCaptchaResponse(string id) + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse { - return new StringResponse - { - Id = id, - Response = GRecaptchaResponse - }; - } + Id = id, + Response = GRecaptchaResponse + }; } -} +} \ No newline at end of file diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/Solution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/Solution.cs index 7e34be5..e1f0a88 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/Solution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/Solution.cs @@ -1,13 +1,12 @@ using CaptchaSharp.Models; using System; -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; + +internal class Solution { - internal class Solution + public virtual CaptchaResponse ToCaptchaResponse(string id) { - public virtual CaptchaResponse ToCaptchaResponse(string id) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } -} +} \ No newline at end of file From a98b356bdc64782730c094ad6f020756f3b92539 Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 15:48:58 +0200 Subject: [PATCH 34/67] Fixed linter errors --- CaptchaSharp/CaptchaSharp.xml | 108 +++++++++--------- .../Requests/Tasks/FunCaptchaTaskProxyless.cs | 8 +- .../Requests/Tasks/GeeTestTaskProxyless.cs | 10 +- .../Requests/Tasks/HCaptchaTaskProxyless.cs | 6 +- .../Requests/Tasks/Proxied/FunCaptchaTask.cs | 8 +- .../Requests/Tasks/Proxied/GeeTestTask.cs | 10 +- .../Requests/Tasks/Proxied/HCaptchaTask.cs | 6 +- .../Proxied/RecaptchaV2EnterpriseTask.cs | 8 +- .../Requests/Tasks/Proxied/RecaptchaV2Task.cs | 6 +- .../Requests/Tasks/Proxied/TurnstileTask.cs | 8 +- .../RecaptchaV2EnterpriseTaskProxyless.cs | 8 +- .../Tasks/RecaptchaV3TaskProxyless.cs | 8 +- .../Responses/AntiCaptchaResponse.cs | 17 +++ .../FuncaptchaAntiCaptchaTaskSolution.cs | 10 +- .../GeeTestAntiCaptchaTaskSolution.cs | 18 ++- .../TaskCreationAntiCaptchaResponse.cs | 6 + .../Requests/Tasks/CustomTaskProxyless.cs | 2 +- .../Requests/Tasks/CapSolverTaskProxyless.cs | 2 +- .../Requests/Tasks/FunCaptchaTaskProxyless.cs | 8 +- .../Requests/Tasks/GeeTestTaskProxyless.cs | 8 +- .../Requests/Tasks/HCaptchaTaskProxyless.cs | 6 +- .../Requests/Tasks/Proxied/DataDomeTask.cs | 6 +- .../Requests/Tasks/Proxied/FunCaptchaTask.cs | 8 +- .../Requests/Tasks/Proxied/GeeTestTask.cs | 8 +- .../Requests/Tasks/Proxied/HCaptchaTask.cs | 6 +- .../Proxied/RecaptchaV2EnterpriseTask.cs | 4 +- .../Requests/Tasks/Proxied/RecaptchaV2Task.cs | 8 +- .../Requests/Tasks/Proxied/RecaptchaV3Task.cs | 8 +- .../RecaptchaV2EnterpriseTaskProxyless.cs | 4 +- .../Tasks/RecaptchaV2TaskProxyless.cs | 8 +- .../Tasks/RecaptchaV3TaskProxyless.cs | 8 +- .../Responses/Solutions/DataDomeSolution.cs | 8 +- .../Responses/Solutions/FuncaptchaSolution.cs | 10 +- .../Responses/Solutions/GeeTestSolution.cs | 20 ++-- .../Solutions/ImageCaptchaSolution.cs | 12 +- .../Responses/Solutions/RecaptchaSolution.cs | 10 +- .../TwoCaptcha/TwoCaptchaCapyResponse.cs | 8 +- .../TwoCaptchaCloudflareTurnstileResponse.cs | 3 +- .../TwoCaptcha/TwoCaptchaGeeTestResponse.cs | 22 ++-- CaptchaSharp/Services/AntiCaptchaService.cs | 17 +-- .../Services/BestCaptchaSolverService.cs | 32 +++--- CaptchaSharp/Services/CapMonsterService.cs | 4 +- CaptchaSharp/Services/CapSolverService.cs | 32 +++--- CaptchaSharp/Services/CaptchaCoderService.cs | 7 +- CaptchaSharp/Services/CaptchaService.cs | 41 ++++++- .../Services/DeathByCaptchaService.cs | 37 +++--- CaptchaSharp/Services/ImageTyperzService.cs | 39 +++---- .../Services/MetaBypassTechService.cs | 30 ++--- CaptchaSharp/Services/NineKwService.cs | 30 ++--- CaptchaSharp/Services/NopechaService.cs | 27 ++--- CaptchaSharp/Services/TrueCaptchaService.cs | 20 ++-- CaptchaSharp/Services/TwoCaptchaService.cs | 12 +- 52 files changed, 365 insertions(+), 395 deletions(-) diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 2ad21a6..6cf2a89 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -460,6 +460,41 @@ The type of the task. + + + Represents the response from the AntiCaptcha API. + + + + + The ID of the error. + + + + + The error code. + + + + + The error description. + + + + + Whether the response is an error. + + + + + Represents the response from the AntiCaptcha API when creating a task. + + + + + The ID of the created task. + + A generic captcha response. @@ -680,11 +715,6 @@ Your secret api key. - - - The default used for requests. - - The ID of the software developer. @@ -767,11 +797,6 @@ Your secret api key. - - - The default used for requests. - - Initializes a new . @@ -854,11 +879,6 @@ Your secret api key. - - - The default used for requests. - - The ID of the app. @@ -932,9 +952,6 @@ Your secret api key. - - The default used for requests. - Initializes a . @@ -969,6 +986,16 @@ Returns a list of flags that denote the capabilities of the service in terms of additional parameters to provide when solving text or image based captchas. + + + The default used for requests. + + + + + Initializes a with a custom . + + Retrieves the remaining balance in USD as a . Thrown when the provided credentials are invalid. @@ -1284,6 +1311,14 @@ + + + + + + Disposes the if it was created by this instance. + + The service provided by https://captchas.io/ @@ -1344,11 +1379,6 @@ Your DeathByCaptcha account password. - - - The default used for requests. - - Initializes a . @@ -1416,11 +1446,6 @@ Your secret api key. - - - The default used for requests. - - The ID of the software developer. @@ -1494,11 +1519,6 @@ The password. - - - The default used for requests. - - The current access token. @@ -1551,11 +1571,6 @@ Your secret api key. - - - The default used for requests. - - Initializes a . @@ -1623,11 +1638,6 @@ Your secret api key. - - - The default used for requests. - - Initializes a . @@ -1683,11 +1693,6 @@ Your secret api key. - - - The default used for requests. - - Initializes a . @@ -1715,11 +1720,6 @@ Your secret api key. - - - The default used for requests. - - Set it to false if the service does not support json responses. @@ -1738,7 +1738,7 @@ Initializes a . The API key to use. - The to use for requests. If null, a default one will be created. + The to use for requests. If null, a default one will be created. diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs index 07c6bc1..dd0dc2b 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs @@ -2,12 +2,12 @@ internal class FunCaptchaTaskProxyless : AntiCaptchaTaskProxyless { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsitePublicKey { get; set; } + public string? FuncaptchaApiJSSubdomain { get; set; } public FunCaptchaTaskProxyless() { Type = "FunCaptchaTaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs index 824eb08..50d24cf 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs @@ -2,13 +2,13 @@ internal class GeeTestTaskProxyless : AntiCaptchaTaskProxyless { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } - public string GeetestApiServerSubdomain { get; set; } + public string? WebsiteURL { get; set; } + public string? Gt { get; set; } + public string? Challenge { get; set; } + public string? GeetestApiServerSubdomain { get; set; } public GeeTestTaskProxyless() { Type = "GeeTestTaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs index 652c200..a1e8f05 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs @@ -2,11 +2,11 @@ internal class HCaptchaTaskProxyless : AntiCaptchaTaskProxyless { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } public HCaptchaTaskProxyless() { Type = "HCaptchaTaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs index 00ccec7..79e7e5d 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs @@ -2,12 +2,12 @@ internal class FunCaptchaTask : AntiCaptchaTask { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsitePublicKey { get; set; } + public string? FuncaptchaApiJSSubdomain { get; set; } public FunCaptchaTask() { Type = "FunCaptchaTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs index 4795fa8..d1ac0f2 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs @@ -2,13 +2,13 @@ internal class GeeTestTask : AntiCaptchaTask { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } - public string GeetestApiServerSubdomain { get; set; } + public string? WebsiteURL { get; set; } + public string? Gt { get; set; } + public string? Challenge { get; set; } + public string? GeetestApiServerSubdomain { get; set; } public GeeTestTask() { Type = "GeeTestTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs index 1938973..1f7458e 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs @@ -2,11 +2,11 @@ internal class HCaptchaTask : AntiCaptchaTask { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } public HCaptchaTask() { Type = "HCaptchaTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs index 8db8cd4..7643cee 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs @@ -4,12 +4,12 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; internal class RecaptchaV2EnterpriseTask : AntiCaptchaTask { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public Dictionary EnterprisePayload { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public Dictionary? EnterprisePayload { get; set; } public RecaptchaV2EnterpriseTask() { Type = "RecaptchaV2EnterpriseTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs index e2c1622..c97c9ef 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs @@ -2,12 +2,12 @@ internal class RecaptchaV2Task : AntiCaptchaTask { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } public bool IsInvisible { get; set; } public RecaptchaV2Task() { Type = "RecaptchaV2Task"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs index 325aa2d..2733bb3 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs @@ -2,10 +2,10 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks.Proxied; internal class TurnstileTask : AntiCaptchaTask { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } - public string Action { get; set; } - public string TurnstileCData { get; set; } + public string? WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + public string? Action { get; set; } + public string? TurnstileCData { get; set; } public TurnstileTask() { diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs index 03e8048..0973589 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs @@ -4,12 +4,12 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests.Tasks; internal class RecaptchaV2EnterpriseTaskProxyless : AntiCaptchaTaskProxyless { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public Dictionary EnterprisePayload { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public Dictionary? EnterprisePayload { get; set; } public RecaptchaV2EnterpriseTaskProxyless() { Type = "RecaptchaV2EnterpriseTaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs index cb5e9c0..6a49189 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs @@ -2,9 +2,9 @@ internal class RecaptchaV3TaskProxyless : AntiCaptchaTaskProxyless { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string PageAction { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? PageAction { get; set; } public float MinScore { get; set; } public bool IsEnterprise { get; set; } @@ -12,4 +12,4 @@ public RecaptchaV3TaskProxyless() { Type = "RecaptchaV3TaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/AntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/AntiCaptchaResponse.cs index 21e6088..c0705bd 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/AntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/AntiCaptchaResponse.cs @@ -2,12 +2,29 @@ namespace CaptchaSharp.Models.AntiCaptcha.Responses; +/// +/// Represents the response from the AntiCaptcha API. +/// public class AntiCaptchaResponse { + /// + /// The ID of the error. + /// public int ErrorId { get; set; } + + /// + /// The error code. + /// public string? ErrorCode { get; set; } + + /// + /// The error description. + /// public string? ErrorDescription { get; set; } + /// + /// Whether the response is an error. + /// [JsonIgnore] public bool IsError => ErrorId > 0; } diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs index 33ad31e..740c87d 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/FuncaptchaAntiCaptchaTaskSolution.cs @@ -1,17 +1,15 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; internal class FuncaptchaAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { - public string Token { get; set; } + public string? Token { get; set; } public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { Id = id, - Response = Token + Response = Token! }; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs index 308e504..b15dce8 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs @@ -1,21 +1,19 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; +namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; internal class GeeTestAntiCaptchaTaskSolution : AntiCaptchaTaskSolution { - public string Challenge { get; set; } - public string Validate { get; set; } - public string SecCode { get; set; } + public string? Challenge { get; set; } + public string? Validate { get; set; } + public string? SecCode { get; set; } public override CaptchaResponse ToCaptchaResponse(string id) { return new GeeTestResponse() { Id = id, - Challenge = Challenge, - Validate = Validate, - SecCode = SecCode + Challenge = Challenge!, + Validate = Validate!, + SecCode = SecCode! }; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs index 2ed0e5f..7ab0817 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/TaskCreationAntiCaptchaResponse.cs @@ -1,6 +1,12 @@ namespace CaptchaSharp.Models.AntiCaptcha.Responses; +/// +/// Represents the response from the AntiCaptcha API when creating a task. +/// public class TaskCreationAntiCaptchaResponse : AntiCaptchaResponse { + /// + /// The ID of the created task. + /// public int TaskId { get; set; } } diff --git a/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs index 26148d5..6c3c59a 100644 --- a/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs +++ b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/CustomTaskProxyless.cs @@ -6,7 +6,7 @@ namespace CaptchaSharp.Models.CapMonsterCloud.Requests.Tasks; internal abstract class CustomTaskProxyless : AntiCaptchaTaskProxyless { [JsonProperty("class")] - public string Class { get; set; } + public string? Class { get; set; } protected CustomTaskProxyless() { diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs index 0e139df..e49a4cc 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/CapSolverTaskProxyless.cs @@ -2,5 +2,5 @@ internal class CapSolverTaskProxyless { - public string Type { get; set; } + public string? Type { get; set; } } diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs index 9236743..d6eed28 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/FunCaptchaTaskProxyless.cs @@ -2,12 +2,12 @@ internal class FunCaptchaTaskProxyless : CapSolverTaskProxyless { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsitePublicKey { get; set; } + public string? FuncaptchaApiJSSubdomain { get; set; } public FunCaptchaTaskProxyless() { Type = "FunCaptchaTaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs index d83b9ad..d371eca 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/GeeTestTaskProxyless.cs @@ -2,9 +2,9 @@ internal class GeeTestTaskProxyless : CapSolverTaskProxyless { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } + public string? WebsiteURL { get; set; } + public string? Gt { get; set; } + public string? Challenge { get; set; } public string? GeetestApiServerSubdomain { get; set; } public int Version { get; set; } @@ -12,4 +12,4 @@ public GeeTestTaskProxyless() { Type = "GeeTestTaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs index 7e67d71..583d3ae 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/HCaptchaTaskProxyless.cs @@ -2,11 +2,11 @@ internal class HCaptchaTaskProxyless : CapSolverTaskProxyless { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } public HCaptchaTaskProxyless() { Type = "HCaptchaTaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs index caf6243..31fd7f9 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/DataDomeTask.cs @@ -2,11 +2,11 @@ internal class DataDomeTask : CapSolverTask { - public string WebsiteURL { get; set; } - public string CaptchaURL { get; set; } + public string? WebsiteURL { get; set; } + public string? CaptchaURL { get; set; } public DataDomeTask() { Type = "DatadomeSliderTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs index 9e2d355..8ba81e2 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/FunCaptchaTask.cs @@ -2,12 +2,12 @@ internal class FunCaptchaTask : CapSolverTask { - public string WebsiteURL { get; set; } - public string WebsitePublicKey { get; set; } - public string FuncaptchaApiJSSubdomain { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsitePublicKey { get; set; } + public string? FuncaptchaApiJSSubdomain { get; set; } public FunCaptchaTask() { Type = "FunCaptchaTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs index 99585db..190c80b 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/GeeTestTask.cs @@ -2,13 +2,13 @@ internal class GeeTestTask : CapSolverTask { - public string WebsiteURL { get; set; } - public string Gt { get; set; } - public string Challenge { get; set; } + public string? WebsiteURL { get; set; } + public string? Gt { get; set; } + public string? Challenge { get; set; } public string? GeetestApiServerSubdomain { get; set; } public GeeTestTask() { Type = "GeeTestTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs index d51f8ef..c871348 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/HCaptchaTask.cs @@ -2,11 +2,11 @@ internal class HCaptchaTask : CapSolverTask { - public string WebsiteKey { get; set; } - public string WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } public HCaptchaTask() { Type = "HCaptchaTask"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs index 93a595c..a0ce224 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs @@ -5,8 +5,8 @@ namespace CaptchaSharp.Models.CapSolver.Requests.Tasks.Proxied; internal class RecaptchaV2EnterpriseTask : CapSolverTask { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } [JsonProperty("enterprisePayload", NullValueHandling = NullValueHandling.Ignore)] public JObject? EnterprisePayload { get; set; } diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs index df688bc..0064dbd 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV2Task.cs @@ -2,13 +2,13 @@ internal class RecaptchaV2Task : CapSolverTask { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } public bool IsInvisible { get; set; } - public string RecaptchaDataSValue { get; set; } = string.Empty; + public string? RecaptchaDataSValue { get; set; } = string.Empty; public RecaptchaV2Task() { Type = "RecaptchaV2Task"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs index 7e6a244..82e29ee 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/RecaptchaV3Task.cs @@ -2,9 +2,9 @@ internal class RecaptchaV3Task : CapSolverTask { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string PageAction { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? PageAction { get; set; } public float MinScore { get; set; } public bool IsEnterprise { get; set; } @@ -12,4 +12,4 @@ public RecaptchaV3Task() { Type = "RecaptchaV3Task"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs index 200fad0..ef55b88 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs @@ -5,8 +5,8 @@ namespace CaptchaSharp.Models.CapSolver.Requests.Tasks; internal class RecaptchaV2EnterpriseTaskProxyless : CapSolverTaskProxyless { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } [JsonProperty("enterprisePayload", NullValueHandling = NullValueHandling.Ignore)] public JObject? EnterprisePayload { get; set; } diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs index 47be30a..79ce438 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV2TaskProxyless.cs @@ -2,13 +2,13 @@ internal class RecaptchaV2TaskProxyless : CapSolverTaskProxyless { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } public bool IsInvisible { get; set; } - public string RecaptchaDataSValue { get; set; } = string.Empty; + public string? RecaptchaDataSValue { get; set; } = string.Empty; public RecaptchaV2TaskProxyless() { Type = "RecaptchaV2TaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs index ea7aa78..fb35d8e 100644 --- a/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs +++ b/CaptchaSharp/Models/CapSolver/Requests/Tasks/RecaptchaV3TaskProxyless.cs @@ -2,9 +2,9 @@ internal class RecaptchaV3TaskProxyless : CapSolverTaskProxyless { - public string WebsiteURL { get; set; } - public string WebsiteKey { get; set; } - public string PageAction { get; set; } + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? PageAction { get; set; } public float MinScore { get; set; } public bool IsEnterprise { get; set; } @@ -12,4 +12,4 @@ public RecaptchaV3TaskProxyless() { Type = "RecaptchaV3TaskProxyless"; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/DataDomeSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/DataDomeSolution.cs index 69f0198..dabaf03 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/DataDomeSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/DataDomeSolution.cs @@ -1,17 +1,15 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; internal class DataDomeSolution : Solution { - public string Cookie { get; set; } + public string? Cookie { get; set; } public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { Id = id, - Response = Cookie + Response = Cookie! }; } } diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs index d237409..c8bcc45 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/FuncaptchaSolution.cs @@ -1,17 +1,15 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; internal class FuncaptchaSolution : Solution { - public string Token { get; set; } + public string? Token { get; set; } public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { Id = id, - Response = Token + Response = Token! }; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs index b6f6029..29f54f9 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/GeeTestSolution.cs @@ -1,21 +1,19 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; internal class GeeTestSolution : Solution { - public string Challenge { get; set; } - public string Validate { get; set; } - public string SecCode { get; set; } + public string? Challenge { get; set; } + public string? Validate { get; set; } + public string? SecCode { get; set; } public override CaptchaResponse ToCaptchaResponse(string id) { - return new GeeTestResponse() + return new GeeTestResponse { Id = id, - Challenge = Challenge, - Validate = Validate, - SecCode = SecCode + Challenge = Challenge!, + Validate = Validate!, + SecCode = SecCode! }; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs index 81b9bbd..2318b34 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/ImageCaptchaSolution.cs @@ -1,18 +1,16 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; internal class ImageCaptchaSolution : Solution { - public string Text { get; set; } - public string Url { get; set; } + public string? Text { get; set; } + public string? Url { get; set; } public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { Id = id, - Response = Text + Response = Text! }; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs b/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs index 7bae839..7b2359b 100644 --- a/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs +++ b/CaptchaSharp/Models/CapSolver/Responses/Solutions/RecaptchaSolution.cs @@ -1,17 +1,15 @@ -using CaptchaSharp.Models; - -namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; +namespace CaptchaSharp.Models.CapSolver.Responses.Solutions; internal class RecaptchaSolution : Solution { - public string GRecaptchaResponse { get; set; } + public string? GRecaptchaResponse { get; set; } public override CaptchaResponse ToCaptchaResponse(string id) { return new StringResponse { Id = id, - Response = GRecaptchaResponse + Response = GRecaptchaResponse! }; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs index 4165b50..9ebb126 100644 --- a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs @@ -15,12 +15,12 @@ internal class CapySolution public CapyResponse ToCapyResponse(string id) { - return new CapyResponse() + return new CapyResponse { Id = id, - CaptchaKey = CaptchaKey, - ChallengeKey = ChallengeKey, - Answer = Answer + CaptchaKey = CaptchaKey!, + ChallengeKey = ChallengeKey!, + Answer = Answer! }; } } diff --git a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs index 7d960f2..5d317e5 100644 --- a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCloudflareTurnstileResponse.cs @@ -1,4 +1,3 @@ -using CaptchaSharp.Models; using Newtonsoft.Json; namespace CaptchaSharp.Models.TwoCaptcha; @@ -16,7 +15,7 @@ public CloudflareTurnstileResponse ToCloudflareTurnstileResponse(string id) return new CloudflareTurnstileResponse() { Id = id, - Response = Request, + Response = Request!, UserAgent = UserAgent }; } diff --git a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs index 9040201..c2d1db5 100644 --- a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs @@ -5,28 +5,28 @@ namespace CaptchaSharp.Models.TwoCaptcha; internal class TwoCaptchaGeeTestResponse : TwoCaptchaResponse { - public new GeeTestSolution Request { get; set; } + public new GeeTestSolution? Request { get; set; } } internal class GeeTestSolution { - [JsonProperty(PropertyName = "geetest_challenge")] - public string Challenge { get; set; } + [JsonProperty("geetest_challenge")] + public string? Challenge { get; set; } - [JsonProperty(PropertyName = "geetest_validate")] - public string Validate { get; set; } + [JsonProperty("geetest_validate")] + public string? Validate { get; set; } - [JsonProperty(PropertyName = "geetest_seccode")] - public string Seccode { get; set; } + [JsonProperty("geetest_seccode")] + public string? SecCode { get; set; } public GeeTestResponse ToGeeTestResponse(string id) { - return new GeeTestResponse() + return new GeeTestResponse { Id = id, - Challenge = Challenge, - Validate = Validate, - SecCode = Seccode + Challenge = Challenge!, + Validate = Validate!, + SecCode = SecCode! }; } } diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index 7cc2b13..8a19a5a 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -26,11 +26,6 @@ public class AntiCaptchaService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - protected readonly HttpClient HttpClient; - /// /// The ID of the software developer. /// @@ -41,10 +36,9 @@ public class AntiCaptchaService : CaptchaService /// /// Your secret api key. /// The to use for requests. If null, a default one will be created. - public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) + public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - this.HttpClient = httpClient ?? new HttpClient(); this.HttpClient.BaseAddress = new Uri("https://api.anti-captcha.com"); } @@ -110,7 +104,7 @@ public override async Task SolveRecaptchaV2Async( if (!string.IsNullOrEmpty(dataS)) { - ((RecaptchaV2EnterpriseTask)content.Task).EnterprisePayload.Add("s", dataS); + ((RecaptchaV2EnterpriseTask)content.Task).EnterprisePayload?.Add("s", dataS); } } else @@ -124,7 +118,7 @@ public override async Task SolveRecaptchaV2Async( if (!string.IsNullOrEmpty(dataS)) { - ((RecaptchaV2EnterpriseTaskProxyless)content.Task).EnterprisePayload.Add("s", dataS); + ((RecaptchaV2EnterpriseTaskProxyless)content.Task).EnterprisePayload?.Add("s", dataS); } } } @@ -165,10 +159,7 @@ public override async Task SolveRecaptchaV3Async( string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - if (minScore != 0.3f && minScore != 0.7f && minScore != 0.9f) - { - throw new NotSupportedException("Only min scores of 0.3, 0.7 and 0.9 are supported"); - } + // Only min scores of 0.3, 0.7 and 0.9 are supported var content = CreateTaskRequest(); diff --git a/CaptchaSharp/Services/BestCaptchaSolverService.cs b/CaptchaSharp/Services/BestCaptchaSolverService.cs index 2302b9c..3e9562b 100644 --- a/CaptchaSharp/Services/BestCaptchaSolverService.cs +++ b/CaptchaSharp/Services/BestCaptchaSolverService.cs @@ -21,11 +21,6 @@ public class BestCaptchaSolverService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - private const string _affiliateId = "5e95fff9fe5f8247ff965ac3"; /// @@ -33,18 +28,17 @@ public class BestCaptchaSolverService : CaptchaService /// /// The API key to use. /// The to use for requests. If null, a default one will be created. - public BestCaptchaSolverService(string apiKey, HttpClient? httpClient = null) + public BestCaptchaSolverService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - _httpClient = httpClient ?? new HttpClient(); - _httpClient.BaseAddress = new Uri("https://bcsapi.xyz/api/"); + HttpClient.BaseAddress = new Uri("https://bcsapi.xyz/api/"); } #region Getting the Balance /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "user/balance", new StringPairCollection() .Add("access_token", ApiKey), @@ -84,7 +78,7 @@ public override async Task SolveImageCaptchaAsync( MaxLength = options?.MaxLength }; - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/image", payload, cancellationToken: cancellationToken) @@ -119,7 +113,7 @@ public override async Task SolveRecaptchaV2Async( payload.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/recaptcha", payload, cancellationToken: cancellationToken) @@ -148,7 +142,7 @@ public override async Task SolveRecaptchaV3Async( payload.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/recaptcha", payload, cancellationToken: cancellationToken) @@ -175,7 +169,7 @@ public override async Task SolveFuncaptchaAsync( payload.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/funcaptcha", payload, cancellationToken: cancellationToken) @@ -201,7 +195,7 @@ public override async Task SolveHCaptchaAsync( payload.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/hcaptcha", payload, cancellationToken: cancellationToken) @@ -229,7 +223,7 @@ public override async Task SolveGeeTestAsync( payload.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/geetest", payload, cancellationToken: cancellationToken) @@ -254,7 +248,7 @@ public override async Task SolveCapyAsync( payload.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/capy", payload, cancellationToken: cancellationToken) @@ -282,7 +276,7 @@ public override async Task SolveCloudflareTurnstile payload.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "captcha/turnstile", payload, cancellationToken: cancellationToken) @@ -315,7 +309,7 @@ private async Task GetResult( protected override async Task CheckResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var json = await _httpClient.GetStringAsync( + var json = await HttpClient.GetStringAsync( $"captcha/{task.Id}", new StringPairCollection() .Add("access_token", ApiKey), @@ -424,7 +418,7 @@ public override async Task ReportSolution( "BestCaptchaSolver does not support reporting correct solutions."); } - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( $"captcha/bad/{id}", new BcsRequest { diff --git a/CaptchaSharp/Services/CapMonsterService.cs b/CaptchaSharp/Services/CapMonsterService.cs index f824ae8..351ec27 100644 --- a/CaptchaSharp/Services/CapMonsterService.cs +++ b/CaptchaSharp/Services/CapMonsterService.cs @@ -32,8 +32,8 @@ public override async Task SolveImageCaptchaAsync base64 = "base64," + base64; } - var response = await HttpClient.PostToStringAsync - ("in.php", + var response = await HttpClient.PostToStringAsync( + "in.php", new StringPairCollection() .Add("key", ApiKey) .Add("method", "base64") diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index 8bab5d6..fa7b5c5 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -25,11 +25,6 @@ public class CapSolverService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - /// /// The ID of the app. /// @@ -40,18 +35,17 @@ public class CapSolverService : CaptchaService /// /// Your secret api key. /// The to use for requests. If null, a default one will be created. - public CapSolverService(string apiKey, HttpClient? httpClient = null) + public CapSolverService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - this._httpClient = httpClient ?? new HttpClient(); - this._httpClient.BaseAddress = new Uri("https://api.capsolver.com"); + HttpClient.BaseAddress = new Uri("https://api.capsolver.com"); } #region Getting the Balance /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "getBalance", new Request { ClientKey = ApiKey }, cancellationToken: cancellationToken) @@ -72,7 +66,7 @@ public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "createTask", new CaptchaTaskRequest { @@ -158,7 +152,7 @@ public override async Task SolveRecaptchaV2Async( } } - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) @@ -198,7 +192,7 @@ public override async Task SolveRecaptchaV3Async( }.SetProxy(proxy); } - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) @@ -239,7 +233,7 @@ public override async Task SolveFuncaptchaAsync( }; } - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) @@ -273,7 +267,7 @@ public override async Task SolveHCaptchaAsync( }; } - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) @@ -312,7 +306,7 @@ public override async Task SolveGeeTestAsync( }; } - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) @@ -340,7 +334,7 @@ public override async Task SolveDataDomeAsync( CaptchaURL = captchaUrl }.SetProxy(proxy); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) @@ -368,7 +362,7 @@ public override async Task SolveCloudflareTurnstile } }; - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "createTask", content, cancellationToken: cancellationToken) @@ -401,7 +395,7 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "getTaskResult", new GetTaskResultRequest { ClientKey = ApiKey, TaskId = task.Id }, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -448,7 +442,7 @@ private async Task GetResult( /// public override async Task ReportSolution(string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "feedbackTask", new CaptchaTaskFeedbackRequest { diff --git a/CaptchaSharp/Services/CaptchaCoderService.cs b/CaptchaSharp/Services/CaptchaCoderService.cs index 4324ab5..f3e85d4 100644 --- a/CaptchaSharp/Services/CaptchaCoderService.cs +++ b/CaptchaSharp/Services/CaptchaCoderService.cs @@ -20,17 +20,12 @@ public class CaptchaCoderService : CaptchaService /// public string ApiKey { get; set; } - /// The default used for requests. - protected readonly HttpClient HttpClient; - /// /// Initializes a . /// - public CaptchaCoderService(string apiKey, HttpClient? httpClient = null) + public CaptchaCoderService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - this.HttpClient = httpClient ?? new HttpClient(); - this.HttpClient.BaseAddress = new Uri("http://api.captchacoder.com/"); // Since this service replies directly with the solution to the task creation request diff --git a/CaptchaSharp/Services/CaptchaService.cs b/CaptchaSharp/Services/CaptchaService.cs index af53d08..e98f259 100644 --- a/CaptchaSharp/Services/CaptchaService.cs +++ b/CaptchaSharp/Services/CaptchaService.cs @@ -2,13 +2,14 @@ using CaptchaSharp.Exceptions; using CaptchaSharp.Models; using System; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace CaptchaSharp.Services; /// Abstract class for a generic captcha solving service. -public abstract class CaptchaService +public abstract class CaptchaService : IDisposable { /// The maximum allowed time for captcha completion. /// If this is exceeded, a is thrown. @@ -20,6 +21,22 @@ public abstract class CaptchaService /// Returns a list of flags that denote the capabilities of the service in terms of additional /// parameters to provide when solving text or image based captchas. public virtual CaptchaServiceCapabilities Capabilities => CaptchaServiceCapabilities.None; + + /// + /// The default used for requests. + /// + protected readonly HttpClient HttpClient; + + private readonly bool _disposeHttpClient; + + /// + /// Initializes a with a custom . + /// + protected CaptchaService(HttpClient? httpClient = null) + { + HttpClient = httpClient ?? new HttpClient(); + _disposeHttpClient = httpClient is null; + } /// Retrieves the remaining balance in USD as a . /// Thrown when the provided credentials are invalid. @@ -399,7 +416,7 @@ protected async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : CaptchaResponse { - var start = DateTime.Now; + var start = DateTime.UtcNow; T? result; // Initial delay @@ -412,7 +429,7 @@ protected async Task GetResult( result = await CheckResult(task, cancellationToken); await Task.Delay(PollingInterval, cancellationToken); } - while (!task.Completed && DateTime.Now - start < Timeout); + while (!task.Completed && DateTime.UtcNow - start < Timeout); if (!task.Completed || result is null) { @@ -429,4 +446,22 @@ protected async Task GetResult( { throw new NotImplementedException(); } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the if it was created by this instance. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing && _disposeHttpClient) + { + HttpClient.Dispose(); + } + } } diff --git a/CaptchaSharp/Services/DeathByCaptchaService.cs b/CaptchaSharp/Services/DeathByCaptchaService.cs index d0d9176..6a75600 100644 --- a/CaptchaSharp/Services/DeathByCaptchaService.cs +++ b/CaptchaSharp/Services/DeathByCaptchaService.cs @@ -32,11 +32,6 @@ public class DeathByCaptchaService : CaptchaService /// public string Password { get; set; } - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - /* * Sometimes the DBC API randomly replies with query strings even when json is requested, so * we will avoid using the Accept: application/json header. @@ -48,20 +43,18 @@ public class DeathByCaptchaService : CaptchaService /// Your DeathByCaptcha account name. /// Your DeathByCaptcha account password. /// The used for requests. If null, a default one will be created. - public DeathByCaptchaService(string username, string password, HttpClient? httpClient = null) + public DeathByCaptchaService(string username, string password, HttpClient? httpClient = null) : base(httpClient) { Username = username; Password = password; - this._httpClient = httpClient ?? new HttpClient(); - - this._httpClient.BaseAddress = new Uri("http://api.dbcapi.me/api/"); + HttpClient.BaseAddress = new Uri("http://api.dbcapi.me/api/"); } #region Getting the Balance /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "user", GetAuthPair(), cancellationToken: cancellationToken) @@ -91,7 +84,7 @@ public override async Task GetBalanceAsync(CancellationToken cancellati public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("captchafile", $"base64:{base64}") @@ -126,7 +119,7 @@ public override async Task SolveRecaptchaV2Async( }; } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 4) @@ -167,7 +160,7 @@ public override async Task SolveRecaptchaV3Async( }; } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 5) @@ -204,7 +197,7 @@ public override async Task SolveFuncaptchaAsync( }; } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 6) @@ -241,7 +234,7 @@ public override async Task SolveHCaptchaAsync( }; } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 7) @@ -284,7 +277,7 @@ public override async Task SolveKeyCaptchaAsync( }; } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 10) @@ -323,7 +316,7 @@ public override async Task SolveGeeTestAsync( }; } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 8) @@ -360,7 +353,7 @@ public override async Task SolveCapyAsync( }; } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 15) @@ -394,7 +387,7 @@ public override async Task SolveDataDomeAsync( CaptchaUrl = captchaUrl }.SetProxy(proxy); - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 21) @@ -425,7 +418,7 @@ public override async Task SolveCloudflareTurnstile Action = action, }.SetProxy(proxy); - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( "captcha", GetAuthPair() .Add("type", 12) @@ -466,7 +459,7 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var response = await _httpClient.GetAsync($"captcha/{task.Id}", cancellationToken); + var response = await HttpClient.GetAsync($"captcha/{task.Id}", cancellationToken); var query = HttpUtility.ParseQueryString(await DecodeIsoResponse(response)); var text = query["text"]; @@ -535,7 +528,7 @@ public override async Task ReportSolution( throw new NotSupportedException("This service doesn't allow reporting of good solutions"); } - var response = await _httpClient.PostAsync( + var response = await HttpClient.PostAsync( $"captcha/{id}/report", GetAuthPair(), cancellationToken: cancellationToken) diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index db3a69a..994dd27 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -23,11 +23,6 @@ public class ImageTyperzService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - /// /// The ID of the software developer. /// @@ -38,23 +33,21 @@ public class ImageTyperzService : CaptchaService /// /// Your secret api key. /// The to use for requests. If null, a default one will be created. - public ImageTyperzService(string apiKey, HttpClient? httpClient = null) + public ImageTyperzService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - this._httpClient = httpClient ?? new HttpClient(); - - this._httpClient.BaseAddress = new Uri("http://captchatypers.com"); + HttpClient.BaseAddress = new Uri("http://captchatypers.com"); // Since this service replies directly with the solution to the task creation request (for image captchas) // we need to set a high timeout here, or it will not finish in time - this._httpClient.Timeout = Timeout; + HttpClient.Timeout = Timeout; } #region Getting the Balance /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( "Forms/RequestBalanceToken.ashx", GetAuthPair() .Add("action", "REQUESTBALANCE"), @@ -75,7 +68,7 @@ public override async Task GetBalanceAsync(CancellationToken cancellati public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( "Forms/UploadFileAndGetTextNEWToken.ashx", GetAuthAffiliatePair() .Add("action", "UPLOADCAPTCHA") @@ -98,7 +91,7 @@ public override async Task SolveRecaptchaV2Async( string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", GetAuthAffiliatePair() .Add("action", "UPLOADCAPTCHA") @@ -119,7 +112,7 @@ public override async Task SolveRecaptchaV3Async (string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( enterprise ? "captchaapi/UploadRecaptchaEnt.ashx" : "captchaapi/UploadRecaptchaToken.ashx", GetAuthAffiliatePair() .Add("action", "UPLOADCAPTCHA") @@ -142,7 +135,7 @@ public override async Task SolveFuncaptchaAsync( string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( "captchaapi/UploadFunCaptcha.ashx", GetAuthAffiliatePair() .Add("action", "UPLOADCAPTCHA") @@ -163,7 +156,7 @@ public override async Task SolveHCaptchaAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( "captchaapi/UploadHCaptchaUser.ashx", GetAuthAffiliatePair() .Add("captchatype", 11) @@ -183,7 +176,7 @@ public override async Task SolveGeeTestAsync( string gt, string challenge, string siteUrl, string? apiServer = null, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync( + var response = await HttpClient.GetStringAsync( "captchaapi/UploadGeeTestToken.ashx", GetAuthAffiliatePair() .Add("action", "UPLOADCAPTCHA") @@ -204,7 +197,7 @@ public override async Task SolveCapyAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( "captchaapi/UploadCapyCaptchaUser.ashx", GetAuthAffiliatePair() .Add("action", "UPLOADCAPTCHA") @@ -224,7 +217,7 @@ public override async Task SolveCloudflareTurnstile string siteKey, string siteUrl, string? action = null, string? data = null, string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( "captchaapi/Uploadturnstile.ashx", GetAuthAffiliatePair() .Add("action", "UPLOADCAPTCHA") @@ -269,7 +262,7 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var responseJson = await _httpClient.GetStringAsync( + var responseJson = await HttpClient.GetStringAsync( "captchaapi/GetCaptchaResponseJson.ashx", GetAuthPair() .Add("action", "GETTEXT") @@ -318,8 +311,8 @@ private async Task GetResult( } // TODO: Handle Capy response - - else if (typeof(T) == typeof(CloudflareTurnstileResponse)) + + if (typeof(T) == typeof(CloudflareTurnstileResponse)) { if (task.Type is not CaptchaType.CloudflareTurnstile) { @@ -351,7 +344,7 @@ private async Task GetResult( public override async Task ReportSolution( string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostToStringAsync( + var response = await HttpClient.PostToStringAsync( "Forms/SetBadImageToken.ashx", GetAuthPair() .Add("imageid", id) diff --git a/CaptchaSharp/Services/MetaBypassTechService.cs b/CaptchaSharp/Services/MetaBypassTechService.cs index 4254ea8..ed5a094 100644 --- a/CaptchaSharp/Services/MetaBypassTechService.cs +++ b/CaptchaSharp/Services/MetaBypassTechService.cs @@ -34,11 +34,6 @@ public class MetaBypassTechService : CaptchaService /// The password. /// public string Password { get; set; } - - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; /// /// The current access token. @@ -54,19 +49,18 @@ public class MetaBypassTechService : CaptchaService /// The password. /// The to use for requests. If null, a default one will be created. public MetaBypassTechService(string clientId, string clientSecret, - string username, string password, HttpClient? httpClient = null) + string username, string password, HttpClient? httpClient = null) : base(httpClient) { ClientId = clientId; ClientSecret = clientSecret; Username = username; Password = password; - _httpClient = httpClient ?? new HttpClient(); - _httpClient.BaseAddress = new Uri("https://app.metabypass.tech/CaptchaSolver/"); + HttpClient.BaseAddress = new Uri("https://app.metabypass.tech/CaptchaSolver/"); // Since some captchas are returned directly in the response body, // we need to set a high timeout to account for those requests - _httpClient.Timeout = Timeout; + HttpClient.Timeout = Timeout; } #region Getting the Balance @@ -76,7 +70,7 @@ public override async Task GetBalanceAsync( { await EnsureAccessTokenAsync().ConfigureAwait(false); - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "api/v1/me", new StringPairCollection(), cancellationToken) @@ -122,7 +116,7 @@ public override async Task SolveImageCaptchaAsync( MaxLength = options?.MaxLength ?? 0 }; - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "api/v1/services/captchaSolver", payload, cancellationToken: cancellationToken) @@ -163,7 +157,7 @@ public override async Task SolveRecaptchaV2Async( Version = "2" }; - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "api/v1/services/bypassReCaptcha", payload, cancellationToken: cancellationToken) @@ -202,7 +196,7 @@ public override async Task SolveRecaptchaV3Async( Version = "3" }; - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "api/v1/services/bypassReCaptcha", payload, cancellationToken: cancellationToken) @@ -234,7 +228,7 @@ public override async Task SolveRecaptchaV3Async( "The getCaptchaResult method is only supported for ReCaptchaV2 tasks"); } - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "api/v1/services/getCaptchaResult", new StringPairCollection() .Add("recaptcha_id", task.Id), @@ -285,7 +279,7 @@ private async Task GetAccessTokenAsync() Password = Password }; - using var response = await _httpClient.PostJsonAsync( + using var response = await HttpClient.PostJsonAsync( "oauth/token", payload, cancellationToken: default) @@ -302,7 +296,7 @@ private async Task GetAccessTokenAsync() } _accessToken = json.Deserialize(); - _httpClient.DefaultRequestHeaders.Add("Authorization", + HttpClient.DefaultRequestHeaders.Add("Authorization", $"{_accessToken.TokenType} {_accessToken.AccessToken}"); } @@ -316,7 +310,7 @@ private async Task RefreshAccessTokenAsync(MbtAccessTokenResponse tokenResponse) RefreshToken = tokenResponse.RefreshToken }; - using var response = await _httpClient.PostJsonAsync( + using var response = await HttpClient.PostJsonAsync( "oauth/token", payload, cancellationToken: default) @@ -333,7 +327,7 @@ private async Task RefreshAccessTokenAsync(MbtAccessTokenResponse tokenResponse) } _accessToken = json.Deserialize(); - _httpClient.DefaultRequestHeaders.Add("Authorization", + HttpClient.DefaultRequestHeaders.Add("Authorization", $"{_accessToken.TokenType} {_accessToken.AccessToken}"); } #endregion diff --git a/CaptchaSharp/Services/NineKwService.cs b/CaptchaSharp/Services/NineKwService.cs index c194b2e..1a052eb 100644 --- a/CaptchaSharp/Services/NineKwService.cs +++ b/CaptchaSharp/Services/NineKwService.cs @@ -21,32 +21,26 @@ public class NineKwService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - /// /// Initializes a . /// /// Your secret api key. /// The to use for requests. If null, a default one will be created. - public NineKwService(string apiKey, HttpClient? httpClient = null) + public NineKwService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - _httpClient = httpClient ?? new HttpClient(); - _httpClient.BaseAddress = new Uri("https://www.9kw.eu/"); + HttpClient.BaseAddress = new Uri("https://www.9kw.eu/"); // Since this service replies directly with the solution to the task creation request (for image captchas) // we need to set a high timeout here, or it will not finish in time - _httpClient.Timeout = Timeout; + HttpClient.Timeout = Timeout; } #region Getting the Balance /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaguthaben") @@ -69,7 +63,7 @@ public override async Task SolveTextCaptchaAsync( string text, TextCaptchaOptions? options = default, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartAsync( + var response = await HttpClient.PostMultipartAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -89,7 +83,7 @@ public override async Task SolveImageCaptchaAsync( string base64, ImageCaptchaOptions? options = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.PostMultipartAsync( + var response = await HttpClient.PostMultipartAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -115,7 +109,7 @@ public override async Task SolveRecaptchaV2Async( string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -137,7 +131,7 @@ public override async Task SolveRecaptchaV3Async( string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -159,7 +153,7 @@ public override async Task SolveFuncaptchaAsync( string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -181,7 +175,7 @@ public override async Task SolveHCaptchaAsync( string siteKey, string siteUrl, Proxy? proxy = null, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchaupload") @@ -219,7 +213,7 @@ private async Task GetResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchacorrectdata") @@ -261,7 +255,7 @@ private async Task GetResult( public override async Task ReportSolution( string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "index.cgi", GetAuthPair() .Add("action", "usercaptchacorrectback") diff --git a/CaptchaSharp/Services/NopechaService.cs b/CaptchaSharp/Services/NopechaService.cs index 6df2b84..076f508 100644 --- a/CaptchaSharp/Services/NopechaService.cs +++ b/CaptchaSharp/Services/NopechaService.cs @@ -22,22 +22,15 @@ public class NopechaService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - /// /// Initializes a . /// /// The API key to use. - /// The to use for requests. If null, a default one will be created. - public NopechaService(string apiKey, HttpClient? httpClient = null) + /// The to use for requests. If null, a default one will be created. + public NopechaService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - _httpClient = httpClient ?? new HttpClient(); - - _httpClient.BaseAddress = new Uri("https://api.nopecha.com"); + HttpClient.BaseAddress = new Uri("https://api.nopecha.com"); } #region Getting the Balance @@ -45,7 +38,7 @@ public NopechaService(string apiKey, HttpClient? httpClient = null) public override async Task GetBalanceAsync( CancellationToken cancellationToken = default) { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "status", new StringPairCollection() .Add("key", ApiKey), @@ -73,7 +66,7 @@ public override async Task SolveImageCaptchaAsync( ImageData = [base64] }; - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "", payload, cancellationToken: cancellationToken) @@ -102,7 +95,7 @@ public override async Task SolveRecaptchaV2Async( payload.SetProxy(proxy, siteUrl); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) @@ -132,7 +125,7 @@ public override async Task SolveRecaptchaV3Async( payload.SetProxy(proxy, siteUrl); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) @@ -157,7 +150,7 @@ public override async Task SolveHCaptchaAsync( payload.SetProxy(proxy, siteUrl); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) @@ -195,7 +188,7 @@ public override async Task SolveCloudflareTurnstile payload.SetProxy(proxy, siteUrl); - var response = await _httpClient.PostJsonAsync( + var response = await HttpClient.PostJsonAsync( "token", payload, cancellationToken: cancellationToken) @@ -226,7 +219,7 @@ private async Task GetResult( protected override async Task CheckResult( CaptchaTask task, CancellationToken cancellationToken = default) where T : class { - var response = await _httpClient.GetJsonAsync( + var response = await HttpClient.GetJsonAsync( "", new StringPairCollection() .Add("key", ApiKey) diff --git a/CaptchaSharp/Services/TrueCaptchaService.cs b/CaptchaSharp/Services/TrueCaptchaService.cs index 7b07c4a..cf3fbef 100644 --- a/CaptchaSharp/Services/TrueCaptchaService.cs +++ b/CaptchaSharp/Services/TrueCaptchaService.cs @@ -26,31 +26,25 @@ public class TrueCaptchaService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - private readonly HttpClient _httpClient; - /// /// Initializes a . /// /// Your user id. /// Your secret api key. - /// The to use for requests. If null, a default one will be created. - public TrueCaptchaService(string userId, string apiKey, HttpClient? httpClient = null) + /// The to use for requests. If null, a default one will be created. + public TrueCaptchaService(string userId, string apiKey, HttpClient? httpClient = null) : base(httpClient) { UserId = userId; ApiKey = apiKey; - this._httpClient = httpClient ?? new HttpClient(); - this._httpClient.BaseAddress = new Uri("https://api.apitruecaptcha.org/"); - this._httpClient.Timeout = Timeout; + this.HttpClient.BaseAddress = new Uri("https://api.apitruecaptcha.org/"); + this.HttpClient.Timeout = Timeout; } /// public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { - var response = await _httpClient.GetStringAsync( + var response = await HttpClient.GetStringAsync( "one/getbalance", new StringPairCollection() .Add("username", UserId) @@ -77,7 +71,7 @@ public override async Task SolveImageCaptchaAsync { "data", base64 } }; - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "one/gettext", content, camelizeKeys: false, @@ -108,7 +102,7 @@ public override async Task ReportSolution( { "request_id", id } }; - var response = await _httpClient.PostJsonToStringAsync( + var response = await HttpClient.PostJsonToStringAsync( "one/report_error", content, camelizeKeys: false, diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index b56f035..c820e95 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -23,11 +23,6 @@ public class TwoCaptchaService : CaptchaService /// public string ApiKey { get; set; } - /// - /// The default used for requests. - /// - protected readonly HttpClient HttpClient; - /// /// Set it to false if the service does not support json responses. /// @@ -46,12 +41,9 @@ public class TwoCaptchaService : CaptchaService /// Initializes a . /// The API key to use. /// The to use for requests. If null, a default one will be created. - public TwoCaptchaService(string apiKey, HttpClient? httpClient = null) + public TwoCaptchaService(string apiKey, HttpClient? httpClient = null) : base(httpClient) { ApiKey = apiKey; - HttpClient = httpClient ?? new HttpClient(); - - // TODO: Use https instead of http if possible HttpClient.BaseAddress = new Uri("http://2captcha.com"); } @@ -485,7 +477,7 @@ internal async Task GetResult( if (solution.Type == JTokenType.Object) { return response.Deserialize() - .Request.ToGeeTestResponse(task.Id) as T; + .Request?.ToGeeTestResponse(task.Id) as T; } } else if (task.Type == CaptchaType.Capy) From f12e05da1d0d9a8a0e91ea7a41c024b384c7c9da Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 16:46:42 +0200 Subject: [PATCH 35/67] Improved readme --- README.md | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index cbbc1cb..36e9285 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # CaptchaSharp -A .NET Standard 2.0 library that implements the APIs of the most used **captcha solving services** out there. -The library is fully documented, asynchronous and very easy to use. All services derive from the same `CaptchaService` class so you can allow users to use their favourite service without the need of a separate library for each one of them! +A .NET library that implements the APIs of the most used **captcha solving services** out there. +The library is fully documented, asynchronous and very easy to use. + +All services derive from the same `CaptchaService` class and have the same code API, so it's very easy to switch between services! ## Supported Captchas This library supports the following captcha types @@ -14,19 +16,15 @@ This library supports the following captcha types - GeeTest - Capy -Although this sounds very exciting, sadly not all captcha types are supported by the services. You can find a table of the supported captcha types for each major service below. +Not every captcha type is supported by each service. You can find a table of the supported captcha types for each major service at the following link -![Supported Captcha Types](https://i.imgur.com/7m8Qo3K.png) +[Captcha availability table](https://example.com) ## Adding CaptchaSharp to your project Simply install the nuget package via `Install-Package CaptchaSharp` -If you need more solvers you can install additional solvers (mostly ones that implement the 2captcha API) like this - -`Install-Package CaptchaSharp.Services.More` - ## Usage First of all, initialize your solver of choice by providing your credentials, for example ```csharp @@ -38,16 +36,16 @@ You can get your remaining balance like this decimal balance = await service.GetBalanceAsync(); ``` -If the provided credentials are wrong, the method above will return a `BadAuthenticationException` that you can catch and process in order to let the user know he needs to check his credentials. +If the provided credentials are wrong, the method above will return a `BadAuthenticationException`. -If the credentials are correct and the balance is greater than the minimum required for solving a captcha, we can proceed to solve a ReCaptchaV2 task (you will need to provide the required parameters found in the webpage source code). +If the credentials are correct and the balance is greater than the minimum required for solving a captcha, you can proceed to solve a captcha (e.g., a ReCaptchaV2 task) like this. ```csharp StringResponse solution = await service.SolveRecaptchaV2Async("site key", "site url"); ``` The method above can throw the following exceptions: -- `TaskCreationException` when the task could not be created for example due to zero balance or incorrect parameters. +- `TaskCreationException` when the task could not be created, for example due to zero balance or incorrect parameters. - `TaskSolutionException` when the task could not be completed, for example when an image captcha cannot be decoded. - `TimeoutException` when the captcha solution took too long to complete. @@ -57,28 +55,28 @@ You can configure a custom timeout like this service.Timeout = TimeSpan.FromMinutes(3); ``` -The returned `StringResponse` will contain two fields: +The returned solution will contain two fields: -An `Id` which you can use for reporting a bad solution (if the service supports it) like this -```csharp -await service.ReportSolution(solution.Id, CaptchaType.ReCaptchaV2); -``` -if the report failed, the method above will throw a `TaskReportException`. +- an `Id` which you can use for reporting a bad solution (if the service supports it) like this + ```csharp + await service.ReportSolution(solution.Id, CaptchaType.ReCaptchaV2); + ``` + if the report failed, the method above will throw a `TaskReportException`. -And finally your solution as plaintext -```csharp -Console.WriteLine($"The solution is {solution.Response}"); -``` -In addition, mind that not every service supports every type of captcha! If a method or specific parameters are not supported, a `NotSupportedException` will be thrown. +- your solution as plaintext + ```csharp + Console.WriteLine($"The solution is {solution.Response}"); + ``` + +If a method or some of its parameters are not supported, a `NotSupportedException` or `ArgumentException` will be thrown. ## Unit Tests -Unit tests for the major services are included in the `CaptchaSharp.Tests` project. In order to test, you need to: -1. Run any test once and let it fail. It will create a file called `config.json` in your `bin/Debug` folder. +Unit tests are included in the `CaptchaSharp.Tests` project. In order to test, you need to: +1. Run any test once and let it fail. It will create a file called `config.json` in your `bin/Debug/net8.0` folder. 2. Add your credentials to the `config.json` file. You can also add a proxy to test proxied endpoints. 3. Run the tests (one at a time, to avoid overloading the service and overspending). 4. If you need to test a captcha on a specific website, you can edit the `ServiceTests` class and change the parameters as you need. ## What needs to be improved -- Use C# anonymous types or `JObject` for the `AntiCaptchaSolver` instead of a million classes! - Implement better exception handling, for example when there is zero balance or when a solver method returns a bad authentication, they will currently fall in the generic `TaskCreationException` type. From 7d0421feb0d6d0a7ff6ecdaaaed63d1d7fefe4fe Mon Sep 17 00:00:00 2001 From: Ruri Date: Sun, 21 Jul 2024 17:22:42 +0200 Subject: [PATCH 36/67] Improved readme and added xlsx url --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 36e9285..dcdcffb 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ This library supports the following captcha types - GeeTest - Capy -Not every captcha type is supported by each service. You can find a table of the supported captcha types for each major service at the following link +Not every captcha type is supported by each service. You can find a spreadsheet with a breakdown of the supported captcha types for each implemented service at the following link -[Captcha availability table](https://example.com) +[CaptchaSharp Services Availability](https://1drv.ms/x/s!Al8HxSfx2JL3ePfRK23aUt34eCk?e=WNCPh9) ## Adding CaptchaSharp to your project Simply install the nuget package via @@ -71,6 +71,9 @@ The returned solution will contain two fields: If a method or some of its parameters are not supported, a `NotSupportedException` or `ArgumentException` will be thrown. +## The service I want to use is not implemented +If the service you want to use is not implemented, you can easily implement it yourself by deriving from the `CaptchaService` class and implementing the abstract methods, or you can open an issue, and we will try to implement it as soon as possible. + ## Unit Tests Unit tests are included in the `CaptchaSharp.Tests` project. In order to test, you need to: 1. Run any test once and let it fail. It will create a file called `config.json` in your `bin/Debug/net8.0` folder. @@ -79,4 +82,7 @@ Unit tests are included in the `CaptchaSharp.Tests` project. In order to test, y 4. If you need to test a captcha on a specific website, you can edit the `ServiceTests` class and change the parameters as you need. ## What needs to be improved -- Implement better exception handling, for example when there is zero balance or when a solver method returns a bad authentication, they will currently fall in the generic `TaskCreationException` type. +- Drop `Newtonsoft.Json` for `System.Text.Json` +- `StringPairCollection.Add()` should also take null values, and by default ignore the key-value pair if the value is null. +- Implement better exception handling for specific error codes. For example when there is zero balance or when a solver method returns a bad authentication, they will currently fall in the generic `TaskCreationException` type. +- Add support for recognition APIs (right now only token-based APIs are supported). From d263ec5467fe4612601111ca1cca14968b299f13 Mon Sep 17 00:00:00 2001 From: Ruri Date: Mon, 22 Jul 2024 00:40:16 +0200 Subject: [PATCH 37/67] Added EzCaptchaService with recaptcha v2 Other methods are coming --- CaptchaSharp.Tests/ConfigFixture.cs | 1 + CaptchaSharp.Tests/EzCaptchaServiceTests.cs | 30 +++ CaptchaSharp/CaptchaSharp.xml | 183 ++++++++++++--- .../{Request.cs => AntiCaptchaRequest.cs} | 2 +- ...st.cs => CaptchaTaskAntiCaptchaRequest.cs} | 2 +- ....cs => GetTaskResultAntiCaptchaRequest.cs} | 2 +- .../Requests/RecaptchaV2AntiCaptchaRequest.cs | 6 + .../Requests/RecaptchaV2CaptchaRequest.cs | 6 - ...s => ReportIncorrectAntiCaptchaRequest.cs} | 4 +- .../GetBalanceAntiCaptchaResponse.cs | 2 +- .../Solutions/AntiCaptchaTaskSolution.cs | 3 +- .../Requests/CaptchaTaskEzCaptchaRequest.cs | 28 +++ .../EzCaptcha/Requests/EzCaptchaRequest.cs | 12 + .../Requests/GetTaskResultEzCaptchaRequest.cs | 6 + .../Requests/Tasks/EzCaptchaTaskProxyless.cs | 13 ++ .../ReCaptchaV2EnterpriseTaskProxyless.cs | 13 ++ .../ReCaptchaV2SEnterpriseTaskProxyless.cs | 14 ++ .../Tasks/ReCaptchaV2STaskProxyless.cs | 14 ++ .../Tasks/ReCaptchaV2TaskProxyless.cs | 13 ++ .../EzCaptcha/Responses/EzCaptchaResponse.cs | 30 +++ .../Responses/GetBalanceEzCaptchaResponse.cs | 6 + .../GetTaskResultEzCaptchaResponse.cs | 12 + .../Solutions/EzCaptchaTaskSolution.cs | 11 + .../RecaptchaEzCaptchaTaskSolution.cs | 15 ++ .../TaskCreationEzCaptchaResponse.cs | 12 + CaptchaSharp/Services/AntiCaptchaService.cs | 34 +-- CaptchaSharp/Services/AzCaptchaService.cs | 2 +- .../Services/BestCaptchaSolverService.cs | 2 +- .../Services/CapMonsterCloudService.cs | 2 +- CaptchaSharp/Services/CapSolverService.cs | 2 +- CaptchaSharp/Services/CaptchaAiService.cs | 2 +- CaptchaSharp/Services/CaptchaCoderService.cs | 2 +- CaptchaSharp/Services/CaptchasIoService.cs | 2 +- .../Services/DeathByCaptchaService.cs | 2 +- CaptchaSharp/Services/EzCaptchaService.cs | 213 ++++++++++++++++++ CaptchaSharp/Services/HumanCoderService.cs | 2 +- CaptchaSharp/Services/ImageTyperzService.cs | 2 +- .../Services/MetaBypassTechService.cs | 2 +- CaptchaSharp/Services/NextCaptchaService.cs | 2 +- CaptchaSharp/Services/NineKwService.cs | 2 +- CaptchaSharp/Services/NoCaptchaAiService.cs | 2 +- CaptchaSharp/Services/NopechaService.cs | 2 +- CaptchaSharp/Services/RuCaptchaService.cs | 2 +- CaptchaSharp/Services/TrueCaptchaService.cs | 2 +- CaptchaSharp/Services/TwoCaptchaService.cs | 2 +- 45 files changed, 642 insertions(+), 81 deletions(-) create mode 100644 CaptchaSharp.Tests/EzCaptchaServiceTests.cs rename CaptchaSharp/Models/AntiCaptcha/Requests/{Request.cs => AntiCaptchaRequest.cs} (88%) rename CaptchaSharp/Models/AntiCaptcha/Requests/{CaptchaTaskRequest.cs => CaptchaTaskAntiCaptchaRequest.cs} (92%) rename CaptchaSharp/Models/AntiCaptcha/Requests/{GetTaskResultRequest.cs => GetTaskResultAntiCaptchaRequest.cs} (58%) create mode 100644 CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2AntiCaptchaRequest.cs delete mode 100644 CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs rename CaptchaSharp/Models/AntiCaptcha/Requests/{ReportIncorrectCaptchaRequest.cs => ReportIncorrectAntiCaptchaRequest.cs} (56%) create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/CaptchaTaskEzCaptchaRequest.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/EzCaptchaRequest.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/GetTaskResultEzCaptchaRequest.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/EzCaptchaTaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2EnterpriseTaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2SEnterpriseTaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2STaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2TaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Responses/EzCaptchaResponse.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Responses/GetBalanceEzCaptchaResponse.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Responses/GetTaskResultEzCaptchaResponse.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Responses/Solutions/EzCaptchaTaskSolution.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Responses/Solutions/RecaptchaEzCaptchaTaskSolution.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Responses/TaskCreationEzCaptchaResponse.cs create mode 100644 CaptchaSharp/Services/EzCaptchaService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index def3a47..1ad4261 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -62,4 +62,5 @@ public class Credentials public string NopechaApiKey { get; set; } = string.Empty; public string BestCaptchaSolverApiKey { get; set; } = string.Empty; public string CaptchaAiApiKey { get; set; } = string.Empty; + public string EzCaptchaApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/EzCaptchaServiceTests.cs b/CaptchaSharp.Tests/EzCaptchaServiceTests.cs new file mode 100644 index 0000000..87928b6 --- /dev/null +++ b/CaptchaSharp.Tests/EzCaptchaServiceTests.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class EzCaptchaFixture : ServiceFixture +{ + public EzCaptchaFixture() + { + Service = new EzCaptchaService(Config.Credentials.EzCaptchaApiKey); + } +} + +public class EzCaptchaServiceTests(EzCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); + [Fact] public Task SolveFunCaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFunCaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 6cf2a89..e21712a 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -420,34 +420,34 @@ Serializes an object to a json string and converts the property names to a camelCase based convention. - + - A request to solve a captcha task. + Represents a request to the AntiCaptcha API. - + - The task to solve. + Your AntiCaptcha API key. - + - The soft ID to use. Default is 0. + A request to solve a captcha task. - + - The language pool to use. Default is "en". + The task to solve. - + - Represents a request to the AntiCaptcha API. + The soft ID to use. Default is 0. - + - Your AntiCaptcha API key. + The language pool to use. Default is "en". @@ -550,6 +550,81 @@ when submitting the response to the target website. + + + A request to solve a captcha task. + + + + + The task to solve. + + + + + The soft ID to use. Default is 0. + + + + + The language pool to use. Default is "en". + + + + + Represents a request to the EzCaptcha API. + + + + + Your EzCaptcha API key. + + + + + A task that does not require a proxy. + + + + + The type of the task. + + + + + Represents the response from the EzCaptcha API. + + + + + The ID of the error. + + + + + The error code. + + + + + The error description. + + + + + Whether the response is an error. + + + + + Represents the response from the EzCaptcha API when creating a task. + + + + + The ID of the created task. + + The solution of a GeeTest captcha. @@ -707,7 +782,7 @@ - The service provided by https://anti-captcha.com/ + The service provided by https://anti-captcha.com/ @@ -769,7 +844,7 @@ - Creates a new . + Creates a new . @@ -777,7 +852,7 @@ - The service provided by https://azcaptcha.com/ + The service provided by https://azcaptcha.com/ @@ -789,7 +864,7 @@ - The service offered by https://bestcaptchasolver.com/ + The service offered by https://bestcaptchasolver.com/ @@ -839,7 +914,7 @@ - The service provided by https://capmonster.cloud/ + The service provided by https://capmonster.cloud/ @@ -871,7 +946,7 @@ - The service provided by https://capsolver.com/ + The service provided by https://capsolver.com/ @@ -929,7 +1004,7 @@ - The service provided by https://captchaai.com/ + The service provided by https://captchaai.com/ @@ -944,7 +1019,7 @@ - The service provided by https://captchacoder.com/ + The service provided by https://captchacoder.com/ @@ -1321,7 +1396,7 @@ - The service provided by https://captchas.io/ + The service provided by https://captchas.io/ @@ -1366,7 +1441,7 @@ - The service provided by https://www.deathbycaptcha.com/ + The service provided by https://www.deathbycaptcha.com/ @@ -1426,9 +1501,53 @@ + + + The service offered by https://www.ez-captcha.com/ + + + + + Your secret api key. + + + + + The ID of the software developer. + + + + + Initializes a . + + + + + + + + + + + Gets the result of a task. + + + + + + + + Parses the solution of a DataDome captcha. + + + + + Creates a new . + + - The service provided by https://humancoder.com/ + The service provided by https://humancoder.com/ @@ -1438,7 +1557,7 @@ - The service provided by https://www.imagetyperz.com/ + The service provided by https://www.imagetyperz.com/ @@ -1496,7 +1615,7 @@ - The service provided by https://metabypass.tech/. + The service provided by https://metabypass.tech/ @@ -1551,7 +1670,7 @@ - The service offered by https://nextcaptcha.com/ + The service offered by https://nextcaptcha.com/ @@ -1563,7 +1682,7 @@ - The service provided by https://www.9kw.eu/ + The service provided by https://www.9kw.eu/ @@ -1618,7 +1737,7 @@ - The service provided by https://nocaptchaai.com/ + The service provided by https://nocaptchaai.com/ @@ -1630,7 +1749,7 @@ - The service provided by https://nopecha.com/ + The service provided by https://nopecha.com/ @@ -1668,7 +1787,7 @@ - The service provided by https://rucaptcha.com/ + The service provided by https://rucaptcha.com/ @@ -1680,7 +1799,7 @@ - The service provided by https://apitruecaptcha.org/ + The service provided by https://apitruecaptcha.org/ @@ -1712,7 +1831,7 @@ - The service provided by https://2captcha.com/ + The service provided by https://2captcha.com/ diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/AntiCaptchaRequest.cs similarity index 88% rename from CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/AntiCaptchaRequest.cs index b8f0ddd..f37e2ec 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/Request.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/AntiCaptchaRequest.cs @@ -3,7 +3,7 @@ /// /// Represents a request to the AntiCaptcha API. /// -public class Request +public class AntiCaptchaRequest { /// /// Your AntiCaptcha API key. diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskAntiCaptchaRequest.cs similarity index 92% rename from CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskRequest.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskAntiCaptchaRequest.cs index 3b50c63..a3e0a33 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/CaptchaTaskAntiCaptchaRequest.cs @@ -6,7 +6,7 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests; /// /// A request to solve a captcha task. /// -public class CaptchaTaskRequest : Request +public class CaptchaTaskAntiCaptchaRequest : AntiCaptchaRequest { /// /// The task to solve. diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultAntiCaptchaRequest.cs similarity index 58% rename from CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultAntiCaptchaRequest.cs index 6bf63ad..e8bec8d 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/GetTaskResultAntiCaptchaRequest.cs @@ -1,6 +1,6 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests; -internal class GetTaskResultRequest : Request +internal class GetTaskResultAntiCaptchaRequest : AntiCaptchaRequest { public int TaskId { get; set; } } \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2AntiCaptchaRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2AntiCaptchaRequest.cs new file mode 100644 index 0000000..9ea4d6a --- /dev/null +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2AntiCaptchaRequest.cs @@ -0,0 +1,6 @@ +namespace CaptchaSharp.Models.AntiCaptcha.Requests; + +internal class RecaptchaV2AntiCaptchaRequest : AntiCaptchaRequest +{ + +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs deleted file mode 100644 index 8afc527..0000000 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/RecaptchaV2CaptchaRequest.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace CaptchaSharp.Models.AntiCaptcha.Requests; - -internal class RecaptchaV2CaptchaRequest : Request -{ - -} \ No newline at end of file diff --git a/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs b/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectAntiCaptchaRequest.cs similarity index 56% rename from CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs rename to CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectAntiCaptchaRequest.cs index 3dc880f..251f9fd 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectCaptchaRequest.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Requests/ReportIncorrectAntiCaptchaRequest.cs @@ -1,6 +1,6 @@ namespace CaptchaSharp.Models.AntiCaptcha.Requests; -internal class ReportIncorrectCaptchaRequest : Request +internal class ReportIncorrectAntiCaptchaRequest : AntiCaptchaRequest { public long TaskId { get; set; } -} \ No newline at end of file +} diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs index bd95a69..01c51a6 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/GetBalanceAntiCaptchaResponse.cs @@ -2,5 +2,5 @@ internal class GetBalanceAntiCaptchaResponse : AntiCaptchaResponse { - public float Balance { get; set; } + public decimal Balance { get; set; } } diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs index 807a0f6..c59c7eb 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/AntiCaptchaTaskSolution.cs @@ -1,5 +1,4 @@ -using CaptchaSharp.Models; -using System; +using System; namespace CaptchaSharp.Models.AntiCaptcha.Responses.Solutions; diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/CaptchaTaskEzCaptchaRequest.cs b/CaptchaSharp/Models/EzCaptcha/Requests/CaptchaTaskEzCaptchaRequest.cs new file mode 100644 index 0000000..1741478 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/CaptchaTaskEzCaptchaRequest.cs @@ -0,0 +1,28 @@ +using CaptchaSharp.Models.EzCaptcha.Requests.Tasks; +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EzCaptcha.Requests; + +/// +/// A request to solve a captcha task. +/// +public class CaptchaTaskEzCaptchaRequest : EzCaptchaRequest +{ + /// + /// The task to solve. + /// + public EzCaptchaTaskProxyless Task { get; set; } = null!; + + /// + /// The soft ID to use. Default is 0. + /// + [JsonProperty("softId", DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? SoftId { get; set; } + + /// + /// The language pool to use. Default is "en". + /// + [JsonProperty("languagePool", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LanguagePool { get; set; } +} + diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/EzCaptchaRequest.cs b/CaptchaSharp/Models/EzCaptcha/Requests/EzCaptchaRequest.cs new file mode 100644 index 0000000..c6cfc37 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/EzCaptchaRequest.cs @@ -0,0 +1,12 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests; + +/// +/// Represents a request to the EzCaptcha API. +/// +public class EzCaptchaRequest +{ + /// + /// Your EzCaptcha API key. + /// + public string ClientKey { get; set; } = ""; +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/GetTaskResultEzCaptchaRequest.cs b/CaptchaSharp/Models/EzCaptcha/Requests/GetTaskResultEzCaptchaRequest.cs new file mode 100644 index 0000000..0ec00f6 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/GetTaskResultEzCaptchaRequest.cs @@ -0,0 +1,6 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests; + +internal class GetTaskResultEzCaptchaRequest : EzCaptchaRequest +{ + public required string TaskId { get; set; } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/EzCaptchaTaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/EzCaptchaTaskProxyless.cs new file mode 100644 index 0000000..9237fce --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/EzCaptchaTaskProxyless.cs @@ -0,0 +1,13 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +/// +/// A task that does not require a proxy. +/// +public class EzCaptchaTaskProxyless +{ + /// + /// The type of the task. + /// + public string? Type { get; set; } +} + diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2EnterpriseTaskProxyless.cs new file mode 100644 index 0000000..e975240 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2EnterpriseTaskProxyless.cs @@ -0,0 +1,13 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class ReCaptchaV2EnterpriseTaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public bool IsInvisible { get; set; } + + public ReCaptchaV2EnterpriseTaskProxyless() + { + Type = "ReCaptchaV2EnterpriseTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2SEnterpriseTaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2SEnterpriseTaskProxyless.cs new file mode 100644 index 0000000..3db77b0 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2SEnterpriseTaskProxyless.cs @@ -0,0 +1,14 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class ReCaptchaV2SEnterpriseTaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public bool IsInvisible { get; set; } + public string? DataS { get; set; } + + public ReCaptchaV2SEnterpriseTaskProxyless() + { + Type = "ReCaptchaV2SEnterpriseTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2STaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2STaskProxyless.cs new file mode 100644 index 0000000..7f31613 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2STaskProxyless.cs @@ -0,0 +1,14 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class ReCaptchaV2STaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public bool IsInvisible { get; set; } + public string? DataS { get; set; } + + public ReCaptchaV2STaskProxyless() + { + Type = "ReCaptchaV2STaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2TaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2TaskProxyless.cs new file mode 100644 index 0000000..0c47ce1 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV2TaskProxyless.cs @@ -0,0 +1,13 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class ReCaptchaV2TaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public bool IsInvisible { get; set; } + + public ReCaptchaV2TaskProxyless() + { + Type = "ReCaptchaV2TaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Responses/EzCaptchaResponse.cs b/CaptchaSharp/Models/EzCaptcha/Responses/EzCaptchaResponse.cs new file mode 100644 index 0000000..691056b --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Responses/EzCaptchaResponse.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EzCaptcha.Responses; + +/// +/// Represents the response from the EzCaptcha API. +/// +public class EzCaptchaResponse +{ + /// + /// The ID of the error. + /// + public int ErrorId { get; set; } + + /// + /// The error code. + /// + public string? ErrorCode { get; set; } + + /// + /// The error description. + /// + public string? ErrorDescription { get; set; } + + /// + /// Whether the response is an error. + /// + [JsonIgnore] + public bool IsError => ErrorId > 0; +} diff --git a/CaptchaSharp/Models/EzCaptcha/Responses/GetBalanceEzCaptchaResponse.cs b/CaptchaSharp/Models/EzCaptcha/Responses/GetBalanceEzCaptchaResponse.cs new file mode 100644 index 0000000..15d3220 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Responses/GetBalanceEzCaptchaResponse.cs @@ -0,0 +1,6 @@ +namespace CaptchaSharp.Models.EzCaptcha.Responses; + +internal class GetBalanceEzCaptchaResponse : EzCaptchaResponse +{ + public decimal Balance { get; set; } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Responses/GetTaskResultEzCaptchaResponse.cs b/CaptchaSharp/Models/EzCaptcha/Responses/GetTaskResultEzCaptchaResponse.cs new file mode 100644 index 0000000..f012538 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Responses/GetTaskResultEzCaptchaResponse.cs @@ -0,0 +1,12 @@ +using CaptchaSharp.Models.EzCaptcha.Responses.Solutions; + +namespace CaptchaSharp.Models.EzCaptcha.Responses; + +internal class GetTaskResultEzCaptchaResponse : EzCaptchaResponse +{ + public string? Status { get; set; } + + public EzCaptchaTaskSolution? EzCaptchaTaskSolution { get; set; } + + public bool IsReady => Status != "processing"; +} diff --git a/CaptchaSharp/Models/EzCaptcha/Responses/Solutions/EzCaptchaTaskSolution.cs b/CaptchaSharp/Models/EzCaptcha/Responses/Solutions/EzCaptchaTaskSolution.cs new file mode 100644 index 0000000..9785607 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Responses/Solutions/EzCaptchaTaskSolution.cs @@ -0,0 +1,11 @@ +using System; + +namespace CaptchaSharp.Models.EzCaptcha.Responses.Solutions; + +internal class EzCaptchaTaskSolution +{ + public virtual CaptchaResponse ToCaptchaResponse(string id) + { + throw new NotImplementedException(); + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Responses/Solutions/RecaptchaEzCaptchaTaskSolution.cs b/CaptchaSharp/Models/EzCaptcha/Responses/Solutions/RecaptchaEzCaptchaTaskSolution.cs new file mode 100644 index 0000000..ce9069a --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Responses/Solutions/RecaptchaEzCaptchaTaskSolution.cs @@ -0,0 +1,15 @@ +namespace CaptchaSharp.Models.EzCaptcha.Responses.Solutions; + +internal class RecaptchaEzCaptchaTaskSolution : EzCaptchaTaskSolution +{ + public string? GRecaptchaResponse { get; set; } + + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse + { + Id = id, + Response = GRecaptchaResponse! + }; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Responses/TaskCreationEzCaptchaResponse.cs b/CaptchaSharp/Models/EzCaptcha/Responses/TaskCreationEzCaptchaResponse.cs new file mode 100644 index 0000000..e7d74ab --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Responses/TaskCreationEzCaptchaResponse.cs @@ -0,0 +1,12 @@ +namespace CaptchaSharp.Models.EzCaptcha.Responses; + +/// +/// Represents the response from the EzCaptcha API when creating a task. +/// +public class TaskCreationEzCaptchaResponse : EzCaptchaResponse +{ + /// + /// The ID of the created task. + /// + public string? TaskId { get; set; } +} diff --git a/CaptchaSharp/Services/AntiCaptchaService.cs b/CaptchaSharp/Services/AntiCaptchaService.cs index 8a19a5a..2c13855 100644 --- a/CaptchaSharp/Services/AntiCaptchaService.cs +++ b/CaptchaSharp/Services/AntiCaptchaService.cs @@ -17,7 +17,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://anti-captcha.com/ +/// The service provided by https://anti-captcha.com/ /// public class AntiCaptchaService : CaptchaService { @@ -47,7 +47,7 @@ public AntiCaptchaService(string apiKey, HttpClient? httpClient = null) : base(h public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) { var response = await HttpClient.PostJsonAsync( - "getBalance", new Request { ClientKey = ApiKey }, + "getBalance", new AntiCaptchaRequest { ClientKey = ApiKey }, cancellationToken: cancellationToken).ConfigureAwait(false); if (response.IsError) @@ -55,7 +55,7 @@ public override async Task GetBalanceAsync(CancellationToken cancellati throw new BadAuthenticationException($"{response.ErrorCode}: {response.ErrorDescription}"); } - return new decimal(response.Balance); + return response.Balance; } #endregion @@ -68,7 +68,7 @@ public override async Task SolveImageCaptchaAsync( var response = await HttpClient.PostJsonAsync( "createTask", AddImageCapabilities( - new CaptchaTaskRequest + new CaptchaTaskAntiCaptchaRequest { ClientKey = ApiKey, SoftId = SoftId, @@ -361,7 +361,7 @@ protected async Task GetResult( { var response = await HttpClient.PostJsonToStringAsync( "getTaskResult", - new GetTaskResultRequest { ClientKey = ApiKey, TaskId = int.Parse(task.Id) }, + new GetTaskResultAntiCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(task.Id) }, cancellationToken: cancellationToken).ConfigureAwait(false); var result = response.Deserialize(); @@ -430,7 +430,7 @@ public override async Task ReportSolution( await HttpClient.PostJsonToStringAsync( "reportCorrectRecaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, + new ReportIncorrectAntiCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken).ConfigureAwait(false); return; @@ -440,17 +440,17 @@ await HttpClient.PostJsonToStringAsync( { CaptchaType.ImageCaptcha => await HttpClient.PostJsonAsync( "reportIncorrectImageCaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, + new ReportIncorrectAntiCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken) .ConfigureAwait(false), CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3 => await HttpClient .PostJsonAsync("reportIncorrectRecaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, + new ReportIncorrectAntiCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken) .ConfigureAwait(false), CaptchaType.HCaptcha => await HttpClient.PostJsonAsync( "reportIncorrectHcaptcha", - new ReportIncorrectCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, + new ReportIncorrectAntiCaptchaRequest { ClientKey = ApiKey, TaskId = int.Parse(id) }, cancellationToken: cancellationToken) .ConfigureAwait(false), _ => throw new NotSupportedException("Reporting is not supported for this captcha type") @@ -465,11 +465,11 @@ await HttpClient.PostJsonToStringAsync( #region Private Methods /// - /// Creates a new . + /// Creates a new . /// - protected CaptchaTaskRequest CreateTaskRequest() + protected CaptchaTaskAntiCaptchaRequest CreateTaskRequest() { - return new CaptchaTaskRequest + return new CaptchaTaskAntiCaptchaRequest { ClientKey = ApiKey, SoftId = SoftId @@ -489,14 +489,14 @@ protected CaptchaTaskRequest CreateTaskRequest() CaptchaServiceCapabilities.MaxLength | CaptchaServiceCapabilities.Instructions; - private static CaptchaTaskRequest AddImageCapabilities(CaptchaTaskRequest request, ImageCaptchaOptions? options) + private static CaptchaTaskAntiCaptchaRequest AddImageCapabilities(CaptchaTaskAntiCaptchaRequest antiCaptchaRequest, ImageCaptchaOptions? options) { if (options == null) { - return request; + return antiCaptchaRequest; } - if (request.Task is not ImageCaptchaTask task) + if (antiCaptchaRequest.Task is not ImageCaptchaTask task) { throw new NotSupportedException( "Image options are only supported for image captchas"); @@ -517,7 +517,7 @@ private static CaptchaTaskRequest AddImageCapabilities(CaptchaTaskRequest reques task.MaxLength = options.MaxLength; task.Comment = options.TextInstructions; - request.LanguagePool = options.CaptchaLanguage switch + antiCaptchaRequest.LanguagePool = options.CaptchaLanguage switch { CaptchaLanguage.NotSpecified or CaptchaLanguage.English => "en", CaptchaLanguage.Russian or CaptchaLanguage.Ukrainian or CaptchaLanguage.Kazakh @@ -525,7 +525,7 @@ CaptchaLanguage.Russian or CaptchaLanguage.Ukrainian or CaptchaLanguage.Kazakh _ => throw new NotSupportedException($"The {options.CaptchaLanguage} language is not supported") }; - return request; + return antiCaptchaRequest; } #endregion } diff --git a/CaptchaSharp/Services/AzCaptchaService.cs b/CaptchaSharp/Services/AzCaptchaService.cs index 914a50f..37acbb0 100644 --- a/CaptchaSharp/Services/AzCaptchaService.cs +++ b/CaptchaSharp/Services/AzCaptchaService.cs @@ -7,7 +7,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://azcaptcha.com/ +/// The service provided by https://azcaptcha.com/ /// public class AzCaptchaService : CustomTwoCaptchaService { diff --git a/CaptchaSharp/Services/BestCaptchaSolverService.cs b/CaptchaSharp/Services/BestCaptchaSolverService.cs index 3e9562b..16905a6 100644 --- a/CaptchaSharp/Services/BestCaptchaSolverService.cs +++ b/CaptchaSharp/Services/BestCaptchaSolverService.cs @@ -12,7 +12,7 @@ namespace CaptchaSharp.Services; /// -/// The service offered by https://bestcaptchasolver.com/ +/// The service offered by https://bestcaptchasolver.com/ /// public class BestCaptchaSolverService : CaptchaService { diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index c072269..4b41155 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -12,7 +12,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://capmonster.cloud/ +/// The service provided by https://capmonster.cloud/ /// public class CapMonsterCloudService : CustomAntiCaptchaService { diff --git a/CaptchaSharp/Services/CapSolverService.cs b/CaptchaSharp/Services/CapSolverService.cs index fa7b5c5..acb9bef 100644 --- a/CaptchaSharp/Services/CapSolverService.cs +++ b/CaptchaSharp/Services/CapSolverService.cs @@ -16,7 +16,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://capsolver.com/ +/// The service provided by https://capsolver.com/ /// public class CapSolverService : CaptchaService { diff --git a/CaptchaSharp/Services/CaptchaAiService.cs b/CaptchaSharp/Services/CaptchaAiService.cs index 18b67d3..0129ff8 100644 --- a/CaptchaSharp/Services/CaptchaAiService.cs +++ b/CaptchaSharp/Services/CaptchaAiService.cs @@ -10,7 +10,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://captchaai.com/ +/// The service provided by https://captchaai.com/ /// public class CaptchaAiService : CustomTwoCaptchaService { diff --git a/CaptchaSharp/Services/CaptchaCoderService.cs b/CaptchaSharp/Services/CaptchaCoderService.cs index f3e85d4..0b84b3c 100644 --- a/CaptchaSharp/Services/CaptchaCoderService.cs +++ b/CaptchaSharp/Services/CaptchaCoderService.cs @@ -11,7 +11,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://captchacoder.com/ +/// The service provided by https://captchacoder.com/ /// public class CaptchaCoderService : CaptchaService { diff --git a/CaptchaSharp/Services/CaptchasIoService.cs b/CaptchaSharp/Services/CaptchasIoService.cs index 523c306..b04d5c9 100644 --- a/CaptchaSharp/Services/CaptchasIoService.cs +++ b/CaptchaSharp/Services/CaptchasIoService.cs @@ -5,7 +5,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://captchas.io/ +/// The service provided by https://captchas.io/ /// public class CaptchasIoService : CustomTwoCaptchaService { diff --git a/CaptchaSharp/Services/DeathByCaptchaService.cs b/CaptchaSharp/Services/DeathByCaptchaService.cs index 6a75600..6db71b1 100644 --- a/CaptchaSharp/Services/DeathByCaptchaService.cs +++ b/CaptchaSharp/Services/DeathByCaptchaService.cs @@ -18,7 +18,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://www.deathbycaptcha.com/ +/// The service provided by https://www.deathbycaptcha.com/ /// public class DeathByCaptchaService : CaptchaService { diff --git a/CaptchaSharp/Services/EzCaptchaService.cs b/CaptchaSharp/Services/EzCaptchaService.cs new file mode 100644 index 0000000..4d43d85 --- /dev/null +++ b/CaptchaSharp/Services/EzCaptchaService.cs @@ -0,0 +1,213 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Exceptions; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; +using CaptchaSharp.Models.EzCaptcha.Responses; +using CaptchaSharp.Models.EzCaptcha.Responses.Solutions; +using CaptchaSharp.Models.EzCaptcha.Requests; +using CaptchaSharp.Models.EzCaptcha.Requests.Tasks; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services; + +/// +/// The service offered by https://www.ez-captcha.com/ +/// +public class EzCaptchaService : CaptchaService +{ + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The ID of the software developer. + /// + private const int _softId = 82832; + + /// + /// Initializes a . + /// + public EzCaptchaService(string apiKey, HttpClient? httpClient = null) + : base(httpClient) + { + ApiKey = apiKey; + HttpClient.BaseAddress = new Uri("https://api.ez-captcha.com"); + } + + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostJsonAsync( + "getBalance", new EzCaptchaRequest { ClientKey = ApiKey }, + cancellationToken: cancellationToken).ConfigureAwait(false); + + if (response.IsError) + { + throw new BadAuthenticationException($"{response.ErrorCode}: {response.ErrorDescription}"); + } + + return response.Balance; + } + #endregion + + #region Solve Methods + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + // All recaptcha tasks are proxyless, so the proxy is disregarded + + var content = CreateTaskRequest(); + + if (enterprise) + { + if (!string.IsNullOrEmpty(dataS)) + { + content.Task = new ReCaptchaV2SEnterpriseTaskProxyless() + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + IsInvisible = invisible, + DataS = dataS, + }; + } + else + { + content.Task = new ReCaptchaV2EnterpriseTaskProxyless() + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + IsInvisible = invisible, + }; + } + } + else + { + if (!string.IsNullOrEmpty(dataS)) + { + content.Task = new ReCaptchaV2STaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + IsInvisible = invisible, + DataS = dataS + }; + } + else + { + content.Task = new ReCaptchaV2TaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + IsInvisible = invisible + }; + } + } + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.ReCaptchaV2, + cancellationToken).ConfigureAwait(false); + } + #endregion + + #region Getting the result + /// + /// Gets the result of a task. + /// + private async Task GetResult( + TaskCreationEzCaptchaResponse ezCaptchaResponse, CaptchaType type, + CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (ezCaptchaResponse.IsError) + { + throw new TaskCreationException($"{ezCaptchaResponse.ErrorCode}: {ezCaptchaResponse.ErrorDescription}"); + } + + var task = new CaptchaTask(ezCaptchaResponse.TaskId!, type); + + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } + + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + var response = await HttpClient.PostJsonToStringAsync( + "getTaskResult", + new GetTaskResultEzCaptchaRequest { ClientKey = ApiKey, TaskId = task.Id }, + cancellationToken: cancellationToken).ConfigureAwait(false); + + var result = response.Deserialize(); + + if (!result.IsReady) + { + return null; + } + + task.Completed = true; + + if (result.IsError) + { + throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); + } + + var jObject = JObject.Parse(response); + var solution = jObject["solution"]; + + if (solution is null) + { + throw new TaskSolutionException(response); + } + + if (task.Type == CaptchaType.DataDome) + { + return ParseDataDomeSolution(task.Id, solution) as T; + } + + result.EzCaptchaTaskSolution = task.Type switch + { + CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3 or CaptchaType.HCaptcha => + solution.ToObject()! as EzCaptchaTaskSolution, + _ => throw new NotSupportedException($"The {task.Type} captcha type is not supported") + } ?? throw new TaskSolutionException(response); + + return result.EzCaptchaTaskSolution.ToCaptchaResponse(task.Id) as T; + } + + /// + /// Parses the solution of a DataDome captcha. + /// + protected virtual StringResponse ParseDataDomeSolution(string taskId, JToken? solution) + { + throw new NotImplementedException("DataDome captcha solving is not supported"); + } + #endregion + + #region Private Methods + /// + /// Creates a new . + /// + private CaptchaTaskEzCaptchaRequest CreateTaskRequest() + { + return new CaptchaTaskEzCaptchaRequest + { + ClientKey = ApiKey, + SoftId = _softId + }; + } + #endregion +} diff --git a/CaptchaSharp/Services/HumanCoderService.cs b/CaptchaSharp/Services/HumanCoderService.cs index 0bab864..427b6ec 100644 --- a/CaptchaSharp/Services/HumanCoderService.cs +++ b/CaptchaSharp/Services/HumanCoderService.cs @@ -4,7 +4,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://humancoder.com/ +/// The service provided by https://humancoder.com/ /// public class HumanCoderService : CaptchaCoderService { diff --git a/CaptchaSharp/Services/ImageTyperzService.cs b/CaptchaSharp/Services/ImageTyperzService.cs index 994dd27..682bdce 100644 --- a/CaptchaSharp/Services/ImageTyperzService.cs +++ b/CaptchaSharp/Services/ImageTyperzService.cs @@ -14,7 +14,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://www.imagetyperz.com/ +/// The service provided by https://www.imagetyperz.com/ /// public class ImageTyperzService : CaptchaService { diff --git a/CaptchaSharp/Services/MetaBypassTechService.cs b/CaptchaSharp/Services/MetaBypassTechService.cs index ed5a094..43ef262 100644 --- a/CaptchaSharp/Services/MetaBypassTechService.cs +++ b/CaptchaSharp/Services/MetaBypassTechService.cs @@ -11,7 +11,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://metabypass.tech/. +/// The service provided by https://metabypass.tech/ /// public class MetaBypassTechService : CaptchaService { diff --git a/CaptchaSharp/Services/NextCaptchaService.cs b/CaptchaSharp/Services/NextCaptchaService.cs index 09bbed6..1ba0f15 100644 --- a/CaptchaSharp/Services/NextCaptchaService.cs +++ b/CaptchaSharp/Services/NextCaptchaService.cs @@ -5,7 +5,7 @@ namespace CaptchaSharp.Services; /// -/// The service offered by https://nextcaptcha.com/ +/// The service offered by https://nextcaptcha.com/ /// public class NextCaptchaService : CustomAntiCaptchaService { diff --git a/CaptchaSharp/Services/NineKwService.cs b/CaptchaSharp/Services/NineKwService.cs index 1a052eb..627f49c 100644 --- a/CaptchaSharp/Services/NineKwService.cs +++ b/CaptchaSharp/Services/NineKwService.cs @@ -12,7 +12,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://www.9kw.eu/ +/// The service provided by https://www.9kw.eu/ /// public class NineKwService : CaptchaService { diff --git a/CaptchaSharp/Services/NoCaptchaAiService.cs b/CaptchaSharp/Services/NoCaptchaAiService.cs index 80c094a..d6a1829 100644 --- a/CaptchaSharp/Services/NoCaptchaAiService.cs +++ b/CaptchaSharp/Services/NoCaptchaAiService.cs @@ -5,7 +5,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://nocaptchaai.com/ +/// The service provided by https://nocaptchaai.com/ /// public class NoCaptchaAiService : CustomTwoCaptchaService { diff --git a/CaptchaSharp/Services/NopechaService.cs b/CaptchaSharp/Services/NopechaService.cs index 076f508..a337bca 100644 --- a/CaptchaSharp/Services/NopechaService.cs +++ b/CaptchaSharp/Services/NopechaService.cs @@ -13,7 +13,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://nopecha.com/ +/// The service provided by https://nopecha.com/ /// public class NopechaService : CaptchaService { diff --git a/CaptchaSharp/Services/RuCaptchaService.cs b/CaptchaSharp/Services/RuCaptchaService.cs index cd97d21..1e2a811 100644 --- a/CaptchaSharp/Services/RuCaptchaService.cs +++ b/CaptchaSharp/Services/RuCaptchaService.cs @@ -4,7 +4,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://rucaptcha.com/ +/// The service provided by https://rucaptcha.com/ /// public class RuCaptchaService : CustomTwoCaptchaService { diff --git a/CaptchaSharp/Services/TrueCaptchaService.cs b/CaptchaSharp/Services/TrueCaptchaService.cs index cf3fbef..9ee863c 100644 --- a/CaptchaSharp/Services/TrueCaptchaService.cs +++ b/CaptchaSharp/Services/TrueCaptchaService.cs @@ -12,7 +12,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://apitruecaptcha.org/ +/// The service provided by https://apitruecaptcha.org/ /// public class TrueCaptchaService : CaptchaService { diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index c820e95..5d096f6 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -14,7 +14,7 @@ namespace CaptchaSharp.Services; /// -/// The service provided by https://2captcha.com/ +/// The service provided by https://2captcha.com/ /// public class TwoCaptchaService : CaptchaService { From 9e7a13e95713640d8517337aae649e331b22363a Mon Sep 17 00:00:00 2001 From: Ruri Date: Mon, 22 Jul 2024 01:20:14 +0200 Subject: [PATCH 38/67] Added more EzCaptchaService methods --- CaptchaSharp.Tests/EzCaptchaServiceTests.cs | 2 - CaptchaSharp/CaptchaSharp.xml | 9 ++ .../Requests/Tasks/FuncaptchaTaskProxyless.cs | 13 +++ .../Requests/Tasks/HcaptchaTaskProxyless.cs | 12 +++ .../ReCaptchaV3EnterpriseTaskProxyless.cs | 18 ++++ .../Tasks/ReCaptchaV3TaskProxyless.cs | 17 ++++ .../Tasks/ReCaptchaV3TaskProxylessS9.cs | 18 ++++ CaptchaSharp/Services/EzCaptchaService.cs | 93 +++++++++++++++++++ 8 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/FuncaptchaTaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/HcaptchaTaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3EnterpriseTaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxyless.cs create mode 100644 CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxylessS9.cs diff --git a/CaptchaSharp.Tests/EzCaptchaServiceTests.cs b/CaptchaSharp.Tests/EzCaptchaServiceTests.cs index 87928b6..8651c4e 100644 --- a/CaptchaSharp.Tests/EzCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/EzCaptchaServiceTests.cs @@ -23,8 +23,6 @@ public class EzCaptchaServiceTests(EzCaptchaFixture fixture, ITestOutputHelper o [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); [Fact] public Task SolveRecaptchaV3EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV3EnterpriseTest_NoProxy(); [Fact] public Task SolveFunCaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); - [Fact] public Task SolveFunCaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); - [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index e21712a..b7a7663 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1527,6 +1527,15 @@ + + + + + + + + + Gets the result of a task. diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/FuncaptchaTaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/FuncaptchaTaskProxyless.cs new file mode 100644 index 0000000..f66b75d --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/FuncaptchaTaskProxyless.cs @@ -0,0 +1,13 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class FuncaptchaTaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + public string? FuncaptchaApiJSSubdomain { get; set; } + + public FuncaptchaTaskProxyless() + { + Type = "FuncaptchaTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/HcaptchaTaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/HcaptchaTaskProxyless.cs new file mode 100644 index 0000000..75eb57e --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/HcaptchaTaskProxyless.cs @@ -0,0 +1,12 @@ +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class HcaptchaTaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteKey { get; set; } + public string? WebsiteURL { get; set; } + + public HcaptchaTaskProxyless() + { + Type = "HcaptchaTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3EnterpriseTaskProxyless.cs new file mode 100644 index 0000000..acc5116 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3EnterpriseTaskProxyless.cs @@ -0,0 +1,18 @@ +using CaptchaSharp.Models.EzCaptcha.Requests.Tasks; +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class ReCaptchaV3EnterpriseTaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + + [JsonProperty("pageAction", NullValueHandling = NullValueHandling.Ignore)] + public string? PageAction { get; set; } + + public ReCaptchaV3EnterpriseTaskProxyless() + { + Type = "ReCaptchaV3EnterpriseTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxyless.cs new file mode 100644 index 0000000..4285646 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxyless.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class ReCaptchaV3TaskProxyless : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + + [JsonProperty("pageAction", NullValueHandling = NullValueHandling.Ignore)] + public string? PageAction { get; set; } + + public ReCaptchaV3TaskProxyless() + { + Type = "ReCaptchaV3TaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxylessS9.cs b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxylessS9.cs new file mode 100644 index 0000000..e9e1f35 --- /dev/null +++ b/CaptchaSharp/Models/EzCaptcha/Requests/Tasks/ReCaptchaV3TaskProxylessS9.cs @@ -0,0 +1,18 @@ +using CaptchaSharp.Models.EzCaptcha.Requests.Tasks; +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EzCaptcha.Requests.Tasks; + +internal class ReCaptchaV3TaskProxylessS9 : EzCaptchaTaskProxyless +{ + public string? WebsiteURL { get; set; } + public string? WebsiteKey { get; set; } + + [JsonProperty("pageAction", NullValueHandling = NullValueHandling.Ignore)] + public string? PageAction { get; set; } + + public ReCaptchaV3TaskProxylessS9() + { + Type = "ReCaptchaV3TaskProxylessS9"; + } +} diff --git a/CaptchaSharp/Services/EzCaptchaService.cs b/CaptchaSharp/Services/EzCaptchaService.cs index 4d43d85..7c6fddc 100644 --- a/CaptchaSharp/Services/EzCaptchaService.cs +++ b/CaptchaSharp/Services/EzCaptchaService.cs @@ -120,6 +120,99 @@ public override async Task SolveRecaptchaV2Async( return await GetResult(response, CaptchaType.ReCaptchaV2, cancellationToken).ConfigureAwait(false); } + + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (enterprise) + { + content.Task = new ReCaptchaV3EnterpriseTaskProxyless() + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + PageAction = string.IsNullOrEmpty(action) ? null : action, + }; + } + else + { + if (Math.Abs(minScore - 0.9f) < 0.000001) + { + content.Task = new ReCaptchaV3TaskProxylessS9() + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + PageAction = string.IsNullOrEmpty(action) ? null : action, + }; + } + else + { + content.Task = new ReCaptchaV3TaskProxyless() + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl, + PageAction = string.IsNullOrEmpty(action) ? null : action, + }; + } + } + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.ReCaptchaV3, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + content.Task = new FuncaptchaTaskProxyless + { + WebsiteKey = publicKey, + WebsiteURL = siteUrl, + FuncaptchaApiJSSubdomain = serviceUrl + }; + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.FunCaptcha, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + content.Task = new HcaptchaTaskProxyless + { + WebsiteKey = siteKey, + WebsiteURL = siteUrl + }; + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.HCaptcha, + cancellationToken).ConfigureAwait(false); + } #endregion #region Getting the result From e55f88f1fff9967be92694b8e88e9d78688a8152 Mon Sep 17 00:00:00 2001 From: Ruri Date: Mon, 22 Jul 2024 13:00:37 +0200 Subject: [PATCH 39/67] Added SolveCaptchaService --- CaptchaSharp.Tests/ConfigFixture.cs | 1 + .../SolveCaptchaServiceTests.cs | 36 ++ CaptchaSharp/CaptchaSharp.xml | 116 +++++ .../GeeTestAntiCaptchaTaskSolution.cs | 2 +- .../CaptchaTaskSolveCaptchaRequest.cs | 10 + .../GetTaskResultSolveCaptchaRequest.cs | 9 + .../Requests/SolveCaptchaRequest.cs | 15 + .../Requests/Tasks/FunCaptchaTaskProxyless.cs | 20 + .../Requests/Tasks/GeeTestTaskProxyless.cs | 23 + .../Requests/Tasks/HCaptchaTaskProxyless.cs | 17 + .../Requests/Tasks/Proxied/FunCaptchaTask.cs | 20 + .../Requests/Tasks/Proxied/GeeTestTask.cs | 23 + .../Requests/Tasks/Proxied/HCaptchaTask.cs | 17 + .../Proxied/RecaptchaV2EnterpriseTask.cs | 20 + .../Requests/Tasks/Proxied/RecaptchaV2Task.cs | 20 + .../Tasks/Proxied/SolveCaptchaTask.cs | 44 ++ .../Requests/Tasks/Proxied/TurnstileTask.cs | 17 + .../RecaptchaV2EnterpriseTaskProxyless.cs | 20 + .../Tasks/RecaptchaV2TaskProxyless.cs | 20 + .../Tasks/RecaptchaV3TaskProxyless.cs | 23 + .../Tasks/SolveCaptchaTaskProxyless.cs | 48 +++ .../Requests/Tasks/TurnstileTaskProxyless.cs | 17 + .../GetBalanceSolveCaptchaResponse.cs | 6 + .../GetTaskResultSolveCaptchaResponse.cs | 16 + .../FuncaptchaSolveCaptchaTaskSolution.cs | 15 + .../GeeTestSolveCaptchaTaskSolution.cs | 19 + .../HCaptchaSolveCaptchaTaskSolution.cs | 18 + .../RecaptchaSolveCaptchaTaskSolution.cs | 18 + .../Solutions/SolveCaptchaTaskSolution.cs | 11 + .../Responses/SolveCaptchaResponse.cs | 33 ++ .../TaskCreationSolveCaptchaResponse.cs | 9 + CaptchaSharp/Services/SolveCaptchaService.cs | 401 ++++++++++++++++++ 32 files changed, 1083 insertions(+), 1 deletion(-) create mode 100644 CaptchaSharp.Tests/SolveCaptchaServiceTests.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/CaptchaTaskSolveCaptchaRequest.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/GetTaskResultSolveCaptchaRequest.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/SolveCaptchaRequest.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/SolveCaptchaTask.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/SolveCaptchaTaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/GetBalanceSolveCaptchaResponse.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/GetTaskResultSolveCaptchaResponse.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/FuncaptchaSolveCaptchaTaskSolution.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/GeeTestSolveCaptchaTaskSolution.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/HCaptchaSolveCaptchaTaskSolution.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/RecaptchaSolveCaptchaTaskSolution.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/SolveCaptchaTaskSolution.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/SolveCaptchaResponse.cs create mode 100644 CaptchaSharp/Models/SolveCaptcha/Responses/TaskCreationSolveCaptchaResponse.cs create mode 100644 CaptchaSharp/Services/SolveCaptchaService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 1ad4261..99d7a27 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -63,4 +63,5 @@ public class Credentials public string BestCaptchaSolverApiKey { get; set; } = string.Empty; public string CaptchaAiApiKey { get; set; } = string.Empty; public string EzCaptchaApiKey { get; set; } = string.Empty; + public string SolveCaptchaApiKey { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/SolveCaptchaServiceTests.cs b/CaptchaSharp.Tests/SolveCaptchaServiceTests.cs new file mode 100644 index 0000000..12b6efc --- /dev/null +++ b/CaptchaSharp.Tests/SolveCaptchaServiceTests.cs @@ -0,0 +1,36 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class SolveCaptchaFixture : ServiceFixture +{ + public SolveCaptchaFixture() + { + Service = new SolveCaptchaService( + Config.Credentials.SolveCaptchaApiKey); + } +} + +public class SolveCaptchaServiceTests(SolveCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_NoProxy_ValidSolution() => RecaptchaV2InvisibleTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2InvisibleAsync_WithProxy_ValidSolution() => RecaptchaV2InvisibleTest_WithProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_NoProxy_ValidSolution() => RecaptchaV2EnterpriseTest_NoProxy(); + [Fact] public Task SolveRecaptchaV2EnterpriseAsync_WithProxy_ValidSolution() => RecaptchaV2EnterpriseTest_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); + [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); + [Fact] public Task SolveGeeTestAsync_WithProxy_ValidSolution() => GeeTestTest_WithProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index b7a7663..a7d4536 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -740,6 +740,66 @@ + + + Base request for the SolveCaptcha service. + + + + + The soft ID to use. Default is 0. + + + + + A task that does not require a proxy. + + + + + The method to use. + + + + + The user agent to use. + + + + + The cookies to use, formatted like "name1=value1; name2=value2". + + + + + Set some parameters from a proxy. + + + + + Represents the response from the SolveCaptcha API. + + + + + The ID of the error. + + + + + The error code. + + + + + The error description. + + + + + Whether the response is an error. + + A collection of string pairs. @@ -1806,6 +1866,62 @@ The API key to use. The to use for requests. If null, a default one will be created. + + + The service offered by https://solvecaptcha.net/ + + + + + Your secret api key. + + + + + The ID of the software developer. + + + + + Initializes a . + + Your secret api key. + The to use for requests. If null, a default one will be created. + + + + + + + + + + + + + + + + + + + + + + + + + Gets the result of a task. + + + + + + + + Creates a new . + + The service provided by https://apitruecaptcha.org/ diff --git a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs index b15dce8..7edc5f6 100644 --- a/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs +++ b/CaptchaSharp/Models/AntiCaptcha/Responses/Solutions/GeeTestAntiCaptchaTaskSolution.cs @@ -8,7 +8,7 @@ internal class GeeTestAntiCaptchaTaskSolution : AntiCaptchaTaskSolution public override CaptchaResponse ToCaptchaResponse(string id) { - return new GeeTestResponse() + return new GeeTestResponse { Id = id, Challenge = Challenge!, diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/CaptchaTaskSolveCaptchaRequest.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/CaptchaTaskSolveCaptchaRequest.cs new file mode 100644 index 0000000..4163093 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/CaptchaTaskSolveCaptchaRequest.cs @@ -0,0 +1,10 @@ +using CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests; + +internal class CaptchaTaskSolveCaptchaRequest : SolveCaptchaRequest +{ + [JsonProperty("task")] + public SolveCaptchaTaskProxyless? Task { get; set; } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/GetTaskResultSolveCaptchaRequest.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/GetTaskResultSolveCaptchaRequest.cs new file mode 100644 index 0000000..d918bc6 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/GetTaskResultSolveCaptchaRequest.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests; + +internal class GetTaskResultSolveCaptchaRequest : SolveCaptchaRequest +{ + [JsonProperty("task_id")] + public long TaskId { get; set; } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/SolveCaptchaRequest.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/SolveCaptchaRequest.cs new file mode 100644 index 0000000..6f7198d --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/SolveCaptchaRequest.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests; + +/// +/// Base request for the SolveCaptcha service. +/// +public class SolveCaptchaRequest +{ + /// + /// The soft ID to use. Default is 0. + /// + [JsonProperty("affiliate_id", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? AffiliateId { get; set; } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs new file mode 100644 index 0000000..4d161d9 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/FunCaptchaTaskProxyless.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +internal class FunCaptchaTaskProxyless : SolveCaptchaTaskProxyless +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("sub_domain", NullValueHandling = NullValueHandling.Ignore)] + public string? SubDomain { get; set; } + + public FunCaptchaTaskProxyless() + { + Method = "FunCaptchaTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs new file mode 100644 index 0000000..5fdc143 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/GeeTestTaskProxyless.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +internal class GeeTestTaskProxyless : SolveCaptchaTaskProxyless +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("gt")] + public required string Gt { get; set; } + + [JsonProperty("challenge")] + public required string Challenge { get; set; } + + [JsonProperty("geetestApiServerSubdomain", NullValueHandling = NullValueHandling.Ignore)] + public string? GeeTestApiServerSubdomain { get; set; } + + public GeeTestTaskProxyless() + { + Method = "GeeTestTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs new file mode 100644 index 0000000..5172d05 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/HCaptchaTaskProxyless.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +internal class HCaptchaTaskProxyless : SolveCaptchaTaskProxyless +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + public HCaptchaTaskProxyless() + { + Method = "HCaptchaTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs new file mode 100644 index 0000000..c002026 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/FunCaptchaTask.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; + +internal class FunCaptchaTask : SolveCaptchaTask +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("sub_domain", NullValueHandling = NullValueHandling.Ignore)] + public string? SubDomain { get; set; } + + public FunCaptchaTask() + { + Method = "FunCaptchaTask"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs new file mode 100644 index 0000000..63fc2df --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/GeeTestTask.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; + +internal class GeeTestTask : SolveCaptchaTask +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("gt")] + public required string Gt { get; set; } + + [JsonProperty("challenge")] + public required string Challenge { get; set; } + + [JsonProperty("geetestApiServerSubdomain", NullValueHandling = NullValueHandling.Ignore)] + public string? GeeTestApiServerSubdomain { get; set; } + + public GeeTestTask() + { + Method = "GeeTestTask"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs new file mode 100644 index 0000000..e78a104 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/HCaptchaTask.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; + +internal class HCaptchaTask : SolveCaptchaTask +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + public HCaptchaTask() + { + Method = "HCaptchaTask"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs new file mode 100644 index 0000000..fa7380a --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2EnterpriseTask.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; + +internal class RecaptchaV2EnterpriseTask : SolveCaptchaTask +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("enterprisePayload", NullValueHandling = NullValueHandling.Ignore)] + public string? EnterprisePayload { get; set; } + + public RecaptchaV2EnterpriseTask() + { + Method = "RecaptchaV2EnterpriseTask"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs new file mode 100644 index 0000000..2782bec --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/RecaptchaV2Task.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; + +internal class RecaptchaV2Task : SolveCaptchaTask +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("data_s", NullValueHandling = NullValueHandling.Ignore)] + public string? DataS { get; set; } + + public RecaptchaV2Task() + { + Method = "RecaptchaV2Task"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/SolveCaptchaTask.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/SolveCaptchaTask.cs new file mode 100644 index 0000000..707ee10 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/SolveCaptchaTask.cs @@ -0,0 +1,44 @@ +using System; +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; + +internal class SolveCaptchaTask : SolveCaptchaTaskProxyless +{ + [JsonProperty("proxy_type", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyType { get; set; } + + [JsonProperty("proxy_address", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyAddress { get; set; } + + [JsonProperty("proxy_port", NullValueHandling = NullValueHandling.Ignore)] + public int? ProxyPort { get; set; } + + [JsonProperty("proxy_login", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyLogin { get; set; } + + [JsonProperty("proxy_password", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyPassword { get; set; } + + public override void SetParamsFromProxy(Proxy? proxy) + { + base.SetParamsFromProxy(proxy); + + if (proxy is null || string.IsNullOrEmpty(proxy.Host)) + { + return; + } + + if (!System.Net.IPAddress.TryParse(proxy.Host, out _)) + { + throw new NotSupportedException( + "Only IP addresses are supported for the proxy host"); + } + + ProxyAddress = proxy.Host; + ProxyPort = proxy.Port; + ProxyType = proxy.Type.ToString().ToLower(); + ProxyLogin = proxy.Username; + ProxyPassword = proxy.Password; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs new file mode 100644 index 0000000..eb0e0f8 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/Proxied/TurnstileTask.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; + +internal class TurnstileTask : SolveCaptchaTask +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + public TurnstileTask() + { + Method = "TurnstileTask"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs new file mode 100644 index 0000000..91fd6d4 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2EnterpriseTaskProxyless.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +internal class RecaptchaV2EnterpriseTaskProxyless : SolveCaptchaTaskProxyless +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("enterprisePayload", NullValueHandling = NullValueHandling.Ignore)] + public string? EnterprisePayload { get; set; } + + public RecaptchaV2EnterpriseTaskProxyless() + { + Method = "RecaptchaV2EnterpriseTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs new file mode 100644 index 0000000..f67b8d5 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV2TaskProxyless.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +internal class RecaptchaV2TaskProxyless : SolveCaptchaTaskProxyless +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("data_s", NullValueHandling = NullValueHandling.Ignore)] + public string? DataS { get; set; } + + public RecaptchaV2TaskProxyless() + { + Method = "RecaptchaV2TaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs new file mode 100644 index 0000000..b0407d4 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/RecaptchaV3TaskProxyless.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +internal class RecaptchaV3TaskProxyless : SolveCaptchaTaskProxyless +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + [JsonProperty("page_action", NullValueHandling = NullValueHandling.Ignore)] + public string? PageAction { get; set; } + + [JsonProperty("min_score", NullValueHandling = NullValueHandling.Ignore)] + public double? MinScore { get; set; } + + public RecaptchaV3TaskProxyless() + { + Method = "RecaptchaV3TaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/SolveCaptchaTaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/SolveCaptchaTaskProxyless.cs new file mode 100644 index 0000000..88a5ef6 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/SolveCaptchaTaskProxyless.cs @@ -0,0 +1,48 @@ +using System.Linq; +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +/// +/// A task that does not require a proxy. +/// +public class SolveCaptchaTaskProxyless +{ + /// + /// The method to use. + /// + [JsonProperty("method")] + public string? Method { get; set; } + + /// + /// The user agent to use. + /// + [JsonProperty("user_agent", NullValueHandling = NullValueHandling.Ignore)] + public string? UserAgent { get; set; } + + /// + /// The cookies to use, formatted like "name1=value1; name2=value2". + /// + [JsonProperty("cookies", NullValueHandling = NullValueHandling.Ignore)] + public string? Cookies { get; set; } + + /// + /// Set some parameters from a proxy. + /// + public virtual void SetParamsFromProxy(Proxy? proxy) + { + if (proxy is null) + { + return; + } + + UserAgent = proxy.UserAgent; + + if (proxy.Cookies == null) + { + return; + } + + Cookies = string.Join("; ", proxy.Cookies.Select(c => $"{c.Name}={c.Value}")); + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs new file mode 100644 index 0000000..a26b75a --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Requests/Tasks/TurnstileTaskProxyless.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; + +internal class TurnstileTaskProxyless : SolveCaptchaTaskProxyless +{ + [JsonProperty("page_url")] + public required string PageUrl { get; set; } + + [JsonProperty("site_key")] + public required string SiteKey { get; set; } + + public TurnstileTaskProxyless() + { + Method = "TurnstileTaskProxyless"; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/GetBalanceSolveCaptchaResponse.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/GetBalanceSolveCaptchaResponse.cs new file mode 100644 index 0000000..841a8cd --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/GetBalanceSolveCaptchaResponse.cs @@ -0,0 +1,6 @@ +namespace CaptchaSharp.Models.SolveCaptcha.Responses; + +internal class GetBalanceSolveCaptchaResponse : SolveCaptchaResponse +{ + public decimal Balance { get; set; } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/GetTaskResultSolveCaptchaResponse.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/GetTaskResultSolveCaptchaResponse.cs new file mode 100644 index 0000000..13eb192 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/GetTaskResultSolveCaptchaResponse.cs @@ -0,0 +1,16 @@ +using CaptchaSharp.Models.SolveCaptcha.Responses.Solutions; +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Responses; + +internal class GetTaskResultSolveCaptchaResponse : SolveCaptchaResponse +{ + [JsonProperty("status")] + public string? Status { get; set; } + + [JsonProperty("solution")] + public SolveCaptchaTaskSolution? SolveCaptchaTaskSolution { get; set; } + + [JsonIgnore] + public bool IsReady => Status != "processing"; +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/FuncaptchaSolveCaptchaTaskSolution.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/FuncaptchaSolveCaptchaTaskSolution.cs new file mode 100644 index 0000000..b3ac282 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/FuncaptchaSolveCaptchaTaskSolution.cs @@ -0,0 +1,15 @@ +namespace CaptchaSharp.Models.SolveCaptcha.Responses.Solutions; + +internal class FuncaptchaSolveCaptchaTaskSolution : SolveCaptchaTaskSolution +{ + public string? Token { get; set; } + + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse + { + Id = id, + Response = Token! + }; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/GeeTestSolveCaptchaTaskSolution.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/GeeTestSolveCaptchaTaskSolution.cs new file mode 100644 index 0000000..a3d5032 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/GeeTestSolveCaptchaTaskSolution.cs @@ -0,0 +1,19 @@ +namespace CaptchaSharp.Models.SolveCaptcha.Responses.Solutions; + +internal class GeeTestSolveCaptchaTaskSolution : SolveCaptchaTaskSolution +{ + public string? Challenge { get; set; } + public string? Validate { get; set; } + public string? SecCode { get; set; } + + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new GeeTestResponse + { + Id = id, + Challenge = Challenge!, + Validate = Validate!, + SecCode = SecCode! + }; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/HCaptchaSolveCaptchaTaskSolution.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/HCaptchaSolveCaptchaTaskSolution.cs new file mode 100644 index 0000000..7e5501b --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/HCaptchaSolveCaptchaTaskSolution.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Responses.Solutions; + +internal class HCaptchaSolveCaptchaTaskSolution : SolveCaptchaTaskSolution +{ + [JsonProperty("hCaptchaResponse")] + public string? HCaptchaResponse { get; set; } + + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse + { + Id = id, + Response = HCaptchaResponse! + }; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/RecaptchaSolveCaptchaTaskSolution.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/RecaptchaSolveCaptchaTaskSolution.cs new file mode 100644 index 0000000..6bf30f3 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/RecaptchaSolveCaptchaTaskSolution.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Responses.Solutions; + +internal class RecaptchaSolveCaptchaTaskSolution : SolveCaptchaTaskSolution +{ + [JsonProperty("gRecaptchaResponse")] + public string? GRecaptchaResponse { get; set; } + + public override CaptchaResponse ToCaptchaResponse(string id) + { + return new StringResponse + { + Id = id, + Response = GRecaptchaResponse! + }; + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/SolveCaptchaTaskSolution.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/SolveCaptchaTaskSolution.cs new file mode 100644 index 0000000..4dc3971 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/Solutions/SolveCaptchaTaskSolution.cs @@ -0,0 +1,11 @@ +using System; + +namespace CaptchaSharp.Models.SolveCaptcha.Responses.Solutions; + +internal class SolveCaptchaTaskSolution +{ + public virtual CaptchaResponse ToCaptchaResponse(string id) + { + throw new NotImplementedException(); + } +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/SolveCaptchaResponse.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/SolveCaptchaResponse.cs new file mode 100644 index 0000000..c7daa21 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/SolveCaptchaResponse.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Responses; + +/// +/// Represents the response from the SolveCaptcha API. +/// +public class SolveCaptchaResponse +{ + /// + /// The ID of the error. + /// + [JsonProperty("error_id")] + public int ErrorId { get; set; } + + /// + /// The error code. + /// + [JsonProperty("error_code")] + public string? ErrorCode { get; set; } + + /// + /// The error description. + /// + [JsonProperty("error_description")] + public string? ErrorDescription { get; set; } + + /// + /// Whether the response is an error. + /// + [JsonIgnore] + public bool IsError => ErrorId > 0; +} diff --git a/CaptchaSharp/Models/SolveCaptcha/Responses/TaskCreationSolveCaptchaResponse.cs b/CaptchaSharp/Models/SolveCaptcha/Responses/TaskCreationSolveCaptchaResponse.cs new file mode 100644 index 0000000..ba08918 --- /dev/null +++ b/CaptchaSharp/Models/SolveCaptcha/Responses/TaskCreationSolveCaptchaResponse.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.SolveCaptcha.Responses; + +internal class TaskCreationSolveCaptchaResponse : SolveCaptchaResponse +{ + [JsonProperty("task_id")] + public long TaskId { get; set; } +} diff --git a/CaptchaSharp/Services/SolveCaptchaService.cs b/CaptchaSharp/Services/SolveCaptchaService.cs new file mode 100644 index 0000000..ce4db68 --- /dev/null +++ b/CaptchaSharp/Services/SolveCaptchaService.cs @@ -0,0 +1,401 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Exceptions; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; +using CaptchaSharp.Models.SolveCaptcha.Requests; +using CaptchaSharp.Models.SolveCaptcha.Requests.Tasks; +using CaptchaSharp.Models.SolveCaptcha.Requests.Tasks.Proxied; +using CaptchaSharp.Models.SolveCaptcha.Responses; +using CaptchaSharp.Models.SolveCaptcha.Responses.Solutions; +using Newtonsoft.Json.Linq; + +namespace CaptchaSharp.Services; + +/// +/// The service offered by https://solvecaptcha.net/ +/// +public class SolveCaptchaService : CaptchaService +{ + /// + /// Your secret api key. + /// + public string ApiKey { get; set; } + + /// + /// The ID of the software developer. + /// + private const string _affiliateId = "1f9531ae-f170-4beb-9148-09da563df4bd"; + + /// + /// Initializes a . + /// + /// Your secret api key. + /// The to use for requests. If null, a default one will be created. + public SolveCaptchaService(string apiKey, HttpClient? httpClient = null) : base(httpClient) + { + ApiKey = apiKey; + HttpClient.BaseAddress = new Uri("https://solvecaptcha.net/api/"); + HttpClient.DefaultRequestHeaders.Authorization + = new AuthenticationHeaderValue("Bearer", apiKey); + } + + #region Getting the Balance + /// + public override async Task GetBalanceAsync(CancellationToken cancellationToken = default) + { + var response = await HttpClient.GetJsonAsync( + "getBalance", new StringPairCollection(), + cancellationToken: cancellationToken).ConfigureAwait(false); + + if (response.IsError) + { + throw new BadAuthenticationException($"{response.ErrorCode}: {response.ErrorDescription}"); + } + + return response.Balance; + } + #endregion + + #region Solve Methods + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, bool invisible = false, + Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (enterprise) + { + if (!string.IsNullOrEmpty(proxy?.Host)) + { + content.Task = new RecaptchaV2EnterpriseTask + { + SiteKey = siteKey, + PageUrl = siteUrl, + EnterprisePayload = string.IsNullOrEmpty(dataS) + ? null + : $"{{\"s\":\"{dataS}\"}}" + }; + } + else + { + content.Task = new RecaptchaV2EnterpriseTaskProxyless + { + SiteKey = siteKey, + PageUrl = siteUrl, + EnterprisePayload = string.IsNullOrEmpty(dataS) + ? null + : $"{{\"s\":\"{dataS}\"}}" + }; + } + } + else + { + if (!string.IsNullOrEmpty(proxy?.Host)) + { + content.Task = new RecaptchaV2Task + { + SiteKey = siteKey, + PageUrl = siteUrl, + DataS = string.IsNullOrEmpty(dataS) ? null : dataS + }; + } + else + { + content.Task = new RecaptchaV2TaskProxyless + { + SiteKey = siteKey, + PageUrl = siteUrl, + DataS = string.IsNullOrEmpty(dataS) ? null : dataS + }; + } + } + + content.Task.SetParamsFromProxy(proxy); + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.ReCaptchaV2, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + content.Task = new RecaptchaV3TaskProxyless + { + SiteKey = siteKey, + PageUrl = siteUrl, + PageAction = string.IsNullOrEmpty(action) ? null : action, + MinScore = minScore + }; + + content.Task.SetParamsFromProxy(proxy); + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.ReCaptchaV3, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (!string.IsNullOrEmpty(proxy?.Host)) + { + content.Task = new FunCaptchaTask + { + SiteKey = publicKey, + SubDomain = serviceUrl, + PageUrl = siteUrl, + }; + } + else + { + content.Task = new FunCaptchaTaskProxyless + { + SiteKey = publicKey, + SubDomain = serviceUrl, + PageUrl = siteUrl, + }; + } + + content.Task.SetParamsFromProxy(proxy); + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.FunCaptcha, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (!string.IsNullOrEmpty(proxy?.Host)) + { + content.Task = new HCaptchaTask + { + SiteKey = siteKey, + PageUrl = siteUrl + }; + } + else + { + content.Task = new HCaptchaTaskProxyless + { + SiteKey = siteKey, + PageUrl = siteUrl + }; + } + + content.Task.SetParamsFromProxy(proxy); + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.HCaptcha, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveGeeTestAsync( + string gt, string challenge, string siteUrl, string? apiServer = null, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (!string.IsNullOrEmpty(proxy?.Host)) + { + content.Task = new GeeTestTask + { + Gt = gt, + Challenge = challenge, + PageUrl = siteUrl, + GeeTestApiServerSubdomain = apiServer + }; + } + else + { + content.Task = new GeeTestTaskProxyless + { + Gt = gt, + Challenge = challenge, + PageUrl = siteUrl, + GeeTestApiServerSubdomain = apiServer + }; + } + + content.Task.SetParamsFromProxy(proxy); + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.GeeTest, + cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task SolveCloudflareTurnstileAsync( + string siteKey, string siteUrl, string? action = null, string? data = null, + string? pageData = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var content = CreateTaskRequest(); + + if (!string.IsNullOrEmpty(proxy?.Host)) + { + content.Task = new TurnstileTask + { + SiteKey = siteKey, + PageUrl = siteUrl, + }; + } + else + { + content.Task = new TurnstileTaskProxyless + { + SiteKey = siteKey, + PageUrl = siteUrl, + }; + } + + content.Task.SetParamsFromProxy(proxy); + + var response = await HttpClient.PostJsonAsync( + "createTask", + content, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult(response, CaptchaType.CloudflareTurnstile, + cancellationToken).ConfigureAwait(false); + } + #endregion + + #region Getting the result + /// + /// Gets the result of a task. + /// + private async Task GetResult( + TaskCreationSolveCaptchaResponse antiCaptchaResponse, CaptchaType type, + CancellationToken cancellationToken = default) + where T : CaptchaResponse + { + if (antiCaptchaResponse.IsError) + { + throw new TaskCreationException($"{antiCaptchaResponse.ErrorCode}: {antiCaptchaResponse.ErrorDescription}"); + } + + var task = new CaptchaTask(antiCaptchaResponse.TaskId.ToString(), type); + + return await GetResult(task, cancellationToken).ConfigureAwait(false); + } + + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) + where T : class + { + var response = await HttpClient.PostJsonToStringAsync( + "getTaskResult", + new GetTaskResultSolveCaptchaRequest { TaskId = long.Parse(task.Id) }, + cancellationToken: cancellationToken).ConfigureAwait(false); + + // Sometimes the API returns an empty json array + if (response == "[]") + { + throw new TaskSolutionException("The API returned an empty json array"); + } + + GetTaskResultSolveCaptchaResponse result; + + try + { + result = response.Deserialize(); + } + catch (Exception e) + { + throw new TaskSolutionException("The API returned an invalid json", e); + } + + if (!result.IsReady) + { + return null; + } + + task.Completed = true; + + if (result.IsError) + { + throw new TaskSolutionException($"{result.ErrorCode}: {result.ErrorDescription}"); + } + + var jObject = JObject.Parse(response); + var solution = jObject["solution"]; + + if (solution is null) + { + throw new TaskSolutionException(response); + } + + result.SolveCaptchaTaskSolution = task.Type switch + { + CaptchaType.ReCaptchaV2 or CaptchaType.ReCaptchaV3 => + solution.ToObject()! as SolveCaptchaTaskSolution, + CaptchaType.FunCaptcha or CaptchaType.CloudflareTurnstile => + solution.ToObject()!, + CaptchaType.GeeTest => solution.ToObject(), + CaptchaType.HCaptcha => solution.ToObject(), + _ => throw new NotSupportedException($"The {task.Type} captcha type is not supported") + } ?? throw new TaskSolutionException(response); + + return result.SolveCaptchaTaskSolution.ToCaptchaResponse(task.Id) as T; + } + #endregion + + #region Private Methods + /// + /// Creates a new . + /// + private CaptchaTaskSolveCaptchaRequest CreateTaskRequest() + { + return new CaptchaTaskSolveCaptchaRequest + { + AffiliateId = _affiliateId + }; + } + #endregion +} From 681580b2728fc0295b232b2eaebaa233f27debab Mon Sep 17 00:00:00 2001 From: Ruri Date: Mon, 22 Jul 2024 13:09:31 +0200 Subject: [PATCH 40/67] Added UserAgent to capmonster.cloud datadome task --- .../Requests/Tasks/DataDomeTaskProxyless.cs | 3 +++ CaptchaSharp/Services/CapMonsterCloudService.cs | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs index f92cd76..3a15cc7 100644 --- a/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs +++ b/CaptchaSharp/Models/CapMonsterCloud/Requests/Tasks/DataDomeTaskProxyless.cs @@ -10,6 +10,9 @@ internal class DataDomeTaskProxyless : CustomTaskProxyless [JsonProperty("metadata")] public required DataDomeMetadata Metadata { get; set; } + + [JsonProperty("userAgent")] + public string? UserAgent { get; set; } public DataDomeTaskProxyless() { diff --git a/CaptchaSharp/Services/CapMonsterCloudService.cs b/CaptchaSharp/Services/CapMonsterCloudService.cs index 4b41155..2f3be10 100644 --- a/CaptchaSharp/Services/CapMonsterCloudService.cs +++ b/CaptchaSharp/Services/CapMonsterCloudService.cs @@ -50,6 +50,12 @@ public override async Task SolveDataDomeAsync( nameof(proxy), "DataDome requires cookies"); } + if (string.IsNullOrEmpty(proxy.UserAgent)) + { + throw new ArgumentNullException( + nameof(proxy), "DataDome requires a user agent"); + } + // The cookie must contain datadome=... and nothing else var datadomeCookie = Array.Find(proxy.Cookies, c => c.Name == "datadome").Value; @@ -64,6 +70,7 @@ public override async Task SolveDataDomeAsync( content.Task = new DataDomeTaskProxyless { WebsiteURL = siteUrl, + UserAgent = proxy.UserAgent, Metadata = new DataDomeMetadata { CaptchaUrl = captchaUrl, From fd691e2c5779173c27d8cff5740016d4af6ea3c3 Mon Sep 17 00:00:00 2001 From: Ruri Date: Mon, 22 Jul 2024 19:04:08 +0200 Subject: [PATCH 41/67] Added EndCaptchaService --- CaptchaSharp.Tests/ConfigFixture.cs | 2 + CaptchaSharp.Tests/EndCaptchaServiceTests.cs | 32 ++ CaptchaSharp/CaptchaSharp.xml | 47 +++ .../EndCaptcha/EndCaptchaTokenParams.cs | 23 ++ .../EndCaptcha/FuncaptchaTokenParams.cs | 12 + .../Models/EndCaptcha/HCaptchaTokenParams.cs | 12 + .../EndCaptcha/RecaptchaV2TokenParams.cs | 12 + .../EndCaptcha/RecaptchaV3TokenParams.cs | 18 ++ CaptchaSharp/Services/EndCaptchaService.cs | 288 ++++++++++++++++++ 9 files changed, 446 insertions(+) create mode 100644 CaptchaSharp.Tests/EndCaptchaServiceTests.cs create mode 100644 CaptchaSharp/Models/EndCaptcha/EndCaptchaTokenParams.cs create mode 100644 CaptchaSharp/Models/EndCaptcha/FuncaptchaTokenParams.cs create mode 100644 CaptchaSharp/Models/EndCaptcha/HCaptchaTokenParams.cs create mode 100644 CaptchaSharp/Models/EndCaptcha/RecaptchaV2TokenParams.cs create mode 100644 CaptchaSharp/Models/EndCaptcha/RecaptchaV3TokenParams.cs create mode 100644 CaptchaSharp/Services/EndCaptchaService.cs diff --git a/CaptchaSharp.Tests/ConfigFixture.cs b/CaptchaSharp.Tests/ConfigFixture.cs index 99d7a27..dc57a79 100644 --- a/CaptchaSharp.Tests/ConfigFixture.cs +++ b/CaptchaSharp.Tests/ConfigFixture.cs @@ -64,4 +64,6 @@ public class Credentials public string CaptchaAiApiKey { get; set; } = string.Empty; public string EzCaptchaApiKey { get; set; } = string.Empty; public string SolveCaptchaApiKey { get; set; } = string.Empty; + public string EndCaptchaUsername { get; set; } = string.Empty; + public string EndCaptchaPassword { get; set; } = string.Empty; } diff --git a/CaptchaSharp.Tests/EndCaptchaServiceTests.cs b/CaptchaSharp.Tests/EndCaptchaServiceTests.cs new file mode 100644 index 0000000..e37ba56 --- /dev/null +++ b/CaptchaSharp.Tests/EndCaptchaServiceTests.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using CaptchaSharp.Services; +using Xunit; +using Xunit.Abstractions; + +namespace CaptchaSharp.Tests; + +public class EndCaptchaFixture : ServiceFixture +{ + public EndCaptchaFixture() + { + Service = new EndCaptchaService( + Config.Credentials.EndCaptchaUsername, + Config.Credentials.EndCaptchaPassword); + } +} + +public class EndCaptchaServiceTests(EndCaptchaFixture fixture, ITestOutputHelper output) + : ServiceTests(fixture, output), IClassFixture +{ + [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); + [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); + [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); + [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); + [Fact] public Task SolveRecaptchaV3Async_NoProxy_ValidSolution() => RecaptchaV3Test_NoProxy(); + [Fact] public Task SolveRecaptchaV3Async_WithProxy_ValidSolution() => RecaptchaV3Test_WithProxy(); + [Fact] public Task SolveFuncaptchaAsync_NoProxy_ValidSolution() => FunCaptchaTest_NoProxy(); + [Fact] public Task SolveFuncaptchaAsync_WithProxy_ValidSolution() => FunCaptchaTest_WithProxy(); + [Fact] public Task SolveHCaptchaAsync_NoProxy_ValidSolution() => HCaptchaTest_NoProxy(); + [Fact] public Task SolveHCaptchaAsync_WithProxy_ValidSolution() => HCaptchaTest_WithProxy(); +} diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index a7d4536..4b2102e 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -1561,6 +1561,53 @@ + + + The service provided by https://endcaptcha.com/ + + + + + Your username. + + + + + Your password. + + + + + Initializes a . + + The username to use. + The password to use. + The to use for requests. If null, a default one will be created. + + + + + + + + + + + + + + + + + + + + + + + + + The service offered by https://www.ez-captcha.com/ diff --git a/CaptchaSharp/Models/EndCaptcha/EndCaptchaTokenParams.cs b/CaptchaSharp/Models/EndCaptcha/EndCaptchaTokenParams.cs new file mode 100644 index 0000000..dffe177 --- /dev/null +++ b/CaptchaSharp/Models/EndCaptcha/EndCaptchaTokenParams.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EndCaptcha; + +internal class EndCaptchaTokenParams +{ + [JsonProperty("proxy", NullValueHandling = NullValueHandling.Ignore)] + public string? Proxy { get; set; } + + [JsonProperty("proxytype", NullValueHandling = NullValueHandling.Ignore)] + public string? ProxyType { get; set; } + + public void SetProxy(Proxy? proxy) + { + if (proxy?.Host is null) + { + return; + } + + Proxy = $"{proxy.Type.ToString().ToLower()}://{proxy.Host}:{proxy.Port}"; + ProxyType = proxy.Type.ToString().ToUpper(); + } +} diff --git a/CaptchaSharp/Models/EndCaptcha/FuncaptchaTokenParams.cs b/CaptchaSharp/Models/EndCaptcha/FuncaptchaTokenParams.cs new file mode 100644 index 0000000..285e939 --- /dev/null +++ b/CaptchaSharp/Models/EndCaptcha/FuncaptchaTokenParams.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EndCaptcha; + +internal class FuncaptchaTokenParams : EndCaptchaTokenParams +{ + [JsonProperty("publickey")] + public required string PublicKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Models/EndCaptcha/HCaptchaTokenParams.cs b/CaptchaSharp/Models/EndCaptcha/HCaptchaTokenParams.cs new file mode 100644 index 0000000..f7f6dc7 --- /dev/null +++ b/CaptchaSharp/Models/EndCaptcha/HCaptchaTokenParams.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EndCaptcha; + +internal class HCaptchaTokenParams : EndCaptchaTokenParams +{ + [JsonProperty("sitekey")] + public required string SiteKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Models/EndCaptcha/RecaptchaV2TokenParams.cs b/CaptchaSharp/Models/EndCaptcha/RecaptchaV2TokenParams.cs new file mode 100644 index 0000000..0e7f9a1 --- /dev/null +++ b/CaptchaSharp/Models/EndCaptcha/RecaptchaV2TokenParams.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EndCaptcha; + +internal class RecaptchaV2TokenParams : EndCaptchaTokenParams +{ + [JsonProperty("googlekey")] + public required string GoogleKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Models/EndCaptcha/RecaptchaV3TokenParams.cs b/CaptchaSharp/Models/EndCaptcha/RecaptchaV3TokenParams.cs new file mode 100644 index 0000000..c87ecab --- /dev/null +++ b/CaptchaSharp/Models/EndCaptcha/RecaptchaV3TokenParams.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.EndCaptcha; + +internal class RecaptchaV3TokenParams : EndCaptchaTokenParams +{ + [JsonProperty("googlekey")] + public required string GoogleKey { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } + + [JsonProperty("action")] + public required string Action { get; set; } + + [JsonProperty("min_score")] + public required double MinScore { get; set; } +} diff --git a/CaptchaSharp/Services/EndCaptchaService.cs b/CaptchaSharp/Services/EndCaptchaService.cs new file mode 100644 index 0000000..16c1009 --- /dev/null +++ b/CaptchaSharp/Services/EndCaptchaService.cs @@ -0,0 +1,288 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using CaptchaSharp.Enums; +using CaptchaSharp.Exceptions; +using CaptchaSharp.Extensions; +using CaptchaSharp.Models; +using CaptchaSharp.Models.EndCaptcha; + +namespace CaptchaSharp.Services; + +/// +/// The service provided by https://endcaptcha.com/ +/// +public class EndCaptchaService : CaptchaService +{ + /// + /// Your username. + /// + public string Username { get; set; } + + /// + /// Your password. + /// + public string Password { get; set; } + + /// + /// Initializes a . + /// + /// The username to use. + /// The password to use. + /// The to use for requests. If null, a default one will be created. + public EndCaptchaService(string username, string password, + HttpClient? httpClient = null) : base(httpClient) + { + Username = username; + Password = password; + HttpClient.BaseAddress = new Uri("http://api.endcaptcha.com"); + } + + #region Getting the Balance + /// + public override async Task GetBalanceAsync( + CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync( + "balance", + new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + if (response.StartsWith("ERROR:")) + { + throw new BadAuthenticationException(response); + } + + return decimal.Parse(response); + } + #endregion + + #region Solve Methods + /// + public override async Task SolveImageCaptchaAsync( + string base64, ImageCaptchaOptions? options = null, + CancellationToken cancellationToken = default) + { + // It doesn't work when using base64:... as per the docs + var content = new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .ToMultipartFormDataContent(); + + var bytes = Convert.FromBase64String(base64); + content.Add(new ByteArrayContent(bytes), "image", "image.jpg"); + + var response = await HttpClient.PostMultipartToStringAsync( + "upload", + content, + cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.ImageCaptcha, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveRecaptchaV2Async( + string siteKey, string siteUrl, string dataS = "", bool enterprise = false, + bool invisible = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var tokenParams = new RecaptchaV2TokenParams + { + GoogleKey = siteKey, + PageUrl = siteUrl, + }; + + tokenParams.SetProxy(proxy); + + var response = await HttpClient.PostMultipartToStringAsync( + "upload", + new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .Add("type", 4) + .Add("token_params", tokenParams.Serialize()) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.ReCaptchaV2, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveRecaptchaV3Async( + string siteKey, string siteUrl, string action = "verify", float minScore = 0.4f, + bool enterprise = false, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var tokenParams = new RecaptchaV3TokenParams + { + GoogleKey = siteKey, + PageUrl = siteUrl, + Action = action, + MinScore = minScore + }; + + tokenParams.SetProxy(proxy); + + var response = await HttpClient.PostMultipartToStringAsync( + "upload", + new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .Add("type", 5) + .Add("token_params", tokenParams.Serialize()) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.ReCaptchaV3, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveFuncaptchaAsync( + string publicKey, string serviceUrl, string siteUrl, bool noJs = false, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var tokenParams = new FuncaptchaTokenParams + { + PublicKey = publicKey, + PageUrl = siteUrl + }; + + tokenParams.SetProxy(proxy); + + var response = await HttpClient.PostMultipartToStringAsync( + "upload", + new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .Add("type", 6) + .Add("funcaptcha_params", tokenParams.Serialize()) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.FunCaptcha, cancellationToken) + .ConfigureAwait(false); + } + + /// + public override async Task SolveHCaptchaAsync( + string siteKey, string siteUrl, Proxy? proxy = null, + CancellationToken cancellationToken = default) + { + var tokenParams = new HCaptchaTokenParams + { + SiteKey = siteKey, + PageUrl = siteUrl + }; + + tokenParams.SetProxy(proxy); + + var response = await HttpClient.PostMultipartToStringAsync( + "upload", + new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .Add("type", 7) + .Add("hcaptcha_params", tokenParams.Serialize()) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + response, CaptchaType.HCaptcha, cancellationToken) + .ConfigureAwait(false); + } + #endregion + + #region Getting the result + private async Task GetResult( + string response, CaptchaType type, + CancellationToken cancellationToken = default) where T : CaptchaResponse + { + if (response.StartsWith("ERROR:")) + { + throw new TaskSolutionException(response); + } + + if (response.StartsWith("UNSOLVED_YET:")) + { + var task = new CaptchaTask(response.Split('/')[1], type); + return await GetResult(task, cancellationToken) + .ConfigureAwait(false); + } + + return (new StringResponse + { + Id = "0", + Response = response + } as T)!; + } + + /// + protected override async Task CheckResult( + CaptchaTask task, CancellationToken cancellationToken = default) where T : class + { + var response = await HttpClient.PostMultipartToStringAsync( + $"poll/{task.Id}", + new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + if (response.StartsWith("ERROR:")) + { + throw new TaskSolutionException(response); + } + + if (response.StartsWith("UNSOLVED_YET")) + { + return null; + } + + return new StringResponse + { + Id = task.Id, + Response = response + } as T; + } + #endregion + + #region Reporting the solution + /// + public override async Task ReportSolution( + string id, CaptchaType type, bool correct = false, CancellationToken cancellationToken = default) + { + // Since the service doesn't always return the captcha id, + // we can also pass the hash here (future development) + var response = await HttpClient.PostMultipartToStringAsync( + "report", + new StringPairCollection() + .Add("username", Username) + .Add("password", Password) + .Add("captcha_id", id) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + if (response.StartsWith("ERROR:")) + { + throw new TaskReportException(response); + } + } + #endregion +} From 44c2aac85cce37daf7012c09c2d14f93bb6c3f34 Mon Sep 17 00:00:00 2001 From: Ruri Date: Wed, 24 Jul 2024 12:53:48 +0200 Subject: [PATCH 42/67] Added SolveLeminCroppedAsync method and 2cap/dbc support --- .../DeathByCaptchaServiceTests.cs | 2 + CaptchaSharp.Tests/ImageTyperzServiceTests.cs | 1 + CaptchaSharp.Tests/ServiceTests.cs | 19 +++ CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 2 + CaptchaSharp/CaptchaSharp.xml | 50 +++++++ CaptchaSharp/Enums/CaptchaType.cs | 3 + .../Responses/LeminCroppedDbcResponse.cs | 12 ++ .../Tasks/LeminCroppedDbcTaskProxyless.cs | 12 ++ .../Tasks/Proxied/LeminCroppedDbcTask.cs | 12 ++ CaptchaSharp/Models/LeminCroppedResponse.cs | 17 +++ .../TwoCaptcha/TwoCaptchaCapyResponse.cs | 18 ++- .../TwoCaptcha/TwoCaptchaGeeTestResponse.cs | 12 +- .../TwoCaptchaLeminCroppedResponse.cs | 27 ++++ CaptchaSharp/Services/CaptchaService.cs | 31 +++++ .../Services/DeathByCaptchaService.cs | 48 +++++++ CaptchaSharp/Services/TwoCaptchaService.cs | 130 ++++++++++++------ README.md | 7 +- 17 files changed, 346 insertions(+), 57 deletions(-) create mode 100644 CaptchaSharp/Models/DeathByCaptcha/Responses/LeminCroppedDbcResponse.cs create mode 100644 CaptchaSharp/Models/DeathByCaptcha/Tasks/LeminCroppedDbcTaskProxyless.cs create mode 100644 CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/LeminCroppedDbcTask.cs create mode 100644 CaptchaSharp/Models/LeminCroppedResponse.cs create mode 100644 CaptchaSharp/Models/TwoCaptcha/TwoCaptchaLeminCroppedResponse.cs diff --git a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs index 11fd476..df75c24 100644 --- a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs @@ -44,4 +44,6 @@ public class DeathByCaptchaServiceTests(DeathByCaptchaFixture fixture, ITestOutp [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); + [Fact] public Task SolveLeminCroppedAsync_NoProxy_ValidSolution() => LeminCroppedTest_NoProxy(); + [Fact] public Task SolveLeminCroppedAsync_WithProxy_ValidSolution() => LeminCroppedTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs index f971b92..66da92c 100644 --- a/CaptchaSharp.Tests/ImageTyperzServiceTests.cs +++ b/CaptchaSharp.Tests/ImageTyperzServiceTests.cs @@ -17,6 +17,7 @@ public class ImageTyperzServiceTests(ImageTyperzFixture fixture, ITestOutputHelp : ServiceTests(fixture, output), IClassFixture { [Fact] public Task GetBalanceAsync_ValidKey_GetsBalance() => BalanceTest(); + [Fact] public Task ReportSolution_NoException() => ReportImageSolutionTest(); [Fact] public Task SolveImageCaptchaAsync_ValidCaptcha_ValidSolution() => ImageCaptchaTest(); [Fact] public Task SolveRecaptchaV2Async_NoProxy_ValidSolution() => RecaptchaV2Test_NoProxy(); [Fact] public Task SolveRecaptchaV2Async_WithProxy_ValidSolution() => RecaptchaV2Test_WithProxy(); diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index 34b6c33..adaa81e 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -402,4 +402,23 @@ protected Task CloudflareTurnstileTest_NoProxy() => CloudflareTurnstileTest(new }); protected Task CloudflareTurnstileTest_WithProxy() => CloudflareTurnstileTest(_fixture.Config.Proxy); + + private async Task LeminCroppedTest(Proxy? proxy) + { + var solution = await Service.SolveLeminCroppedAsync( + captchaId: "CROPPED_3dfdd5c_d1872b526b794d83ba3b365eb15a200b", + siteUrl: "https://2captcha.com/demo/lemin", + proxy: proxy); + + Assert.NotEqual(string.Empty, solution.Answer); + Assert.NotEqual(string.Empty, solution.ChallengeId); + + _output.WriteLine($"Captcha ID: {solution.Id}"); + _output.WriteLine($"Answer: {solution.Answer}"); + _output.WriteLine($"Challenge ID: {solution.ChallengeId}"); + } + + protected Task LeminCroppedTest_NoProxy() => LeminCroppedTest(null); + + protected Task LeminCroppedTest_WithProxy() => LeminCroppedTest(_fixture.Config.Proxy); } diff --git a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs index 24c0e0e..faf0fbb 100644 --- a/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/TwoCaptchaServiceTests.cs @@ -43,4 +43,6 @@ public class TwoCaptchaServiceTests(TwoCaptchaFixture fixture, ITestOutputHelper [Fact] public Task SolveDataDomeAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); + [Fact] public Task SolveLeminCroppedAsync_NoProxy_ValidSolution() => LeminCroppedTest_NoProxy(); + [Fact] public Task SolveLeminCroppedAsync_WithProxy_ValidSolution() => LeminCroppedTest_WithProxy(); } diff --git a/CaptchaSharp/CaptchaSharp.xml b/CaptchaSharp/CaptchaSharp.xml index 4b2102e..98c17bc 100644 --- a/CaptchaSharp/CaptchaSharp.xml +++ b/CaptchaSharp/CaptchaSharp.xml @@ -256,6 +256,9 @@ Cloudflare's Turnstile captcha. + + Lemin Cropped captcha. + @@ -702,6 +705,21 @@ The captcha id. + + + A captcha response for Lemin Cropped Captchas. + + + + + The answer to the challenge. + + + + + The challenge ID. + + Seconds since UNIX epoch. @@ -1422,6 +1440,32 @@ + + Solves a Lemin Cropped captcha. + + The value of the captcha_id parameter on the page. + The URL where the captcha appears. + The domain part of script URL you found on page. If null, the default one will be used. + The id of captcha parent div element. + + + A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + fetch the captcha without using a proxy. + + + A token that can be used to cancel the async task. + + + A containing the captcha id to be used with + and the + captcha solution as plaintext. + + + + + + Reports a captcha solution as good or bad to the service. @@ -1555,6 +1599,9 @@ + + + @@ -2067,6 +2114,9 @@ + + + diff --git a/CaptchaSharp/Enums/CaptchaType.cs b/CaptchaSharp/Enums/CaptchaType.cs index 9fa700f..7874df6 100644 --- a/CaptchaSharp/Enums/CaptchaType.cs +++ b/CaptchaSharp/Enums/CaptchaType.cs @@ -38,4 +38,7 @@ public enum CaptchaType /// Cloudflare's Turnstile captcha. CloudflareTurnstile = 1 << 10, + + /// Lemin Cropped captcha. + LeminCropped = 1 << 11, } diff --git a/CaptchaSharp/Models/DeathByCaptcha/Responses/LeminCroppedDbcResponse.cs b/CaptchaSharp/Models/DeathByCaptcha/Responses/LeminCroppedDbcResponse.cs new file mode 100644 index 0000000..029df60 --- /dev/null +++ b/CaptchaSharp/Models/DeathByCaptcha/Responses/LeminCroppedDbcResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.DeathByCaptcha.Responses; + +internal class LeminCroppedDbcResponse +{ + [JsonProperty("answer")] + public required string Answer { get; set; } + + [JsonProperty("challengeid")] + public required string ChallengeId { get; set; } +} diff --git a/CaptchaSharp/Models/DeathByCaptcha/Tasks/LeminCroppedDbcTaskProxyless.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/LeminCroppedDbcTaskProxyless.cs new file mode 100644 index 0000000..5df44af --- /dev/null +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/LeminCroppedDbcTaskProxyless.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks; + +internal class LeminCroppedDbcTaskProxyless : DbcTaskProxyless +{ + [JsonProperty("captchaid")] + public required string CaptchaId { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/LeminCroppedDbcTask.cs b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/LeminCroppedDbcTask.cs new file mode 100644 index 0000000..80e543e --- /dev/null +++ b/CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/LeminCroppedDbcTask.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.DeathByCaptcha.Tasks.Proxied; + +internal class LeminCroppedDbcTask : DbcTask +{ + [JsonProperty("captchaid")] + public required string CaptchaId { get; set; } + + [JsonProperty("pageurl")] + public required string PageUrl { get; set; } +} diff --git a/CaptchaSharp/Models/LeminCroppedResponse.cs b/CaptchaSharp/Models/LeminCroppedResponse.cs new file mode 100644 index 0000000..7d3f504 --- /dev/null +++ b/CaptchaSharp/Models/LeminCroppedResponse.cs @@ -0,0 +1,17 @@ +namespace CaptchaSharp.Models; + +/// +/// A captcha response for Lemin Cropped Captchas. +/// +public class LeminCroppedResponse : CaptchaResponse +{ + /// + /// The answer to the challenge. + /// + public required string Answer { get; set; } + + /// + /// The challenge ID. + /// + public required string ChallengeId { get; set; } +} diff --git a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs index 9ebb126..0006f6d 100644 --- a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaCapyResponse.cs @@ -1,4 +1,5 @@ using CaptchaSharp.Models; +using Newtonsoft.Json; namespace CaptchaSharp.Models.TwoCaptcha; @@ -9,18 +10,23 @@ internal class TwoCaptchaCapyResponse : TwoCaptchaResponse internal class CapySolution { - public string? CaptchaKey { get; set; } - public string? ChallengeKey { get; set; } - public string? Answer { get; set; } + [JsonProperty("captchakey")] + public required string CaptchaKey { get; set; } + + [JsonProperty("challengekey")] + public required string ChallengeKey { get; set; } + + [JsonProperty("answer")] + public required string Answer { get; set; } public CapyResponse ToCapyResponse(string id) { return new CapyResponse { Id = id, - CaptchaKey = CaptchaKey!, - ChallengeKey = ChallengeKey!, - Answer = Answer! + CaptchaKey = CaptchaKey, + ChallengeKey = ChallengeKey, + Answer = Answer }; } } diff --git a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs index c2d1db5..78a6ebb 100644 --- a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaGeeTestResponse.cs @@ -11,22 +11,22 @@ internal class TwoCaptchaGeeTestResponse : TwoCaptchaResponse internal class GeeTestSolution { [JsonProperty("geetest_challenge")] - public string? Challenge { get; set; } + public required string Challenge { get; set; } [JsonProperty("geetest_validate")] - public string? Validate { get; set; } + public required string Validate { get; set; } [JsonProperty("geetest_seccode")] - public string? SecCode { get; set; } + public required string SecCode { get; set; } public GeeTestResponse ToGeeTestResponse(string id) { return new GeeTestResponse { Id = id, - Challenge = Challenge!, - Validate = Validate!, - SecCode = SecCode! + Challenge = Challenge, + Validate = Validate, + SecCode = SecCode }; } } diff --git a/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaLeminCroppedResponse.cs b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaLeminCroppedResponse.cs new file mode 100644 index 0000000..a96f10e --- /dev/null +++ b/CaptchaSharp/Models/TwoCaptcha/TwoCaptchaLeminCroppedResponse.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace CaptchaSharp.Models.TwoCaptcha; + +internal class TwoCaptchaLeminCroppedResponse : TwoCaptchaResponse +{ + public new LeminCroppedSolution? Request { get; set; } +} + +internal class LeminCroppedSolution +{ + [JsonProperty("answer")] + public required string Answer { get; set; } + + [JsonProperty("challenge_id")] + public required string ChallengeId { get; set; } + + public LeminCroppedResponse ToLeminCroppedResponse(string id) + { + return new LeminCroppedResponse + { + Id = id, + Answer = Answer, + ChallengeId = ChallengeId + }; + } +} diff --git a/CaptchaSharp/Services/CaptchaService.cs b/CaptchaSharp/Services/CaptchaService.cs index e98f259..b10a469 100644 --- a/CaptchaSharp/Services/CaptchaService.cs +++ b/CaptchaSharp/Services/CaptchaService.cs @@ -388,6 +388,37 @@ public virtual Task SolveCloudflareTurnstileAsync( { throw new NotSupportedException(); } + + /// Solves a Lemin Cropped captcha. + /// + /// The value of the captcha_id parameter on the page. + /// The URL where the captcha appears. + /// The domain part of script URL you found on page. If null, the default one will be used. + /// The id of captcha parent div element. + /// + /// + /// A proxy that can be used by the captcha service to fetch the captcha challenge from the same IP you are + /// going to send it from when you submit the form. It can help bypass some blocks. If null, the service will + /// fetch the captcha without using a proxy. + /// + /// + /// A token that can be used to cancel the async task. + /// + /// + /// A containing the captcha id to be used with + /// and the + /// captcha solution as plaintext. + /// + /// + /// + /// + /// + public virtual Task SolveLeminCroppedAsync( + string captchaId, string siteUrl, string apiServer = "https://api.leminnow.com/", + string? divId = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } /// /// Reports a captcha solution as good or bad to the service. diff --git a/CaptchaSharp/Services/DeathByCaptchaService.cs b/CaptchaSharp/Services/DeathByCaptchaService.cs index 6db71b1..9faa184 100644 --- a/CaptchaSharp/Services/DeathByCaptchaService.cs +++ b/CaptchaSharp/Services/DeathByCaptchaService.cs @@ -430,6 +430,43 @@ public override async Task SolveCloudflareTurnstile HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), CaptchaType.CloudflareTurnstile, cancellationToken); } + + /// + public override async Task SolveLeminCroppedAsync( + string captchaId, string siteUrl, string apiServer = "https://api.leminnow.com/", + string? divId = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + DbcTaskProxyless task; + + if (proxy is not null) + { + task = new LeminCroppedDbcTask + { + CaptchaId = captchaId, + PageUrl = siteUrl + }.SetProxy(proxy); + } + else + { + task = new LeminCroppedDbcTaskProxyless + { + CaptchaId = captchaId, + PageUrl = siteUrl + }; + } + + var response = await HttpClient.PostAsync( + "captcha", + GetAuthPair() + .Add("type", 14) + .Add("lemin_params", task.Serialize()), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await GetResult( + HttpUtility.ParseQueryString(await DecodeIsoResponse(response)), + CaptchaType.LeminCropped, cancellationToken); + } #endregion #region Getting the result @@ -500,6 +537,17 @@ private async Task GetResult( } as T; } + if (typeof(T) == typeof(LeminCroppedResponse)) + { + var leminCroppedResponse = text.Deserialize(); + return new LeminCroppedResponse + { + Id = task.Id, + Answer = leminCroppedResponse.Answer, + ChallengeId = leminCroppedResponse.ChallengeId + } as T; + } + if (typeof(T) == typeof(CloudflareTurnstileResponse)) { return new CloudflareTurnstileResponse diff --git a/CaptchaSharp/Services/TwoCaptchaService.cs b/CaptchaSharp/Services/TwoCaptchaService.cs index 5d096f6..435da8f 100644 --- a/CaptchaSharp/Services/TwoCaptchaService.cs +++ b/CaptchaSharp/Services/TwoCaptchaService.cs @@ -413,6 +413,36 @@ public override async Task SolveCloudflareTurnstile response, CaptchaType.CloudflareTurnstile, cancellationToken).ConfigureAwait(false); } + + /// + public override async Task SolveLeminCroppedAsync( + string captchaId, string siteUrl, string apiServer = "https://api.leminnow.com/", + string? divId = null, Proxy? proxy = null, CancellationToken cancellationToken = default) + { + var response = await HttpClient.PostMultipartToStringAsync("in.php", + new StringPairCollection() + .Add("key", ApiKey) + .Add("method", "lemin") + .Add("captcha_id", captchaId) + .Add("pageurl", siteUrl) + .Add("api_server", apiServer) + .Add("div_id", divId ?? string.Empty, !string.IsNullOrEmpty(divId)) + .Add("soft_id", _softId) + .Add("json", "1", UseJsonFlag) + .Add("header_acao", "1", AddAcaoHeader) + .Add(ConvertProxy(proxy)) + .ToMultipartFormDataContent(), + cancellationToken) + .ConfigureAwait(false); + + return UseJsonFlag + ? await GetResult( + response.Deserialize(), CaptchaType.LeminCropped, + cancellationToken).ConfigureAwait(false) + : await GetResult( + response, CaptchaType.LeminCropped, + cancellationToken).ConfigureAwait(false); + } #endregion #region Getting the result @@ -462,69 +492,81 @@ internal async Task GetResult( task.Completed = true; - if (UseJsonFlag) + try { - if (task.Type == CaptchaType.GeeTest) + if (UseJsonFlag) { - var jObject = JObject.Parse(response); - var solution = jObject["request"]; - - if (solution is null) + if (task.Type == CaptchaType.GeeTest) { - throw new TaskSolutionException("No solution found"); + var jObject = JObject.Parse(response); + var solution = jObject["request"]; + + if (solution is null) + { + throw new TaskSolutionException("No solution found"); + } + + if (solution.Type == JTokenType.Object) + { + return response.Deserialize() + .Request?.ToGeeTestResponse(task.Id) as T; + } } - - if (solution.Type == JTokenType.Object) + else if (task.Type == CaptchaType.Capy) { - return response.Deserialize() - .Request?.ToGeeTestResponse(task.Id) as T; + var jObject = JObject.Parse(response); + var solution = jObject["request"]; + + if (solution is null) + { + throw new TaskSolutionException("No solution found"); + } + + if (solution.Type == JTokenType.Object) + { + return response.Deserialize() + .Request!.ToCapyResponse(task.Id) as T; + } } - } - else if (task.Type == CaptchaType.Capy) - { - var jObject = JObject.Parse(response); - var solution = jObject["request"]; - - if (solution is null) + else if (task.Type == CaptchaType.CloudflareTurnstile) + { + return response.Deserialize() + .ToCloudflareTurnstileResponse(task.Id) as T; + } + else if (task.Type == CaptchaType.LeminCropped) { - throw new TaskSolutionException("No solution found"); + return response.Deserialize() + .Request!.ToLeminCroppedResponse(task.Id) as T; } - - if (solution.Type == JTokenType.Object) + + var tcResponse = response.Deserialize(); + + if (tcResponse.IsErrorCode) { - return response.Deserialize() - .Request!.ToCapyResponse(task.Id) as T; + throw new TaskSolutionException(tcResponse.ErrorText!); } + + return new StringResponse { Id = task.Id, Response = tcResponse.Request! } as T; } - else if (task.Type == CaptchaType.CloudflareTurnstile) + + if (IsErrorCode(response)) { - return response.Deserialize() - .ToCloudflareTurnstileResponse(task.Id) as T; + throw new TaskSolutionException(response); } - var tcResponse = response.Deserialize(); + response = TakeSecondSlice(response); - if (tcResponse.IsErrorCode) + return task.Type switch { - throw new TaskSolutionException(tcResponse.ErrorText!); - } - - return new StringResponse { Id = task.Id, Response = tcResponse.Request! } as T; + CaptchaType.GeeTest => response.Deserialize().ToGeeTestResponse(task.Id) as T, + CaptchaType.Capy => response.Deserialize().ToCapyResponse(task.Id) as T, + _ => new StringResponse { Id = task.Id, Response = response } as T + }; } - - if (IsErrorCode(response)) + catch (Exception ex) { - throw new TaskSolutionException(response); + throw new TaskSolutionException(response, ex); } - - response = TakeSecondSlice(response); - - return task.Type switch - { - CaptchaType.GeeTest => response.Deserialize().ToGeeTestResponse(task.Id) as T, - CaptchaType.Capy => response.Deserialize().ToCapyResponse(task.Id) as T, - _ => new StringResponse { Id = task.Id, Response = response } as T - }; } #endregion diff --git a/README.md b/README.md index dcdcffb..365c867 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,18 @@ All services derive from the same `CaptchaService` class and have the same code This library supports the following captcha types - Text (with language options) - Image (with options like phrase, case sensitivity, calculations) -- FunCaptcha - ReCaptcha V2 (incl. invisible, enterprise) - ReCaptcha V3 (incl. enterprise) +- FunCaptcha - HCaptcha - KeyCaptcha - GeeTest - Capy +- DataDome +- Cloudflare Turnstile +- Lemin Cropped + +Proxies are supported for services that support them. Not every captcha type is supported by each service. You can find a spreadsheet with a breakdown of the supported captcha types for each implemented service at the following link From 0b14d6940c1d0cf9a66ebee1569fa0810b72d4a9 Mon Sep 17 00:00:00 2001 From: Ruri Date: Wed, 24 Jul 2024 17:24:23 +0200 Subject: [PATCH 43/67] Added SolveAmazonWafAsync and 2cap/dbc/capsolver support --- CaptchaSharp.Tests/CapSolverServiceTests.cs | 2 + .../DeathByCaptchaServiceTests.cs | 2 + CaptchaSharp.Tests/ServiceTests.cs | 34 ++++++++++++++ CaptchaSharp.Tests/TwoCaptchaServiceTests.cs | 2 + CaptchaSharp/CaptchaSharp.xml | 44 +++++++++++++++++- CaptchaSharp/Enums/CaptchaType.cs | 3 ++ .../Requests/Tasks/AntiAwsWafTaskProxyless.cs | 26 +++++++++++ .../Requests/Tasks/Proxied/AntiAwsWafTask.cs | 26 +++++++++++ .../Responses/Solutions/AmazonWafSolution.cs | 18 ++++++++ .../Tasks/AmazonWafDbcTaskProxyless.cs | 24 ++++++++++ .../Tasks/Proxied/AmazonWafDbcTask.cs | 24 ++++++++++ .../TwoCaptcha/TwoCaptchaAmazonWafResponse.cs | 26 +++++++++++ .../TwoCaptcha/TwoCaptchaCapyResponse.cs | 3 +- CaptchaSharp/Services/CapSolverService.cs | 41 +++++++++++++++++ CaptchaSharp/Services/CaptchaService.cs | 40 +++++++++++++++-- .../Services/DeathByCaptchaService.cs | 45 +++++++++++++++++++ CaptchaSharp/Services/TwoCaptchaService.cs | 37 +++++++++++++++ README.md | 1 + 18 files changed, 391 insertions(+), 7 deletions(-) create mode 100644 CaptchaSharp/Models/CapSolver/Requests/Tasks/AntiAwsWafTaskProxyless.cs create mode 100644 CaptchaSharp/Models/CapSolver/Requests/Tasks/Proxied/AntiAwsWafTask.cs create mode 100644 CaptchaSharp/Models/CapSolver/Responses/Solutions/AmazonWafSolution.cs create mode 100644 CaptchaSharp/Models/DeathByCaptcha/Tasks/AmazonWafDbcTaskProxyless.cs create mode 100644 CaptchaSharp/Models/DeathByCaptcha/Tasks/Proxied/AmazonWafDbcTask.cs create mode 100644 CaptchaSharp/Models/TwoCaptcha/TwoCaptchaAmazonWafResponse.cs diff --git a/CaptchaSharp.Tests/CapSolverServiceTests.cs b/CaptchaSharp.Tests/CapSolverServiceTests.cs index 2e08820..f042eb9 100644 --- a/CaptchaSharp.Tests/CapSolverServiceTests.cs +++ b/CaptchaSharp.Tests/CapSolverServiceTests.cs @@ -36,4 +36,6 @@ public class CapSolverServiceTests(CapSolverFixture fixture, ITestOutputHelper o [Fact] public Task SolveGeeTestAsync_NoProxy_ValidSolution() => GeeTestTest_NoProxy(); [Fact] public Task SolveDataDomeTestAsync_WithProxy_ValidSolution() => DataDomeTest_WithProxy(); [Fact] public Task SolveCloudflareTurnstileAsync_NoProxy_ValidSolution() => CloudflareTurnstileTest_NoProxy(); + [Fact] public Task SolveAmazonWafAsync_NoProxy_ValidSolution() => AmazonWafTest_NoProxy(); + [Fact] public Task SolveAmazonWafAsync_WithProxy_ValidSolution() => AmazonWafTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs index df75c24..0796919 100644 --- a/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs +++ b/CaptchaSharp.Tests/DeathByCaptchaServiceTests.cs @@ -46,4 +46,6 @@ public class DeathByCaptchaServiceTests(DeathByCaptchaFixture fixture, ITestOutp [Fact] public Task SolveCloudflareTurnstileAsync_WithProxy_ValidSolution() => CloudflareTurnstileTest_WithProxy(); [Fact] public Task SolveLeminCroppedAsync_NoProxy_ValidSolution() => LeminCroppedTest_NoProxy(); [Fact] public Task SolveLeminCroppedAsync_WithProxy_ValidSolution() => LeminCroppedTest_WithProxy(); + [Fact] public Task SolveAmazonWafAsync_NoProxy_ValidSolution() => AmazonWafTest_NoProxy(); + [Fact] public Task SolveAmazonWafAsync_WithProxy_ValidSolution() => AmazonWafTest_WithProxy(); } diff --git a/CaptchaSharp.Tests/ServiceTests.cs b/CaptchaSharp.Tests/ServiceTests.cs index adaa81e..68564ad 100644 --- a/CaptchaSharp.Tests/ServiceTests.cs +++ b/CaptchaSharp.Tests/ServiceTests.cs @@ -421,4 +421,38 @@ private async Task LeminCroppedTest(Proxy? proxy) protected Task LeminCroppedTest_NoProxy() => LeminCroppedTest(null); protected Task LeminCroppedTest_WithProxy() => LeminCroppedTest(_fixture.Config.Proxy); + + private async Task AmazonWafTest(Proxy? proxy) + { + using var httpClient = new HttpClient(); + using var response = await httpClient.GetAsync("https://nopecha.com/captcha/awscaptcha"); + var pageSource = await response.Content.ReadAsStringAsync(); + + var captchaPage = Regex.Match(pageSource, "