Add region group

This commit is contained in:
DHR60 2026-02-27 17:43:20 +08:00
parent 94f15b9150
commit d4df6b91f0
15 changed files with 136 additions and 3 deletions

View file

@ -90,8 +90,6 @@ public class Global
public const int Hysteria2DefaultHopInt = 10;
public const string PolicyGroupDefaultAllFilter = "^(?!.*(剩余|过期|到期|重置)).*$";
public static readonly List<string> IEProxyProtocols =
[
"{ip}:{http_port}",

View file

@ -1163,6 +1163,16 @@ public static class ConfigHandler
return 0;
}
private const string PolicyGroupDefaultAllFilter = "^(?!.*(?:剩余|过期|到期|重置)).*$";
/// <summary>
/// Combines a region pattern with PolicyGroupDefaultAllFilter so results must
/// match the region keyword AND not contain expiry/traffic noise words.
/// Result pattern: ^(?!.*(?:剩余|过期|到期|重置)).*(?:regionPattern).*$
/// </summary>
private static string CombineWithDefaultAllFilter(string regionPattern)
=> $"^(?!.*(?:剩余|过期|到期|重置)).*(?:{regionPattern}).*$";
/// <summary>
/// Create a group server that combines multiple servers for load balancing
/// Generates a PolicyGroup profile with references to the sub-items
@ -1195,7 +1205,7 @@ public static class ConfigHandler
MultipleLoad = EMultipleLoad.LeastPing,
GroupType = profile.ConfigType.ToString(),
SubChildItems = subId,
Filter = Global.PolicyGroupDefaultAllFilter,
Filter = PolicyGroupDefaultAllFilter,
};
profile.SetProtocolExtra(extraItem);
var ret = await AddServerCommon(config, profile, true);
@ -1204,6 +1214,76 @@ public static class ConfigHandler
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>
/// 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

View file

@ -1023,6 +1023,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Group by Region 的本地化字符串。
/// </summary>
public static string menuGenRegionGroup {
get {
return ResourceManager.GetString("menuGenRegionGroup", resourceCulture);
}
}
/// <summary>
/// 查找类似 Global Hotkey Setting 的本地化字符串。
/// </summary>

View file

@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root>

View file

@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root>

View file

@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root>

View file

@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root>

View file

@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root>

View file

@ -1665,4 +1665,7 @@
<data name="menuAllServers" xml:space="preserve">
<value>全部配置项</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>按地区分组</value>
</data>
</root>

View file

@ -1665,4 +1665,7 @@
<data name="menuAllServers" xml:space="preserve">
<value>All configurations</value>
</data>
<data name="menuGenRegionGroup" xml:space="preserve">
<value>Group by Region</value>
</data>
</root>

View file

@ -45,6 +45,7 @@ public class ProfilesViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupAllServerCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupRegionServerCmd { get; }
//servers move
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
@ -133,6 +134,10 @@ public class ProfilesViewModel : MyReactiveObject
{
await GenGroupAllServer();
}, canEditRemove);
GenGroupRegionServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await GenGroupRegionServer();
}, canEditRemove);
//servers move
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
@ -623,6 +628,19 @@ public class ProfilesViewModel : MyReactiveObject
await RefreshServers();
}
private async Task GenGroupRegionServer()
{
var ret = await ConfigHandler.AddGroupRegionServer(_config, SelectedSub);
if (ret.Success != true)
{
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
return;
}
var indexIdList = ret.Data as List<string>;
_pendingSelectIndexId = indexIdList?.FirstOrDefault();
await RefreshServers();
}
public async Task SortServer(string colName)
{
if (colName.IsNullOrEmpty())

View file

@ -195,6 +195,7 @@
<Separator />
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupServer}">
<MenuItem x:Name="menuGenGroupAllServer" Header="{x:Static resx:ResUI.menuAllServers}" />
<MenuItem x:Name="menuGenGroupRegionServer" Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>

View file

@ -61,6 +61,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
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.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupRegionServerCmd, v => v.menuGenGroupRegionServer).DisposeWith(disposables);
//servers move
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);

View file

@ -245,6 +245,10 @@
x:Name="menuGenGroupAllServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAllServers}" />
<MenuItem
x:Name="menuGenGroupRegionServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>

View file

@ -55,6 +55,7 @@ public partial class ProfilesView
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.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GenGroupRegionServerCmd, v => v.menuGenGroupRegionServer).DisposeWith(disposables);
//servers move
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);