From d4d128867bacf214263e629079adcaa47555ded9 Mon Sep 17 00:00:00 2001 From: Matthew Lugo Date: Wed, 6 Oct 2021 22:28:31 -0400 Subject: [PATCH] 1.2.0 - Added Go implementation --- .DS_Store | Bin 0 -> 6148 bytes README.md | 3 + captchatools-go/README.md | 152 +++++++++++++++++++++++++++++++++ captchatools-go/anticaptcha.go | 128 +++++++++++++++++++++++++++ captchatools-go/capmonster.go | 128 +++++++++++++++++++++++++++ captchatools-go/go.mod | 3 + captchatools-go/harvester.go | 45 ++++++++++ captchatools-go/twocaptcha.go | 121 ++++++++++++++++++++++++++ captchatools-go/types.go | 126 +++++++++++++++++++++++++++ 9 files changed, 706 insertions(+) create mode 100644 .DS_Store create mode 100644 captchatools-go/README.md create mode 100644 captchatools-go/anticaptcha.go create mode 100644 captchatools-go/capmonster.go create mode 100644 captchatools-go/go.mod create mode 100644 captchatools-go/harvester.go create mode 100644 captchatools-go/twocaptcha.go create mode 100644 captchatools-go/types.go diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7d58cda5cbed6a02eeca88792a2a488a8ad14cd0 GIT binary patch literal 6148 zcmeHK!AiqG6r8O^QxxpcV}8JbenG6sp`acNcoc0a^$;7eR`8Y&5`R(W?LJDA+JhIN z@+Q2U&F0O9oejHL0B*RLoB(|Q5kt7sWAOvzxx^Ye+OkV_zK;TrxW_9hUWRROK<4fM z1!wJ<^R_yFCFY~DD#m3sW{!Lyw~W&-57vOgO*$N7imIu0@}^&?-}0APSNKHv;Y7A literal 0 HcmV?d00001 diff --git a/README.md b/README.md index ae0d395..bba7459 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Captcha Tools Python module to help solve captchas with Capmonster, 2Captcha and Anticaptcha API's! +#### Go(lang) +To see documentation for the Go implementation, [click here](https://github.com/Matthew17-21/Captcha-Tools/captchatools-go) + # Install ```python pip3 install captchatools diff --git a/captchatools-go/README.md b/captchatools-go/README.md new file mode 100644 index 0000000..d6ad548 --- /dev/null +++ b/captchatools-go/README.md @@ -0,0 +1,152 @@ +# Captcha Tools (Go) +Go package to help solve captchas with Capmonster, 2Captcha and Anticaptcha API's! + +# Install +```go +go get github.com/Matthew17-21/Captcha-Tools/captchatools-go +``` +##### To update +```go +go get -u github.com/Matthew17-21/Captcha-Tools/captchatools-go +``` + +# How to use +```go +package main + +import ( + "fmt" + + captchatools "github.com/Matthew17-21/Captcha-Tools/captchatools-go" +) + +func main() { + solver, err := captchatools.NewHarvester(captchatools.CapmonsterSite, &captchatools.Config{ + Api_key: "ENTER YOUR API KEY HERE", + Sitekey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", + CaptchaURL: "https://www.google.com/recaptcha/api2/demo", + CaptchaType: "V2", + }) + if err != nil { + panic(err) + } + fmt.Println(solver.GetToken()) +} + +``` +V3 Captcha Exmaple: +```go +func v3Example() { + solver, err := captchatools.NewHarvester(captchatools.AnticaptchaSite, &captchatools.Config{ + Api_key: "ENTER YOUR API KEY HERE", + Sitekey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", + CaptchaURL: "..........", + CaptchaType: "V3", + Action: "submit", + MinScore: 0.9, + }) + if err != nil { + panic(err) + } + fmt.Println(solver.GetToken()) +} + +``` +### captchatools.NewHarvester() Parameters: +| Parameter | Required | Type | Default | Description| +| :-------------: |:-------------:| :-----:| :-----:| :-----:| +| solving_site | true | int| -| The captcha solving site that will be used. Refer to [the site IDs](https://github.com/Matthew17-21/Captcha-Tools/captchatools-go#site-specific-support). Alternatively, you can use shortcuts such as `captchatools.AnticaptchaSite` | +| Config| true | captchatools.Config | - | Configurations for the captchas you are solving. | + + +### Config struct fields: +| Field | Required | Type | Default | Description| +| :-------------: |:-------------:| :-----:| :-----:| :-----:| +| Api_key | true | String| -| The API Key for the captcha solving site| +| Sitekey| true | String | - | Sitekey from the site where captcha is loaded| +| CaptchaURL | true| String | - | URL where the captcha is located| +| CaptchaType| true| String | - | Type of captcha you are solving. Either captcha `v2`, `v3` or `hcaptcha` (`hcap` works aswell)| +| Action | false | String | - | Action that is associated with the V3 captcha.
__This param is only required when solving V3 captchas__| +| IsInvisibleCaptcha| false | bool | - | If the captcha is invisible or not.
__This param is only required when solving invisible captchas__| +| MinScore | false | float32 | - | Minimum score for v3 captchas.
__This param is only required when solving V3 and it needs a higher / lower score__| + + + +# Supported Sites +- **[Capmonster](https://capmonster.cloud/)** +- **[2Captcha](https://www.2captcha.com/)** +- **[Anticaptcha](https://www.anti-captcha.com/)** + +##### Site-Specific Support: +| Site |Site ID| Captcha Types Supported | Task Types Supported| +| :-------------: |:-------------:|:-------------:| :-----:| +| Capmonster |1| Recaptcha V2,
Recaptcha V3,
HCaptcha | RecaptchaV2TaskProxyless,
RecaptchaV3TaskProxyless,
HCaptchaTaskProxyless | +| Anticaptcha |2| Recaptcha V2,
Recaptcha V3,
HCaptcha | RecaptchaV2TaskProxyless,
RecaptchaV3TaskProxyless,
HCaptchaTaskProxyless | +| 2Captcha |3| Recaptcha V2,
Recaptcha V3,
HCaptcha | - | + + +# Recommendations +1. For 2Captcha, don't run more than 60 tasks per API key. +2. Handle errors appropriately. + * If a `ErrNoBalance` is thrown, tasks should stop. Some sites will temporarily ban IP's if constant requests come in. + +# Errors +| Errors | Raised | +| :--------:| :-----:| +| `ErrNoBalance` | Balance is below 0 for captcha solving site| +| `ErrWrongAPIKey` | Incorrect API Key for captcha solving site| +| `ErrWrongSitekey` | Incorrect sitekey | +| `ErrIncorrectCapType` | Incorrectly chose a captcha type. When initializing a new harvester. Refer to [the captcha types](https://github.com/Matthew17-21/Captcha-Tools/captchatools-go#how-to-use) | +| `ErrNoHarvester` | When the user did not / incorrectly chose a captcha harvester. Refer to the [guide](https://github.com/Matthew17-21/Captcha-Tools/captchatools-go#how-to-use) | + +```go +package main + +import ( + "fmt" + + captchatools "github.com/Matthew17-21/Captcha-Tools/captchatools-go" +) + +func main() { + solver, err := captchatools.NewHarvester(captchatools.AnticaptchaSite, &captchatools.Config{ + Api_key: "ENTER YOUR API KEY HERE", + Sitekey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", + CaptchaURL: "https://www.google.com/recaptcha/api2/demo", + CaptchaType: "V2", + }) + if err != nil { + switch err { + case captchatools.ErrNoBalance: + fmt.Println("No balance.") + panic(err) + } + } + fmt.Println(solver.GetToken()) +} + +``` + + +# TO DO +1. [] Document code better +2. [] 2Captcha + * [] Clean up code + * [] Proxy support + * [] Cookie support + * [] User Agent Support + * [] Different type of captchas +3. [] Anticaptcha + * [] Clean up code + * [] Proxy support + * [] Cookie support + * [] User Agent Support + * [] Different type of captchas +4. [] Capmonster + * [] Clean up code + * [] Proxy support + * [] Cookie support + * [] User Agent Support + * [] Different type of captchas +5. [] Add DeathByCaptcha +6. [] Allow for refunds \ No newline at end of file diff --git a/captchatools-go/anticaptcha.go b/captchatools-go/anticaptcha.go new file mode 100644 index 0000000..d77cae5 --- /dev/null +++ b/captchatools-go/anticaptcha.go @@ -0,0 +1,128 @@ +package captchatoolsgo + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" +) + +// This file will contain the code to interact with anticaptcha.com API + +func (t *Anticaptcha) GetToken() (string, error) { + return t.getCaptchaAnswer() +} + +// Method to get Queue ID from the API. +func (t *Anticaptcha) getID() (int, error) { + // Get Payload + payload, _ := t.createPayload() + + // Make request to get answer + response := &capmonsterIDResponse{} + for { + resp, err := http.Post("https://api.anti-captcha.com/createTask", "application/json", bytes.NewBuffer([]byte(payload))) + if err != nil { + time.Sleep(3 * time.Second) + continue + } + body, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + json.Unmarshal(body, response) + + // Parse the response + if response.ErrorID == 0 { // Means there was no error + return response.TaskID, nil + } + switch response.ErrorCode { + case "ERROR_ZERO_BALANCE": + return 0, ErrNoBalance + case "ERROR_RECAPTCHA_INVALID_SITEKEY": + return 0, ErrWrongSitekey + case "ERROR_KEY_DOES_NOT_EXIST": + return 0, ErrWrongAPIKey + } + + } +} + +// This method gets the captcha token from the Capmonster API +func (t *Anticaptcha) getCaptchaAnswer() (string, error) { + // Get Queue ID + queueID, err := t.getID() + if err != nil { + return "", err + } + + // Get Captcha Answer + payload, _ := json.Marshal(capmonsterCapAnswerPayload{ + ClientKey: t.config.Api_key, + TaskID: queueID, + }) + response := &capmonsterTokenResponse{} + for { + resp, err := http.Post("https://api.anti-captcha.com/getTaskResult", "application/json", bytes.NewBuffer([]byte(payload))) + if err != nil { + time.Sleep(3 * time.Second) + continue + } + + // Parse Response + body, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + fmt.Println(string(body)) + json.Unmarshal(body, response) + if response.Status == "ready" { + return response.Solution.GRecaptchaResponse, nil + } else if response.ErrorID == 12 || response.ErrorID == 16 { // Captcha unsolvable || TaskID doesn't exist + t.GetToken() + } + time.Sleep(3 * time.Second) + } +} + +/* + createPayload returns the payloads required to interact with the API. + + Possible errors that can be returned: + 1) ErrIncorrectCapType +*/ +func (t *Anticaptcha) createPayload() (string, error) { + // Define the payload we are going to send to the API + payload := capmonsterIDPayload{ + ClientKey: t.config.Api_key, + Task: struct { + WebsiteURL string "json:\"websiteURL\"" + WebsiteKey string "json:\"websiteKey\"" + Type string "json:\"type\"" + IsInvisible bool "json:\"isInvisible,omitempty\"" + MinScore float32 "json:\"minScore,omitempty\"" + PageAction string "json:\"pageAction,omitempty\"" + }{ + WebsiteURL: t.config.CaptchaURL, + WebsiteKey: t.config.Sitekey, + Type: t.config.CaptchaType, + }, + } + + // Add any other keys to the payload + switch t.config.CaptchaType { + case "v2": + payload.Task.Type = "NoCaptchaTaskProxyless" + if t.config.IsInvisibleCaptcha { + payload.Task.IsInvisible = t.config.IsInvisibleCaptcha + } + case "v3": + payload.Task.Type = "RecaptchaV3TaskProxyless" + payload.Task.MinScore = t.config.MinScore + payload.Task.PageAction = t.config.Action + case "hcaptcha", "hcap": + payload.Task.Type = "HCaptchaTaskProxyless" + default: + return "", ErrIncorrectCapType + } + encoded, _ := json.Marshal(payload) + return string(encoded), nil +} diff --git a/captchatools-go/capmonster.go b/captchatools-go/capmonster.go new file mode 100644 index 0000000..66af24b --- /dev/null +++ b/captchatools-go/capmonster.go @@ -0,0 +1,128 @@ +package captchatoolsgo + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "time" +) + +/* + This file will contain the code to interact with capmonster.cloud API +*/ + +func (t *Capmonster) GetToken() (string, error) { + return t.getCaptchaAnswer() +} + +// Method to get Queue ID from the API. +func (t *Capmonster) getID() (int, error) { + // Get Payload + payload, _ := t.createPayload() + + // Make request to get answer + for { + resp, err := http.Post("https://api.capmonster.cloud/createTask", "application/json", bytes.NewBuffer([]byte(payload))) + if err != nil { + time.Sleep(3 * time.Second) + continue + } + body, _ := ioutil.ReadAll(resp.Body) + response := &capmonsterIDResponse{} + resp.Body.Close() + json.Unmarshal(body, response) + + // Parse the response + if response.ErrorID == 0 { // Means there was no error + return response.TaskID, nil + } + switch response.ErrorCode { + case "ERROR_ZERO_BALANCE": + return 0, ErrNoBalance + case "ERROR_RECAPTCHA_INVALID_SITEKEY": + return 0, ErrWrongSitekey + case "ERROR_KEY_DOES_NOT_EXIST": + return 0, ErrWrongAPIKey + } + + } +} + +// This method gets the captcha token from the Capmonster API +func (t *Capmonster) getCaptchaAnswer() (string, error) { + // Get Queue ID + queueID, err := t.getID() + if err != nil { + return "", err + } + + // Get Captcha Answer + payload, _ := json.Marshal(capmonsterCapAnswerPayload{ + ClientKey: t.config.Api_key, + TaskID: queueID, + }) + response := &capmonsterTokenResponse{} + for { + resp, err := http.Post("https://api.capmonster.cloud/getTaskResult", "application/json", bytes.NewBuffer([]byte(payload))) + if err != nil { + time.Sleep(3 * time.Second) + continue + } + + // Parse Response + body, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + json.Unmarshal(body, response) + if response.Status == "ready" { + return response.Solution.GRecaptchaResponse, nil + } else if response.ErrorID == 12 || response.ErrorID == 16 { // Captcha unsolvable || TaskID doesn't exist + t.GetToken() + } + time.Sleep(3 * time.Second) + } +} + +/* + createPayload returns the payloads required to interact with the API. + + Possible errors that can be returned: + 1) ErrIncorrectCapType +*/ +func (t *Capmonster) createPayload() (string, error) { + // Define the payload we are going to send to the API + payload := capmonsterIDPayload{ + ClientKey: t.config.Api_key, + Task: struct { + WebsiteURL string "json:\"websiteURL\"" + WebsiteKey string "json:\"websiteKey\"" + Type string "json:\"type\"" + IsInvisible bool "json:\"isInvisible,omitempty\"" + MinScore float32 "json:\"minScore,omitempty\"" + PageAction string "json:\"pageAction,omitempty\"" + }{ + WebsiteURL: t.config.CaptchaURL, + WebsiteKey: t.config.Sitekey, + Type: t.config.CaptchaType, + }, + } + + // Add any other keys to the payload + switch t.config.CaptchaType { + case "v2": + payload.Task.Type = "NoCaptchaTaskProxyless" + if t.config.IsInvisibleCaptcha { + payload.Task.IsInvisible = t.config.IsInvisibleCaptcha + } + case "v3": + payload.Task.Type = "RecaptchaV3TaskProxyless" + payload.Task.MinScore = t.config.MinScore + payload.Task.PageAction = t.config.Action + case "hcaptcha", "hcap": + payload.Task.Type = "HCaptchaTaskProxyless" + default: + return "", ErrIncorrectCapType + } + encoded, _ := json.Marshal(payload) + return string(encoded), nil +} diff --git a/captchatools-go/go.mod b/captchatools-go/go.mod new file mode 100644 index 0000000..e9b8caa --- /dev/null +++ b/captchatools-go/go.mod @@ -0,0 +1,3 @@ +module github.com/Matthew17-21/Captcha-Tools/captchatools-go + +go 1.16 diff --git a/captchatools-go/harvester.go b/captchatools-go/harvester.go new file mode 100644 index 0000000..bfe1c42 --- /dev/null +++ b/captchatools-go/harvester.go @@ -0,0 +1,45 @@ +package captchatoolsgo + +import "strings" + +/* + NewHarvester returns a captcha harvester based on the info given + by the caller. An error is returned if there is no proper + solving_site argument. + + To make the implementation similiar to the Python version, + this function was needed. + + For documentation on how to use this, checkout + https://github.com/Matthew17-21/Captcha-Tools +*/ +func NewHarvester(solving_site int, config *Config) (*Harvester, error) { + h := &Harvester{} + config.CaptchaType = strings.ToLower(config.CaptchaType) + config.CaptchaURL = strings.ToLower(config.CaptchaURL) + + // Check for any errors + switch strings.ToLower(config.CaptchaType) { + case "hcaptcha", "hcap", "v2", "v3": + default: + return nil, ErrIncorrectCapType + } + + // Get A Harvester + switch solving_site { + case AnticaptchaSite: + h.childHarvester = &Anticaptcha{config: config} + case CapmonsterSite: + h.childHarvester = &Capmonster{config: config} + case TwoCaptchaSite: + h.childHarvester = &Twocaptcha{config: config} + default: + return nil, ErrNoHarvester + } + return h, nil +} + +// GetToken returns a captcha token from the selected solving site +func (h *Harvester) GetToken() (string, error) { + return h.childHarvester.GetToken() +} diff --git a/captchatools-go/twocaptcha.go b/captchatools-go/twocaptcha.go new file mode 100644 index 0000000..fa46634 --- /dev/null +++ b/captchatools-go/twocaptcha.go @@ -0,0 +1,121 @@ +package captchatoolsgo + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" +) + +// This file will contain the code to interact with anticaptcha.com API + +func (t *Twocaptcha) GetToken() (string, error) { + return t.getCaptchaAnswer() +} + +// Method to get Queue ID from the API. +func (t *Twocaptcha) getID() (string, error) { + // Get Payload + payload, _ := t.createPayload() + + // Make request to get answer + for { + resp, err := http.Post("http://2captcha.com/in.php", "application/json", bytes.NewBuffer([]byte(payload))) + if err != nil { + time.Sleep(3 * time.Second) + continue + } + body, _ := ioutil.ReadAll(resp.Body) + response := &twocaptchaResponse{} + resp.Body.Close() + json.Unmarshal(body, response) + + // Parse the response + if response.Status == 1 { // Means there was no error + return response.Request, nil + } + switch response.Request { + case "ERROR_ZERO_BALANCE": + return "", ErrNoBalance + case "ERROR_WRONG_GOOGLEKEY": + return "", ErrWrongSitekey + case "ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST": + return "", ErrWrongAPIKey + } + + } +} + +// This method gets the captcha token from the Capmonster API +func (t *Twocaptcha) getCaptchaAnswer() (string, error) { + // Get Queue ID + queueID, err := t.getID() + if err != nil { + return "", err + } + + // Get Captcha Answer + response := &twocaptchaResponse{} + urlToAnswer := fmt.Sprintf( + "http://2captcha.com/res.php?key=%v&action=get&id=%v&json=1", + t.config.Api_key, + queueID, + ) + for { + resp, err := http.Get(urlToAnswer) + if err != nil { + time.Sleep(3 * time.Second) + continue + } + + // Parse Response + body, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + json.Unmarshal(body, response) + if response.Status == 1 { + return response.Request, nil + } else if response.Request == "ERROR_CAPTCHA_UNSOLVABLE" { + t.GetToken() + } + time.Sleep(3 * time.Second) + } +} + +/* + createPayload returns the payloads required to interact with the API. + + Possible errors that can be returned: + 1) ErrIncorrectCapType +*/ +func (t *Twocaptcha) createPayload() (string, error) { + // Define the payload we are going to send to the API + payload := twoCapIDPayload{ + Key: t.config.Api_key, + Pageurl: t.config.CaptchaURL, + JSON: 1, + Method: "userrecaptcha", + } + + // Add any other keys to the payload + switch t.config.CaptchaType { + case "v2": + payload.Googlekey = t.config.Sitekey + if t.config.IsInvisibleCaptcha { + payload.Invisible = 1 + } + case "v3": + payload.Googlekey = t.config.Sitekey + payload.Version = "v3" + payload.Action = t.config.Action + payload.MinScore = t.config.MinScore + case "hcaptcha", "hcap": + payload.Method = "hcaptcha" + payload.Sitekey = t.config.Sitekey + default: + return "", ErrIncorrectCapType + } + encoded, _ := json.Marshal(payload) + return string(encoded), nil +} diff --git a/captchatools-go/types.go b/captchatools-go/types.go new file mode 100644 index 0000000..ddf4ddd --- /dev/null +++ b/captchatools-go/types.go @@ -0,0 +1,126 @@ +package captchatoolsgo + +import "errors" + +// General type declarations +const ( + MODULE_VERSION string = "1.2.0" // Current version of this module + + // The int 1 will represent Capmonter + CapmonsterSite int = 1 + + // The int 2 will represent Anticaptcha + AnticaptchaSite int = 2 + + // The int 3 will represent 2captcha + TwoCaptchaSite int = 3 +) + +type ( + // Configurations for the captchas you are solving. + // For more a detailed documentation, visit + // https://github.com/Matthew17-21/Captcha-Tools + Config struct { + Api_key string // The API Key for the captcha solving site. + Sitekey string // Sitekey from the site where captcha is loaded. + CaptchaURL string // URL where the captcha is located. + CaptchaType string // Type of captcha you are solving. Visit https://github.com/Matthew17-21/Captcha-Tools for types + Action string // Action that is associated with the V3 captcha. + IsInvisibleCaptcha bool // If the captcha is invisible or not. + MinScore float32 // Minimum score for v3 captchas. + } + + /* + - type Harvester will be used to represent a captcha harvester. + + - In order to to have the same functionality/implementation as the + Python version, type Harvester has the `childHarvester` field, which is of type interface. + This allows us to set the field as a pointer to a real captcha harvester + and use the `GetToken` method that each captcha harvester struct has. + (Polymorphism) + + - For documentation, visit https://github.com/Matthew17-21/Captcha-Tools + */ + Harvester struct { + childHarvester GetTokenFunc + } + + // GetTokenFunc will allow us to interact with the `GetToken` struct method + // that each captcha harvester has. + GetTokenFunc interface { + GetToken() (string, error) + } + + Anticaptcha struct { + config *Config + } + + Capmonster struct { + config *Config + } + + Twocaptcha struct { + config *Config + } +) + +// Payload type declarations +type ( + // This struct will be the payload to get the queue ID from capmonster + capmonsterIDPayload struct { + ClientKey string `json:"clientKey"` + Task struct { + WebsiteURL string `json:"websiteURL"` + WebsiteKey string `json:"websiteKey"` + Type string `json:"type"` + IsInvisible bool `json:"isInvisible,omitempty"` + MinScore float32 `json:"minScore,omitempty"` + PageAction string `json:"pageAction,omitempty"` + } `json:"task"` + } + capmonsterCapAnswerPayload struct { + ClientKey string `json:"clientKey"` + TaskID int `json:"taskId"` + } +) + +// Response type declarations +type ( + capmonsterIDResponse struct { + ErrorID int `json:"errorId"` + ErrorCode string `json:"errorCode"` + TaskID int `json:"taskId"` + } + + capmonsterTokenResponse struct { + ErrorID int `json:"errorId"` + Solution struct { + GRecaptchaResponse string `json:"gRecaptchaResponse"` + } `json:"solution"` + Status string `json:"status"` + } + + twoCapIDPayload struct { + Key string `json:"key"` + Method string `json:"method"` + Googlekey string `json:"googlekey"` + Pageurl string `json:"pageurl"` + JSON int `json:"json"` + Sitekey string `json:"sitekey,omitempty"` + Invisible int `json:"invisible,omitempty"` + Version string `json:"version,omitempty"` + Action string `json:"action,omitempty"` + MinScore float32 `json:"min_score,omitempty"` + } + twocaptchaResponse struct { + Status int `json:"status"` + Request string `json:"request"` + } +) + +// Error type declarations +var ErrNoBalance error = errors.New("no balance on site") +var ErrWrongAPIKey error = errors.New("incorrect API Key for captcha solving site") +var ErrWrongSitekey = errors.New("incorrect API Key for captcha solving site") +var ErrNoHarvester = errors.New("incorrectly chose a captcha harvester. Refer to guide") +var ErrIncorrectCapType = errors.New("incorrectly chose a captcha type. Refer to guide")