Skip to content

Commit 0c796a1

Browse files
committed
Add IP info & flag emoji to test,add ip info column for main window
Fetch and display tested server IP and country (with emoji) in speed tests. Adds CountryExtension for country->emoji mapping and a new IpInfoResult type; ConnectionHandler now retrieves/parses IP API results and GetRealPingTime signature adjusted. Models and entities (ProfileItemModel, ProfileExItem, SpeedTestResult) gain IpInfo fields; ProfileExManager can store test IP info. UI/UX updated: new IpInfo column in ProfilesView (desktop and Avalonia), ResUI resource strings for "IP Info", and EServerColName ordering supports IpInfo. SpeedtestService now captures IP info and forwards it to the view model via the update function.
1 parent a9824fe commit 0c796a1

21 files changed

Lines changed: 218 additions & 24 deletions
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
namespace ServiceLib.Common;
2+
3+
/// <summary>
4+
/// Extension methods for country code utilities
5+
/// </summary>
6+
public static class CountryExtension
7+
{
8+
/// <summary>
9+
/// Country code to emoji flag mapping for common countries
10+
/// </summary>
11+
private static readonly Dictionary<string, string> CountryEmojiMap = new(StringComparer.OrdinalIgnoreCase)
12+
{
13+
// Asia
14+
{ "CN", "🇨🇳" }, // China
15+
{ "HK", "🇭🇰" }, // Hong Kong
16+
{ "TW", "🇹🇼" }, // Taiwan
17+
{ "JP", "🇯🇵" }, // Japan
18+
{ "SG", "🇸🇬" }, // Singapore
19+
{ "KR", "🇰🇷" }, // South Korea
20+
{ "TH", "🇹🇭" }, // Thailand
21+
{ "VN", "🇻🇳" }, // Vietnam
22+
{ "ID", "🇮🇩" }, // Indonesia
23+
{ "PH", "🇵🇭" }, // Philippines
24+
{ "MY", "🇲🇾" }, // Malaysia
25+
{ "IN", "🇮🇳" }, // India
26+
{ "PK", "🇵🇰" }, // Pakistan
27+
{ "BD", "🇧🇩" }, // Bangladesh
28+
{ "LK", "🇱🇰" }, // Sri Lanka
29+
{ "KH", "🇰🇭" }, // Cambodia
30+
{ "LA", "🇱🇦" }, // Laos
31+
{ "MM", "🇲🇲" }, // Myanmar
32+
33+
// Americas
34+
{ "US", "🇺🇸" }, // United States
35+
{ "CA", "🇨🇦" }, // Canada
36+
{ "MX", "🇲🇽" }, // Mexico
37+
{ "BR", "🇧🇷" }, // Brazil
38+
{ "AR", "🇦🇷" }, // Argentina
39+
{ "CL", "🇨🇱" }, // Chile
40+
{ "CO", "🇨🇴" }, // Colombia
41+
42+
// Europe
43+
{ "GB", "🇬🇧" }, // United Kingdom
44+
{ "DE", "🇩🇪" }, // Germany
45+
{ "FR", "🇫🇷" }, // France
46+
{ "IT", "🇮🇹" }, // Italy
47+
{ "ES", "🇪🇸" }, // Spain
48+
{ "RU", "🇷🇺" }, // Russia
49+
{ "NL", "🇳🇱" }, // Netherlands
50+
{ "CH", "🇨🇭" }, // Switzerland
51+
{ "SE", "🇸🇪" }, // Sweden
52+
{ "NO", "🇳🇴" }, // Norway
53+
{ "DK", "🇩🇰" }, // Denmark
54+
{ "FI", "🇫🇮" }, // Finland
55+
{ "PL", "🇵🇱" }, // Poland
56+
{ "CZ", "🇨🇿" }, // Czech Republic
57+
{ "AT", "🇦🇹" }, // Austria
58+
{ "GR", "🇬🇷" }, // Greece
59+
{ "PT", "🇵🇹" }, // Portugal
60+
{ "TR", "🇹🇷" }, // Turkey
61+
{ "UA", "🇺🇦" }, // Ukraine
62+
{ "RO", "🇷🇴" }, // Romania
63+
64+
// Middle East & Central Asia
65+
{ "AE", "🇦🇪" }, // United Arab Emirates
66+
{ "SA", "🇸🇦" }, // Saudi Arabia
67+
{ "IL", "🇮🇱" }, // Israel
68+
{ "KZ", "🇰🇿" }, // Kazakhstan
69+
70+
// Oceania
71+
{ "AU", "🇦🇺" }, // Australia
72+
{ "NZ", "🇳🇿" }, // New Zealand
73+
74+
// Africa
75+
{ "ZA", "🇿🇦" }, // South Africa
76+
{ "EG", "🇪🇬" }, // Egypt
77+
};
78+
79+
/// <summary>
80+
/// Converts country code to flag emoji using predefined mapping
81+
/// Example: "US" -> "🇺🇸", "CN" -> "🇨🇳"
82+
/// </summary>
83+
public static string? CountryToEmoji(this string? countryCode)
84+
{
85+
if (countryCode.IsNullOrEmpty())
86+
{
87+
return null;
88+
}
89+
90+
return CountryEmojiMap.TryGetValue(countryCode, out var emoji) ? emoji : null;
91+
}
92+
}

v2rayN/ServiceLib/Enums/EServerColName.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public enum EServerColName
1212
SubRemarks,
1313
DelayVal,
1414
SpeedVal,
15+
IpInfo,
1516

1617
TodayDown,
1718
TodayUp,

v2rayN/ServiceLib/Handler/ConfigHandler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,7 @@ from t33 in t3b.DefaultIfEmpty()
943943
EServerColName.StreamSecurity => lstProfile.OrderBy(t => t.StreamSecurity).ToList(),
944944
EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(),
945945
EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(),
946+
EServerColName.IpInfo => lstProfile.OrderBy(t => t.IpInfo).ToList(),
946947
EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(),
947948
EServerColName.TodayDown => lstProfile.OrderBy(t => t.TodayDown).ToList(),
948949
EServerColName.TodayUp => lstProfile.OrderBy(t => t.TodayUp).ToList(),
@@ -963,6 +964,7 @@ from t33 in t3b.DefaultIfEmpty()
963964
EServerColName.StreamSecurity => lstProfile.OrderByDescending(t => t.StreamSecurity).ToList(),
964965
EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(),
965966
EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(),
967+
EServerColName.IpInfo => lstProfile.OrderByDescending(t => t.IpInfo).ToList(),
966968
EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(),
967969
EServerColName.TodayDown => lstProfile.OrderByDescending(t => t.TodayDown).ToList(),
968970
EServerColName.TodayUp => lstProfile.OrderByDescending(t => t.TodayUp).ToList(),

v2rayN/ServiceLib/Handler/ConnectionHandler.cs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public static class ConnectionHandler
1010
public static async Task<string> RunAvailabilityCheck()
1111
{
1212
var time = await GetRealPingTimeInfo();
13-
var ip = time > 0 ? await GetIPInfo() ?? Global.None : Global.None;
13+
var ip = time > 0 ? await GetIPInfo() : Global.None;
1414

1515
return string.Format(ResUI.TestMeOutput, time, ip);
1616
}
@@ -21,7 +21,9 @@ public static async Task<string> RunAvailabilityCheck()
2121
private static async Task<string?> GetIPInfo()
2222
{
2323
var webProxy = await GetWebProxy();
24-
return await GetIPInfo(webProxy);
24+
25+
var ipInfo = await GetIPInfo(webProxy);
26+
return ipInfo?.ToString() ?? Global.None;
2527
}
2628

2729
/// <summary>
@@ -33,11 +35,10 @@ private static async Task<int> GetRealPingTimeInfo()
3335
try
3436
{
3537
var webProxy = await GetWebProxy();
36-
var url = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
3738

3839
for (var i = 0; i < 2; i++)
3940
{
40-
responseTime = await GetRealPingTime(url, webProxy, 10);
41+
responseTime = await GetRealPingTime(webProxy, 10);
4142
if (responseTime > 0)
4243
{
4344
break;
@@ -65,8 +66,9 @@ private static async Task<int> GetRealPingTimeInfo()
6566
/// <summary>
6667
/// Measures response time by sending HTTP requests through proxy.
6768
/// </summary>
68-
public static async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
69+
public static async Task<int> GetRealPingTime(IWebProxy? webProxy, int downloadTimeout)
6970
{
71+
var url = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
7072
var responseTime = -1;
7173
try
7274
{
@@ -98,30 +100,37 @@ public static async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, i
98100
/// <summary>
99101
/// Gets IP and country information through specified proxy.
100102
/// </summary>
101-
public static async Task<string?> GetIPInfo(IWebProxy? webProxy)
103+
public static async Task<IpInfoResult?> GetIPInfo(IWebProxy? webProxy)
102104
{
103-
var url = AppManager.Instance.Config.SpeedTestItem.IPAPIUrl;
104-
if (url.IsNullOrEmpty())
105+
try
105106
{
106-
return null;
107-
}
107+
var url = AppManager.Instance.Config.SpeedTestItem.IPAPIUrl;
108+
if (url.IsNullOrEmpty())
109+
{
110+
return null;
111+
}
108112

109-
var downloadHandle = new DownloadService();
110-
var result = await downloadHandle.TryDownloadString(url, webProxy, "");
111-
if (result == null)
112-
{
113-
return null;
114-
}
113+
var downloadHandle = new DownloadService();
114+
var result = await downloadHandle.TryDownloadString(url, webProxy, "");
115+
if (result == null)
116+
{
117+
return null;
118+
}
119+
120+
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
121+
if (ipInfo == null)
122+
{
123+
return null;
124+
}
125+
126+
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
127+
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code ?? "unknown";
115128

116-
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
117-
if (ipInfo == null)
129+
return new IpInfoResult(country, ip);
130+
}
131+
catch
118132
{
119133
return null;
120134
}
121-
122-
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
123-
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code;
124-
125-
return $"({country ?? "unknown"}) {ip}";
126135
}
127136
}

v2rayN/ServiceLib/Manager/ProfileExManager.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ public void SetTestMessage(string indexId, string message)
150150
IndexIdEnqueue(indexId);
151151
}
152152

153+
public void SetTestIpInfo(string indexId, string ipInfo)
154+
{
155+
var profileEx = GetProfileExItem(indexId);
156+
157+
profileEx.IpInfo = ipInfo;
158+
IndexIdEnqueue(indexId);
159+
}
160+
153161
public void SetSort(string indexId, int sort)
154162
{
155163
var profileEx = GetProfileExItem(indexId);

v2rayN/ServiceLib/Models/Dto/IPAPIInfo.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,12 @@ public class LocationInfo
1717
{
1818
public string? country_code { get; set; }
1919
}
20+
21+
public readonly record struct IpInfoResult(string Country, string? Ip)
22+
{
23+
public override string ToString()
24+
{
25+
var emoji = Country.CountryToEmoji();
26+
return $"{emoji}({Country}) {Ip}";
27+
}
28+
}

v2rayN/ServiceLib/Models/Dto/ProfileItemModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class ProfileItemModel : ReactiveObject
2626
[Reactive]
2727
public string SpeedVal { get; set; }
2828

29+
[Reactive]
30+
public string IpInfo { get; set; }
31+
2932
[Reactive]
3033
public string TodayUp { get; set; }
3134

v2rayN/ServiceLib/Models/Dto/SpeedTestResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ public class SpeedTestResult
88
public string? Delay { get; set; }
99

1010
public string? Speed { get; set; }
11+
12+
public string? IpInfo { get; set; }
1113
}

v2rayN/ServiceLib/Models/Entities/ProfileExItem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ public class ProfileExItem
1010
public decimal Speed { get; set; }
1111
public int Sort { get; set; }
1212
public string? Message { get; set; }
13+
public string? IpInfo { get; set; }
1314
}

v2rayN/ServiceLib/Resx/ResUI.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)