deathbycaptcha 4.1.5 → 5.0.0

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.
@@ -0,0 +1,21 @@
1
+ RSpec.configure do |config|
2
+ # Run specs in random order to surface order dependencies. If you find an
3
+ # order dependency and want to debug it, you can fix the order by providing
4
+ # the seed, which is printed after each run.
5
+ # --seed 1234
6
+ config.order = "random"
7
+
8
+ # Print tests results with color.
9
+ config.color = true
10
+
11
+ # Only accept the new syntax of "expect" instead of "should".
12
+ config.expect_with :rspec do |c|
13
+ c.syntax = :expect
14
+ end
15
+ end
16
+
17
+ require 'yaml'
18
+ require 'deathbycaptcha'
19
+
20
+
21
+ CREDENTIALS = YAML.load_file('spec/credentials.yml')
metadata CHANGED
@@ -1,91 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deathbycaptcha
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.5
5
- prerelease:
4
+ version: 5.0.0
6
5
  platform: ruby
7
6
  authors:
8
- - Rafael Barbolo Lopes, Rafael Ivan Garcia
7
+ - Débora Setton Fernandes, Rafael Barbolo, Rafael Ivan Garcia
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-03-06 00:00:00.000000000 Z
11
+ date: 2015-01-26 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: rest-client
14
+ name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: 1.6.1
22
- type: :runtime
19
+ version: '1.7'
20
+ type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: 1.6.1
26
+ version: '1.7'
30
27
  - !ruby/object:Gem::Dependency
31
- name: json
28
+ name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - "~>"
36
32
  - !ruby/object:Gem::Version
37
- version: 1.4.6
38
- type: :runtime
33
+ version: '10.0'
34
+ type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - "~>"
44
39
  - !ruby/object:Gem::Version
45
- version: 1.4.6
46
- description: Ruby API for DeathByCaptcha (Captcha Solver as a Service)
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ description: DeathByCaptcha allows you to solve captchas with manual labor
47
56
  email:
48
- - tech@infosimples.com.br
57
+ - team@infosimples.com.br
49
58
  executables: []
50
59
  extensions: []
51
60
  extra_rdoc_files: []
52
61
  files:
53
- - .gitignore
62
+ - ".gitignore"
63
+ - CHANGELOG.md
54
64
  - Gemfile
55
- - MIT-LICENSE
65
+ - LICENSE.txt
56
66
  - README.md
57
67
  - Rakefile
68
+ - captchas/1.png
58
69
  - deathbycaptcha.gemspec
59
70
  - lib/deathbycaptcha.rb
60
71
  - lib/deathbycaptcha/client.rb
61
- - lib/deathbycaptcha/config.rb
62
- - lib/deathbycaptcha/errors.rb
63
- - lib/deathbycaptcha/http_client.rb
64
- - lib/deathbycaptcha/socket_client.rb
72
+ - lib/deathbycaptcha/client/http.rb
73
+ - lib/deathbycaptcha/client/socket.rb
74
+ - lib/deathbycaptcha/exceptions.rb
75
+ - lib/deathbycaptcha/models.rb
76
+ - lib/deathbycaptcha/models/captcha.rb
77
+ - lib/deathbycaptcha/models/server_status.rb
78
+ - lib/deathbycaptcha/models/user.rb
79
+ - lib/deathbycaptcha/patches.rb
65
80
  - lib/deathbycaptcha/version.rb
66
- homepage: http://www.infosimples.com.br/en/open-source/captcha-solving-api/
67
- licenses: []
81
+ - spec/credentials.yml.example
82
+ - spec/lib/client_spec.rb
83
+ - spec/spec_helper.rb
84
+ homepage: https://github.com/infosimples/deathbycaptcha
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
68
88
  post_install_message:
69
89
  rdoc_options: []
70
90
  require_paths:
71
91
  - lib
72
92
  required_ruby_version: !ruby/object:Gem::Requirement
73
- none: false
74
93
  requirements:
75
- - - ! '>='
94
+ - - ">="
76
95
  - !ruby/object:Gem::Version
77
96
  version: '0'
78
97
  required_rubygems_version: !ruby/object:Gem::Requirement
79
- none: false
80
98
  requirements:
81
- - - ! '>='
99
+ - - ">="
82
100
  - !ruby/object:Gem::Version
83
101
  version: '0'
84
102
  requirements: []
85
- rubyforge_project: deathbycaptcha
86
- rubygems_version: 1.8.25
103
+ rubyforge_project:
104
+ rubygems_version: 2.4.3
87
105
  signing_key:
88
- specification_version: 3
106
+ specification_version: 4
89
107
  summary: Ruby API for DeathByCaptcha (Captcha Solver as a Service)
90
- test_files: []
91
- has_rdoc:
108
+ test_files:
109
+ - spec/credentials.yml.example
110
+ - spec/lib/client_spec.rb
111
+ - spec/spec_helper.rb
@@ -1,42 +0,0 @@
1
- module DeathByCaptcha
2
-
3
- #
4
- # Config class
5
- # based on http://mjijackson.com/2010/02/flexible-ruby-config-objects
6
- #
7
- class Config
8
-
9
- def initialize(data = {})
10
- @data = {}
11
- update!(data)
12
- end
13
-
14
- def update!(data)
15
- data.each do |key, value|
16
- self[key] = value
17
- end
18
- end
19
-
20
- def [](key)
21
- @data[key.to_sym]
22
- end
23
-
24
- def []=(key, value)
25
- if value.class == Hash
26
- @data[key.to_sym] = Config.new(value)
27
- else
28
- @data[key.to_sym] = value
29
- end
30
- end
31
-
32
- def method_missing(sym, *args)
33
- if sym.to_s =~ /(.+)=$/
34
- self[$1] = args.first
35
- else
36
- self[sym]
37
- end
38
- end
39
-
40
- end
41
-
42
- end
@@ -1,69 +0,0 @@
1
- module DeathByCaptcha
2
-
3
- module Errors
4
-
5
- #
6
- # Custom Error class for rescuing from DeathByCaptcha API errors.
7
- #
8
- class Error < StandardError
9
-
10
- def initialize(message)
11
- super("#{message} (DEATHBYCAPTCHA API ERROR)")
12
- end
13
-
14
- end
15
-
16
- #
17
- # Raised when a method tries to access a not implemented method.
18
- #
19
- class NotImplemented < Error
20
- def initialize
21
- super('The requested functionality was not implemented')
22
- end
23
- end
24
-
25
- #
26
- # Raised when a HTTP call fails.
27
- #
28
- class CallError < Error
29
- def initialize
30
- super('HTTP call failed')
31
- end
32
- end
33
-
34
- #
35
- # Raised when the user is not allowed to access the API.
36
- #
37
- class AccessDenied < Error
38
- def initialize
39
- super('Access denied, please check your credentials and/or balance')
40
- end
41
- end
42
-
43
- #
44
- # Raised when the captcha file could not be loaded or is empty.
45
- #
46
- class CaptchaEmpty < Error
47
- def initialize
48
- super('CAPTCHA image is empty or could not be loaded')
49
- end
50
- end
51
-
52
- #
53
- # Raised when the size of the captcha file is too big.
54
- #
55
- class CaptchaOverflow < Error
56
- def initialize
57
- super('CAPTCHA image is too big')
58
- end
59
- end
60
-
61
- class ServiceOverload < Error
62
- def initialize
63
- super('CAPTCHA was rejected due to service overload, try again later')
64
- end
65
- end
66
-
67
- end
68
-
69
- end
@@ -1,98 +0,0 @@
1
- require 'rest_client'
2
- require 'json'
3
- require 'digest/md5'
4
-
5
- module DeathByCaptcha
6
-
7
- #
8
- # DeathByCaptcha HTTP API client
9
- #
10
- class HTTPClient < DeathByCaptcha::Client
11
-
12
- def get_user
13
- call('user', userpwd)
14
- end
15
-
16
- def get_captcha(cid)
17
- call("captcha/#{cid}")
18
- end
19
-
20
- def report(cid)
21
- call("captcha/#{cid}/report", userpwd)['is_correct']
22
- end
23
-
24
- def remove(cid)
25
- not call("captcha/#{cid}/remove", userpwd)['captcha']
26
- end
27
-
28
- #
29
- # Protected methods.
30
- #
31
- protected
32
-
33
- def upload(captcha, options = {})
34
- options = {
35
- :is_case_sensitive => false,
36
- :is_raw_content => false
37
- }.merge(options)
38
-
39
- data = userpwd
40
- data[:swid] = config.software_vendor_id
41
- data[:is_case_sensitive] = (options[:is_case_sensitive] ? 1 : 0)
42
- data[:captchafile] = load_file(captcha, options[:is_raw_content], options[:headers])
43
- response = call('captcha', data)
44
-
45
- return response if response['captcha']
46
- end
47
-
48
- #
49
- # Private methods.
50
- #
51
- private
52
-
53
- def call(cmd, payload = {}, headers = {})
54
-
55
- headers = {} unless headers.is_a?(Hash)
56
- headers['Accept'] = config.http_response_type if headers['Accept'].nil?
57
- headers['User-Agent'] = config.api_version if headers['User-Agent'].nil?
58
-
59
- log('SEND', "#{cmd} #{payload}")
60
-
61
- begin
62
- url = "#{config.http_base_url}/#{cmd}"
63
-
64
- if payload.empty?
65
- response = RestClient.get(url, headers)
66
- else
67
- response = RestClient.post(url, payload, headers)
68
- end
69
-
70
- log('RECV', "#{response.size} #{response}")
71
-
72
- return JSON.load(response)
73
-
74
- rescue RestClient::Unauthorized => exc
75
- raise DeathByCaptcha::Errors::AccessDenied
76
-
77
- rescue RestClient::RequestFailed => exc
78
- raise DeathByCaptcha::Errors::CallError
79
-
80
- rescue RestClient::ServiceUnavailable => exc
81
- raise DeathByCaptcha::Errors::ServiceOverload
82
-
83
- else
84
- raise DeathByCaptcha::Errors::CallError
85
-
86
- end
87
-
88
- return {}
89
-
90
- end
91
-
92
- end
93
-
94
- def self.http_client(username, password, extra = {})
95
- DeathByCaptcha::HTTPClient.new(username, password, extra)
96
- end
97
-
98
- end
@@ -1,224 +0,0 @@
1
- require 'json'
2
- require 'digest/md5'
3
- require 'thread'
4
- require 'socket'
5
- require 'base64'
6
-
7
- module DeathByCaptcha
8
-
9
- #
10
- # DeathByCaptcha Socket API client
11
- #
12
- class SocketClient < DeathByCaptcha::Client
13
-
14
- def initialize(username, password, extra = {})
15
- @mutex = Mutex.new
16
- @socket = nil
17
-
18
- super(username, password, extra)
19
- end
20
-
21
- def get_user
22
- call('user')
23
- end
24
-
25
- def get_captcha(cid)
26
- call('captcha', { :captcha => cid })
27
- end
28
-
29
- def report(cid)
30
- call('report', { :captcha => cid })[:is_correct]
31
- end
32
-
33
- #
34
- # Protected methods.
35
- #
36
- protected
37
-
38
- def upload(captcha, is_case_sensitive = false, is_raw_content = false)
39
- data = {
40
- :captcha => Base64.encode64(load_file(captcha, is_raw_content).read),
41
- :is_case_sensitive => (is_case_sensitive ? 1 : 0)
42
- }
43
-
44
- call('upload', data)
45
- end
46
-
47
- #
48
- # Private methods.
49
- #
50
- private
51
-
52
- def connect
53
- unless @socket
54
- log('CONN')
55
- begin
56
- random_port = config.socket_ports[rand(config.socket_ports.size)]
57
- # Creates a new Socket.
58
- addr = Socket.pack_sockaddr_in(random_port, config.socket_host)
59
-
60
- @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
61
- @socket.connect_nonblock(addr)
62
- rescue Errno::EINPROGRESS
63
- log("INPROG", 'Waiting...')
64
- rescue Exception => e
65
- close # Closes the socket.
66
- log('CONN', 'Could not connect.')
67
- log('CONN', e.backtrace.join("\n"))
68
- raise e
69
- end
70
- end
71
- @socket
72
- end
73
-
74
- def close
75
- if @socket
76
- log('CLOSE')
77
- begin
78
- @socket.close
79
- rescue Exception => e
80
- log('CLOSE', 'Could not close socket.')
81
- log('CLOSE', e.backtrace.join("\n"))
82
- ensure
83
- @socket = nil
84
- end
85
- end
86
- end
87
-
88
- def send(sock, buf)
89
- # buf += '\n'
90
- fds = [sock]
91
- deadline = Time.now.to_f + 3 * config.polls_interval
92
- while deadline > Time.now.to_f and not buf.empty? do
93
- _, wr, ex = IO.select([], fds, fds, config.polls_interval)
94
-
95
- if ex and ex.any?
96
- raise IOError.new('send(): select() excepted')
97
- elsif wr
98
- while buf and not buf.empty? do
99
- begin
100
- sent = wr.first.send(buf, 0)
101
- buf = buf[sent, buf.size - sent]
102
- rescue Exception => e
103
- if [35, 36].include? e.errno
104
- break
105
- else
106
- raise e
107
- end
108
- end
109
- end
110
- end
111
- end
112
-
113
- unless buf.empty?
114
- raise IOError.new('send() timed out')
115
- else
116
- return self
117
- end
118
- end
119
-
120
- def recv(sock)
121
- fds = [sock]
122
- buf = ''
123
-
124
- deadline = Time.now.to_f() + 3 * config.polls_interval
125
- while deadline > Time.now.to_f do
126
- rd, _, ex = IO.select(fds, [], fds, config.polls_interval)
127
- if ex and ex.any?
128
- raise IOError.new('send(): select() excepted')
129
- elsif rd
130
- while true do
131
- begin
132
- s = rd.first.recv_nonblock(256)
133
- rescue Errno::EAGAIN
134
- break
135
- rescue Exception => e
136
- if [35, 36].include? e.errno
137
- break
138
- else
139
- raise e
140
- end
141
- else
142
- if not s
143
- raise IOError.new('recv(): connection lost')
144
- else
145
- buf += s
146
- end
147
- end
148
- end
149
- break if buf.size > 0
150
- end
151
- end
152
-
153
- return buf[0, buf.size] if buf.size > 0
154
- raise IOError.new('recv() timed out')
155
- end
156
-
157
- def call(cmd, data = {})
158
- data = {} if data.nil?
159
- data.merge!({ :cmd => cmd, :version => config.api_version })
160
-
161
- request = data.to_json
162
- log('SEND', request.to_s)
163
-
164
- response = nil
165
-
166
- (0...1).each do
167
- if not @socket and cmd != 'login'
168
- call('login', userpwd)
169
- end
170
-
171
- # Locks other threads.
172
- # If another thread has already acquired the lock, this thread will be locked.
173
- @mutex.lock
174
-
175
- begin
176
- sock = connect
177
- send(sock, request)
178
-
179
- response = recv(sock)
180
- rescue Exception => e
181
- log('SEND', e.message)
182
- log('SEND', e.backtrace.join("\n"))
183
- close
184
- else
185
- # If no exception raised.
186
- break
187
- ensure
188
- @mutex.unlock
189
- end
190
-
191
- end
192
-
193
- if response.nil?
194
- msg = 'Connection timed out during API request'
195
- log('SEND', msg)
196
-
197
- raise Exception.new(msg)
198
- end
199
-
200
- log('RECV', response.to_s)
201
-
202
- begin
203
- response = JSON.load(response)
204
- rescue Exception => e
205
- raise Exception.new('Invalid API response')
206
- end
207
-
208
- if 0x00 < response['status'] and 0x10 > response['status']
209
- raise DeathByCaptcha::Errors::AccessDenied
210
- elsif 0xff == response['status']
211
- raise Exception.new('API server error occured')
212
- else
213
- return response
214
- end
215
-
216
- end
217
-
218
- end
219
-
220
- def self.socket_client(username, password, extra = {})
221
- DeathByCaptcha::SocketClient.new(username, password, extra)
222
- end
223
-
224
- end