ruby-2captcha 0.2.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rspec_status +5 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +67 -0
- data/LICENSE +21 -0
- data/README.md +347 -0
- data/Rakefile +12 -0
- data/api_2captcha.gemspec +39 -0
- data/lib/api_2captcha/api2captcha_exceptions.rb +25 -0
- data/lib/api_2captcha/client.rb +274 -0
- data/lib/api_2captcha/version.rb +5 -0
- data/lib/api_2captcha.rb +12 -0
- data/sig/api_2captcha.rbs +4 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 18dcba74509e7e51524b088628bf3c9445dcd9c17536f6e7c17ff306086ef0f8
|
4
|
+
data.tar.gz: d0c4df575ef009f3f669be578006dd188645196ebf91159318a30d06073cb062
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 127de2e70f39112270a793ca6ce3f06bed13793ba2ba12a9048ac4262950adbdde49dc365235fd3609b76fee606af26e526faee323790d79f30ba13d14394ee1
|
7
|
+
data.tar.gz: 5ab07fd23cdd12284f5d7fe8aef289461b6f9269641353ed1e53f64ea15bd6bf637636fa2eecb3e002c11a77209aed8b2d1e74474c08bc0c6e0f4c736a632a50
|
data/.rspec
ADDED
data/.rspec_status
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
example_id | status | run_time |
|
2
|
+
---------------------------------- | ------ | --------------- |
|
3
|
+
./spec/api_2captcha_spec.rb[1:1] | passed | 0.00937 seconds |
|
4
|
+
./spec/api_2captcha_spec.rb[1:2:1] | passed | 0.00111 seconds |
|
5
|
+
./spec/api_2captcha_spec.rb[1:3:1] | passed | 7.1 seconds |
|
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.0
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ruby-2captcha (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
date (3.3.3)
|
11
|
+
diff-lcs (1.5.0)
|
12
|
+
json (2.6.3)
|
13
|
+
open-uri (0.3.0)
|
14
|
+
stringio
|
15
|
+
time
|
16
|
+
uri
|
17
|
+
parallel (1.23.0)
|
18
|
+
parser (3.2.2.1)
|
19
|
+
ast (~> 2.4.1)
|
20
|
+
rainbow (3.1.1)
|
21
|
+
rake (13.0.6)
|
22
|
+
regexp_parser (2.8.0)
|
23
|
+
rexml (3.2.5)
|
24
|
+
rspec (3.12.0)
|
25
|
+
rspec-core (~> 3.12.0)
|
26
|
+
rspec-expectations (~> 3.12.0)
|
27
|
+
rspec-mocks (~> 3.12.0)
|
28
|
+
rspec-core (3.12.2)
|
29
|
+
rspec-support (~> 3.12.0)
|
30
|
+
rspec-expectations (3.12.3)
|
31
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
32
|
+
rspec-support (~> 3.12.0)
|
33
|
+
rspec-mocks (3.12.5)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.12.0)
|
36
|
+
rspec-support (3.12.0)
|
37
|
+
rubocop (1.51.0)
|
38
|
+
json (~> 2.3)
|
39
|
+
parallel (~> 1.10)
|
40
|
+
parser (>= 3.2.0.0)
|
41
|
+
rainbow (>= 2.2.2, < 4.0)
|
42
|
+
regexp_parser (>= 1.8, < 3.0)
|
43
|
+
rexml (>= 3.2.5, < 4.0)
|
44
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
45
|
+
ruby-progressbar (~> 1.7)
|
46
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
47
|
+
rubocop-ast (1.28.1)
|
48
|
+
parser (>= 3.2.1.0)
|
49
|
+
ruby-progressbar (1.13.0)
|
50
|
+
stringio (3.0.6)
|
51
|
+
time (0.2.2)
|
52
|
+
date
|
53
|
+
unicode-display_width (2.4.2)
|
54
|
+
uri (0.12.1)
|
55
|
+
|
56
|
+
PLATFORMS
|
57
|
+
ruby
|
58
|
+
|
59
|
+
DEPENDENCIES
|
60
|
+
open-uri
|
61
|
+
rake (~> 13.0)
|
62
|
+
rspec (~> 3.0)
|
63
|
+
rubocop (~> 1.21)
|
64
|
+
ruby-2captcha!
|
65
|
+
|
66
|
+
BUNDLED WITH
|
67
|
+
2.4.12
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 2captcha
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,347 @@
|
|
1
|
+
# Ruby 2Captcha API Client
|
2
|
+
|
3
|
+
This is the easiest way to quickly integrate 2Captcha into your code and automate solving of any type of captcha.
|
4
|
+
|
5
|
+
A Ruby client for the 2Captcha API.
|
6
|
+
|
7
|
+
- [Installation](#installation)
|
8
|
+
- [Configuration](#configuration)
|
9
|
+
- [Client instance options](#client-instance-options)
|
10
|
+
- [Solve captcha](#solve-captcha)
|
11
|
+
- [Normal Captcha](#normal-captcha)
|
12
|
+
- [Text](#text-captcha)
|
13
|
+
- [ReCaptcha v2](#recaptcha-v2)
|
14
|
+
- [ReCaptcha v3](#recaptcha-v3)
|
15
|
+
- [GeeTest](#geetest)
|
16
|
+
- [hCaptcha](#hcaptcha)
|
17
|
+
- [KeyCaptcha](#keycaptcha)
|
18
|
+
- [Capy](#capy)
|
19
|
+
- [Grid](#grid)
|
20
|
+
- [Canvas](#canvas)
|
21
|
+
- [ClickCaptcha](#clickcaptcha)
|
22
|
+
- [Rotate](#rotate)
|
23
|
+
- [AmazonWAF](#amazon-waf)
|
24
|
+
- [CloudflareTurnstile](#cloudflare-turnstile)
|
25
|
+
- [Lemin Cropped Captcha](#lemin-cropped-captcha)
|
26
|
+
- [GeeTest V4](#geetest-v4)
|
27
|
+
- [Audio](#audio)
|
28
|
+
- [Other methods](#other-methods)
|
29
|
+
- [send / getResult](#send--getresult)
|
30
|
+
- [balance](#balance)
|
31
|
+
- [report](#report)
|
32
|
+
- [Error handling](#error-handling)
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
Add this line to your application's Gemfile:
|
37
|
+
|
38
|
+
```bash
|
39
|
+
gem 'ruby-2captcha'
|
40
|
+
```
|
41
|
+
|
42
|
+
And then execute:
|
43
|
+
|
44
|
+
```bash
|
45
|
+
$ bundle
|
46
|
+
```
|
47
|
+
Or install it yourself as:
|
48
|
+
|
49
|
+
```bash
|
50
|
+
$ gem install ruby-2captcha
|
51
|
+
```
|
52
|
+
|
53
|
+
## Configuration
|
54
|
+
To use the api2captcha gem, you'll need to import the module and create a Client instance. Here's an example:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require 'api-2captcha'
|
58
|
+
|
59
|
+
client = Api2Captcha.new(api_key: "YOUR_API_KEY")
|
60
|
+
```
|
61
|
+
|
62
|
+
There are a few options that can be configured using the Client instance:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
client.apy_key = "YOUR_API_KEY"
|
66
|
+
```
|
67
|
+
|
68
|
+
### Client instance options
|
69
|
+
|
70
|
+
|Option|Default value|Description|
|
71
|
+
|---|---|---|
|
72
|
+
|soft_id|-|your software ID obtained after publishing in [2captcha sofware catalog]|
|
73
|
+
|callback|-|URL of your web-sever that receives the captcha recognition result. The URl should be first registered in [pingback settings] of your account|
|
74
|
+
|default_timeout|120|Timeout in seconds for all captcha types except ReCaptcha. Defines how long the module tries to get the answer from `res.php` API endpoint|
|
75
|
+
|polling_interval|10|Interval in seconds between requests to `res.php` API endpoint, setting values less than 5 seconds is not recommended|
|
76
|
+
|
77
|
+
> **IMPORTANT:** once `callback` is defined for `Client` instance, all methods return only the captcha ID and DO NOT poll the API to get the result. The result will be sent to the callback URL.
|
78
|
+
To get the answer manually use [getResult method](#send--getresult)
|
79
|
+
|
80
|
+
## Solve captcha
|
81
|
+
When you submit any image-based captcha use can provide additional options to help 2captcha workers to solve it properly.
|
82
|
+
|
83
|
+
### Captcha options
|
84
|
+
| Option | Default Value | Description |
|
85
|
+
| ------------- | ------------- | -------------------------------------------------------------------------------------------------- |
|
86
|
+
| numeric | 0 | Defines if captcha contains numeric or other symbols [see more info in the API docs][post options] |
|
87
|
+
| min_len | 0 | minimal answer lenght |
|
88
|
+
| max_len | 0 | maximum answer length |
|
89
|
+
| phrase | 0 | defines if the answer contains multiple words or not |
|
90
|
+
| case_sensitive | 0 | defines if the answer is case sensitive |
|
91
|
+
| calc | 0 | defines captcha requires calculation |
|
92
|
+
| lang | - | defines the captcha language, see the [list of supported languages] |
|
93
|
+
| hint_image | - | an image with hint shown to workers with the captcha, translated into instructionsimg API parameter |
|
94
|
+
| hint_text | - | hint or task text shown to workers with the captcha |
|
95
|
+
|
96
|
+
Below you can find basic examples for every captcha type. Check out [examples directory] to find more examples with all available options.
|
97
|
+
|
98
|
+
### Normal Captcha
|
99
|
+
To bypass a normal captcha (distorted text on image) use the following method. This method also can be used to recognize any text on the image.
|
100
|
+
```ruby
|
101
|
+
result = client.normal({ image: 'path/to/captcha.jpg'})
|
102
|
+
# OR
|
103
|
+
result = client.normal({
|
104
|
+
image: 'https://site-with-captcha.com/path/to/captcha.jpg'
|
105
|
+
})
|
106
|
+
```
|
107
|
+
|
108
|
+
### Text Captcha
|
109
|
+
This method can be used to bypass a captcha that requires to answer a question provided in clear text.
|
110
|
+
```ruby
|
111
|
+
result = client.text({
|
112
|
+
textcaptcha:'If tomorrow is Saturday, what day is today?',
|
113
|
+
lang: "en"
|
114
|
+
})
|
115
|
+
```
|
116
|
+
|
117
|
+
### ReCaptcha v2
|
118
|
+
Use this method to solve ReCaptcha V2 and obtain a token to bypass the protection.
|
119
|
+
```ruby
|
120
|
+
result = client.recaptcha_v2({
|
121
|
+
googlekey: '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-',
|
122
|
+
pageurl: 'https://mysite.com/page/with/recaptcha_v2',
|
123
|
+
invisible: 1
|
124
|
+
})
|
125
|
+
```
|
126
|
+
|
127
|
+
### ReCaptcha v3
|
128
|
+
This method provides ReCaptcha V3 solver and returns a token.
|
129
|
+
```ruby
|
130
|
+
result = client.recaptcha_v3({
|
131
|
+
googlekey: '6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-',
|
132
|
+
pageurl: 'https://mysite.com/page/with/recaptcha_v3',
|
133
|
+
version: 'v3',
|
134
|
+
score: 0.3,
|
135
|
+
action: 'verify'
|
136
|
+
})
|
137
|
+
```
|
138
|
+
|
139
|
+
### FunCaptcha
|
140
|
+
FunCaptcha (Arkoselabs) solving method. Returns a token.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
result := client.funcaptcha({
|
144
|
+
publickey: "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC",
|
145
|
+
pageurl: "https://mysite.com/page/with/funcaptcha",
|
146
|
+
surl: "https://client-api.arkoselabs.com"})
|
147
|
+
```
|
148
|
+
|
149
|
+
### GeeTest
|
150
|
+
Method to solve GeeTest puzzle captcha. Returns a set of tokens as JSON.
|
151
|
+
```ruby
|
152
|
+
result = client.geetest({
|
153
|
+
gt: 'f1ab2cdefa3456789012345b6c78d90e',
|
154
|
+
api_server: 'api-na.geetest.com',
|
155
|
+
challenge: '12345678abc90123d45678ef90123a456b',
|
156
|
+
pageurl: 'https://www.site.com/page/'
|
157
|
+
})
|
158
|
+
```
|
159
|
+
|
160
|
+
### hCaptcha
|
161
|
+
Use this method to solve hCaptcha challenge. Returns a token to bypass captcha.
|
162
|
+
```ruby
|
163
|
+
result = client.hcaptcha({
|
164
|
+
sitekey: '10000000-ffff-ffff-ffff-000000000001',
|
165
|
+
pageurl: 'https://www.site.com/page/'
|
166
|
+
})
|
167
|
+
```
|
168
|
+
|
169
|
+
### KeyCaptcha
|
170
|
+
Token-based method to solve KeyCaptcha.
|
171
|
+
```ruby
|
172
|
+
result = client.keycaptcha({
|
173
|
+
s_s_c_user_id: 10,
|
174
|
+
s_s_c_session_id: '493e52c37c10c2bcdf4a00cbc9ccd1e8',
|
175
|
+
s_s_c_web_server_sign: '9006dc725760858e4c0715b835472f22-pz-',
|
176
|
+
s_s_c_web_server_sign2: '2ca3abe86d90c6142d5571db98af6714',
|
177
|
+
pageurl: 'https://www.keycaptcha.ru/demo-magnetic/'
|
178
|
+
})
|
179
|
+
```
|
180
|
+
|
181
|
+
### Capy
|
182
|
+
Token-based method to bypass Capy puzzle captcha.
|
183
|
+
```ruby
|
184
|
+
result = client.capy({
|
185
|
+
sitekey: 'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v',
|
186
|
+
pageurl: 'http://mysite.com/',
|
187
|
+
api_server: 'https://jp.api.capy.me/'
|
188
|
+
})
|
189
|
+
```
|
190
|
+
|
191
|
+
### Grid
|
192
|
+
Grid method is originally called Old ReCaptcha V2 method. The method can be used to bypass any type of captcha where you can apply a grid on image and need to click specific grid boxes. Returns numbers of boxes.
|
193
|
+
```ruby
|
194
|
+
result = client.grid({
|
195
|
+
image: 'path/to/captcha.jpg',
|
196
|
+
rows: 3,
|
197
|
+
cols: 3,
|
198
|
+
previous_id: 0,
|
199
|
+
lang: 'en',
|
200
|
+
hint_image: 'path/to/hint.jpg',
|
201
|
+
hint_text: 'Select all images with an Orange'
|
202
|
+
})
|
203
|
+
```
|
204
|
+
|
205
|
+
### Canvas
|
206
|
+
Canvas method can be used when you need to draw a line around an object on image. Returns a set of points' coordinates to draw a polygon.
|
207
|
+
```ruby
|
208
|
+
result = client.canvas({
|
209
|
+
image: 'path/to/captcha.jpg',
|
210
|
+
previous_id: 0,
|
211
|
+
lang: 'en',
|
212
|
+
hint_image: 'path/to/hint.jpg',
|
213
|
+
hint_text: 'Draw around apple'
|
214
|
+
})
|
215
|
+
```
|
216
|
+
|
217
|
+
### ClickCaptcha
|
218
|
+
ClickCaptcha method returns coordinates of points on captcha image. Can be used if you need to click on particular points on the image.
|
219
|
+
```ruby
|
220
|
+
result = client.coordinates({
|
221
|
+
image: 'path/to/captcha.jpg',
|
222
|
+
lang: 'en',
|
223
|
+
hint_image: 'path/to/hint.jpg',
|
224
|
+
hint_text: 'Connect the dots'
|
225
|
+
})
|
226
|
+
```
|
227
|
+
|
228
|
+
### Rotate
|
229
|
+
This method can be used to solve a captcha that asks to rotate an object. Mostly used to bypass FunCaptcha. Returns the rotation angle.
|
230
|
+
```ruby
|
231
|
+
result = client.rotate({
|
232
|
+
image: 'path/to/captcha.jpg',
|
233
|
+
angle: 40,
|
234
|
+
lang: 'en',
|
235
|
+
hint_image: 'path/to/hint.jpg',
|
236
|
+
hint_text: 'Put the images in the correct way'
|
237
|
+
})
|
238
|
+
```
|
239
|
+
|
240
|
+
### Lemin Cropped Captcha
|
241
|
+
Use this method to solve hCaptcha challenge. Returns JSON with answer containing the following values: answer, challenge_id.
|
242
|
+
```ruby
|
243
|
+
result = client.lemin({
|
244
|
+
captcha_id: 'CROPPED_1abcd2f_a1234b567c890d12ef3a456bc78d901d',
|
245
|
+
div_id: 'lemin-cropped-captcha',
|
246
|
+
pageurl: 'https://www.site.com/page/',
|
247
|
+
api_server: "https://api.leminnow.com/"
|
248
|
+
})
|
249
|
+
```
|
250
|
+
|
251
|
+
### Cloudflare Turnstile
|
252
|
+
Use this method to solve Cloudflare Turnstile. Returns JSON with the token.
|
253
|
+
```ruby
|
254
|
+
result = client.turnstile({
|
255
|
+
sitekey: '0x1AAAAAAAAkg0s2VIOD34y5',
|
256
|
+
pageurl: 'http://mysite.com/'
|
257
|
+
})
|
258
|
+
```
|
259
|
+
|
260
|
+
### Amazon WAF
|
261
|
+
Use this method to solve Amazon WAF Captcha also known as AWS WAF Captcha is a part of Intelligent threat mitigation for Amazon AWS. Returns JSON with the token.
|
262
|
+
```ruby
|
263
|
+
result = client.amazon_waf({
|
264
|
+
sitekey: '0x1AAAAAAAAkg0s2VIOD34y5',
|
265
|
+
iv: 'CgAHbCe2GgAAAAAj',
|
266
|
+
context: '9BUgmlm48F92WUoqv97a49ZuEJJ50TCk9MVr3C7WMtQ0X6flVbufM4n8mjFLmbLVAPgaQ1Jydeaja94iAS49ljb+sUNLoukWedAQZKrlY4RdbOOzvcFqmD/ZepQFS9N5w15Exr4VwnVq+HIxTsDJwRviElWCdzKDebN/mk8/eX2n7qJi5G3Riq0tdQw9+C4diFZU5E97RSeahejOAAJTDqduqW6uLw9NsjJBkDRBlRjxjn5CaMMo5pYOxYbGrM8Un1JH5DMOLeXbq1xWbC17YSEoM1cRFfTgOoc+VpCe36Ai9Kc=',
|
267
|
+
pageurl: 'https://non-existent-example.execute-api.us-east-1.amazonaws.com/latest',
|
268
|
+
challenge_script: "https://41bcdd4fb3cb.610cd090.us-east-1.token.awswaf.com/41bcdd4fb3cb/0d21de737ccb/cd77baa6c832/challenge.js",
|
269
|
+
captcha_script: "https://41bcdd4fb3cb.610cd090.us-east-1.captcha.awswaf.com/41bcdd4fb3cb/0d21de737ccb/cd77baa6c832/captcha.js"
|
270
|
+
})
|
271
|
+
```
|
272
|
+
|
273
|
+
### GeeTest v4
|
274
|
+
Use this method to solve GeeTest v4. Returns the response in JSON.
|
275
|
+
```ruby
|
276
|
+
result = client.geetest_v4({
|
277
|
+
captcha_id: 'e392e1d7fd421dc63325744d5a2b9c73',
|
278
|
+
pageurl: 'https://www.site.com/page/'
|
279
|
+
})
|
280
|
+
```
|
281
|
+
|
282
|
+
### Audio
|
283
|
+
This method can be used to solve a audio captcha
|
284
|
+
```ruby
|
285
|
+
result = client.audio({
|
286
|
+
audio: 'path/to/audio.jpg',
|
287
|
+
lang: "en"
|
288
|
+
})
|
289
|
+
```
|
290
|
+
|
291
|
+
## Other methods
|
292
|
+
|
293
|
+
### send / getResult
|
294
|
+
These methods can be used for manual captcha submission and answer polling.
|
295
|
+
```ruby
|
296
|
+
|
297
|
+
# example for normal captcha
|
298
|
+
captcha_id = client.send('path/to/captcha.jpg')
|
299
|
+
|
300
|
+
# or for another captcha, for example for lemin
|
301
|
+
captcha_id = client.send({
|
302
|
+
method:"lemin",
|
303
|
+
captcha_id: "CROPPED_3dfdd5c_d1872b526b794d83ba3b365eb15a200b",
|
304
|
+
api_server: "api.leminnow.com",
|
305
|
+
div_id: "lemin-cropped-captcha",
|
306
|
+
pageurl: "https://www.site.com/page/"
|
307
|
+
})
|
308
|
+
|
309
|
+
time.sleep(20)
|
310
|
+
|
311
|
+
# Get result
|
312
|
+
result = client.get_result(captcha_id)
|
313
|
+
```
|
314
|
+
|
315
|
+
### balance
|
316
|
+
Use this method to get your account's balance
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
balance = client.get_balance
|
320
|
+
```
|
321
|
+
|
322
|
+
### report
|
323
|
+
Use this method to report good or bad captcha answer.
|
324
|
+
```ruby
|
325
|
+
client.report(captcha_id, True) # captcha solved correctly
|
326
|
+
client.report(captcha_id, False) # captcha solved incorrectly
|
327
|
+
```
|
328
|
+
|
329
|
+
### Error handling
|
330
|
+
In case of an error, the captcha solver throws an exception. It's important to properly handle these cases. We recommend using `begin rescue` to handle exceptions.
|
331
|
+
```ruby
|
332
|
+
begin
|
333
|
+
result = client.text('If tomorrow is Saturday, what day is today?')
|
334
|
+
rescue Api2Captcha::ValidationException => e
|
335
|
+
# invalid parameters passed
|
336
|
+
puts(e)
|
337
|
+
rescue Api2Captcha::NetworkException => e
|
338
|
+
# network error occurred
|
339
|
+
puts(e)
|
340
|
+
rescue Api2Captcha::ApiException => e
|
341
|
+
# api respond with error
|
342
|
+
puts(e)
|
343
|
+
rescue Api2Captcha::TimeoutException => e
|
344
|
+
# captcha is not solved so far
|
345
|
+
puts(e)
|
346
|
+
end
|
347
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/api_2captcha/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "ruby-2captcha"
|
7
|
+
spec.version = Api2Captcha::VERSION
|
8
|
+
spec.authors = ["2captcha.com"]
|
9
|
+
spec.email = ["info@2captcha.com"]
|
10
|
+
|
11
|
+
spec.summary = "Write a short summary, because RubyGems requires one."
|
12
|
+
spec.description = "Write a longer description or delete this line."
|
13
|
+
spec.homepage = "https://github.com/2captcha/"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
# spec.metadata["allowed_push_host"] = "https://rubygems.pkg.github.com/2captcha"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/2captcha/"
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/2captcha/"
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
|
37
|
+
# For more information and examples about making a new gem, check out our
|
38
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Api2Captcha
|
2
|
+
class ValidationException < StandardError
|
3
|
+
def initialize(message = 'Invalid parameters passed')
|
4
|
+
super(message)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class NetworkException < StandardError
|
9
|
+
def initialize(message = 'Network error occurred')
|
10
|
+
super(message)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ApiException < StandardError
|
15
|
+
def initialize(message = 'API response error')
|
16
|
+
super(message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TimeoutException < StandardError
|
21
|
+
def initialize(message = 'Captcha solving timed out')
|
22
|
+
super(message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'base64'
|
6
|
+
require 'open-uri'
|
7
|
+
|
8
|
+
class Api2Captcha::Client
|
9
|
+
DEFAULT_DOMAIN = "2captcha.com"
|
10
|
+
BASE_URL_FORMAT = "https://%s"
|
11
|
+
|
12
|
+
attr_reader :api_key, :soft_id
|
13
|
+
|
14
|
+
attr_accessor :domain, :callback,
|
15
|
+
:default_timeout,
|
16
|
+
:recaptcha_timeout,
|
17
|
+
:polling_interval
|
18
|
+
|
19
|
+
def initialize(api_key, soft_id = 0, callback = nil)
|
20
|
+
@api_key = api_key
|
21
|
+
@soft_id = soft_id
|
22
|
+
@callback = callback
|
23
|
+
@default_timeout = 120
|
24
|
+
@recaptcha_timeout = 600
|
25
|
+
@polling_interval = 10
|
26
|
+
@domain = DEFAULT_DOMAIN
|
27
|
+
end
|
28
|
+
|
29
|
+
def solve(method, params = {}, file_path = nil, return_id: false)
|
30
|
+
params["method"] = method
|
31
|
+
params["key"] = @api_key
|
32
|
+
params["soft_id"] = @soft_id
|
33
|
+
params["json"] = 1
|
34
|
+
|
35
|
+
complete_params = get_params(params)
|
36
|
+
captcha_id = send_request(complete_params)
|
37
|
+
return_id ? get_result(captcha_id) : captcha_id
|
38
|
+
end
|
39
|
+
|
40
|
+
def send(*args)
|
41
|
+
raise ArgumentError,
|
42
|
+
"Invalid arguments of the send method" unless args.size == 1
|
43
|
+
|
44
|
+
arg = args.first
|
45
|
+
if arg.is_a?(String)
|
46
|
+
solve("POST", {}, arg, return_id: true)
|
47
|
+
elsif arg.is_a?(Hash)
|
48
|
+
method = arg.delete(:method) || "POST"
|
49
|
+
solve(method, arg, return_id: true)
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Invalid arguments of the send method"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_result(captcha_id)
|
56
|
+
uri = URI("#{base_url}/res.php?key=#{@api_key}&action=get&id=#{captcha_id}&json=1")
|
57
|
+
start_time = Time.now
|
58
|
+
|
59
|
+
loop do
|
60
|
+
response = make_request(uri)
|
61
|
+
|
62
|
+
case response
|
63
|
+
when Net::HTTPSuccess
|
64
|
+
response_json = JSON.parse(response.body)
|
65
|
+
if response_json["status"] == 1
|
66
|
+
return response_json["request"]
|
67
|
+
elsif response_json["request"] == "CAPCHA_NOT_READY"
|
68
|
+
sleep(polling_interval)
|
69
|
+
else
|
70
|
+
raise ApiException, "API Error: #{response_json["error_text"]}"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
raise NetworkException, "Network Error: #{response.code.to_i}"
|
74
|
+
end
|
75
|
+
|
76
|
+
raise TimeoutException, "Timeout" if Time.now - start_time > default_timeout
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def report(captcha_id, is_correct)
|
81
|
+
report = is_correct ? "reportgood" : "reportbad"
|
82
|
+
uri = URI("#{base_url}/res.php?key=#{@api_key}&action=#{report}&id=#{captcha_id}")
|
83
|
+
make_request(uri)
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_balance
|
87
|
+
response = make_res_request({ "action" => "getbalance" }, "getbalance")
|
88
|
+
return response["request"].to_f
|
89
|
+
end
|
90
|
+
|
91
|
+
def normal(params)
|
92
|
+
solve("post", params)
|
93
|
+
end
|
94
|
+
|
95
|
+
def text(params)
|
96
|
+
solve("textcaptcha", params)
|
97
|
+
end
|
98
|
+
|
99
|
+
def recaptcha_v2(params)
|
100
|
+
solve("userrecaptcha", params)
|
101
|
+
end
|
102
|
+
|
103
|
+
def recaptcha_v3(params)
|
104
|
+
solve("userrecaptcha", params)
|
105
|
+
end
|
106
|
+
|
107
|
+
def funcaptcha(params)
|
108
|
+
solve("funcaptcha", params)
|
109
|
+
end
|
110
|
+
|
111
|
+
def geetest(params)
|
112
|
+
solve("geetest", params)
|
113
|
+
end
|
114
|
+
|
115
|
+
def hcaptcha(params)
|
116
|
+
solve("hcaptcha", params)
|
117
|
+
end
|
118
|
+
|
119
|
+
def keycaptcha(params)
|
120
|
+
solve("keycaptcha", params)
|
121
|
+
end
|
122
|
+
|
123
|
+
def capy(params)
|
124
|
+
solve("capy", params)
|
125
|
+
end
|
126
|
+
|
127
|
+
def grid(params)
|
128
|
+
params["recaptcha"] = 1
|
129
|
+
solve("capy", params)
|
130
|
+
end
|
131
|
+
|
132
|
+
def canvas(params)
|
133
|
+
params["recaptcha"] = 1
|
134
|
+
params["canvas"] = 1
|
135
|
+
solve("post", params)
|
136
|
+
end
|
137
|
+
|
138
|
+
def coordinates(params)
|
139
|
+
params["coordinatescaptcha"] = 1
|
140
|
+
|
141
|
+
solve("post", params)
|
142
|
+
end
|
143
|
+
|
144
|
+
def rotate(params)
|
145
|
+
solve("rotatecaptcha", params)
|
146
|
+
end
|
147
|
+
|
148
|
+
def geetest_v4(params)
|
149
|
+
solve("geetest_v4", params)
|
150
|
+
end
|
151
|
+
|
152
|
+
def lemin(params)
|
153
|
+
solve("lemin", params)
|
154
|
+
end
|
155
|
+
|
156
|
+
def turnstile(params)
|
157
|
+
solve("turnstile", params)
|
158
|
+
end
|
159
|
+
|
160
|
+
def amazon_waf(params)
|
161
|
+
solve("amazon_waf", params)
|
162
|
+
end
|
163
|
+
|
164
|
+
def audio(params)
|
165
|
+
audio = params.delete(:audio)
|
166
|
+
audio_content = File.file?(audio) ? File.binread(audio) : audio
|
167
|
+
|
168
|
+
params = params.merge(
|
169
|
+
"body" => Base64.strict_encode64(audio_content),
|
170
|
+
"lang" => params[:lang]
|
171
|
+
)
|
172
|
+
solve("audio", params)
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def base_url
|
178
|
+
BASE_URL_FORMAT % @domain
|
179
|
+
end
|
180
|
+
|
181
|
+
def send_request(params)
|
182
|
+
uri = URI("#{base_url}/in.php")
|
183
|
+
req = Net::HTTP::Post.new(uri)
|
184
|
+
req.content_type = 'application/json'
|
185
|
+
req.body = params.to_json
|
186
|
+
captcha_id = get_captcha_id(make_request(uri, req))
|
187
|
+
handle_response(captcha_id)
|
188
|
+
end
|
189
|
+
|
190
|
+
def get_params(params)
|
191
|
+
params[:image].nil? ? params : file_params(params)
|
192
|
+
end
|
193
|
+
|
194
|
+
def file_params(params)
|
195
|
+
image = params.delete(:image)
|
196
|
+
hint_image = params.delete(:hint_image)
|
197
|
+
|
198
|
+
image_content = get_image_content(image)
|
199
|
+
hint_image_content = get_image_content(hint_image) if hint_image
|
200
|
+
result_params = {
|
201
|
+
"method" => "base64",
|
202
|
+
"body" => Base64.strict_encode64(image_content),
|
203
|
+
"filename" => File.basename(image),
|
204
|
+
"ext" => File.extname(image).delete(".")
|
205
|
+
}
|
206
|
+
|
207
|
+
result_params["imginstructions"] = Base64.strict_encode64(hint_image_content) if hint_image_content
|
208
|
+
params.merge(result_params)
|
209
|
+
end
|
210
|
+
|
211
|
+
def get_image_content(image)
|
212
|
+
return download_image(image) if image.start_with?('http')
|
213
|
+
return File.binread(image) if File.file?(image)
|
214
|
+
image
|
215
|
+
end
|
216
|
+
|
217
|
+
def download_image(url)
|
218
|
+
response = URI.open(url)
|
219
|
+
if response.status[0] != '200'
|
220
|
+
raise StandardError, "File could not be downloaded from url: #{url}"
|
221
|
+
end
|
222
|
+
response.read
|
223
|
+
end
|
224
|
+
|
225
|
+
def handle_response(captcha_id)
|
226
|
+
captcha_result = get_result(captcha_id) if @callback.nil?
|
227
|
+
@callback&.call(captcha_id)
|
228
|
+
captcha_result
|
229
|
+
end
|
230
|
+
|
231
|
+
def get_captcha_id(response)
|
232
|
+
case response
|
233
|
+
when Net::HTTPSuccess
|
234
|
+
response_json = JSON.parse(response.body.strip)
|
235
|
+
if response_json["status"] == 1
|
236
|
+
response_json["request"]
|
237
|
+
else
|
238
|
+
raise ApiException, "API Error: #{response.body.strip}"
|
239
|
+
end
|
240
|
+
else
|
241
|
+
raise NetworkException, "Network Error: #{response.code.to_i}"
|
242
|
+
end
|
243
|
+
rescue JSON::ParserError => e
|
244
|
+
raise "Failed to parse response: #{e.message}"
|
245
|
+
end
|
246
|
+
|
247
|
+
def make_request(uri, req = nil)
|
248
|
+
if req.nil?
|
249
|
+
Net::HTTP.get_response(uri)
|
250
|
+
else
|
251
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
252
|
+
http.request(req)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def make_res_request(request, action)
|
258
|
+
uri = URI("#{base_url}/res.php?key=#{@api_key}&action=#{action}&json=1")
|
259
|
+
req = Net::HTTP::Post.new(uri)
|
260
|
+
req.content_type = 'application/json'
|
261
|
+
req.body = request.to_json
|
262
|
+
|
263
|
+
response = make_request(uri, req)
|
264
|
+
|
265
|
+
case response
|
266
|
+
when Net::HTTPSuccess
|
267
|
+
return JSON.parse(response.body)
|
268
|
+
else
|
269
|
+
raise NetworkException, "Network Error: #{response.code.to_i}"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
|
data/lib/api_2captcha.rb
ADDED
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-2captcha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- 2captcha.com
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Write a longer description or delete this line.
|
14
|
+
email:
|
15
|
+
- info@2captcha.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".rspec"
|
21
|
+
- ".rspec_status"
|
22
|
+
- ".rubocop.yml"
|
23
|
+
- ".ruby-version"
|
24
|
+
- Gemfile
|
25
|
+
- Gemfile.lock
|
26
|
+
- LICENSE
|
27
|
+
- README.md
|
28
|
+
- Rakefile
|
29
|
+
- api_2captcha.gemspec
|
30
|
+
- lib/api_2captcha.rb
|
31
|
+
- lib/api_2captcha/api2captcha_exceptions.rb
|
32
|
+
- lib/api_2captcha/client.rb
|
33
|
+
- lib/api_2captcha/version.rb
|
34
|
+
- sig/api_2captcha.rbs
|
35
|
+
homepage: https://github.com/2captcha/
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
metadata:
|
39
|
+
homepage_uri: https://github.com/2captcha/
|
40
|
+
source_code_uri: https://github.com/2captcha/
|
41
|
+
changelog_uri: https://github.com/2captcha/
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.6.0
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubygems_version: 3.2.3
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: Write a short summary, because RubyGems requires one.
|
61
|
+
test_files: []
|