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
rename_node:
# - {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}
managed_config:
@@ -65,6 +67,8 @@ emojis:
remove_old_emoji: true
rules:
# - {match: "(流量|时间|应急)", emoji: "🏳️‍🌈"}
# - {script: "function getEmoji(node){}"}
# - {script: "path:/path/to/script.js"}
- {import: snippets/emoji.txt}
rulesets:
+4
View File
@@ -107,6 +107,8 @@ clash_use_new_field_name=true
;rename_node=IPLC@专线
;rename_node=RELAY@中转
;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
@@ -139,6 +141,8 @@ remove_old_emoji=true
;rule=(流量|时间|应急|过期|Bandwidth|expire),🏳️‍🌈
;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
+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
{
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] == '.'));
}
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)
{
YAML::Node object;
std::string url, match, rep, strLine;
std::string script, url, match, rep, strLine;
for(unsigned i = 0; i < node.size(); i++)
{
object = node[i];
object["import"] >> url;
if(url.size())
object["script"] >>= script;
if(script.size())
{
url = "!!import:" + url;
dest.emplace_back(url);
dest.emplace_back("!!script:" + script);
continue;
}
object["match"] >> match;
object["replace"] >> rep;
object["import"] >>= url;
if(url.size())
{
dest.emplace_back("!!import:" + url);
continue;
}
object["match"] >>= match;
object["replace"] >>= rep;
if(match.size() && rep.size())
strLine = match + delimiter + rep;
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++)
{
object = node[i];
object["import"] >> url;
object["import"] >>= url;
if(url.size())
{
url = "!!import:" + url;
dest.emplace_back(url);
continue;
}
object["match"] >> match;
object["emoji"] >> rep;
object["match"] >>= match;
object["emoji"] >>= rep;
if(match.size() && rep.size())
strLine = match + "," + rep;
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)
{
std::string strLine, name, type;
string_array tempArray;
YAML::Node object;
unsigned int i, j;
for(i = 0; i < node.size(); i++)
{
std::string strLine, name, type;
name.clear();
eraseElements(tempArray);
object = node[i];
object["import"] >> name;
object["import"] >>= name;
if(name.size())
{
name = "!!import:" + name;
dest.emplace_back(name);
dest.emplace_back("!!import:" + name);
continue;
}
std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout;
object["name"] >> name;
object["type"] >> type;
object["name"] >>= name;
object["type"] >>= type;
tempArray.emplace_back(name);
tempArray.emplace_back(type);
object["url"] >> url;
object["interval"] >> interval;
object["tolerance"] >> tolerance;
object["timeout"] >> timeout;
object["url"] >>= url;
object["interval"] >>= interval;
object["tolerance"] >>= tolerance;
object["timeout"] >>= timeout;
for(j = 0; j < object["rule"].size(); j++)
tempArray.emplace_back(safe_as<std::string>(object["rule"][j]));
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++)
{
url.clear();
name.clear();
object = node[i];
object["import"] >> name;
object["import"] >>= name;
if(name.size())
{
name = "!!import:" + name;
dest.emplace_back(name);
dest.emplace_back("!!import:" + name);
continue;
}
object["ruleset"] >> url;
object["group"] >> group;
object["rule"] >> name;
object["ruleset"] >>= url;
object["group"] >>= group;
object["rule"] >>= name;
if(url.size())
strLine = group + "," + url;
else if(name.size())
+40 -3
View File
@@ -6,8 +6,11 @@
#include "misc.h"
#include "multithread.h"
#include "nodeinfo.h"
#include "socket.h"
#include "webget.h"
extern int cache_config;
extern std::string proxy_config;
std::string parseProxy(const std::string &source);
@@ -40,6 +43,23 @@ std::string foldPathString(const std::string &path)
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)
{
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)
{
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);
duk_push_lstring(ctx, content.c_str(), content.size());
return 1;
@@ -104,7 +125,21 @@ static duk_ret_t atob(duk_context *ctx)
static duk_ret_t btoa(duk_context *ctx)
{
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;
}
@@ -123,12 +158,14 @@ duk_context *duktape_init()
duk_push_c_function(ctx, native_print, DUK_VARARGS);
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_push_c_function(ctx, atob, 1);
duk_put_global_string(ctx, "atob");
duk_push_c_function(ctx, btoa, 1);
duk_put_global_string(ctx, "btoa");
duk_push_c_function(ctx, getGeoIP, DUK_VARARGS);
duk_put_global_string(ctx, "geoip");
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)
{
string_array vArray;
string_size pos;
std::string match, rep;
std::string remark = node.remarks, real_rule;
duk_context *ctx = duktape_init();
defer(duk_destroy_heap(ctx);)
for(const std::string &x : rename_array)
{
vArray = split(x, "@");
if(vArray.size() == 1)
if(startsWith(x, "!!script:"))
{
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;
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())
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)
{
string_array vArray;
std::string real_rule;
string_size pos;
duk_context *ctx = duktape_init();
defer(duk_destroy_heap(ctx);)
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(",");
if(pos == x.npos)
continue;
@@ -545,8 +596,16 @@ std::string addEmoji(const nodeInfo &node, const string_array &emoji_array)
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;
int cnt = 2;
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)
x.remarks = "[" + type + "] " + x.remarks;
processRemark(x.remarks, remark, remarks_list);
processRemark(x.remarks, remark, remarks_list, false);
hostname = GetMember(json, "Hostname");
port = GetMember(json, "Port");
+1 -1
View File
@@ -5,7 +5,7 @@
#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, 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);