namespace ServiceLib.Manager; public sealed class ClashApiManager { private const string _tag = "ClashApiManager"; private static readonly Lazy instance = new(() => new()); public static ClashApiManager Instance => instance.Value; private static string ApiUrl => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort2}"; public async Task GetProxies() { for (var i = 0; i < 3; i++) { var url = $"{ApiUrl}/proxies"; var resultTask = HttpClientHelper.Instance.TryGetAsync(url); var url2 = $"{ApiUrl}/providers/proxies"; var result2Task = HttpClientHelper.Instance.TryGetAsync(url2); await Task.WhenAll(resultTask, result2Task); var result = await resultTask; var result2 = await result2Task; var clashProxies = JsonUtils.Deserialize(result); var clashProviders = JsonUtils.Deserialize(result2); if (clashProxies is null && clashProviders is null) { await Task.Delay(2000); continue; } var item = new ClashItem(); if (clashProxies?.proxies is { Count: > 0 } proxies) { item = item with { Proxies = proxies }; } if (clashProviders.providers is { Count: > 0 } providers) { foreach (var provider in providers) { foreach (var proxy in provider.Value.proxies ?? []) { if (string.IsNullOrEmpty(proxy.name) || !item.Proxies.TryAdd(proxy.name, proxy)) { continue; } item.ProviderIndexMap.Add(proxy.name, provider.Key); } } } return item; } return null; } public async Task TestDelay(string name, ClashItem? clashItem) { if (clashItem?.ProviderIndexMap.TryGetValue(name, out var providerName) == true) { return await TestProviderProxyDelay(name, providerName); } return await TestProxyDelay(name); } private async Task TestProxyDelay(string name) { var url = $"{ApiUrl}/proxies/{Utils.UrlEncode(name)}/delay?timeout=10000&url=" + Utils.UrlEncode(AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl); return await SendTestRequest(url); } private async Task TestProviderProxyDelay(string name, string providerName) { var url = $"{ApiUrl}/providers/proxies/{Utils.UrlEncode(providerName)}/{Utils.UrlEncode(name)}/healthcheck?timeout=10000&url=" + Utils.UrlEncode(AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl); return await SendTestRequest(url); } private async Task SendTestRequest(string url) { var result = await HttpClientHelper.Instance.TryGetAsync(url); var jsonObject = JsonUtils.ParseJson(result) as JsonObject; return jsonObject?["delay"] is { } n && n.GetValueKind() == JsonValueKind.Number && n.GetValue().TryGetInt32(out var v) ? v : -1; } public async Task SetActiveProxy(string groupName, string nodeName) { try { var url = $"{ApiUrl}/proxies/{Utils.UrlEncode(groupName)}"; var headers = new Dictionary(); headers.Add("name", nodeName); await HttpClientHelper.Instance.PutAsync(url, headers); } catch (Exception ex) { Logging.SaveLog(_tag, ex); } } public async Task UpdateClashMode(string mode) { var headers = new Dictionary { { "mode", mode }, }; await UpdateConfig(headers); } public async Task UpdateConfig(Dictionary headers) { var urlBase = $"{ApiUrl}/configs"; await HttpClientHelper.Instance.PatchAsync(urlBase, headers); } public async Task> GetClashModes() { var jsonNode = await GetConfig(); if ((jsonNode?["mode-list"] ?? jsonNode?["modes"]) is not JsonArray { Count: > 0 } modesArray) { return []; } var modes = new List(); foreach (var mode in modesArray) { if (mode is JsonValue jsonValue && jsonValue.GetValueKind() == JsonValueKind.String) { modes.Add(jsonValue.GetValue()); } } return modes; } public async Task GetClashMode() { var jsonNode = await GetConfig(); if (jsonNode["mode"] is not JsonValue jsonValue || jsonValue.GetValueKind() != JsonValueKind.String) { return null; } return jsonValue.GetValue(); } public async Task GetConfig() { var url = $"{ApiUrl}/configs"; var result = await HttpClientHelper.Instance.TryGetAsync(url); var jsonNode = JsonUtils.ParseJson(result); if (jsonNode is not JsonObject jsonObject) { return new JsonObject(); } return jsonObject; } public async Task GetConnections() { try { var url = $"{ApiUrl}/connections"; var result = await HttpClientHelper.Instance.TryGetAsync(url); var clashConnections = JsonUtils.Deserialize(result); return clashConnections; } catch (Exception ex) { Logging.SaveLog(_tag, ex); } return null; } public async Task CloseConnection(string id) { try { var url = $"{ApiUrl}/connections/{id}"; await HttpClientHelper.Instance.DeleteAsync(url); } catch (Exception ex) { Logging.SaveLog(_tag, ex); } } }