Enhancements

Fix when generating configurations with external configuration file with rule generator disabled, generated rules will still be added.
Add support for configuration files in YAML format.
Optimize codes.
This commit is contained in:
Tindy X
2020-02-07 00:11:35 +08:00
parent 5b5ae714f7
commit 8998e646e7
3 changed files with 430 additions and 16 deletions

View File

@@ -0,0 +1,22 @@
custom:
enable_rule_generator: false
overwrite_original_rules: false
custom_proxy_group:
- {import: snippets/groups_forcerule.txt}
# surge_ruleset:
# - {import: snippets/ruleset_remote.txt}
clash_rule_base: base/forcerule.yml
# surge_rule_base: base/surge.conf
# surfboard_rule_base: base/surfboard.conf
# mellow_rule_base: base/mellow.conf
# quan_rule_base: base/quan.conf
# quanx_rule_base: base/quanx.conf
# rename_node:
# - {import: snippet/rename.txt}
# emoji:
# - {import: snippets/emoji.txt}

75
base/pref.yml Normal file
View File

@@ -0,0 +1,75 @@
common:
api_mode: false
api_access_token: password
default_url: []
exclude_remarks: ["(到期|剩余流量|时间|官网|产品)"]
include_remarks: []
clash_rule_base: base/simple_base.yml
surge_rule_base: base/surge.conf
surfboard_rule_base: base/surfboard.conf
mellow_rule_base: base/mellow.conf
quan_rule_base: base/quan.conf
quanx_rule_base: base/quanx.conf
proxy_ruleset: SYSTEM
proxy_subscription: NONE
append_proxy_type: false
userinfo:
stream_rule:
- {match: "^剩余流量:(.*?) (.*)$", replace: "total=$1&left=$2"}
- {match: "^Bandwidth: (.*?)/(.*)$", replace: "used=$1&total=$2"}
- {match: "^.*剩余(.*?)(?:\\s*?)@(?:.*)$", replace: "total=$1"}
time_rule:
- {match: "^过期时间:(\\d+)-(\\d+)-(\\d+) (\\d+):(\\d+):(\\d+)$", replace: "$1:$2:$3:$4:$5:$6"}
- {match: "^到期时间:(\\d+)-(\\d+)-(\\d+)$", replace: "$1:$2:$3:0:0:0"}
- {match: "^Smart Access expire: (\\d+)/(\\d+)/(\\d+)$", replace: "$1:$2:$3:0:0:0"}
node_pref:
udp_flag: false
tcp_fast_open_flag: false
sort_flag: false
skip_cert_verify_flag: false
rename_node:
# - {match: "\\(?((x|X)?(\\d+)(\\.?\\d+)?)((\\s?倍率?)|(x|X))\\)?", replace: "$1x"}
- {import: snippets/rename_node.txt}
managed_config:
write_managed_config: true
managed_config_prefix: "http://127.0.0.1:25500"
surge_external_proxy:
surge_ssr_path: "" # /usr/bin/ssr-local
emojis:
add_emoji: true
remove_old_emoji: true
rules:
# - {match: "(流量|时间|应急)", emoji: "🏳️‍🌈"}
- {import: snippets/emoji.txt}
ruleset:
enabled: true
overwrite_original_rules: false
update_ruleset_on_request: false
surge_ruleset:
# - {rule: "GEOIP,CN", group: "DIRECT"}
# - {ruleset: "rules/LocalAreaNetwork.list", group: "DIRECT"}
- {import: snippets/rulesets.txt}
proxy_group:
custom_proxy_group:
# - {name: UrlTest, type: url-test, rule: [".*"], url: http://www.gstatic.com/generate_204, interval: 300}
# - {name: Proxy, type: select, rule: [".*"]}
# - {name: group1, type: select, rule: ["!!GROUPID=0"]}
# - {name: v2ray, type: select, rule: ["!!GROUP=V2RayProvider"]}
# - {import: snippets/groups_forcerule.txt}
- {import: snippets/groups.txt}
server:
listen: 0.0.0.0
port: 25500
advanced:
print_debug_info: false
max_pending_connections: 10240
max_concurrent_threads: 4

View File

@@ -2,6 +2,7 @@
#include <string>
#include <mutex>
#include <unistd.h>
#include <numeric>
#include <yaml-cpp/yaml.h>
@@ -19,7 +20,7 @@
#include "logger.h"
//common settings
std::string pref_path = "pref.ini";
std::string pref_path = "pref.yml";
bool generator_mode = false;
string_array def_exclude_remarks, def_include_remarks, rulesets, stream_rules, time_rules;
std::vector<ruleset_content> ruleset_content_array;
@@ -50,6 +51,12 @@ INIReader surge_base, mellow_base;
string_array regex_blacklist = {"(.*)*"};
template <typename T> void operator >> (const YAML::Node& node, T& i)
{
if(node.IsDefined()) //fail-safe
i = node.as<T>();
};
#ifndef _WIN32
void SetConsoleTitle(std::string title)
{
@@ -122,6 +129,135 @@ int importItems(string_array &target)
return 0;
}
void readRegexMatch(YAML::Node node, std::string delimiter, string_array &dest)
{
YAML::Node object;
std::string url, match, rep, strLine;
for(unsigned i = 0; i < node.size(); i++)
{
object = node[i];
object["import"] >> url;
if(url.size())
{
url = "!!import:" + url;
dest.emplace_back(url);
continue;
}
object["match"] >> match;
object["replace"] >> rep;
if(match.size() && rep.size())
strLine = match + delimiter + rep;
else
continue;
dest.emplace_back(strLine);
}
importItems(dest);
}
void readEmoji(YAML::Node node, string_array &dest)
{
YAML::Node object;
std::string url, match, rep, strLine;
for(unsigned i = 0; i < node.size(); i++)
{
object = node[i];
object["import"] >> url;
if(url.size())
{
url = "!!import:" + url;
dest.emplace_back(url);
continue;
}
object["match"] >> match;
object["emoji"] >> rep;
if(match.size() && rep.size())
strLine = match + "," + rep;
else
continue;
dest.emplace_back(strLine);
}
importItems(dest);
}
void readGroup(YAML::Node node, string_array &dest)
{
std::string strLine, name, type, url, interval;
string_array tempArray;
YAML::Node object;
unsigned int i, j;
for(i = 0; i < node.size(); i++)
{
eraseElements(tempArray);
object = node[i];
object["import"] >> name;
if(name.size())
{
name = "!!import:" + name;
dest.emplace_back(name);
continue;
}
url = "http://www.gstatic.com/generate_204", interval = "300";
object["name"] >> name;
object["type"] >> type;
tempArray.emplace_back(name);
tempArray.emplace_back(type);
if(object["url"].as<std::string>().size())
object["url"] >> url;
if(object["interval"].as<std::string>().size())
object["interval"] >> interval;
for(j = 0; j < object["rule"].size(); j++)
tempArray.emplace_back(object["rule"][i].as<std::string>());
if(type != "select")
{
tempArray.emplace_back(url);
tempArray.emplace_back(interval);
}
if((type == "select" && tempArray.size() < 3) || (type != "select" && tempArray.size() < 5))
continue;
strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b) -> std::string
{
return std::move(a) + "`" + std::move(b);
});
dest.emplace_back(strLine);
}
importItems(dest);
}
void readRuleset(YAML::Node node, string_array &dest)
{
std::string strLine, name, url, group;
YAML::Node object;
for(unsigned int i = 0; i < node.size(); i++)
{
name = "";
object = node[i];
object["import"] >> name;
if(name.size())
{
name = "!!import:" + name;
dest.emplace_back(name);
continue;
}
object["ruleset"] >> url;
object["group"] >> group;
object["rule"] >> name;
if(url.size())
strLine = group + "," + url;
else if(name.size())
strLine = group + ",[]" + name;
else
continue;
dest.emplace_back(strLine);
}
importItems(dest);
}
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &rca)
{
eraseElements(rca);
@@ -177,10 +313,151 @@ void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &r
}
}
void readYAMLConf(YAML::Node &node)
{
YAML::Node section = node["common"];
std::string strLine;
string_array tempArray;
section["api_mode"] >> api_mode;
section["api_access_token"] >> access_token;
if(section["default_url"].IsSequence())
{
section["default_url"] >> tempArray;
if(tempArray.size())
strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b)
{
return std::move(a) + "|" + std::move(b);
});
default_url = strLine;
}
if(section["exclude_remarks"].IsSequence())
section["exclude_remarks"] >> def_exclude_remarks;
if(section["include_remarks"].IsSequence())
section["include_remarks"] >> def_include_remarks;
section["clash_rule_base"] >> clash_rule_base;
section["surge_rule_base"] >> surge_rule_base;
section["surfboard_rule_base"] >> surfboard_rule_base;
section["mellow_rule_base"] >> mellow_rule_base;
section["quan_rule_base"] >> quan_rule_base;
section["quanx_rule_base"] >> quanx_rule_base;
section["append_proxy_type"] >> append_proxy_type;
section["proxy_ruleset"] >> proxy_ruleset;
section["proxy_subscription"] >> proxy_subscription;
if(node["userinfo"].IsDefined())
{
section = node["userinfo"];
if(section["stream_rule"].IsSequence())
{
readRegexMatch(section["stream_rule"], "@", tempArray);
safe_set_streams(tempArray);
eraseElements(tempArray);
}
if(section["time_rule"].IsSequence())
{
readRegexMatch(section["time_rule"], "@", tempArray);
safe_set_times(tempArray);
eraseElements(tempArray);
}
}
if(node["node_pref"].IsDefined())
{
section = node["node_pref"];
section["udp_flag"] >> udp_flag;
section["tcp_fast_open_flag"] >> tfo_flag;
section["sort_flag"] >> do_sort;
section["skip_cert_verify_flag"] >> scv_flag;
}
if(section["rename_node"].IsSequence())
{
readRegexMatch(section["rename_node"], "@", tempArray);
safe_set_renames(tempArray);
eraseElements(tempArray);
}
if(node["managed_config"].IsDefined())
{
section = node["managed_config"];
section["write_managed_config"] >> write_managed_config;
section["managed_config_prefix"] >> managed_config_prefix;
}
if(node["surge_external_proxy"].IsDefined())
node["surge_external_proxy"]["surge_ssr_path"] >> surge_ssr_path;
if(node["emojis"].IsDefined())
{
section = node["emojis"];
section["add_emoji"] >> add_emoji;
section["remove_old_emoji"] >> remove_old_emoji;
if(section["rules"].IsSequence())
{
readEmoji(section["rules"], tempArray);
safe_set_emojis(tempArray);
eraseElements(tempArray);
}
}
if(node["ruleset"].IsDefined())
{
section = node["ruleset"];
section["enabled"] >> enable_rule_generator;
if(!enable_rule_generator)
{
overwrite_original_rules = false;
update_ruleset_on_request = false;
}
else
{
section["overwrite_original_rules"] >> overwrite_original_rules;
section["update_ruleset_on_request"] >> update_ruleset_on_request;
}
if(section["surge_ruleset"].IsSequence())
readRuleset(section["surge_ruleset"], rulesets);
}
if(node["proxy_group"].IsDefined() && node["proxy_group"]["custom_proxy_group"].IsDefined())
readGroup(node["proxy_group"]["custom_proxy_group"], clash_extra_group);
if(node["server"].IsDefined())
{
node["server"]["listen"] >> listen_address;
node["server"]["port"] >> listen_port;
}
if(node["advanced"].IsDefined())
{
node["advanced"]["print_debug_info"] >> print_debug_info;
node["advanced"]["max_pending_connections"] >> max_pending_connections;
node["advanced"]["max_concurrent_threads"] >> max_concurrent_threads;
}
}
void readConf()
{
guarded_mutex guard(on_configuring);
std::cerr<<"Reading preference settings..."<<std::endl;
eraseElements(def_exclude_remarks);
eraseElements(def_include_remarks);
eraseElements(clash_extra_group);
eraseElements(rulesets);
try
{
YAML::Node yaml = YAML::LoadFile(pref_path);
if(yaml.size() && yaml["common"])
return readYAMLConf(yaml);
}
catch (YAML::Exception &e)
{
//ignore
}
INIReader ini;
ini.allow_dup_section_titles = true;
//ini.do_utf8_to_gbk = true;
@@ -191,10 +468,6 @@ void readConf()
return;
}
eraseElements(def_exclude_remarks);
eraseElements(def_include_remarks);
eraseElements(clash_extra_group);
eraseElements(rulesets);
string_array tempArray;
ini.EnterSection("common");
@@ -354,6 +627,34 @@ struct ExternalConfig
bool enable_rule_generator = true;
};
int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
{
YAML::Node section = node["custom"], object;
std::string name, type, url, interval;
std::string group, strLine;
section["clash_rule_base"] >> ext.clash_rule_base;
section["surge_rule_base"] >> ext.surge_rule_base;
section["surfboard_rule_base"] >> ext.surfboard_rule_base;
section["mellow_rule_base"] >> ext.mellow_rule_base;
section["quan_rule_base"] >> ext.quan_rule_base;
section["quanx_rule_base"] >> ext.quanx_rule_base;
ext.enable_rule_generator = section["enable_rule_generator"].as<bool>();
ext.overwrite_original_rules = section["overwrite_original_rules"].as<bool>();
if(section["custom_proxy_group"].size())
readGroup(section["custom_proxy_group"], ext.custom_proxy_group);
if(section["surge_ruleset"].size())
readRuleset(section["surge_ruleset"], ext.surge_ruleset);
if(section["rename_node"].size())
readRegexMatch(section["rename_node"], "@", ext.rename);
return 0;
}
int loadExternalConfig(std::string &path, ExternalConfig &ext, std::string proxy)
{
std::string base_content;
@@ -362,6 +663,17 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext, std::string proxy
else
base_content = webGet(path, proxy);
try
{
YAML::Node yaml = YAML::Load(base_content);
if(yaml.size() && yaml["custom"])
return loadExternalYAML(yaml, ext);
}
catch (YAML::Exception &e)
{
//ignore
}
INIReader ini;
ini.store_isolated_line = true;
ini.SetIsolatedItemsSection("custom");
@@ -458,7 +770,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
std::vector<ruleset_content> rca;
extra_settings ext;
std::string subInfo;
bool ruleset_group_updated = false;
bool ruleset_updated = false;
if(std::find(regex_blacklist.cbegin(), regex_blacklist.cend(), include) != regex_blacklist.cend() || std::find(regex_blacklist.cbegin(), regex_blacklist.cend(), exclude) != regex_blacklist.cend())
return "Invalid request!";
@@ -528,13 +840,16 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
{
extra_ruleset = extconf.surge_ruleset;
refreshRulesets(extra_ruleset, rca);
ruleset_group_updated = true;
ruleset_updated = true;
}
else
{
if(update_ruleset_on_request)
refreshRulesets(rulesets, ruleset_content_array);
rca = ruleset_content_array;
if(ext.enable_rule_generator)
{
if(update_ruleset_on_request)
refreshRulesets(rulesets, ruleset_content_array);
rca = ruleset_content_array;
}
}
}
else
@@ -545,8 +860,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
extra_group = split(groups, "@");
if(!extra_group.size())
extra_group = clash_extra_group;
else
ruleset_group_updated = true;
}
else
extra_group = clash_extra_group;
@@ -564,7 +877,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
else
{
refreshRulesets(extra_ruleset, rca);
ruleset_group_updated = true;
ruleset_updated = true;
}
}
else
@@ -650,7 +963,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(target == "clash" || target == "clashr")
{
std::cerr<<"Clash"<<((target == "clashr") ? "R" : "")<<std::endl;
if(ruleset_group_updated || update_ruleset_on_request || ext_clash_base != clash_rule_base)
if(ruleset_updated || update_ruleset_on_request || ext_clash_base != clash_rule_base)
{
if(fileExist(ext_clash_base))
base_content = fileGet(ext_clash_base, false);
@@ -661,7 +974,8 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
else
{
YAML::Node yamlnode = clash_base;
YAML::Node yamlnode;
yamlnode = clash_base;
netchToClash(nodes, yamlnode, extra_group, target == "clashr", ext);
output_content = YAML::Dump(yamlnode);
}
@@ -704,7 +1018,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
else if(target == "mellow")
{
std::cerr<<"Mellow"<<std::endl;
if(ruleset_group_updated || update_ruleset_on_request || ext_mellow_base != mellow_rule_base)
if(ruleset_updated || update_ruleset_on_request || ext_mellow_base != mellow_rule_base)
{
if(fileExist(ext_mellow_base))
base_content = fileGet(ext_mellow_base, false);
@@ -942,7 +1256,10 @@ int main(int argc, char *argv[])
{
std::string token = getUrlArg(argument, "token");
if(token != access_token)
{
*status_code = 403;
return "Unauthorized\n";
}
}
std::string type = getUrlArg(argument, "type");
if(type == "form")