package flaresolverr import ( "encoding/json" "fmt" "net/http" "strings" "time" "resty.dev/v3" ) type instanceKey struct { url string sessionID string proxy string } type instanceOptions struct { sessionID string proxy string } type InstanceOption func(*instanceOptions) func WithSessionID(sessionID string) InstanceOption { return func(opts *instanceOptions) { opts.sessionID = sessionID } } func WithProxy(proxy string) InstanceOption { return func(opts *instanceOptions) { opts.proxy = proxy } } var instances map[instanceKey]*Flaresolverr = make(map[instanceKey]*Flaresolverr) type Flaresolverr struct { url string v1Url string sessionID string proxy string } func GetInstance(flaresolverrURL string, opts ...InstanceOption) (*Flaresolverr, error) { config := instanceOptions{} for _, opt := range opts { if opt != nil { opt(&config) } } key := instanceKey{ url: flaresolverrURL, sessionID: config.sessionID, proxy: config.proxy, } if instance, ok := instances[key]; ok { return instance, nil } flareSolverr := &Flaresolverr{ url: flaresolverrURL, v1Url: strings.TrimSuffix(flaresolverrURL, "/") + "/v1", sessionID: config.sessionID, proxy: config.proxy, } instances[key] = flareSolverr return flareSolverr, nil } func (f *Flaresolverr) requestV1(req *V1RequestBase) (*V1ResponseBase, error) { resp, err := NewRestyClient().R().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.Bytes(), &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(reqURL string, req *V1RequestBase) (*V1ResponseBase, error) { if req == nil { req = &V1RequestBase{ MaxTimeout: 60000, // 60 秒 } } req.Cmd = "request.get" req.URL = reqURL req.Session = f.sessionID if f.proxy != "" { req.Proxy.Url = f.proxy } return f.requestV1(req) } func (f *Flaresolverr) PostV1(reqURL string, req *V1RequestBase) (*V1ResponseBase, error) { if req == nil { req = &V1RequestBase{ MaxTimeout: 60000, // 60 秒 } } req.Cmd = "request.post" req.URL = reqURL req.Session = f.sessionID if f.proxy != "" { req.Proxy.Url = f.proxy } return f.requestV1(req) } func (f *Flaresolverr) CreateSessionV1(sessionID string) (*V1ResponseBase, error) { req := &V1RequestBase{ Session: sessionID, Cmd: "sessions.create", } if f.proxy != "" { req.Proxy.Url = f.proxy } 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 ConvertCookiesToStr(cookies []*http.Cookie) string { c := []string{} for _, cookie := range cookies { c = append(c, fmt.Sprintf("%s=%s", cookie.Name, cookie.Value)) } return strings.Join(c, "; ") } func (f *Flaresolverr) simulateRequest(method string, url string, ua string, cookies []*http.Cookie, body any) (*resty.Response, error) { var resp *resty.Response var err error c := NewRestyClient() if f.proxy != "" { c = c.SetProxy(f.proxy) } else { c.RemoveProxy() } req := c.R(). SetHeader("User-Agent", ua). SetHeader("Cookie", ConvertCookiesToStr(cookies)). SetHeader("Accept-Encoding", "gzip, deflate") switch strings.ToUpper(method) { case "GET": resp, err = req.Get(url) case "POST": resp, err = req.SetBody(body).Post(url) } if err != nil { return nil, err } return resp, nil } func (f *Flaresolverr) SimulateGet(url string, ua string, cookies []*http.Cookie) (*resty.Response, error) { return f.simulateRequest("GET", url, ua, cookies, nil) } func (f *Flaresolverr) SimulatePost(url string, ua string, cookies []*http.Cookie, body any) (*resty.Response, error) { return f.simulateRequest("POST", url, ua, cookies, body) }