Enhanced Profile Configuration Parsing and Tag Matching in profile.ini (#820)

1. exclude/include supports tag matching for "!!GROUP=", "!!GROUPID=", and "!!INSERT=".
2. exclude/include supports multiple expressions separated by the delimiter '`'.
3. Supports parsing multi-line contents for "url=", "rename=", and "exclude/include=" by merging them into a single line using delimiters.
This commit is contained in:
Se7enMuting 2025-05-16 20:01:13 +08:00 committed by GitHub
parent e17246df7c
commit c2610a3804
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 113 additions and 16 deletions

View File

@ -174,13 +174,13 @@ int addNodes(std::string link, std::vector<Proxy> &allNodes, int groupID, parse_
if(!getSubInfoFromHeader(extra_headers, subInfo))
getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo);
}
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
for(Proxy &x : nodes)
{
x.GroupId = groupID;
if(custom_group.size())
x.Group = custom_group;
}
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
copyNodes(nodes, allNodes);
}
else
@ -206,13 +206,13 @@ int addNodes(std::string link, std::vector<Proxy> &allNodes, int groupID, parse_
{
getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo);
}
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
for(Proxy &x : nodes)
{
x.GroupId = groupID;
if(!custom_group.empty())
x.Group = custom_group;
}
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
copyNodes(nodes, allNodes);
break;
default:

View File

@ -531,10 +531,58 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
ext.rename_array = safe_get_renames();
/// check custom include/exclude settings
if(!argIncludeRemark.empty() && regValid(argIncludeRemark))
lIncludeRemarks = string_array{argIncludeRemark};
if(!argExcludeRemark.empty() && regValid(argExcludeRemark))
lExcludeRemarks = string_array{argExcludeRemark};
if (!argIncludeRemark.empty())
{
// Check if the delimiter ` is present
if (argIncludeRemark.find('`') != std::string::npos)
{
// Split argIncludeRemark using ` as the delimiter
string_array splitIncludeRemarks = split(argIncludeRemark, "`");
// Filter out invalid regular expressions
string_array tempValidRemarks;
for (const auto& remark : splitIncludeRemarks)
{
if (!remark.empty() && regValid(remark)) // Validate each split element using regValid
{
tempValidRemarks.push_back(remark);
}
}
if (!tempValidRemarks.empty())
lIncludeRemarks = tempValidRemarks;
}
else
{
// If no delimiter is found, follow the original logic
if (regValid(argIncludeRemark))
lIncludeRemarks = string_array{argIncludeRemark};
}
}
if (!argExcludeRemark.empty())
{
// Check if the delimiter ` is present
if (argExcludeRemark.find('`') != std::string::npos)
{
// Split argExcludeRemark using ` as the delimiter
string_array splitExcludeRemarks = split(argExcludeRemark, "`");
// Filter out invalid regular expressions
string_array tempValidRemarks;
for (const auto& remark : splitExcludeRemarks)
{
if (!remark.empty() && regValid(remark)) // Validate each split element using regValid
{
tempValidRemarks.push_back(remark);
}
}
if (!tempValidRemarks.empty())
lExcludeRemarks = tempValidRemarks;
}
else
{
// If no delimiter is found, follow the original logic
if (regValid(argExcludeRemark))
lExcludeRemarks = string_array{argExcludeRemark};
}
}
/// initialize script runtime
if(authorized && !global.scriptCleanContext)
@ -569,6 +617,8 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
{
groupID = -1;
urls = split(global.insertUrls, "|");
// Remove empty urls
urls.erase(std::remove_if(urls.begin(), urls.end(), [](const std::string& str) { return str.empty(); }), urls.end());
importItems(urls, true);
for(std::string &x : urls)
{
@ -588,6 +638,8 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
}
urls = split(argUrl, "|");
// Remove empty urls
urls.erase(std::remove_if(urls.begin(), urls.end(), [](const std::string& str) { return str.empty(); }), urls.end());
importItems(urls, true);
groupID = 0;
for(std::string &x : urls)
@ -1154,6 +1206,34 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
return YAML::Dump(clash);
}
// Merge multiple key-values based on delimiters
static void merge_values(const string_multimap& source,
const std::string& key,
std::string& merged,
char delimiter)
{
auto range = source.equal_range(key);
for (auto iter = range.first; iter != range.second; ++iter)
{
if (!iter->second.empty())
{
if (!merged.empty()) merged += delimiter;
merged += iter->second;
}
}
}
// Update container
static void update_container(string_multimap& container,
const std::string& key,
const std::string& merged)
{
if (!merged.empty()) {
container.erase(key);
container.emplace(key, merged);
}
}
std::string getProfile(RESPONSE_CALLBACK_ARGS)
{
auto &argument = request.argument;
@ -1221,14 +1301,13 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
return "Forbidden";
}
}
/// check if more than one profile is provided
// Handle url
std::string all_urls;
merge_values(contents, "url", all_urls, '|');
if(profiles.size() > 1)
{
writeLog(0, "Multiple profiles are provided. Trying to combine profiles...", LOG_TYPE_INFO);
std::string all_urls, url;
auto iter = contents.find("url");
if(iter != contents.end())
all_urls = iter->second;
for(size_t i = 1; i < profiles.size(); i++)
{
name = profiles[i];
@ -1242,10 +1321,12 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
writeLog(0, "Ignoring broken profile '" + name + "'...", LOG_LEVEL_WARNING);
continue;
}
url = ini.get("Profile", "url");
if(!url.empty())
string_multimap profile_items;
ini.get_items("Profile", profile_items); // Get all key-value pairs in the [Profile] section
size_t before_length = all_urls.length();
merge_values(profile_items, "url", all_urls, '|');
if (all_urls.length() > before_length)
{
all_urls += "|" + url;
writeLog(0, "Profile url from '" + name + "' added.", LOG_LEVEL_INFO);
}
else
@ -1253,8 +1334,24 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
writeLog(0, "Profile '" + name + "' does not have url key. Skipping...", LOG_LEVEL_INFO);
}
}
iter->second = all_urls;
}
// Update the url key-value pairs in contents uniformly
update_container(contents, "url", all_urls);
// Handle rename
std::string all_renames;
merge_values(contents, "rename", all_renames, '`');
update_container(contents, "rename", all_renames);
// Handle exclude
std::string all_excludes;
merge_values(contents, "exclude", all_excludes, '`');
update_container(contents, "exclude", all_excludes);
// Handle include
std::string all_includes;
merge_values(contents, "include", all_includes, '`');
update_container(contents, "include", all_includes);
contents.emplace("token", token);
contents.emplace("profile_data", base64Encode(global.managedConfigPrefix + "/getprofile?" + joinArguments(argument)));