namespace ServiceLib.Manager; public class GroupProfileManager { public static async Task HasCycle(ProfileItem item) { return await HasCycle(item.IndexId, item.GetProtocolExtra()); } public static async Task HasCycle(string? indexId, ProtocolExtraItem? extraInfo) { return await HasCycle(indexId, extraInfo, new HashSet(), new HashSet()); } public static async Task HasCycle(string? indexId, ProtocolExtraItem? extraInfo, HashSet visited, HashSet stack) { if (indexId.IsNullOrEmpty() || extraInfo == null) { return false; } if (stack.Contains(indexId)) { return true; } if (visited.Contains(indexId)) { return false; } visited.Add(indexId); stack.Add(indexId); try { if (extraInfo.GroupType.IsNullOrEmpty()) { return false; } if (extraInfo.ChildItems.IsNullOrEmpty()) { return false; } var childIds = Utils.String2List(extraInfo.ChildItems) ?.Where(p => !string.IsNullOrEmpty(p)) .ToList(); if (childIds == null) { return false; } foreach (var child in childIds) { var childItem = await AppManager.Instance.GetProfileItem(child); if (await HasCycle(child, childItem?.GetProtocolExtra(), visited, stack)) { return true; } } return false; } finally { stack.Remove(indexId); } } public static async Task<(List Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem) { var protocolExtra = profileItem?.GetProtocolExtra(); var items = await GetChildProfileItems(protocolExtra); var subItems = await GetSubChildProfileItems(protocolExtra); items.AddRange(subItems); return (items, protocolExtra); } public static async Task> GetChildProfileItems(ProtocolExtraItem? extra) { if (extra == null || extra.ChildItems.IsNullOrEmpty()) { return new(); } var childProfiles = (await Task.WhenAll( Utils.String2List(extra.ChildItems) .Where(p => !p.IsNullOrEmpty()) .Select(AppManager.Instance.GetProfileItem) )) .Where(p => p != null && p.IsValid() && p.ConfigType != EConfigType.Custom ) .ToList(); return childProfiles; } public static async Task> GetSubChildProfileItems(ProtocolExtraItem? extra) { if (extra == null || extra.Filter.IsNullOrEmpty()) { return new(); } var childProfiles = await AppManager.Instance.ProfileItems(extra.SubChildItems ?? string.Empty); return childProfiles.Where(p => p != null && p.IsValid() && !p.ConfigType.IsComplexType() && (extra.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, extra.Filter)) ) .ToList(); } public static async Task> GetAllChildDomainAddresses(ProfileItem profileItem) { var childAddresses = new HashSet(); var (childItems, _) = await GetChildProfileItems(profileItem); foreach (var child in childItems) { if (!child.IsComplex()) { childAddresses.Add(child.Address); } else if (child.ConfigType.IsGroupType()) { var subAddresses = await GetAllChildDomainAddresses(child); foreach (var addr in subAddresses) { childAddresses.Add(addr); } } } return childAddresses; } public static async Task> GetAllChildEchQuerySni(ProfileItem profileItem) { var childAddresses = new HashSet(); var (childItems, _) = await GetChildProfileItems(profileItem); foreach (var childNode in childItems) { if (!childNode.IsComplex() && !childNode.EchConfigList.IsNullOrEmpty()) { if (childNode.StreamSecurity == Global.StreamSecurity && childNode.EchConfigList?.Contains("://") == true) { var idx = childNode.EchConfigList.IndexOf('+'); childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni); } else { childAddresses.Add(childNode.Sni); } } else if (childNode.ConfigType.IsGroupType()) { var subAddresses = await GetAllChildDomainAddresses(childNode); foreach (var addr in subAddresses) { childAddresses.Add(addr); } } } return childAddresses; } }