mirror of
https://github.com/2dust/v2rayN.git
synced 2026-10-17 02:35:00 +03:00
Adjust Clash API (#10088)
This commit is contained in:
@@ -1,113 +1,104 @@
|
||||
using static ServiceLib.Models.Dto.ClashProxies;
|
||||
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public sealed class ClashApiManager
|
||||
{
|
||||
private const string _tag = "ClashApiManager";
|
||||
private static readonly Lazy<ClashApiManager> instance = new(() => new());
|
||||
public static ClashApiManager Instance => instance.Value;
|
||||
|
||||
private static readonly string _tag = "ClashApiHandler";
|
||||
private Dictionary<string, ProxiesItem>? _proxies;
|
||||
public Dictionary<string, object> ProfileContent { get; set; }
|
||||
private static string ApiUrl => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
|
||||
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync()
|
||||
public async Task<ClashItem?> GetProxies()
|
||||
{
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var url = $"{GetApiUrl()}/proxies";
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);
|
||||
var url = $"{ApiUrl}/proxies";
|
||||
var resultTask = HttpClientHelper.Instance.TryGetAsync(url);
|
||||
|
||||
var url2 = $"{GetApiUrl()}/providers/proxies";
|
||||
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
|
||||
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<ClashProxies>(result);
|
||||
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);
|
||||
|
||||
if (clashProxies != null || clashProviders != null)
|
||||
if (clashProxies is null && clashProviders is null)
|
||||
{
|
||||
_proxies = clashProxies?.proxies;
|
||||
return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders);
|
||||
await Task.Delay(2000);
|
||||
continue;
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
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 void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Func<ClashProxyModel?, string, Task> updateFunc)
|
||||
public async Task<int> TestDelay(string name, ClashItem? clashItem)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
if (clashItem?.ProviderIndexMap.TryGetValue(name, out var providerName) == true)
|
||||
{
|
||||
if (blAll)
|
||||
{
|
||||
if (_proxies == null)
|
||||
{
|
||||
await GetClashProxiesAsync();
|
||||
}
|
||||
lstProxy = [];
|
||||
lstProxy.AddRange(from kv in _proxies ?? []
|
||||
where !Global.notAllowTestType.Contains(kv.Value.type?.ToLower())
|
||||
select new ClashProxyModel()
|
||||
{
|
||||
Name = kv.Value.name,
|
||||
Type = kv.Value.type?.ToLower(),
|
||||
});
|
||||
}
|
||||
|
||||
if (lstProxy is not { Count: > 0 })
|
||||
{
|
||||
return;
|
||||
}
|
||||
var urlBase = $"{GetApiUrl()}/proxies";
|
||||
urlBase += @"/{0}/delay?timeout=10000&url=" + AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
|
||||
var tasks = new List<Task>();
|
||||
foreach (var it in lstProxy)
|
||||
{
|
||||
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var name = it.Name;
|
||||
var url = string.Format(urlBase, name);
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
await updateFunc?.Invoke(it, result);
|
||||
}));
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
await Task.Delay(1000);
|
||||
await updateFunc?.Invoke(null, "");
|
||||
});
|
||||
return await TestProviderProxyDelay(name, providerName);
|
||||
}
|
||||
return await TestProxyDelay(name);
|
||||
}
|
||||
|
||||
public List<ProxiesItem>? GetClashProxyGroups()
|
||||
private async Task<int> 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<int> 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<int> 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<JsonElement>().TryGetInt32(out var v)
|
||||
? v
|
||||
: -1;
|
||||
}
|
||||
|
||||
public async Task SetActiveProxy(string groupName, string nodeName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileContent = ProfileContent;
|
||||
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClashSetActiveProxy(string name, string nameNode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/proxies/{name}";
|
||||
var url = $"{ApiUrl}/proxies/{Utils.UrlEncode(groupName)}";
|
||||
var headers = new Dictionary<string, string>();
|
||||
headers.Add("name", nameNode);
|
||||
headers.Add("name", nodeName);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -116,39 +107,67 @@ public sealed class ClashApiManager
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClashConfigUpdate(Dictionary<string, string> headers)
|
||||
public async Task UpdateClashMode(string mode)
|
||||
{
|
||||
if (_proxies == null)
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
return;
|
||||
}
|
||||
{ "mode", mode },
|
||||
};
|
||||
await UpdateConfig(headers);
|
||||
}
|
||||
|
||||
var urlBase = $"{GetApiUrl()}/configs";
|
||||
public async Task UpdateConfig(Dictionary<string, string> headers)
|
||||
{
|
||||
var urlBase = $"{ApiUrl}/configs";
|
||||
|
||||
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
|
||||
}
|
||||
|
||||
public async Task ClashConfigReload(string filePath)
|
||||
public async Task<List<string>> GetClashModes()
|
||||
{
|
||||
await ClashConnectionClose("");
|
||||
try
|
||||
var jsonNode = await GetConfig();
|
||||
if ((jsonNode?["mode-list"] ?? jsonNode?["modes"]) is not JsonArray { Count: > 0 } modesArray)
|
||||
{
|
||||
var url = $"{GetApiUrl()}/configs?force=true";
|
||||
var headers = new Dictionary<string, string>();
|
||||
headers.Add("path", filePath);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
return [];
|
||||
}
|
||||
catch (Exception ex)
|
||||
var modes = new List<string>();
|
||||
foreach (var mode in modesArray)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
if (mode is JsonValue jsonValue && jsonValue.GetValueKind() == JsonValueKind.String)
|
||||
{
|
||||
modes.Add(jsonValue.GetValue<string>());
|
||||
}
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
public async Task<ClashConnections?> GetClashConnectionsAsync()
|
||||
public async Task<string?> GetClashMode()
|
||||
{
|
||||
var jsonNode = await GetConfig();
|
||||
if (jsonNode["mode"] is not JsonValue jsonValue || jsonValue.GetValueKind() != JsonValueKind.String)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return jsonValue.GetValue<string>();
|
||||
}
|
||||
|
||||
public async Task<JsonObject> 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<ClashConnections?> GetConnections()
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/connections";
|
||||
var url = $"{ApiUrl}/connections";
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
|
||||
|
||||
@@ -162,11 +181,11 @@ public sealed class ClashApiManager
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task ClashConnectionClose(string id)
|
||||
public async Task CloseConnection(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/connections/{id}";
|
||||
var url = $"{ApiUrl}/connections/{id}";
|
||||
await HttpClientHelper.Instance.DeleteAsync(url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -174,9 +193,4 @@ public sealed class ClashApiManager
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetApiUrl()
|
||||
{
|
||||
return $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user