Le typage fort de Go, la concurrence intégrée et le déploiement mono-binaire en font un choix solide pour les systèmes d'automatisation. Ce guide crée une bibliothèque client CaptchaAI qui suit les conventions Go : prise en charge de context.Context, injection http.Client personnalisée et valeurs d'erreur saisies.
Structure du paquet
captchaai/
├── client.go # Main client and solve logic
├── errors.go # Error types
├── types.go # Request/response structs
└── client_test.go # Tests
Types d'erreurs
// errors.go
package captchaai
import "fmt"
// APIError represents a CaptchaAI API error response.
type APIError struct {
Code string
Message string
}
func (e *APIError) Error() string {
return fmt.Sprintf("captchaai: %s (%s)", e.Message, e.Code)
}
// IsFatal returns true if this error should not be retried.
func (e *APIError) IsFatal() bool {
switch e.Code {
case "ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST",
"ERROR_ZERO_BALANCE", "ERROR_IP_NOT_ALLOWED":
return true
}
return false
}
// TimeoutError indicates the solve exceeded the configured timeout.
type TimeoutError struct {
TaskID string
}
func (e *TimeoutError) Error() string {
return fmt.Sprintf("captchaai: task %s timed out", e.TaskID)
}
Espèces
// types.go
package captchaai
import "time"
// ClientOption configures the CaptchaAI client.
type ClientOption func(*Client)
// WithPollInterval sets the polling interval between result checks.
func WithPollInterval(d time.Duration) ClientOption {
return func(c *Client) { c.pollInterval = d }
}
// WithTimeout sets the maximum time to wait for a solution.
func WithTimeout(d time.Duration) ClientOption {
return func(c *Client) { c.timeout = d }
}
// RecaptchaV2Params holds parameters for reCAPTCHA v2 solving.
type RecaptchaV2Params struct {
SiteKey string
PageURL string
Invisible bool
Cookies string
}
// RecaptchaV3Params holds parameters for reCAPTCHA v3 solving.
type RecaptchaV3Params struct {
SiteKey string
PageURL string
Action string
}
// TurnstileParams holds parameters for Cloudflare Turnstile solving.
type TurnstileParams struct {
SiteKey string
PageURL string
Action string
CData string
}
// ImageParams holds parameters for image/OCR CAPTCHA solving.
type ImageParams struct {
Base64Image string
CaseSensitive bool
MinLength int
MaxLength int
}
type submitResponse struct {
Status int `json:"status"`
Request string `json:"request"`
}
type pollResponse struct {
Status int `json:"status"`
Request string `json:"request"`
}
Implémentation client
// client.go
package captchaai
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
const (
submitURL = "https://ocr.captchaai.com/in.php"
resultURL = "https://ocr.captchaai.com/res.php"
defaultPollInterval = 5 * time.Second
defaultTimeout = 180 * time.Second
)
// Client interacts with the CaptchaAI API.
type Client struct {
apiKey string
httpClient *http.Client
pollInterval time.Duration
timeout time.Duration
}
// New creates a CaptchaAI client with the given API key and options.
func New(apiKey string, opts ...ClientOption) *Client {
c := &Client{
apiKey: apiKey,
httpClient: http.DefaultClient,
pollInterval: defaultPollInterval,
timeout: defaultTimeout,
}
for _, opt := range opts {
opt(c)
}
return c
}
// WithHTTPClient sets a custom HTTP client (e.g., for proxy support).
func WithHTTPClient(hc *http.Client) ClientOption {
return func(c *Client) { c.httpClient = hc }
}
func (c *Client) submit(ctx context.Context, params url.Values) (string, error) {
params.Set("key", c.apiKey)
params.Set("json", "1")
req, err := http.NewRequestWithContext(ctx, http.MethodPost, submitURL, nil)
if err != nil {
return "", fmt.Errorf("captchaai: build request: %w", err)
}
req.URL.RawQuery = params.Encode()
resp, err := c.httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("captchaai: submit: %w", err)
}
defer resp.Body.Close()
var result submitResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", fmt.Errorf("captchaai: decode submit response: %w", err)
}
if result.Status != 1 {
return "", &APIError{Code: result.Request, Message: "submit failed"}
}
return result.Request, nil
}
func (c *Client) poll(ctx context.Context, taskID string) (string, error) {
deadline := time.After(c.timeout)
for {
select {
case <-ctx.Done():
return "", ctx.Err()
case <-deadline:
return "", &TimeoutError{TaskID: taskID}
case <-time.After(c.pollInterval):
}
params := url.Values{
"key": {c.apiKey},
"action": {"get"},
"id": {taskID},
"json": {"1"},
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, resultURL+"?"+params.Encode(), nil)
if err != nil {
return "", fmt.Errorf("captchaai: build poll request: %w", err)
}
resp, err := c.httpClient.Do(req)
if err != nil {
continue // Retry on network error
}
var result pollResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
resp.Body.Close()
continue
}
resp.Body.Close()
if result.Request == "CAPCHA_NOT_READY" {
continue
}
if result.Status == 1 {
return result.Request, nil
}
return "", &APIError{Code: result.Request, Message: "solve failed"}
}
}
// SolveRecaptchaV2 solves a reCAPTCHA v2 challenge.
func (c *Client) SolveRecaptchaV2(ctx context.Context, p RecaptchaV2Params) (string, error) {
params := url.Values{
"method": {"userrecaptcha"},
"googlekey": {p.SiteKey},
"pageurl": {p.PageURL},
}
if p.Invisible {
params.Set("invisible", "1")
}
if p.Cookies != "" {
params.Set("cookies", p.Cookies)
}
taskID, err := c.submit(ctx, params)
if err != nil {
return "", err
}
return c.poll(ctx, taskID)
}
// SolveRecaptchaV3 solves a reCAPTCHA v3 challenge.
func (c *Client) SolveRecaptchaV3(ctx context.Context, p RecaptchaV3Params) (string, error) {
params := url.Values{
"method": {"userrecaptcha"},
"version": {"v3"},
"googlekey": {p.SiteKey},
"pageurl": {p.PageURL},
}
if p.Action != "" {
params.Set("action", p.Action)
}
taskID, err := c.submit(ctx, params)
if err != nil {
return "", err
}
return c.poll(ctx, taskID)
}
// SolveTurnstile solves a Cloudflare Turnstile challenge.
func (c *Client) SolveTurnstile(ctx context.Context, p TurnstileParams) (string, error) {
params := url.Values{
"method": {"turnstile"},
"sitekey": {p.SiteKey},
"pageurl": {p.PageURL},
}
if p.Action != "" {
params.Set("action", p.Action)
}
if p.CData != "" {
params.Set("data", p.CData)
}
taskID, err := c.submit(ctx, params)
if err != nil {
return "", err
}
return c.poll(ctx, taskID)
}
// SolveImage solves an image/text CAPTCHA from base64.
func (c *Client) SolveImage(ctx context.Context, p ImageParams) (string, error) {
params := url.Values{
"method": {"base64"},
"body": {p.Base64Image},
}
if p.CaseSensitive {
params.Set("regsense", "1")
}
if p.MinLength > 0 {
params.Set("min_len", strconv.Itoa(p.MinLength))
}
if p.MaxLength > 0 {
params.Set("max_len", strconv.Itoa(p.MaxLength))
}
taskID, err := c.submit(ctx, params)
if err != nil {
return "", err
}
return c.poll(ctx, taskID)
}
// GetBalance returns the current account balance.
func (c *Client) GetBalance(ctx context.Context) (float64, error) {
params := url.Values{
"key": {c.apiKey},
"action": {"getbalance"},
"json": {"1"},
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, resultURL+"?"+params.Encode(), nil)
if err != nil {
return 0, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
var result pollResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return 0, err
}
return strconv.ParseFloat(result.Request, 64)
}
Utilisation
package main
import (
"context"
"fmt"
"log"
"time"
"your-module/captchaai"
)
func main() {
client := captchaai.New("YOUR_API_KEY",
captchaai.WithTimeout(120*time.Second),
captchaai.WithPollInterval(5*time.Second),
)
ctx := context.Background()
// Check balance
balance, err := client.GetBalance(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Balance: $%.2f\n", balance)
// Solve reCAPTCHA v2
token, err := client.SolveRecaptchaV2(ctx, captchaai.RecaptchaV2Params{
SiteKey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
PageURL: "https://example.com/login",
})
if err != nil {
var apiErr *captchaai.APIError
if errors.As(err, &apiErr) && apiErr.IsFatal() {
log.Fatalf("Fatal API error: %s", apiErr.Code)
}
log.Fatal(err)
}
fmt.Printf("Token: %s...\n", token[:40])
// Solve with context timeout
solveCtx, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
turnstileToken, err := client.SolveTurnstile(solveCtx, captchaai.TurnstileParams{
SiteKey: "0x4AAAAAAADnPIDROrmt1Wwj",
PageURL: "https://example.com/checkout",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Turnstile: %s...\n", turnstileToken[:40])
}
Dépannage
| Problème | Parce que | Corriger |
|---|---|---|
context deadline exceeded |
La résolution a pris plus de temps que le délai d'expiration du contexte | Utilisez un délai d'expiration de contexte plus long ou augmentez le client WithTimeout |
captchaai: submit failed (ERROR_ZERO_BALANCE) |
Pas de fonds | Rechargez sur le tableau de bord CaptchaAI |
| Les sondages ne sont jamais terminés | Problèmes de réseau ou URL d'API incorrecte | Vérifiez la connectivité ; vérifier les constantes d'URL |
Erreur du compilateur sur errors.As |
Importation manquante | Ajouter "errors" aux importations |
| Client HTTP personnalisé non utilisé | Option WithHTTPClient oubliée |
Option de passe dans New() : captchaai.New(key, captchaai.WithHTTPClient(myClient)) |
FAQ
Pourquoi utiliser context.Context au lieu d'un simple timeout ?
Le contexte s'intègre au modèle d'annulation standard de Go. Si le gestionnaire HTTP ou la goroutine parent est annulé, la résolution CAPTCHA s'arrête immédiatement : aucune boucle d'interrogation orpheline consommant des crédits API.
Comment puis-je l'utiliser avec un proxy ?
Injectez un http.Client personnalisé avec un transport proxy. Cela achemine tout le trafic du SDK via votre proxy sans modifier la bibliothèque.
Dois-je utiliser go install ou le fournisseur ?
Pour les projets privés, utilisez go mod vendor. Pour les bibliothèques réutilisables, publiez en tant que module Go avec versionnage sémantique et laissez les consommateurs importer avec go get.
Articles connexes
- Captchaai Ip liste blanche sécurité des clés API
- Rotation des clés API Captchaai
- Concurrents de cartographie des points de terminaison de l'API Captchaai
Prochaines étapes
Créez votre client Go CAPTCHA -récupérez votre clé API CaptchaAIet commencez par le package ci-dessus.
Guides associés :