Skip to content

Commit 780cee1

Browse files
committed
added cloudflare turnstile
1 parent f69a410 commit 780cee1

File tree

4 files changed

+166
-5
lines changed

4 files changed

+166
-5
lines changed

acmethods.go

+80-5
Original file line numberDiff line numberDiff line change
@@ -277,22 +277,29 @@ func antiCaptchaMethods(solver *Solver, preferredDomain string) *solveMethods {
277277
"isInvisible": o.Invisible,
278278
}
279279

280-
applyProxy(taskData, o.Proxy, "HCaptchaTask")
280+
baseTask := "HCaptchaTask"
281+
if solver.service == CapSolver {
282+
baseTask = "HCaptchaTurboTask"
283+
}
284+
285+
applyProxy(taskData, o.Proxy, baseTask)
281286

282287
if o.UserAgent != "" {
283288
taskData["userAgent"] = o.UserAgent
284289
}
285290

286291
if o.EnterprisePayload != nil {
292+
ep := o.EnterprisePayload
293+
287294
// some apis are slightly different
288-
if o.EnterprisePayload.RQData != "" {
295+
if ep.RQData != "" {
289296
taskData["data"] = o.EnterprisePayload.RQData
290297
}
291298

292-
ep := o.EnterprisePayload
299+
payload := map[string]interface{}{}
293300

294-
payload := map[string]interface{}{
295-
"sentry": ep.Sentry,
301+
if ep.Sentry {
302+
payload["sentry"] = ep.Sentry
296303
}
297304

298305
if ep.RQData != "" {
@@ -422,6 +429,74 @@ func antiCaptchaMethods(solver *Solver, preferredDomain string) *solveMethods {
422429
UserAgent: userAgent,
423430
}, nil
424431
}
432+
433+
methods.Cloudflare = func(o CloudflareOptions) (*Solution, error) {
434+
if o.Proxy == nil {
435+
return nil, errors.New("proxy is required")
436+
}
437+
438+
if o.Metadata == nil {
439+
o.Metadata = map[string]string{}
440+
}
441+
442+
// only set metadata type if one wasn't provided
443+
if _, ok := o.Metadata["type"]; !ok {
444+
switch o.Type {
445+
case CloudflareTypeChallenge:
446+
o.Metadata["type"] = "challenge"
447+
case CloudflareTypeTurnstile:
448+
o.Metadata["type"] = "turnstile"
449+
default:
450+
o.Metadata["type"] = ""
451+
}
452+
}
453+
454+
if _, ok := o.Metadata["action"]; !ok && o.Action != "" {
455+
o.Metadata["action"] = o.Action
456+
}
457+
458+
if _, ok := o.Metadata["cdata"]; !ok && o.CData != "" {
459+
o.Metadata["cdata"] = o.CData
460+
}
461+
462+
taskData := map[string]interface{}{
463+
"websiteURL": o.PageURL,
464+
"metadata": o.Metadata,
465+
}
466+
467+
if o.SiteKey != "" {
468+
taskData["websiteKey"] = o.SiteKey
469+
}
470+
471+
applyProxy(taskData, o.Proxy, "AntiCloudflareTask")
472+
473+
return createResponse(taskData)
474+
}
475+
} else {
476+
methods.Cloudflare = func(o CloudflareOptions) (*Solution, error) {
477+
if o.Type == CloudflareTypeChallenge {
478+
return nil, errors.New("cloudflare challenge type is not supported by this solver")
479+
}
480+
481+
taskData := map[string]interface{}{
482+
"websiteURL": o.PageURL,
483+
"websiteKey": o.SiteKey,
484+
}
485+
486+
if o.Action != "" {
487+
taskData["action"] = o.Action
488+
}
489+
490+
if o.Metadata != nil {
491+
for k, v := range o.Metadata {
492+
taskData[k] = v
493+
}
494+
}
495+
496+
applyProxy(taskData, o.Proxy, "TurnstileTask")
497+
498+
return createResponse(taskData)
499+
}
425500
}
426501

427502
return methods

captchas.go

+37
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type solveMethods struct {
1111
HCaptcha func(HCaptchaOptions) (*Solution, error)
1212
FunCaptcha func(FunCaptchaOptions) (*Solution, error)
1313
Kasada func(KasadaOptions) (*KasadaSolution, error)
14+
Cloudflare func(CloudflareOptions) (*Solution, error)
1415
}
1516

1617
// GetBalance returns the balance of the account
@@ -50,6 +51,13 @@ func (s *Solver) FunCaptcha(o FunCaptchaOptions) (*Solution, error) {
5051
return s.methods.FunCaptcha(o)
5152
}
5253

54+
func (s *Solver) Cloudflare(o CloudflareOptions) (*Solution, error) {
55+
if s.methods.Cloudflare == nil {
56+
return nil, errors.New("service does not support cloudflare")
57+
}
58+
return s.methods.Cloudflare(o)
59+
}
60+
5361
// Kasada is only supported with capsolver.com
5462
func (s *Solver) Kasada(o KasadaOptions) (*KasadaSolution, error) {
5563
if s.methods.Kasada == nil {
@@ -149,6 +157,28 @@ type RecaptchaV2Options struct {
149157
APIDomain string
150158
}
151159

160+
// CloudflareOptions cloudflare challenges only work with capsolver.com, turnstile works with other solvers
161+
type CloudflareOptions struct {
162+
PageURL string
163+
Proxy *Proxy
164+
165+
// SiteKey is only used for CloudflareTypeTurnstile
166+
SiteKey string
167+
168+
// Type must only be CloudflareTypeTurnstile if the solver is not capsolver.com
169+
Type CloudflareType
170+
171+
// Metadata will only be used on capsolver.com, for any other solver it will just append the fields
172+
Metadata map[string]string
173+
174+
// Action and CData are only used for turnstile. CData is only used for 2captcha and capsolver.com
175+
Action string
176+
CData string
177+
178+
// HTML is only needed for cloudflare challenges
179+
HTML string
180+
}
181+
152182
type Solution struct {
153183
Text string
154184

@@ -183,3 +213,10 @@ type KasadaSolution struct {
183213
// UserAgent is the user agent used to solve the captcha
184214
UserAgent string
185215
}
216+
217+
type CloudflareType int
218+
219+
const (
220+
CloudflareTypeTurnstile CloudflareType = iota
221+
CloudflareTypeChallenge
222+
)

examples/cloudflare_turnstile.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/median/captchago"
6+
)
7+
8+
func main() {
9+
solver, err := captchago.New(captchago.AntiCaptcha, "YOUR_API_KEY")
10+
if err != nil {
11+
panic(err)
12+
}
13+
14+
sol, err := solver.Cloudflare(captchago.CloudflareOptions{
15+
PageURL: "https://demo.turnstile.workers.dev/",
16+
SiteKey: "1x00000000000000000000AA",
17+
Type: captchago.CloudflareTypeTurnstile,
18+
})
19+
20+
if err != nil {
21+
panic(err)
22+
}
23+
24+
fmt.Println(sol.Text)
25+
fmt.Println(fmt.Sprintf("Solved in %v ms", sol.Speed))
26+
}

tcmethods.go

+23
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,29 @@ func twoCaptchaMethods(solver *Solver, preferredDomain string) *solveMethods {
227227
payload["enterprise"] = 1
228228
}
229229

230+
return createResponse(payload)
231+
},
232+
Cloudflare: func(o CloudflareOptions) (*Solution, error) {
233+
payload := map[string]interface{}{
234+
"method": "turnstile",
235+
"sitekey": o.SiteKey,
236+
"pageurl": o.PageURL,
237+
}
238+
239+
if o.Action != "" {
240+
payload["action"] = o.Action
241+
}
242+
243+
if o.CData != "" {
244+
payload["data"] = o.CData
245+
}
246+
247+
if o.Metadata != nil {
248+
for k, v := range o.Metadata {
249+
payload[k] = v
250+
}
251+
}
252+
230253
return createResponse(payload)
231254
},
232255
}

0 commit comments

Comments
 (0)