mirror of
https://github.com/asdlokj1qpi233/subconverter.git
synced 2025-10-27 20:03:01 +00:00
Add support for exporting complete Quantumult X configuration file
This commit is contained in:
@@ -27,6 +27,9 @@ surfboard_rule_base=surfboard.conf
|
||||
;Mellow config base used by the generator, supports local files/URL
|
||||
mellow_rule_base=mellow.conf
|
||||
|
||||
;Quantumult X config base used by the generator, supports local files/URL
|
||||
quanx_rule_base=quanx.conf
|
||||
|
||||
;Proxy used to download rulesets or subscriptions, set to NONE or empty to disable it, set to SYSTEM to use system proxy.
|
||||
;Accept cURL-supported proxies (http:// https:// socks4a:// socks5://)
|
||||
proxy_ruleset=SYSTEM
|
||||
|
||||
37
base/quanx.conf
Normal file
37
base/quanx.conf
Normal file
@@ -0,0 +1,37 @@
|
||||
[general]
|
||||
excluded_routes=192.168.0.0/16, 172.16.0.0/12, 100.64.0.0/10, 10.0.0.0/8
|
||||
geo_location_checker=http://ip-api.com/json/?lang=zh-CN, https://github.com/KOP-XIAO/QuantumultX/raw/master/Scripts/IP_API.js
|
||||
network_check_url=http://www.baidu.com/
|
||||
server_check_url=http://www.gstatic.com/generate_204
|
||||
|
||||
[dns]
|
||||
server=119.29.29.29
|
||||
server=223.5.5.5
|
||||
server=1.0.0.1
|
||||
server=8.8.8.8
|
||||
|
||||
[policy]
|
||||
static=♻️ 自动选择, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Auto.png
|
||||
static=🔰 节点选择, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Proxy.png
|
||||
static=🌍 国外媒体, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/GlobalMedia.png
|
||||
static=🌏 国内媒体, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/DomesticMedia.png
|
||||
static=Ⓜ️ 微软服务, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Microsoft.png
|
||||
static=📲 电报信息, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Telegram.png
|
||||
static=🍎 苹果服务, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Apple.png
|
||||
static=🎯 全球直连, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Direct.png
|
||||
static=🛑 全球拦截, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Advertising.png
|
||||
static=🐟 漏网之鱼, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Final.png
|
||||
|
||||
[server_remote]
|
||||
|
||||
[filter_remote]
|
||||
|
||||
[rewrite_remote]
|
||||
|
||||
[server_local]
|
||||
|
||||
[filter_local]
|
||||
|
||||
[rewrite_local]
|
||||
|
||||
[mitm]
|
||||
@@ -775,7 +775,10 @@ public:
|
||||
return;
|
||||
eraseElements(ini_content.at(section));
|
||||
if(cached_section == section)
|
||||
{
|
||||
eraseElements(cached_section_content);
|
||||
cached_section.erase();
|
||||
}
|
||||
//section_order.erase(std::find(section_order.begin(), section_order.end(), section));
|
||||
}
|
||||
|
||||
|
||||
16
src/main.cpp
16
src/main.cpp
@@ -40,7 +40,7 @@ std::string proxy_ruleset, proxy_subscription;
|
||||
|
||||
std::string clash_rule_base;
|
||||
string_array clash_extra_group;
|
||||
std::string surge_rule_base, surfboard_rule_base, mellow_rule_base;
|
||||
std::string surge_rule_base, surfboard_rule_base, mellow_rule_base, quanx_rule_base;
|
||||
std::string surge_ssr_path;
|
||||
|
||||
//pre-compiled rule bases
|
||||
@@ -172,6 +172,8 @@ void readConf()
|
||||
surfboard_rule_base = ini.Get("surfboard_rule_base");
|
||||
if(ini.ItemExist("mellow_rule_base"))
|
||||
mellow_rule_base = ini.Get("mellow_rule_base");
|
||||
if(ini.ItemExist("quanx_rule_base"))
|
||||
quanx_rule_base = ini.Get("quanx_rule_base");
|
||||
if(ini.ItemExist("append_proxy_type"))
|
||||
append_proxy_type = ini.GetBool("append_proxy_type");
|
||||
if(ini.ItemExist("proxy_ruleset"))
|
||||
@@ -672,9 +674,19 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
else if(target == "quanx")
|
||||
{
|
||||
std::cerr<<"Quantumult X"<<std::endl;
|
||||
output_content = netchToQuanX(nodes, ext);
|
||||
|
||||
if(fileExist(quanx_rule_base))
|
||||
base_content = fileGet(quanx_rule_base, false);
|
||||
else
|
||||
base_content = webGet(quanx_rule_base, getSystemProxy());
|
||||
|
||||
output_content = netchToQuanX(nodes, base_content, rca, extra_group, ext);
|
||||
|
||||
if(upload == "true")
|
||||
uploadGist("quanx", upload_path, output_content, false);
|
||||
|
||||
if(write_managed_config && managed_config_prefix.size())
|
||||
output_content = "#!MANAGED-CONFIG " + managed_config_prefix + "/sub?" + argument + "\n\n" + output_content;
|
||||
}
|
||||
else if(target == "ssd")
|
||||
{
|
||||
|
||||
@@ -355,11 +355,23 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
std::string rule_group, rule_path, retrived_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
|
||||
base_rule.SetCurrentSection("Rule");
|
||||
switch(surge_ver)
|
||||
{
|
||||
case 0:
|
||||
base_rule.SetCurrentSection("RoutingRule"); //Mellow
|
||||
break;
|
||||
case -1:
|
||||
base_rule.SetCurrentSection("filter_local"); //Quantumult X
|
||||
break;
|
||||
default:
|
||||
base_rule.SetCurrentSection("Rule");
|
||||
}
|
||||
|
||||
if(overwrite_original_rules)
|
||||
base_rule.EraseSection();
|
||||
|
||||
const std::string rule_match_regex = "^(.*?,.*?)(,.*)(,.*)$";
|
||||
|
||||
for(ruleset_content &x : ruleset_content_array)
|
||||
{
|
||||
rule_group = x.rule_group;
|
||||
@@ -369,7 +381,21 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
strLine = retrived_rules.substr(2);
|
||||
if(strLine == "MATCH")
|
||||
strLine = "FINAL";
|
||||
allRules.emplace_back(strLine + "," + rule_group);
|
||||
strLine += "," + rule_group;
|
||||
if(surge_ver == -1)
|
||||
{
|
||||
if(std::count(strLine.begin(), strLine.end(), ',') > 2 && regReplace(strLine, rule_match_regex, "$2") == ",no-resolve")
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
else
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(std::count(strLine.begin(), strLine.end(), ',') > 2)
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
}
|
||||
strLine = replace_all_distinct(strLine, ",,", ",");
|
||||
allRules.emplace_back(strLine);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -390,12 +416,24 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
strStrm<<retrived_rules;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
if(surge_ver == -1 && (strLine.find("IP-CIDR6") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0 || strLine.find("AND") == 0 || strLine.find("OR") == 0)) //remove unsupported types
|
||||
continue;
|
||||
strLine = replace_all_distinct(strLine, "\r", ""); //remove line break
|
||||
if(!strLine.size() || strLine.find("#") == 0 || strLine.find(";") == 0) //remove comments
|
||||
continue;
|
||||
strLine += "," + rule_group;
|
||||
if(std::count(strLine.begin(), strLine.end(), ',') > 2)
|
||||
strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2");
|
||||
if(surge_ver == -1)
|
||||
{
|
||||
if(std::count(strLine.begin(), strLine.end(), ',') > 2 && regReplace(strLine, rule_match_regex, "$2") == ",no-resolve")
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
else
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(std::count(strLine.begin(), strLine.end(), ',') > 2)
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
}
|
||||
allRules.emplace_back(strLine);
|
||||
}
|
||||
}
|
||||
@@ -1302,7 +1340,29 @@ std::string netchToQuan(std::vector<nodeInfo> &nodes, extra_settings &ext)
|
||||
return base64_encode(allLinks);
|
||||
}
|
||||
|
||||
std::string netchToQuanX(std::vector<nodeInfo> &nodes, extra_settings &ext)
|
||||
std::string netchToQuanX(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext)
|
||||
{
|
||||
INIReader ini;
|
||||
ini.store_any_line = true;
|
||||
if(!ext.nodelist && ini.Parse(base_conf) != 0)
|
||||
return std::string();
|
||||
|
||||
netchToQuanX(nodes, ini, ruleset_content_array, extra_proxy_group, ext);
|
||||
|
||||
if(!ext.nodelist)
|
||||
{
|
||||
string_array allnodes;
|
||||
ini.GetAll("server_local", "{NONAME}", allnodes);
|
||||
std::string allLinks = std::accumulate(allnodes.begin(), allnodes.end(), allnodes[0], [](std::string a, std::string b)
|
||||
{
|
||||
return std::move(a) + "\n" + std::move(b);
|
||||
});
|
||||
return allLinks;
|
||||
}
|
||||
return ini.ToString();
|
||||
}
|
||||
|
||||
void netchToQuanX(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext)
|
||||
{
|
||||
rapidjson::Document json;
|
||||
std::string type;
|
||||
@@ -1310,8 +1370,9 @@ std::string netchToQuanX(std::vector<nodeInfo> &nodes, extra_settings &ext)
|
||||
std::string password, plugin, pluginopts;
|
||||
std::string id, transproto, host, path;
|
||||
std::string protocol, protoparam, obfs, obfsparam;
|
||||
std::string proxyStr, allLinks;
|
||||
std::string proxyStr;
|
||||
bool tlssecure;
|
||||
std::vector<nodeInfo> nodelist;
|
||||
|
||||
std::for_each(nodes.begin(), nodes.end(), [ext](nodeInfo &x)
|
||||
{
|
||||
@@ -1331,6 +1392,8 @@ std::string netchToQuanX(std::vector<nodeInfo> &nodes, extra_settings &ext)
|
||||
});
|
||||
}
|
||||
|
||||
ini.SetCurrentSection("server_local");
|
||||
ini.EraseSection();
|
||||
for(nodeInfo &x : nodes)
|
||||
{
|
||||
json.Parse(x.proxyStr.data());
|
||||
@@ -1391,10 +1454,99 @@ std::string netchToQuanX(std::vector<nodeInfo> &nodes, extra_settings &ext)
|
||||
if(ext.udp)
|
||||
proxyStr += ", udp-relay=true";
|
||||
proxyStr += ", tag=" + remark;
|
||||
allLinks += proxyStr + "\n";
|
||||
|
||||
ini.Set("{NONAME}", proxyStr);
|
||||
nodelist.emplace_back(x);
|
||||
}
|
||||
|
||||
return allLinks;
|
||||
if(ext.nodelist)
|
||||
return;
|
||||
|
||||
string_multimap original_groups;
|
||||
string_array filtered_nodelist;
|
||||
ini.SetCurrentSection("policy");
|
||||
ini.GetItems(original_groups);
|
||||
ini.EraseSection();
|
||||
|
||||
std::string singlegroup;
|
||||
std::string name, proxies;
|
||||
string_array vArray;
|
||||
for(std::string &x : extra_proxy_group)
|
||||
{
|
||||
eraseElements(filtered_nodelist);
|
||||
unsigned int rules_upper_bound = 0;
|
||||
|
||||
vArray = split(x, "`");
|
||||
if(vArray.size() < 3)
|
||||
continue;
|
||||
|
||||
if(vArray[1] == "select")
|
||||
{
|
||||
type = "static";
|
||||
rules_upper_bound = vArray.size();
|
||||
}
|
||||
else if(vArray[1] == "url-test")
|
||||
{
|
||||
if(vArray.size() < 5)
|
||||
continue;
|
||||
type = "static";
|
||||
rules_upper_bound = vArray.size() - 2;
|
||||
}
|
||||
else if(vArray[1] == "fallback")
|
||||
{
|
||||
if(vArray.size() < 5)
|
||||
continue;
|
||||
type = "available";
|
||||
rules_upper_bound = vArray.size() - 2;
|
||||
}
|
||||
else if(vArray[1] == "load-balance")
|
||||
{
|
||||
if(vArray.size() < 5)
|
||||
continue;
|
||||
type = "round-robin";
|
||||
rules_upper_bound = vArray.size() - 2;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
name = vArray[0];
|
||||
|
||||
for(unsigned int i = 2; i < rules_upper_bound; i++)
|
||||
groupGenerate(vArray[i], nodelist, filtered_nodelist, true);
|
||||
|
||||
if(!filtered_nodelist.size())
|
||||
filtered_nodelist.emplace_back("direct");
|
||||
|
||||
auto iter = std::find_if(original_groups.begin(), original_groups.end(), [name](const string_multimap::value_type &n)
|
||||
{
|
||||
std::string groupdata = n.second;
|
||||
std::string::size_type cpos = groupdata.find(",");
|
||||
if(cpos != std::string::npos)
|
||||
return trim(groupdata.substr(0, cpos)) == name;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
if(iter != original_groups.end())
|
||||
{
|
||||
vArray = split(iter->second, ",");
|
||||
if(vArray.size() > 1)
|
||||
{
|
||||
if(trim(vArray[vArray.size() - 1]).find("img-url") == 0)
|
||||
filtered_nodelist.emplace_back(trim(vArray[vArray.size() - 1]));
|
||||
}
|
||||
}
|
||||
|
||||
proxies = std::accumulate(std::next(filtered_nodelist.begin()), filtered_nodelist.end(), filtered_nodelist[0], [](std::string a, std::string b)
|
||||
{
|
||||
return std::move(a) + ", " + std::move(b);
|
||||
});
|
||||
|
||||
singlegroup = type + "=" + name + ", " + proxies;
|
||||
ini.Set("{NONAME}", singlegroup);
|
||||
}
|
||||
|
||||
if(ext.enable_rule_generator)
|
||||
rulesetToSurge(ini, ruleset_content_array, -1, ext.overwrite_original_rules);
|
||||
}
|
||||
|
||||
std::string netchToSSD(std::vector<nodeInfo> &nodes, std::string &group, extra_settings &ext)
|
||||
@@ -1675,8 +1827,7 @@ void netchToMellow(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rul
|
||||
}
|
||||
|
||||
if(ext.enable_rule_generator)
|
||||
rulesetToSurge(ini, ruleset_content_array, 2, ext.overwrite_original_rules);
|
||||
ini.RenameSection("Rule", "RoutingRule");
|
||||
rulesetToSurge(ini, ruleset_content_array, 0, ext.overwrite_original_rules);
|
||||
}
|
||||
|
||||
std::string buildGistData(std::string name, std::string content)
|
||||
|
||||
@@ -39,7 +39,8 @@ std::string netchToSS(std::vector<nodeInfo> &nodes, extra_settings &ext);
|
||||
std::string netchToSSSub(std::vector<nodeInfo> &nodes, extra_settings &ext);
|
||||
std::string netchToSSR(std::vector<nodeInfo> &nodes, extra_settings &ext);
|
||||
std::string netchToVMess(std::vector<nodeInfo> &nodes, extra_settings &ext);
|
||||
std::string netchToQuanX(std::vector<nodeInfo> &nodes, extra_settings &ext);
|
||||
std::string netchToQuanX(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
|
||||
void netchToQuanX(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
|
||||
std::string netchToQuan(std::vector<nodeInfo> &nodes, extra_settings &ext);
|
||||
std::string netchToSSD(std::vector<nodeInfo> &nodes, std::string &group, extra_settings &ext);
|
||||
std::string buildGistData(std::string name, std::string content);
|
||||
|
||||
Reference in New Issue
Block a user