diff --git a/.ruby-version b/.ruby-version index 4a36342..e70b452 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.0.0 +2.6.0 diff --git a/README.md b/README.md index e050ea9..26b86d2 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ A Ruby client for the 2Captcha API. - [Error handling](#error-handling) ## Installation - Add this line to your application's Gemfile: ```bash @@ -56,7 +55,7 @@ $ gem install ruby-2captcha To use the api2captcha gem, you'll need to import the module and create a Client instance. Here's an example: ```ruby -require 'api-2captcha' +require 'api_2captcha' client = Api2Captcha.new("YOUR_API_KEY") ``` diff --git a/README.ru.md b/README.ru.md index 766df00..530d6af 100644 --- a/README.ru.md +++ b/README.ru.md @@ -32,7 +32,6 @@ Ruby-клиент для API 2Captcha. - [Обработка ошибок](#error-handling) ## Установка - Автоматическая установка гема с помощью Bundler. Добавьте следующую строку в ваш Gemfile: ```ruby gem 'ruby-2captcha' @@ -54,7 +53,10 @@ gem install ruby-2captcha Описание всех необходимых параметров для настройки установленного гема. Экземпляр класса Api2Captcha можно создать следующим образом: + ```ruby +require 'api_2captcha' + client = Api2Captcha.new("YOUR_API_KEY") ``` diff --git a/lib/api_2captcha.rb b/lib/api_2captcha.rb index cba5433..6279c2e 100644 --- a/lib/api_2captcha.rb +++ b/lib/api_2captcha.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require_relative "api_2captcha/version" +require_relative 'api_2captcha/api2captcha_exceptions' +require_relative 'api_2captcha/client' +require_relative 'api_2captcha/version' module Api2Captcha - require_relative 'api_2captcha/api2captcha_exceptions' def self.new(*args) Client.new(*args) end end -require 'api_2captcha/client' diff --git a/lib/api_2captcha/client.rb b/lib/api_2captcha/client.rb index c2b6c2a..c6db55c 100644 --- a/lib/api_2captcha/client.rb +++ b/lib/api_2captcha/client.rb @@ -5,277 +5,279 @@ require 'base64' require 'open-uri' -class Api2Captcha::Client - DEFAULT_DOMAIN = "2captcha.com" - BASE_URL_FORMAT = "https://%s" - - attr_reader :api_key, :soft_id - - attr_accessor :domain, :callback, - :default_timeout, - :recaptcha_timeout, - :polling_interval - - def initialize(api_key, soft_id = 0, callback = nil) - @api_key = api_key - @soft_id = soft_id - @callback = callback - @default_timeout = 120 - @recaptcha_timeout = 600 - @polling_interval = 10 - @domain = DEFAULT_DOMAIN - end - - def solve(method, params = {}, file_path = nil, return_id: false) - params["method"] = method - params["key"] = @api_key - params["soft_id"] = @soft_id - params["json"] = 1 - - if @callback - params["pingback"] = @callback - return_id = true +module Api2Captcha + class Client + DEFAULT_DOMAIN = "2captcha.com" + BASE_URL_FORMAT = "https://%s" + + attr_reader :api_key, :soft_id + + attr_accessor :domain, :callback, + :default_timeout, + :recaptcha_timeout, + :polling_interval + + def initialize(api_key, soft_id = 0, callback = nil) + @api_key = api_key + @soft_id = soft_id + @callback = callback + @default_timeout = 120 + @recaptcha_timeout = 600 + @polling_interval = 10 + @domain = DEFAULT_DOMAIN end - complete_params = get_params(params) - captcha_id = send_request(complete_params) - return captcha_id if return_id - get_result(captcha_id) - end + def solve(method, params = {}, file_path = nil, return_id: false) + params["method"] = method + params["key"] = @api_key + params["soft_id"] = @soft_id + params["json"] = 1 - def send(*args) - raise ArgumentError, - "Invalid arguments of the send method" unless args.size == 1 + if @callback + params["pingback"] = @callback + return_id = true + end - arg = args.first - if arg.is_a?(String) - solve("POST", {}, arg, return_id: true) - elsif arg.is_a?(Hash) - method = arg.delete(:method) || "POST" - solve(method, arg, return_id: true) - else - raise ArgumentError, "Invalid arguments of the send method" + complete_params = get_params(params) + captcha_id = send_request(complete_params) + return captcha_id if return_id + get_result(captcha_id) end - end - def get_result(captcha_id) - uri = URI("#{base_url}/res.php?key=#{@api_key}&action=get&id=#{captcha_id}&json=1") - start_time = Time.now + def send(*args) + raise ArgumentError, + "Invalid arguments of the send method" unless args.size == 1 - loop do - response = make_request(uri) + arg = args.first + if arg.is_a?(String) + solve("POST", {}, arg, return_id: true) + elsif arg.is_a?(Hash) + method = arg.delete(:method) || "POST" + solve(method, arg, return_id: true) + else + raise ArgumentError, "Invalid arguments of the send method" + end + end - case response - when Net::HTTPSuccess - response_json = JSON.parse(response.body) - if response_json["status"] == 1 - return response_json["request"] - elsif response_json["request"] == "CAPCHA_NOT_READY" - sleep(polling_interval) + def get_result(captcha_id) + uri = URI("#{base_url}/res.php?key=#{@api_key}&action=get&id=#{captcha_id}&json=1") + start_time = Time.now + + loop do + response = make_request(uri) + + case response + when Net::HTTPSuccess + response_json = JSON.parse(response.body) + if response_json["status"] == 1 + return response_json["request"] + elsif response_json["request"] == "CAPCHA_NOT_READY" + sleep(polling_interval) + else + raise ApiException, "API Error: #{response_json["request"]}" + end else - raise ApiException, "API Error: #{response_json["error_text"]}" + raise NetworkException, "Network Error: #{response.code.to_i}" end - else - raise NetworkException, "Network Error: #{response.code.to_i}" - end - raise TimeoutException, "Timeout" if Time.now - start_time > default_timeout + raise TimeoutException, "Timeout" if Time.now - start_time > default_timeout + end end - end - def report(captcha_id, is_correct) - report = is_correct ? "reportgood" : "reportbad" - uri = URI("#{base_url}/res.php?key=#{@api_key}&action=#{report}&id=#{captcha_id}") - make_request(uri) - end + def report(captcha_id, is_correct) + report = is_correct ? "reportgood" : "reportbad" + uri = URI("#{base_url}/res.php?key=#{@api_key}&action=#{report}&id=#{captcha_id}") + make_request(uri) + end - def get_balance - response = make_res_request({ "action" => "getbalance" }, "getbalance") - return response["request"].to_f - end + def get_balance + response = make_res_request({ "action" => "getbalance" }, "getbalance") + return response["request"].to_f + end - def normal(params) - solve("post", params) - end + def normal(params) + solve("post", params) + end - def text(params) - solve("textcaptcha", params) - end + def text(params) + solve("textcaptcha", params) + end - def recaptcha_v2(params) - solve("userrecaptcha", params) - end + def recaptcha_v2(params) + solve("userrecaptcha", params) + end - def recaptcha_v3(params) - solve("userrecaptcha", params) - end + def recaptcha_v3(params) + solve("userrecaptcha", params) + end - def funcaptcha(params) - solve("funcaptcha", params) - end + def funcaptcha(params) + solve("funcaptcha", params) + end - def geetest(params) - solve("geetest", params) - end + def geetest(params) + solve("geetest", params) + end - def hcaptcha(params) - solve("hcaptcha", params) - end + def hcaptcha(params) + solve("hcaptcha", params) + end - def keycaptcha(params) - solve("keycaptcha", params) - end + def keycaptcha(params) + solve("keycaptcha", params) + end - def capy(params) - solve("capy", params) - end + def capy(params) + solve("capy", params) + end - def grid(params) - params["recaptcha"] = 1 - solve("post", params) - end + def grid(params) + params["recaptcha"] = 1 + solve("post", params) + end - def canvas(params) - params["recaptcha"] = 1 - params["canvas"] = 1 - solve("post", params) - end + def canvas(params) + params["recaptcha"] = 1 + params["canvas"] = 1 + solve("post", params) + end - def coordinates(params) - params["coordinatescaptcha"] = 1 + def coordinates(params) + params["coordinatescaptcha"] = 1 - solve("post", params) - end + solve("post", params) + end - def rotate(params) - solve("rotatecaptcha", params) - end + def rotate(params) + solve("rotatecaptcha", params) + end - def geetest_v4(params) - solve("geetest_v4", params) - end + def geetest_v4(params) + solve("geetest_v4", params) + end - def lemin(params) - solve("lemin", params) - end + def lemin(params) + solve("lemin", params) + end - def turnstile(params) - solve("turnstile", params) - end + def turnstile(params) + solve("turnstile", params) + end - def amazon_waf(params) - solve("amazon_waf", params) - end + def amazon_waf(params) + solve("amazon_waf", params) + end - def audio(params) - audio = params.delete(:audio) - audio_content = File.file?(audio) ? File.binread(audio) : audio + def audio(params) + audio = params.delete(:audio) + audio_content = File.file?(audio) ? File.binread(audio) : audio - params = params.merge( - "body" => Base64.strict_encode64(audio_content), - "lang" => params[:lang] - ) - solve("audio", params) - end + params = params.merge( + "body" => Base64.strict_encode64(audio_content), + "lang" => params[:lang] + ) + solve("audio", params) + end - def yandex(params) - solve("yandex", params) - end + def yandex(params) + solve("yandex", params) + end - private + private - def base_url - BASE_URL_FORMAT % @domain - end + def base_url + BASE_URL_FORMAT % @domain + end - def send_request(params) - uri = URI("#{base_url}/in.php") - req = Net::HTTP::Post.new(uri) - req.content_type = 'application/json' - req.body = params.to_json - captcha_id = get_captcha_id(make_request(uri, req)) - end + def send_request(params) + uri = URI("#{base_url}/in.php") + req = Net::HTTP::Post.new(uri) + req.content_type = 'application/json' + req.body = params.to_json + captcha_id = get_captcha_id(make_request(uri, req)) + end - def get_params(params) - params[:image].nil? ? params : file_params(params) - end + def get_params(params) + params[:image].nil? ? params : file_params(params) + end - def file_params(params) - image = params.delete(:image) - hint_image = params.delete(:hint_image) - - image_content = get_image_content(image) - hint_image_content = get_image_content(hint_image) if hint_image - result_params = { - "method" => "base64", - "body" => Base64.strict_encode64(image_content), - "filename" => File.basename(image), - "ext" => File.extname(image).delete(".") - } - - result_params["imginstructions"] = Base64.strict_encode64(hint_image_content) if hint_image_content - params.merge(result_params) - end + def file_params(params) + image = params.delete(:image) + hint_image = params.delete(:hint_image) + + image_content = get_image_content(image) + hint_image_content = get_image_content(hint_image) if hint_image + result_params = { + "method" => "base64", + "body" => Base64.strict_encode64(image_content), + "filename" => File.basename(image), + "ext" => File.extname(image).delete(".") + } + + result_params["imginstructions"] = Base64.strict_encode64(hint_image_content) if hint_image_content + params.merge(result_params) + end - def get_image_content(image) - return download_image(image) if image.start_with?('http') - return File.binread(image) if File.file?(image) - image - end + def get_image_content(image) + return download_image(image) if image.start_with?('http') + return File.binread(image) if File.file?(image) + image + end - def download_image(url) - response = URI.open(url) - if response.status[0] != '200' - raise StandardError, "File could not be downloaded from url: #{url}" + def download_image(url) + response = URI.open(url) + if response.status[0] != '200' + raise StandardError, "File could not be downloaded from url: #{url}" + end + response.read end - response.read - end - def handle_response(captcha_id) - captcha_result = get_result(captcha_id) if @callback.nil? - @callback&.call(captcha_id) - captcha_result - end + def handle_response(captcha_id) + captcha_result = get_result(captcha_id) if @callback.nil? + @callback&.call(captcha_id) + captcha_result + end - def get_captcha_id(response) - case response - when Net::HTTPSuccess - response_json = JSON.parse(response.body.strip) - if response_json["status"] == 1 - response_json["request"] + def get_captcha_id(response) + case response + when Net::HTTPSuccess + response_json = JSON.parse(response.body.strip) + if response_json["status"] == 1 + response_json["request"] + else + raise ApiException, "API Error: #{response_json["error_text"]}" + end else - raise ApiException, "API Error: #{response.body.strip}" + raise NetworkException, "Network Error: #{response.code.to_i}" end - else - raise NetworkException, "Network Error: #{response.code.to_i}" + rescue JSON::ParserError => e + raise "Failed to parse response: #{e.message}" end - rescue JSON::ParserError => e - raise "Failed to parse response: #{e.message}" - end - def make_request(uri, req = nil) - if req.nil? - Net::HTTP.get_response(uri) - else - Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| - http.request(req) + def make_request(uri, req = nil) + if req.nil? + Net::HTTP.get_response(uri) + else + Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(req) + end end end - end - def make_res_request(request, action) - uri = URI("#{base_url}/res.php?key=#{@api_key}&action=#{action}&json=1") - req = Net::HTTP::Post.new(uri) - req.content_type = 'application/json' - req.body = request.to_json + def make_res_request(request, action) + uri = URI("#{base_url}/res.php?key=#{@api_key}&action=#{action}&json=1") + req = Net::HTTP::Post.new(uri) + req.content_type = 'application/json' + req.body = request.to_json - response = make_request(uri, req) + response = make_request(uri, req) - case response - when Net::HTTPSuccess - return JSON.parse(response.body) - else - raise NetworkException, "Network Error: #{response.code.to_i}" + case response + when Net::HTTPSuccess + return JSON.parse(response.body) + else + raise Api2Captcha::NetworkException, "Network Error: #{response.code.to_i}" + end end end end