Enhancements

Fix support for parsing keyword type in Clash domain rule-provider.
Fix potential error on reading configuration files.
Fix not processing remarks with comma when exporting some subscriptions.
Add geoip as script function.
Add support for using script to rename nodes and add Emojis to remarks.
This commit is contained in:
Tindy X committed 2020-06-21 08:17:12 +08:00
1 parent 5f87abec16
commit 46f334a564
6 files changed
+158 -44

No files matched your search

+4
View File
@@ -48,6 +48,8 @@ node_pref:
clash_use_new_field_name: true clash_use_new_field_name: true
rename_node: rename_node:
# - {match: "\\(?((x|X)?(\\d+)(\\.?\\d+)?)((\\s?倍率?)|(x|X))\\)?", replace: "$1x"} # - {match: "\\(?((x|X)?(\\d+)(\\.?\\d+)?)((\\s?倍率?)|(x|X))\\)?", replace: "$1x"}
# - {script: "function rename(node){}"}
# - {script: "path:/path/to/script.js"}
- {import: snippets/rename_node.txt} - {import: snippets/rename_node.txt}
managed_config: managed_config:
@@ -65,6 +67,8 @@ emojis:
remove_old_emoji: true remove_old_emoji: true
rules: rules:
# - {match: "(流量|时间|应急)", emoji: "🏳️‍🌈"} # - {match: "(流量|时间|应急)", emoji: "🏳️‍🌈"}
# - {script: "function getEmoji(node){}"}
# - {script: "path:/path/to/script.js"}
- {import: snippets/emoji.txt} - {import: snippets/emoji.txt}
rulesets: rulesets:
+4
View File
@@ -107,6 +107,8 @@ clash_use_new_field_name=true
;rename_node=IPLC@专线 ;rename_node=IPLC@专线
;rename_node=RELAY@中转 ;rename_node=RELAY@中转
;rename_node=BGP-@ ;rename_node=BGP-@
;rename_node=script:function rename(node) {\n const info = JSON.parse(node.ProxyInfo);\n const geoinfo = JSON.parse(geoip(info.Hostname));\n if(geoinfo.country_code == "CN")\n return "CN " + node.Remark;\n}
;rename_node=script:path:/path/to/script.js
rename_node=!!import:snippets/rename_node.txt rename_node=!!import:snippets/rename_node.txt
@@ -139,6 +141,8 @@ remove_old_emoji=true
;rule=(流量|时间|应急|过期|Bandwidth|expire),🏳️‍🌈 ;rule=(流量|时间|应急|过期|Bandwidth|expire),🏳️‍🌈
;rule=AC,🇦🇨 ;rule=AC,🇦🇨
;rule=script:function getEmoji(node) {\n const info = JSON.parse(node.ProxyInfo);\n const geoinfo = JSON.parse(geoip(info.Hostname));\n if(geoinfo.country_code == "CN")\n return "🏳️‍🌈";\n}
;rule=script:path:/path/to/script/.js
rule=!!import:snippets/emoji.txt rule=!!import:snippets/emoji.txt
+40 -30
View File
@@ -136,7 +136,17 @@ std::string convertRuleset(const std::string &content, int type)
{ {
if(strLine[0] == '.' || (lineSize >= 2 && strLine[0] == '+' && strLine[1] == '.')) /// suffix if(strLine[0] == '.' || (lineSize >= 2 && strLine[0] == '+' && strLine[1] == '.')) /// suffix
{ {
output += "DOMAIN-SUFFIX,"; bool keyword_flag = false;
while(endsWith(strLine, ".*"))
{
keyword_flag = true;
strLine.erase(strLine.size() - 2);
}
output += "DOMAIN-";
if(keyword_flag)
output += "KEYWORD,";
else
output += "SUFFIX,";
strLine.erase(0, 2 - (strLine[0] == '.')); strLine.erase(0, 2 - (strLine[0] == '.'));
} }
else else
@@ -369,20 +379,25 @@ int importItems(string_array &target, bool scope_limit = true)
void readRegexMatch(YAML::Node node, std::string delimiter, string_array &dest, bool scope_limit = true) void readRegexMatch(YAML::Node node, std::string delimiter, string_array &dest, bool scope_limit = true)
{ {
YAML::Node object; YAML::Node object;
std::string url, match, rep, strLine; std::string script, url, match, rep, strLine;
for(unsigned i = 0; i < node.size(); i++) for(unsigned i = 0; i < node.size(); i++)
{ {
object = node[i]; object = node[i];
object["import"] >> url; object["script"] >>= script;
if(url.size()) if(script.size())
{ {
url = "!!import:" + url; dest.emplace_back("!!script:" + script);
dest.emplace_back(url);
continue; continue;
} }
object["match"] >> match; object["import"] >>= url;
object["replace"] >> rep; if(url.size())
{
dest.emplace_back("!!import:" + url);
continue;
}
object["match"] >>= match;
object["replace"] >>= rep;
if(match.size() && rep.size()) if(match.size() && rep.size())
strLine = match + delimiter + rep; strLine = match + delimiter + rep;
else else
@@ -400,15 +415,15 @@ void readEmoji(YAML::Node node, string_array &dest, bool scope_limit = true)
for(unsigned i = 0; i < node.size(); i++) for(unsigned i = 0; i < node.size(); i++)
{ {
object = node[i]; object = node[i];
object["import"] >> url; object["import"] >>= url;
if(url.size()) if(url.size())
{ {
url = "!!import:" + url; url = "!!import:" + url;
dest.emplace_back(url); dest.emplace_back(url);
continue; continue;
} }
object["match"] >> match; object["match"] >>= match;
object["emoji"] >> rep; object["emoji"] >>= rep;
if(match.size() && rep.size()) if(match.size() && rep.size())
strLine = match + "," + rep; strLine = match + "," + rep;
else else
@@ -420,32 +435,30 @@ void readEmoji(YAML::Node node, string_array &dest, bool scope_limit = true)
void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true) void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true)
{ {
std::string strLine, name, type;
string_array tempArray; string_array tempArray;
YAML::Node object; YAML::Node object;
unsigned int i, j; unsigned int i, j;
for(i = 0; i < node.size(); i++) for(i = 0; i < node.size(); i++)
{ {
std::string strLine, name, type;
name.clear();
eraseElements(tempArray); eraseElements(tempArray);
object = node[i]; object = node[i];
object["import"] >> name; object["import"] >>= name;
if(name.size()) if(name.size())
{ {
name = "!!import:" + name; dest.emplace_back("!!import:" + name);
dest.emplace_back(name);
continue; continue;
} }
std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout; std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout;
object["name"] >> name; object["name"] >>= name;
object["type"] >> type; object["type"] >>= type;
tempArray.emplace_back(name); tempArray.emplace_back(name);
tempArray.emplace_back(type); tempArray.emplace_back(type);
object["url"] >> url; object["url"] >>= url;
object["interval"] >> interval; object["interval"] >>= interval;
object["tolerance"] >> tolerance; object["tolerance"] >>= tolerance;
object["timeout"] >> timeout; object["timeout"] >>= timeout;
for(j = 0; j < object["rule"].size(); j++) for(j = 0; j < object["rule"].size(); j++)
tempArray.emplace_back(safe_as<std::string>(object["rule"][j])); tempArray.emplace_back(safe_as<std::string>(object["rule"][j]));
switch(hash_(type)) switch(hash_(type))
@@ -481,19 +494,16 @@ void readRuleset(YAML::Node node, string_array &dest, bool scope_limit = true)
for(unsigned int i = 0; i < node.size(); i++) for(unsigned int i = 0; i < node.size(); i++)
{ {
url.clear();
name.clear();
object = node[i]; object = node[i];
object["import"] >> name; object["import"] >>= name;
if(name.size()) if(name.size())
{ {
name = "!!import:" + name; dest.emplace_back("!!import:" + name);
dest.emplace_back(name);
continue; continue;
} }
object["ruleset"] >> url; object["ruleset"] >>= url;
object["group"] >> group; object["group"] >>= group;
object["rule"] >> name; object["rule"] >>= name;
if(url.size()) if(url.size())
strLine = group + "," + url; strLine = group + "," + url;
else if(name.size()) else if(name.size())
+40 -3
View File
@@ -6,8 +6,11 @@
#include "misc.h" #include "misc.h"
#include "multithread.h" #include "multithread.h"
#include "nodeinfo.h" #include "nodeinfo.h"
#include "socket.h"
#include "webget.h"
extern int cache_config; extern int cache_config;
extern std::string proxy_config;
std::string parseProxy(const std::string &source); std::string parseProxy(const std::string &source);
@@ -40,6 +43,23 @@ std::string foldPathString(const std::string &path)
return output; return output;
} }
static int duktape_get_arguments_str(duk_context *ctx, duk_idx_t min_count, duk_idx_t max_count, ...)
{
duk_idx_t nargs = duk_get_top(ctx);
if((min_count >= 0 && nargs < min_count) || (max_count >= 0 && nargs > max_count))
return 0;
va_list vl;
va_start(vl, max_count);
for(duk_idx_t idx = 0; idx < nargs; idx++)
{
std::string *arg = va_arg(vl, std::string*);
if(arg)
*arg = duk_safe_to_string(ctx, idx);
}
va_end(vl);
return 1;
}
duk_ret_t cb_resolve_module(duk_context *ctx) duk_ret_t cb_resolve_module(duk_context *ctx)
{ {
const char *requested_id = duk_get_string(ctx, 0); const char *requested_id = duk_get_string(ctx, 0);
@@ -88,7 +108,8 @@ static duk_ret_t native_print(duk_context *ctx)
static duk_ret_t fetch(duk_context *ctx) static duk_ret_t fetch(duk_context *ctx)
{ {
std::string filepath = duk_safe_to_string(ctx, -1), proxy = duk_safe_to_string(ctx, -1); std::string filepath, proxy;
duktape_get_arguments_str(ctx, 1, 2, &filepath, &proxy);
std::string content = fetchFile(filepath, proxy, cache_config); std::string content = fetchFile(filepath, proxy, cache_config);
duk_push_lstring(ctx, content.c_str(), content.size()); duk_push_lstring(ctx, content.c_str(), content.size());
return 1; return 1;
@@ -104,7 +125,21 @@ static duk_ret_t atob(duk_context *ctx)
static duk_ret_t btoa(duk_context *ctx) static duk_ret_t btoa(duk_context *ctx)
{ {
std::string data = duk_safe_to_string(ctx, -1); std::string data = duk_safe_to_string(ctx, -1);
duk_push_string(ctx, base64_decode(data, true).c_str()); data = base64_decode(data, true);
duk_push_lstring(ctx, data.c_str(), data.size());
return 1;
}
static duk_ret_t getGeoIP(duk_context *ctx)
{
std::string address, proxy;
duktape_get_arguments_str(ctx, 1, 2, &address, &proxy);
if(!isIPv4(address) && !isIPv6(address))
address = hostnameToIPAddr(address);
if(address.empty())
duk_push_undefined(ctx);
else
duk_push_string(ctx, fetchFile("https://api.ip.sb/geoip/" + address, parseProxy(proxy), cache_config).c_str());
return 1; return 1;
} }
@@ -123,12 +158,14 @@ duk_context *duktape_init()
duk_push_c_function(ctx, native_print, DUK_VARARGS); duk_push_c_function(ctx, native_print, DUK_VARARGS);
duk_put_global_string(ctx, "print"); duk_put_global_string(ctx, "print");
duk_push_c_function(ctx, fetch, 1); duk_push_c_function(ctx, fetch, DUK_VARARGS);
duk_put_global_string(ctx, "fetch"); duk_put_global_string(ctx, "fetch");
duk_push_c_function(ctx, atob, 1); duk_push_c_function(ctx, atob, 1);
duk_put_global_string(ctx, "atob"); duk_put_global_string(ctx, "atob");
duk_push_c_function(ctx, btoa, 1); duk_push_c_function(ctx, btoa, 1);
duk_put_global_string(ctx, "btoa"); duk_put_global_string(ctx, "btoa");
duk_push_c_function(ctx, getGeoIP, DUK_VARARGS);
duk_put_global_string(ctx, "geoip");
return ctx; return ctx;
} }
+69 -10
View File
@@ -492,20 +492,44 @@ bool applyMatcher(const std::string &rule, std::string &real_rule, const nodeInf
std::string nodeRename(const nodeInfo &node, const string_array &rename_array) std::string nodeRename(const nodeInfo &node, const string_array &rename_array)
{ {
string_array vArray; string_size pos;
std::string match, rep;
std::string remark = node.remarks, real_rule; std::string remark = node.remarks, real_rule;
duk_context *ctx = duktape_init();
defer(duk_destroy_heap(ctx);)
for(const std::string &x : rename_array) for(const std::string &x : rename_array)
{ {
vArray = split(x, "@"); if(startsWith(x, "!!script:"))
if(vArray.size() == 1)
{ {
vArray.emplace_back(""); std::string script = x.substr(7);
if(startsWith(script, "path:"))
script = fileGet(script.substr(5), true);
if(ctx)
{
if(duktape_peval(ctx, script) == 0)
{
duk_get_global_string(ctx, "rename");
duktape_push_nodeinfo(ctx, node);
if(duk_pcall(ctx, 1) == 0)
remark = duktape_get_res_str(ctx);
}
else
{
writeLog(0, "Error when trying to parse rename script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
duk_pop(ctx); // pop err
}
} }
else if(vArray.size() != 2)
continue; continue;
if(applyMatcher(vArray[0], real_rule, node) && real_rule.size()) }
remark = regReplace(remark, real_rule, vArray[1]); pos = x.rfind("@");
match = x.substr(0, pos);
if(pos != x.npos && pos < x.size())
rep = x.substr(pos + 1);
else
rep.clear();
if(applyMatcher(match, real_rule, node) && real_rule.size())
remark = regReplace(remark, real_rule, rep);
} }
if(remark.empty()) if(remark.empty())
return node.remarks; return node.remarks;
@@ -530,12 +554,39 @@ std::string removeEmoji(const std::string &orig_remark)
std::string addEmoji(const nodeInfo &node, const string_array &emoji_array) std::string addEmoji(const nodeInfo &node, const string_array &emoji_array)
{ {
string_array vArray;
std::string real_rule; std::string real_rule;
string_size pos; string_size pos;
duk_context *ctx = duktape_init();
defer(duk_destroy_heap(ctx);)
for(const std::string &x : emoji_array) for(const std::string &x : emoji_array)
{ {
if(startsWith(x, "!!script:"))
{
std::string script = x.substr(7);
if(startsWith(script, "path:"))
script = fileGet(script.substr(5), true);
if(ctx)
{
if(duktape_peval(ctx, script) == 0)
{
duk_get_global_string(ctx, "getEmoji");
duktape_push_nodeinfo(ctx, node);
if(duk_pcall(ctx, 1) == 0)
{
std::string ret = duktape_get_res_str(ctx);
if(ret.size())
return ret + " " + node.remarks;
}
}
else
{
writeLog(0, "Error when trying to parse emoji script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
duk_pop(ctx); // pop err
}
}
continue;
}
pos = x.rfind(","); pos = x.rfind(",");
if(pos == x.npos) if(pos == x.npos)
continue; continue;
@@ -545,8 +596,16 @@ std::string addEmoji(const nodeInfo &node, const string_array &emoji_array)
return node.remarks; return node.remarks;
} }
void processRemark(std::string &oldremark, std::string &newremark, string_array &remarks_list) void processRemark(std::string &oldremark, std::string &newremark, string_array &remarks_list, bool proc_comma = true)
{ {
if(proc_comma)
{
if(oldremark.find(',') != oldremark.npos)
{
oldremark.insert(0, "\"");
oldremark.append("\"");
}
}
newremark = oldremark; newremark = oldremark;
int cnt = 2; int cnt = 2;
while(std::find(remarks_list.begin(), remarks_list.end(), newremark) != remarks_list.end()) while(std::find(remarks_list.begin(), remarks_list.end(), newremark) != remarks_list.end())
@@ -1091,7 +1150,7 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
if(ext.append_proxy_type) if(ext.append_proxy_type)
x.remarks = "[" + type + "] " + x.remarks; x.remarks = "[" + type + "] " + x.remarks;
processRemark(x.remarks, remark, remarks_list); processRemark(x.remarks, remark, remarks_list, false);
hostname = GetMember(json, "Hostname"); hostname = GetMember(json, "Hostname");
port = GetMember(json, "Port"); port = GetMember(json, "Port");
+1 -1
View File
@@ -5,7 +5,7 @@
#include "misc.h" #include "misc.h"
std::string webGet(const std::string &url, const std::string &proxy); std::string webGet(const std::string &url, const std::string &proxy = "");
std::string webGet(const std::string &url, const std::string &proxy, unsigned int cache_ttl); std::string webGet(const std::string &url, const std::string &proxy, unsigned int cache_ttl);
std::string webGet(const std::string &url, const std::string &proxy, std::string &response_headers, unsigned int cache_ttl = 0); std::string webGet(const std::string &url, const std::string &proxy, std::string &response_headers, unsigned int cache_ttl = 0);
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData); int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);