Conversation
| // 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"; |
There was a problem hiding this comment.
PolicyGroupExcludeKeywords PolicyGroupRegionFilters ,是否放到 Global 中合适?
There was a problem hiding this comment.
第一版就是放在 Global 里的,主要是顾虑一个是中文和 emoji 这种,另一个是只有 ConfigHandler 使用
There was a problem hiding this comment.
不排除后面可能在编辑组的时候,会让用户自己选择 PolicyGroupRegionFilters 下拉
There was a problem hiding this comment.
那就改成 editable combobox 这种?
There was a problem hiding this comment.
PolicyGroupRegionFilters 也放到 Global 里面吗?
稍微有点不好看
private const string PolicyGroupExcludeKeywords = @"剩余|过期|到期|重置|[Rr]emaining|[Ee]xpir|[Rr]eset";
public const string PolicyGroupDefaultAllFilter = $"^(?!.*(?:{PolicyGroupExcludeKeywords})).*$";
private static string CombineWithDefaultAllFilter(string regionPattern)
{
return $"^(?!.*(?:{PolicyGroupExcludeKeywords})).*(?:{regionPattern}).*$";
}
public static readonly Dictionary<string, string> PolicyGroupRegionFilters = new()
{
{ "JP", CombineWithDefaultAllFilter("日本|\\\\b[Jj][Pp]\\\\b|🇯🇵|[Jj]apan") },
{ "US", CombineWithDefaultAllFilter("美国|\\b[Uu][Ss]\\b|🇺🇸|[Uu]nited [Ss]tates|\\b[Uu][Ss][Aa]\\b") },
{ "HK", CombineWithDefaultAllFilter("香港|\\b[Hh][Kk]\\b|🇭🇰|[Hh]ong ?[Kk]ong") },
{ "TW", CombineWithDefaultAllFilter("台湾|台灣|\\b[Tt][Ww]\\b|🇹🇼|[Tt]aiwan") },
{ "KR", CombineWithDefaultAllFilter("韩国|\\b[Kk][Rr]\\b|🇰🇷|[Kk]orea") },
{ "SG", CombineWithDefaultAllFilter("新加坡|\\b[Ss][Gg]\\b|🇸🇬|[Ss]ingapore") },
{ "DE", CombineWithDefaultAllFilter("德国|\\b[Dd][Ee]\\b|🇩🇪|[Gg]ermany") },
{ "FR", CombineWithDefaultAllFilter("法国|\\b[Ff][Rr]\\b|🇫🇷|[Ff]rance") },
{ "GB", CombineWithDefaultAllFilter("英国|\\b[Gg][Bb]\\b|🇬🇧|[Uu]nited [Kk]ingdom|[Bb]ritain") },
{ "CA", CombineWithDefaultAllFilter("加拿大|🇨🇦|[Cc]anada") },
{ "AU", CombineWithDefaultAllFilter("澳大利亚|\\b[Aa][Uu]\\b|🇦🇺|[Aa]ustralia") },
{ "RU", CombineWithDefaultAllFilter("俄罗斯|\\b[Rr][Uu]\\b|🇷🇺|[Rr]ussia") },
{ "BR", CombineWithDefaultAllFilter("巴西|\\b[Bb][Rr]\\b|🇧🇷|[Bb]razil") },
{ "IN", CombineWithDefaultAllFilter("印度|🇮🇳|[Ii]ndia") },
{ "VN", CombineWithDefaultAllFilter("越南|\\b[Vv][Nn]\\b|🇻🇳|[Vv]ietnam") },
{ "ID", CombineWithDefaultAllFilter("印度尼西亚|\\b[Ii][Dd]\\b|🇮🇩|[Ii]ndonesia") },
{ "MX", CombineWithDefaultAllFilter("墨西哥|\\b[Mm][Xx]\\b|🇲🇽|[Mm]exico") }
};There was a problem hiding this comment.
加上吧,打算是在 Global 里加上
private const string PolicyGroupExcludeKeywords = @"剩余|过期|到期|重置|[Rr]emaining|[Ee]xpir|[Rr]eset";
public const string PolicyGroupDefaultAllFilter = $"^(?!.*(?:{PolicyGroupExcludeKeywords})).*$";
再加上个简单的例子组个 List
PolicyGroupRegionFilters 就不放在 Global 里了,Global 里放个函数看起来怪怪的
There was a problem hiding this comment.
Pull request overview
This PR updates the Profiles context menu and underlying ViewModel/handler logic to support “one-click” Policy Group generation, including generating a single “all configurations” group and generating multiple groups “by region”, with automatic selection of the newly created group.
Changes:
- Replaced the old “multi-select generate policy group” menu/actions with two new actions: generate for all configs, and generate by region.
- Added new
ConfigHandlerhelpers to create filter-based PolicyGroup profiles (default exclude “traffic/expiry” noise words, plus region matching). - Updated
ProfilesViewModelselection logic to auto-select the newly generated group after refresh.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| v2rayN/v2rayN/Views/ProfilesView.xaml(.cs) | Reworked WPF context menu and command bindings to the new “generate group” actions. |
| v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml(.cs) | Same menu/command binding changes for the desktop UI. |
| v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs | Added new commands + pending-selection behavior to focus the generated group. |
| v2rayN/ServiceLib/Handler/ConfigHandler.cs | Implemented AddGroupAllServer / AddGroupRegionServer and new filter/region regex helpers. |
| v2rayN/ServiceLib/Manager/GroupProfileManager.cs | Adjusted sub-child selection logic (but introduces a functional regression—see comments). |
| v2rayN/ServiceLib/Resx/ResUI*.resx (+ Designer) | Removed old menu keys and added new menu localization keys. |
Files not reviewed (1)
- v2rayN/ServiceLib/Resx/ResUI.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)
v2rayN/ServiceLib/Handler/ConfigHandler.cs:1220
AddGroupAllServeralways creates a PolicyGroup profile even if the computed child list is empty (e.g., subscription contains only excluded “traffic/expiry” noise words, or the filter is too restrictive). This can leave behind empty/invalid groups. Consider resolving child items viaGroupProfileManager.GetChildProfileItemsByProtocolExtra(extraItem)first (similar toAddGroupRegionServer) and returnSuccess=false(or skip creation) when there are no matching children.
public static async Task<RetResult> AddGroupAllServer(Config config, SubItem? subItem)
{
var result = new RetResult();
var indexId = Utils.GetGuid(false);
var subId = subItem?.Id;
var remark = subItem is null ? ResUI.TbConfigTypePolicyGroup : $"{subItem.Remarks} - {ResUI.TbConfigTypePolicyGroup}";
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 = PolicyGroupDefaultAllFilter,
};
profile.SetProtocolExtra(extraItem);
var ret = await AddServerCommon(config, profile, true);
result.Success = ret == 0;
result.Data = indexId;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <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> |
There was a problem hiding this comment.
The newly added menu strings in the zh-Hant resource file are in English (e.g., "Generate Policy Group", "All configurations", "Group by Region"), which is a regression compared to the previously localized menu entries in this file. Please provide Traditional Chinese translations for these new keys (or remove the entries to fall back to the default resource).
| <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> | |
| <value>生成策略群組</value> | |
| </data> | |
| <data name="menuAllServers" xml:space="preserve"> | |
| <value>所有設定</value> | |
| </data> | |
| <data name="menuGenRegionGroup" xml:space="preserve"> | |
| <value>按地區分組</value> |
| <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> |
There was a problem hiding this comment.
The newly added menu strings in the Russian resource file are in English ("Generate Policy Group", "All configurations", "Group by Region"). Since the removed keys were translated, this is a user-visible localization regression. Please add Russian translations for these new keys (or omit them to fall back to the default resource).
| <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> | |
| <value>Создать группу политик</value> | |
| </data> | |
| <data name="menuAllServers" xml:space="preserve"> | |
| <value>Все конфигурации</value> | |
| </data> | |
| <data name="menuGenRegionGroup" xml:space="preserve"> | |
| <value>Сгруппировать по региону</value> |
| <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> |
There was a problem hiding this comment.
The newly added menu strings in the Hungarian resource file are in English ("Generate Policy Group", "All configurations", "Group by Region"). This is a localization regression compared to the previously translated entries removed in this PR. Please add Hungarian translations for these new keys (or omit them to fall back to the default resource).
| <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> | |
| <value>Házirendcsoport létrehozása</value> | |
| </data> | |
| <data name="menuAllServers" xml:space="preserve"> | |
| <value>Minden konfiguráció</value> | |
| </data> | |
| <data name="menuGenRegionGroup" xml:space="preserve"> | |
| <value>Csoportosítás régió szerint</value> |
| <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> |
There was a problem hiding this comment.
The newly added menu strings in the French resource file are in English ("Generate Policy Group", "All configurations", "Group by Region"), whereas the removed keys were French. Please translate these new keys to French (or omit them to fall back to the default resource) to avoid a UI language regression.
| <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> | |
| <value>Générer un groupe de stratégies</value> | |
| </data> | |
| <data name="menuAllServers" xml:space="preserve"> | |
| <value>Toutes les configurations</value> | |
| </data> | |
| <data name="menuGenRegionGroup" xml:space="preserve"> | |
| <value>Grouper par région</value> |
| <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> |
There was a problem hiding this comment.
The newly added menu strings in the fa-Ir resource file are in English ("Generate Policy Group", "All configurations", "Group by Region"). Since the removed keys had localized values, this is a user-visible regression. Please provide Persian translations for these keys (or omit them to fall back to the default resource).
| <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> | |
| <value>ایجاد گروه سیاست</value> | |
| </data> | |
| <data name="menuAllServers" xml:space="preserve"> | |
| <value>همه پیکربندیها</value> | |
| </data> | |
| <data name="menuGenRegionGroup" xml:space="preserve"> | |
| <value>گروهبندی بر اساس منطقه</value> |
|
Done |
一键生成策略组
#8848 (comment)
可生成 全部配置项 和 按地区分组 策略组
生成后将自动滚动到生成的策略组位置