fix(CapSolver): separate CapSolver api from AntiGateV2
All checks were successful
release-tag / release (push) Successful in 1m53s
All checks were successful
release-tag / release (push) Successful in 1m53s
This commit is contained in:
parent
81193a05ef
commit
dd10f15834
@ -4,6 +4,7 @@
|
|||||||
на данный момент реализованы:
|
на данный момент реализованы:
|
||||||
- RuCaptcha
|
- RuCaptcha
|
||||||
- AntiGateV2
|
- AntiGateV2
|
||||||
|
- CapSolver
|
||||||
|
|
||||||
## установка и запуск (windows)
|
## установка и запуск (windows)
|
||||||
- скачайте мост со [страницы релизов](https://git.sati.ac/sati.ac/bridge/releases)
|
- скачайте мост со [страницы релизов](https://git.sati.ac/sati.ac/bridge/releases)
|
||||||
|
@ -23,10 +23,7 @@ func (a *antigateV2Api) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *antigateV2Api) Domains() []string {
|
func (a *antigateV2Api) Domains() []string {
|
||||||
return []string{
|
return []string{"api.anti-captcha.com"}
|
||||||
"api.anti-captcha.com",
|
|
||||||
"api.capsolver.com",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *antigateV2Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
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")
|
api.ctx.Logger.WithFields(logrus.Fields{"handler": api.Name(), "response": response}).Debug("response")
|
||||||
|
|
||||||
marshaled, _ := json.Marshal(response)
|
marshaled, _ := json.Marshal(response)
|
||||||
|
|
||||||
|
if _, ok := response.(*antigateError); ok {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
}
|
||||||
|
|
||||||
w.Write(marshaled)
|
w.Write(marshaled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
245
api/CapSolver.go
Normal file
245
api/CapSolver.go
Normal 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
|
||||||
|
}
|
@ -166,6 +166,7 @@ func NewApiServer(ctx *ApiContext) *ApiServer {
|
|||||||
newStatsApi(ctx),
|
newStatsApi(ctx),
|
||||||
newAntigateV2Api(ctx),
|
newAntigateV2Api(ctx),
|
||||||
newRuCaptchaApi(ctx),
|
newRuCaptchaApi(ctx),
|
||||||
|
newCapSolverApi(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
server := &ApiServer{
|
server := &ApiServer{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user