This commit is contained in:
228
api/AntiGateV2.go
Normal file
228
api/AntiGateV2.go
Normal file
@ -0,0 +1,228 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"git.sati.ac/sati.ac/sati-go"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type antigateV2Api struct {
|
||||
ctx *ApiContext
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
func (a *antigateV2Api) Name() string {
|
||||
return "AntiGateV2"
|
||||
}
|
||||
|
||||
func (a *antigateV2Api) Domains() []string {
|
||||
return []string{
|
||||
"api.anti-captcha.com",
|
||||
"api.capsolver.com",
|
||||
}
|
||||
}
|
||||
|
||||
func (a *antigateV2Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
a.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
type antigateError struct {
|
||||
ErrorId uint32 `json:"erorrId"`
|
||||
ErrorCode string `json:"errorCode"`
|
||||
ErrorDescription string `json:"errorDescription"`
|
||||
}
|
||||
|
||||
var (
|
||||
errorNoSuchMethod = &antigateError{
|
||||
ErrorId: 14,
|
||||
ErrorCode: "ERROR_NO_SUCH_METHOD",
|
||||
ErrorDescription: "Request made to API with a method that does not exist",
|
||||
}
|
||||
|
||||
errorCaptchaUnsolvable = &antigateError{
|
||||
ErrorId: 12,
|
||||
ErrorCode: "ERROR_CAPTCHA_UNSOLVABLE",
|
||||
ErrorDescription: "Captcha unsolvable",
|
||||
}
|
||||
|
||||
errorNoSuchCaptchaId = &antigateError{
|
||||
ErrorId: 16,
|
||||
ErrorCode: "ERROR_NO_SUCH_CAPCHA_ID",
|
||||
ErrorDescription: "The captcha you are requesting does not exist in your active captchas list or has expired",
|
||||
}
|
||||
|
||||
errorTaskAbsent = &antigateError{
|
||||
ErrorId: 22,
|
||||
ErrorCode: "ERROR_TASK_ABSENT",
|
||||
ErrorDescription: `"task" property is empty or not set in the createTask method`,
|
||||
}
|
||||
|
||||
errorTaskNotSupported = &antigateError{
|
||||
ErrorId: 23,
|
||||
ErrorCode: "ERROR_TASK_NOT_SUPPORTED",
|
||||
ErrorDescription: "Task type is not supported or typed incorrectly",
|
||||
}
|
||||
|
||||
errorInternal = &antigateError{
|
||||
ErrorId: 1000,
|
||||
ErrorCode: "ERROR_INTERNAL",
|
||||
ErrorDescription: "Internal error, check bridge logs",
|
||||
}
|
||||
|
||||
errorBadRequest = &antigateError{
|
||||
ErrorId: 1001,
|
||||
ErrorCode: "ERROR_BAD_REQUEST",
|
||||
ErrorDescription: "Bad request",
|
||||
}
|
||||
)
|
||||
|
||||
func (a *antigateV2Api) getTaskResult(request struct {
|
||||
TaskId uint32 `json:"taskId"`
|
||||
}) any {
|
||||
task := a.ctx.Registry.Get(request.TaskId)
|
||||
if task == nil {
|
||||
return errorNoSuchCaptchaId
|
||||
}
|
||||
|
||||
if task.State == StateError {
|
||||
return errorCaptchaUnsolvable
|
||||
}
|
||||
|
||||
response := &struct {
|
||||
ErrorId uint `json:"errorId"`
|
||||
Status string `json:"status"`
|
||||
Solution any `json:"solution"`
|
||||
Cost string `json:"cost"`
|
||||
Ip string `json:"ip"`
|
||||
CreateTime int64 `json:"createTime"`
|
||||
EndTime *int64 `json:"endTime,omitempty"`
|
||||
SolveCount uint `json:"solveCount"`
|
||||
}{
|
||||
CreateTime: task.CreateTime,
|
||||
SolveCount: 1,
|
||||
Ip: a.ctx.Config.AntiGateV2.Ip,
|
||||
Cost: "0",
|
||||
}
|
||||
|
||||
if task.State == StateProcessing {
|
||||
response.Status = "processing"
|
||||
} else {
|
||||
response.Status = "ready"
|
||||
response.EndTime = &task.EndTime
|
||||
response.Cost = task.Entity.Cost
|
||||
switch task.Result.(type) {
|
||||
case *sati.ReCaptcha2Result:
|
||||
response.Solution = struct {
|
||||
GRecaptchaResponse string `json:"gRecaptchaResponse"`
|
||||
}{task.Result.(*sati.ReCaptcha2Result).Token}
|
||||
case *sati.TurnstileResult:
|
||||
response.Solution = struct {
|
||||
Token string `json:"token"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
}{
|
||||
task.Result.(*sati.TurnstileResult).Token,
|
||||
a.ctx.Config.AntiGateV2.TurnstileUserAgent,
|
||||
}
|
||||
default:
|
||||
return errorTaskNotSupported
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func (a *antigateV2Api) createTask(request struct {
|
||||
Task map[string]any `json:"task"`
|
||||
}) any {
|
||||
taskType, ok := request.Task["type"].(string)
|
||||
if !ok {
|
||||
return errorTaskAbsent
|
||||
}
|
||||
|
||||
var id uint32
|
||||
|
||||
switch taskType {
|
||||
case "TurnstileTask", "TurnstileTaskProxyless":
|
||||
var task struct {
|
||||
WebsiteURL string `json:"websiteURL"`
|
||||
WebsiteKey string `json:"websiteKey"`
|
||||
Action *string `json:"action"`
|
||||
}
|
||||
mapstructure.Decode(request.Task, &task)
|
||||
id = a.ctx.Registry.CreateTask(&sati.TurnstileTask{
|
||||
PageUrl: task.WebsiteURL,
|
||||
SiteKey: task.WebsiteKey,
|
||||
Action: task.Action,
|
||||
})
|
||||
case "RecaptchaV2Task", "RecaptchaV2TaskProxyless":
|
||||
var task struct {
|
||||
WebsiteURL string `json:"websiteURL"`
|
||||
WebsiteKey string `json:"websiteKey"`
|
||||
}
|
||||
mapstructure.Decode(request.Task, &task)
|
||||
id = a.ctx.Registry.CreateTask(&sati.ReCaptcha2Task{
|
||||
PageUrl: task.WebsiteURL,
|
||||
SiteKey: task.WebsiteKey,
|
||||
})
|
||||
default:
|
||||
return errorTaskNotSupported
|
||||
}
|
||||
|
||||
return &struct {
|
||||
ErrorId uint32 `json:"errorId"`
|
||||
TaskId uint32 `json:"taskId"`
|
||||
}{0, id}
|
||||
}
|
||||
|
||||
func (a *antigateV2Api) getBalance(struct{}) any {
|
||||
balance, err := a.ctx.Api.GetBalance()
|
||||
if err != nil {
|
||||
a.ctx.Logger.WithFields(logrus.Fields{
|
||||
"handler": a.Name(),
|
||||
"method": "getBalance",
|
||||
}).Error(err.Error())
|
||||
return errorInternal
|
||||
}
|
||||
|
||||
return &struct {
|
||||
ErrorId uint `json:"errorId"`
|
||||
CaptchaCredits uint `json:"captchaCredits"`
|
||||
Balance float64 `json:"balance"`
|
||||
}{0, 0, balance.InexactFloat64()}
|
||||
}
|
||||
|
||||
func jsonHandler[T any](handler func(data T) any) func(http.ResponseWriter, *http.Request) {
|
||||
emptyRequest := reflect.Zero(reflect.TypeOf(handler).In(0)).Interface().(T)
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
data, _ := io.ReadAll(r.Body)
|
||||
request := emptyRequest
|
||||
if err := json.Unmarshal(data, &request); err != nil {
|
||||
marshaled, _ := json.Marshal(errorBadRequest)
|
||||
w.Write(marshaled)
|
||||
return
|
||||
}
|
||||
|
||||
response := handler(request)
|
||||
|
||||
marshaled, _ := json.Marshal(response)
|
||||
w.Write(marshaled)
|
||||
}
|
||||
}
|
||||
|
||||
func newAntigateV2Api(ctx *ApiContext) ApiHandler {
|
||||
api := &antigateV2Api{ctx, http.NewServeMux()}
|
||||
|
||||
api.mux.HandleFunc("/createTask", jsonHandler(api.createTask))
|
||||
api.mux.HandleFunc("/getTaskResult", jsonHandler(api.getTaskResult))
|
||||
api.mux.HandleFunc("/getBalance", jsonHandler(api.getBalance))
|
||||
|
||||
return api
|
||||
}
|
Reference in New Issue
Block a user