deathbycaptcha 4.0.2 → 4.1.1
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.
- data/README.md +5 -0
- data/deathbycaptcha.gemspec +1 -1
- data/lib/deathbycaptcha/client.rb +24 -31
- data/lib/deathbycaptcha/error.rb +14 -7
- data/lib/deathbycaptcha/http_client.rb +14 -9
- data/lib/deathbycaptcha/socket_client.rb +15 -24
- data/lib/deathbycaptcha/version.rb +2 -2
- metadata +8 -10
data/README.md
CHANGED
|
@@ -12,6 +12,11 @@ Thread-safety note
|
|
|
12
12
|
|
|
13
13
|
The API is thread-safe, which means it is perfectly fine to share a client instance between multiple threads.
|
|
14
14
|
|
|
15
|
+
Latest version
|
|
16
|
+
--------------
|
|
17
|
+
|
|
18
|
+
The latest version of this API is 4.1.1.
|
|
19
|
+
|
|
15
20
|
Installation
|
|
16
21
|
------------
|
|
17
22
|
|
data/deathbycaptcha.gemspec
CHANGED
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
|
11
11
|
s.platform = Gem::Platform::RUBY
|
|
12
12
|
s.authors = ["Rafael Barbolo Lopes, Rafael Ivan Garcia"]
|
|
13
13
|
s.email = ["tech@infosimples.com.br"]
|
|
14
|
-
s.homepage = "http://
|
|
14
|
+
s.homepage = "http://www.infosimples.com.br/en/open-source/captcha-solving-api/"
|
|
15
15
|
s.summary = %q{Ruby API for DeathByCaptcha (Captcha Solver as a Service)}
|
|
16
16
|
s.description = %q{Ruby API for DeathByCaptcha (Captcha Solver as a Service)}
|
|
17
17
|
|
|
@@ -11,7 +11,7 @@ module DeathByCaptcha
|
|
|
11
11
|
|
|
12
12
|
attr_accessor :config
|
|
13
13
|
|
|
14
|
-
def initialize(username, password, extra={})
|
|
14
|
+
def initialize(username, password, extra = {})
|
|
15
15
|
data = {
|
|
16
16
|
:is_verbose => false, # If true, prints messages during execution
|
|
17
17
|
:logger_output => STDOUT, # Logger output path or IO instance
|
|
@@ -23,11 +23,9 @@ module DeathByCaptcha
|
|
|
23
23
|
:http_base_url => 'http://api.deathbycaptcha.com/api', # Base HTTP API url
|
|
24
24
|
:http_response_type => 'application/json', # Preferred HTTP API server's response content type, do not change
|
|
25
25
|
:socket_host => 'api.deathbycaptcha.com', # Socket API server's host
|
|
26
|
-
:socket_port => (8123..8130).map {|p| p}, # Socket API server's ports range
|
|
26
|
+
:socket_port => (8123..8130).map { |p| p }, # Socket API server's ports range
|
|
27
27
|
:username => username, # DeathByCaptcha username
|
|
28
|
-
:
|
|
29
|
-
:password => '', # DeathByCaptcha user's password not encrypted
|
|
30
|
-
:password_is_hashed => true # True, if the password is hashed
|
|
28
|
+
:password => password, # DeathByCaptcha user's password not encrypted
|
|
31
29
|
}.merge(extra)
|
|
32
30
|
|
|
33
31
|
@config = DeathByCaptcha::Config.new(data) # Config instance
|
|
@@ -70,13 +68,6 @@ module DeathByCaptcha
|
|
|
70
68
|
raise DeathByCaptcha::Errors::NotImplemented
|
|
71
69
|
end
|
|
72
70
|
|
|
73
|
-
#
|
|
74
|
-
# Remove an unsolved CAPTCHA
|
|
75
|
-
#
|
|
76
|
-
def remove(cid)
|
|
77
|
-
raise DeathByCaptcha::Errors::NotImplemented
|
|
78
|
-
end
|
|
79
|
-
|
|
80
71
|
#
|
|
81
72
|
# Upload a CAPTCHA
|
|
82
73
|
#
|
|
@@ -84,7 +75,7 @@ module DeathByCaptcha
|
|
|
84
75
|
# whether the CAPTCHA is case-sensitive or not. Returns CAPTCHA details
|
|
85
76
|
# on success.
|
|
86
77
|
#
|
|
87
|
-
def upload(captcha, options={})
|
|
78
|
+
def upload(captcha, options = {})
|
|
88
79
|
raise DeathByCaptcha::Errors::NotImplemented
|
|
89
80
|
end
|
|
90
81
|
|
|
@@ -97,27 +88,30 @@ module DeathByCaptcha
|
|
|
97
88
|
# timeout (in seconds). Removes unsolved CAPTCHAs. Returns CAPTCHA
|
|
98
89
|
# details if (correctly) solved.
|
|
99
90
|
#
|
|
100
|
-
def decode(captcha, options={})
|
|
101
|
-
options = {
|
|
91
|
+
def decode(captcha, options = {})
|
|
92
|
+
options = {
|
|
93
|
+
:timeout => config.default_timeout,
|
|
94
|
+
:is_case_sensitive => false,
|
|
95
|
+
:is_raw_content => false
|
|
96
|
+
}.merge(options)
|
|
97
|
+
|
|
102
98
|
deadline = Time.now + options[:timeout]
|
|
103
99
|
c = upload(captcha, options)
|
|
104
100
|
if c
|
|
101
|
+
|
|
105
102
|
while deadline > Time.now and (c['text'].nil? or c['text'].empty?)
|
|
106
103
|
sleep(config.polls_interval)
|
|
107
104
|
c = get_captcha(c['captcha'])
|
|
108
105
|
end
|
|
109
106
|
|
|
110
107
|
if c['text']
|
|
111
|
-
if c['is_correct']
|
|
112
|
-
return c
|
|
113
|
-
end
|
|
108
|
+
return c if c['is_correct']
|
|
114
109
|
else
|
|
115
110
|
remove(c['captcha'])
|
|
116
111
|
end
|
|
117
112
|
|
|
118
113
|
end
|
|
119
114
|
|
|
120
|
-
|
|
121
115
|
end
|
|
122
116
|
|
|
123
117
|
#
|
|
@@ -129,11 +123,7 @@ module DeathByCaptcha
|
|
|
129
123
|
# Return a hash with the user's credentials
|
|
130
124
|
#
|
|
131
125
|
def userpwd
|
|
132
|
-
|
|
133
|
-
return {:username => config.username, :password => config.password_hashed, :is_hashed => '1'}
|
|
134
|
-
else
|
|
135
|
-
return {:username => config.username, :password => config.password}
|
|
136
|
-
end
|
|
126
|
+
{ :username => config.username, :password => config.password }
|
|
137
127
|
end
|
|
138
128
|
|
|
139
129
|
#
|
|
@@ -144,7 +134,7 @@ module DeathByCaptcha
|
|
|
144
134
|
#
|
|
145
135
|
# Log a command and a message
|
|
146
136
|
#
|
|
147
|
-
def log(cmd, msg='')
|
|
137
|
+
def log(cmd, msg = '')
|
|
148
138
|
if @config.is_verbose
|
|
149
139
|
@logger.info "#{cmd}: #{msg}"
|
|
150
140
|
end
|
|
@@ -159,22 +149,24 @@ module DeathByCaptcha
|
|
|
159
149
|
# => a url if it's a String and starts with 'http://'
|
|
160
150
|
# => a filesystem path otherwise
|
|
161
151
|
#
|
|
162
|
-
def load_file(captcha, is_raw_content=false)
|
|
152
|
+
def load_file(captcha, is_raw_content = false)
|
|
153
|
+
|
|
163
154
|
file = nil
|
|
155
|
+
|
|
164
156
|
if is_raw_content
|
|
165
157
|
# Create a temporary file, write the raw content and return it
|
|
166
158
|
tmp_file_path = File.join(Dir.tmpdir, "captcha_#{Time.now.to_i}_#{rand}")
|
|
167
|
-
File.open(tmp_file_path, 'wb') {|f| f.write captcha}
|
|
159
|
+
File.open(tmp_file_path, 'wb') { |f| f.write captcha }
|
|
168
160
|
file = File.open(tmp_file_path, 'r')
|
|
169
161
|
|
|
170
162
|
elsif captcha.kind_of? File
|
|
171
163
|
# simply return the file
|
|
172
164
|
file = captcha
|
|
173
165
|
|
|
174
|
-
elsif captcha.kind_of? String and captcha
|
|
166
|
+
elsif captcha.kind_of? String and captcha.match(/^https?:\/\//i)
|
|
175
167
|
# Create a temporary file, download the file, write it to tempfile and return it
|
|
176
168
|
tmp_file_path = File.join(Dir.tmpdir, "captcha_#{Time.now.to_i}_#{rand}")
|
|
177
|
-
File.open(tmp_file_path, '
|
|
169
|
+
File.open(tmp_file_path, 'wb') { |f| f.write RestClient.get(captcha) }
|
|
178
170
|
file = File.open(tmp_file_path, 'r')
|
|
179
171
|
|
|
180
172
|
else
|
|
@@ -189,9 +181,10 @@ module DeathByCaptcha
|
|
|
189
181
|
raise DeathByCaptcha::Errors::CaptchaOverflow
|
|
190
182
|
end
|
|
191
183
|
|
|
192
|
-
|
|
184
|
+
file
|
|
185
|
+
|
|
193
186
|
end
|
|
194
187
|
|
|
195
188
|
end
|
|
196
189
|
|
|
197
|
-
end
|
|
190
|
+
end
|
data/lib/deathbycaptcha/error.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
module DeathByCaptcha
|
|
2
|
+
|
|
2
3
|
module Errors
|
|
3
4
|
|
|
4
|
-
|
|
5
5
|
#
|
|
6
6
|
# Custom Error class for rescuing from DeathByCaptcha API errors
|
|
7
7
|
#
|
|
8
8
|
class Error < StandardError
|
|
9
9
|
|
|
10
10
|
def initialize(message)
|
|
11
|
-
super
|
|
11
|
+
super("#{message} (DEATHBYCAPTCHA API ERROR)")
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
end
|
|
@@ -18,7 +18,7 @@ module DeathByCaptcha
|
|
|
18
18
|
#
|
|
19
19
|
class NotImplemented < Error
|
|
20
20
|
def initialize
|
|
21
|
-
super
|
|
21
|
+
super('The requested functionality was not implemented')
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -27,7 +27,7 @@ module DeathByCaptcha
|
|
|
27
27
|
#
|
|
28
28
|
class CallError < Error
|
|
29
29
|
def initialize
|
|
30
|
-
super
|
|
30
|
+
super('HTTP call failed')
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -36,7 +36,7 @@ module DeathByCaptcha
|
|
|
36
36
|
#
|
|
37
37
|
class AccessDenied < Error
|
|
38
38
|
def initialize
|
|
39
|
-
super
|
|
39
|
+
super('Access denied, please check your credentials and/or balance')
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -45,7 +45,7 @@ module DeathByCaptcha
|
|
|
45
45
|
#
|
|
46
46
|
class CaptchaEmpty
|
|
47
47
|
def initialize
|
|
48
|
-
super
|
|
48
|
+
super('CAPTCHA image is empty or could not be loaded')
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -54,9 +54,16 @@ module DeathByCaptcha
|
|
|
54
54
|
#
|
|
55
55
|
class CaptchaOverflow
|
|
56
56
|
def initialize
|
|
57
|
-
super
|
|
57
|
+
super('CAPTCHA image is too big')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class ServiceOverload
|
|
62
|
+
def initialize
|
|
63
|
+
super('CAPTCHA was rejected due to service overload, try again later')
|
|
58
64
|
end
|
|
59
65
|
end
|
|
60
66
|
|
|
61
67
|
end
|
|
68
|
+
|
|
62
69
|
end
|
|
@@ -25,22 +25,23 @@ module DeathByCaptcha
|
|
|
25
25
|
not call("captcha/#{cid}/remove", userpwd)['captcha']
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def a(nome, test=false, valor="verdade")
|
|
29
|
-
puts nome,test,valor
|
|
30
|
-
end
|
|
31
|
-
|
|
32
28
|
#
|
|
33
29
|
# Protected methods.
|
|
34
30
|
#
|
|
35
31
|
protected
|
|
36
32
|
|
|
37
|
-
def upload(captcha, options={})
|
|
38
|
-
options = {
|
|
33
|
+
def upload(captcha, options = {})
|
|
34
|
+
options = {
|
|
35
|
+
:is_case_sensitive => false,
|
|
36
|
+
:is_raw_content => false
|
|
37
|
+
}.merge(options)
|
|
38
|
+
|
|
39
39
|
data = userpwd
|
|
40
40
|
data[:swid] = config.software_vendor_id
|
|
41
41
|
data[:is_case_sensitive] = options[:is_case_sensitive] ? 1 : 0
|
|
42
42
|
data[:captchafile] = load_file(captcha, options[:is_raw_content])
|
|
43
43
|
response = call('captcha', data)
|
|
44
|
+
|
|
44
45
|
return response if response['captcha']
|
|
45
46
|
end
|
|
46
47
|
|
|
@@ -49,7 +50,9 @@ module DeathByCaptcha
|
|
|
49
50
|
#
|
|
50
51
|
private
|
|
51
52
|
|
|
52
|
-
def call(cmd, payload={}, headers={})
|
|
53
|
+
def call(cmd, payload = {}, headers = {})
|
|
54
|
+
|
|
55
|
+
headers = {} unless headers.is_a?(Hash)
|
|
53
56
|
headers['Accept'] = config.http_response_type if headers['Accept'].nil?
|
|
54
57
|
headers['User-Agent'] = config.api_version if headers['User-Agent'].nil?
|
|
55
58
|
|
|
@@ -74,6 +77,9 @@ module DeathByCaptcha
|
|
|
74
77
|
rescue RestClient::RequestFailed => exc
|
|
75
78
|
raise DeathByCaptcha::Errors::AccessDenied
|
|
76
79
|
|
|
80
|
+
rescue RestClient::ServiceUnavailable => exc
|
|
81
|
+
raise DeathByCaptcha::Errors::ServiceOverload
|
|
82
|
+
|
|
77
83
|
else
|
|
78
84
|
raise DeathByCaptcha::Errors::CallError
|
|
79
85
|
|
|
@@ -85,8 +91,7 @@ module DeathByCaptcha
|
|
|
85
91
|
|
|
86
92
|
end
|
|
87
93
|
|
|
88
|
-
|
|
89
|
-
def self.http_client(username, password, extra={})
|
|
94
|
+
def self.http_client(username, password, extra = {})
|
|
90
95
|
DeathByCaptcha::HTTPClient.new(username, password, extra)
|
|
91
96
|
end
|
|
92
97
|
|
|
@@ -25,7 +25,7 @@ module DeathByCaptcha
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def get_user
|
|
28
|
-
call('user'
|
|
28
|
+
call('user')
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def get_captcha(cid)
|
|
@@ -33,19 +33,8 @@ module DeathByCaptcha
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def report(cid)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
data = userpwd
|
|
39
|
-
data['captcha'] = cid
|
|
40
|
-
not call('report', data)[:is_correct]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def remove(cid)
|
|
44
|
-
not call("captcha/#{cid}/remove", userpwd)[:captcha]
|
|
45
|
-
|
|
46
|
-
data = userpwd
|
|
47
|
-
data['captcha'] = cid
|
|
48
|
-
not call('remove', data)[:captcha]
|
|
36
|
+
data = { 'captcha' => cid }
|
|
37
|
+
call('report', data)[:is_correct]
|
|
49
38
|
end
|
|
50
39
|
|
|
51
40
|
#
|
|
@@ -53,14 +42,13 @@ module DeathByCaptcha
|
|
|
53
42
|
#
|
|
54
43
|
protected
|
|
55
44
|
|
|
56
|
-
def upload(captcha, is_case_sensitive=false, is_raw_content=false)
|
|
57
|
-
data =
|
|
58
|
-
|
|
45
|
+
def upload(captcha, is_case_sensitive = false, is_raw_content = false)
|
|
46
|
+
data = {
|
|
47
|
+
:captcha => Base64.encode64(load_file(captcha, is_raw_content).read),
|
|
48
|
+
:is_case_sensitive => (is_case_sensitive ? 1 : 0)
|
|
49
|
+
}
|
|
59
50
|
|
|
60
|
-
data
|
|
61
|
-
response = call('upload', data)
|
|
62
|
-
|
|
63
|
-
response
|
|
51
|
+
call('upload', data)
|
|
64
52
|
end
|
|
65
53
|
|
|
66
54
|
#
|
|
@@ -178,13 +166,13 @@ module DeathByCaptcha
|
|
|
178
166
|
end
|
|
179
167
|
end
|
|
180
168
|
|
|
181
|
-
return buf[0, buf.size
|
|
169
|
+
return buf[0, buf.size] if buf.size > 0
|
|
182
170
|
raise IOError.new('recv() timed out')
|
|
183
171
|
end
|
|
184
172
|
|
|
185
173
|
def call(cmd, data = {})
|
|
186
174
|
data = {} if data.nil?
|
|
187
|
-
data.merge!({:cmd => cmd, :version => config.api_version})
|
|
175
|
+
data.merge!({ :cmd => cmd, :version => config.api_version })
|
|
188
176
|
|
|
189
177
|
request = data.to_json
|
|
190
178
|
log('SEND', request.to_s)
|
|
@@ -192,6 +180,10 @@ module DeathByCaptcha
|
|
|
192
180
|
response = nil
|
|
193
181
|
|
|
194
182
|
(0...1).each do
|
|
183
|
+
if not @socket and cmd != 'login'
|
|
184
|
+
call('login', userpwd)
|
|
185
|
+
end
|
|
186
|
+
|
|
195
187
|
# Locks other threads.
|
|
196
188
|
# If another thread has already acquired the lock, this thread will be locked.
|
|
197
189
|
@mutex.lock
|
|
@@ -241,7 +233,6 @@ module DeathByCaptcha
|
|
|
241
233
|
|
|
242
234
|
end
|
|
243
235
|
|
|
244
|
-
|
|
245
236
|
def self.socket_client(username, password, extra={})
|
|
246
237
|
DeathByCaptcha::SocketClient.new(username, password, extra)
|
|
247
238
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: deathbycaptcha
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.1.1
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,12 +9,11 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2011-
|
|
13
|
-
default_executable:
|
|
12
|
+
date: 2011-10-11 00:00:00.000000000Z
|
|
14
13
|
dependencies:
|
|
15
14
|
- !ruby/object:Gem::Dependency
|
|
16
15
|
name: rest-client
|
|
17
|
-
requirement: &
|
|
16
|
+
requirement: &2160211420 !ruby/object:Gem::Requirement
|
|
18
17
|
none: false
|
|
19
18
|
requirements:
|
|
20
19
|
- - ~>
|
|
@@ -22,10 +21,10 @@ dependencies:
|
|
|
22
21
|
version: 1.6.1
|
|
23
22
|
type: :runtime
|
|
24
23
|
prerelease: false
|
|
25
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *2160211420
|
|
26
25
|
- !ruby/object:Gem::Dependency
|
|
27
26
|
name: json
|
|
28
|
-
requirement: &
|
|
27
|
+
requirement: &2160210400 !ruby/object:Gem::Requirement
|
|
29
28
|
none: false
|
|
30
29
|
requirements:
|
|
31
30
|
- - ~>
|
|
@@ -33,7 +32,7 @@ dependencies:
|
|
|
33
32
|
version: 1.4.6
|
|
34
33
|
type: :runtime
|
|
35
34
|
prerelease: false
|
|
36
|
-
version_requirements: *
|
|
35
|
+
version_requirements: *2160210400
|
|
37
36
|
description: Ruby API for DeathByCaptcha (Captcha Solver as a Service)
|
|
38
37
|
email:
|
|
39
38
|
- tech@infosimples.com.br
|
|
@@ -54,8 +53,7 @@ files:
|
|
|
54
53
|
- lib/deathbycaptcha/http_client.rb
|
|
55
54
|
- lib/deathbycaptcha/socket_client.rb
|
|
56
55
|
- lib/deathbycaptcha/version.rb
|
|
57
|
-
|
|
58
|
-
homepage: http://github.com/infosimples/deathbycaptcha
|
|
56
|
+
homepage: http://www.infosimples.com.br/en/open-source/captcha-solving-api/
|
|
59
57
|
licenses: []
|
|
60
58
|
post_install_message:
|
|
61
59
|
rdoc_options: []
|
|
@@ -75,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
73
|
version: '0'
|
|
76
74
|
requirements: []
|
|
77
75
|
rubyforge_project: deathbycaptcha
|
|
78
|
-
rubygems_version: 1.
|
|
76
|
+
rubygems_version: 1.8.10
|
|
79
77
|
signing_key:
|
|
80
78
|
specification_version: 3
|
|
81
79
|
summary: Ruby API for DeathByCaptcha (Captcha Solver as a Service)
|