215 lines
5.1 KiB
Go
215 lines
5.1 KiB
Go
package flaresolverr
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Danny-Dasilva/CycleTLS/cycletls"
|
|
)
|
|
|
|
var instances map[string]*Flaresolverr = make(map[string]*Flaresolverr)
|
|
|
|
type Flaresolverr struct {
|
|
url string
|
|
v1Url string
|
|
sessionID string
|
|
cycletlsC *cycletlsClient
|
|
proxy string
|
|
}
|
|
|
|
func GetInstance(URL string, sessionID string, proxy string) (*Flaresolverr, error) {
|
|
if instance, ok := instances[URL]; ok {
|
|
return instance, nil
|
|
}
|
|
flareSolverr := &Flaresolverr{
|
|
url: URL,
|
|
v1Url: strings.TrimSuffix(URL, "/") + "/v1",
|
|
sessionID: sessionID,
|
|
proxy: proxy,
|
|
}
|
|
instances[URL] = flareSolverr
|
|
return flareSolverr, nil
|
|
}
|
|
|
|
func (f *Flaresolverr) requestV1(req *V1RequestBase) (*V1ResponseBase, error) {
|
|
resp, err := request().SetBody(req).Post(f.v1Url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode() != 200 {
|
|
return nil, fmt.Errorf("request failed: status code: %v", resp.StatusCode())
|
|
}
|
|
var res V1ResponseBase
|
|
err = json.Unmarshal(resp.Body(), &res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if res.Solution != nil {
|
|
if res.Solution.RawCookies != nil {
|
|
for _, cookie := range res.Solution.RawCookies {
|
|
sec := int64(cookie.Expires)
|
|
nsec := int64((cookie.Expires - float64(sec)) * 1e9)
|
|
res.Solution.Cookies = append(res.Solution.Cookies, &http.Cookie{
|
|
Name: cookie.Name,
|
|
Value: cookie.Value,
|
|
Expires: time.Unix(sec, nsec),
|
|
Domain: cookie.Domain,
|
|
Path: cookie.Path,
|
|
Secure: cookie.Secure,
|
|
HttpOnly: cookie.HttpOnly,
|
|
})
|
|
}
|
|
}
|
|
|
|
if res.Solution.RawResponse != nil {
|
|
switch v := res.Solution.RawResponse.(type) {
|
|
case string:
|
|
res.Solution.Response = res.Solution.RawResponse.(string)
|
|
case map[string]any:
|
|
jsonBytes, err := json.Marshal(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res.Solution.Response = string(jsonBytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &res, nil
|
|
}
|
|
|
|
func (f *Flaresolverr) GetV1(URL string, req *V1RequestBase) (*V1ResponseBase, error) {
|
|
if req == nil {
|
|
req = &V1RequestBase{
|
|
MaxTimeout: 60000, // 60 秒
|
|
}
|
|
}
|
|
req.Cmd = "request.get"
|
|
req.URL = URL
|
|
req.Session = f.sessionID
|
|
|
|
return f.requestV1(req)
|
|
}
|
|
|
|
func (f *Flaresolverr) PostV1(URL string, req *V1RequestBase) (*V1ResponseBase, error) {
|
|
if req == nil {
|
|
req = &V1RequestBase{
|
|
MaxTimeout: 60000, // 60 秒
|
|
}
|
|
}
|
|
req.Cmd = "request.post"
|
|
req.URL = URL
|
|
req.Session = f.sessionID
|
|
|
|
return f.requestV1(req)
|
|
}
|
|
|
|
func (f *Flaresolverr) CreateSessionV1(sessionID string) (*V1ResponseBase, error) {
|
|
req := &V1RequestBase{
|
|
Session: sessionID,
|
|
Cmd: "sessions.create",
|
|
}
|
|
return f.requestV1(req)
|
|
}
|
|
|
|
func (f *Flaresolverr) DestroySessionV1(sessionID string) (*V1ResponseBase, error) {
|
|
req := &V1RequestBase{
|
|
Session: sessionID,
|
|
Cmd: "sessions.destroy",
|
|
}
|
|
return f.requestV1(req)
|
|
}
|
|
|
|
func (f *Flaresolverr) ListSessionsV1(sessionID string) (*V1ResponseBase, error) {
|
|
req := &V1RequestBase{
|
|
Session: sessionID,
|
|
Cmd: "sessions.list",
|
|
}
|
|
return f.requestV1(req)
|
|
}
|
|
|
|
func (f *Flaresolverr) GetJa3AndUserAgent() (ua string, ja3 string, err error) {
|
|
resp, err := f.GetV1("https://tls.peet.ws/api/tls", nil)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
return resp.Solution.UserAgent, resp.Solution.RawResponse.(map[string]any)["tls"].(map[string]any)["ja3"].(string), nil
|
|
}
|
|
|
|
func (f *Flaresolverr) preSimulateRequest(opts *SimulateOptions) error {
|
|
if f.cycletlsC == nil {
|
|
ua, ja3, err := f.GetJa3AndUserAgent()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.cycletlsC = NewCycletlsClient(ja3, ua)
|
|
}
|
|
opts.Cookies = ConvertToCycletlsCookies(opts.HttpCookies)
|
|
if f.proxy != "" {
|
|
opts.Proxy = f.proxy
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *Flaresolverr) SimulateGet(URL string, opts *SimulateOptions) (cycletls.Response, error) {
|
|
err := f.preSimulateRequest(opts)
|
|
if err != nil {
|
|
return cycletls.Response{}, err
|
|
}
|
|
|
|
return f.cycletlsC.Get(URL, &opts.Options)
|
|
}
|
|
|
|
func (f *Flaresolverr) SimulatePost(URL string, opts *SimulateOptions) (cycletls.Response, error) {
|
|
err := f.preSimulateRequest(opts)
|
|
if err != nil {
|
|
return cycletls.Response{}, err
|
|
}
|
|
|
|
return f.cycletlsC.Post(URL, &opts.Options)
|
|
}
|
|
|
|
type SimulateOptions struct {
|
|
cycletls.Options
|
|
HttpCookies []*http.Cookie
|
|
}
|
|
|
|
func ConvertToCycletlsCookies(cookies []*http.Cookie) []cycletls.Cookie {
|
|
var convertedCookies []cycletls.Cookie
|
|
for _, cookie := range cookies {
|
|
convertedCookies = append(convertedCookies, cycletls.Cookie{
|
|
Name: cookie.Name,
|
|
Value: cookie.Value,
|
|
Path: cookie.Path,
|
|
Domain: cookie.Domain,
|
|
Expires: cookie.Expires,
|
|
MaxAge: cookie.MaxAge,
|
|
Secure: cookie.Secure,
|
|
HTTPOnly: cookie.HttpOnly,
|
|
SameSite: cookie.SameSite,
|
|
})
|
|
}
|
|
return convertedCookies
|
|
}
|
|
|
|
func ConvertToCookies(cookies []cycletls.Cookie) []*http.Cookie {
|
|
var convertedCookies []*http.Cookie
|
|
for _, cookie := range cookies {
|
|
convertedCookies = append(convertedCookies, &http.Cookie{
|
|
Name: cookie.Name,
|
|
Value: cookie.Value,
|
|
Path: cookie.Path,
|
|
Domain: cookie.Domain,
|
|
Expires: cookie.Expires,
|
|
MaxAge: cookie.MaxAge,
|
|
Secure: cookie.Secure,
|
|
HttpOnly: cookie.HTTPOnly,
|
|
SameSite: cookie.SameSite,
|
|
})
|
|
}
|
|
return convertedCookies
|
|
}
|