diff --git a/.gitignore b/.gitignore index 4416ca1..f00fc59 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ dist/ *.exe -.idea/* \ No newline at end of file +.idea/* +result.yaml \ No newline at end of file diff --git a/README.md b/README.md index 4f07bef..7d07eab 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ Usage of clash-speedtest: download concurrent size (default 4) -f string filter proxies by name, use regexp (default ".*") - -output string - output result to csv file + -output yaml / csv + output result to csv / yaml file -size int download size for testing proxies (default 104857600) -sort string @@ -61,6 +61,8 @@ TW-IEPL-01 109.34KB/s 73.00ms USA-GIA 14.42KB/s 688.00ms ``` +> 当您指定了 `--output yaml` 的时候,会自动将排序后的节点以完整配置输出,方便您编辑自己的节点文件 + ## 如何使用自定义服务器进行测速 ```shell diff --git a/main.go b/main.go index c25b5cd..0a6f3f5 100644 --- a/main.go +++ b/main.go @@ -32,10 +32,26 @@ var ( downloadSizeConfig = flag.Int("size", 1024*1024*100, "download size for testing proxies") timeoutConfig = flag.Duration("timeout", time.Second*5, "timeout for testing proxies") sortField = flag.String("sort", "b", "sort field for testing proxies, b for bandwidth, t for TTFB") - output = flag.String("output", "", "output result to csv file") + output = flag.String("output", "", "output result to csv/yaml file") concurrent = flag.Int("concurrent", 4, "download concurrent size") ) +type CProxy struct { + C.Proxy + SecretConfig any +} + +type Result struct { + Name string + Bandwidth float64 + TTFB time.Duration +} + +var ( + red = "\033[31m" + green = "\033[32m" +) + type RawConfig struct { Providers map[string]map[string]any `yaml:"proxy-providers"` Proxies []map[string]any `yaml:"proxies"` @@ -74,7 +90,7 @@ func main() { log.Fatalln("Please specify the configuration file") } - var allProxies = make(map[string]C.Proxy) + var allProxies = make(map[string]CProxy) arURL := strings.Split(*configPathConfig, ",") for _, v := range arURL { var u any @@ -155,12 +171,18 @@ func main() { } } - if *output != "" { - writeToCSV(*output, results) + if strings.EqualFold(*output, "yaml") { + if err := writeNodeConfigurationToYAML("result.yaml", results, allProxies); err != nil { + log.Fatalln("Failed to write yaml: %s", err) + } + } else if strings.EqualFold(*output, "csv") { + if err := writeToCSV("result.csv", results); err != nil { + log.Fatalln("Failed to write csv: %s", err) + } } } -func filterProxies(filter string, proxies map[string]C.Proxy) []string { +func filterProxies(filter string, proxies map[string]CProxy) []string { filterRegexp := regexp.MustCompile(filter) filteredProxies := make([]string, 0, len(proxies)) for name := range proxies { @@ -172,14 +194,14 @@ func filterProxies(filter string, proxies map[string]C.Proxy) []string { return filteredProxies } -func loadProxies(buf []byte) (map[string]C.Proxy, error) { +func loadProxies(buf []byte) (map[string]CProxy, error) { rawCfg := &RawConfig{ Proxies: []map[string]any{}, } if err := yaml.Unmarshal(buf, rawCfg); err != nil { return nil, err } - proxies := make(map[string]C.Proxy) + proxies := make(map[string]CProxy) proxiesConfig := rawCfg.Proxies providersConfig := rawCfg.Providers @@ -192,7 +214,7 @@ func loadProxies(buf []byte) (map[string]C.Proxy, error) { if _, exist := proxies[proxy.Name()]; exist { return nil, fmt.Errorf("proxy %s is the duplicate name", proxy.Name()) } - proxies[proxy.Name()] = proxy + proxies[proxy.Name()] = CProxy{Proxy: proxy, SecretConfig: config} } for name, config := range providersConfig { if name == provider.ReservedName { @@ -206,23 +228,12 @@ func loadProxies(buf []byte) (map[string]C.Proxy, error) { return nil, fmt.Errorf("initial proxy provider %s error: %w", pd.Name(), err) } for _, proxy := range pd.Proxies() { - proxies[fmt.Sprintf("[%s] %s", name, proxy.Name())] = proxy + proxies[fmt.Sprintf("[%s] %s", name, proxy.Name())] = CProxy{Proxy: proxy} } } return proxies, nil } -type Result struct { - Name string - Bandwidth float64 - TTFB time.Duration -} - -var ( - red = "\033[31m" - green = "\033[32m" -) - func (r *Result) Printf(format string) { color := "" if r.Bandwidth < 1024*1024 { @@ -230,7 +241,7 @@ func (r *Result) Printf(format string) { } else if r.Bandwidth > 1024*1024*10 { color = green } - fmt.Printf(format, color, formatName(r.Name), formatBandwidth(r.Bandwidth), formatMillseconds(r.TTFB)) + fmt.Printf(format, color, formatName(r.Name), formatBandwidth(r.Bandwidth), formatMilliseconds(r.TTFB)) } func TestProxyConcurrent(name string, proxy C.Proxy, downloadSize int, timeout time.Duration, concurrentCount int) *Result { @@ -290,7 +301,7 @@ func TestProxy(name string, proxy C.Proxy, downloadSize int, timeout time.Durati return &Result{name, -1, -1}, 0 } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { + if resp.StatusCode-http.StatusOK > 100 { return &Result{name, -1, -1}, 0 } ttfb := time.Since(start) @@ -339,13 +350,36 @@ func formatBandwidth(v float64) string { return fmt.Sprintf("%.02fTB/s", v) } -func formatMillseconds(v time.Duration) string { +func formatMilliseconds(v time.Duration) string { if v <= 0 { return "N/A" } return fmt.Sprintf("%.02fms", float64(v.Milliseconds())) } +func writeNodeConfigurationToYAML(filePath string, results []Result, proxies map[string]CProxy) error { + fp, err := os.Create(filePath) + if err != nil { + return err + } + defer fp.Close() + + var sortedProxies []any + for _, result := range results { + if v, ok := proxies[result.Name]; ok { + sortedProxies = append(sortedProxies, v.SecretConfig) + } + } + + bytes, err := yaml.Marshal(sortedProxies) + if err != nil { + return err + } + + _, err = fp.Write(bytes) + return err +} + func writeToCSV(filePath string, results []Result) error { csvFile, err := os.Create(filePath) if err != nil { @@ -367,7 +401,7 @@ func writeToCSV(filePath string, results []Result) error { fmt.Sprintf("%.2f", result.Bandwidth/1024/1024), strconv.FormatInt(result.TTFB.Milliseconds(), 10), } - err := csvWriter.Write(line) + err = csvWriter.Write(line) if err != nil { return err }