mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-28 05:03:02 +00:00
Add region group
This commit is contained in:
parent
10a53fc48e
commit
90bc0e98ed
15 changed files with 136 additions and 3 deletions
|
|
@ -90,8 +90,6 @@ public class Global
|
||||||
|
|
||||||
public const int Hysteria2DefaultHopInt = 10;
|
public const int Hysteria2DefaultHopInt = 10;
|
||||||
|
|
||||||
public const string PolicyGroupDefaultAllFilter = "^(?!.*(剩余|过期|到期|重置)).*$";
|
|
||||||
|
|
||||||
public static readonly List<string> IEProxyProtocols =
|
public static readonly List<string> IEProxyProtocols =
|
||||||
[
|
[
|
||||||
"{ip}:{http_port}",
|
"{ip}:{http_port}",
|
||||||
|
|
|
||||||
|
|
@ -1163,6 +1163,16 @@ public static class ConfigHandler
|
||||||
return 0;
|
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>
|
/// <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 PolicyGroup profile with references to the sub-items
|
/// Generates a PolicyGroup profile with references to the sub-items
|
||||||
|
|
@ -1195,7 +1205,7 @@ public static class ConfigHandler
|
||||||
MultipleLoad = EMultipleLoad.LeastPing,
|
MultipleLoad = EMultipleLoad.LeastPing,
|
||||||
GroupType = profile.ConfigType.ToString(),
|
GroupType = profile.ConfigType.ToString(),
|
||||||
SubChildItems = subId,
|
SubChildItems = subId,
|
||||||
Filter = Global.PolicyGroupDefaultAllFilter,
|
Filter = PolicyGroupDefaultAllFilter,
|
||||||
};
|
};
|
||||||
profile.SetProtocolExtra(extraItem);
|
profile.SetProtocolExtra(extraItem);
|
||||||
var ret = await AddServerCommon(config, profile, true);
|
var ret = await AddServerCommon(config, profile, true);
|
||||||
|
|
@ -1204,6 +1214,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
|
||||||
|
|
|
||||||
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -1077,6 +1077,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Group by Region 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuGenRegionGroup {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuGenRegionGroup", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Global Hotkey Setting 的本地化字符串。
|
/// 查找类似 Global Hotkey Setting 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1650,4 +1650,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuAllServers" xml:space="preserve">
|
<data name="menuAllServers" xml:space="preserve">
|
||||||
<value>All configurations</value>
|
<value>All configurations</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||||
|
<value>Group by Region</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1647,4 +1647,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuAllServers" xml:space="preserve">
|
<data name="menuAllServers" xml:space="preserve">
|
||||||
<value>All configurations</value>
|
<value>All configurations</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||||
|
<value>Group by Region</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
||||||
|
|
@ -1650,4 +1650,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuAllServers" xml:space="preserve">
|
<data name="menuAllServers" xml:space="preserve">
|
||||||
<value>All configurations</value>
|
<value>All configurations</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||||
|
<value>Group by Region</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1650,4 +1650,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuAllServers" xml:space="preserve">
|
<data name="menuAllServers" xml:space="preserve">
|
||||||
<value>All configurations</value>
|
<value>All configurations</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||||
|
<value>Group by Region</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1650,4 +1650,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="menuAllServers" xml:space="preserve">
|
<data name="menuAllServers" xml:space="preserve">
|
||||||
<value>All configurations</value>
|
<value>All configurations</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||||
|
<value>Group by Region</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1647,4 +1647,7 @@
|
||||||
<data name="menuAllServers" xml:space="preserve">
|
<data name="menuAllServers" xml:space="preserve">
|
||||||
<value>全部配置项</value>
|
<value>全部配置项</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||||
|
<value>按地区分组</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1647,4 +1647,7 @@
|
||||||
<data name="menuAllServers" xml:space="preserve">
|
<data name="menuAllServers" xml:space="preserve">
|
||||||
<value>All configurations</value>
|
<value>All configurations</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuGenRegionGroup" xml:space="preserve">
|
||||||
|
<value>Group by Region</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -45,6 +45,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
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> GenGroupAllServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> GenGroupAllServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> GenGroupRegionServerCmd { get; }
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||||
|
|
@ -133,6 +134,10 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
await GenGroupAllServer();
|
await GenGroupAllServer();
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
|
GenGroupRegionServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await GenGroupRegionServer();
|
||||||
|
}, canEditRemove);
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
|
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
|
@ -623,6 +628,19 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
await RefreshServers();
|
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)
|
public async Task SortServer(string colName)
|
||||||
{
|
{
|
||||||
if (colName.IsNullOrEmpty())
|
if (colName.IsNullOrEmpty())
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,7 @@
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupServer}">
|
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupServer}">
|
||||||
<MenuItem x:Name="menuGenGroupAllServer" Header="{x:Static resx:ResUI.menuAllServers}" />
|
<MenuItem x:Name="menuGenGroupAllServer" Header="{x:Static resx:ResUI.menuAllServers}" />
|
||||||
|
<MenuItem x:Name="menuGenGroupRegionServer" Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</DataGrid.ContextMenu>
|
</DataGrid.ContextMenu>
|
||||||
|
|
|
||||||
|
|
@ -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.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.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).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
|
//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);
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,10 @@
|
||||||
x:Name="menuGenGroupAllServer"
|
x:Name="menuGenGroupAllServer"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuAllServers}" />
|
Header="{x:Static resx:ResUI.menuAllServers}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuGenGroupRegionServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuGenRegionGroup}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</DataGrid.ContextMenu>
|
</DataGrid.ContextMenu>
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ public partial class ProfilesView
|
||||||
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.GenGroupAllServerCmd, v => v.menuGenGroupAllServer).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
|
//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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue