string inputFilePath = project.Variables["VPN_File_TXT"].Value;
string outputCsvPath = project.Variables["VPN_File_CSV"].Value;
// ====================================================================
// ШАГ 1: БЕЗОПАСНАЯ ЧИСТКА (Всего ~5 МБ в памяти, нет зависаний файлов)
// ====================================================================
if (System.IO.File.Exists(inputFilePath))
{
// Читаем весь файл разом. Это сразу освобождает файл от "занятости" процессом
string[] allLines = System.IO.File.ReadAllLines(inputFilePath);
var goodLines = new System.Collections.Generic.List<string>(allLines.Length);
foreach (string line in allLines)
{
string trimLine = line.Trim();
if (string.IsNullOrEmpty(trimLine)) continue;
// Отсеиваем всё, что не является нужным протоколом
if (!trimLine.StartsWith("vless://") &&
!trimLine.StartsWith("vmess://") &&
!trimLine.StartsWith("ss://") &&
!trimLine.StartsWith("trojan://"))
{
continue;
}
// Отсеиваем краш-строки (без шифрования и с бинарным мусором Outline)
if (trimLine.Contains("encryption=none") ||
trimLine.Contains("\\x") ||
trimLine.Contains("prefix=▬") ||
trimLine.Contains("¨"))
{
continue;
}
// Проверка на невидимые управляющие символы (ASCII < 32)
bool hasControlChars = false;
for (int i = 0; i < trimLine.Length; i++)
{
if (char.IsControl(trimLine[i]))
{
hasControlChars = true;
break;
}
}
if (hasControlChars) continue;
goodLines.Add(trimLine);
}
// Перезаписываем очищенный список
System.IO.File.WriteAllLines(inputFilePath, goodLines);
project.SendInfoToLog($"Очистка завершена. Осталось валидных строк: {goodLines.Count}");
}
// ====================================================================
// ШАГ 2: НАСТРОЙКА И ЗАПУСК XRAY-KNIFE
// ====================================================================
var path = project.ExecuteMacro(project.Path) + @"SYS\proxy.exe";
var arguments = " net http -f \"" + inputFilePath + "\" -x csv -o \"" + outputCsvPath + "\" -t " + project.Variables["Threads_Checks"].Value + " -r -d " + project.Variables["Max_Latency"].Value;
var startInfo = new System.Diagnostics.ProcessStartInfo(path);
startInfo.Arguments = arguments;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
// ВАЖНО! Отключаем RedirectStandardOutput, чтобы программа не зависала (Deadlock) из-за переполнения буфера
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardError = true; // Оставляем только ошибки, их мало
var process = System.Diagnostics.Process.Start(startInfo);
project.Variables["processId"].Value = process.Id.ToString();
// ====================================================================
// ШАГ 3: ОЖИДАНИЕ ЗАВЕРШЕНИЯ (Теперь без зависаний)
// ====================================================================
string errorLog = process.StandardError.ReadToEnd();
process.WaitForExit();
if (!string.IsNullOrEmpty(errorLog) && errorLog.Contains("Error"))
{
project.SendWarningToLog("xray-knife выдал ошибку: " + errorLog);
}
// ====================================================================
// ШАГ 4: ЧИСТКА ГОТОВОГО CSV ОТ МЕРТВЫХ ПРОКСИ
// ====================================================================
if (System.IO.File.Exists(outputCsvPath))
{
string[] csvLines = System.IO.File.ReadAllLines(outputCsvPath);
var validCsvLines = new System.Collections.Generic.List<string>(csvLines.Length);
for (int i = 0; i < csvLines.Length; i++)
{
string line = csvLines[i];
if (i == 0)
{
validCsvLines.Add(line);
continue;
}
if (line.Contains("-1") ||
line.IndexOf("error", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
line.IndexOf("timeout", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
line.IndexOf("failed", System.StringComparison.OrdinalIgnoreCase) >= 0)
{
continue;
}
validCsvLines.Add(line);
}
System.IO.File.WriteAllLines(outputCsvPath, validCsvLines);
project.SendInfoToLog($"Проверка завершена. Найдено рабочих прокси: {validCsvLines.Count - 1}");
}