diff --git a/.back/create_public_branch.py b/.back/create_public_branch.py index 844b960b..b351f487 100755 --- a/.back/create_public_branch.py +++ b/.back/create_public_branch.py @@ -47,43 +47,104 @@ def modify_go_mod(filepath): print(f"✓ Removed privatespeedtest/security from {filepath}") +def remove_code_block(lines, start_marker, end_condition='empty_line'): + """ + Remove code block from lines starting with start_marker. + + Args: + lines: List of file lines + start_marker: String or list of strings to identify block start + end_condition: 'empty_line' (default) or 'closing_brace' or custom function + + Returns: + Modified lines with the block removed + """ + if isinstance(start_marker, str): + start_marker = [start_marker] + + result = [] + skip_mode = False + brace_depth = 0 + + i = 0 + while i < len(lines): + line = lines[i] + + # Check if we should start skipping + if not skip_mode: + for marker in start_marker: + if marker in line: + skip_mode = True + if end_condition == 'closing_brace': + # Count opening braces on the function declaration line + brace_depth = line.count('{') - line.count('}') + break + + if not skip_mode: + result.append(line) + else: + # We're in skip mode + if end_condition == 'empty_line': + # Skip until we find an empty line + if line.strip() == '': + skip_mode = False + # Don't add the empty line, continue to next + elif end_condition == 'closing_brace': + # Track brace depth + brace_depth += line.count('{') - line.count('}') + if brace_depth == 0 and '}' in line: + # Function ended, skip until next empty line + end_condition = 'empty_line' + + i += 1 + + return result + + def modify_speed_go(filepath): """ - Brutally remove privatespeedtest-related code from speed.go - by deleting known blocks from upstream source. + Remove privatespeedtest-related code from speed.go. + Uses line-by-line processing for reliability. """ content = read_file(filepath) - - content = re.sub( - r'\n\s*"time"\s*\n', - '\n', - content, - flags=re.MULTILINE - ) - + lines = content.split('\n') + + # Remove specific code blocks by their comment markers + blocks_to_remove = [ + '// formatString 格式化字符串到指定宽度', + '// printTableRow 打印表格行', + '// privateSpeedTest 使用 privatespeedtest 进行单个运营商测速', + '// privateSpeedTestWithFallback 使用私有测速,如果失败则回退到 global 节点', + '// 对于三网测速(cmcc、cu、ct)和 other,优先使用 privatespeedtest 进行私有测速', + ] + + for block_marker in blocks_to_remove: + lines = remove_code_block(lines, block_marker) + + # Reconstruct content + content = '\n'.join(lines) + + # Remove privatespeedtest import content = re.sub( r'\n\s*"github\.com/oneclickvirt/privatespeedtest/pst"\s*\n', '\n', content, flags=re.MULTILINE ) - + + # Remove time import (only used by privatespeedtest) content = re.sub( - r'// formatString 格式化字符串到指定宽度[\s\S]*?return nil\n}\n', - '', - content, - flags=re.MULTILINE - ) - - content = re.sub( - r'^[ \t]*// 对于三网测速(cmcc、cu、ct),优先使用 privatespeedtest 进行私有测速[\s\S]*?\n\s*\n', + r'\n\s*"time"\s*\n', '\n', content, flags=re.MULTILINE ) - + + # Clean up multiple consecutive empty lines (optional) + content = re.sub(r'\n{3,}', '\n\n', content) + write_file(filepath, content) - print(f"✓ Cleanly removed privatespeedtest from {filepath}") + print(f"✓ Removed privatespeedtest from {filepath}") def modify_utils_go(filepath): """ diff --git a/README_NEW_USER.md b/README_NEW_USER.md index be3783d3..708daac0 100644 --- a/README_NEW_USER.md +++ b/README_NEW_USER.md @@ -482,6 +482,8 @@ Abuser 或 Abuse 的滥用得分会直接影响机器的正常使用(中国境 先测的官方推荐的测速点,然后测有代表性的国际测速点,最后测国内三大运营商ping值最低的测速点。 +由于 speedtest.net 和 speedtest.cn 平台公开的测速节点被刷BTPT的刷烂了(他们为了对等上传PCDN的流量狂刷下载),所以这块本人独家融合的境内私有测速节点不再公开,优先使用私有的境内运营商测速节点进行测速,且写死限制每个IP每日仅支持获取测速数据10次,超限自动降级为使用公共测速节点进行测速 + 境内使用为主就看境内测速即可,境外使用看境外测速,官方测速点可以代表受测的宿主机本地带宽基准。 一般来说中国境外的服务器的带宽100Mbps起步,中国境内的服务器1Mbps带宽起步,具体看线路优劣,带宽特别大有时候未必用得上,够用就行了。 diff --git a/goecs.go b/goecs.go index aaa53816..72302324 100644 --- a/goecs.go +++ b/goecs.go @@ -27,7 +27,7 @@ import ( ) var ( - ecsVersion = "v0.1.108" // 融合怪版本号 + ecsVersion = "v0.1.109" // 融合怪版本号 configs = params.NewConfig(ecsVersion) // 全局配置实例 userSetFlags = make(map[string]bool) // 用于跟踪哪些参数是用户显式设置的 ) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 4722494e..c26445e1 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -348,7 +348,17 @@ func RunSpeedTests(config *params.Config, output, tempOutput string, outputMutex tests.CustomSP("net", "ct", config.SpNum, config.Language) tests.CustomSP("net", "cmcc", config.SpNum, config.Language) } else if config.Choice == "2" || config.Choice == "3" || config.Choice == "4" || config.Choice == "5" { - tests.CustomSP("net", "global", 4, config.Language) + // 中文模式:就近测速 + 三网各1个 + Other 1个(带回退) + if config.Language == "zh" { + tests.NearbySP() + tests.CustomSP("net", "other", 1, config.Language) + tests.CustomSP("net", "cu", 1, config.Language) + tests.CustomSP("net", "ct", 1, config.Language) + tests.CustomSP("net", "cmcc", 1, config.Language) + } else { + // 英文模式:保持原有逻辑,测4个global节点 + tests.CustomSP("net", "global", 4, config.Language) + } } else if config.Choice == "6" { tests.CustomSP("net", "global", 11, config.Language) } @@ -442,7 +452,7 @@ func HandleSignalInterrupt(sig chan os.Signal, config *params.Config, startTime // 使用context来控制上传goroutine uploadCtx, uploadCancel := context.WithTimeout(context.Background(), 30*time.Second) defer uploadCancel() - + go func() { httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, config.FilePath, config.EnableUpload) select { @@ -455,7 +465,7 @@ func HandleSignalInterrupt(sig chan os.Signal, config *params.Config, startTime return } }() - + select { case result := <-resultChan: uploadCancel() // 成功完成,取消context diff --git a/internal/tests/speed.go b/internal/tests/speed.go index f0570cbf..c799f95a 100644 --- a/internal/tests/speed.go +++ b/internal/tests/speed.go @@ -69,7 +69,7 @@ func printTableRow(result pst.SpeedTestResult) { } latency := fmt.Sprintf("%.2f ms", result.PingLatency.Seconds()*1000) packetLoss := "N/A" - fmt.Print(formatString(location, 16)) + fmt.Print(formatString(location, 15)) fmt.Print(formatString(upload, 16)) fmt.Print(formatString(download, 16)) fmt.Print(formatString(latency, 16)) @@ -78,25 +78,26 @@ func printTableRow(result pst.SpeedTestResult) { } // privateSpeedTest 使用 privatespeedtest 进行单个运营商测速 -// operator 参数:只支持 "cmcc"、"cu"、"ct" -func privateSpeedTest(num int, operator string) error { +// operator 参数:只支持 "cmcc"、"cu"、"ct"、"other" +// 返回值:实际测试的节点数量和错误信息 +func privateSpeedTest(num int, operator string) (int, error) { defer func() { if r := recover(); r != nil { fmt.Fprintf(os.Stderr, "[WARN] privateSpeedTest panic: %v\n", r) } }() *pst.NoProgress = true - *pst.Quiet = true - *pst.NoHeader = true - *pst.NoProjectURL = true + *pst.Quiet = true + *pst.NoHeader = true + *pst.NoProjectURL = true // 加载服务器列表 serverList, err := pst.LoadServerList() if err != nil { - return fmt.Errorf("加载自定义服务器列表失败") + return 0, fmt.Errorf("加载自定义服务器列表失败") } // 使用三网测速模式(每个运营商选择指定数量的最低延迟节点) serversPerISP := num - if serversPerISP <= 0 || serversPerISP > 5{ + if serversPerISP <= 0 || serversPerISP > 5 { serversPerISP = 2 } // 单个运营商测速:先过滤服务器列表 @@ -108,8 +109,10 @@ func privateSpeedTest(num int, operator string) error { carrierType = "Unicom" case "ct": carrierType = "Telecom" + case "other": + carrierType = "Other" default: - return fmt.Errorf("不支持的运营商类型: %s", operator) + return 0, fmt.Errorf("不支持的运营商类型: %s", operator) } // 过滤出指定运营商的服务器 filteredServers := pst.FilterServersByISP(serverList.Servers, carrierType) @@ -121,13 +124,13 @@ func privateSpeedTest(num int, operator string) error { // 使用 FindBestServers 选择最佳服务器 candidateServers, err := pst.FindBestServers( filteredServers, - candidateCount, // 选择更多候选节点用于去重 - 5*time.Second, // ping 超时 - true, // 显示进度条 - true, // 静默 + candidateCount, // 选择更多候选节点用于去重 + 5*time.Second, // ping 超时 + true, // 显示进度条 + true, // 静默 ) if err != nil { - return fmt.Errorf("分组查找失败") + return 0, fmt.Errorf("分组查找失败") } // 去重:确保同一运营商内城市不重复 seenCities := make(map[string]bool) @@ -147,18 +150,18 @@ func privateSpeedTest(num int, operator string) error { } } if len(bestServers) == 0 { - return fmt.Errorf("去重后没有可用的服务器") + return 0, fmt.Errorf("去重后没有可用的服务器") } // 执行测速并逐个打印结果(不打印表头) for i, serverInfo := range bestServers { result := pst.RunSpeedTest( serverInfo.Server, - false, // 不禁用下载测试 - false, // 不禁用上传测试 - 6, // 并发线程数 - 12*time.Second, // 超时时间 + false, // 不禁用下载测试 + false, // 不禁用上传测试 + 6, // 并发线程数 + 12*time.Second, // 超时时间 &serverInfo, - false, // 不显示进度条 + false, // 不显示进度条 ) if result.Success { printTableRow(result) @@ -168,7 +171,31 @@ func privateSpeedTest(num int, operator string) error { time.Sleep(1 * time.Second) } } - return nil + // 返回实际测试的节点数量 + return len(bestServers), nil +} + +// privateSpeedTestWithFallback 使用私有测速,如果失败则回退到 global 节点 +// 主要用于 Other 类型的测速 +func privateSpeedTestWithFallback(num int, operator, language string) { + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "[WARN] privateSpeedTestWithFallback panic: %v\n", r) + } + }() + // 先尝试私有节点测速 + testedCount, err := privateSpeedTest(num, operator) + if err != nil || testedCount == 0 { + // 私有节点失败,回退到 global 节点 + var url, parseType string + url = model.NetGlobal + parseType = "id" + if runtime.GOOS == "windows" || sp.OfficialAvailableTest() != nil { + sp.CustomSpeedTest(url, parseType, num, language) + } else { + sp.OfficialCustomSpeedTest(url, parseType, num, language) + } + } } func CustomSP(platform, operator string, num int, language string) { @@ -177,16 +204,23 @@ func CustomSP(platform, operator string, num int, language string) { fmt.Fprintf(os.Stderr, "[WARN] CustomSP panic: %v\n", r) } }() - // 对于三网测速(cmcc、cu、ct),优先使用 privatespeedtest 进行私有测速 + // 对于三网测速(cmcc、cu、ct)和 other,优先使用 privatespeedtest 进行私有测速 opLower := strings.ToLower(operator) - if opLower == "cmcc" || opLower == "cu" || opLower == "ct" { - err := privateSpeedTest(num, opLower) + if opLower == "cmcc" || opLower == "cu" || opLower == "ct" || opLower == "other" { + testedCount, err := privateSpeedTest(num, opLower) if err != nil { - fmt.Fprintf(os.Stderr, "[WARN] privatespeedtest failed\n") - // 继续使用原有的兜底方案 - } else { - // 测速成功,直接返回 + fmt.Fprintf(os.Stderr, "[WARN] privatespeedtest failed: %v\n", err) + // 全部失败,继续使用原有的公共节点兜底方案 + } else if testedCount >= num { + // 私有节点测速成功且数量达标,直接返回 return + } else if testedCount > 0 { + // 部分私有节点测速成功,但数量不足,用公共节点补充 + fmt.Fprintf(os.Stderr, "[INFO] 私有节点仅测试了 %d 个,补充 %d 个公共节点\n", testedCount, num-testedCount) + num = num - testedCount // 只测剩余数量的公共节点 + // 继续执行下面的公共节点测速逻辑 + } else { + // testedCount == 0,继续使用公共节点 } } @@ -223,7 +257,8 @@ func CustomSP(platform, operator string, num int, language string) { url = model.NetJP } else if strings.ToLower(operator) == "sg" { url = model.NetSG - } else if strings.ToLower(operator) == "global" { + } else if strings.ToLower(operator) == "global" || strings.ToLower(operator) == "other" { + // other 类型回退到 global 节点 url = model.NetGlobal } parseType = "id"