deathbycaptcha 4.1.5 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 85231874d833eab0bd84ac29b980d0c00bcb308d
4
+ data.tar.gz: 91e8dec5412a607e32032e6f8b209a969d288ca7
5
+ SHA512:
6
+ metadata.gz: 2d397c364a4b95457252ddd07bdf9a52e8bc0d298932a591515812074310cb98492278cf8679e49e4c1c5b2b33b2eed9faeaff009347699856130724899f7829
7
+ data.tar.gz: 9f0e76625b2565b2340866068dc10b9290259d2346ec91f8505ea70234e8ca2df09ec0b41a0d573d064b6c74a8128ebbad8dca21fdc61de91c6a102a4264ba0c
data/.gitignore CHANGED
@@ -1,4 +1,17 @@
1
- *.gem
2
- .bundle
3
- Gemfile.lock
4
- pkg/*
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /spec/credentials.yml
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
16
+ .ruby-version
17
+ .ruby-gemset
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ ### Comming next
2
+
3
+ * Review Socket client, check if it's worth keeping a pool of open connections
4
+ * Add more tests
5
+
6
+ ### 5.0.0 - 2015-01-26
7
+
8
+ * The code has been completely refactored/rewritten
9
+ * Bugs reported until 2014-12-31 fixed
10
+ * Suggestions from community have been taken in consideration
11
+ * We no longer follow the versioninig system of the official clients of
12
+ DeathByCaptcha. We have bumped to version 5.0.0 and will use Semantic Versioning
13
+ from now on.
14
+
15
+ ### 4.x
16
+
17
+ The code was a port from the official DeathByCaptcha python client.
18
+
19
+ It didn't have many Ruby conventions, so we decided to abandon it and write a
20
+ new version 5.0.0.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in deathbycaptcha.gemspec
4
4
  gemspec
@@ -1,4 +1,6 @@
1
- Copyright (C) 2011 by Infosimples. http://www.infosimples.com.br/
1
+ Copyright (c) 2015 Rafael Barbolo
2
+
3
+ MIT License
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
@@ -17,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,94 +1,175 @@
1
- DeathByCaptcha
2
- ==============
1
+ Developed by [Infosimples](https://infosimples.com), a brazilian company that
2
+ offers [data extraction solutions](https://infosimples.com/en/data-engineering)
3
+ and [Ruby on Rails development](https://infosimples.com/en/software-development).
3
4
 
4
- DeathByCaptcha is a Ruby API for acessing the http://www.deathbycaptcha.com services.
5
5
 
6
- It supports HTTP and socket-based connections, with the latter being recommended for having faster responses and overall better performance.
6
+ # DeathByCaptcha
7
7
 
8
- When using socket connections, make sure that outgoing TCP traffic to <b>api.deathbycaptcha.com</b> to the ports range in <b>8123-8130</b> is not blocked by your firewall.
8
+ DeathByCaptcha is a Ruby API for DeathByCaptcha - http://www.deathbycaptcha.com.
9
9
 
10
- Thread-safety note
11
- ------------------
12
10
 
13
- The API is thread-safe, which means it is perfectly fine to share a client instance between multiple threads.
11
+ ## Installation
14
12
 
15
- Installation
16
- ------------
13
+ Add this line to your application's Gemfile:
17
14
 
18
- You can install the latest DeathByCaptcha gem with:
15
+ ```ruby
16
+ gem 'deathbycaptcha', '~> 5.0.0'
17
+ ```
19
18
 
20
- gem install deathbycaptcha
19
+ And then execute:
21
20
 
22
- You can add it to your Gemfile:
21
+ $ bundle
23
22
 
24
- gem 'deathbycaptcha'
23
+ Or install it yourself as:
25
24
 
26
- Examples
27
- --------
25
+ $ gem install deathbycaptcha
28
26
 
29
- ### Create a client
30
27
 
31
- #### HTTP client
28
+ ## Usage
32
29
 
33
- require 'deathbycaptcha'
30
+ 1. **Create a client**
34
31
 
35
- client = DeathByCaptcha.http_client('myusername', 'mypassword')
32
+ ```ruby
33
+ # Create a client (:socket and :http clients are available)
34
+ #
35
+ client = DeathByCaptcha.new('myusername', 'mypassword', :socket)
36
+ ```
36
37
 
37
- #### Socket client
38
+ 2. **Solve a captcha**
38
39
 
39
- require 'deathbycaptcha'
40
+ There are two methods available: **decode** and **decode!**
41
+ * **decode** doesn't raise exceptions.
42
+ * **decode!** may raise a *DeathByCaptcha::Error* if something goes wrong.
40
43
 
41
- client = DeathByCaptcha.socket_client('myusername', 'mypassword')
44
+ If the solution is not available, an empty captcha object will be returned.
42
45
 
43
- #### Verbose mode (for debugging purposes)
46
+ ```ruby
47
+ captcha = client.decode(url: 'http://bit.ly/1xXZcKo')
48
+ captcha.text # Solution of the captcha
49
+ captcha.id # Numeric ID of the captcha solved by DeathByCaptcha
50
+ captcha.is_correct # true if the solution is correct
51
+ ```
44
52
 
45
- client.config.is_verbose = true
53
+ You can also specify *path*, *file*, *raw* and *raw64* when decoding an image.
46
54
 
47
- #### Decoding captcha
55
+ ```ruby
56
+ client.decode(path: 'path/to/my/captcha/file')
48
57
 
49
- ##### From URL
58
+ client.decode(file: File.open('path/to/my/captcha/file', 'rb'))
50
59
 
51
- response = client.decode 'http://www.phpcaptcha.org/securimage/securimage_show.php'
60
+ client.decode(raw: File.open('path/to/my/captcha/file', 'rb').read)
52
61
 
53
- puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
62
+ client.decode(raw64: Base64.encode64(File.open('path/to/my/captcha/file', 'rb').read))
63
+ ```
54
64
 
55
- ##### From file path
65
+ > Internally, the gem will always convert the image to raw64 (binary base64 encoded).
56
66
 
57
- response = client.decode 'path/to/my/captcha/file'
67
+ 3. **Retrieve a previously solved captcha**
58
68
 
59
- puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
69
+ ```ruby
70
+ captcha = client.captcha('130920620') # with 130920620 as the captcha id
71
+ ```
60
72
 
61
- ##### From a file
73
+ 4. **Report incorrectly solved captcha for refund**
62
74
 
63
- file = File.open('path/to/my/captcha/file', 'r')
75
+ ```ruby
76
+ captcha = client.report!('130920620') # with 130920620 as the captcha id
77
+ ```
64
78
 
65
- response = client.decode file
79
+ > ***Warning:*** *do not abuse on this method, otherwise you may get banned*
66
80
 
67
- puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
81
+ 5. **Retrieve your user information (which has the current credit balance)**
68
82
 
69
- ##### From raw content
83
+ ```ruby
84
+ user = client.user()
85
+ user.is_banned # true if the user is banned
86
+ user.balance # Credit balance in US cents
87
+ user.rate # Captcha rate, i.e. charges for one solved captcha in US cents
88
+ user.id # Numeric ID of your account
89
+ ```
70
90
 
71
- raw_content = File.open('path/to/my/captcha/file', 'r').read
91
+ 6. **Retrieve DeathByCaptcha server status**
72
92
 
73
- response = client.decode(raw_content, :is_raw_content => true)
93
+ ```ruby
94
+ status = client.status()
95
+ status.todays_accuracy # Current accuracy of DeathByCaptcha
96
+ status.solved_in # Estimated seconds to solve a captcha right now
97
+ status.is_service_overloaded # true if DeathByCaptcha is overloaded/unresponsive
98
+ ```
74
99
 
75
- puts "captcha id: #{response['captcha']}, solution: #{response['text']}, is_correct: #{response['is_correct']}}"
100
+ ## Notes
76
101
 
77
- #### Get the solution of a captcha
102
+ #### Thread-safety
78
103
 
79
- puts client.get_captcha('130920620')['text'] # where 130920620 is the captcha id
104
+ The API is thread-safe, which means it is perfectly fine to share a client
105
+ instance between multiple threads.
80
106
 
81
- #### Get user account information
107
+ #### HTTP and Socket clients
82
108
 
83
- puts client.get_user
109
+ The API supports HTTP and socket-based connections, with the latter being
110
+ recommended for having faster responses and overall better performance. The two
111
+ clients have the same methods/interface.
84
112
 
85
- Maintainers
86
- -----------
113
+ When using the socket client, make sure that outgoing TCP traffic to
114
+ **api.dbcapi.me** to the ports in range **8123-8130** is not blocked by your
115
+ firewall.
87
116
 
88
- * Rafael Barbolo Lopes (http://github.com/barbolo)
89
- * Rafael Ivan Garcia (http://github.com/rafaelivan)
117
+ #### Ruby dependencies
90
118
 
91
- License
92
- -------
119
+ DeathByCaptcha >= 5.0.0 don't require specific dependencies. That saves you
120
+ memmory and avoid conflicts with other gems.
93
121
 
94
- MIT License. Copyright (C) 2011 by Infosimples. http://www.infosimples.com.br
122
+ #### Input image format
123
+
124
+ Any format you use in the decode method (url, file, path, raw, raw64) will
125
+ always be converted to a raw64, which is a binary base64 encoded string. So, if
126
+ you already have this format available on your side, there's no need to do
127
+ convertions before calling the API.
128
+
129
+ > Our recomendation is to never convert your image format, unless needed. Let
130
+ > the gem convert internally. It may save you resources (CPU, memmory and IO).
131
+
132
+ #### Versioning
133
+
134
+ We no longer follow the versioninig system of the official clients of
135
+ DeathByCaptcha. We have bumped to version 5.0.0 and will use
136
+ [Semantic Versioning](http://semver.org/) from now on.
137
+
138
+ #### Upgrade from 4.x to 5.x
139
+
140
+ Any 5.x version is incompatible with any 4.x version. Please, review your code
141
+ before upgrading.
142
+
143
+ #### Ruby versions
144
+
145
+ This gem has been tested on the following versions of Ruby:
146
+
147
+ * MRI 2.2.0
148
+ * MRI 2.1.5
149
+ * MRI 2.0.0
150
+ * MRI 1.9.3
151
+
152
+ # Maintainers
153
+
154
+ * [Débora Setton Fernandes](http://github.com/deborasetton)
155
+ * [Rafael Barbolo](http://github.com/barbolo)
156
+ * [Rafael Ivan Garcia](http://github.com/rafaelivan)
157
+
158
+
159
+ ## Contributing
160
+
161
+ 1. Fork it ( https://github.com/infosimples/deathbycaptcha/fork )
162
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
163
+ 3. **Run/add tests (RSpec)**
164
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
165
+ 5. Push to the branch (`git push origin my-new-feature`)
166
+ 6. Create a new Pull Request
167
+ 7. Yay. Thanks for contributing :)
168
+
169
+ All contributors:
170
+ https://github.com/infosimples/deathbycaptcha/graphs/contributors
171
+
172
+
173
+ # License
174
+
175
+ MIT License. Copyright (C) 2011-2015 Infosimples. https://infosimples.com/
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
- require 'bundler'
2
- Bundler::GemHelper.install_tasks
1
+ require "bundler/gem_tasks"
2
+
data/captchas/1.png ADDED
Binary file
@@ -1,24 +1,24 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "deathbycaptcha/version"
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'deathbycaptcha/version'
4
5
 
5
- Gem::Specification.new do |s|
6
- s.add_dependency 'rest-client', '~> 1.6.1'
7
- s.add_dependency 'json', '>= 1.4.6'
8
-
9
- s.name = "deathbycaptcha"
10
- s.version = DeathByCaptcha::VERSION
11
- s.platform = Gem::Platform::RUBY
12
- s.authors = ["Rafael Barbolo Lopes, Rafael Ivan Garcia"]
13
- s.email = ["tech@infosimples.com.br"]
14
- s.homepage = "http://www.infosimples.com.br/en/open-source/captcha-solving-api/"
15
- s.summary = %q{Ruby API for DeathByCaptcha (Captcha Solver as a Service)}
16
- s.description = %q{Ruby API for DeathByCaptcha (Captcha Solver as a Service)}
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "deathbycaptcha"
8
+ spec.version = DeathByCaptcha::VERSION
9
+ spec.authors = ["Débora Setton Fernandes, Rafael Barbolo, Rafael Ivan Garcia"]
10
+ spec.email = ["team@infosimples.com.br"]
11
+ spec.summary = %q{Ruby API for DeathByCaptcha (Captcha Solver as a Service)}
12
+ spec.description = %q{DeathByCaptcha allows you to solve captchas with manual labor}
13
+ spec.homepage = "https://github.com/infosimples/deathbycaptcha"
14
+ spec.license = "MIT"
17
15
 
18
- s.rubyforge_project = "deathbycaptcha"
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
19
20
 
20
- s.files = `git ls-files`.split("\n")
21
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
- s.require_paths = ["lib"]
24
- end
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.1"
24
+ end
@@ -1,9 +1,14 @@
1
- require 'deathbycaptcha/version'
2
- require 'deathbycaptcha/config'
3
- require 'deathbycaptcha/errors'
4
- require 'deathbycaptcha/client'
5
- require 'deathbycaptcha/http_client'
6
- require 'deathbycaptcha/socket_client'
1
+ require 'base64'
2
+ require 'json'
3
+ require 'bigdecimal'
4
+ require 'socket'
5
+ require 'net/http'
7
6
 
8
7
  module DeathByCaptcha
9
8
  end
9
+
10
+ require 'deathbycaptcha/client'
11
+ require 'deathbycaptcha/exceptions'
12
+ require 'deathbycaptcha/models'
13
+ require 'deathbycaptcha/patches'
14
+ require 'deathbycaptcha/version'
@@ -1,188 +1,202 @@
1
- require 'logger'
2
- require 'digest/sha1'
3
- require 'rest_client'
4
-
5
1
  module DeathByCaptcha
6
2
 
3
+ # Create a DeathByCaptcha API client. This is a shortcut to
4
+ # DeathByCaptcha::Client.create.
7
5
  #
8
- # DeathByCaptcha API Client
6
+ def self.new(*args)
7
+ DeathByCaptcha::Client.create(*args)
8
+ end
9
+
10
+ # DeathByCaptcha::Client is a common interface inherited by DBC clients like
11
+ # DeathByCaptcha::Client::HTTP and DeathByCaptcha::Client::Socket.
9
12
  #
10
13
  class Client
11
14
 
12
- attr_accessor :config
13
-
14
- def initialize(username, password, extra = {})
15
- data = {
16
- :is_verbose => false, # If true, prints messages during execution.
17
- :logger_output => STDOUT, # Logger output path or IO instance.
18
- :api_version => API_VERSION, # API version (used as user-agent with http requests).
19
- :software_vendor_id => 0, # API unique software ID.
20
- :max_captcha_file_size => (64 * 1024), # Maximum CAPTCHA image filesize, currently 64K.
21
- :default_timeout => 60, # Default CAPTCHA timeout.
22
- :polls_interval => 5, # Default decode polling interval.
23
- :http_base_url => 'http://api.dbcapi.me/api', # Base HTTP API url.
24
- :http_response_type => 'application/json', # Preferred HTTP API server's response content type, do not change.
25
- :socket_host => 'api.dbcapi.me', # Socket API server's host.
26
- :socket_ports => (8123..8130).map { |p| p }, # Socket API server's ports range.
27
- :username => username, # DeathByCaptcha username.
28
- :password => password, # DeathByCaptcha user's password not encrypted.
29
- }.merge(extra)
30
-
31
- @config = DeathByCaptcha::Config.new(data) # Config instance.
32
- @logger = Logger.new(@config.logger_output) # Logger.
33
- end
34
-
35
- #
36
- # Fetch the user's details -- balance, rate and banned status.
37
- #
38
- def get_user
39
- raise DeathByCaptcha::Errors::NotImplemented
40
- end
15
+ attr_accessor :username, :password, :timeout, :polling
41
16
 
17
+ # Create a DeathByCaptcha API client
42
18
  #
43
- # Fetch the user's balance (in US cents).
44
- #
45
- def get_balance
46
- raise DeathByCaptcha::Errors::NotImplemented
47
- end
48
-
19
+ # @param [String] username Username of the DeathByCaptcha account.
20
+ # @param [String] password Password of the DeathByCaptcha account.
21
+ # @param [Symbol] connection Connection type (:socket, :http)
22
+ # @param [Hash] options Options hash.
23
+ # @option options [Integer] :timeout (60) Seconds before giving up.
24
+ # @option options [Integer] :polling (5) Seconds for polling the solution.
49
25
  #
50
- # Fetch a CAPTCHA details -- its numeric ID, text and correctness.
26
+ # @return [DeathByCaptcha::Client] A Socket or HTTP Client instance.
51
27
  #
52
- def get_captcha(cid)
53
- raise DeathByCaptcha::Errors::NotImplemented
28
+ def self.create(username, password, connection = :socket, options = {})
29
+ case connection
30
+ when :socket
31
+ DeathByCaptcha::Client::Socket.new(username, password, options)
32
+ when :http
33
+ DeathByCaptcha::Client::HTTP.new(username, password, options)
34
+ else
35
+ raise DeathByCaptcha::InvalidClientConnection
36
+ end
54
37
  end
55
38
 
39
+ # Create a DeathByCaptcha client.
56
40
  #
57
- # Fetch a CAPTCHA text.
58
- #
59
- def get_text(cid)
60
- raise DeathByCaptcha::Errors::NotImplemented
61
- end
62
-
41
+ # @param [String] username Username of the DeathByCaptcha account.
42
+ # @param [String] password Password of the DeathByCaptcha account.
43
+ # @param [Hash] options Options hash.
44
+ # @option options [Integer] :timeout (60) Seconds before giving up.
45
+ # @option options [Integer] :polling (5) Seconds for polling the solution.
63
46
  #
64
- # Report a CAPTCHA as incorrectly solved.
47
+ # @return [DeathByCaptcha::Client] A Client instance.
65
48
  #
66
- def report(cid)
67
- raise DeathByCaptcha::Errors::NotImplemented
49
+ def initialize(username, password, options = {})
50
+ self.username = username
51
+ self.password = password
52
+ self.timeout = options[:timeout] || 60
53
+ self.polling = options[:polling] || 5
68
54
  end
69
55
 
56
+ # Decode the text from an image (i.e. solve a captcha).
70
57
  #
71
- # Upload a CAPTCHA.
58
+ # @param [Hash] options Options hash.
59
+ # @option options [String] :url URL of the image to be decoded.
60
+ # @option options [String] :path File path of the image to be decoded.
61
+ # @option options [File] :file File instance with image to be decoded.
62
+ # @option options [String] :raw Binary content of the image to be decoded.
63
+ # @option options [String] :raw64 Binary content encoded in base64 of the image to be decoded.
72
64
  #
73
- # Accepts file names, file objects or urls, and an optional flag telling
74
- # whether the CAPTCHA is case-sensitive or not. Returns CAPTCHA details
75
- # on success.
65
+ # @return [DeathByCaptcha::Captcha] The captcha (with solution) or an empty hash if something goes wrong.
76
66
  #
77
- def upload(captcha, options = {})
78
- raise DeathByCaptcha::Errors::NotImplemented
67
+ def decode(options = {})
68
+ decode!(options)
69
+ rescue DeathByCaptcha::Error
70
+ DeathByCaptcha::Captcha.new
79
71
  end
80
72
 
73
+ # Decode the text from an image (i.e. solve a captcha).
81
74
  #
82
- # Try to solve a CAPTCHA.
83
- #
84
- # See Client.upload() for arguments details.
75
+ # @param [Hash] options Options hash.
76
+ # @option options [String] :url URL of the image to be decoded.
77
+ # @option options [String] :path File path of the image to be decoded.
78
+ # @option options [File] :file File instance with image to be decoded.
79
+ # @option options [String] :raw Binary content of the image to be decoded.
80
+ # @option options [String] :raw64 Binary content encoded in base64 of the image to be decoded.
85
81
  #
86
- # Uploads a CAPTCHA, polls for its status periodically with arbitrary
87
- # timeout (in seconds). Removes unsolved CAPTCHAs. Returns CAPTCHA
88
- # details if (correctly) solved.
82
+ # @return [DeathByCaptcha::Captcha] The captcha (with solution if an error is not raised).
89
83
  #
90
- def decode(captcha, options = {})
91
- options = {
92
- :timeout => config.default_timeout,
93
- :is_case_sensitive => false,
94
- :is_raw_content => false
95
- }.merge(options)
84
+ def decode!(options = {})
85
+ deadline = Time.now + self.timeout
96
86
 
97
- deadline = Time.now + options[:timeout]
98
- c = upload(captcha, options)
99
- if c
87
+ raw64 = load_captcha(options)
88
+ raise DeathByCaptcha::InvalidCaptcha if raw64.to_s.empty?
100
89
 
101
- while deadline > Time.now and (c['text'].nil? or c['text'].empty?)
102
- sleep(config.polls_interval)
103
- c = get_captcha(c['captcha'])
104
- end
90
+ _captcha = self.upload(raw64)
91
+ while _captcha.text.to_s.empty? && deadline > Time.now
92
+ sleep(self.polling)
93
+ _captcha = self.captcha(_captcha.id)
94
+ end
105
95
 
106
- if c['text']
107
- return c if c['is_correct']
108
- else
109
- remove(c['captcha'])
110
- end
96
+ raise DeathByCaptcha::IncorrectSolution if !_captcha.is_correct
97
+ raise DeathByCaptcha::Timeout if _captcha.text.to_s.empty?
111
98
 
112
- end
99
+ _captcha
113
100
  end
114
101
 
102
+ # Retrieve information from an uploaded captcha.
103
+ #
104
+ # @param [Integer] captcha_id Numeric ID of the captcha.
115
105
  #
116
- # Protected methods.
106
+ # @return [DeathByCaptcha::Captcha] The captcha object.
117
107
  #
118
- protected
108
+ def captcha(captcha_id)
109
+ raise NotImplementedError
110
+ end
119
111
 
112
+ # Report incorrectly solved captcha for refund.
113
+ #
114
+ # @param [Integer] captcha_id Numeric ID of the captcha.
120
115
  #
121
- # Return a hash with the user's credentials
116
+ # @return [DeathByCaptcha::Captcha] The captcha object.
122
117
  #
123
- def userpwd
124
- { :username => config.username, :password => config.password }
118
+ def report!(captcha_id)
119
+ raise NotImplementedError
125
120
  end
126
121
 
122
+ # Retrieve your user information (which has the current credit balance).
127
123
  #
128
- # Private methods.
124
+ # @return [DeathByCaptcha::User] The user object.
129
125
  #
130
- private
126
+ def user
127
+ raise NotImplementedError
128
+ end
131
129
 
130
+ # Retrieve DeathByCaptcha server status.
132
131
  #
133
- # Log a command and a message
132
+ # @return [DeathByCaptcha::ServerStatus] The server status object.
134
133
  #
135
- def log(cmd, msg = '')
136
- if @config.is_verbose
137
- @logger.info "#{cmd}: #{msg}"
138
- end
134
+ def status
135
+ raise NotImplementedError
139
136
  end
140
137
 
138
+ # Upload a captcha to DeathByCaptcha.
141
139
  #
142
- # Return the File instance that corresponds to the captcha
140
+ # This method will not return the solution. It's only useful if you want to
141
+ # implement your own "decode" function.
143
142
  #
144
- # captcha can be:
145
- # => a raw file content if is_raw_content is true
146
- # => a File if its kind of File
147
- # => a url if it's a String and starts with 'http://'
148
- # => a filesystem path otherwise
143
+ # @return [DeathByCaptcha::Captcha] The captcha object (not solved yet).
149
144
  #
150
- def load_file(captcha, is_raw_content = false, headers = {})
151
-
152
- file = nil
153
-
154
- if is_raw_content
155
- # Create a temporary file, write the raw content and return it
156
- tmp_file_path = File.join(Dir.tmpdir, "captcha_#{Time.now.to_i}_#{rand}")
157
- File.open(tmp_file_path, 'wb') { |f| f.write captcha }
158
- file = File.open(tmp_file_path, 'r')
159
-
160
- elsif captcha.kind_of? File
161
- # simply return the file
162
- file = captcha
145
+ def upload(raw64)
146
+ raise NotImplementedError
147
+ end
163
148
 
164
- elsif captcha.kind_of? String and captcha.match(/^https?:\/\//i)
165
- # Create a temporary file, download the file, write it to tempfile and return it
166
- tmp_file_path = File.join(Dir.tmpdir, "captcha_#{Time.now.to_i}_#{rand}")
167
- File.open(tmp_file_path, 'wb') { |f| f.write RestClient.get(captcha, headers) }
168
- file = File.open(tmp_file_path, 'r')
149
+ private
169
150
 
151
+ # Load a captcha raw content encoded in base64 from options.
152
+ #
153
+ # @param [Hash] options Options hash.
154
+ # @option options [String] :url URL of the image to be decoded.
155
+ # @option options [String] :path File path of the image to be decoded.
156
+ # @option options [File] :file File instance with image to be decoded.
157
+ # @option options [String] :raw Binary content of the image to be decoded.
158
+ # @option options [String] :raw64 Binary content encoded in base64 of the image to be decoded.
159
+ #
160
+ # @return [String] The binary image base64 encoded.
161
+ #
162
+ def load_captcha(options)
163
+ if options[:raw64]
164
+ options[:raw64]
165
+ elsif options[:raw]
166
+ Base64.encode64(options[:raw])
167
+ elsif options[:file]
168
+ Base64.encode64(options[:file].read())
169
+ elsif options[:path]
170
+ Base64.encode64(File.open(options[:path], 'rb').read)
171
+ elsif options[:url]
172
+ Base64.encode64(open_url(options[:url]))
170
173
  else
171
- # Return the File opened
172
- file = File.open(captcha, 'r')
173
-
174
+ ''
174
175
  end
176
+ rescue
177
+ ''
178
+ end
179
+
180
+ def open_url(url)
181
+ uri = URI(url)
175
182
 
176
- if file.nil?
177
- raise DeathByCaptcha::Errors::CaptchaEmpty
178
- elsif config.max_captcha_file_size <= File.size?(file).to_i
179
- raise DeathByCaptcha::Errors::CaptchaOverflow
183
+ http = Net::HTTP.new(uri.host, uri.port)
184
+
185
+ if uri.scheme == 'https'
186
+ http.use_ssl = true
187
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
180
188
  end
181
189
 
182
- file
190
+ res = http.get(uri.request_uri)
183
191
 
192
+ if (redirect = res.header['location'])
193
+ open_url(redirect)
194
+ else
195
+ res.body
196
+ end
184
197
  end
185
-
186
198
  end
187
-
188
199
  end
200
+
201
+ require 'deathbycaptcha/client/http'
202
+ require 'deathbycaptcha/client/socket'