mirror of
https://github.com/asdlokj1qpi233/subconverter.git
synced 2025-10-28 20:32:42 +00:00
Enhancements
Add insert_url option to add node(s) before all exported subscriptions. Add /getprofile interface for easy access. Add an option to control whether to add Subscription-UserInfo or not. Add include/exclude settings to external configurations. Allow loading local subscriptions in API mode if the right token is provided. Optimize codes.
This commit is contained in:
@@ -51,3 +51,7 @@ clash_rule_base=base/forcerule.yml
|
||||
;Options for adding emojis
|
||||
;emoji=(流量|时间|应急),🏳️🌈
|
||||
;emoji=阿根廷,🇦🇷
|
||||
|
||||
;Optims for filtering nodes
|
||||
;include_remarks=
|
||||
;exclude_remarks=
|
||||
|
||||
@@ -20,3 +20,6 @@ custom:
|
||||
|
||||
# emoji:
|
||||
# - {import: snippets/emoji.txt}
|
||||
|
||||
# include_remarks: []
|
||||
# exclude_remarks: []
|
||||
|
||||
@@ -2,6 +2,7 @@ common:
|
||||
api_mode: false
|
||||
api_access_token: password
|
||||
default_url: []
|
||||
insert_url: []
|
||||
exclude_remarks: ["(到期|剩余流量|时间|官网|产品)"]
|
||||
include_remarks: []
|
||||
clash_rule_base: base/simple_base.yml
|
||||
@@ -8,6 +8,9 @@ api_access_token=password
|
||||
;Default URLs, used when no URL is provided in request, use "|" to separate multiple subscription links, supports local files/URL
|
||||
default_url=
|
||||
|
||||
;URLs to insert before subscription links, can be used to add node(s) to all exported subscriptions, use "|" to separate multiple subscription links, supports local files/URL
|
||||
insert_url=
|
||||
|
||||
;Exclude nodes which remarks match the following patterns. Supports regular expression.
|
||||
exclude_remarks=(到期|剩余流量|时间|官网|产品)
|
||||
;exclude_remarks=(other rule)
|
||||
|
||||
12
base/profiles/example_profile.ini
Normal file
12
base/profiles/example_profile.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
[Profile]
|
||||
;This is an example profile for the /getprofile interface
|
||||
;The options works the same as the arguments in the /sub interface
|
||||
;Arguments that needed URLEncode before is not needed here
|
||||
;For more available options, please check the readme section
|
||||
|
||||
target=clash
|
||||
url=ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwYXNzd29yZA@www.example.com:1080#Example
|
||||
;config=config/example_external_config.ini
|
||||
;surge_ver=3
|
||||
;udp=true
|
||||
;emoji=false
|
||||
@@ -21,10 +21,10 @@ std::string pref_path = "pref.ini";
|
||||
bool generator_mode = false;
|
||||
string_array def_exclude_remarks, def_include_remarks, rulesets, stream_rules, time_rules;
|
||||
std::vector<ruleset_content> ruleset_content_array;
|
||||
std::string listen_address = "127.0.0.1", default_url, managed_config_prefix;
|
||||
std::string listen_address = "127.0.0.1", default_url, insert_url, managed_config_prefix;
|
||||
int listen_port = 25500, max_pending_connections = 10, max_concurrent_threads = 4;
|
||||
bool api_mode = true, write_managed_config = false, enable_rule_generator = true, update_ruleset_on_request = false, overwrite_original_rules = true;
|
||||
bool print_debug_info = false, cfw_child_process = false;
|
||||
bool print_debug_info = false, cfw_child_process = false, append_userinfo = true;
|
||||
std::string access_token;
|
||||
extern std::string custom_group;
|
||||
|
||||
@@ -363,6 +363,16 @@ void readYAMLConf(YAML::Node &node)
|
||||
});
|
||||
default_url = strLine;
|
||||
}
|
||||
if(section["insert_url"].IsSequence())
|
||||
{
|
||||
section["insert_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);
|
||||
});
|
||||
insert_url = strLine;
|
||||
}
|
||||
if(section["exclude_remarks"].IsSequence())
|
||||
section["exclude_remarks"] >> def_exclude_remarks;
|
||||
if(section["include_remarks"].IsSequence())
|
||||
@@ -403,6 +413,7 @@ void readYAMLConf(YAML::Node &node)
|
||||
section["sort_flag"] >> do_sort;
|
||||
section["skip_cert_verify_flag"] >> scv_flag;
|
||||
section["filter_deprecated_nodes"] >> filter_deprecated;
|
||||
section["append_sub_userinfo"] >> append_userinfo;
|
||||
}
|
||||
|
||||
if(section["rename_node"].IsSequence())
|
||||
@@ -510,6 +521,8 @@ void readConf()
|
||||
access_token = ini.Get("api_access_token");
|
||||
if(ini.ItemExist("default_url"))
|
||||
default_url = ini.Get("default_url");
|
||||
if(ini.ItemExist("insert_url"))
|
||||
insert_url = ini.Get("insert_url");
|
||||
if(ini.ItemPrefixExist("exclude_remarks"))
|
||||
ini.GetAll("exclude_remarks", def_exclude_remarks);
|
||||
if(ini.ItemPrefixExist("include_remarks"))
|
||||
@@ -553,6 +566,8 @@ void readConf()
|
||||
scv_flag = ini.GetBool("skip_cert_verify_flag");
|
||||
if(ini.ItemExist("filter_deprecated_nodes"))
|
||||
filter_deprecated = ini.GetBool("filter_deprecated_nodes");
|
||||
if(ini.ItemExist("append_sub_userinfo"))
|
||||
append_userinfo = ini.GetBool("append_sub_userinfo");
|
||||
if(ini.ItemPrefixExist("rename_node"))
|
||||
{
|
||||
ini.GetAll("rename_node", tempArray);
|
||||
@@ -656,6 +671,8 @@ struct ExternalConfig
|
||||
std::string quanx_rule_base;
|
||||
string_array rename;
|
||||
string_array emoji;
|
||||
string_array include;
|
||||
string_array exclude;
|
||||
bool overwrite_original_rules = false;
|
||||
bool enable_rule_generator = true;
|
||||
};
|
||||
@@ -685,6 +702,9 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
|
||||
if(section["rename_node"].size())
|
||||
readRegexMatch(section["rename_node"], "@", ext.rename);
|
||||
|
||||
section["include_remarks"] >> ext.include;
|
||||
section["exclude_remarks"] >> ext.exclude;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -756,6 +776,10 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext, std::string proxy
|
||||
ini.GetAll("emoji", ext.emoji);
|
||||
importItems(ext.emoji);
|
||||
}
|
||||
if(ini.ItemPrefixExist("include_remarks"))
|
||||
ini.GetAll("include_remarks", ext.include);
|
||||
if(ini.ItemPrefixExist("exclude_remarks"))
|
||||
ini.GetAll("exclude_remarks", ext.exclude);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -798,14 +822,14 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
std::string group = UrlDecode(getUrlArg(argument, "group")), upload = getUrlArg(argument, "upload"), upload_path = getUrlArg(argument, "upload_path"), version = getUrlArg(argument, "ver");
|
||||
std::string append_type = getUrlArg(argument, "append_type"), tfo = getUrlArg(argument, "tfo"), udp = getUrlArg(argument, "udp"), nodelist = getUrlArg(argument, "list");
|
||||
std::string include = UrlDecode(getUrlArg(argument, "include")), exclude = UrlDecode(getUrlArg(argument, "exclude")), sort_flag = getUrlArg(argument, "sort");
|
||||
std::string scv = getUrlArg(argument, "scv"), fdn = getUrlArg(argument, "fdn"), token = getUrlArg(argument, "token"), expand = getUrlArg(argument, "expand");
|
||||
std::string scv = getUrlArg(argument, "scv"), fdn = getUrlArg(argument, "fdn"), expand = getUrlArg(argument, "expand"), append_sub_userinfo = getUrlArg(argument, "append_info");
|
||||
std::string base_content, output_content;
|
||||
string_array extra_group, extra_ruleset, include_remarks, exclude_remarks;
|
||||
std::string groups = urlsafe_base64_decode(getUrlArg(argument, "groups")), ruleset = urlsafe_base64_decode(getUrlArg(argument, "ruleset")), config = UrlDecode(getUrlArg(argument, "config"));
|
||||
std::vector<ruleset_content> rca;
|
||||
extra_settings ext;
|
||||
std::string subInfo;
|
||||
bool ruleset_updated = false;
|
||||
bool ruleset_updated = false, authorized = getUrlArg(argument, "token") == access_token;
|
||||
|
||||
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!";
|
||||
@@ -815,8 +839,10 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
std::string ext_quan_base = quan_rule_base, ext_quanx_base = quanx_rule_base;
|
||||
|
||||
//validate urls
|
||||
if(!url.size() && (!api_mode || token == access_token))
|
||||
if(!url.size() && (!api_mode || authorized))
|
||||
url = default_url;
|
||||
if(insert_url.size())
|
||||
url = insert_url + "|" + url;
|
||||
if(!url.size() || !target.size())
|
||||
{
|
||||
*status_code = 400;
|
||||
@@ -913,6 +939,10 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
rca = ruleset_content_array;
|
||||
}
|
||||
}
|
||||
if(extconf.include.size())
|
||||
include_remarks = extconf.include;
|
||||
if(extconf.exclude.size())
|
||||
exclude_remarks = extconf.exclude;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -974,7 +1004,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
x = trim(x);
|
||||
std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
|
||||
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, stream_temp, time_temp, subInfo) == -1)
|
||||
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, stream_temp, time_temp, subInfo, authorized) == -1)
|
||||
{
|
||||
*status_code = 400;
|
||||
return std::string("The following link doesn't contain any valid node info: " + x);
|
||||
@@ -993,7 +1023,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
for(nodeInfo &x : nodes)
|
||||
x.group = group;
|
||||
|
||||
if(subInfo.size() && groupID == 1)
|
||||
if(subInfo.size() && (append_sub_userinfo.size() ? append_sub_userinfo == "true" : append_userinfo))
|
||||
extra_headers.emplace("Subscription-UserInfo", subInfo);
|
||||
|
||||
string_array dummy_group;
|
||||
@@ -1186,6 +1216,8 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return "Please insert your subscription link instead of clicking the default link.";
|
||||
}
|
||||
if(insert_url.size())
|
||||
url = insert_url + "|" + url;
|
||||
if(!api_mode || cfw_child_process)
|
||||
readConf();
|
||||
|
||||
@@ -1216,7 +1248,7 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
x = trim(x);
|
||||
std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
|
||||
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, dummy, dummy, subInfo) == -1)
|
||||
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, dummy, dummy, subInfo, false) == -1)
|
||||
{
|
||||
*status_code = 400;
|
||||
return std::string("The following link doesn't contain any valid node info: " + x);
|
||||
@@ -1290,7 +1322,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
std::string subInfo;
|
||||
std::cerr<<"Fetching node data from url '"<<url<<"'."<<std::endl;
|
||||
if(addNodes(url, nodes, 0, proxy, dummy_str_array, dummy_str_array, dummy_str_array, dummy_str_array, subInfo) == -1)
|
||||
if(addNodes(url, nodes, 0, proxy, dummy_str_array, dummy_str_array, dummy_str_array, dummy_str_array, subInfo, false) == -1)
|
||||
{
|
||||
*status_code = 400;
|
||||
return std::string("The following link doesn't contain any valid node info: " + url);
|
||||
@@ -1412,3 +1444,43 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
return YAML::Dump(clash);
|
||||
}
|
||||
|
||||
std::string getProfile(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string name = UrlDecode(getUrlArg(argument, "name")), token = getUrlArg(argument, "token");
|
||||
if(token != access_token)
|
||||
{
|
||||
*status_code = 403;
|
||||
return "Forbidden";
|
||||
}
|
||||
if(!fileExist(name))
|
||||
{
|
||||
*status_code = 404;
|
||||
return "Profile not found";
|
||||
}
|
||||
std::cerr<<"Trying to load profile '" + name + "'.\n";
|
||||
INIReader ini;
|
||||
if(ini.ParseFile(name) != INIREADER_EXCEPTION_NONE && !ini.SectionExist("Profile"))
|
||||
{
|
||||
std::cerr<<"Load profile failed! Reason: "<<ini.GetLastError()<<"\n";
|
||||
*status_code = 500;
|
||||
return "Broken profile!";
|
||||
}
|
||||
std::cerr<<"Trying to parse profile '" + name + "'.\n";
|
||||
string_multimap contents;
|
||||
ini.GetItems("Profile", contents);
|
||||
if(!contents.size())
|
||||
{
|
||||
std::cerr<<"Load profile failed! Reason: Empty Profile section\n";
|
||||
*status_code = 500;
|
||||
return "Broken profile!";
|
||||
}
|
||||
contents.emplace("token", token);
|
||||
std::string query;
|
||||
for(auto &x : contents)
|
||||
{
|
||||
query += x.first + "=" + UrlEncode(x.second) + "&";
|
||||
}
|
||||
query.erase(query.size() - 1);
|
||||
return subconverter(query, postdata, status_code, extra_headers);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &rca);
|
||||
void readConf();
|
||||
void generateBase();
|
||||
std::string getProfile(RESPONSE_CALLBACK_ARGS);
|
||||
std::string getRuleset(RESPONSE_CALLBACK_ARGS);
|
||||
std::string subconverter(RESPONSE_CALLBACK_ARGS);
|
||||
std::string simpleToClashR(RESPONSE_CALLBACK_ARGS);
|
||||
|
||||
@@ -183,6 +183,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
append_response("GET", "/getruleset", "text/plain;charset=utf-8", getRuleset);
|
||||
|
||||
append_response("GET", "/getprofile", "text/plain;charset=utf-8", getProfile);
|
||||
|
||||
append_response("GET", "/clash", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
return subconverter(argument + "&target=clash", postdata, status_code, extra_headers);
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
std::string override_conf_port;
|
||||
int socksport;
|
||||
bool ss_libev, ssr_libev;
|
||||
extern bool api_mode;
|
||||
|
||||
void copyNodes(std::vector<nodeInfo> &source, std::vector<nodeInfo> &dest)
|
||||
{
|
||||
@@ -22,7 +21,7 @@ void copyNodes(std::vector<nodeInfo> &source, std::vector<nodeInfo> &dest)
|
||||
}
|
||||
}
|
||||
|
||||
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo)
|
||||
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized)
|
||||
{
|
||||
int linkType = -1;
|
||||
std::vector<nodeInfo> nodes;
|
||||
@@ -97,7 +96,7 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
|
||||
}
|
||||
break;
|
||||
case SPEEDTEST_MESSAGE_FOUNDLOCAL:
|
||||
if(api_mode)
|
||||
if(!authorized)
|
||||
return -1;
|
||||
writeLog(LOG_TYPE_INFO, "Parsing configuration file data...");
|
||||
if(explodeConf(link, override_conf_port, socksport, ss_libev, ssr_libev, nodes) == SPEEDTEST_ERROR_UNRECOGFILE)
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
|
||||
#include "nodeinfo.h"
|
||||
|
||||
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo);
|
||||
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized);
|
||||
|
||||
#endif // NODEMANIP_H_INCLUDED
|
||||
|
||||
@@ -269,7 +269,7 @@ std::string removeEmoji(std::string remark)
|
||||
while(true)
|
||||
{
|
||||
if(remark[0] == emoji_id[0] && remark[1] == emoji_id[1])
|
||||
remark = remark.substr(4);
|
||||
remark.erase(0, 4);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user