From 3e20ef1e8a6d813c518f36824613777b42f58614 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 10 Apr 2025 23:04:54 +0800 Subject: [PATCH] add anytls support --- v2rayN/ServiceLib/Enums/EConfigType.cs | 3 +- v2rayN/ServiceLib/Global.cs | 6 ++- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 31 +++++++++++ v2rayN/ServiceLib/Handler/CoreHandler.cs | 2 +- v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs | 54 +++++++++++++++++++ v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 5 ++ v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 9 ++++ v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 5 +- v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 11 ++-- v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 11 ++-- .../CoreConfig/CoreConfigSingboxService.cs | 5 ++ .../CoreConfig/CoreConfigV2rayService.cs | 6 ++- .../ServiceLib/Services/SpeedtestService.cs | 4 +- .../ViewModels/MainWindowViewModel.cs | 5 ++ .../Views/AddServerWindow.axaml | 20 +++++++ .../Views/AddServerWindow.axaml.cs | 9 ++++ v2rayN/v2rayN.Desktop/Views/MainWindow.axaml | 1 + .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 1 + v2rayN/v2rayN/Views/AddServerWindow.xaml | 29 ++++++++++ v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 8 +++ v2rayN/v2rayN/Views/MainWindow.xaml | 4 ++ v2rayN/v2rayN/Views/MainWindow.xaml.cs | 1 + 25 files changed, 222 insertions(+), 17 deletions(-) create mode 100644 v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs diff --git a/v2rayN/ServiceLib/Enums/EConfigType.cs b/v2rayN/ServiceLib/Enums/EConfigType.cs index f56d0e0f..6698f962 100644 --- a/v2rayN/ServiceLib/Enums/EConfigType.cs +++ b/v2rayN/ServiceLib/Enums/EConfigType.cs @@ -11,5 +11,6 @@ public enum EConfigType Hysteria2 = 7, TUIC = 8, WireGuard = 9, - HTTP = 10 + HTTP = 10, + Anytls = 11 } diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 78459920..fae23dc3 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -169,7 +169,8 @@ public class Global { EConfigType.Trojan, "trojan://" }, { EConfigType.Hysteria2, "hysteria2://" }, { EConfigType.TUIC, "tuic://" }, - { EConfigType.WireGuard, "wireguard://" } + { EConfigType.WireGuard, "wireguard://" }, + { EConfigType.Anytls, "anytls://" } }; public static readonly Dictionary ProtocolTypes = new() @@ -182,7 +183,8 @@ public class Global { EConfigType.Trojan, "trojan" }, { EConfigType.Hysteria2, "hysteria2" }, { EConfigType.TUIC, "tuic" }, - { EConfigType.WireGuard, "wireguard" } + { EConfigType.WireGuard, "wireguard" }, + { EConfigType.Anytls, "anytls" } }; public static readonly List VmessSecurities = diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index a23b1c67..52baab5f 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -259,6 +259,7 @@ public class ConfigHandler EConfigType.Hysteria2 => await AddHysteria2Server(config, item), EConfigType.TUIC => await AddTuicServer(config, item), EConfigType.WireGuard => await AddWireguardServer(config, item), + EConfigType.Anytls => await AddAnytlsServer(config, item), _ => -1, }; return ret; @@ -783,6 +784,35 @@ public class ConfigHandler return 0; } + /// + /// Add or edit a Anytls server + /// Validates and processes Anytls-specific settings + /// + /// Current configuration + /// Anytls profile to add + /// Whether to save to file + /// 0 if successful, -1 if failed + public static async Task AddAnytlsServer(Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.ConfigType = EConfigType.Anytls; + profileItem.CoreType = ECoreType.sing_box; + + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Network = string.Empty; + if (profileItem.StreamSecurity.IsNullOrEmpty()) + { + profileItem.StreamSecurity = Global.StreamSecurity; + } + if (profileItem.Id.IsNullOrEmpty()) + { + return -1; + } + await AddServerCommon(config, profileItem, toFile); + return 0; + } + /// /// Sort the server list by the specified column /// Updates the sort order in the profile extension data @@ -1277,6 +1307,7 @@ public class ConfigHandler EConfigType.Hysteria2 => await AddHysteria2Server(config, profileItem, false), EConfigType.TUIC => await AddTuicServer(config, profileItem, false), EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false), + EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false), _ => -1, }; diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 41ea866d..fb1d1dbb 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -101,7 +101,7 @@ public class CoreHandler public async Task LoadCoreConfigSpeedtest(List selecteds) { - var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) ? ECoreType.sing_box : ECoreType.Xray; + var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray; var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); var configPath = Utils.GetBinConfigPath(fileName); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); diff --git a/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs new file mode 100644 index 00000000..bad36b19 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs @@ -0,0 +1,54 @@ +using static QRCoder.PayloadGenerator; + +namespace ServiceLib.Handler.Fmt; +public class AnytlsFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + var parsedUrl = Utils.TryUri(str); + if (parsedUrl == null) + { + return null; + } + + ProfileItem item = new() + { + ConfigType = EConfigType.Anytls, + Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + Address = parsedUrl.IdnHost, + Port = parsedUrl.Port, + }; + var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); + item.Id = rawUserInfo; + + var query = Utils.ParseQueryString(parsedUrl.Query); + item.Sni = query["sni"] ?? Global.None; + item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var pw = item.Id; + var dicQuery = new Dictionary(); + if (item.Sni.IsNotEmpty()) + { + dicQuery.Add("sni", item.Sni); + } + dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0"); + + return ToUri(EConfigType.Anytls, item.Address, item.Port, pw, dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs index 3e8ab2ae..814d753d 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs @@ -18,6 +18,7 @@ public class FmtHandler EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item), EConfigType.TUIC => TuicFmt.ToUri(item), EConfigType.WireGuard => WireguardFmt.ToUri(item), + EConfigType.Anytls => AnytlsFmt.ToUri(item), _ => null, }; @@ -75,6 +76,10 @@ public class FmtHandler { return WireguardFmt.Resolve(str, out msg); } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Anytls])) + { + return AnytlsFmt.Resolve(str, out msg); + } else { msg = ResUI.NonvmessOrssProtocol; diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 0b9cc936..a90f81ab 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -672,6 +672,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Add [Anytls] Configuration 的本地化字符串。 + /// + public static string menuAddAnytlsServer { + get { + return ResourceManager.GetString("menuAddAnytlsServer", resourceCulture); + } + } + /// /// 查找类似 Add a custom configuration Configuration 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index a937a262..97c6d1e7 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1416,4 +1416,7 @@ صادر کردن سرور - + + Add [Anytls] Configuration + + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 05a12c10..0d1a60e4 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1416,4 +1416,7 @@ Export server + + Add [Anytls] Configuration + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 1fb3d23b..9d63ab68 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1416,4 +1416,7 @@ Export Configuration + + Add [Anytls] Configuration + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 824ea29a..11c1fba1 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1416,4 +1416,7 @@ Export server + + Add [Anytls] Configuration + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index a5f07957..660e9c4d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1071,13 +1071,13 @@ 拥塞控制算法 - + 前置代理配置文件别名 - + 落地代理配置文件別名 - + 请确保配置文件别名存在并唯一 @@ -1413,4 +1413,7 @@ 导出配置文件 - + + 添加 [Anytls] 配置文件 + + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index b8a5011a..250e9775 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1071,13 +1071,13 @@ 擁塞控制算法 - + 前置代理設定檔別名 - + 落地代理設定檔別名 - + 請確保設定檔別名存在並且唯一 @@ -1413,4 +1413,7 @@ 匯出設定檔 - + + 新增 [Anytls] 設定檔 + + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 66d05cf7..617718b7 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -759,6 +759,11 @@ public class CoreConfigSingboxService outbound.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); break; } + case EConfigType.Anytls: + { + outbound.password = node.Id; + break; + } } await GenOutboundTls(node, outbound); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 95511836..17621e4c 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -1291,7 +1291,8 @@ public class CoreConfigV2rayService if (prevNode is not null && prevNode.ConfigType != EConfigType.Custom && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC) + && prevNode.ConfigType != EConfigType.TUIC + && prevNode.ConfigType != EConfigType.Anytls) { var prevOutbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(prevNode, prevOutbound); @@ -1309,7 +1310,8 @@ public class CoreConfigV2rayService if (nextNode is not null && nextNode.ConfigType != EConfigType.Custom && nextNode.ConfigType != EConfigType.Hysteria2 - && nextNode.ConfigType != EConfigType.TUIC) + && nextNode.ConfigType != EConfigType.TUIC + && prevNode.ConfigType != EConfigType.Anytls) { var nextOutbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(nextNode, nextOutbound); diff --git a/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayN/ServiceLib/Services/SpeedtestService.cs index f4cd64a2..5ae9196d 100644 --- a/v2rayN/ServiceLib/Services/SpeedtestService.cs +++ b/v2rayN/ServiceLib/Services/SpeedtestService.cs @@ -357,8 +357,8 @@ public class SpeedtestService private List> GetTestBatchItem(List lstSelected, int pageSize) { List> lstTest = new(); - var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC)).ToList(); - var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC).ToList(); + var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)).ToList(); + var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls).ToList(); for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++) { diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index ec07b825..d27c6be5 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -20,6 +20,7 @@ public class MainWindowViewModel : MyReactiveObject public ReactiveCommand AddHysteria2ServerCmd { get; } public ReactiveCommand AddTuicServerCmd { get; } public ReactiveCommand AddWireguardServerCmd { get; } + public ReactiveCommand AddAnytlsServerCmd { get; } public ReactiveCommand AddCustomServerCmd { get; } public ReactiveCommand AddServerViaClipboardCmd { get; } public ReactiveCommand AddServerViaScanCmd { get; } @@ -111,6 +112,10 @@ public class MainWindowViewModel : MyReactiveObject { await AddServerAsync(true, EConfigType.WireGuard); }); + AddAnytlsServerCmd = ReactiveCommand.CreateFromTask(async () => + { + await AddServerAsync(true, EConfigType.Anytls); + }); AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () => { await AddServerAsync(true, EConfigType.Custom); diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index ef1885af..02c5dc6f 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -481,6 +481,26 @@ HorizontalAlignment="Left" Watermark="1500" /> + + + + + gridTls.IsVisible = false; break; + + case EConfigType.Anytls: + gridAnytls.IsVisible = true; + cmbCoreType.IsEnabled = false; + break; } gridTlsMore.IsVisible = false; @@ -193,6 +198,10 @@ public partial class AddServerWindow : ReactiveWindow this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); break; + + case EConfigType.Anytls: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); + break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml index bbc78a72..af4ae529 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml @@ -46,6 +46,7 @@ + diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 91328046..7c2dd397 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -83,6 +83,7 @@ public partial class MainWindow : ReactiveWindow this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index ba06cd3f..aae8d336 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -646,6 +646,35 @@ materialDesign:HintAssist.Hint="1500" Style="{StaticResource DefTextBox}" /> + + + + + + + + + + + + + + vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); break; + + case EConfigType.Anytls: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); + break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml b/v2rayN/v2rayN/Views/MainWindow.xaml index 6c233ef4..a439edd5 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml +++ b/v2rayN/v2rayN/Views/MainWindow.xaml @@ -107,6 +107,10 @@ x:Name="menuAddTuicServer" Height="{StaticResource MenuItemHeight}" Header="{x:Static resx:ResUI.menuAddTuicServer}" /> + diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index b837f65c..48ef5e44 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -80,6 +80,7 @@ public partial class MainWindow this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);