first commit

This commit is contained in:
sam 2024-10-12 01:32:31 +01:00
commit 3d27f14905
7 changed files with 347 additions and 0 deletions

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
BSD 2-Clause License
Copyright (c) 2024, samjblack
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

56
README.md Normal file
View File

@ -0,0 +1,56 @@
# EzCaptcha Client Go
## Supported Captchas
- AkamaiWeb
- AkamaiBMP
- HCaptcha + Enterprise
- ReCaptchaV2 + Enterprise
- ReCaptchaV3 + Enterprise
## Installing
```sh
go get -u github.com/samjblack/ezcaptcha-client-go
```
## Usage
```go
client, err := ezcaptcha.NewClient("your-api-key", "", 0)
if err != nil {
log.Fatal(err)
}
task, err := client.NewReCaptchaV2Task(&ezcaptcha.ReCaptchaV2{
// For availabe types please refer to ezcaptcha's docs (https://ezcaptcha.atlassian.net/wiki/spaces/IS/pages/7045121/EzCaptcha+API+Docs+English)
Type: "ReCaptchaV2TaskProxyless",
WebsiteURL: "https://www.google.com/recaptcha/api2/demo",
WebsiteKey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
IsInvisible: false,
})
if err != nil {
log.Fatal(err)
}
result, err := client.GetResult(task)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Solution.GRecaptchaResponse)
```
## AkamaiBMP Example
```go
client, err := ezcaptcha.NewClient("your-api-key", "", 0)
if err != nil {
log.Fatal(err)
}
result, err := client.SolveAkamaiBMP(&ezcaptcha.AkamaiBMP{
Type: "AkamaiBMPTaskProxyless",
BundleID: "example.bundle.id",
Device: "ios",
Version: "3.3.5",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Solution.Sensor)
```

71
ezcaptcha.go Normal file
View File

@ -0,0 +1,71 @@
package ezcaptcha
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"
)
var (
ApiURL string = "https://api.ez-captcha.com"
SyncApiURL string = "https://sync.ez-captcha.com"
CreateTaskEndPoint string = "/createTask"
CreateSyncTaskEndpoint string = "/createSyncTask"
GetTaskResultEndPoint string = "/getTaskResult"
GetBalanceEndPoint string = "/getBalance"
)
func NewClient(clientKey, appID string, attempts int) (*Client, error) {
if attempts == 0 {
attempts = 45
} else if attempts < 0 {
return nil, fmt.Errorf("attempts cannot have a negative value")
}
return &Client{
ClientKey: clientKey,
AppID: appID,
Attempts: attempts,
}, nil
}
func (c *Client) GetResult(task *CreateTaskResponse) (*SolutionResponse, error) {
response := &SolutionResponse{}
var err error
for i := 0; i < c.Attempts; i++ {
time.Sleep(3 * time.Second)
response, err = c.getResult(task)
if err != nil {
return nil, err
}
if response.ErrorID != 0 {
return nil, errors.New(fmt.Sprintf("%s", response.ErrorDescription))
}
if response.Status == "ready" {
break
}
}
return response, nil
}
func (c *Client) GetBalance() (int, error) {
requestData := GetBalanceRequest{
ClientKey: c.ClientKey,
}
b, _ := json.Marshal(requestData)
resp, err := http.Post(fmt.Sprintf("%s%s", ApiURL, GetBalanceEndPoint), "application/json", bytes.NewReader(b))
if err != nil {
return 0, err
}
defer resp.Body.Close()
response := &GetBalanceResponse{}
b, _ = io.ReadAll(resp.Body)
json.Unmarshal(b, response)
if response.ErrorID != 0 {
return 0, errors.New(fmt.Sprintf("%s", response.ErrorDescription))
}
return response.Balance, nil
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/samjblack/ezcaptcha-client-go
go 1.22.5

73
requests.go Normal file
View File

@ -0,0 +1,73 @@
package ezcaptcha
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
func (c *Client) getResult(task *CreateTaskResponse) (*SolutionResponse, error) {
requestData := SolutionRequest{
ClientKey: c.ClientKey,
TaskID: task.TaskID,
}
b, _ := json.Marshal(requestData)
resp, err := http.Post(fmt.Sprintf("%s%s", ApiURL, GetTaskResultEndPoint), "application/json", bytes.NewReader(b))
if err != nil {
return nil, err
}
response := &SolutionResponse{}
b, _ = io.ReadAll(resp.Body)
json.Unmarshal(b, response)
defer resp.Body.Close()
return response, nil
}
func (c *Client) createTask(task interface{}) (*CreateTaskResponse, error) {
requestData := &TaskRequest{
Client: *c,
Task: task,
}
b, _ := json.Marshal(requestData)
resp, err := http.Post(fmt.Sprintf("%s%s", ApiURL, CreateTaskEndPoint), "application/json", bytes.NewReader(b))
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, _ = io.ReadAll(resp.Body)
response := &CreateTaskResponse{}
err = json.Unmarshal(b, response)
if err != nil {
return nil, err
}
if response.ErrorID != 0 {
return nil, fmt.Errorf(response.ErrorDescription)
}
return response, nil
}
func (c *Client) solveAkamai(task interface{}) (*AkamaiResponse, error) {
requestData := &TaskRequest{
Client: *c,
Task: task,
}
b, _ := json.Marshal(requestData)
resp, err := http.Post(fmt.Sprintf("%s%s", SyncApiURL, CreateSyncTaskEndpoint), "application/json", bytes.NewReader(b))
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, _ = io.ReadAll(resp.Body)
response := &AkamaiResponse{}
err = json.Unmarshal(b, response)
if err != nil {
return nil, err
}
if response.ErrorID != 0 {
return nil, errors.New(fmt.Sprintf("%s", response.ErrorDescription))
}
return response, nil
}

21
tasks.go Normal file
View File

@ -0,0 +1,21 @@
package ezcaptcha
func (c *Client) NewReCaptchaV2Task(task *ReCaptchaV2) (*CreateTaskResponse, error) {
return c.createTask(task)
}
func (c *Client) NewReCaptchaV3Task(task *ReCaptchaV3) (*CreateTaskResponse, error) {
return c.createTask(task)
}
func (c *Client) NewHCaptchaTask(task *HCaptcha) (*CreateTaskResponse, error) {
return c.createTask(task)
}
func (c *Client) SolveAkamai(task *Akamai) (*AkamaiResponse, error) {
return c.solveAkamai(task)
}
func (c *Client) SolveAkamaiBMP(task *AkamaiBMP) (*AkamaiResponse, error) {
return c.solveAkamai(task)
}

99
types.go Normal file
View File

@ -0,0 +1,99 @@
package ezcaptcha
type Client struct {
ClientKey string `json:"clientKey"`
AppID string `json:"appId,omitempty"`
Attempts int
}
type GetBalanceRequest struct {
ClientKey string `json:"clientKey"`
}
// Structs which are using for creating tasks
type TaskRequest struct {
Client
Task any `json:"task"`
}
type SolutionRequest struct {
ClientKey string `json:"clientKey"`
TaskID string `json:"taskId"`
}
type ReCaptchaV2 struct {
Type string `json:"type"`
WebsiteURL string `json:"websiteURL"`
WebsiteKey string `json:"websiteKey"`
IsInvisible bool `json:"isInvisible,omitempty"`
Sa string `json:"sa,omitempty"`
S string `json:"s,omitempty"`
}
type ReCaptchaV3 struct {
Type string `json:"type"`
WebsiteURL string `json:"websiteURL"`
WebsiteKey string `json:"websiteKey"`
IsInvisible bool `json:"isInvisible,omitempty"`
PageAction string `json:"pageAction,omitempty"`
}
type HCaptcha struct {
Type string `json:"type"`
WebsiteURL string `json:"websiteURL"`
WebsiteKey string `json:"websiteKey"`
EnterpricePayload string `json:"enterprisePayload,omitempty"`
IsInvisible bool `json:"isInvisible,omitempty"`
}
type Akamai struct {
Type string `json:"type"`
PageURL string `json:"pageUrl"`
Bmsz string `json:"bmsz"`
UA string `json:"ua"`
Lang string `json:"lang"`
}
type AkamaiBMP struct {
Type string `json:"type"`
BundleID string `json:"bundleID"`
Device string `json:"device"`
Version string `json:"version"`
}
// Structs to parse the response from their api
type CreateTaskResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorDescription string `json:"errorDescription,omitempty"`
TaskID string `json:"taskId,omitempty"`
}
type SolutionResponse struct {
ErrorID int `json:"errorId"`
ErrorCode any `json:"errorCode,omitempty"`
ErrorDescription any `json:"errorDescription,omitempty"`
Solution struct {
GRecaptchaResponse string `json:"gRecaptchaResponse,omitempty"`
} `json:"solution"`
Status string `json:"status,omitempty"`
}
type AkamaiResponse struct {
ErrorID int `json:"errorId"`
ErrorCode any `json:"errorCode,omitempty"`
ErrorDescription any `json:"errorDescription,omitempty"`
Solution struct {
Payload string `json:"payload,omitempty"`
Sensor string `json:"sensor,omitempty"`
} `json:"solution"`
Status string `json:"status"`
}
type GetBalanceResponse struct {
Balance int `json:"balance,omitempty"`
ErrorID int `json:"errorId"`
ErrorCode any `json:"errorCode,omitempty"`
ErrorDescription any `json:"errorDescription,omitempty"`
}