This commit is contained in:
DHR60 2026-02-27 09:50:42 +00:00 committed by GitHub
commit 624ec1a17c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 216 additions and 355 deletions

View file

@ -1163,48 +1163,42 @@ public static class ConfigHandler
return 0; return 0;
} }
// Matches subscription-info noise words in both Chinese and English.
// Chinese: 剩余(remaining), 过期/到期(expired/expiry), 重置(reset)
// English: remaining, expir(e/ed/y), reset
private const string PolicyGroupExcludeKeywords = @"剩余|过期|到期|重置|[Rr]emaining|[Ee]xpir|[Rr]eset";
private const string PolicyGroupDefaultAllFilter = $"^(?!.*(?:{PolicyGroupExcludeKeywords})).*$";
/// <summary>
/// Combines a region pattern with PolicyGroupDefaultAllFilter so results must
/// match the region keyword AND not contain expiry/traffic noise words (CN + EN).
/// Result pattern: ^(?!.*(?:...excludeKeywords...)).*(?:regionPattern).*$
/// </summary>
private static string CombineWithDefaultAllFilter(string regionPattern)
{
return $"^(?!.*(?:{PolicyGroupExcludeKeywords})).*(?:{regionPattern}).*$";
}
/// <summary> /// <summary>
/// Create a group server that combines multiple servers for load balancing /// Create a group server that combines multiple servers for load balancing
/// Generates a configuration file that references multiple servers /// Generates a PolicyGroup profile with references to the sub-items
/// </summary> /// </summary>
/// <param name="config">Current configuration</param> /// <param name="config">Current configuration</param>
/// <param name="selecteds">Selected servers to combine</param> /// <param name="subItem">Sub-item for grouping</param>
/// <param name="coreType">Core type to use (Xray or sing_box)</param>
/// <param name="multipleLoad">Load balancing algorithm</param>
/// <returns>Result object with success state and data</returns> /// <returns>Result object with success state and data</returns>
public static async Task<RetResult> AddGroupServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad, string? subId) public static async Task<RetResult> AddGroupAllServer(Config config, SubItem? subItem)
{ {
var result = new RetResult(); var result = new RetResult();
var indexId = Utils.GetGuid(false); var indexId = Utils.GetGuid(false);
var childProfileIndexId = Utils.List2String(selecteds.Select(p => p.IndexId).ToList()); var subId = subItem?.Id;
var remark = subId.IsNullOrEmpty() ? string.Empty : $"{(await AppManager.Instance.GetSubItem(subId))?.Remarks} "; var remark = subItem is null ? ResUI.TbConfigTypePolicyGroup : $"{subItem.Remarks} - {ResUI.TbConfigTypePolicyGroup}";
if (coreType == ECoreType.Xray)
{
remark += multipleLoad switch
{
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerXrayLeastPing,
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerXrayFallback,
EMultipleLoad.Random => ResUI.menuGenGroupMultipleServerXrayRandom,
EMultipleLoad.RoundRobin => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
EMultipleLoad.LeastLoad => ResUI.menuGenGroupMultipleServerXrayLeastLoad,
_ => ResUI.menuGenGroupMultipleServerXrayRoundRobin,
};
}
else if (coreType == ECoreType.sing_box)
{
remark += multipleLoad switch
{
EMultipleLoad.LeastPing => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
EMultipleLoad.Fallback => ResUI.menuGenGroupMultipleServerSingBoxFallback,
_ => ResUI.menuGenGroupMultipleServerSingBoxLeastPing,
};
}
var profile = new ProfileItem var profile = new ProfileItem
{ {
IndexId = indexId, IndexId = indexId,
CoreType = coreType, CoreType = ECoreType.Xray,
ConfigType = EConfigType.PolicyGroup, ConfigType = EConfigType.PolicyGroup,
Remarks = remark, Remarks = remark,
IsSub = false IsSub = false
@ -1215,8 +1209,10 @@ public static class ConfigHandler
} }
var extraItem = new ProtocolExtraItem var extraItem = new ProtocolExtraItem
{ {
ChildItems = childProfileIndexId, MultipleLoad = EMultipleLoad.LeastPing,
MultipleLoad = multipleLoad, GroupType = profile.ConfigType.ToString(),
SubChildItems = subId,
Filter = PolicyGroupDefaultAllFilter,
}; };
profile.SetProtocolExtra(extraItem); profile.SetProtocolExtra(extraItem);
var ret = await AddServerCommon(config, profile, true); var ret = await AddServerCommon(config, profile, true);
@ -1225,6 +1221,76 @@ public static class ConfigHandler
return result; return result;
} }
private static readonly Dictionary<string, string> PolicyGroupRegionFilters = new()
{
{ "JP", "日本|[Jj][Pp]|🇯🇵" },
{ "US", "美国|[Uu][Ss]|🇺🇸" },
{ "HK", "香港|[Hh][Kk]|🇭🇰" },
{ "TW", "台湾|[Tt][Ww]|🇹🇼" },
{ "KR", "韩国|[Kk][Rr]|🇰🇷" },
{ "SG", "新加坡|[Ss][Gg]|🇸🇬" },
{ "DE", "德国|[Dd][Ee]|🇩🇪" },
{ "FR", "法国|[Ff][Rr]|🇫🇷" },
{ "GB", "英国|[Gg][Bb]|🇬🇧" },
{ "CA", "加拿大|[Cc][Aa]|🇨🇦" },
{ "AU", "澳大利亚|[Aa][Uu]|🇦🇺" },
{ "RU", "俄罗斯|[Rr][Uu]|🇷🇺" },
{ "BR", "巴西|[Bb][Rr]|🇧🇷" },
{ "IN", "印度|[Ii][Nn]|🇮🇳" },
{ "VN", "越南|[Vv][Nn]|🇻🇳" },
{ "ID", "印度尼西亚|[Ii][Dd]|🇮🇩" },
{ "MX", "墨西哥|[Mm][Xx]|🇲🇽" }
};
public static async Task<RetResult> AddGroupRegionServer(Config config, SubItem? subItem)
{
var result = new RetResult();
List<string> indexIdList = [];
foreach (var regionFilter in PolicyGroupRegionFilters)
{
var indexId = Utils.GetGuid(false);
var subId = subItem?.Id;
var remark = subItem is null ? ResUI.TbConfigTypePolicyGroup : $"{subItem.Remarks} - {ResUI.TbConfigTypePolicyGroup} - {regionFilter.Key}";
var profile = new ProfileItem
{
IndexId = indexId,
CoreType = ECoreType.Xray,
ConfigType = EConfigType.PolicyGroup,
Remarks = remark,
IsSub = false
};
if (!subId.IsNullOrEmpty())
{
profile.Subid = subId;
}
var extraItem = new ProtocolExtraItem
{
MultipleLoad = EMultipleLoad.LeastPing,
GroupType = profile.ConfigType.ToString(),
SubChildItems = subId,
Filter = CombineWithDefaultAllFilter(regionFilter.Value),
};
profile.SetProtocolExtra(extraItem);
var childProfile = await GroupProfileManager.GetChildProfileItemsByProtocolExtra(extraItem);
if (childProfile.Count == 0)
{
continue;
}
var ret = await AddServerCommon(config, profile, true);
if (ret == 0)
{
indexIdList.Add(indexId);
}
}
result.Success = indexIdList.Count > 0;
result.Data = indexIdList;
return result;
}
/// <summary> /// <summary>
/// Get a SOCKS server profile for pre-SOCKS functionality /// Get a SOCKS server profile for pre-SOCKS functionality
/// Used when TUN mode is enabled or when a custom config has a pre-SOCKS port /// Used when TUN mode is enabled or when a custom config has a pre-SOCKS port

View file

@ -128,7 +128,7 @@ public class GroupProfileManager
private static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra) private static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra)
{ {
if (extra == null || extra.SubChildItems.IsNullOrEmpty()) if (extra == null || extra.Filter.IsNullOrEmpty())
{ {
return []; return [];
} }

View file

@ -888,6 +888,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 All configurations 的本地化字符串。
/// </summary>
public static string menuAllServers {
get {
return ResourceManager.GetString("menuAllServers", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Backup and Restore 的本地化字符串。 /// 查找类似 Backup and Restore 的本地化字符串。
/// </summary> /// </summary>
@ -1060,74 +1069,20 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Generate Policy Group from Multiple Profiles 的本地化字符串。 /// 查找类似 Generate Policy Group 的本地化字符串。
/// </summary> /// </summary>
public static string menuGenGroupMultipleServer { public static string menuGenGroupServer {
get { get {
return ResourceManager.GetString("menuGenGroupMultipleServer", resourceCulture); return ResourceManager.GetString("menuGenGroupServer", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// 查找类似 Fallback by sing-box 的本地化字符串。 /// 查找类似 Group by Region 的本地化字符串。
/// </summary> /// </summary>
public static string menuGenGroupMultipleServerSingBoxFallback { public static string menuGenRegionGroup {
get { get {
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxFallback", resourceCulture); return ResourceManager.GetString("menuGenRegionGroup", resourceCulture);
}
}
/// <summary>
/// 查找类似 LeastPing by sing-box 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerSingBoxLeastPing {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerSingBoxLeastPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Fallback by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayFallback {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayFallback", resourceCulture);
}
}
/// <summary>
/// 查找类似 LeastLoad by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayLeastLoad {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastLoad", resourceCulture);
}
}
/// <summary>
/// 查找类似 LeastPing by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayLeastPing {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayLeastPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Random by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayRandom {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRandom", resourceCulture);
}
}
/// <summary>
/// 查找类似 RoundRobin by Xray 的本地化字符串。
/// </summary>
public static string menuGenGroupMultipleServerXrayRoundRobin {
get {
return ResourceManager.GetString("menuGenGroupMultipleServerXrayRoundRobin", resourceCulture);
} }
} }

View file

@ -1371,24 +1371,6 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>مخفی و پورت می شود، با کاما (،) جدا می شود</value> <value>مخفی و پورت می شود، با کاما (،) جدا می شود</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Generate Policy Group from Multiple Profiles</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>چند سرور تصادفی توسط Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>چند سرور RoundRobin توسط Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>چند سرور LeastPing توسط Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>چند سرور LeastLoad توسط Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>LeastPing چند سرور توسط sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>صادر کردن سرور</value> <value>صادر کردن سرور</value>
</data> </data>
@ -1533,12 +1515,6 @@
<data name="TbFallback" xml:space="preserve"> <data name="TbFallback" xml:space="preserve">
<value>Fallback</value> <value>Fallback</value>
</data> </data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve"> <data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value> <value>Core '{0}' does not support network type '{1}'.</value>
</data> </data>
@ -1668,4 +1644,13 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbFinalmask" xml:space="preserve"> <data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value> <value>Finalmask</value>
</data> </data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>Generate Policy Group</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root> </root>

View file

@ -1368,24 +1368,6 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>Écrase le port ; pour plusieurs groupes, séparer par virgules (,)</value> <value>Écrase le port ; pour plusieurs groupes, séparer par virgules (,)</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Générer un groupe de stratégie depuis plusieurs profils</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Xray aléatoire (multi-sélection)</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Xray équilibrage (tourniquet) multi-sélection</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>Xray latence minimale (multi-sélection)</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Xray le plus stable (multi-sélection)</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>sing-box latence minimale (multi-sélection)</value>
</data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>Exporter</value> <value>Exporter</value>
</data> </data>
@ -1530,12 +1512,6 @@
<data name="TbFallback" xml:space="preserve"> <data name="TbFallback" xml:space="preserve">
<value>Basculement (failover)</value> <value>Basculement (failover)</value>
</data> </data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>sing-box basculement (multi-sélection)</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Xray basculement (multi-sélection)</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve"> <data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Le cœur « {0} » ne prend pas en charge le type de réseau « {1} ».</value> <value>Le cœur « {0} » ne prend pas en charge le type de réseau « {1} ».</value>
</data> </data>
@ -1665,4 +1641,13 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbFinalmask" xml:space="preserve"> <data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value> <value>Finalmask</value>
</data> </data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>Generate Policy Group</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root> </root>

View file

@ -1371,24 +1371,6 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>A portot lefedi, vesszővel (,) elválasztva</value> <value>A portot lefedi, vesszővel (,) elválasztva</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Generate Policy Group from Multiple Profiles</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Több konfiguráció véletlenszerűen Xray szerint</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Több konfiguráció RoundRobin Xray szerint</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>Több konfiguráció legkisebb pinggel Xray szerint</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Több konfiguráció legkisebb terheléssel Xray szerint</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>Több konfiguráció legkisebb pinggel sing-box szerint</value>
</data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>Konfiguráció exportálása</value> <value>Konfiguráció exportálása</value>
</data> </data>
@ -1533,12 +1515,6 @@
<data name="TbFallback" xml:space="preserve"> <data name="TbFallback" xml:space="preserve">
<value>Fallback</value> <value>Fallback</value>
</data> </data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve"> <data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value> <value>Core '{0}' does not support network type '{1}'.</value>
</data> </data>
@ -1668,4 +1644,13 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbFinalmask" xml:space="preserve"> <data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value> <value>Finalmask</value>
</data> </data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>Generate Policy Group</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root> </root>

View file

@ -1371,24 +1371,6 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>Will cover the port, separate with commas (,)</value> <value>Will cover the port, separate with commas (,)</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Generate Policy Group from Multiple Profiles</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Random by Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>RoundRobin by Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>LeastPing by Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>LeastLoad by Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>LeastPing by sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>Export</value> <value>Export</value>
</data> </data>
@ -1533,12 +1515,6 @@
<data name="TbFallback" xml:space="preserve"> <data name="TbFallback" xml:space="preserve">
<value>Fallback</value> <value>Fallback</value>
</data> </data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve"> <data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value> <value>Core '{0}' does not support network type '{1}'.</value>
</data> </data>
@ -1668,4 +1644,13 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbFinalmask" xml:space="preserve"> <data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value> <value>Finalmask</value>
</data> </data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>Generate Policy Group</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root> </root>

View file

@ -1371,24 +1371,6 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>Заменит указанный порт, перечисляйте через запятую (,)</value> <value>Заменит указанный порт, перечисляйте через запятую (,)</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Generate Policy Group from Multiple Profiles</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>Случайный (Xray)</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>Круговой (Xray)</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>Минимальная нагрузка (Xray)</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value>
</data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>Экспортировать конфигурацию</value> <value>Экспортировать конфигурацию</value>
</data> </data>
@ -1533,12 +1515,6 @@
<data name="TbFallback" xml:space="preserve"> <data name="TbFallback" xml:space="preserve">
<value>Fallback</value> <value>Fallback</value>
</data> </data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve"> <data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value> <value>Core '{0}' does not support network type '{1}'.</value>
</data> </data>
@ -1668,4 +1644,13 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbFinalmask" xml:space="preserve"> <data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value> <value>Finalmask</value>
</data> </data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>Generate Policy Group</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root> </root>

View file

@ -1368,24 +1368,6 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>会覆盖端口,多组时用逗号 (,) 隔开</value> <value>会覆盖端口,多组时用逗号 (,) 隔开</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>多选生成策略组</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>多选随机 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多选负载均衡 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>多选最低延迟 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多选最稳定 Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多选最低延迟 sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>导出</value> <value>导出</value>
</data> </data>
@ -1530,12 +1512,6 @@
<data name="TbFallback" xml:space="preserve"> <data name="TbFallback" xml:space="preserve">
<value>故障转移</value> <value>故障转移</value>
</data> </data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>多选故障转移 sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>多选故障转移 Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve"> <data name="CoreNotSupportNetwork" xml:space="preserve">
<value>核心 '{0}' 不支持网络类型 '{1}'。</value> <value>核心 '{0}' 不支持网络类型 '{1}'。</value>
</data> </data>
@ -1665,4 +1641,13 @@
<data name="TbFinalmask" xml:space="preserve"> <data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value> <value>Finalmask</value>
</data> </data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>一键生成策略组</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>全部配置项</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>按地区分组</value>
</data>
</root> </root>

View file

@ -1368,24 +1368,6 @@
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value> <value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>多選生成策略組</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>多選隨機 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多選負載平衡 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>多選最低延遲 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多選最穩定 Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多選最低延遲 sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve"> <data name="menuExportConfig" xml:space="preserve">
<value>匯出</value> <value>匯出</value>
</data> </data>
@ -1530,12 +1512,6 @@
<data name="TbFallback" xml:space="preserve"> <data name="TbFallback" xml:space="preserve">
<value>容錯移轉</value> <value>容錯移轉</value>
</data> </data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>多選容錯移轉 sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>多選容錯移轉 Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve"> <data name="CoreNotSupportNetwork" xml:space="preserve">
<value>核心 '{0}' 不支援網路類型 '{1}'.</value> <value>核心 '{0}' 不支援網路類型 '{1}'.</value>
</data> </data>
@ -1665,4 +1641,13 @@
<data name="TbFinalmask" xml:space="preserve"> <data name="TbFinalmask" xml:space="preserve">
<value>Finalmask</value> <value>Finalmask</value>
</data> </data>
<data name="menuGenGroupServer" xml:space="preserve">
<value>Generate Policy Group</value>
</data>
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root> </root>

View file

@ -8,6 +8,7 @@ public class ProfilesViewModel : MyReactiveObject
private string _serverFilter = string.Empty; private string _serverFilter = string.Empty;
private Dictionary<string, bool> _dicHeaderSort = new(); private Dictionary<string, bool> _dicHeaderSort = new();
private SpeedtestService? _speedtestService; private SpeedtestService? _speedtestService;
private string? _pendingSelectIndexId;
#endregion private prop #endregion private prop
@ -43,13 +44,8 @@ public class ProfilesViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; } public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; } public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; } public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRandomCmd { get; } public ReactiveCommand<Unit, Unit> GenGroupAllServerCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRoundRobinCmd { get; } public ReactiveCommand<Unit, Unit> GenGroupRegionServerCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastPingCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastLoadCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayFallbackCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxLeastPingCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxFallbackCmd { get; }
//servers move //servers move
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; } public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
@ -134,33 +130,13 @@ public class ProfilesViewModel : MyReactiveObject
{ {
await ShareServerAsync(); await ShareServerAsync();
}, canEditRemove); }, canEditRemove);
GenGroupMultipleServerXrayRandomCmd = ReactiveCommand.CreateFromTask(async () => GenGroupAllServerCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Random); await GenGroupAllServer();
}, canEditRemove); }, canEditRemove);
GenGroupMultipleServerXrayRoundRobinCmd = ReactiveCommand.CreateFromTask(async () => GenGroupRegionServerCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.RoundRobin); await GenGroupRegionServer();
}, canEditRemove);
GenGroupMultipleServerXrayLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastPing);
}, canEditRemove);
GenGroupMultipleServerXrayLeastLoadCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad);
}, canEditRemove);
GenGroupMultipleServerXrayFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Fallback);
}, canEditRemove);
GenGroupMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing);
}, canEditRemove);
GenGroupMultipleServerSingBoxFallbackCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.Fallback);
}, canEditRemove); }, canEditRemove);
//servers move //servers move
@ -392,15 +368,14 @@ public class ProfilesViewModel : MyReactiveObject
ProfileItems.AddRange(lstModel); ProfileItems.AddRange(lstModel);
if (lstModel.Count > 0) if (lstModel.Count > 0)
{ {
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId); ProfileItemModel? selected = null;
if (selected != null) if (!_pendingSelectIndexId.IsNullOrEmpty())
{ {
SelectedProfile = selected; selected = lstModel.FirstOrDefault(t => t.IndexId == _pendingSelectIndexId);
} _pendingSelectIndexId = null;
else
{
SelectedProfile = lstModel.First();
} }
selected ??= lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
SelectedProfile = selected ?? lstModel.First();
} }
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null); await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
@ -641,29 +616,29 @@ public class ProfilesViewModel : MyReactiveObject
await _updateView?.Invoke(EViewAction.ShareServer, url); await _updateView?.Invoke(EViewAction.ShareServer, url);
} }
private async Task GenGroupMultipleServer(ECoreType coreType, EMultipleLoad multipleLoad) private async Task GenGroupAllServer()
{ {
var lstSelected = await GetProfileItems(true); var ret = await ConfigHandler.AddGroupAllServer(_config, SelectedSub);
if (lstSelected == null)
{
return;
}
var ret = await ConfigHandler.AddGroupServer4Multiple(_config, lstSelected, coreType, multipleLoad, SelectedSub?.Id);
if (ret.Success != true) if (ret.Success != true)
{ {
NoticeManager.Instance.Enqueue(ResUI.OperationFailed); NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
return; return;
} }
if (ret?.Data?.ToString() == _config.IndexId) _pendingSelectIndexId = ret.Data?.ToString();
await RefreshServers();
}
private async Task GenGroupRegionServer()
{
var ret = await ConfigHandler.AddGroupRegionServer(_config, SelectedSub);
if (ret.Success != true)
{ {
await RefreshServers(); NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
Reload(); return;
}
else
{
await SetDefaultServer(ret?.Data?.ToString());
} }
var indexIdList = ret.Data as List<string>;
_pendingSelectIndexId = indexIdList?.FirstOrDefault();
await RefreshServers();
} }
public async Task SortServer(string colName) public async Task SortServer(string colName)

View file

@ -193,14 +193,9 @@
<MenuItem x:Name="menuExport2ShareUrlBase64" Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" /> <MenuItem x:Name="menuExport2ShareUrlBase64" Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" />
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupMultipleServer}"> <MenuItem Header="{x:Static resx:ResUI.menuGenGroupServer}">
<MenuItem x:Name="menuGenGroupMultipleServerXrayRandom" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRandom}" /> <MenuItem x:Name="menuGenGroupAllServer" Header="{x:Static resx:ResUI.menuAllServers}" />
<MenuItem x:Name="menuGenGroupMultipleServerXrayRoundRobin" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRoundRobin}" /> <MenuItem x:Name="menuGenGroupRegionServer" Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
<MenuItem x:Name="menuGenGroupMultipleServerXrayLeastPing" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastPing}" />
<MenuItem x:Name="menuGenGroupMultipleServerXrayLeastLoad" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastLoad}" />
<Separator />
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxLeastPing" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
<MenuItem x:Name="menuGenGroupMultipleServerSingBoxFallback" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
</MenuItem> </MenuItem>
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>

View file

@ -60,12 +60,8 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRandomCmd, v => v.menuGenGroupMultipleServerXrayRandom).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRoundRobinCmd, v => v.menuGenGroupMultipleServerXrayRoundRobin).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.GenGroupRegionServerCmd, v => v.menuGenGroupRegionServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
//servers move //servers move
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables); //this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);

View file

@ -240,32 +240,15 @@
Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" /> Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" />
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupMultipleServer}"> <MenuItem Header="{x:Static resx:ResUI.menuGenGroupServer}">
<MenuItem <MenuItem
x:Name="menuGenGroupMultipleServerXrayRandom" x:Name="menuGenGroupAllServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRandom}" /> Header="{x:Static resx:ResUI.menuAllServers}" />
<MenuItem <MenuItem
x:Name="menuGenGroupMultipleServerXrayRoundRobin" x:Name="menuGenGroupRegionServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRoundRobin}" /> Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
<MenuItem
x:Name="menuGenGroupMultipleServerXrayLeastPing"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastPing}" />
<MenuItem
x:Name="menuGenGroupMultipleServerXrayLeastLoad"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayLeastLoad}" />
<Separator />
<MenuItem
x:Name="menuGenGroupMultipleServerSingBoxLeastPing"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxLeastPing}" />
<MenuItem
x:Name="menuGenGroupMultipleServerSingBoxFallback"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenGroupMultipleServerSingBoxFallback}" />
</MenuItem> </MenuItem>
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>

View file

@ -54,12 +54,8 @@ public partial class ProfilesView
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRandomCmd, v => v.menuGenGroupMultipleServerXrayRandom).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRoundRobinCmd, v => v.menuGenGroupMultipleServerXrayRoundRobin).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.GenGroupRegionServerCmd, v => v.menuGenGroupRegionServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);
//servers move //servers move
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);