anti_captcha 2.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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ea4b4a53f966ab69db9ea92a2575ee32afe55c88
4
+ data.tar.gz: 13674c466b8c24af5b4e1e995b56151fff45d1a4
5
+ SHA512:
6
+ metadata.gz: d6fa39db3ce217ed087511f235f0a6af458cdc78899f5b3e51c886064e64eb9ad7087a61c1e007b36d5600a13b7ece8b89738e1bf4591ffe2d7ca92d66d1899a
7
+ data.tar.gz: 7d3b40a511221ddd807698b06c55b13e98ec10b3dcf48284266444bb43aca3a38f1f39a9976db68295d14f3d53164936b3ff7985f10e743e4a55f907295db1a8
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .byebug_history
11
+ *.gem
12
+
13
+ # rspec failure tracking
14
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.15.3
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at rafael.ivan@infosimples.com.br. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in anti_captcha.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Rafael Ivan Garcia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,141 @@
1
+ Developed by [Infosimples](https://infosimples.com).
2
+
3
+ # AntiCaptcha
4
+
5
+ AntiCaptcha is a Ruby API for Anti-Captcha - [Anti-Captcha.com](http://getcaptchasolution.com/ipuz16klxh).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'anti_captcha'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install anti_captcha
22
+
23
+ ## Usage
24
+
25
+ 1. **Create a client**
26
+
27
+ ```ruby
28
+ # Create a client
29
+ client = AntiCaptcha.new('my_key')
30
+ ```
31
+
32
+ 2. **Solve Image CAPTCHA**
33
+
34
+ There are two methods available:
35
+
36
+ - `decode_image`: solves image CAPTCHAs. It doesn't raise exceptions.
37
+ - `decode_image!`: solves image CAPTCHAs. It may raise an `AntiCaptcha::Error` if something goes wrong.
38
+
39
+ If the solution is not available, an empty solution object will be returned.
40
+
41
+ ```ruby
42
+ solution = client.decode_image!(path: 'path/to/my/captcha/file')
43
+ solution.text # CAPTCHA solution.
44
+ solution.url # Image URL.
45
+ solution.task_result.task_id # The ID of the task.
46
+ ```
47
+
48
+ You can also specify *file*, *body* and *body64* when decoding an image.
49
+
50
+ ```ruby
51
+ client.decode_image!(file: File.open('path/to/my/captcha/file', 'rb'))
52
+
53
+ client.decode_image!(body: File.open('path/to/my/captcha/file', 'rb').read)
54
+
55
+ client.decode_image!(body64: Base64.encode64(File.open('path/to/my/captcha/file', 'rb').read))
56
+ ```
57
+
58
+ > Internally, the gem will always convert the image to body64 (binary base64 encoded).
59
+
60
+ 3. **Report incorrectly solved image CAPTCHA for refund**
61
+
62
+ It is only possible to report incorrectly solved image CAPTCHAs.
63
+
64
+ ```ruby
65
+ client.report_incorrect_image_catpcha!(task_id)
66
+ ```
67
+
68
+ 4. **Get your account balance**
69
+
70
+ ```ruby
71
+ client.get_balance!
72
+ ```
73
+
74
+ 5. **Get current stats of a queue.**
75
+
76
+ Queue IDs:
77
+ - `1` Standart ImageToText, English language.
78
+ - `2` Standart ImageToText, Russian language.
79
+ - `5` Recaptcha NoCaptcha tasks.
80
+ - `6` Recaptcha Proxyless task.
81
+
82
+ ```ruby
83
+ client.get_queue_stats!(queue_id)
84
+ ```
85
+
86
+ 6. **Clickable CAPTCHAs (e.g. "No CAPTCHA reCAPTCHA")**
87
+
88
+ This method allows you to solve CAPTCHAs similar to
89
+ [reCAPTCHA v2](https://support.google.com/recaptcha/?hl=en#6262736).
90
+
91
+ There are two methods available:
92
+
93
+ - `decode_nocaptcha`: solves NoCaptcha CAPTCHAs. It doesn't raise exceptions.
94
+ - `decode_nocaptcha!`: solves NoCaptcha CAPTCHAs. It may raise an `AntiCaptcha::Error` if something goes wrong.
95
+
96
+ **Send the `website_key` and `website_url` parameters**
97
+
98
+ This method requires no browser emulation. You can send two parameters that
99
+ identify the website in which the CAPTCHA is found.
100
+
101
+ ```ruby
102
+ options = {
103
+ website_key: 'xyz',
104
+ website_url: 'http://example.com/example=1'
105
+ }
106
+
107
+ solution = client.decode_nocaptcha!(options)
108
+ solution.g_recaptcha_response # Solution of the captcha
109
+ ```
110
+
111
+ The solution (`solution.g_recaptcha_response`) will be a code that validates
112
+ the form, like the following:
113
+
114
+ ```ruby
115
+ "1JJHJ_VuuHAqJKxcaasbTsqw-L1Sm4gD57PTeaEr9-MaETG1vfu2H5zlcwkjsRoZoHxx6V9yUDw8Ig-hYD8kakmSnnjNQd50w_Y_tI3aDLp-s_7ZmhH6pcaoWWsid5hdtMXyvrP9DscDuCLBf7etLle8caPWSaYCpAq9DOTtj5NpSg6-OeCJdGdkjsakFUMeGeqmje87wSajcjmdjl_w4XZBY2zy8fUH6XoAGZ6AeCTulIljBQDObQynKDd-rutPvKNxZasDk-LbhTfw508g1lu9io6jnvm3kbAdnkfZ0x0PkGiUMHU7hnuoW6bXo2Yn_Zt5tDWL7N7wFtY6B0k7cTy73f8er508zReOuoyz2NqL8smDCmcJu05ajkPGt20qzpURMwHaw"
116
+ ```
117
+
118
+ ## Notes
119
+
120
+ #### Ruby dependencies
121
+
122
+ AntiCaptcha doesn't require specific dependencies. That saves you memory and
123
+ avoid conflicts with other gems.
124
+
125
+ #### Input image format
126
+
127
+ Any format you use in the decode method (file, path, body, body64) will always
128
+ be converted to a body64, which is a binary base64 encoded string. So, if you
129
+ already have this format available on your side, there's no need to do
130
+ convertions before calling the API.
131
+
132
+ > Our recomendation is to never convert your image format, unless needed. Let
133
+ > the gem convert internally. It may save you resources (CPU, memory and IO).
134
+
135
+ #### Versioning
136
+
137
+ AntiCaptcha gem uses [Semantic Versioning](http://semver.org/).
138
+
139
+ # License
140
+
141
+ MIT License. Copyright (C) 2011-2015 Infosimples. https://infosimples.com/
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "anti_captcha/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "anti_captcha"
8
+ spec.version = AntiCaptcha::VERSION
9
+ spec.authors = ["Infosimples"]
10
+ spec.email = ["team@infosimples.com.br"]
11
+
12
+ spec.summary = %q{Ruby API for Anti Captcha (CAPTCHA Solver as a Service)}
13
+ spec.description = %q{TwoCaptcha is an automated CAPTCHA solving service}
14
+ spec.homepage = "https://github.com/infosimples/anti_captcha"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.15"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "anti_captcha"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,39 @@
1
+ require 'base64'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'openssl'
5
+
6
+ #
7
+ # The module AntiCaptcha contains all the code for the anti_captcha gem.
8
+ # It acts as a safely namespace that isolates logic of AntiCaptcha from any
9
+ # project that uses it.
10
+ #
11
+ module AntiCaptcha
12
+ #
13
+ # Creates an Anti Captcha API client. This is a shortcut to
14
+ # AntiCaptcha::Client.new.
15
+ #
16
+ def self.new(key, options = {})
17
+ AntiCaptcha::Client.new(key, options)
18
+ end
19
+
20
+ #
21
+ # Base class of a model object returned by AntiCaptcha API.
22
+ #
23
+ class Model
24
+ def initialize(values = {})
25
+ values.each do |key, value|
26
+ send("#{key}=", value) if respond_to?("#{key}=")
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require 'anti_captcha/http'
33
+ require 'anti_captcha/errors'
34
+ require 'anti_captcha/models/solution'
35
+ require 'anti_captcha/models/image_to_text_solution'
36
+ require 'anti_captcha/models/no_captcha_solution'
37
+ require 'anti_captcha/models/task_result'
38
+ require 'anti_captcha/client'
39
+ require 'anti_captcha/version'
@@ -0,0 +1,385 @@
1
+ module AntiCaptcha
2
+ # AntiCaptcha::Client is a client that communicates with the Anti Captcha API:
3
+ # https://anti-captcha.com.
4
+ #
5
+ class Client
6
+ BASE_URL = 'https://api.anti-captcha.com/:action'
7
+
8
+ attr_accessor :client_key, :timeout, :polling
9
+
10
+ #
11
+ # Creates a client for the Anti Captcha API.
12
+ #
13
+ # @param [String] client_key The key of the Anti Captcha account.
14
+ # @param [Hash] options Options hash.
15
+ # @option options [Integer] :timeout (60) Seconds before giving up of a
16
+ # captcha being solved.
17
+ # @option options [Integer] :polling (5) Seconds before checking answer
18
+ # again.
19
+ #
20
+ # @return [AntiCaptcha::Client] A Client instance.
21
+ #
22
+ def initialize(client_key, options = {})
23
+ self.client_key = client_key
24
+ self.timeout = options[:timeout] || 60
25
+ self.polling = options[:polling] || 5
26
+ end
27
+
28
+ #
29
+ # Decodes an image CAPTCHA.
30
+ #
31
+ # @see `AntiCaptcha::Client#decode_image!`
32
+ #
33
+ def decode_image(options)
34
+ decode_image!(options)
35
+ rescue
36
+ AntiCaptcha::ImageToTextSolution.new
37
+ end
38
+
39
+ #
40
+ # Decodes an image CAPTCHA.
41
+ #
42
+ # @param [Hash] options Options hash.
43
+ # @option options [String] :body64 File body encoded in base64. Make sure to
44
+ # send it without line breaks.
45
+ # @option options [String] :body Binary file body.
46
+ # @option options [String] :path File path of the image to be decoded.
47
+ # @option options [File] :file File instance with image to be
48
+ # decoded.
49
+ # @option options [Boolean] :phrase If the worker must enter an answer
50
+ # with at least one "space".
51
+ # @option options [Boolean] :case If the answer must be entered with case
52
+ # sensitivity.
53
+ # @option options [Integer] :numeric 0 - no requirements; 1 - only numbers
54
+ # are allowed; 2 - any letters are
55
+ # allowed except numbers.
56
+ # @option options [Boolean] :math If the answer must be calculated.
57
+ # @option options [Integer] :min_length Defines minimum length of the
58
+ # answer. 0 - no requirements.
59
+ # @option options [Integer] :max_length Defines maximum length of the
60
+ # answer. 0 - no requirements.
61
+ #
62
+ # @return [AntiCaptcha::ImageToTextSolution] The solution of the image
63
+ # CAPTCHA.
64
+ #
65
+ def decode_image!(options)
66
+ started_at = Time.now
67
+
68
+ options[:body64] = load_captcha(options)
69
+ task = create_task!('ImageToTextTask', options)
70
+
71
+ if task['taskId']
72
+ api_result = get_task_result!(task['taskId'])
73
+
74
+ while api_result['status'] != 'ready'
75
+ sleep(polling)
76
+ api_result = get_task_result!(task['taskId'])
77
+ raise AntiCaptcha::Timeout if (Time.now - started_at) > timeout
78
+ end
79
+
80
+ task_result = AntiCaptcha::TaskResult.new(
81
+ task_id: task['taskId'],
82
+ error_id: api_result['errorId'],
83
+ error_code: api_result['errorCode'],
84
+ error_description: api_result['errorDescription'],
85
+ status: api_result['status'],
86
+ cost: api_result['cost'],
87
+ ip: api_result['ip'],
88
+ create_time: api_result['createTime'],
89
+ end_time: api_result['endTime'],
90
+ solve_count: api_result['solveCount']
91
+ )
92
+
93
+ return AntiCaptcha::ImageToTextSolution.new(
94
+ api_response: api_result,
95
+ task_result: task_result,
96
+ url: api_result['solution']['url'],
97
+ text: api_result['solution']['text']
98
+ )
99
+
100
+ else
101
+ raise AntiCaptcha.raise_error('taskId not received from Anti Captcha.')
102
+ end
103
+ end
104
+
105
+ #
106
+ # Decodes a NoCaptcha CAPTCHA.
107
+ #
108
+ # @see `AntiCaptcha::Client#decode_nocaptcha!`
109
+ #
110
+ def decode_nocaptcha(options, proxy = nil)
111
+ decode_nocaptcha!(options, proxy)
112
+ rescue
113
+ AntiCaptcha::NoCaptchaSolution.new
114
+ end
115
+
116
+ #
117
+ # Decodes a NoCaptcha CAPTCHA.
118
+ #
119
+ # @param [Hash] options Options hash.
120
+ # @option options [String] :website_url
121
+ # @option options [String] :website_key
122
+ # @option options [String] :language_pool
123
+ #
124
+ # @param [Hash] proxy Not mandatory. A hash with configs of the proxy that
125
+ # has to be used. Defaults to `nil`.
126
+ # @option proxy [String] :proxy_type
127
+ # @option proxy [String] :proxy_address
128
+ # @option proxy [String] :proxy_port
129
+ # @option proxy [String] :proxy_login
130
+ # @option proxy [String] :proxy_login
131
+ # @option proxy [String] :proxy_password
132
+ # @option proxy [String] :user_agent
133
+ #
134
+ # @return [AntiCaptcha::NoCaptchaSolution] The solution of the NoCaptcha.
135
+ #
136
+ def decode_nocaptcha!(options, proxy = nil)
137
+ started_at = Time.now
138
+
139
+ if proxy.nil?
140
+ task_type = 'NoCaptchaTaskProxyless'
141
+ hsh = options
142
+ else
143
+ task_type = 'NoCaptchaTask'
144
+ hsh = options.merge(proxy)
145
+ end
146
+
147
+ task = create_task!(task_type, hsh)
148
+
149
+ if task['taskId']
150
+ api_result = get_task_result!(task['taskId'])
151
+
152
+ while api_result['status'] != 'ready'
153
+ sleep(polling)
154
+ api_result = get_task_result!(task['taskId'])
155
+ raise AntiCaptcha::Timeout if (Time.now - started_at) > timeout
156
+ end
157
+
158
+ task_result = AntiCaptcha::TaskResult.new(
159
+ task_id: task['taskId'],
160
+ error_id: api_result['errorId'],
161
+ error_code: api_result['errorCode'],
162
+ error_description: api_result['errorDescription'],
163
+ status: api_result['status'],
164
+ cost: api_result['cost'],
165
+ ip: api_result['ip'],
166
+ create_time: api_result['createTime'],
167
+ end_time: api_result['endTime'],
168
+ solve_count: api_result['solveCount']
169
+ )
170
+
171
+ return AntiCaptcha::NoCaptchaSolution.new(
172
+ api_response: api_result,
173
+ task_result: task_result,
174
+ g_recaptcha_response: api_result['solution']['gRecaptchaResponse'],
175
+ g_recaptcha_response_md5: api_result['solution']['gRecaptchaResponseMD5']
176
+ )
177
+
178
+ else
179
+ raise AntiCaptcha.raise_error('taskId not received from Anti Captcha.')
180
+ end
181
+ end
182
+
183
+ #
184
+ # Creates a task for solving the selected CAPTCHA type.
185
+ #
186
+ # @param [String] type The type of the CAPTCHA.
187
+ # @param [Hash] options Options hash.
188
+ # # Image to text CAPTCHA
189
+ # @option options [String] :body64 File body encoded in base64. Make sure to
190
+ # send it without line breaks.
191
+ # @option options [Boolean] :phrase If the worker must enter an answer
192
+ # with at least one "space".
193
+ # @option options [Boolean] :case If the answer must be entered with case
194
+ # sensitivity.
195
+ # @option options [Integer] :numeric 0 - no requirements; 1 - only numbers
196
+ # are allowed; 2 - any letters are
197
+ # allowed except numbers.
198
+ # @option options [Boolean] :math If the answer must be calculated.
199
+ # @option options [Integer] :min_length Defines minimum length of the
200
+ # answer. 0 - no requirements.
201
+ # @option options [Integer] :max_length Defines maximum length of the
202
+ # answer. 0 - no requirements.
203
+ # # NoCaptcha
204
+ # @option options [String] :website_url Address of target web page.
205
+ # @option options [String] :website_key Recaptcha website key.
206
+ # @option options [String] :language_pool
207
+ # @option options [String] :proxy_type
208
+ # @option options [String] :proxy_address
209
+ # @option options [String] :proxy_port
210
+ # @option options [String] :proxy_login
211
+ # @option options [String] :proxy_login
212
+ # @option options [String] :proxy_password
213
+ # @option options [String] :user_agent
214
+ #
215
+ # @return [Hash] Information about the task.
216
+ #
217
+ def create_task!(type, options)
218
+ args = {
219
+ languagePool: (options[:language_pool] || 'en'),
220
+ softId: '859'
221
+ }
222
+
223
+ case type
224
+ when 'ImageToTextTask'
225
+ args[:task] = {
226
+ type: 'ImageToTextTask',
227
+ body: options[:body64],
228
+ phrase: options[:phrase],
229
+ case: options[:case],
230
+ numeric: options[:numeric],
231
+ math: options[:math],
232
+ minLength: options[:min_length],
233
+ maxLength: options[:max_length]
234
+ }
235
+
236
+ when 'NoCaptchaTaskProxyless'
237
+ args[:task] = {
238
+ type: 'NoCaptchaTaskProxyless',
239
+ websiteURL: options[:website_url],
240
+ websiteKey: options[:website_key]
241
+ }
242
+
243
+ when 'NoCaptchaTask'
244
+ args[:task] = {
245
+ type: 'NoCaptchaTask',
246
+ websiteURL: options[:website_url],
247
+ websiteKey: options[:website_key],
248
+ proxyType: options[:proxy_type],
249
+ proxyAddress: options[:proxy_address],
250
+ proxyPort: options[:proxy_port],
251
+ proxyLogin: options[:proxy_login],
252
+ proxyPassword: options[:proxy_password],
253
+ userAgent: options[:user_agent]
254
+ }
255
+
256
+ else
257
+ message = "Invalid task type: '#{type}'. Allowed types: " +
258
+ "#{%w(ImageToTextTask NoCaptchaTaskProxyless NoCaptchaTask).join(', ')}"
259
+ raise AntiCaptcha.raise_error(message)
260
+ end
261
+
262
+ request('createTask', args)
263
+ end
264
+
265
+ #
266
+ # Creates a task for solving the selected CAPTCHA type.
267
+ #
268
+ # @param [String] task_id The ID of the CAPTCHA task.
269
+ #
270
+ # @return [Hash] Information about the task.
271
+ #
272
+ def get_task_result!(task_id)
273
+ args = { taskId: task_id }
274
+ request('getTaskResult', args)
275
+ end
276
+
277
+ #
278
+ # Retrieves account balance.
279
+ #
280
+ # @return [Hash] Information about the account balance.
281
+ #
282
+ def get_balance!
283
+ request('getBalance')
284
+ end
285
+
286
+ #
287
+ # This method allows you to define if it is a suitable time to upload new
288
+ # tasks.
289
+ #
290
+ # @param [String] queue_id The ID of the queue. Options:
291
+ # 1 - standart ImageToText, English language.
292
+ # 2 - standart ImageToText, Russian language.
293
+ # 5 - Recaptcha NoCaptcha tasks.
294
+ # 6 - Recaptcha Proxyless task.
295
+ #
296
+ # @return [Hash] Information about the queue.
297
+ #
298
+ def get_queue_stats!(queue_id)
299
+ args = { queueId: queue_id }
300
+ request('getQueueStats', args)
301
+ end
302
+
303
+ #
304
+ # Complaints are accepted only for image CAPTCHAs. A complaint is checked by
305
+ # 5 workers, 3 of them must confirm it.
306
+ #
307
+ # @param [String] task_id The ID of the CAPTCHA task.
308
+ #
309
+ # @return [Hash] Information about the complaint.
310
+ #
311
+ def report_incorrect_image_catpcha!(task_id)
312
+ args = { taskId: task_id }
313
+ request('getTaskResult', args)
314
+ end
315
+
316
+ #
317
+ # Private methods.
318
+ #
319
+ private
320
+
321
+ #
322
+ # Performs an HTTP request to the Anti Captcha API.
323
+ #
324
+ # @param [String] action API method name.
325
+ # @param [Hash] payload Data to be sent through the HTTP request.
326
+ #
327
+ # @return [String] Response from the Anti Captcha API.
328
+ #
329
+ def request(action, payload = nil)
330
+ payload ||= {}
331
+
332
+ response = AntiCaptcha::HTTP.post_request(
333
+ url: BASE_URL.gsub(':action', action),
334
+ timeout: self.timeout,
335
+ json_payload: payload.merge(clientKey: self.client_key).to_json
336
+ )
337
+
338
+ response = JSON.load(response)
339
+ validate_response(response)
340
+
341
+ response
342
+ end
343
+
344
+ #
345
+ # Validates the response from Anti Captcha API.
346
+ #
347
+ # @param [Hash] response The response from Anti Captcha API.
348
+ #
349
+ # @raise [AntiCaptcha::Error] if Anti Captcha API responds with an error.
350
+ #
351
+ def validate_response(response)
352
+ if response['errorId'].to_i > 0
353
+ raise AntiCaptcha.raise_error(response['errorId'],
354
+ response['errorCode'], response['errorDescription'])
355
+ end
356
+ end
357
+
358
+ # Loads a CAPTCHA raw content encoded in base64 from options.
359
+ #
360
+ # @param [Hash] options Options hash.
361
+ # @option options [String] :path File path of the image to be decoded.
362
+ # @option options [File] :file File instance with image to be decoded.
363
+ # @option options [String] :body Binary content of the image to bedecoded.
364
+ # @option options [String] :body64 Binary content encoded in base64 of the
365
+ # image to be decoded.
366
+ #
367
+ # @return [String] The binary image base64 encoded.
368
+ #
369
+ def load_captcha(options)
370
+ if options[:body64]
371
+ options[:body64]
372
+ elsif options[:body]
373
+ Base64.encode64(options[:body])
374
+ elsif options[:file]
375
+ Base64.encode64(options[:file].read)
376
+ elsif options[:path]
377
+ Base64.encode64(File.open(options[:path], 'rb').read)
378
+ else
379
+ raise AntiCaptcha::ArgumentError.new('Illegal image format.')
380
+ end
381
+ rescue
382
+ raise AntiCaptcha::Error
383
+ end
384
+ end
385
+ end
@@ -0,0 +1,19 @@
1
+ module AntiCaptcha
2
+ #
3
+ # This is the base AntiCaptcha exception class. Rescue it if you want to
4
+ # catch any exception that might be raised.
5
+ #
6
+ class Error < Exception
7
+ end
8
+
9
+ class ArgumentError < Error
10
+ end
11
+
12
+ class Timeout < Error
13
+ end
14
+
15
+ def self.raise_error(error_id, error_code, error_description)
16
+ message = "ID: #{error_id} | CODE: #{error_code} | #{error_description}"
17
+ raise AntiCaptcha::Error.new(message)
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ module AntiCaptcha
2
+ #
3
+ # AntiCaptcha::HTTP exposes common HTTP routines.
4
+ #
5
+ class HTTP
6
+
7
+ #
8
+ # Performs a POST HTTP request.
9
+ # Anti Captcha API supports only POST requests.
10
+ #
11
+ # @param [Hash] options Options hash.
12
+ # @param options [String] url URL to be requested.
13
+ # @param options [Hash] payload Data to be sent through the HTTP request.
14
+ # @param options [Integer] timeout HTTP open/read timeout in seconds.
15
+ #
16
+ # @return [String] Response body of the HTTP request.
17
+ #
18
+ def self.post_request(options = {})
19
+ uri = URI(options[:url])
20
+ payload = options[:json_payload] || '{}'
21
+ timeout = options[:timeout] || 60
22
+ headers = { 'User-Agent' => AntiCaptcha::USER_AGENT,
23
+ 'Content-Type' => 'application/json' }
24
+
25
+ req = Net::HTTP::Post.new(uri.request_uri, headers)
26
+ req.body = payload
27
+
28
+ http = Net::HTTP.new(uri.hostname, uri.port)
29
+ http.use_ssl = true if (uri.scheme == 'https')
30
+ http.open_timeout = timeout
31
+ http.read_timeout = timeout
32
+ res = http.request(req)
33
+ res.body
34
+
35
+ rescue Net::OpenTimeout, Net::ReadTimeout
36
+ raise AntiCaptcha::Error.new('Request timed out.')
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ module AntiCaptcha
2
+ class ImageToTextSolution < AntiCaptcha::Solution
3
+ attr_accessor :url, :text
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module AntiCaptcha
2
+ class NoCaptchaSolution < AntiCaptcha::Solution
3
+ attr_accessor :g_recaptcha_response, :g_recaptcha_response_md5
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module AntiCaptcha
2
+ class Solution < AntiCaptcha::Model
3
+ attr_accessor :api_response, :task_result
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module AntiCaptcha
2
+ class TaskResult < AntiCaptcha::Model
3
+ attr_accessor :task_id, :error_id, :error_code, :error_description, :status,
4
+ :cost, :ip, :create_time, :end_time, :solve_count
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module AntiCaptcha
2
+ VERSION = "2.0.0"
3
+ USER_AGENT = "AntiCaptcha/Ruby v#{VERSION}"
4
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anti_captcha
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Infosimples
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-09-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
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.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: TwoCaptcha is an automated CAPTCHA solving service
56
+ email:
57
+ - team@infosimples.com.br
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - CODE_OF_CONDUCT.md
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - anti_captcha.gemspec
71
+ - bin/console
72
+ - bin/setup
73
+ - lib/anti_captcha.rb
74
+ - lib/anti_captcha/client.rb
75
+ - lib/anti_captcha/errors.rb
76
+ - lib/anti_captcha/http.rb
77
+ - lib/anti_captcha/models/image_to_text_solution.rb
78
+ - lib/anti_captcha/models/no_captcha_solution.rb
79
+ - lib/anti_captcha/models/solution.rb
80
+ - lib/anti_captcha/models/task_result.rb
81
+ - lib/anti_captcha/version.rb
82
+ homepage: https://github.com/infosimples/anti_captcha
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.5.1
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Ruby API for Anti Captcha (CAPTCHA Solver as a Service)
106
+ test_files: []