@biggora/claude-plugins 1.1.1 → 1.2.2
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.
- package/.claude/settings.local.json +3 -1
- package/README.md +24 -17
- package/package.json +1 -1
- package/registry/registry.json +319 -244
- package/specs/coding.md +24 -0
- package/specs/pod.md +2 -0
- package/src/skills/captcha/README.md +221 -0
- package/src/skills/captcha/SKILL.md +355 -0
- package/src/skills/captcha/references/captcha-types.md +254 -0
- package/src/skills/captcha/references/services.md +172 -0
- package/src/skills/captcha/references/stealth.md +238 -0
- package/src/skills/captcha/scripts/solve_captcha.py +323 -0
- package/src/skills/captcha/scripts/solve_image_grid.py +350 -0
- package/src/skills/codex-cli/SKILL.md +21 -11
- package/src/skills/gemini-cli/SKILL.md +27 -13
- package/src/skills/gemini-cli/references/commands.md +21 -14
- package/src/skills/gemini-cli/references/configuration.md +23 -18
- package/src/skills/gemini-cli/references/headless-and-scripting.md +7 -17
- package/src/skills/gemini-cli/references/mcp-and-extensions.md +12 -6
- package/src/skills/google-merchant-api/SKILL.md +581 -0
- package/src/skills/google-merchant-api/references/accounts.md +247 -0
- package/src/skills/google-merchant-api/references/content-api-legacy.md +216 -0
- package/src/skills/google-merchant-api/references/datasources.md +233 -0
- package/src/skills/google-merchant-api/references/inventories.md +201 -0
- package/src/skills/google-merchant-api/references/migration.md +267 -0
- package/src/skills/google-merchant-api/references/products.md +316 -0
- package/src/skills/google-merchant-api/references/promotions.md +201 -0
- package/src/skills/google-merchant-api/references/reports.md +240 -0
- package/src/skills/lv-aggregators-api/SKILL.md +113 -0
- package/src/skills/lv-aggregators-api/references/integration-guide.md +368 -0
- package/src/skills/lv-aggregators-api/references/kurpirkt.md +103 -0
- package/src/skills/lv-aggregators-api/references/salidzini.md +122 -0
- package/src/skills/notebook-lm/SKILL.md +1 -1
- package/src/skills/screen-recording/SKILL.md +243 -213
- package/src/skills/screen-recording/references/design-patterns.md +4 -2
- package/src/skills/screen-recording/references/ffmpeg-recording.md +473 -0
- package/src/skills/screen-recording/references/{approach1-programmatic.md → programmatic-generation.md} +45 -22
- package/src/skills/screen-recording/references/python-fallback.md +222 -0
- package/src/skills/tailwindcss-best-practices/SKILL.md +180 -0
- package/src/skills/tailwindcss-best-practices/references/best-practices-utility-patterns.md +87 -0
- package/src/skills/tailwindcss-best-practices/references/core-installation.md +109 -0
- package/src/skills/tailwindcss-best-practices/references/core-preflight.md +200 -0
- package/src/skills/tailwindcss-best-practices/references/core-responsive.md +163 -0
- package/src/skills/tailwindcss-best-practices/references/core-source-detection.md +114 -0
- package/src/skills/tailwindcss-best-practices/references/core-theme.md +108 -0
- package/src/skills/tailwindcss-best-practices/references/core-utility-classes.md +59 -0
- package/src/skills/tailwindcss-best-practices/references/core-variants.md +204 -0
- package/src/skills/tailwindcss-best-practices/references/effects-form-controls.md +76 -0
- package/src/skills/tailwindcss-best-practices/references/effects-mask.md +91 -0
- package/src/skills/tailwindcss-best-practices/references/effects-scroll-snap.md +59 -0
- package/src/skills/tailwindcss-best-practices/references/effects-text-shadow.md +78 -0
- package/src/skills/tailwindcss-best-practices/references/effects-transition-animation.md +80 -0
- package/src/skills/tailwindcss-best-practices/references/effects-visibility-interactivity.md +82 -0
- package/src/skills/tailwindcss-best-practices/references/features-content-detection.md +175 -0
- package/src/skills/tailwindcss-best-practices/references/features-custom-styles.md +203 -0
- package/src/skills/tailwindcss-best-practices/references/features-dark-mode.md +137 -0
- package/src/skills/tailwindcss-best-practices/references/features-functions-directives.md +241 -0
- package/src/skills/tailwindcss-best-practices/references/features-upgrade.md +160 -0
- package/src/skills/tailwindcss-best-practices/references/layout-aspect-ratio.md +39 -0
- package/src/skills/tailwindcss-best-practices/references/layout-columns.md +80 -0
- package/src/skills/tailwindcss-best-practices/references/layout-display.md +110 -0
- package/src/skills/tailwindcss-best-practices/references/layout-flexbox.md +112 -0
- package/src/skills/tailwindcss-best-practices/references/layout-grid.md +87 -0
- package/src/skills/tailwindcss-best-practices/references/layout-height.md +97 -0
- package/src/skills/tailwindcss-best-practices/references/layout-inset.md +103 -0
- package/src/skills/tailwindcss-best-practices/references/layout-logical-properties.md +92 -0
- package/src/skills/tailwindcss-best-practices/references/layout-margin.md +126 -0
- package/src/skills/tailwindcss-best-practices/references/layout-min-max-sizing.md +63 -0
- package/src/skills/tailwindcss-best-practices/references/layout-object-fit-position.md +64 -0
- package/src/skills/tailwindcss-best-practices/references/layout-overflow.md +57 -0
- package/src/skills/tailwindcss-best-practices/references/layout-padding.md +77 -0
- package/src/skills/tailwindcss-best-practices/references/layout-position.md +85 -0
- package/src/skills/tailwindcss-best-practices/references/layout-tables.md +67 -0
- package/src/skills/tailwindcss-best-practices/references/layout-width.md +102 -0
- package/src/skills/tailwindcss-best-practices/references/transform-base.md +68 -0
- package/src/skills/tailwindcss-best-practices/references/transform-rotate.md +70 -0
- package/src/skills/tailwindcss-best-practices/references/transform-scale.md +83 -0
- package/src/skills/tailwindcss-best-practices/references/transform-skew.md +62 -0
- package/src/skills/tailwindcss-best-practices/references/transform-translate.md +77 -0
- package/src/skills/tailwindcss-best-practices/references/typography-font-text.md +142 -0
- package/src/skills/tailwindcss-best-practices/references/typography-list-style.md +65 -0
- package/src/skills/tailwindcss-best-practices/references/typography-text-align.md +60 -0
- package/src/skills/tailwindcss-best-practices/references/visual-background.md +76 -0
- package/src/skills/tailwindcss-best-practices/references/visual-border.md +108 -0
- package/src/skills/tailwindcss-best-practices/references/visual-effects.md +111 -0
- package/src/skills/tailwindcss-best-practices/references/visual-svg.md +82 -0
- package/src/skills/test-mobile-app/SKILL.md +11 -6
- package/src/skills/test-mobile-app/scripts/analyze_apk.py +15 -4
- package/src/skills/test-mobile-app/scripts/check_environment.py +5 -5
- package/src/skills/test-mobile-app/scripts/run_tests.py +1 -1
- package/src/skills/test-web-ui/SKILL.md +264 -84
- package/src/skills/test-web-ui/scripts/discover.py +25 -12
- package/src/skills/test-web-ui/scripts/run_tests.py +3 -2
- package/src/skills/tm-search/SKILL.md +242 -106
- package/src/skills/tm-search/references/scraping-fallback.md +60 -95
- package/src/skills/tm-search/scripts/tm_search.py +453 -375
- package/src/skills/vite-best-practices/SKILL.md +115 -0
- package/src/skills/vite-best-practices/references/build-and-ssr.md +255 -0
- package/src/skills/vite-best-practices/references/core-config.md +231 -0
- package/src/skills/vite-best-practices/references/core-features.md +222 -0
- package/src/skills/vite-best-practices/references/core-plugin-api.md +294 -0
- package/src/skills/vite-best-practices/references/environment-api.md +108 -0
- package/src/skills/vite-best-practices/references/rolldown-migration.md +242 -0
- package/src/skills/screen-recording/references/approach2-xvfb.md +0 -232
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# CAPTCHA Types — Detection & Injection Patterns
|
|
2
|
+
|
|
3
|
+
Reference for identifying which CAPTCHA is on a page and injecting the solved token.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## reCAPTCHA v2 (Checkbox)
|
|
8
|
+
|
|
9
|
+
The most common CAPTCHA. Shows a checkbox; sometimes shows an image grid challenge.
|
|
10
|
+
|
|
11
|
+
### Detection
|
|
12
|
+
```javascript
|
|
13
|
+
// Returns sitekey if reCAPTCHA v2 is present, null otherwise
|
|
14
|
+
const el = document.querySelector('.g-recaptcha, [data-sitekey]:not(.h-captcha):not(.cf-turnstile)');
|
|
15
|
+
el?.dataset?.sitekey ?? null;
|
|
16
|
+
|
|
17
|
+
// Alternative: extract from iframe src
|
|
18
|
+
const frame = document.querySelector('iframe[src*="recaptcha"]');
|
|
19
|
+
frame?.src?.match(/[?&]k=([^&]+)/)?.[1] ?? null;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Parameters to collect
|
|
23
|
+
- `sitekey` — the `data-sitekey` attribute on `.g-recaptcha`
|
|
24
|
+
- `pageurl` — `window.location.href`
|
|
25
|
+
|
|
26
|
+
### Token injection
|
|
27
|
+
```javascript
|
|
28
|
+
function injectRecaptchaV2(token) {
|
|
29
|
+
// Set the hidden response field (may be display:none — that's fine)
|
|
30
|
+
document.querySelectorAll('[name="g-recaptcha-response"]').forEach(el => {
|
|
31
|
+
el.value = token;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Trigger the registered callback
|
|
35
|
+
if (window.___grecaptcha_cfg) {
|
|
36
|
+
Object.values(window.___grecaptcha_cfg.clients || {}).forEach(client => {
|
|
37
|
+
Object.keys(client).forEach(key => {
|
|
38
|
+
const widget = client[key];
|
|
39
|
+
if (widget && typeof widget.callback === 'function') {
|
|
40
|
+
widget.callback(token);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fallback: look for a global callback name in the render call
|
|
47
|
+
// Search page source for: grecaptcha.render(..., { callback: 'FUNCTION_NAME' })
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Notes
|
|
52
|
+
- If injection has no effect, check if there's a `grecaptcha.render(element, { callback: fn })` call
|
|
53
|
+
in the page source — call that function with the token directly.
|
|
54
|
+
- Some sites wrap the callback in `window.onRecaptchaSuccess` or similar. Search the page for
|
|
55
|
+
`grecaptcha` to find where the callback is registered.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## reCAPTCHA v2 Invisible
|
|
60
|
+
|
|
61
|
+
No visible checkbox. Triggered programmatically with `grecaptcha.execute()`. Produces the same
|
|
62
|
+
token format as v2 checkbox.
|
|
63
|
+
|
|
64
|
+
### Detection
|
|
65
|
+
```javascript
|
|
66
|
+
// Invisible reCAPTCHA has no visible .g-recaptcha widget
|
|
67
|
+
// Look for grecaptcha.execute() calls or a render with { size: 'invisible' }
|
|
68
|
+
const src = document.documentElement.innerHTML;
|
|
69
|
+
const isInvisible = src.includes("size: 'invisible'") || src.includes('"invisible"');
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Solve with `--invisible` flag
|
|
73
|
+
```bash
|
|
74
|
+
python scripts/solve_captcha.py --type recaptcha-v2 --invisible --sitekey KEY --pageurl URL
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Token injection
|
|
78
|
+
Same as v2 checkbox injection above.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## reCAPTCHA v3
|
|
83
|
+
|
|
84
|
+
Score-based, invisible. No user interaction at all. The site calls
|
|
85
|
+
`grecaptcha.execute(sitekey, {action: 'actionName'})` in its JS, then validates the returned
|
|
86
|
+
score server-side (scores range 0.0–1.0; bots score low).
|
|
87
|
+
|
|
88
|
+
### Detection
|
|
89
|
+
```javascript
|
|
90
|
+
// Look for grecaptcha.execute with a sitekey in the page source
|
|
91
|
+
// There is no visible widget — detection is by source inspection
|
|
92
|
+
const src = document.documentElement.innerHTML;
|
|
93
|
+
const match = src.match(/grecaptcha\.execute\(['"]([^'"]+)['"]/);
|
|
94
|
+
match?.[1] ?? null; // sitekey if found
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or grep the page HTML for `grecaptcha.execute(` to find the sitekey and action name.
|
|
98
|
+
|
|
99
|
+
### Parameters
|
|
100
|
+
- `sitekey` — from the `grecaptcha.execute('SITEKEY', ...)` call
|
|
101
|
+
- `pageurl` — `window.location.href`
|
|
102
|
+
- `action` — the action string in `{action: 'NAME'}`, e.g. `verify`, `submit`, `login`
|
|
103
|
+
- `min_score` — request a score of at least this value (default 0.5; lower = easier to solve)
|
|
104
|
+
|
|
105
|
+
### Solve
|
|
106
|
+
```bash
|
|
107
|
+
python scripts/solve_captcha.py --type recaptcha-v3 \
|
|
108
|
+
--sitekey SITEKEY --pageurl URL \
|
|
109
|
+
--action submit --min-score 0.7
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Token injection
|
|
113
|
+
v3 tokens are typically consumed by an AJAX call, not a visible form field. The site expects
|
|
114
|
+
the token in a specific request parameter. Two approaches:
|
|
115
|
+
|
|
116
|
+
1. **Intercept the form submit** — inject the token before the AJAX fires:
|
|
117
|
+
```javascript
|
|
118
|
+
// Override grecaptcha.execute to return your solved token
|
|
119
|
+
window.grecaptcha.execute = () => Promise.resolve(TOKEN);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
2. **Set the hidden field** if the site stores the token in an input before submitting:
|
|
123
|
+
```javascript
|
|
124
|
+
document.querySelectorAll('[name="g-recaptcha-response"], [name="token"]').forEach(el => {
|
|
125
|
+
el.value = TOKEN;
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## hCaptcha
|
|
132
|
+
|
|
133
|
+
Common on Cloudflare-protected and privacy-focused sites.
|
|
134
|
+
|
|
135
|
+
### Detection
|
|
136
|
+
```javascript
|
|
137
|
+
const el = document.querySelector('.h-captcha, [data-hcaptcha-sitekey]');
|
|
138
|
+
el?.dataset?.sitekey ?? document.querySelector('[data-sitekey].h-captcha')?.dataset?.sitekey ?? null;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Solve
|
|
142
|
+
```bash
|
|
143
|
+
python scripts/solve_captcha.py --type hcaptcha --sitekey KEY --pageurl URL
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Token injection
|
|
147
|
+
```javascript
|
|
148
|
+
function injectHcaptcha(token) {
|
|
149
|
+
// Set the response field
|
|
150
|
+
document.querySelectorAll('[name="h-captcha-response"]').forEach(el => {
|
|
151
|
+
el.value = token;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Trigger hCaptcha callback
|
|
155
|
+
if (window.hcaptcha) {
|
|
156
|
+
// Some sites register a callback via hcaptcha.render
|
|
157
|
+
const iframes = document.querySelectorAll('iframe[src*="hcaptcha"]');
|
|
158
|
+
// Try the global callback if defined
|
|
159
|
+
if (typeof window.onHcaptchaSuccess === 'function') window.onHcaptchaSuccess(token);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If the above doesn't trigger the form to proceed, look for `data-callback` on the `.h-captcha`
|
|
165
|
+
element and call that function by name:
|
|
166
|
+
```javascript
|
|
167
|
+
const callbackName = document.querySelector('[data-callback]')?.dataset?.callback;
|
|
168
|
+
if (callbackName && window[callbackName]) window[callbackName](token);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Cloudflare Turnstile
|
|
174
|
+
|
|
175
|
+
Cloudflare's newer CAPTCHA, launched 2022. Invisible by default.
|
|
176
|
+
|
|
177
|
+
### Detection
|
|
178
|
+
```javascript
|
|
179
|
+
const el = document.querySelector('.cf-turnstile');
|
|
180
|
+
el?.dataset?.sitekey ?? null;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Solve
|
|
184
|
+
```bash
|
|
185
|
+
python scripts/solve_captcha.py --type turnstile --sitekey KEY --pageurl URL
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Token injection
|
|
189
|
+
```javascript
|
|
190
|
+
function injectTurnstile(token) {
|
|
191
|
+
document.querySelectorAll('[name="cf-turnstile-response"]').forEach(el => {
|
|
192
|
+
el.value = token;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Some sites use a callback
|
|
196
|
+
const callbackName = document.querySelector('.cf-turnstile')?.dataset?.callback;
|
|
197
|
+
if (callbackName && window[callbackName]) window[callbackName](token);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Image / Text CAPTCHA
|
|
204
|
+
|
|
205
|
+
Classic distorted text or simple math CAPTCHAs. Send the image, get text back.
|
|
206
|
+
|
|
207
|
+
### Detection
|
|
208
|
+
```javascript
|
|
209
|
+
// Look for an image element with "captcha" in its src or alt
|
|
210
|
+
document.querySelector('img[src*="captcha" i], img[alt*="captcha" i]')?.src ?? null;
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Capture the image
|
|
214
|
+
```python
|
|
215
|
+
# With Playwright: screenshot just the CAPTCHA element
|
|
216
|
+
captcha_el = page.query_selector('img[alt*="captcha" i]')
|
|
217
|
+
captcha_el.screenshot(path="captcha.png")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Or fetch the image URL directly:
|
|
221
|
+
```python
|
|
222
|
+
import urllib.request
|
|
223
|
+
img_url = page.evaluate("document.querySelector('img[src*=\"captcha\"]').src")
|
|
224
|
+
urllib.request.urlretrieve(img_url, "captcha.png")
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Solve
|
|
228
|
+
```bash
|
|
229
|
+
python scripts/solve_captcha.py --type image --image captcha.png
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Use the result
|
|
233
|
+
```javascript
|
|
234
|
+
// The solved token is the text answer, not a long JWT
|
|
235
|
+
document.querySelector('input[name*="captcha" i]').value = SOLVED_TEXT;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Tips for better accuracy
|
|
239
|
+
- Crop to just the CAPTCHA image (no padding or surrounding UI)
|
|
240
|
+
- Convert to grayscale and increase contrast before sending
|
|
241
|
+
- If the CAPTCHA is math (e.g. "3 + 4 = ?"), the solver returns "7" as text
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Quick Reference Table
|
|
246
|
+
|
|
247
|
+
| Type | Detect selector | Key field to inject | Callback trigger |
|
|
248
|
+
|------|----------------|---------------------|-----------------|
|
|
249
|
+
| reCAPTCHA v2 | `.g-recaptcha` | `[name="g-recaptcha-response"]` | `___grecaptcha_cfg.clients[n][k].callback(token)` |
|
|
250
|
+
| reCAPTCHA v2 invisible | script source | `[name="g-recaptcha-response"]` | same as v2 |
|
|
251
|
+
| reCAPTCHA v3 | script source | hidden input or AJAX param | override `grecaptcha.execute` |
|
|
252
|
+
| hCaptcha | `.h-captcha` | `[name="h-captcha-response"]` | `[data-callback]` function |
|
|
253
|
+
| Turnstile | `.cf-turnstile` | `[name="cf-turnstile-response"]` | `[data-callback]` function |
|
|
254
|
+
| Image | `img[src*=captcha]` | text input near image | n/a |
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# CAPTCHA Solving Services
|
|
2
|
+
|
|
3
|
+
Comparison of the three major services supported by `solve_captcha.py`.
|
|
4
|
+
|
|
5
|
+
All three use the same API endpoint format (2captcha-compatible), so you can switch
|
|
6
|
+
between them with only the `--service` flag.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 2captcha
|
|
11
|
+
|
|
12
|
+
**Website:** https://2captcha.com
|
|
13
|
+
**`--service` flag:** `2captcha` (default)
|
|
14
|
+
|
|
15
|
+
The most widely documented and supported service. Operates since 2009.
|
|
16
|
+
Uses a combination of human workers and AI for solving.
|
|
17
|
+
|
|
18
|
+
### Pricing (approximate)
|
|
19
|
+
| Type | Per 1,000 |
|
|
20
|
+
|------|-----------|
|
|
21
|
+
| reCAPTCHA v2 | $1.00–$1.50 |
|
|
22
|
+
| reCAPTCHA v3 | $2.00–$3.00 |
|
|
23
|
+
| hCaptcha | $1.50–$2.00 |
|
|
24
|
+
| Turnstile | $1.00–$1.50 |
|
|
25
|
+
| Image/text | $0.50–$1.00 |
|
|
26
|
+
|
|
27
|
+
### Getting started
|
|
28
|
+
1. Sign up at https://2captcha.com/register
|
|
29
|
+
2. Add funds (minimum ~$3, accepts cards and crypto)
|
|
30
|
+
3. Copy your API key from https://2captcha.com/enterpage
|
|
31
|
+
|
|
32
|
+
### Free testing
|
|
33
|
+
2captcha offers a demo mode at https://2captcha.com/demo/ — you can test all CAPTCHA
|
|
34
|
+
types without spending credits. The demo page at https://2captcha.com/demo/recaptcha-v2
|
|
35
|
+
is useful for verifying your integration works.
|
|
36
|
+
|
|
37
|
+
### Checking balance
|
|
38
|
+
```bash
|
|
39
|
+
curl "https://2captcha.com/res.php?key=YOUR_KEY&action=getbalance"
|
|
40
|
+
# → 4.25 (balance in USD)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Average solve times
|
|
44
|
+
- reCAPTCHA v2: 20–35s
|
|
45
|
+
- reCAPTCHA v3: 30–60s
|
|
46
|
+
- hCaptcha: 20–40s
|
|
47
|
+
- Image: 5–15s
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## CapMonster Cloud
|
|
52
|
+
|
|
53
|
+
**Website:** https://capmonster.cloud
|
|
54
|
+
**`--service` flag:** `capmonster`
|
|
55
|
+
|
|
56
|
+
AI-powered service (no human workers). Faster and cheaper than 2captcha for most types.
|
|
57
|
+
Uses machine learning models that have been trained on CAPTCHA datasets.
|
|
58
|
+
|
|
59
|
+
### Pricing (approximate)
|
|
60
|
+
| Type | Per 1,000 |
|
|
61
|
+
|------|-----------|
|
|
62
|
+
| reCAPTCHA v2 | $0.60 |
|
|
63
|
+
| reCAPTCHA v3 | $1.50 |
|
|
64
|
+
| hCaptcha | $0.80 |
|
|
65
|
+
| Turnstile | $0.60 |
|
|
66
|
+
| Image/text | $0.30 |
|
|
67
|
+
|
|
68
|
+
### Getting started
|
|
69
|
+
1. Sign up at https://capmonster.cloud
|
|
70
|
+
2. Add funds (minimum $2)
|
|
71
|
+
3. Copy API key from dashboard
|
|
72
|
+
|
|
73
|
+
### API compatibility note
|
|
74
|
+
CapMonster uses the same `/in.php` and `/res.php` endpoint format as 2captcha.
|
|
75
|
+
The `solve_captcha.py` script handles the URL switching automatically via `--service capmonster`.
|
|
76
|
+
|
|
77
|
+
### Average solve times
|
|
78
|
+
- reCAPTCHA v2: 10–25s
|
|
79
|
+
- hCaptcha: 15–30s
|
|
80
|
+
- Image: 2–8s
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Anti-Captcha
|
|
85
|
+
|
|
86
|
+
**Website:** https://anti-captcha.com
|
|
87
|
+
**`--service` flag:** `anticaptcha`
|
|
88
|
+
|
|
89
|
+
One of the original CAPTCHA solving services. Good uptime and reliable for bulk tasks.
|
|
90
|
+
|
|
91
|
+
### Pricing (approximate)
|
|
92
|
+
| Type | Per 1,000 |
|
|
93
|
+
|------|-----------|
|
|
94
|
+
| reCAPTCHA v2 | $0.70 |
|
|
95
|
+
| reCAPTCHA v3 | $1.80 |
|
|
96
|
+
| hCaptcha | $1.00 |
|
|
97
|
+
| Image/text | $0.70 |
|
|
98
|
+
|
|
99
|
+
### Getting started
|
|
100
|
+
1. Sign up at https://anti-captcha.com
|
|
101
|
+
2. Add funds
|
|
102
|
+
3. Copy API key from Account Settings
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Choosing a Service
|
|
107
|
+
|
|
108
|
+
| If you need... | Use |
|
|
109
|
+
|---------------|-----|
|
|
110
|
+
| Best documentation & community | 2captcha |
|
|
111
|
+
| Lowest cost + fastest for reCAPTCHA v2 | CapMonster |
|
|
112
|
+
| Bulk image CAPTCHA at low cost | CapMonster or Anti-Captcha |
|
|
113
|
+
| reCAPTCHA v3 reliability | 2captcha (more worker capacity) |
|
|
114
|
+
| Free credits to test | 2captcha ($1 signup bonus occasionally) |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Using Multiple Services (Fallback)
|
|
119
|
+
|
|
120
|
+
If one service is down or out of credits, fall back to another:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import json, subprocess, os
|
|
124
|
+
|
|
125
|
+
def solve_with_fallback(captcha_type, sitekey, pageurl):
|
|
126
|
+
services = ["2captcha", "capmonster", "anticaptcha"]
|
|
127
|
+
for service in services:
|
|
128
|
+
key_env = f"CAPTCHA_API_KEY_{service.upper().replace('-', '_')}"
|
|
129
|
+
api_key = os.environ.get(key_env) or os.environ.get("CAPTCHA_API_KEY")
|
|
130
|
+
if not api_key:
|
|
131
|
+
continue
|
|
132
|
+
result = subprocess.run(
|
|
133
|
+
["python", "scripts/solve_captcha.py",
|
|
134
|
+
"--type", captcha_type, "--sitekey", sitekey, "--pageurl", pageurl,
|
|
135
|
+
"--service", service, "--api-key", api_key],
|
|
136
|
+
capture_output=True, text=True
|
|
137
|
+
)
|
|
138
|
+
data = json.loads(result.stdout)
|
|
139
|
+
if data.get("success"):
|
|
140
|
+
return data["token"]
|
|
141
|
+
print(f"[captcha] {service} failed: {data.get('error')} — trying next service")
|
|
142
|
+
raise RuntimeError("All CAPTCHA solving services failed")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Checking Your Balance
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# 2captcha
|
|
151
|
+
curl "https://2captcha.com/res.php?key=YOUR_KEY&action=getbalance"
|
|
152
|
+
|
|
153
|
+
# CapMonster
|
|
154
|
+
curl "https://api.capmonster.cloud/res.php?key=YOUR_KEY&action=getbalance"
|
|
155
|
+
|
|
156
|
+
# Anti-Captcha
|
|
157
|
+
curl "https://api.anti-captcha.com/res.php?key=YOUR_KEY&action=getbalance"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Error Codes
|
|
163
|
+
|
|
164
|
+
| Code | Meaning | Fix |
|
|
165
|
+
|------|---------|-----|
|
|
166
|
+
| `ERROR_ZERO_BALANCE` | Account has no credits | Add funds at service dashboard |
|
|
167
|
+
| `ERROR_NO_SLOT_AVAILABLE` | Service overloaded | Retry in a few minutes |
|
|
168
|
+
| `ERROR_WRONG_CAPTCHA_ID` | Invalid task ID | Bug in code — report |
|
|
169
|
+
| `ERROR_CAPTCHA_UNSOLVABLE` | Solver gave up | Retry; if recurring, try another service |
|
|
170
|
+
| `ERROR_WRONG_USER_KEY` | Bad API key | Check key, no trailing spaces |
|
|
171
|
+
| `CAPCHA_NOT_READY` | Still processing | Normal — keep polling |
|
|
172
|
+
| `ERROR_IP_BANNED` | Your IP is blocked | Contact service support |
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Avoiding Bot Detection After Passing CAPTCHA
|
|
2
|
+
|
|
3
|
+
Passing the CAPTCHA itself is often not enough — the site may still flag and block your
|
|
4
|
+
session based on browser fingerprinting or behavioral signals. This document covers the
|
|
5
|
+
most common detection vectors and how to mitigate them.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Why Sites Still Block After CAPTCHA
|
|
10
|
+
|
|
11
|
+
Modern anti-bot systems (Cloudflare, PerimeterX, DataDome, Kasada) score requests on
|
|
12
|
+
multiple signals **beyond** the CAPTCHA solution:
|
|
13
|
+
|
|
14
|
+
1. **Browser fingerprint** — headless Chrome has telltale JS properties
|
|
15
|
+
2. **Mouse / interaction patterns** — no mouse movement, instant form fills
|
|
16
|
+
3. **Request headers** — missing or inconsistent Accept-Language, Referer, etc.
|
|
17
|
+
4. **IP reputation** — data center IPs are pre-flagged
|
|
18
|
+
5. **Cookie / session age** — fresh sessions with no history look suspicious
|
|
19
|
+
6. **Timing** — solving a CAPTCHA in 15s (via API) and submitting instantly
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Playwright Stealth Mode
|
|
24
|
+
|
|
25
|
+
The single most impactful change: install and use `playwright-stealth` to patch the
|
|
26
|
+
headless detection markers.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install playwright-stealth
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from playwright.sync_api import sync_playwright
|
|
34
|
+
from playwright_stealth import stealth_sync
|
|
35
|
+
|
|
36
|
+
with sync_playwright() as p:
|
|
37
|
+
browser = p.chromium.launch(headless=True)
|
|
38
|
+
page = browser.new_page()
|
|
39
|
+
stealth_sync(page) # patches ~30 fingerprinting vectors
|
|
40
|
+
page.goto("https://example.com")
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### What stealth patches
|
|
44
|
+
- `navigator.webdriver` — set to `undefined` instead of `true`
|
|
45
|
+
- Chrome runtime properties — adds `chrome.runtime` and related objects
|
|
46
|
+
- Plugin / mimeType arrays — populated instead of empty
|
|
47
|
+
- `navigator.languages` — realistic value
|
|
48
|
+
- `window.outerWidth/Height` — matches viewport
|
|
49
|
+
- Canvas and WebGL fingerprints — some randomization
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Use a Real Browser Profile (Non-Headless)
|
|
54
|
+
|
|
55
|
+
If stealth mode isn't enough, launch with a real user data directory so the browser
|
|
56
|
+
has history, cookies, and extensions like a real user:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import os
|
|
60
|
+
from playwright.sync_api import sync_playwright
|
|
61
|
+
|
|
62
|
+
user_data = os.path.expanduser("~/.config/captcha-automation-profile")
|
|
63
|
+
|
|
64
|
+
with sync_playwright() as p:
|
|
65
|
+
browser = p.chromium.launch_persistent_context(
|
|
66
|
+
user_data_dir=user_data,
|
|
67
|
+
headless=False, # visible window bypasses some checks
|
|
68
|
+
args=["--disable-blink-features=AutomationControlled"],
|
|
69
|
+
)
|
|
70
|
+
page = browser.new_page()
|
|
71
|
+
page.goto("https://example.com")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The `--disable-blink-features=AutomationControlled` flag prevents the
|
|
75
|
+
`navigator.webdriver` property from being set to `true`.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Realistic Request Headers
|
|
80
|
+
|
|
81
|
+
Missing or wrong headers are a major signal. Set these before navigating:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
page.set_extra_http_headers({
|
|
85
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
86
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
87
|
+
"Sec-Fetch-Site": "none",
|
|
88
|
+
"Sec-Fetch-Mode": "navigate",
|
|
89
|
+
"Sec-Fetch-User": "?1",
|
|
90
|
+
"Sec-Fetch-Dest": "document",
|
|
91
|
+
"Upgrade-Insecure-Requests": "1",
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Also set a realistic User-Agent:
|
|
96
|
+
```python
|
|
97
|
+
browser = p.chromium.launch()
|
|
98
|
+
context = browser.new_context(
|
|
99
|
+
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
100
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
101
|
+
"Chrome/124.0.0.0 Safari/537.36",
|
|
102
|
+
viewport={"width": 1280, "height": 720},
|
|
103
|
+
locale="en-US",
|
|
104
|
+
timezone_id="America/New_York",
|
|
105
|
+
)
|
|
106
|
+
page = context.new_page()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Add Human-Like Delays
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import time, random
|
|
115
|
+
|
|
116
|
+
def human_delay(min_ms=800, max_ms=2500):
|
|
117
|
+
"""Sleep for a random human-like duration."""
|
|
118
|
+
time.sleep(random.uniform(min_ms, max_ms) / 1000)
|
|
119
|
+
|
|
120
|
+
# Between navigation and form fill
|
|
121
|
+
page.goto(url)
|
|
122
|
+
human_delay(1000, 3000)
|
|
123
|
+
|
|
124
|
+
# Between filling each field
|
|
125
|
+
page.fill('#email', 'user@example.com')
|
|
126
|
+
human_delay(300, 900)
|
|
127
|
+
page.fill('#password', 'secret')
|
|
128
|
+
human_delay(500, 1500)
|
|
129
|
+
|
|
130
|
+
# After CAPTCHA solve, before form submit
|
|
131
|
+
inject_captcha_token(page, token)
|
|
132
|
+
human_delay(800, 2000)
|
|
133
|
+
page.click('button[type=submit]')
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Mouse Movement Simulation
|
|
139
|
+
|
|
140
|
+
Sites sometimes require mouse activity before accepting a form submit:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
import asyncio, random, math
|
|
144
|
+
|
|
145
|
+
async def move_mouse_naturally(page, x_end, y_end, steps=20):
|
|
146
|
+
"""Simulate curved mouse movement to a target."""
|
|
147
|
+
box = await page.evaluate("({x: window.innerWidth/2, y: window.innerHeight/2})")
|
|
148
|
+
x_start, y_start = box["x"], box["y"]
|
|
149
|
+
|
|
150
|
+
for i in range(steps):
|
|
151
|
+
t = i / steps
|
|
152
|
+
# Quadratic bezier curve
|
|
153
|
+
cx = x_start + random.randint(-50, 50)
|
|
154
|
+
cy = y_start + random.randint(-50, 50)
|
|
155
|
+
x = (1-t)**2 * x_start + 2*(1-t)*t * cx + t**2 * x_end
|
|
156
|
+
y = (1-t)**2 * y_start + 2*(1-t)*t * cy + t**2 * y_end
|
|
157
|
+
await page.mouse.move(x, y)
|
|
158
|
+
await asyncio.sleep(random.uniform(0.01, 0.04))
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Residential Proxies
|
|
164
|
+
|
|
165
|
+
Data center IPs (AWS, GCP, Azure, VPS) are pre-flagged by most anti-bot systems.
|
|
166
|
+
Use residential proxies for better success rates:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
browser = p.chromium.launch(
|
|
170
|
+
proxy={
|
|
171
|
+
"server": "http://proxy.provider.com:8080",
|
|
172
|
+
"username": "user",
|
|
173
|
+
"password": "pass",
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Residential proxy providers: Bright Data, Oxylabs, Smartproxy, IPRoyal.
|
|
179
|
+
Expect to pay $5–$15/GB for residential IPs vs $0.50/GB for data center IPs.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Viewport and Screen Size
|
|
184
|
+
|
|
185
|
+
Headless browsers default to unusual viewport sizes. Match common desktop dimensions:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
context = browser.new_context(
|
|
189
|
+
viewport={"width": 1920, "height": 1080},
|
|
190
|
+
device_scale_factor=1,
|
|
191
|
+
is_mobile=False,
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Session Warmup
|
|
198
|
+
|
|
199
|
+
Fresh sessions with no cookies or history are suspicious. Before hitting the target page:
|
|
200
|
+
|
|
201
|
+
1. Navigate to the homepage first
|
|
202
|
+
2. Wait a few seconds
|
|
203
|
+
3. Click a link or scroll
|
|
204
|
+
4. Then go to the page you want to interact with
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
page.goto("https://example.com")
|
|
208
|
+
time.sleep(random.uniform(2, 5))
|
|
209
|
+
# scroll down a bit
|
|
210
|
+
page.evaluate("window.scrollBy(0, Math.floor(Math.random() * 300 + 100))")
|
|
211
|
+
time.sleep(random.uniform(1, 3))
|
|
212
|
+
page.goto("https://example.com/login")
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Detection Test
|
|
218
|
+
|
|
219
|
+
Before automating a target site, test your browser fingerprint at:
|
|
220
|
+
- https://bot.sannysoft.com — headless Chrome detection tests
|
|
221
|
+
- https://pixelscan.net — comprehensive fingerprint check
|
|
222
|
+
- https://abrahamjuliot.github.io/creepjs/ — advanced entropy fingerprinting
|
|
223
|
+
|
|
224
|
+
If these sites detect you as a bot, apply more stealth measures before attempting
|
|
225
|
+
the actual target.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Summary Checklist
|
|
230
|
+
|
|
231
|
+
- [ ] Use `playwright-stealth` (or `--disable-blink-features=AutomationControlled`)
|
|
232
|
+
- [ ] Set realistic User-Agent, viewport, locale, timezone
|
|
233
|
+
- [ ] Set Accept-Language and other headers
|
|
234
|
+
- [ ] Add random delays between actions (800ms–3s)
|
|
235
|
+
- [ ] Move mouse before clicking submit
|
|
236
|
+
- [ ] Use residential proxy if data center IP is blocked
|
|
237
|
+
- [ ] Warm up session before hitting the CAPTCHA page
|
|
238
|
+
- [ ] Inject CAPTCHA token and wait 1–2s before submitting
|