fix(CapSolver): separate CapSolver api from AntiGateV2
release-tag / release (push) Successful in 1m53s Details

master v0.3.2
sati.ac 2023-07-14 06:49:25 +03:00
parent 81193a05ef
commit dd10f15834
4 changed files with 253 additions and 4 deletions

View File

@ -4,6 +4,7 @@
на данный момент реализованы:
- RuCaptcha
- AntiGateV2
- CapSolver
## установка и запуск (windows)
- скачайте мост со [страницы релизов](https://git.sati.ac/sati.ac/bridge/releases)

View File

@ -23,10 +23,7 @@ func (a *antigateV2Api) Name() string {
}
func (a *antigateV2Api) Domains() []string {
return []string{
"api.anti-captcha.com",
"api.capsolver.com",
}
return []string{"api.anti-captcha.com"}
}
func (a *antigateV2Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -244,6 +241,11 @@ func jsonHandler[T any](api *antigateV2Api, handler func(data T) any) func(http.
api.ctx.Logger.WithFields(logrus.Fields{"handler": api.Name(), "response": response}).Debug("response")
marshaled, _ := json.Marshal(response)
if _, ok := response.(*antigateError); ok {
w.WriteHeader(400)
}
w.Write(marshaled)
}
}

245
api/CapSolver.go 100644
View File

@ -0,0 +1,245 @@
package api
import (
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"strconv"
"strings"
"git.sati.ac/sati.ac/sati-go"
"github.com/mitchellh/mapstructure"
"github.com/sirupsen/logrus"
)
type capSolverApi struct {
ctx *ApiContext
mux *http.ServeMux
}
func (a *capSolverApi) Name() string {
return "CapSolver"
}
func (a *capSolverApi) Domains() []string {
return []string{"api.capsolver.com"}
}
func (a *capSolverApi) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.mux.ServeHTTP(w, r)
}
type capsolverError struct {
ErrorId uint32 `json:"erorrId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
}
var (
csErrorInvalidTaskData = &capsolverError{
ErrorId: 1,
ErrorCode: "ERROR_INVALID_TASK_DATA",
ErrorDescription: "parse request data error",
}
csErrorCaptchaUnsolvable = &capsolverError{
ErrorId: 1,
ErrorCode: "ERROR_CAPTCHA_UNSOLVABLE",
ErrorDescription: "Captcha unsolvable",
}
csErrorTaskNotSupported = &capsolverError{
ErrorId: 1,
ErrorCode: "ERROR_TASK_NOT_SUPPORTED",
ErrorDescription: "Task type is not supported or typed incorrectly",
}
csErrorInternal = &capsolverError{
ErrorId: 1,
ErrorCode: "ERROR_INTERNAL",
ErrorDescription: "Internal error, check bridge logs",
}
csErrorTaskIdInvalid = &capsolverError{
ErrorId: 1,
ErrorCode: "ERROR_TASKID_INVALID",
ErrorDescription: "Task ID does not exist or is invalid",
}
)
func (a *capSolverApi) getTaskResult(request struct {
TaskId any `json:"taskId"`
}) any {
var taskId uint32
switch id := request.TaskId.(type) {
case float64:
taskId = uint32(id)
case string:
parsed, err := strconv.ParseUint(id, 10, 32)
if err != nil {
return csErrorTaskIdInvalid
}
taskId = uint32(parsed)
default:
return csErrorTaskIdInvalid
}
task := a.ctx.Registry.Get(taskId)
if task == nil {
return csErrorTaskIdInvalid
}
if task.State == StateError {
return csErrorCaptchaUnsolvable
}
response := &struct {
ErrorId uint `json:"errorId"`
Status string `json:"status"`
Solution any `json:"solution"`
Cost string `json:"cost"`
}{Cost: "0"}
if task.State == StateProcessing {
response.Status = "processing"
} else {
response.Status = "ready"
response.Cost = task.Entity.Cost
switch result := task.Result.(type) {
case *sati.ReCaptcha2Result:
response.Solution = struct {
GRecaptchaResponse string `json:"gRecaptchaResponse"`
}{result.Token}
case *sati.TurnstileResult:
response.Solution = struct {
Type string `json:"type"`
Token string `json:"token"`
UserAgent string `json:"userAgent"`
}{"turnstile", result.Token, a.ctx.Config.AntiGateV2.TurnstileUserAgent}
case *sati.FunCaptchaResult:
response.Solution = struct {
Token string `json:"token"`
}{result.Token}
default:
return csErrorTaskNotSupported
}
}
return response
}
func (a *capSolverApi) createTask(request struct {
Task map[string]any `json:"task"`
}) any {
taskType, ok := request.Task["type"].(string)
if !ok {
return csErrorInvalidTaskData
}
var id uint32
switch strings.ToLower(taskType) {
case "anticloudflaretask":
var task struct {
WebsiteURL string `json:"websiteURL"`
WebsiteKey string `json:"websiteKey"`
Metadata struct {
Action *string `json:"action"`
CData *string `json:"cdata"`
} `json:"metadata"`
}
mapstructure.Decode(request.Task, &task)
id = a.ctx.Registry.CreateTask(&sati.TurnstileTask{
PageUrl: task.WebsiteURL,
SiteKey: task.WebsiteKey,
Action: task.Metadata.Action,
CData: task.Metadata.CData,
})
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,
})
case "funcaptchatask", "funcaptchataskproxyless":
var task struct {
WebsiteURL string `json:"websiteURL"`
WebsitePublicKey string `json:"websitePublicKey"`
Data map[string]string `json:"data"`
}
mapstructure.Decode(request.Task, &task)
id = a.ctx.Registry.CreateTask(&sati.FunCaptchaTask{
PageUrl: task.WebsiteURL,
SiteKey: task.WebsitePublicKey,
Data: task.Data,
})
default:
return csErrorTaskNotSupported
}
return &struct {
ErrorId uint32 `json:"errorId"`
TaskId string `json:"taskId"`
}{0, fmt.Sprint(id)}
}
func (a *capSolverApi) 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 csErrorInternal
}
return &struct {
ErrorId uint `json:"errorId"`
Balance float64 `json:"balance"`
Packages []struct{} `json:"packages"`
}{0, balance.InexactFloat64(), []struct{}{}}
}
func csJsonHandler[T any](api *capSolverApi, 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(csErrorInvalidTaskData)
w.Write(marshaled)
return
}
api.ctx.Logger.WithFields(logrus.Fields{"handler": api.Name(), "request": request}).Debug("request")
response := handler(request)
api.ctx.Logger.WithFields(logrus.Fields{"handler": api.Name(), "response": response}).Debug("response")
marshaled, _ := json.Marshal(response)
if csError, ok := response.(*capsolverError); ok && csError.ErrorId == 1 {
w.WriteHeader(400)
}
w.Write(marshaled)
}
}
func newCapSolverApi(ctx *ApiContext) ApiHandler {
api := &capSolverApi{ctx, http.NewServeMux()}
api.mux.HandleFunc("/createTask", csJsonHandler(api, api.createTask))
api.mux.HandleFunc("/getTaskResult", csJsonHandler(api, api.getTaskResult))
api.mux.HandleFunc("/getBalance", csJsonHandler(api, api.getBalance))
return api
}

View File

@ -166,6 +166,7 @@ func NewApiServer(ctx *ApiContext) *ApiServer {
newStatsApi(ctx),
newAntigateV2Api(ctx),
newRuCaptchaApi(ctx),
newCapSolverApi(ctx),
}
server := &ApiServer{