Auto-fill empty node remarks during speed tests

This commit is contained in:
freekof 2026-03-02 22:46:57 +08:00
parent 8ebc168db8
commit 5d76a3e26e
3 changed files with 117 additions and 7 deletions

View file

@ -3,6 +3,11 @@ namespace ServiceLib.Handler;
public static class ConnectionHandler
{
private static readonly string _tag = "ConnectionHandler";
private static readonly string[] _speedtestIpApiUrls =
[
"https://api.ipapi.is",
"https://api.ip.sb/geoip"
];
public static async Task<string> RunAvailabilityCheck()
{
@ -20,23 +25,83 @@ public static class ConnectionHandler
return null;
}
var downloadHandle = new DownloadService();
var result = await downloadHandle.TryDownloadString(url, true, "");
if (result == null)
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{port}");
var ipInfo = await GetIpApiInfo(url, webProxy, 10);
return FormatCountryAndIp(ipInfo, false);
}
public static async Task<string?> GetCountryCodeAndIP(IWebProxy? webProxy, int downloadTimeout = 10)
{
foreach (var url in _speedtestIpApiUrls)
{
var ipInfo = await GetIpApiInfo(url, webProxy, downloadTimeout);
var compact = FormatCountryAndIp(ipInfo, true);
if (compact.IsNotEmpty())
{
return compact;
}
}
return null;
}
private static async Task<IPAPIInfo?> GetIpApiInfo(string url, IWebProxy? webProxy, int downloadTimeout)
{
if (url.IsNullOrEmpty())
{
return null;
}
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(downloadTimeout));
using var client = new HttpClient(new SocketsHttpHandler()
{
Proxy = webProxy,
UseProxy = webProxy != null
});
client.DefaultRequestHeaders.UserAgent.TryParseAdd(Utils.GetVersion(false));
using var response = await client.GetAsync(url, cts.Token).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
return null;
}
var result = await response.Content.ReadAsStringAsync(cts.Token).ConfigureAwait(false);
return JsonUtils.Deserialize<IPAPIInfo>(result);
}
catch
{
return null;
}
}
private static string? FormatCountryAndIp(IPAPIInfo? ipInfo, bool compact)
{
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;
var ip = (ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query)?.Trim();
if (ip.IsNullOrEmpty())
{
return null;
}
return $"({country ?? "unknown"}) {ip}";
var country = (ipInfo.country_code ?? ipInfo.countryCode ?? ipInfo.location?.country_code ?? ipInfo.country)?.Trim();
if (country.IsNullOrEmpty())
{
country = "unknown";
}
else
{
country = country.ToUpperInvariant();
}
return compact ? $"{country}{ip}" : $"({country}) {ip}";
}
private static async Task<int> GetRealPingTimeInfo()

View file

@ -8,5 +8,6 @@ public class ServerTestItem
public int Port { get; set; }
public EConfigType ConfigType { get; set; }
public bool AllowTest { get; set; }
public bool NeedAutoFillRemarks { get; set; }
public int QueueNum { get; set; }
}

View file

@ -6,13 +6,19 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
private readonly Config? _config = config;
private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc;
private static readonly ConcurrentBag<string> _lstExitLoop = new();
private int _remarksUpdated;
public void RunLoop(ESpeedActionType actionType, List<ProfileItem> selecteds)
{
Task.Run(async () =>
{
Interlocked.Exchange(ref _remarksUpdated, 0);
await RunAsync(actionType, selecteds);
await ProfileExManager.Instance.SaveTo();
if (Interlocked.CompareExchange(ref _remarksUpdated, 0, 0) > 0)
{
AppEvents.ProfilesRefreshRequested.Publish();
}
await UpdateFunc("", ResUI.SpeedtestingCompleted);
});
}
@ -80,6 +86,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
Address = it.Address,
Port = it.Port,
ConfigType = it.ConfigType,
NeedAutoFillRemarks = it.Remarks.IsNullOrEmpty(),
QueueNum = selecteds.IndexOf(it)
});
}
@ -297,11 +304,48 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var responseTime = await ConnectionHandler.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
if (responseTime > 0 && it.NeedAutoFillRemarks)
{
await TryAutoFillRemarks(it, webProxy);
}
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
await UpdateFunc(it.IndexId, responseTime.ToString());
return responseTime;
}
private async Task TryAutoFillRemarks(ServerTestItem it, IWebProxy webProxy)
{
if (!it.NeedAutoFillRemarks || it.IndexId.IsNullOrEmpty())
{
return;
}
var remarks = await ConnectionHandler.GetCountryCodeAndIP(webProxy, 8);
if (remarks.IsNullOrEmpty())
{
return;
}
var profileItem = await AppManager.Instance.GetProfileItem(it.IndexId);
if (profileItem == null)
{
return;
}
if (profileItem.Remarks.IsNotEmpty())
{
it.NeedAutoFillRemarks = false;
return;
}
profileItem.Remarks = remarks;
if (await SQLiteHelper.Instance.UpdateAsync(profileItem) > 0)
{
it.NeedAutoFillRemarks = false;
Interlocked.Increment(ref _remarksUpdated);
}
}
private async Task DoSpeedTest(DownloadService downloadHandle, ServerTestItem it)
{
await UpdateFunc(it.IndexId, "", ResUI.Speedtesting);