С чего всё началось
Когда Telegram начал серьёзно деградировать по скорости, а Discord стабильно висеть на «Подключение…“ — сообщество нашло рабочие решения: Zapret от bol‑van и TgWsProxy от Flowseal. Оба инструмента реально работают. Проблема одна — запустить их не так тривиально: батники, конфиги, права администратора, мониторинг процессов вручную.
Я захотел сделать что‑то, чем мог бы пользоваться любой человек — без консоли, без гугления, без «а он вообще запустился?». Так появился NetFix.
Важный момент сразу: NetFix не реализует никаких методов обхода сам. Это GUI‑оболочка, которая управляет запуском Zapret и TgWsProxy — готовых open‑source инструментов. Весь код NetFix открыт, никаких VPN‑соединений не создаётся.
Стек и архитектура
C# /.NET 8.0 / WPF — основной стек
XAML — весь UI
WinForms — только для трей‑иконки (
NotifyIcon)DiscordRichPresence 1.6.1.70 — единственная сторонняя NuGet‑зависимость
Намеренно минимальный набор зависимостей: встроенный HttpClient вместо RestSharp, System.Text.Json вместо Newtonsoft, MediaPlayer вместо NAudio. Меньше зависимостей — меньше поверхности для проблем.
Структура проекта:
NetFix/ ├── Models/ │ ├── DiagModels.cs # DcResult, PingResult, DiagReport, AppSettings │ ├── NoteMap.cs # модели ритм-игры │ └── ZapretConfig.cs # конфиги Zapret ├── Services/ │ ├── AutoDownloadService.cs # скачивание компонентов с GitHub Releases │ ├── DiagnosticsEngine.cs # сетевая диагностика │ ├── DiscordRpcService.cs # Discord Rich Presence │ ├── SettingsService.cs # сохранение настроек (JSON) │ ├── UpdateService.cs # автообновления │ ├── ZapretConfigService.cs # тест и применение конфигов │ └── ComponentVersionService.cs # версии установленных компонентов ├── Views/ │ ├── UpdateWindow.xaml/.cs │ └── ZapretConfigWindow.xaml/.cs └── MainWindow.xaml/.cs # основной экран + ритм-игра
Как работает главный экран
Весь пользовательский сценарий умещается в одно действие: нажать кнопку. Поэтому главный экран максимально минималистичен.

Главная кнопка — анимированная, с aurora‑эффектом (радиальный градиент, который плавно меняет цвет через DispatcherTimer + WPF Storyboard). У кнопки четыре состояния:
Idle — приложение готово к запуску
Loading — идёт запуск компонентов / диагностика
Active — всё работает (подтверждено сетевой проверкой)
Error — что‑то пошло не так
Рядом — два статусных индикатора (эллипсы): один для Zapret, другой для TgWsProxy. Меняют цвет в зависимости от состояния процесса.
По нажатии кнопки происходит следующее:
Проверяется наличие бинарей в папке приложения
Если компонентов нет —
AutoDownloadServiceскачивает их с официальных GitHub ReleasesЗапускаются процессы через
ProcessStartInfoсUseShellExecute = false,CreateNoWindow = trueDiagnosticsEngineделает TCP‑проверки к целевым сервисам и подтверждает что соединение работаетИндикаторы и состояние кнопки обновляются
При закрытии приложения все запущенные процессы корректно завершаются.

Сетевая диагностика
DiagnosticsEngine — отдельный сервис, который делает несколько типов проверок:
DC check — прямое TCP‑соединение к датацентрам Telegram
Ping — базовая проверка связи
DNS — резолвится ли домен
DPI check — проверка что Zapret реально помогает
UDP / throttle — дополнительные проверки для диагностики
Результаты собираются в DiagReport, на основе которого строится UI‑состояние. Это нужно чтобы «процесс запущен» и «всё работает» не были одним и тем же — Zapret может стартовать успешно, но нужный конфиг может не подойти для конкретного провайдера.

UPD - Система модификаций
С момента публикации в NetFix появилась система модов - возможность управлять доменными списками и bat-стратегиями прямо из UI, без блокнота и файлового менеджера.

Два типа модов
.bat Стратегии — пользователь добавляет свой батник обхода. ZapretConfigService.InstallModBat() копирует его в C:\Zapret\mod_{name}.bat, а InjectModConfig() подмешивает запись в кэш конфигов с флагом IsFromMod = true. В окне выбора конфига стратегия отображается с пометкой «мод» и тестируется наравне со стандартными, тот же ZapretConfigService, тот же пайплайн проверки.
Листы доменов - создаёшь именованный список сайтов в UI, активируешь стрелкой в колонку «Активные».

При активации вызывается ModActivator.ApplyListMods(). Метод реализует rebuild-merge: читает целевой файл (list-general.txt или любой другой), удаляет из него домены всех модов (в том числе неактивных), затем добавляет домены только активных. Это решает проблему «деактивировал мод — домены остались в файле» без хранения истории изменений.
csharp
// Удаляем домены ВСЕХ модов (даже неактивных) var allDomainSet = allModDomains.Values .SelectMany(d => d) .ToHashSet(StringComparer.OrdinalIgnoreCase); existing.ExceptWith(allDomainSet); // Добавляем только активные foreach (var mod in group.Where(m => m.IsActive)) foreach (var d in ReadModDomains(mod)) existing.Add(d); File.WriteAllLines(targetPath, existing.OrderBy(d => d));
StringComparer.OrdinalIgnoreCase - не InvariantCultureIgnoreCase, чтобы избежать проблем с турецким I и другими culture-зависимыми сравнениями. Домены регистронезависимы, но culture-neutral.

Каждый мод — папка в {AppDir}/Mods/lists/{name}/ с двумя файлами: mod.json (метаданные: название, автор, версия, описание) и list.txt (домены построчно). Экспортируется в .netfix-mod — обычный ZIP с белым списком файлов при распаковке, чтобы случайный мусор из архива не попал на диск.

Персистентность
Моды хранятся в папке NetFix, а не в C:\Zapret\. После обновления Zapret AutoDownloadService вызывает RefreshMods() → SyncActiveStrategyMods(), которая заново копирует активные .bat в C:\Zapret\ и переинжектит их в кэш конфигов. Список активных модов живёт в settings.json — он не затрагивается при обновлении компонентов.
Конфиги Zapret
Zapret поддерживает разные стратегии обхода DPI, и универсального решения нет — что работает у одного провайдера, может не работать у другого. Поэтому в NetFix есть отдельное окно выбора конфига.
ZapretConfigService прогоняет каждый .bat‑конфиг через реальную сетевую проверку и показывает результаты. Пользователь видит что сработало, что нет — и выбирает рабочий вариант. Результаты кэшируются в ZapretConfigCache чтобы не гонять проверки каждый раз.

Автозагрузка компонентов и обновления
AutoDownloadService при первом запуске (или когда компоненты устарели) скачивает бинари Zapret и TgWsProxy с их официальных GitHub Releases. Проверка идёт через GitHub API, скачивается нужная версия под архитектуру системы.
ComponentVersionService хранит версии установленных компонентов и сравнивает с актуальными. Если вышло обновление TgWsProxy — пользователь получит предложение обновить компонент, независимо от версии самого NetFix.
Обновления самого приложения обрабатываются через UpdateService — тоже GitHub Releases, с диалогом UpdateWindow где показывается changelog.

Настройки
Все настройки хранятся в settings.json рядом с exe-шником. Модель данных:
public class AppSettings { public string ZapretPath { get; set; } = ""; public string TgWsProxyPath { get; set; } = ""; public bool AutostartZapret { get; set; } = false; public bool AutostartTgWsProxy { get; set; } = false; public bool AutostartApp { get; set; } = false; public bool AutoUpdates { get; set; } = true; public bool ShowLongCheckDialog { get; set; } = true; }
Сериализация через System.Text.Json с JsonNamingPolicy.CamelCase. SettingsService умеет в экспорт/импорт — можно бэкапить конфигурацию или переносить между машинами.
Трей‑иконка (NotifyIcon через WinForms interop) даёт быстрый доступ к основным действиям без открытия главного окна.

Ритм‑игра внутри приложения
Это, наверное, самая неожиданная часть. Диагностика или первичная загрузка компонентов может занимать 10–30 секунд. Вместо спиннера я сделал мини‑игру в стиле osu!mania — вертикальные дорожки, падающие ноты.
Что реализовано:
4 дорожки, конфигурируемые keybindings (словарь
lane → Keyв настройках)Hold notes — ноты с удержанием, отдельная логика score и визуал
Импорт.osz — стандартный формат карт osu!. Файл
.oszэто обычный ZIP, внутри.osu(текстовый формат описания карты) и аудио. NetFix парсит.osu, конвертирует в внутренний форматNoteMap/NoteEntry, аудио воспроизводится через ffmpegВстроенные треки — упакованы в ZIP рядом с приложением (MP3 +
notes.json)Discord RPC — когда открыта игра, в статусе Discord показывается название карты
Игровой цикл построен на DispatcherTimer (30fps+). Нотные спрайты — Border элементы на Canvas, без пересоздания: используется пул объектов. Кисти заморожены (brush.Freeze()) и кэшированы вне цикла рендера.
Модель ноты:
public class NoteEntry { public double Time { get; set; } // секунды от начала трека public int Lane { get; set; } // 0–3 [JsonIgnore] public bool Hit { get; set; } [JsonIgnore] public Border? Visual { get; set; } [JsonIgnore] public DropShadowEffect? Effect { get; set; } }

Discord Rich Presence
DiscordRpcService показывает текущее состояние приложения в Discord‑статусе. Четыре состояния: главное меню, диагностика, игра (с названием карты и счётом), редактор карт.

Дизайн и визуальный стиль
Тёмная тема: фон #161616, акцентный цвет #3b82f6 (синий). Glassmorphism‑элементы, aurora‑анимация на главной кнопке. Весь UI — нативный WPF XAML, никаких сторонних UI‑фреймворков (MahApps, MaterialDesign и так далее — намеренно не использовались, чтобы не ломать стиль).
Итог
NetFix — это не очередной впн, не обфусцированный батник и не «доверься нам». Это прозрачная open‑source утилита, которая берёт два проверенных community‑инструмента (Zapret и TgWsProxy) и делает их запуск удобным для обычного пользователя.
Исходный код: github.com/rupleide/NetFix
Обновления и обсуждение: t.me/NetFixRuBi
