feat: remove useless files

This commit is contained in:
2026-06-23 13:55:14 +08:00
Unverified
parent 848836fb5f
commit aad84815cb
5 changed files with 80 additions and 49 deletions
+21 -18
View File
@@ -33,17 +33,17 @@ uv run xrayst
## Quick Start
Windows 下推荐直接使用 `run.bat`
Windows 下推荐直接使用 `run.ps1`
```powershell
.\run.bat "vless://你的节点"
powershell -ExecutionPolicy Bypass -File .\run.ps1 "vless://你的节点"
```
`run.bat` 只接受一个 VLESS 参数,并固定执行:
`run.ps1` 只接受一个 VLESS 参数,并固定执行:
- 第一阶段随机抽取 `3000` 个 IP其他参数使用 `auto` 默认值
- 第二阶段保留真实延迟前 `200` 个,其他参数使用 `auto` 默认值
- 第三阶段使用第二阶段全部结果测速,最终保留前 `10`,并发 `5`timeout `10s`
- 第一阶段随机抽取 `3000` 个 IP按 TCP 延迟保留前 `500` 个,并发 `200`
- 第二阶段读取第一阶段结果,保留真实延迟前 `50` 个,并发 `30`timeout `8s`
- 第三阶段读取第二阶段全部结果测速,最终保留前 `20`;默认下载 `10MB`,硬性总耗时上限 `10s`
完整流程:
@@ -65,8 +65,8 @@ uv run auto 'vless://你的节点' -r 1000 -p 10 -c 20 --speed-count 50
| 阶段 | 命令 | 输入 | 输出 |
| --- | --- | --- | --- |
| `ping` | `uv run auto --stage ping -r 1000 -c 20` | Cloudflare IPv4 段 | `txt/auto_01_ping_ips.txt` |
| `latency` | `uv run auto --stage latency 'vless://你的节点' -p 200 -c 20` | `txt/auto_01_ping_ips.txt` | `txt/auto_02_latency.txt` |
| `ping` | `uv run auto --stage ping -r 1000 -p 500 -c 20` | Cloudflare IPv4 段 | `txt/auto_01_ping_ips.txt` |
| `latency` | `uv run auto --stage latency 'vless://你的节点' -p 200 -c 20 --latency-timeout 8` | `txt/auto_01_ping_ips.txt` | `txt/auto_02_latency.txt` |
| `speed` | `uv run auto --stage speed 'vless://你的节点' --speed-count 50 -p 10` | `txt/auto_02_latency.txt` | `txt/result.txt` |
### Stage 1: Ping
@@ -74,13 +74,14 @@ uv run auto 'vless://你的节点' -r 1000 -p 10 -c 20 --speed-count 50
随机抽取 Cloudflare IP,并测试 TCP 443 是否可连通。
```powershell
uv run auto --stage ping -r 1000 -c 20
uv run auto --stage ping -r 1000 -p 500 -c 20
```
行为:
- 通过的 IP 会实时追加写入 `txt/auto_01_ping_ips.txt`
- 控制台会实时输出 `OK``FAIL`
- 阶段结束后会按 TCP 探测延迟升序排序,并只保留前 `-p` 个写回文件。
- 这一阶段不需要 VLESS 节点。
### Stage 2: Latency
@@ -88,7 +89,7 @@ uv run auto --stage ping -r 1000 -c 20
读取第一阶段输出,通过 Xray 临时代理测试真实 VLESS 链路延迟。
```powershell
uv run auto --stage latency 'vless://你的节点' -p 200 -c 20
uv run auto --stage latency 'vless://你的节点' -p 200 -c 20 --latency-timeout 8
```
配置:
@@ -98,7 +99,7 @@ uv run auto --stage latency 'vless://你的节点' -p 200 -c 20
| 输入文件 | `txt/auto_01_ping_ips.txt` |
| 输出文件 | `txt/auto_02_latency.txt` |
| 测试地址 | `https://www.google.com/generate_204` |
| Timeout | `3s` |
| Timeout | 默认 `3s`,可用 `--latency-timeout` 调整 |
| 排序规则 | 延迟低的优先 |
`-p` 控制这一阶段保留多少个结果。
@@ -119,7 +120,7 @@ uv run auto --stage speed 'vless://你的节点' --speed-count 50 -p 10 --speed-
| 输出文件 | `txt/result.txt` |
| 下载地址 | `https://cachefly.cachefly.net/50mb.test` |
| 实际读取 | `10MB` |
| Timeout | `10s` |
| Timeout | 默认 `10s`,是硬性总耗时上限 |
排序规则:
@@ -172,6 +173,8 @@ uv run auto [node] [options]
| `-r, --random-count` | `100` | 第一阶段随机抽取的 IP 数量 |
| `-p, --top` | `10` | 当前阶段保留数量;speed 阶段表示最终输出数量 |
| `-c, --concurrency` | `20` | ping 和 latency 阶段并发数 |
| `--node-file` | 无 | 从文件读取 VLESS 节点,适合避免命令行中的 `&` 被 shell 解析 |
| `--latency-timeout` | `3` | latency 阶段真实延迟测试 timeout 秒数 |
| `--speed-count` | `0` | speed 阶段取多少个延迟结果测试速度,`0` 表示全部 |
| `--speed-concurrency` | `1` | speed 阶段并发测速数量 |
| `--speed-timeout` | `10` | speed 阶段单个下载测速 timeout 秒数 |
@@ -180,20 +183,20 @@ uv run auto [node] [options]
| `--latency-output` | `txt/auto_02_latency.txt` | 第二阶段输出 |
| `-o, --output` | `txt/result.txt` | 最终节点输出 |
### run.bat
### run.ps1
Windows 批处理入口,用于固定执行推荐三阶段流程。
Windows PowerShell 入口,用于固定执行推荐三阶段流程。VLESS URL 包含 `&`,不建议再用 `.bat` 直接接收参数。
```powershell
.\run.bat "vless://你的节点"
powershell -ExecutionPolicy Bypass -File .\run.ps1 "vless://你的节点"
```
等价于依次执行:
```powershell
uv run auto --stage ping -r 3000
uv run auto --stage latency "vless://你的节点" -p 200
uv run auto --stage speed "vless://你的节点" --speed-count 0 -p 10 --speed-concurrency 5 --speed-timeout 10
uv run auto --stage ping -r 3000 -p 500 -c 200
uv run auto --stage latency --node-file "临时节点文件" -p 50 -c 30 --latency-timeout 8
uv run auto --stage speed --node-file "临时节点文件" --speed-count 0 -p 20
```
### cfst
+31 -10
View File
@@ -56,7 +56,7 @@ def with_alias(node_url: str, alias: str) -> str:
return urlunsplit((parts.scheme, parts.netloc, parts.path, parts.query, quote(alias)))
async def ping_candidates(random_count: int, concurrency: int, output_path: Path) -> list[str]:
async def ping_candidates(random_count: int, concurrency: int, keep_count: int, output_path: Path) -> list[str]:
networks = cfst.load_networks(cfst.DEFAULT_IP_FILE, "", not cfst.DEFAULT_IP_FILE.exists())
if not networks:
return []
@@ -73,19 +73,22 @@ async def ping_candidates(random_count: int, concurrency: int, output_path: Path
return await cfst.probe_ip(ip, "tcp", TARGET_PORT, 1, CF_TIMEOUT_MS, semaphore)
tasks = [asyncio.create_task(probe_one(ip)) for ip in candidates]
passed: list[str] = []
passed: list[cfst.ProbeResult] = []
with output_path.open("a", encoding="utf-8") as fp:
for task in asyncio.as_completed(tasks):
result = await task
if result.received > 0:
passed.append(result.ip)
passed.append(result)
fp.write(result.ip + "\n")
fp.flush()
console.print(f"OK {result.ip:<15}")
console.print(f"OK {result.ip:<15} {result.avg_latency_ms:>8.2f} ms")
else:
console.print(f"[dim]FAIL {result.ip:<15}[/dim]")
return passed
selected = sorted(passed, key=lambda item: item.avg_latency_ms)[:keep_count]
selected_ips = [result.ip for result in selected]
write_ip_list(output_path, selected_ips)
return selected_ips
def run_xray_proxy_measure(
@@ -124,6 +127,7 @@ def test_real_latency(
keep_count: int,
output_path: Path,
concurrency: int,
timeout_s: float,
) -> list[LatencyResult]:
results: list[LatencyResult] = []
with ThreadPoolExecutor(max_workers=max(1, concurrency)) as executor:
@@ -133,7 +137,7 @@ def test_real_latency(
node,
ip,
xray_path,
lambda port: xrayst.measure_latency(port, xrayst.DEFAULT_LATENCY_URL, XRAY_LATENCY_TIMEOUT_S),
lambda port: xrayst.measure_latency(port, xrayst.DEFAULT_LATENCY_URL, timeout_s),
): ip
for ip in ips
}
@@ -258,11 +262,13 @@ def print_results(results: list[xrayst.TestResult]) -> None:
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="One-command Cloudflare IP + xray real speed tester.")
parser.add_argument("node", nargs="?", help="Input vless:// node URL. Required for latency, speed, and all stages.")
parser.add_argument("--node-file", type=Path, help="Read input vless:// node URL from a file.")
parser.add_argument("--stage", choices=["all", "ping", "latency", "speed"], default="all", help="Run one stage or the full pipeline.")
parser.add_argument("-r", "--random-count", type=int, default=100, help="Random candidate IP count.")
parser.add_argument("-p", "--top", type=int, default=10, help="Final node count to keep.")
parser.add_argument("--speed-count", type=int, default=0, help="How many latency results to speed-test. 0 means all.")
parser.add_argument("-c", "--concurrency", type=int, default=20, help="Concurrent probes for ping and latency stages.")
parser.add_argument("--latency-timeout", type=float, default=XRAY_LATENCY_TIMEOUT_S, help="Latency test timeout seconds.")
parser.add_argument("--speed-concurrency", type=int, default=1, help="Concurrent speed tests.")
parser.add_argument("--speed-timeout", type=float, default=XRAY_DOWNLOAD_TIMEOUT_S, help="Download speed test timeout seconds.")
parser.add_argument("--xray", type=Path, default=xrayst.DEFAULT_XRAY, help="Path to xray executable.")
@@ -280,7 +286,14 @@ async def main() -> int:
random.seed(time.time_ns())
needs_node = args.stage in {"all", "latency", "speed"}
if needs_node and not args.node:
node_text = args.node
if args.node_file:
if not args.node_file.exists():
console.print(f"[red]Node file not found:[/red] {args.node_file}")
return 1
node_text = args.node_file.read_text(encoding="utf-8").strip().lstrip("\ufeff")
if needs_node and not node_text:
console.print("[red]VLESS node is required for this stage.[/red]")
return 1
@@ -291,7 +304,7 @@ async def main() -> int:
node: xrayst.VlessNode | None = None
if needs_node:
try:
node = xrayst.parse_vless(args.node)
node = xrayst.parse_vless(node_text)
except ValueError as exc:
console.print(f"[red]{exc}[/red]")
return 1
@@ -303,7 +316,7 @@ async def main() -> int:
if args.stage in {"all", "ping"}:
console.print("1/3 TCP probing random IPs...")
ips = await ping_candidates(args.random_count, args.concurrency, args.ping_output)
ips = await ping_candidates(args.random_count, args.concurrency, args.top, args.ping_output)
if not ips:
console.print("[red]No IP passed the TCP probe.[/red]")
return 1
@@ -318,7 +331,15 @@ async def main() -> int:
console.print(f"[red]No IPs found in {args.ping_output}.[/red]")
return 1
console.print(f"Loaded ping IPs: [cyan]{len(ips)}[/cyan] from [green]{args.ping_output}[/green]")
latency_results = test_real_latency(node, ips, args.xray, args.top, args.latency_output, args.concurrency)
latency_results = test_real_latency(
node,
ips,
args.xray,
args.top,
args.latency_output,
args.concurrency,
args.latency_timeout,
)
if not latency_results:
console.print("[red]No IP passed the real latency test.[/red]")
return 1
-20
View File
@@ -1,20 +0,0 @@
@echo off
setlocal EnableExtensions EnableDelayedExpansion
if "%~1"=="" (
echo Usage: run.bat "vless://..."
exit /b 1
)
set "NODE=%~1"
uv run auto --stage ping -r 3000
if errorlevel 1 exit /b %errorlevel%
uv run auto --stage latency "!NODE!" -p 200
if errorlevel 1 exit /b %errorlevel%
uv run auto --stage speed "!NODE!" --speed-count 0 -p 10 --speed-concurrency 5 --speed-timeout 10
if errorlevel 1 exit /b %errorlevel%
endlocal
+23
View File
@@ -0,0 +1,23 @@
param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$Node
)
$ErrorActionPreference = "Stop"
$nodeFile = Join-Path $env:TEMP "58_fast_vless_node.txt"
Set-Content -LiteralPath $nodeFile -Value $Node -NoNewline -Encoding UTF8
try {
uv run auto --stage ping -r 50 -p 500 -c 200
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
uv run auto --stage latency --node-file $nodeFile -p 50 -c 30
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
uv run auto --stage speed --node-file $nodeFile --speed-count 0 -p 20
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
finally {
Remove-Item -LiteralPath $nodeFile -Force -ErrorAction SilentlyContinue
}
+5 -1
View File
@@ -59,9 +59,10 @@ def parse_vless(url: str) -> VlessNode:
query = parse_qs(parsed.query)
if not parsed.username or not parsed.hostname:
raise ValueError("Invalid VLESS URL: missing uuid or host.")
original_host = query.get("host", query.get("sni", [parsed.hostname]))[0]
return VlessNode(
uuid=parsed.username,
original_host=parsed.hostname,
original_host=original_host,
original_port=parsed.port or 443,
remark=parsed.fragment,
encryption=query.get("encryption", ["none"])[0],
@@ -191,11 +192,14 @@ def measure_download(proxy_port: int, url: str, timeout_s: float, limit_bytes: i
proxy = f"http://127.0.0.1:{proxy_port}"
timeout = httpx.Timeout(timeout_s, connect=timeout_s)
started = time.perf_counter()
deadline = started + timeout_s
bytes_read = 0
with httpx.Client(proxy=proxy, timeout=timeout, follow_redirects=True) as client:
with client.stream("GET", url) as response:
response.raise_for_status()
for chunk in response.iter_bytes(chunk_size=65536):
if time.perf_counter() >= deadline:
break
if not chunk:
continue
bytes_read += len(chunk)