├── .gitignore ├── LICENSE ├── README.md ├── capsolver.go ├── check.go ├── constant.go ├── go.mod ├── request.go ├── task_test.go └── types.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.jpg 2 | .idea/ 3 | *.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | Copyright (c) 2013 The github.com/capsolver/capsolver-go Authors. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following disclaimer 14 | in the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Capsolver 2 | CapSolver official go library 3 | 4 | 5 | ## Supported CAPTCHA types: 6 | - Geetest 7 | - ReCaptchaV2 8 | - ReCaptchav3 9 | - Cloudflare 10 | 11 | 12 | ## Installation 13 | 14 | You don't need this source code unless you want to modify the package. If you just 15 | want to use the package, just run: 16 | 17 | ```sh 18 | go get github.com/capsolver/capsolver-go 19 | ``` 20 | 21 | 22 | 23 | ## Usage 24 | 25 | ```bash 26 | export CAPSOLVER_API_KEY='...' 27 | ``` 28 | 29 | Or set `capsolver apiKey` to its value: 30 | 31 | ```go 32 | capSolver := CapSolver{} 33 | //capSolver := CapSolver{apiKey:"..."} 34 | s, err := capSolver.Solve( 35 | map[string]any{ 36 | "type": "ReCaptchaV2taskProxyLess", 37 | "websiteURL": "https://www.google.com/recaptcha/api2/demo", 38 | "websiteKey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", 39 | }) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | log.Println(s.Solution.GRecaptchaResponse) 44 | 45 | 46 | // recognition 47 | b, err := os.ReadFile("queue-it.jpg") 48 | if err != nil { 49 | panic(err) 50 | } 51 | s, err := capSolver.Solve( 52 | map[string]any{ 53 | "type": "ImageToTextTask", 54 | "module": "queueit", 55 | "body": base64.StdEncoding.EncodeToString(b), 56 | }) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | log.Println(s.Solution.Text) 61 | 62 | // get balance 63 | b, err := capSolver.Balance() 64 | if err != nil { 65 | log.Fatal(err) 66 | } 67 | log.Println(b.Balance) 68 | ``` 69 | 70 | 71 | -------------------------------------------------------------------------------- /capsolver.go: -------------------------------------------------------------------------------- 1 | package capsolver_go 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | func (c CapSolver) Solve(task map[string]any) (*capSolverResponse, error) { 9 | // 10 | capRes, err := request(CREATE_TASK_URI, &capSolverRequest{Task: &task, ClientKey: c.getApiKey()}) 11 | if err != nil { 12 | return nil, err 13 | } 14 | if capRes.ErrorId == 1 { 15 | return nil, errors.New(capRes.ErrorDescription) 16 | } 17 | if capRes.Status == STATUS_READY { 18 | return capRes, nil 19 | } 20 | for i := 0; i < TASK_TIMEOUT; i++ { 21 | capRes, err = request(GET_TASK_URI, &capSolverRequest{ClientKey: c.getApiKey(), TaskId: capRes.TaskId}) 22 | time.Sleep(time.Second * 1) 23 | if err != nil { 24 | return nil, err 25 | } 26 | if capRes.ErrorId == 1 { 27 | return nil, errors.New(capRes.ErrorDescription) 28 | } 29 | if capRes.Status == STATUS_READY { 30 | break 31 | } 32 | } 33 | return capRes, err 34 | } 35 | 36 | func (c CapSolver) Balance() (*capSolverResponse, error) { 37 | capRes, err := request(BALANCE_URI, &capSolverRequest{ClientKey: c.getApiKey()}) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return capRes, nil 42 | 43 | } 44 | 45 | func (c *CapSolver) getApiKey() string { 46 | if c.ApiKey != "" { 47 | return c.ApiKey 48 | } 49 | return ApiKey 50 | } 51 | -------------------------------------------------------------------------------- /check.go: -------------------------------------------------------------------------------- 1 | package capsolver_go 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | SUPPORT_CAPTCHA_TYPES = []string{ 11 | 12 | "HCaptchaTask", 13 | "HCaptchaTaskProxyLess", 14 | "HCaptchaEnterpriseTask", 15 | "HCaptchaEnterpriseTaskProxyLess", 16 | "HCaptchaTurboTask", 17 | 18 | "FunCaptchaTask", 19 | "FunCaptchaTaskProxyLess", 20 | 21 | "GeeTestTask", 22 | "GeeTestTaskProxyLess", 23 | 24 | "ReCaptchaV2Task", 25 | "ReCaptchaV2TaskProxyLess", 26 | 27 | "ReCaptchaV2EnterpriseTaskProxyLess", 28 | "ReCaptchaV2EnterpriseTask", 29 | 30 | "ReCaptchaV3Task", 31 | "ReCaptchaV3TaskProxyLess", 32 | 33 | "ReCaptchaV3EnterpriseTask", 34 | "ReCaptchaV3EnterpriseTaskProxyLess", 35 | 36 | "MtCaptchaTask", 37 | "MtCaptchaTaskProxyLess", 38 | 39 | "DataDomeSliderTask", 40 | 41 | "AntiCloudflareTask", 42 | 43 | "AntiKasadaTask", 44 | 45 | "AntiAkamaiBMPTask", 46 | 47 | "ImageToTextTask", 48 | 49 | "HCaptchaClassification", 50 | 51 | "FunCaptchaClassification", 52 | 53 | "AwsWafClassification", 54 | } 55 | ) 56 | 57 | const ( 58 | websiteKey = "websiteKey" 59 | websiteURL = "websiteURL" 60 | ) 61 | 62 | func checkParams(params map[string]any) error { 63 | 64 | captchaType, ok := params["type"].(string) 65 | exists := false 66 | for _, v := range SUPPORT_CAPTCHA_TYPES { 67 | if v == captchaType { 68 | exists = true 69 | } 70 | } 71 | if !exists { 72 | return errors.New("unSupported Type " + captchaType + "you need to pay attention to capitalization,current support types fellow\n" + formatTaskTypes()) 73 | } 74 | if !ok { 75 | return formatParamError(captchaType, "type") 76 | } 77 | captchaType = strings.ToLower(captchaType) 78 | if strings.Contains(captchaType, "recaptcha") { 79 | if captchaType == "recaptchaclassification" { 80 | } else { 81 | return checkNormalCaptcha(params) 82 | } 83 | } 84 | if strings.Contains(captchaType, "hcaptcha") { 85 | if captchaType == "hcaptchaclassification" { 86 | if _, ok = params["queries"]; !ok { 87 | return formatParamError(captchaType, "queries") 88 | } 89 | if _, ok = params["question"]; !ok { 90 | return formatParamError(captchaType, "question") 91 | } 92 | } else { 93 | return checkNormalCaptcha(params) 94 | } 95 | } 96 | if strings.Contains(captchaType, "funcaptcha") { 97 | if captchaType == "funcaptchaclassification" { 98 | if _, ok = params["images"]; !ok { 99 | return formatParamError(captchaType, "images") 100 | } 101 | } else { 102 | if _, ok := params[websiteURL]; !ok { 103 | return formatParamError(captchaType, websiteURL) 104 | } 105 | if _, ok := params["websitePublicKey"]; !ok { 106 | return formatParamError(captchaType, "websitePublicKey") 107 | } 108 | return nil 109 | } 110 | } 111 | 112 | if strings.Contains(captchaType, "mtcaptcha") { 113 | return checkNormalCaptcha(params) 114 | } 115 | 116 | if strings.Contains(captchaType, "geetesttask") { 117 | if _, ok = params["gt"]; !ok { 118 | return formatParamError(captchaType, "gt") 119 | } 120 | if _, ok = params["challenge"]; !ok { 121 | return formatParamError(captchaType, "challenge") 122 | } 123 | } 124 | if strings.Contains(captchaType, "datadom") { 125 | if _, ok = params["proxy"]; !ok { 126 | return formatParamError(captchaType, "proxy") 127 | } 128 | if _, ok = params["useragent"]; !ok { 129 | return formatParamError(captchaType, "userAgent") 130 | } 131 | } 132 | if strings.Contains(captchaType, "anticloudflare") { 133 | if _, ok = params["metadata"]; !ok { 134 | return formatParamError(captchaType, "metadata") 135 | } 136 | if _, ok = params["proxy"]; !ok { 137 | return formatParamError(captchaType, "proxy") 138 | } 139 | } 140 | if strings.Contains(captchaType, "antikasada") { 141 | if _, ok = params["pageURL"]; !ok { 142 | return formatParamError(captchaType, "pageURL") 143 | } 144 | if _, ok = params["proxy"]; !ok { 145 | return formatParamError(captchaType, "proxy") 146 | } 147 | } 148 | if strings.Contains(captchaType, "antiakamaibmp") { 149 | if _, ok = params["packageName"]; !ok { 150 | return formatParamError(captchaType, "packageName") 151 | } 152 | } 153 | return nil 154 | } 155 | 156 | func checkNormalCaptcha(params map[string]any) error { 157 | captchaType, _ := params["type"].(string) 158 | if _, ok := params[websiteKey]; !ok { 159 | return formatParamError(captchaType, websiteKey) 160 | } 161 | if _, ok := params[websiteURL]; !ok { 162 | return formatParamError(captchaType, websiteURL) 163 | } 164 | return nil 165 | } 166 | 167 | func formatTaskTypes() string { 168 | 169 | t_list := []string{} 170 | for i, v := range SUPPORT_CAPTCHA_TYPES { 171 | t_list = append(t_list, fmt.Sprintf("%d,%s", i, v)) 172 | } 173 | return strings.Join(t_list, "\n") 174 | } 175 | 176 | func formatParamError(captchaType, paramKey string) error { 177 | return errors.New(captchaType + fmt.Sprintf(" need %s param", paramKey)) 178 | } 179 | -------------------------------------------------------------------------------- /constant.go: -------------------------------------------------------------------------------- 1 | package capsolver_go 2 | 3 | import "os" 4 | 5 | var ( 6 | ApiKey = os.Getenv("CAPSOLVER_API_KEY") 7 | ApiHost = os.Getenv("CAPSOLVER_API_HOST") 8 | ) 9 | 10 | const ( 11 | STATUS_READY = "ready" 12 | CREATE_TASK_URI = "/createTask" 13 | GET_TASK_URI = "/getTaskResult" 14 | BALANCE_URI = "/getBalance" 15 | TASK_TIMEOUT = 45 16 | ) 17 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/capsolver/capsolver-go 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /request.go: -------------------------------------------------------------------------------- 1 | package capsolver_go 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | ) 10 | 11 | func init() { 12 | if ApiHost == "" { 13 | ApiHost = "https://api.capsolver.com" 14 | } 15 | } 16 | 17 | func request(uri string, solverRequest *capSolverRequest) (*capSolverResponse, error) { 18 | 19 | b, _ := json.Marshal(solverRequest) 20 | resp, err := http.Post(fmt.Sprintf("%s%s", ApiHost, uri), "application/json", bytes.NewReader(b)) 21 | if err != nil { 22 | return nil, err 23 | } 24 | defer resp.Body.Close() 25 | b, _ = io.ReadAll(resp.Body) 26 | capResponse := &capSolverResponse{} 27 | capResponse.Solution = &solution{} 28 | json.Unmarshal(b, capResponse) 29 | return capResponse, nil 30 | } 31 | -------------------------------------------------------------------------------- /task_test.go: -------------------------------------------------------------------------------- 1 | package capsolver_go 2 | 3 | import ( 4 | "encoding/base64" 5 | "log" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func TestTask(t *testing.T) { 11 | 12 | capSolver := CapSolver{} 13 | s, err := capSolver.Solve( 14 | map[string]any{ 15 | "type": "ReCaptchaV2TaskProxyLess", 16 | "websiteURL": "https://www.google.com/recaptcha/api2/demo", 17 | "websiteKey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", 18 | }) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | log.Println(s.Solution.GRecaptchaResponse) 23 | } 24 | 25 | func TestFunCaptcha(t *testing.T) { 26 | capSolver := CapSolver{} 27 | s, err := capSolver.Solve( 28 | map[string]any{ 29 | "type": "FunCaptchaTask", 30 | "websiteURL": "", 31 | "websitePublicKey": "", 32 | "proxy": "", 33 | }) 34 | 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | log.Println(s.Solution) 39 | 40 | } 41 | func TestHCaptcha(t *testing.T) { 42 | 43 | capSolver := CapSolver{} 44 | s, err := capSolver.Solve( 45 | map[string]any{ 46 | "type": "HCaptchaTurboTask", 47 | "websiteURL": "https://www.discord.com", 48 | "websiteKey": "4c672d35-0701-42b2-88c3-78380b0db560", 49 | "proxy": "your proxy", 50 | }) 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | log.Println(s.Solution.GRecaptchaResponse) 55 | 56 | } 57 | 58 | func TestGeeTest(t *testing.T) { 59 | 60 | capSolver := CapSolver{} 61 | s, err := capSolver.Solve( 62 | map[string]any{ 63 | "type": "GeeTestTaskProxyLess", 64 | }) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | log.Println(s.Solution.GRecaptchaResponse) 69 | } 70 | 71 | func TestDataDom(t *testing.T) { 72 | 73 | capSolver := CapSolver{} 74 | s, err := capSolver.Solve( 75 | map[string]any{ 76 | "type": "DataDomeSliderTask", 77 | }) 78 | if err != nil { 79 | log.Fatal(err) 80 | } 81 | log.Println(s.Solution.GRecaptchaResponse) 82 | } 83 | 84 | func TestAntiCloudflareTask(t *testing.T) { 85 | 86 | capSolver := CapSolver{} 87 | s, err := capSolver.Solve( 88 | map[string]any{ 89 | "type": "AntiCloudflareTask", 90 | }) 91 | if err != nil { 92 | log.Fatal(err) 93 | } 94 | log.Println(s.Solution.GRecaptchaResponse) 95 | } 96 | 97 | func TestAntiKasadaTask(t *testing.T) { 98 | 99 | capSolver := CapSolver{} 100 | s, err := capSolver.Solve( 101 | map[string]any{ 102 | "type": "AntiKasadaTask", 103 | }) 104 | if err != nil { 105 | log.Fatal(err) 106 | } 107 | log.Println(s.Solution.GRecaptchaResponse) 108 | } 109 | 110 | func TestAntiAkamaiBMPTask(t *testing.T) { 111 | 112 | capSolver := CapSolver{} 113 | s, err := capSolver.Solve( 114 | map[string]any{ 115 | "type": "AntiAkamaiBMPTask", 116 | }) 117 | if err != nil { 118 | log.Fatal(err) 119 | } 120 | log.Println(s.Solution.GRecaptchaResponse) 121 | } 122 | 123 | func TestBalance(t *testing.T) { 124 | 125 | capSolver := CapSolver{} 126 | b, err := capSolver.Balance() 127 | if err != nil { 128 | log.Fatal(err) 129 | } 130 | log.Println(b.Balance) 131 | } 132 | 133 | func TestRecognition(t *testing.T) { 134 | 135 | capSolver := CapSolver{} 136 | b, err := os.ReadFile("queue-it.jpg") 137 | if err != nil { 138 | panic(err) 139 | } 140 | s, err := capSolver.Solve( 141 | map[string]any{"type": "ImageToTextTask", 142 | "module": "queueit", 143 | "body": base64.StdEncoding.EncodeToString(b), 144 | }) 145 | if err != nil { 146 | log.Fatal(err) 147 | } 148 | log.Println(s.Solution.Text) 149 | } 150 | 151 | func TestHCaptchaClassfication(t *testing.T) { 152 | capSolver := CapSolver{} 153 | b, err := os.ReadFile("queue-it.jpg") 154 | if err != nil { 155 | panic(err) 156 | } 157 | s, err := capSolver.Solve( 158 | map[string]any{ 159 | "type": "HCaptchaClassification", 160 | "question": "Please click each image containing a truck", 161 | "queries": []string{ 162 | base64.StdEncoding.EncodeToString(b), 163 | }, 164 | }) 165 | if err != nil { 166 | log.Fatal(err) 167 | } 168 | log.Println(s.Solution) 169 | } 170 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package capsolver_go 2 | 3 | type CapSolver struct { 4 | ApiKey string 5 | } 6 | 7 | type solution struct { 8 | Object []bool `json:"objects,omitempty"` 9 | Box []float32 `json:"box,omitempty"` 10 | ImageSizes []int32 `json:"imageSize,omitempty"` 11 | Text string `json:"text,omitempty"` 12 | UserAgent string `json:"userAgent,omitempty"` 13 | ExpireTime int64 `json:"expireTime,omitempty"` 14 | GRecaptchaResponse string `json:"gRecaptchaResponse,omitempty"` 15 | Challenge string `json:"challenge,omitempty"` 16 | Validate string `json:"validate,omitempty"` 17 | CaptchaId string `json:"captcha-id,omitempty"` 18 | CaptchaOutput string `json:"captcha-output,omitempty"` 19 | GenTime string `json:"gen_time,omitempty"` 20 | LotNumber string `json:"lot_number,omitempty"` 21 | PassToken string `json:"pass_token,omitempty"` 22 | RiskType string `json:"risk_Type,omitempty"` 23 | Token string `json:"token,omitempty"` 24 | Cookie string `json:"cookie,omitempty"` 25 | Type string `json:"type,omitempty"` 26 | } 27 | 28 | type capSolverResponse struct { 29 | ErrorId int32 `json:"errorId"` 30 | ErrorCode string `json:"errorCode"` 31 | ErrorDescription string `json:"errorDescription,omitempty"` 32 | Status string `json:"status,omitempty"` 33 | Solution *solution `json:"solution,omitempty"` 34 | TaskId string `json:"taskId,omitempty"` 35 | Balance float32 `json:"balance,omitempty"` 36 | Packages []string `json:"packages,omitempty"` 37 | } 38 | 39 | type capSolverRequest struct { 40 | ClientKey string `json:"ClientKey"` 41 | Task *map[string]any `json:"task,omitempty"` 42 | TaskId string `json:"taskId,omitempty"` 43 | } 44 | type enterPrisePayload struct { 45 | S string `json:"s,omitempty"` 46 | Rqdata string `json:"rqdata,omitempty"` 47 | } 48 | type cookies struct { 49 | Cookies []cookieItem `json:"cookies,omitempty"` 50 | } 51 | type cookieItem struct { 52 | Value string `json:"value,omitempty"` 53 | Name string `json:"name,omitempty"` 54 | } 55 | 56 | type CapSolverTask struct { 57 | Type string `json:"type"` 58 | WebsiteURL string `json:"websiteURL,omitempty"` 59 | WebsiteKey string `json:"websiteKey,omitempty"` 60 | Proxy string `json:"proxy,omitempty"` 61 | EnterPrisePayload *enterPrisePayload `json:"enterprisePayload,omitempty"` 62 | IsInvisible bool `json:"isInvisible,omitempty"` 63 | ApiDomain string `json:"apiDomain,omitempty"` 64 | UserAgent string `json:"userAgent,omitempty"` 65 | Cookies *cookies `json:"cookies,omitempty"` 66 | Module string `json:"module,omitempty"` 67 | Body string `json:"body,omitempty"` 68 | Question string `json:"question,omitempty"` 69 | Queries []string `json:"Queries,omitempty"` 70 | PageAction string `json:"pageAction,omitempty"` 71 | MinScore float32 `json:"MinScore,omitempty"` 72 | Gt string `json:"gt,omitempty"` 73 | Challenge string `json:"challenge,omitempty"` 74 | GeetestApiServerSubdomain string `json:"geetestApiServerSubdomain,omitempty"` 75 | CaptchaId string `json:"captchaId,omitempty"` 76 | CaptchaUrl string `json:"captchaUrl,omitempty"` 77 | Metadata map[string]string `json:"metadata"` 78 | Html string `json:"html"` 79 | } 80 | --------------------------------------------------------------------------------