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 }