package proxy import ( "encoding/json" "fmt" "net/http" "time" "gpt-plus/config" ) // b2proxyResponse represents the JSON response from the B2Proxy API. type b2proxyResponse struct { Code int `json:"code"` Data []struct { IP string `json:"ip"` Port int `json:"port"` } `json:"data"` } // FetchB2Proxy calls the B2Proxy API to get a proxy IP for the given country. // Returns a proxy URL like "socks5://1.2.3.4:10000" or "http://1.2.3.4:10000". // Uses a direct HTTP client (no proxy) with a 10-second timeout. func FetchB2Proxy(cfg config.B2ProxyConfig, countryCode string) (string, error) { // Build the API URL url := fmt.Sprintf("%s/gen?zone=%s&ptype=%d&count=1&proto=%s&stype=json&sessType=sticky&sessTime=%d&sessAuto=1®ion=%s", cfg.APIBase, cfg.Zone, cfg.PType, cfg.Proto, cfg.SessTime, countryCode) // Use a direct HTTP client (must not go through proxy to fetch proxy) directClient := &http.Client{Timeout: 10 * time.Second} resp, err := directClient.Get(url) if err != nil { return "", fmt.Errorf("b2proxy API request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("b2proxy API returned status %d", resp.StatusCode) } var result b2proxyResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return "", fmt.Errorf("b2proxy API decode failed: %w", err) } if result.Code != 200 { return "", fmt.Errorf("b2proxy API error code: %d", result.Code) } if len(result.Data) == 0 { return "", fmt.Errorf("b2proxy API returned no proxy for region %s", countryCode) } entry := result.Data[0] proxyURL := fmt.Sprintf("%s://%s:%d", cfg.Proto, entry.IP, entry.Port) return proxyURL, nil }