From a2cfe6fa5146781cb7f30fd4a28339d47cab58d4 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 11 May 2025 16:59:00 +0800 Subject: [PATCH] Added the current connection information test url option https://github.com/2dust/v2rayN/discussions/7268 --- v2rayN/ServiceLib/Global.cs | 12 +++++- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 2 +- .../ServiceLib/Handler/ConnectionHandler.cs | 42 +++++++++++++++++++ v2rayN/ServiceLib/Models/ConfigItems.cs | 1 + v2rayN/ServiceLib/Models/IPAPIInfo.cs | 13 ++++-- 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 | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 ++ v2rayN/ServiceLib/Services/UpdateService.cs | 15 ------- .../ViewModels/OptionSettingViewModel.cs | 5 ++- .../ViewModels/StatusBarViewModel.cs | 2 +- .../Views/OptionSettingWindow.axaml | 16 ++++++- .../Views/OptionSettingWindow.axaml.cs | 5 +++ v2rayN/v2rayN/Views/OptionSettingWindow.xaml | 17 ++++++++ .../v2rayN/Views/OptionSettingWindow.xaml.cs | 5 +++ 19 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 v2rayN/ServiceLib/Handler/ConnectionHandler.cs diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 78459920..68908b27 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -8,9 +8,7 @@ public class Global public const string GithubUrl = "https://github.com"; public const string GithubApiUrl = "https://api.github.com/repos"; public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; - public const string SpeedPingTestUrl = @"https://www.google.com/generate_204"; public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs"; - public const string IPAPIUrl = "https://api.ip.sb/geoip"; public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string ConfigFileName = "guiNConfig.json"; @@ -519,5 +517,15 @@ public class Global @"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb" ]; + public static readonly List IPAPIUrls = + [ + @"https://speed.cloudflare.com/meta", + @"https://api.ip.sb/geoip", + @"https://api-ipv4.ip.sb/geoip", + @"https://api-ipv6.ip.sb/geoip", + @"https://api.ipapi.is", + @"" + ]; + #endregion const } diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index a8bf06bb..6a25b56f 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -122,7 +122,7 @@ public class ConfigHandler } if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty()) { - config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl; + config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrls.First(); } if (config.SpeedTestItem.MixedConcurrencyCount < 1) { diff --git a/v2rayN/ServiceLib/Handler/ConnectionHandler.cs b/v2rayN/ServiceLib/Handler/ConnectionHandler.cs new file mode 100644 index 00000000..3cfc6020 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/ConnectionHandler.cs @@ -0,0 +1,42 @@ +namespace ServiceLib.Handler; + +public class ConnectionHandler +{ + private static readonly Lazy _instance = new(() => new()); + public static ConnectionHandler Instance => _instance.Value; + + public async Task RunAvailabilityCheck() + { + var downloadHandle = new DownloadService(); + var time = await downloadHandle.RunAvailabilityCheck(null); + var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None; + + return string.Format(ResUI.TestMeOutput, time, ip); + } + + private async Task GetIPInfo(DownloadService downloadHandle) + { + var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl; + if (url.IsNullOrEmpty()) + { + return null; + } + + var result = await downloadHandle.TryDownloadString(url, true, ""); + if (result == null) + { + return null; + } + + var ipInfo = JsonUtils.Deserialize(result); + if (ipInfo == null) + { + return null; + } + + var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query; + var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code; + + return $"({country ?? "unknown"}) {ip}"; + } +} diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 47c96a3f..afd7e7cc 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -156,6 +156,7 @@ public class SpeedTestItem public string SpeedTestUrl { get; set; } public string SpeedPingTestUrl { get; set; } public int MixedConcurrencyCount { get; set; } + public string IPAPIUrl { get; set; } } [Serializable] diff --git a/v2rayN/ServiceLib/Models/IPAPIInfo.cs b/v2rayN/ServiceLib/Models/IPAPIInfo.cs index 21063946..7ba16727 100644 --- a/v2rayN/ServiceLib/Models/IPAPIInfo.cs +++ b/v2rayN/ServiceLib/Models/IPAPIInfo.cs @@ -3,10 +3,17 @@ namespace ServiceLib.Models; internal class IPAPIInfo { public string? ip { get; set; } - public string? city { get; set; } - public string? region { get; set; } - public string? region_code { get; set; } + public string? clientIp { get; set; } + public string? ip_addr { get; set; } + public string? query { get; set; } public string? country { get; set; } public string? country_name { get; set; } public string? country_code { get; set; } + public string? countryCode { get; set; } + public LocationInfo? location { get; set; } +} + +public class LocationInfo +{ + public string? country_code { get; set; } } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index ac507c45..9ba7a90d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3201,6 +3201,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Current connection info test URL 的本地化字符串。 + /// + public static string TbSettingsIPAPIUrl { + get { + return ResourceManager.GetString("TbSettingsIPAPIUrl", resourceCulture); + } + } + /// /// 查找类似 Keep older entries when de-duplicating 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index adb95f2c..d10e18e7 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1416,4 +1416,7 @@ صادر کردن سرور - + + Current connection info test URL + + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 130c3579..4f8824b7 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1416,4 +1416,7 @@ Export server + + Current connection info test URL + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 7ec5d653..240aa55b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1416,4 +1416,7 @@ Export Configuration + + Current connection info test URL + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 9103641b..4468989f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1416,4 +1416,7 @@ Export server + + Current connection info test URL + \ 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 150a9353..97305242 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1413,4 +1413,7 @@ 导出配置文件 + + 当前连接信息测试地址 + \ 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 e827d0ea..35148566 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1413,4 +1413,7 @@ 匯出設定檔 + + 目前連接資訊測試地址 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/UpdateService.cs b/v2rayN/ServiceLib/Services/UpdateService.cs index 914bddc5..5c509519 100644 --- a/v2rayN/ServiceLib/Services/UpdateService.cs +++ b/v2rayN/ServiceLib/Services/UpdateService.cs @@ -243,21 +243,6 @@ public class UpdateService _updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo")); } - public async Task RunAvailabilityCheck() - { - var downloadHandle = new DownloadService(); - var time = await downloadHandle.RunAvailabilityCheck(null); - var ip = Global.None; - //if (time > 0) - //{ - // var result = await downloadHandle.TryDownloadString(Global.IPAPIUrl, true, Global.IPAPIUrl); - // var ipInfo = JsonUtils.Deserialize(result); - // ip = $"({ipInfo?.country_code}) {ipInfo?.ip}"; - //} - - return string.Format(ResUI.TestMeOutput, time, ip); - } - #region CheckUpdate private private async Task CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease) diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index fd26eb32..57d8cac7 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -70,6 +70,7 @@ public class OptionSettingViewModel : MyReactiveObject [Reactive] public string GeoFileSourceUrl { get; set; } [Reactive] public string SrsFileSourceUrl { get; set; } [Reactive] public string RoutingRulesSourceUrl { get; set; } + [Reactive] public string IPAPIUrl { get; set; } #endregion UI @@ -186,6 +187,7 @@ public class OptionSettingViewModel : MyReactiveObject GeoFileSourceUrl = _config.ConstItem.GeoSourceUrl; SrsFileSourceUrl = _config.ConstItem.SrsSourceUrl; RoutingRulesSourceUrl = _config.ConstItem.RouteRulesTemplateSourceUrl; + IPAPIUrl = _config.SpeedTestItem.IPAPIUrl; #endregion UI @@ -344,6 +346,7 @@ public class OptionSettingViewModel : MyReactiveObject _config.ConstItem.GeoSourceUrl = GeoFileSourceUrl; _config.ConstItem.SrsSourceUrl = SrsFileSourceUrl; _config.ConstItem.RouteRulesTemplateSourceUrl = RoutingRulesSourceUrl; + _config.SpeedTestItem.IPAPIUrl = IPAPIUrl; //systemProxy _config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions; @@ -356,7 +359,7 @@ public class OptionSettingViewModel : MyReactiveObject _config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; - + //coreType await SaveCoreType(); diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 2c48d0bf..82b48aff 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -320,7 +320,7 @@ public class StatusBarViewModel : MyReactiveObject var msg = await Task.Run(async () => { - return await (new UpdateService()).RunAvailabilityCheck(); + return await ConnectionHandler.Instance.RunAvailabilityCheck(); }); NoticeHandler.Instance.SendMessageEx(msg); diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 3ad9b178..004f68a4 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -343,7 +343,7 @@ + RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index 4faa0c27..fd319d92 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -92,6 +92,10 @@ public partial class OptionSettingWindow : ReactiveWindow + { + cmbIPAPIUrl.Items.Add(it); + }); foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation))) { cmbMainGirdOrientation.Items.Add(it.ToString()); @@ -143,6 +147,7 @@ public partial class OptionSettingWindow : ReactiveWindow vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 374dce35..f9b4b13c 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -552,6 +552,7 @@ + @@ -942,6 +943,22 @@ Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}" TextWrapping="Wrap" /> + + + diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index b0b5440c..ecdc39f0 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -101,6 +101,10 @@ public partial class OptionSettingWindow { cmbRoutingRulesSourceUrl.Items.Add(it); }); + Global.IPAPIUrls.ForEach(it => + { + cmbIPAPIUrl.Items.Add(it); + }); foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation))) { cmbMainGirdOrientation.Items.Add(it.ToString()); @@ -162,6 +166,7 @@ public partial class OptionSettingWindow this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables);