private_captcha 0.0.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0681aaa5e68aae1562fbe7fc20c19dc098305f752673f574e50c6c0094fcb95
4
- data.tar.gz: 12c1a1692cd0b3d3be4d775fcebf95389a5fc8d40cfa0fec39783e6f8bc4f06d
3
+ metadata.gz: 70c3d94ed5a3020b3f71df8c50558c2e1a09c5e245374844209d2734bc4d51a8
4
+ data.tar.gz: 6a3a267b62466abc243069c922af1440f77d6feaeac24c96238d1841aa9d4437
5
5
  SHA512:
6
- metadata.gz: fc9382338a62a9cb3d5f49ead2b670bcf3967eae7051308c20cbe436fdbf3046f1177df9a1e2b723ea1e8560b5343e04bfd8288b9da1bc20e9c5bf5b38973a5e
7
- data.tar.gz: 33e903e9de21aba85e9cb34a12015555ed30fc415118c2923495f75f0373bf64d91b9cda145b9936447a8f17c46d979c150fc01c16f9fd2685cc0d8e4661feed
6
+ metadata.gz: 88e3e2caef19070d9aba49ceaf62b9a9eb0fb0c80bf56e6c9c90a86d513137248c9a8435b5fb751604c1bd1814f5d771ba9bf021a3278aa6ac964f13e0021716
7
+ data.tar.gz: 7bb7b45b726e696b3ea57d38c2ad6fbb1a9a7e37ac2ece858f50e43741512c39469f68732894ac0696b70c98c977459f18ebecb2cba12c4e8f294b628d76f4de
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # private-captcha-ruby
2
2
 
3
- ![CI](https://github.com/PrivateCaptcha/private-captcha-ruby/actions/workflows/ci.yaml/badge.svg)
3
+ [![Gem Version](https://badge.fury.io/rb/private_captcha.svg)](https://badge.fury.io/rb/private_captcha)
4
+ ![CI](https://github.com/PrivateCaptcha/private-captcha-ruby/actions/workflows/ci.yaml/badge.svg)
4
5
 
5
6
  Ruby client for server-side verification of Private Captcha solutions.
6
7
 
@@ -37,7 +38,7 @@ end
37
38
  # Verify a captcha solution
38
39
  begin
39
40
  result = client.verify('user-solution-from-frontend')
40
- if result.success
41
+ if result.ok?
41
42
  puts 'Captcha verified successfully!'
42
43
  else
43
44
  puts "Verification failed: #{result.error_message}"
@@ -25,7 +25,7 @@ module PrivateCaptcha
25
25
  end
26
26
 
27
27
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
28
- def verify(solution, max_backoff_seconds: nil, attempts: nil)
28
+ def verify(solution, max_backoff_seconds: nil, attempts: nil, sitekey: nil)
29
29
  raise EmptySolutionError if solution.nil? || solution.empty?
30
30
 
31
31
  max_backoff = max_backoff_seconds || @config.max_backoff_seconds
@@ -38,6 +38,7 @@ module PrivateCaptcha
38
38
  response = nil
39
39
  error = nil
40
40
  attempt = 0
41
+ trace_id = nil
41
42
 
42
43
  max_attempts.times do |i|
43
44
  attempt = i + 1
@@ -51,11 +52,12 @@ module PrivateCaptcha
51
52
  end
52
53
 
53
54
  begin
54
- response = do_verify(solution)
55
+ response = do_verify(solution, sitekey: sitekey)
55
56
  error = nil
56
57
  break
57
58
  rescue RetriableError => e
58
59
  error = e.original_error
60
+ trace_id = e.trace_id if e.trace_id
59
61
  end
60
62
  end
61
63
 
@@ -65,7 +67,8 @@ module PrivateCaptcha
65
67
 
66
68
  if error
67
69
  @logger.error("Failed to verify solution after #{attempt} attempts")
68
- raise VerificationFailedError.new("Failed to verify solution after #{attempt} attempts", attempt)
70
+ raise VerificationFailedError.new("Failed to verify solution after #{attempt} attempts", attempt,
71
+ trace_id: trace_id)
69
72
  end
70
73
 
71
74
  response
@@ -78,7 +81,10 @@ module PrivateCaptcha
78
81
 
79
82
  output = verify(solution)
80
83
 
81
- raise Error, "captcha verification failed: #{output.error_message}" unless output.success
84
+ unless output.ok?
85
+ raise Error.new("captcha verification failed: #{output.error_message}",
86
+ trace_id: output.trace_id)
87
+ end
82
88
 
83
89
  output
84
90
  end
@@ -94,20 +100,23 @@ module PrivateCaptcha
94
100
  end
95
101
 
96
102
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
97
- def do_verify(solution)
103
+ def do_verify(solution, sitekey: nil)
98
104
  request = Net::HTTP::Post.new(@endpoint)
99
105
  request['X-Api-Key'] = @config.api_key
100
106
  request['User-Agent'] = "private-captcha-ruby/#{VERSION}"
101
107
  request['Content-Type'] = 'text/plain'
108
+ request['X-PC-Sitekey'] = sitekey if sitekey
102
109
  request.body = solution
103
110
 
104
111
  @logger.debug('Sending HTTP request') { "path=#{@endpoint.path} method=POST" }
105
112
 
106
113
  response = nil
114
+ trace_id = nil
107
115
  begin
108
116
  response = Net::HTTP.start(@endpoint.hostname, @endpoint.port, use_ssl: true) do |http|
109
117
  http.request(request)
110
118
  end
119
+ trace_id = response['X-Trace-ID']
111
120
  rescue SocketError, IOError, Timeout::Error, SystemCallError => e
112
121
  @logger.debug('Failed to send HTTP request') { "error=#{e.message}" }
113
122
  raise RetriableError, e
@@ -118,7 +127,6 @@ module PrivateCaptcha
118
127
  end
119
128
 
120
129
  status_code = response.code.to_i
121
- request_id = response['X-Trace-ID']
122
130
 
123
131
  case status_code
124
132
  when 429
@@ -126,16 +134,16 @@ module PrivateCaptcha
126
134
  @logger.debug('Rate limited') do
127
135
  "retryAfter=#{retry_after} rateLimit=#{response['X-RateLimit-Limit']}"
128
136
  end
129
- raise RetriableError, HTTPError.new(status_code, retry_after)
137
+ raise RetriableError, HTTPError.new(status_code, retry_after, trace_id: trace_id)
130
138
  when 500, 502, 503, 504, 408, 425
131
- raise RetriableError, HTTPError.new(status_code)
139
+ raise RetriableError, HTTPError.new(status_code, trace_id: trace_id)
132
140
  when 300..599
133
- raise HTTPError, status_code
141
+ raise HTTPError.new(status_code, trace_id: trace_id)
134
142
  end
135
143
 
136
144
  begin
137
145
  json_data = JSON.parse(response.body)
138
- VerifyOutput.from_json(json_data, request_id: request_id)
146
+ VerifyOutput.from_json(json_data, trace_id: trace_id)
139
147
  rescue JSON::ParserError => e
140
148
  @logger.debug('Failed to parse response') { "error=#{e.message}" }
141
149
  raise RetriableError, e
@@ -1,18 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrivateCaptcha
4
- class Error < StandardError; end
4
+ # Error is the base class for Private Captcha errors
5
+ class Error < StandardError
6
+ attr_reader :trace_id
7
+
8
+ def initialize(msg = nil, trace_id: nil)
9
+ @trace_id = trace_id
10
+ super(msg)
11
+ end
12
+ end
5
13
 
6
14
  # EmptyAPIKeyError is raised when the API key is not provided or is empty
7
15
  class EmptyAPIKeyError < Error
8
- def initialize(msg = 'API key is empty')
16
+ def initialize(msg = 'API key is empty', trace_id: nil)
9
17
  super
10
18
  end
11
19
  end
12
20
 
13
21
  # EmptySolutionError is raised when the solution is not provided or is empty
14
22
  class EmptySolutionError < Error
15
- def initialize(msg = 'solution is empty')
23
+ def initialize(msg = 'solution is empty', trace_id: nil)
16
24
  super
17
25
  end
18
26
  end
@@ -21,10 +29,10 @@ module PrivateCaptcha
21
29
  class HTTPError < Error
22
30
  attr_reader :status_code, :seconds
23
31
 
24
- def initialize(status_code, seconds = nil)
32
+ def initialize(status_code, seconds = nil, trace_id: nil)
25
33
  @status_code = status_code
26
34
  @seconds = seconds
27
- super("HTTP error #{status_code}")
35
+ super("HTTP error #{status_code}", trace_id: trace_id)
28
36
  end
29
37
  end
30
38
 
@@ -36,15 +44,19 @@ module PrivateCaptcha
36
44
  @original_error = error
37
45
  super(error.message)
38
46
  end
47
+
48
+ def trace_id
49
+ @original_error.respond_to?(:trace_id) ? @original_error.trace_id : nil
50
+ end
39
51
  end
40
52
 
41
53
  # VerificationFailedError is raised when verification fails after all retry attempts
42
54
  class VerificationFailedError < Error
43
55
  attr_reader :attempts
44
56
 
45
- def initialize(message, attempts)
57
+ def initialize(message, attempts, trace_id: nil)
46
58
  @attempts = attempts
47
- super(message)
59
+ super(message, trace_id: trace_id)
48
60
  end
49
61
  end
50
62
  end
@@ -33,28 +33,32 @@ module PrivateCaptcha
33
33
  }.freeze
34
34
 
35
35
  attr_accessor :success, :code, :origin, :timestamp
36
- attr_reader :request_id, :attempt
36
+ attr_reader :trace_id, :attempt
37
37
 
38
- def initialize(success: false, code: VERIFY_NO_ERROR, origin: nil, timestamp: nil, request_id: nil, attempt: 0)
38
+ def initialize(success: false, code: VERIFY_NO_ERROR, origin: nil, timestamp: nil, trace_id: nil, attempt: 0)
39
39
  @success = success
40
40
  @code = code
41
41
  @origin = origin
42
42
  @timestamp = timestamp
43
- @request_id = request_id
43
+ @trace_id = trace_id
44
44
  @attempt = attempt
45
45
  end
46
46
 
47
+ def ok?
48
+ @success == true && @code == VERIFY_NO_ERROR
49
+ end
50
+
47
51
  def error_message
48
52
  ERROR_MESSAGES.fetch(@code, 'error')
49
53
  end
50
54
 
51
- def self.from_json(json_data, request_id: nil, attempt: 0)
55
+ def self.from_json(json_data, trace_id: nil, attempt: 0)
52
56
  new(
53
57
  success: json_data['success'],
54
58
  code: json_data['code'] || VERIFY_NO_ERROR,
55
59
  origin: json_data['origin'],
56
60
  timestamp: json_data['timestamp'],
57
- request_id: request_id,
61
+ trace_id: trace_id,
58
62
  attempt: attempt
59
63
  )
60
64
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrivateCaptcha
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: private_captcha
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taras Kushnir
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-26 00:00:00.000000000 Z
11
+ date: 2025-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -46,6 +46,7 @@ licenses:
46
46
  metadata:
47
47
  homepage_uri: https://privatecaptcha.com
48
48
  source_code_uri: https://github.com/PrivateCaptcha/private-captcha-ruby
49
+ rubygems_mfa_required: 'true'
49
50
  post_install_message:
50
51
  rdoc_options: []
51
52
  require_paths:
@@ -61,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
62
  - !ruby/object:Gem::Version
62
63
  version: '0'
63
64
  requirements: []
64
- rubygems_version: 3.4.20
65
+ rubygems_version: 3.4.19
65
66
  signing_key:
66
67
  specification_version: 4
67
68
  summary: Ruby client for server-side Private Captcha API