bridge/api/AntiGateV2.go

322 lines
8.2 KiB
Go
Raw Permalink Normal View History

2023-07-02 18:00:59 +02:00
package api
import (
"encoding/json"
2023-07-26 07:08:31 +02:00
"fmt"
2023-07-02 18:00:59 +02:00
"io"
"net/http"
"reflect"
"strconv"
"strings"
2023-07-02 18:00:59 +02:00
"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"}
2023-07-02 18:00:59 +02:00
}
func (a *antigateV2Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.mux.ServeHTTP(w, r)
}
type antigateError struct {
ErrorId uint32 `json:"errorId"`
2023-07-02 18:00:59 +02:00
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",
}
2023-07-26 07:08:31 +02:00
errorProxyConnectRefused = &antigateError{
ErrorId: 25,
ErrorCode: "ERROR_PROXY_CONNECT_REFUSED",
ErrorDescription: "Bad proxy",
}
2023-07-02 18:00:59 +02:00
)
func (a *antigateV2Api) getTaskResult(request struct {
TaskId any `json:"taskId"`
2023-07-02 18:00:59 +02:00
}) 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 errorBadRequest
}
taskId = uint32(parsed)
default:
return errorBadRequest
}
task := a.ctx.Registry.Get(taskId)
2023-07-02 18:00:59 +02:00
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
2023-07-13 18:38:05 +02:00
switch result := task.Result.(type) {
2023-07-02 18:00:59 +02:00
case *sati.ReCaptcha2Result:
response.Solution = struct {
GRecaptchaResponse string `json:"gRecaptchaResponse"`
2023-07-13 18:38:05 +02:00
}{result.Token}
2023-07-02 18:00:59 +02:00
case *sati.TurnstileResult:
response.Solution = struct {
Token string `json:"token"`
UserAgent string `json:"userAgent"`
2023-07-13 18:38:05 +02:00
}{result.Token, a.ctx.Config.AntiGateV2.TurnstileUserAgent}
case *sati.FunCaptchaResult:
response.Solution = struct {
Token string `json:"token"`
}{result.Token}
2023-07-02 18:00:59 +02:00
default:
return errorTaskNotSupported
}
}
return response
}
2023-07-26 07:08:31 +02:00
func collectAntiGateV2Proxy(task any) *string {
var proxy struct {
ProxyType string `json:"proxyType"`
ProxyAddress string `json:"proxyAddress"`
ProxyPort uint16 `json:"proxyPort"`
ProxyLogin *string `json:"proxyLogin"`
ProxyPassword *string `json:"proxyPassword"`
}
if err := mapstructure.Decode(task, proxy); err != nil {
return nil
}
if proxy.ProxyType == "" || proxy.ProxyAddress == "" || proxy.ProxyPort == 0 {
return nil
}
credentials := ""
if proxy.ProxyLogin != nil {
credentials += *proxy.ProxyLogin
if proxy.ProxyPassword != nil {
credentials += ":" + *proxy.ProxyPassword
}
credentials += "@"
}
proxyStr := fmt.Sprintf("%s://%s%s:%d", proxy.ProxyType, credentials, proxy.ProxyAddress, proxy.ProxyPort)
return &proxyStr
}
2023-07-02 18:00:59 +02:00
func (a *antigateV2Api) createTask(request struct {
Task map[string]any `json:"task"`
}) any {
taskType, ok := request.Task["type"].(string)
if !ok {
return errorTaskAbsent
}
2023-07-26 07:08:31 +02:00
taskType = strings.ToLower(taskType)
2023-07-02 18:00:59 +02:00
2023-07-26 07:08:31 +02:00
var satiTask sati.AnyTask
2023-07-02 18:00:59 +02:00
2023-07-26 07:08:31 +02:00
switch taskType {
case "turnstiletask", "turnstiletaskproxyless":
2023-07-02 18:00:59 +02:00
var task struct {
WebsiteURL string `json:"websiteURL"`
WebsiteKey string `json:"websiteKey"`
Action *string `json:"action"`
}
mapstructure.Decode(request.Task, &task)
2023-07-26 07:08:31 +02:00
satiTask = &sati.TurnstileTask{
2023-07-02 18:00:59 +02:00
PageUrl: task.WebsiteURL,
SiteKey: task.WebsiteKey,
Action: task.Action,
2023-07-26 07:08:31 +02:00
}
if taskType == "turnstiletask" {
proxy := collectAntiGateV2Proxy(request.Task)
if proxy == nil {
return errorProxyConnectRefused
}
satiTask.(*sati.TurnstileTask).Proxy = proxy
}
case "recaptchav2task", "recaptchav2taskproxyless":
2023-07-02 18:00:59 +02:00
var task struct {
WebsiteURL string `json:"websiteURL"`
WebsiteKey string `json:"websiteKey"`
}
mapstructure.Decode(request.Task, &task)
2023-07-26 07:08:31 +02:00
satiTask = &sati.ReCaptcha2Task{
2023-07-02 18:00:59 +02:00
PageUrl: task.WebsiteURL,
SiteKey: task.WebsiteKey,
2023-07-26 07:08:31 +02:00
}
if taskType == "funcaptchatask" {
proxy := collectAntiGateV2Proxy(request.Task)
if proxy == nil {
return errorProxyConnectRefused
}
satiTask.(*sati.ReCaptcha2Task).Proxy = proxy
}
case "funcaptchatask", "funcaptchataskproxyless":
2023-07-13 18:38:05 +02:00
var task struct {
WebsiteURL string `json:"websiteURL"`
WebsitePublicKey string `json:"websitePublicKey"`
Data map[string]string `json:"data"`
}
mapstructure.Decode(request.Task, &task)
2023-07-26 07:08:31 +02:00
satiTask = &sati.FunCaptchaTask{
2023-07-13 18:38:05 +02:00
PageUrl: task.WebsiteURL,
SiteKey: task.WebsitePublicKey,
Data: task.Data,
2023-07-26 07:08:31 +02:00
}
if taskType == "funcaptchatask" {
proxy := collectAntiGateV2Proxy(request.Task)
if proxy == nil {
return errorProxyConnectRefused
}
satiTask.(*sati.FunCaptchaTask).Proxy = proxy
}
2023-07-02 18:00:59 +02:00
default:
return errorTaskNotSupported
}
2023-07-26 07:08:31 +02:00
id := a.ctx.Registry.CreateTask(satiTask)
2023-07-02 18:00:59 +02:00
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](api *antigateV2Api, handler func(data T) any) func(http.ResponseWriter, *http.Request) {
2023-07-02 18:00:59 +02:00
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
}
api.ctx.Logger.WithFields(logrus.Fields{"handler": api.Name(), "request": request}).Debug("request")
2023-07-02 18:00:59 +02:00
response := handler(request)
api.ctx.Logger.WithFields(logrus.Fields{"handler": api.Name(), "response": response}).Debug("response")
2023-07-02 18:00:59 +02:00
marshaled, _ := json.Marshal(response)
if _, ok := response.(*antigateError); ok {
w.WriteHeader(400)
}
2023-07-02 18:00:59 +02:00
w.Write(marshaled)
}
}
func newAntigateV2Api(ctx *ApiContext) ApiHandler {
api := &antigateV2Api{ctx, http.NewServeMux()}
api.mux.HandleFunc("/createTask", jsonHandler(api, api.createTask))
api.mux.HandleFunc("/getTaskResult", jsonHandler(api, api.getTaskResult))
api.mux.HandleFunc("/getBalance", jsonHandler(api, api.getBalance))
2023-07-02 18:00:59 +02:00
return api
}