From 88ddc9591c96bd5c84fd48a58ce51ff3c5bcbc6a Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 30 May 2026 13:50:17 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=B5=D0=BD=D0=B5=D0=B4=D0=B6=D0=B5=D1=80=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2/Added=20domain=20man?= =?UTF-8?q?ager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FluxRoute.Core/Services/ProfileBatLauncher.cs | 26 ++ FluxRoute.Core/Services/SettingsService.cs | 12 + FluxRoute/FluxRoute.csproj | 10 +- .../ViewModels/MainViewModel.Orchestrator.cs | 28 +- FluxRoute/ViewModels/MainViewModel.Process.cs | 1 + FluxRoute/ViewModels/MainViewModel.cs | 190 +++++++++++- FluxRoute/Views/MainWindow.xaml | 277 ++++++++++++++++-- 7 files changed, 505 insertions(+), 39 deletions(-) diff --git a/FluxRoute.Core/Services/ProfileBatLauncher.cs b/FluxRoute.Core/Services/ProfileBatLauncher.cs index d93df55..efa7965 100644 --- a/FluxRoute.Core/Services/ProfileBatLauncher.cs +++ b/FluxRoute.Core/Services/ProfileBatLauncher.cs @@ -84,6 +84,32 @@ public static bool TryCreateLaunchPlan( .Where(a => !string.IsNullOrWhiteSpace(a) && a != "^") .ToList(); + // ═══ ИНЪЕКЦИЯ ПОЛЬЗОВАТЕЛЬСКИХ ДОМЕНОВ ИЗ МЕНЕДЖЕРА ═══ + // Автоматически добавляем list-general-user.txt в аргументы winws.exe, + // чтобы домены из UI FluxRoute работали без ручного редактирования BAT-файлов. + var userHostlistPath = Path.Combine(engineDir, "lists", "list-general-user.txt"); + if (File.Exists(userHostlistPath) && new FileInfo(userHostlistPath).Length > 10) + { + bool hasUserList = args.Any(a => a.Contains("list-general-user", StringComparison.OrdinalIgnoreCase)); + if (!hasUserList) + { + args.Add("--hostlist"); + args.Add(userHostlistPath); // Передаем абсолютный путь + } + } + + var userExcludePath = Path.Combine(engineDir, "lists", "list-exclude-user.txt"); + if (File.Exists(userExcludePath) && new FileInfo(userExcludePath).Length > 10) + { + bool hasExclude = args.Any(a => a.Contains("list-exclude-user", StringComparison.OrdinalIgnoreCase)); + if (!hasExclude) + { + args.Add("--hostlist-exclude"); + args.Add(userExcludePath); + } + } + // ═══════════════════════════════════════════════════════ + if (args.Count == 0) { lastCandidateError = "В BAT найден winws.exe, но не найдены аргументы запуска."; diff --git a/FluxRoute.Core/Services/SettingsService.cs b/FluxRoute.Core/Services/SettingsService.cs index 3ca7efe..f0a1de0 100644 --- a/FluxRoute.Core/Services/SettingsService.cs +++ b/FluxRoute.Core/Services/SettingsService.cs @@ -28,6 +28,18 @@ public sealed class AppSettings // Пользовательские сайты для проверки public List UserSites { get; set; } = new(); + // ═══ НОВЫЕ СВОЙСТВА ДЛЯ МЕНЕДЖЕРА ДОМЕНОВ ═══ + /// + /// Список доменов для проверки (включения). + /// + public List CustomTargetDomains { get; set; } = new(); + + /// + /// Список доменов для исключения из проверки. + /// + public List CustomExcludeDomains { get; set; } = new(); + // ═══════════════════════════════════════════ + // Рейтинг профилей public List ProfileRatings { get; set; } = new(); diff --git a/FluxRoute/FluxRoute.csproj b/FluxRoute/FluxRoute.csproj index 560fe61..b81df26 100644 --- a/FluxRoute/FluxRoute.csproj +++ b/FluxRoute/FluxRoute.csproj @@ -37,11 +37,11 @@ - - - - - + + + + + diff --git a/FluxRoute/ViewModels/MainViewModel.Orchestrator.cs b/FluxRoute/ViewModels/MainViewModel.Orchestrator.cs index b9109c9..da43d0b 100644 --- a/FluxRoute/ViewModels/MainViewModel.Orchestrator.cs +++ b/FluxRoute/ViewModels/MainViewModel.Orchestrator.cs @@ -379,11 +379,31 @@ private void UpdateOrchestratorEnabledSites() _orchestrator.EnabledSites = sites; _aiOrchestrator.EnabledSites = sites; - var userTargets = UserCustomSitesText + var userTargets = new List(); + + // Берем домены из менеджера, ИСКЛЮЧАЯ те, что во вкладке "Исключения" + foreach (var domain in CustomTargetDomains) + { + if (!string.IsNullOrWhiteSpace(domain) && + !CustomExcludeDomains.Contains(domain, StringComparer.OrdinalIgnoreCase)) + { + userTargets.Add(BuildUserTargetEntry(domain)); + } + } + + // Fallback на старый TextBox + var legacyTargets = UserCustomSitesText .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - .Where(s => !string.IsNullOrWhiteSpace(s)) - .Select(s => BuildUserTargetEntry(s)) - .ToList(); + .Where(s => !string.IsNullOrWhiteSpace(s) && !s.StartsWith("!") && + !CustomExcludeDomains.Contains(s, StringComparer.OrdinalIgnoreCase)) + .Select(s => BuildUserTargetEntry(s)); + + foreach (var t in legacyTargets) + { + if (!userTargets.Any(x => x.Key == t.Key)) + userTargets.Add(t); + } + _orchestrator.UserSiteTargets = userTargets; _aiOrchestrator.UserSiteTargets = userTargets; } diff --git a/FluxRoute/ViewModels/MainViewModel.Process.cs b/FluxRoute/ViewModels/MainViewModel.Process.cs index 5601909..fcd79f7 100644 --- a/FluxRoute/ViewModels/MainViewModel.Process.cs +++ b/FluxRoute/ViewModels/MainViewModel.Process.cs @@ -109,6 +109,7 @@ private void Start() try { + SyncCustomHostlist(); ProfileBatLauncher.PrepareRuntime(EngineDir); if (ProfileBatLauncher.TryCreateLaunchPlan(SelectedProfile.FullPath, EngineDir, out var plan, out var parseError) && plan is not null) diff --git a/FluxRoute/ViewModels/MainViewModel.cs b/FluxRoute/ViewModels/MainViewModel.cs index 209fc11..812dc01 100644 --- a/FluxRoute/ViewModels/MainViewModel.cs +++ b/FluxRoute/ViewModels/MainViewModel.cs @@ -1,18 +1,19 @@ -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using System.Windows; -using System.Windows.Threading; -using Application = System.Windows.Application; +using FluxRoute.AI.Services; using FluxRoute.Core.Models; using FluxRoute.Core.Services; -using FluxRoute.AI.Services; using FluxRoute.Services; using FluxRoute.Updater.Services; using FluxRoute.Views; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Threading; +using Application = System.Windows.Application; namespace FluxRoute.ViewModels; @@ -29,10 +30,19 @@ public partial class MainViewModel : ObservableObject public ObservableCollection UpdateLogs => Updates.UpdateLogs; public ObservableCollection ServiceLogs => Service.ServiceLogs; + // Коллекции для менеджера доменов (UI) + // ── Пресеты конфигурации ── public ObservableCollection Presets { get; } = new(); public ObservableCollection RunningProcesses { get; } = new(); + // ── Менеджер доменов ── + [ObservableProperty] private string selectedTabMode = "Domains"; + [ObservableProperty] private string newSiteInput = ""; + + public ObservableCollection CustomTargetDomains { get; } = new(); + public ObservableCollection CustomExcludeDomains { get; } = new(); + [ObservableProperty] private string newPresetName = ""; [ObservableProperty] private string newPresetTrigger = ""; @@ -125,6 +135,79 @@ private void RemovePreset(ConfigPreset preset) SaveSettings(); } + // ── Команды менеджера доменов ── + [RelayCommand] + private void AddCustomDomain() + { + var input = NewSiteInput.Trim(); + if (string.IsNullOrEmpty(input)) return; + + // Нормализация: убираем протоколы и www + input = input.Replace("http://", "").Replace("https://", "").Replace("www.", "").TrimEnd('/'); + if (string.IsNullOrEmpty(input)) return; + + var list = SelectedTabMode == "Exclusions" ? CustomExcludeDomains : CustomTargetDomains; + + if (!list.Contains(input, StringComparer.OrdinalIgnoreCase)) + { + list.Add(input); + NewSiteInput = ""; + SaveSettings(); + SyncCustomHostlist(); + AddToRecentLogs($"✅ Добавлен домен: {input} ({(SelectedTabMode == "Exclusions" ? "исключение" : "проверка")})"); + } + else + { + NewSiteInput = ""; + } + } + + [RelayCommand] + private void RemoveCustomDomain(string domain) + { + if (string.IsNullOrEmpty(domain)) return; + var list = SelectedTabMode == "Exclusions" ? CustomExcludeDomains : CustomTargetDomains; + if (list.Contains(domain)) + { + list.Remove(domain); + SaveSettings(); + SyncCustomHostlist(); + AddToRecentLogs($"🗑 Удалён домен: {domain}"); + } + } + + [RelayCommand] + private void SetDomainsMode() + { + SelectedTabMode = "Domains"; + NewSiteInput = ""; + } + + [RelayCommand] + private void SetExclusionsMode() + { + SelectedTabMode = "Exclusions"; + NewSiteInput = ""; + } + + [RelayCommand] + private void ClearDomainList() + { + var list = SelectedTabMode == "Exclusions" ? CustomExcludeDomains : CustomTargetDomains; + if (list.Count == 0) return; + var modeName = SelectedTabMode == "Exclusions" ? "исключений" : "доменов"; + if (CustomDialog.Show( + "🗑 Очистить список", + $"Удалить все {list.Count} записей из списка {modeName}?", + "Очистить", "Отмена", isDanger: true)) + { + list.Clear(); + SaveSettings(); + SyncCustomHostlist(); + AddToRecentLogs($"🗑 Список {modeName} очищен"); + } + } + // ── События ── public event EventHandler? OpenSettingsRequested; public event EventHandler? OpenAboutRequested; @@ -203,6 +286,8 @@ partial void OnSelectedTabIndexChanged(int value) // ── Боковая панель ── [ObservableProperty] private bool isSidebarExpanded = true; + + [RelayCommand] private void ToggleSidebar() => IsSidebarExpanded = !IsSidebarExpanded; @@ -516,6 +601,29 @@ private void ApplySettings(AppSettings settings) SiteInstagram = settings.SiteInstagram; SiteTelegram = settings.SiteTelegram; UserCustomSitesText = string.Join("\n", settings.UserSites ?? new()); + + // Миграция старых данных в новые списки менеджера доменов + CustomTargetDomains.Clear(); + CustomExcludeDomains.Clear(); + + if (settings.CustomTargetDomains?.Count > 0) + { + foreach (var s in settings.CustomTargetDomains) CustomTargetDomains.Add(s); + } + else if (settings.UserSites?.Count > 0) + { + foreach (var s in settings.UserSites.Where(x => !x.StartsWith("!"))) CustomTargetDomains.Add(s); + } + + if (settings.CustomExcludeDomains?.Count > 0) + { + foreach (var s in settings.CustomExcludeDomains) CustomExcludeDomains.Add(s); + } + else if (settings.UserSites?.Count > 0) + { + foreach (var s in settings.UserSites.Where(x => x.StartsWith("!"))) CustomExcludeDomains.Add(s.TrimStart('!')); + } + AutoUpdateEnabled = settings.AutoUpdateEnabled; AutoStartEnabled = settings.AutoStartEnabled; MinimizeToTray = settings.MinimizeToTray; @@ -565,9 +673,11 @@ public void SaveSettings() SiteInstagram = SiteInstagram, SiteTelegram = SiteTelegram, UserSites = UserCustomSitesText - .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) - .Where(s => !string.IsNullOrWhiteSpace(s)) - .ToList(), + .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Where(s => !string.IsNullOrWhiteSpace(s)) + .ToList(), + CustomTargetDomains = CustomTargetDomains.ToList(), + CustomExcludeDomains = CustomExcludeDomains.ToList(), AutoUpdateEnabled = AutoUpdateEnabled, AutoStartEnabled = AutoStartEnabled, MinimizeToTray = MinimizeToTray, @@ -641,6 +751,7 @@ private void MainAction() else Start(); } + private void AddToRecentLogs(string message) { RecentLogs.Add(message); @@ -649,6 +760,63 @@ private void AddToRecentLogs(string message) LastStatusMessage = message; } + // ── Синхронизация пользовательских доменов с движком (winws.exe) ── + // ── Синхронизация пользовательских доменов с движком (winws.exe) ── + private void SyncCustomHostlist() + { + try + { + var listsDir = Path.Combine(EngineDir, "lists"); + Directory.CreateDirectory(listsDir); + var userHostlistPath = Path.Combine(listsDir, "list-general-user.txt"); + + var domains = new HashSet(StringComparer.OrdinalIgnoreCase); + + // 1. Берем домены из нового Менеджера доменов (вкладка "Домены") + foreach (var d in CustomTargetDomains) + { + if (!string.IsNullOrWhiteSpace(d)) + domains.Add(d.Trim()); + } + + // 2. Подхватываем из старого TextBox (для обратной совместимости) + if (!string.IsNullOrWhiteSpace(UserCustomSitesText)) + { + var legacy = UserCustomSitesText + .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Where(s => !s.StartsWith("!")); + foreach (var d in legacy) + domains.Add(d.Trim()); + } + + // Записываем в файл + if (domains.Count > 0) + { + // Используем явное удаление + запись, чтобы избежать кэширования + if (File.Exists(userHostlistPath)) + { + try { File.SetAttributes(userHostlistPath, FileAttributes.Normal); } catch { } + } + File.WriteAllLines(userHostlistPath, domains.OrderBy(x => x), new UTF8Encoding(false)); + Logs.Add($"[Sync] Записано {domains.Count} доменов в list-general-user.txt"); + } + else + { + // Пустой список — удаляем файл или пишем комментарий + if (File.Exists(userHostlistPath)) + File.Delete(userHostlistPath); + else + File.WriteAllText(userHostlistPath, "# custom domains empty\n", new UTF8Encoding(false)); + Logs.Add("[Sync] list-general-user.txt очищен"); + } + } + catch (Exception ex) + { + Logs.Add($"❌ Ошибка записи list-general-user.txt: {ex.Message}"); + Logs.Add($" Stack: {ex.StackTrace?.Split('\n')[0]}"); + } + } + // ── Cleanup ── public void Cleanup() diff --git a/FluxRoute/Views/MainWindow.xaml b/FluxRoute/Views/MainWindow.xaml index ad64c27..cc890c4 100644 --- a/FluxRoute/Views/MainWindow.xaml +++ b/FluxRoute/Views/MainWindow.xaml @@ -2189,7 +2189,7 @@ HorizontalAlignment="Right" d:IsLocked="True" /> - + @@ -2209,6 +2209,27 @@ FontSize="11" Foreground="#7A90B8" HorizontalAlignment="Right" d:IsLocked="True" /> + + + + + + + + + + + @@ -2595,24 +2616,242 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +