feat: remove useless files
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user