From 988a337ae19cf679a9deb5fa434d7a50eaf18298 Mon Sep 17 00:00:00 2001 From: Shawn <45581750+KOP-XIAO@users.noreply.github.com> Date: Fri, 12 Nov 2021 12:07:44 +0800 Subject: [PATCH] bug fixed --- Scripts/resource-parser.js | 2534 +++++++++++++++++++++++++++++++++++- 1 file changed, 2533 insertions(+), 1 deletion(-) diff --git a/Scripts/resource-parser.js b/Scripts/resource-parser.js index 5fee7b5..973d9fe 100644 --- a/Scripts/resource-parser.js +++ b/Scripts/resource-parser.js @@ -472,4 +472,2536 @@ function Type_Check(subs) { // 检查节点名字(重复以及空名)等QuanX 不允许的情形,以及多个空格等“不规范”方式 function TagCheck_QX(content) { - typefix = {"shadowsocks":["𝐬𝐬","𝐒𝐒","🅢🅢","🆂🆂","ⓢⓢ","🅂🅂","SS"],"shadowsocksr":["𝐬𝐬𝐫","𝐒𝐒𝐑","🅢🅢🅡","🆂🆂🆁","ⓢⓢⓡ","🅂 \ No newline at end of file + typefix = {"shadowsocks":["𝐬𝐬","𝐒𝐒","🅢🅢","🆂🆂","ⓢⓢ","🅂🅂","SS"],"shadowsocksr":["𝐬𝐬𝐫","𝐒𝐒𝐑","🅢🅢🅡","🆂🆂🆁","ⓢⓢⓡ","🅂🅁","SSR"],"vmess":["𝐯𝐦𝐞𝐬𝐬","𝐕𝐌𝐄𝐒𝐒","🅥🅜🅔🅢🅢","🆅🅼🅴🆂🆂","ⓥⓜⓔⓢⓢ","🅅🄼🄴🅂🅂","VMESS"],"trojan":["𝐭𝐫𝐨𝐣𝐚𝐧","𝐓𝐑𝐎𝐉𝐀𝐍","🅣🅡🅞🅙🅐🅝","🆃🆁🅾🅹🅰🅽","ⓣⓡⓞⓙⓐⓝ","🅃🅁🄾🄹🄰🄽","TROJAN"],"http":["𝐡𝐭𝐭𝐩","𝐇𝐓𝐓𝐏","🅗🅣🅣🅟","🅷🆃🆃🅿","ⓗⓣⓣⓟ","🄷🅃🅃🄿","HTTP"]} + console.log(content) + var Olist = content.map(item =>item.trim().replace(/\s{2,}/g," ")) + //$notify("","",Olist) + var [Nlist, nmlist] = [ [], [] ] + var [nulllist,duplist] = [ [], [] ]; //记录空名字节点&重名节点 + var no=0 ; + for (var i = 0; i < Olist.length; i++) { + var item = Olist[i] ? Olist[i] : "" + typefix["shadowsocks"]=item.indexOf("ssr-protocol")!=-1? typefix["shadowsocksr"] : typefix["shadowsocks"] + if (item.replace(/ /gm, "").indexOf("tag=") != -1) { + var nl = item.slice(item.indexOf("tag")) + var nm = nl.slice(nl.indexOf("=") + 1) + if (nm == "") { //空名字 + tp = typefix[item.split("=")[0].trim()][3] + nm = tp + " | " + item.split("=")[1].split(",")[0].split(":")[0] + item = item.split("tag")[0] + "tag=" + nm.replace("shadowsocks", "ss") + nulllist.push(nm.replace("shadowsocks", "ss")) + } + var ni = 0 + while (nmlist.indexOf(nm) != -1) { //重名情形 + nm = ni==0? nm+ NoReplace(ni+1):nm.split(" ").slice(0,nm.split(" ").length-2).join(" ") + NoReplace(ni+1) + item = Pdel != 1 ? item.split("tag")[0] + "tag=" + nm : "" + ni = ni + 1 + } + if (ni != 0) { duplist.push(nm) } + nmlist.push(nm) + if (Pcap) { + item = Capitalize(item,Pcap) + console.log(item) + } + if (Pptn || Pnptn) { + item = Pattern(item,Pptn,Pnptn) + console.log(item) + } + ni = 0 + if (item) { + Nlist.push(item) + } + }// if "tag=" + } // for + if (nulllist.length >= 1) { + no = nulllist.length <= 10 ? emojino[nulllist.length] : nulllist.length; + $notify("⚠️ 引用" + "⟦" + subtag + "⟧" + " 内有" + no + "个空节点名 ", "✅ 已将节点“类型+IP”设为节点名", " ⨁ " + nulllist.join("\n ⨁ "), nan_link) + } + if (duplist.length >= 1) { + no = duplist.length <= 10 ? emojino[duplist.length] : duplist.length; + if (Pdel!=1){ + $notify("⚠️ 引用" + "⟦" + subtag + "⟧" + " 内有" + no + "个名字重复的节点 ", "✅ 已添加数字区分, 删除请添加参数 del=1:", " ⨁ " + duplist.join("\n ⨁ "), nan_link) + } else { + $notify("⚠️ 引用" + "⟦" + subtag + "⟧" + " 内有" + no + "个名字重复的节点 ", "❌️ 已全部删除,如需保留请去除参数 del=1:", " ⨁ " + duplist.join("\n ⨁ "), nan_link) + } + } + return Nlist +} + +//节点名重名时添加数字序号替换 +function NoReplace(cnt) { + if(cnt){ + for (var i=0;i<10;i++) { + cnt = cnt.toString().replace(new RegExp(patn[0][i], "gmi"),patn[5][i]) + } + return " " + cnt + " " + } +} + + +// 对节点名pattern化操作 +function PatternN(cnt, para,npara) { + if(cnt){ + if(para!=""){//字符 + for (var i=0;i<26;i++) { + cnt = cnt.toLowerCase() + cnt = cnt.replace(new RegExp(pat[0][i], "gmi"),pat[para][i]) + } + } + if(npara!=""){ //数字 + for (var i=0;i<10;i++) { + cnt = cnt.replace(new RegExp(patn[0][i], "gmi"),patn[npara][i]) + } + } + console.log(cnt) + return cnt + } +} + + +function Pattern(cnt,para,npara) { + if (para != "" || npara != "") { + cnt = cnt.split("tag=")[0] +"tag="+ PatternN(cnt.split("tag=")[1],para,npara) + } + return cnt +} + + +//大小写 +function Capitalize(cnt,para) { + if (para == 1) { + cnt = cnt.split("tag=")[0] +"tag="+ cnt.split("tag=")[1].toUpperCase() + } else if (para == -1) { + cnt = cnt.split("tag=")[0] +"tag="+ cnt.split("tag=")[1].toLowerCase() + } else if (para == 0) { + cnt =cnt.split("tag=")[0] +"tag="+titleCase(cnt.split("tag=")[1]) + } + return cnt +} + +function titleCase(str) { + var newStr = str.split(" "); + for(var i = 0; iitem.trim()).filter(Boolean) + let b=Array.from(new Array(items.length),(val,index)=>index+1); + //console.log(b[0]) + for (var i=0; i< items.length;i++){ + ind = items[i].split("node_index_prefix")[1][0] + ind = !/^(0|1|2|3|4|5|6|7|8)$/.test(ind) ? 0 : ind + console.log("handle index"+ind) + items[i] = items[i].replace(/node_index_prefix(\d{0,1})/g,PatternN((i+1).toString(),"",ind)) + } + console.log(items) + return items +} + + +// 操作emoji占位符 +function emoji_prefix_handle(item) { + if(item.indexOf("node_emoji_flag_prefix")!=-1) { + item = item.replace(/node_emoji_flag_prefix\d{0,1}/g,getnode_emoji(item,item.split("node_emoji_flag_prefix")[1][0])) + //console.log(item) + } + return item +} + +// 获取emoji +function getnode_emoji(item,ind){ + ind = !/^(1|2)$/.test(ind) ? 2 : ind + if(item.indexOf("tag=")!=-1) { + return get_emoji(ind,item.split("tag=")[1])[1] + } +} + +// 操作订阅的 tag +function tag_handle(item) { + if(item.indexOf("node_tag_prefix")!=-1) { + //item = item.replace(/node_tag_prefix/g,subtag) + //console.log(item.split("node_tag_prefix")[1][1]) + ptnn = /\d/.test(item.split("node_tag_prefix")[1][0])? item.split("node_tag_prefix")[1][0]:"" + nptnn = /\d/.test(item.split("node_tag_prefix")[1][1])? item.split("node_tag_prefix")[1][1]:"" + //console.log(ptnn) + item = item.replace(/node_tag_prefix\d{0,2}/g,PatternN(subtag,ptnn,nptnn)) + } + return item +} + +// 用于单条 URI 的 tag 参数, 直接指定节点名 +function URI_TAG(cnt0,tag0) { + cnt0 = cnt0.split("tag=")[0] + "tag=" + tag0 + return cnt0 +} + +// 用于某些奇葩用户不使用 raw 链接的问题 +function rawtest(cnt) { + var Preg0 = RegExp(".*js-file-line\".*?\<\/td\>", "i") + if (Preg0.test(cnt)) { + return cnt.replace(/(.*js-file-line\"\>)(.*?)(\<\/td\>)/g,"$2").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").trim() + } +} + +function ToRaw(cnt) { + cnt = cnt.split("\n").map(rawtest).filter(Boolean).join("\n") + var rawlink = link0.replace("github.com","raw.githubusercontent.com").replace("/blob","") + if (cnt) { + $notify( "⚠️⚠️ 将尝试解析该资源" + "⟦" + subtag + "⟧" , "🚥 请正确使用GitHub的 raw 链接" , "❌ 你的链接:"+link0+"\n✅ 正确链接:"+rawlink, {"open-url":rawlink}) + } else if(content0.indexOf("gridcell")!=-1) { + $notify( "⚠️⚠️ 解析该资源" + " ⟦" + subtag + "⟧ 失败" , "🚥 你的链接似乎是目录,而不是文件" , "❌ 你的链接:"+link0, {"open-url":link0}) + } + return cnt +} + +function CDN(cnt) { + console.log("CDN start") + cnt = cnt.join("\n").replace(/https:\/\/raw.githubusercontent.com\/(.*?)\/(.*?)\/(.*)/gmi,"https://cdn.jsdelivr.net/gh/$1/$2@$3") + return cnt +} + +//url-regex 转换成 Quantumult X +function URX2QX(subs) { + var nrw = [] + var rw = "" + subs = subs.split("\n") + var NoteK = ["//", "#", ";"]; //排除注释项 + for (var i = 0; i < subs.length; i++) { + const notecheck = (item) => subs[i].indexOf(item) == 0 + if (!NoteK.some(notecheck)) { + if (subs[i].slice(0, 9) == "URL-REGEX") { // regex 类型 + rw = subs[i].replace(/ /g, "").split(",REJECT")[0].split("GEX,")[1] + " url " + "reject-200" + nrw.push(rw) + } else if (subs[i].indexOf("data=") != -1 && subs.indexOf("[Map Local]") != -1){ // Map Local 类型 + rw = subs[i].replace(/ /g, "").split("data=")[0] + " url " + "reject-dict" + nrw.push(rw) + } + } + } + return nrw +} + +//script&rewrite 转换成 Quantumult X +function SCP2QX(subs) { + var nrw = [] + var rw = "" + subs = subs.split("\n").map(x => x.trim().replace(/\s+/g," ")) + for (var i = 0; i < subs.length; i++) { + try { + if (subs[i].slice(0, 8) == "hostname") { + hn = subs[i].replace(/\%.*\%/g, "") + nrw.push(hn) + } + var SC = ["type=", ".js", "pattern=", "script-path="] + var NoteK = ["//", "#", ";"]; //排除注释项 + const sccheck = (item) => subs[i].indexOf(item) != -1 + const notecheck = (item) => subs[i].indexOf(item) == 0 + if (!NoteK.some(notecheck)){ + if (SC.every(sccheck)) { // surge js 新格式 + ptn = subs[i].replace(/\s/gi,"").split("pattern=")[1].split(",")[0] + js = subs[i].replace(/\s/gi,"").split("script-path=")[1].split(",")[0] + type = subs[i].replace(/\s/gi,"").split("type=")[1].split(",")[0].trim() + subsi = subs[i].replace(/ /g,"").replace(/\=true/g,"=1") + if (type == "http-response" && subsi.indexOf("requires-body=1") != -1) { + type = "script-response-body " + } else if (type == "http-response" && subsi.indexOf("requires-body=1") == -1) { + type = "script-response-header " + } else if (type == "http-request" && subsi.indexOf("requires-body=1") != -1) { + type = "script-request-body " + } else if (type == "http-request" && subsi.indexOf("requires-body=1") == -1) { + type = "script-request-header " + } else {type = "" } + if (type != "") { + rw = ptn + " url " + type + js + nrw.push(rw) + } + } else if (subs[i].indexOf(" 302") != -1 || subs[i].indexOf(" 307") != -1) { //rewrite 302&307 复写 + //tpe = subs[i].indexOf(" 302") != -1? "302":"307" + rw = subs[i].split(" ")[0] + " url " + subs[i].split(" ")[2] + " " + subs[i].split(" ")[1].trim() + //if(rw.indexOf("307")!=-1) {$notify("XX",subs[i],rw.split(" "))} + nrw.push(rw) + } else if(subs[i].split(" ")[2] == "header") { // rewrite header 类型 + var pget = subs[i].split(" ")[0].split(".com")[1] + var pgetn = subs[i].split(" ")[1].split(".com")[1] + rw = subs[i].split(" ")[0] + " url 302 " + subs[i].split(" ")[1] + //rw = subs[i].split(" ")[0] + " url request-header ^GET " + pget +"(.+\\r\\n)Host:.+(\\r\\n) request-header GET " + pgetn + "$1Host: " + subs[i].split(" ")[1].split("://")[1].split(".com")[0] + ".com$2" + nrw.push(rw) + } else if(subs[i].split(" ")[1] == "header-replace") { // rewrite header-replace 类型 + console.log(subs[i]) + var pget = subs[i].split("header-replace")[1].split(":")[0].trim() + var pgetn = subs[i].split("header-replace")[1].trim() + rw = subs[i].split(" ")[0] + " url request-header " +"(.+\\r\\n)"+pget+":.+(\\r\\n) request-header " + "$1" + pgetn + "$2" + nrw.push(rw) + } else if(subs[i].indexOf(" - reject") != -1) { // rewrite reject 类型 + rw = subs[i].split(" ")[0] + " url reject-200" + nrw.push(rw) + } else if (subs[i].indexOf("script-path") != -1) { //surge js 旧写法 + type = subs[i].replace(/\s+/g," ").split(" ")[0] + js = subs[i].split("script-path")[1].split("=")[1].split(",")[0] + ptn = subs[i].replace(/\s+/g," ").split(" ")[1] + subsi = subs[i].replace(/ /g,"").replace(/\=true/g,"=1") + if (type == "http-response" && subsi.indexOf("requires-body=1") != -1) { + type = "script-response-body " + } else if (type == "http-response" && subsi.indexOf("requires-body=1") == -1) { + type = "script-response-header " + } else if (type == "http-request" && subsi.indexOf("requires-body=1") != -1) { + type = "script-request-body " + } else if (type == "http-request" && subsi.indexOf("requires-body=1") == -1) { + type = "script-request-header " + } else {type = "" } + if (type != "") { + rw = ptn + " url " + type + js + nrw.push(rw) + } + + } + } + } catch (err) { + $notify("❌️解析此条时出现错误,已忽略",subs[i],err) + } + } + return nrw +} +// 如果 URL-Regex 跟 rewrite/script 都需要 +function SGMD2QX(subs) { + var nrw0 = URX2QX(subs) + var nrw1 = SCP2QX(subs) + var nrwt = [...nrw0, ...nrw1] + return nrwt +} + +//Rewrite过滤,使用+连接多个关键词(逻辑"或"):in 为保留,out 为排除 +function Rewrite_Filter(subs, Pin, Pout,Preg,Pregout) { + var Nlist = []; + var noteK = ["//", "#", ";"]; + var hnc = 0; + var dwrite = [] + var hostname = "" + for (var i = 0; i < subs.length; i++) { + subi = subs[i].trim(); + var subii = subi.replace(/ /g, "") + if (subi != "" && (subi.indexOf(" url ")!=-1 || /^hostname\=/.test(subii))) { + const notecheck = (item) => subi.indexOf(item) == 0 + if (noteK.some(notecheck)) { // 注释项跳过 + continue; + } else if (hnc == 0 && subii.indexOf("hostname=") == 0) { //hostname 部分 + hostname = (Phin0 || Phout0 || Preg || Pregout) ? HostNamecheck(subi, Phin0, Phout0) : subi;//hostname 部分 + } else if (subii.indexOf("hostname=") != 0) { //rewrite 部分 + var inflag = Rcheck(subi, Pin); + var outflag = Rcheck(subi, Pout); + if (outflag == 1 || inflag == 0) { + dwrite.push(subi.replace(" url "," - ")); //out 命中 + } else if (outflag == 0 && inflag != 0) { //out 未命中 && in 未排除 + Nlist.push(subi); + } else if (outflag == 2 && inflag != 0) { //无 out 参数 && in 未排除 + Nlist.push(subi); + } + } + } + } + if (Pntf0 != 0) { + nowrite = dwrite.length <= 10 ? emojino[dwrite.length] : dwrite.length + no1write = Nlist.length <= 10 ? emojino[Nlist.length] : Nlist.length + if (Pin0 && no1write != " 0️⃣ ") { //有 in 参数就通知保留项目 + $notify("🤖 " + "重写引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 筛选参数: " + pfi + pfo, "☠️ 重写 rewrite 中保留以下" + no1write + "个匹配项:" + "\n ⨷ " + Nlist.join("\n ⨷ "), rwrite_link) + } else if (dwrite.length > 0) { + $notify("🤖 " + "重写引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 筛选参数: " + pfi + pfo, "☠️ 重写 rewrite 中已禁用以下" + nowrite + "个匹配项:" + "\n ⨷ " + dwrite.join("\n ⨷ "), rwrite_link) + } + } + if (Nlist.length == 0) { $notify("🤖 " + "重写引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 筛选参数: " + pfi + pfo, "⚠️ 筛选后剩余rewrite规则数为 0️⃣ 条, 请检查参数及原始链接", nan_link) } + if(Preg){ Nlist = Nlist.map(Regex).filter(Boolean) // regex to filter rewrites + RegCheck(Nlist, "重写引用", "regex", Preg) } + if(Pregout){ Nlist = Nlist.map(RegexOut).filter(Boolean) // regex to delete rewrites + RegCheck(Nlist, "重写引用", "regout", Pregout) } + if (hostname != "") { Nlist.push(hostname) } + Nlist =Phide ==1? Nlist : [...dwrite,...Nlist] + return Nlist +} + +// 主机名处理 +function HostNamecheck(content, parain, paraout) { + var hname = content.replace(/ /g, "").split("=")[1].split(","); + var nname = []; + var dname = []; //删除项 + for (var i = 0; i < hname.length; i++) { + dd = hname[i] + const excludehn = (item) => dd.indexOf(item) != -1; + if (paraout && paraout != "") { //存在 out 参数时 + if (!paraout.some(excludehn)) { //out 未命中🎯️ + if (parain && parain != "") { + if (parain.some(excludehn)) { //Pin 命中🎯️ + nname.push(hname[i]) + } else { + dname.push(hname[i]) + } //Pin 未命中🎯️的记录 + } else { nname.push(hname[i]) } //无in 参数 + } else { dname.push(hname[i]) } //out 参数命中 + } else if (parain && parain != "") { //不存在 out,但有 in 参数时 + if (parain.some(excludehn)) { //Pin 命中🎯️ + nname.push(hname[i]) + } else { dname.push(hname[i]) } + } else { + nname.push(hname[i]) + } + } //for j + if (Pntf0 != 0) { + if (paraout || parain) { + var noname = dname.length <= 10 ? emojino[dname.length] : dname.length + var no1name = nname.length <= 10 ? emojino[nname.length] : nname.length + if (parain && no1name != " 0️⃣ ") { + $notify("🤖 " + "重写引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 筛选参数: " + pfihn + pfohn, "☠️ 主机名 hostname 中已保留以下" + no1name + "个匹配项:" + "\n ⨷ " + nname.join(","), rwhost_link) + } else if (dname.length > 0) { + $notify("🤖 " + "重写引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 筛选参数: " + pfihn + pfohn, "☠️ 主机名 hostname 中已删除以下" + noname + "个匹配项:" + "\n ⨷ " + dname.join(","), rwhost_link) + } + } + } + if (nname.length == 0) { + $notify("🤖 " + "重写引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 筛选参数: " + pfihn + pfohn, "⚠️ 主机名 hostname 中剩余 0️⃣ 项, 请检查参数及原始链接", nan_link) + } + if(Preg){ nname = nname.map(Regex).filter(Boolean) + RegCheck(nname, "主机名hostname","regex", Preg) } + if(Pregout){ nname = nname.map(RegexOut).filter(Boolean) + RegCheck(nname, "主机名hostname", "regout", Pregout) } + hname = "hostname=" + nname.join(", "); + return hname +} + +//Rewrite 筛选的函数 +function Rcheck(content, param) { + name = content.toUpperCase() + if (param) { + var flag = 0; //没命中 + const checkpara = (item) => name.indexOf(item.toUpperCase()) != -1; + if (param.some(checkpara)) { + flag = 1 //命中 + } + return flag + } else { //if param + return 2 + } //无参数 +} + +//分流规则转换及过滤(in&out),可用于 surge 及 quanx 的 rule-list +function Rule_Handle(subs, Pout, Pin) { + cnt = subs //.split("\n"); + Tin = Pin; //保留参数 + Tout = Pout; //过滤参数 + ply = Ppolicy; //策略组 + var nlist = [] + var RuleK = ["//", "#", ";"]; + var RuleK2 = ["host,", "-suffix,", "domain,", "-keyword,", "ip-cidr,", "ip-cidr6,", "geoip,", "user-agent,", "ip6-cidr,"]; + if (Tout != "" && Tout != null) { // 有 out 参数时 + var dlist = []; + for (var i = 0; i < cnt.length; i++) { + cc = cnt[i].replace(/^\s*\-\s/g,"").trim() + const exclude = (item) => cc.indexOf(item) != -1; // 删除项 + const RuleCheck = (item) => cc.toLowerCase().indexOf(item) != -1; //无视注释行 + if (Tout.some(exclude) && !RuleK.some(RuleCheck) && RuleK2.some(RuleCheck)) { + dlist.push("-" + Rule_Policy(cc)) // 注释掉条目 + } else if (!RuleK.some(RuleCheck) && cc) { //if Pout.some, 不操作注释项 + dd = Rule_Policy(cc); + if (Tin != "" && Tin != null) { + const include = (item) => dd.indexOf(item) != -1; // 保留项 + if (Tin.some(include)) { + nlist.push(dd); + } + } else { + nlist.push(dd); + } + } //else if cc + }//for cnt + var no = dlist.length <= 10 ? emojino[dlist.length] : dlist.length + if (dlist.length > 0) { + if (Pntf0 != 0) { $notify("🤖 " + "分流引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 禁用: " + Tout, "☠️ 已禁用以下" + no + "条匹配规则:" + "\n ⨷ " + dlist.join("\n ⨷ "), rule_link) } + } else { $notify("🤖 " + "分流引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 禁用: " + Tout, "⚠️ 未发现任何匹配项, 请检查参数或原始链接", nan_link) } + if (Tin != "" && Tin != null) { //有 in 跟 out 参数时 + if (nlist.length > 0) { + var noin0 = nlist.length <= 10 ? emojino[nlist.length] : nlist.length + if (Pntf0 != 0) { + $notify("🤖 " + "分流引用 ➟ " + "⟦" + subtag + "⟧", "✅ 保留:" + Tin, "🎯 已保留以下 " + noin0 + "条匹配规则:" + "\n ⨁ " + nlist.join("\n ⨁ "), rule_link) + } + } else { + $notify("🤖 " + "分流引用 ➟ " + "⟦" + subtag + "⟧", "✅ 保留:" + Tin + ",⛔️ 禁用: " + Tout, "⚠️ 筛选后剩余规则数为 0️⃣ 条, 请检查参数及原始链接", nan_link) + } + } else {// if Tin (No Tin) + if (nlist.length == 0) { + $notify("🤖 " + "分流引用 ➟ " + "⟦" + subtag + "⟧", "⛔️ 禁用: " + Tout, "⚠️ 筛选后剩余规则数为 0️⃣ 条, 请检查参数及原始链接", nan_link) + } + } + nlist =Phide ==1? nlist : [...dlist,...nlist] + //return nlist; + } else if (Tin != "" && Tin != null) { //if Tout + var dlist = []; + for (var i = 0; i < cnt.length; i++) { + cc = cnt[i].replace(/^\s*\-\s/g,"").trim() + const RuleCheck = (item) => cc.indexOf(item) != -1; //无视注释行 + if (!RuleK.some(RuleCheck) && cc) { //if Pout.some, 不操作注释项 + dd = Rule_Policy(cc); + const include = (item) => dd.indexOf(item) != -1; // 保留项 + if (Tin.some(include)) { + nlist.push(dd); + } else { dlist.push("-" + dd) } + } + } // for cnt + if (nlist.length > 0) { + var noin = nlist.length <= 10 ? emojino[nlist.length] : nlist.length + if (Pntf0 != 0) { + $notify("🤖 " + "分流引用 ➟ " + "⟦" + subtag + "⟧", "✅ 保留:" + Tin, "🎯 已保留以下 " + noin + "条匹配规则:" + "\n ⨁ " + nlist.join("\n ⨁ "), rule_link) + } + } else { $notify("🤖 " + "分流引用 ➟ " + "⟦" + subtag + "⟧", "✅ 保留:" + Tin, "⚠️ 筛选后剩余规则数为 0️⃣ 条, 请检查参数及原始链接", nan_link) } + nlist =Phide ==1? nlist : [...dlist,...nlist] + //return nlist; + } else { //if Tin + nlist = cnt.map(Rule_Policy) + //return cnt.map(Rule_Policy) + } + nlist = Pfcr == 1? nlist.filter(Boolean).map(item => item+", force-cellular") : nlist.filter(Boolean) + nlist = Pfcr == 2? nlist.filter(Boolean).map(item => item+", multi-interface") : nlist.filter(Boolean) + if (Pvia!="") { + nlist = Pvia ==0? nlist.filter(Boolean).map(item => item+", via-interface=%TUN%") : nlist.filter(Boolean).map(item => item+", via-interface="+Pvia) + } + + return nlist +} + +function Rule_Policy(content) { //增加、替换 policy + var cnt = content.replace(/^\s*\-\s/g,"").replace(/REJECT-TINYGIF/gi,"reject").trim().split("//")[0].split(","); + var RuleK = ["//", "#", ";","[","/", "hostname"]; + var RuleK1 = ["host", "domain", "ip-cidr", "geoip", "user-agent", "ip6-cidr"]; + const RuleCheck = (item) => cnt[0].trim().toLowerCase().indexOf(item) == 0; //无视注释行 + const RuleCheck1 = (item) => cnt[0].trim().toLowerCase().indexOf(item) == 0 ; //无视 quanx 不支持的规则类别&排除 hostname + if (RuleK1.some(RuleCheck1) && !RuleK.some(RuleCheck) ) { + if (cnt.length == 3 && cnt.indexOf("no-resolve") == -1) { + ply0 = Ppolicy != "Shawn" ? Ppolicy : cnt[2] + nn = cnt[0] + ", " + cnt[1] + ", " + ply0 + } else if (cnt.length == 2) { //Surge rule-set + ply0 = Ppolicy != "Shawn" ? Ppolicy : "Shawn" + nn = cnt[0] + ", " + cnt[1] + ", " + ply0 + } else if (cnt.length == 3 && cnt[2].indexOf("no-resolve") != -1) { + ply0 = Ppolicy != "Shawn" ? Ppolicy : "Shawn" + nn = cnt[0] + ", " + cnt[1] + ", " + ply0 //+ ", " + cnt[2] + } else if (cnt.length == 4 && cnt[3].indexOf("no-resolve") != -1) { + ply0 = Ppolicy != "Shawn" ? Ppolicy : cnt[2] + nn = cnt[0] + ", " + cnt[1] + ", " + ply0 //+ ", " + cnt[3] + } else if (!RuleK.some(RuleCheck) && content) { + //$notify("未能解析" + "⟦" + subtag + "⟧" + "其中部分规则:", content, nan_link); + return "" + } else { return "" } + if (cnt[0].indexOf("URL-REGEX") != -1 || cnt[0].indexOf("PROCESS") != -1) { + nn = "" + } else { nn = nn.replace("IP-CIDR6", "ip6-cidr") } + return nn + } else if (cnt.length == 1 && !RuleK.some(RuleCheck) && cnt[0]!="" && cnt[0].indexOf("payload:")==-1) { // 纯域名/ip 列表 + return rule_list_handle(cnt[0]) + } else { return "" }//if RuleK1 check +} + +// 处理纯列表 +function rule_list_handle(cnt) { + if(cnt.trim().indexOf(" ")==-1){ + if(cnt.indexOf("::")!=-1 && cnt.indexOf("/")!=-1) { // ip-v6? + cnt = "ip6-cidr, " + cnt + cnt = Ppolicy == "Shawn" ? cnt+", Shawn" : cnt+", "+Ppolicy + } else if (cnt.split("/").length == 2) {//ip-cidr + cnt = "ip-cidr, " + cnt + cnt = Ppolicy == "Shawn" ? cnt+", Shawn" : cnt+", "+Ppolicy + } else if (cnt.indexOf("payload:")==-1) { //host - suffix + cnt = "host-suffix, " + cnt + cnt = Ppolicy == "Shawn" ? cnt+", Shawn" : cnt+", "+Ppolicy + } + return cnt + } +} + +// Domain-Set +function Domain2Rule(content) { + var cnt = content.split("\n"); + var RuleK = ["//", "#", ";"] + var nlist = [] + for (var i = 0; i< cnt.length; i++) { + cc = cnt[i].trim(); + const RuleCheck = (item) => cc.indexOf(item) != -1; //无视注释行 + if(!RuleK.some(RuleCheck) && cc) { + if (cc[0] == "."){ + nlist.push("host-suffix, " + cc.slice(1 , cc.length) ) + } else { + nlist.push("host, " + cc ) + } + } + } + return nlist.join("\n") +} +// filter 正则指定替换 regex1@policy1+regex2@policy2 +function policy_sets(cnt,para) { + pcnt = para.split("+") + cnt=cnt//.split("\n") + for (i=0;i filter_set(item, pcnt[i])) + } + } + cnt=cnt.filter(Boolean)//.join("\n") + return cnt + console.log(cnt) +} + +//策略指定 +function filter_set(cnt,para){ + if (cnt){ + paras=[para.split("@")[0],para.slice(para.split("@")[0].length+"@".length)] + console.log(para.split("@")[0].length+"@".length,paras) + cnt = cnt.split(",") + reg = RegExp(paras[0]) + console.log(paras,cnt) + if(cnt.length == 3){ + if (reg.test(cnt[1]) || reg.test(cnt[2])) { + cnt[2] = paras[1] + } + } + return cnt.join(",") + } +} + + +// 正则替换 filter/rewrite 的部分 +// 用途:如 tiktok 换区: JP -> KR ,如淘宝比价脚本 -> lite 横幅通知版本 +function ReplaceReg(cnt, para) { + var cnt0 = cnt//.join("\n") + //$notify("0","",cnt0) + var pp = para.replace(/\\\@/g,"atsymbol").replace(/\\\+/g,"plussymbol").split("+"); + for (var i = 0; i < pp.length; i++) { + var p1 = decodeURIComponent(pp[i].split("@")[0]).replace(/atsymbol/g,"\@").replace(/plussymbol/g,"\\\+").replace(/\,/g,","); + var p2 = decodeURIComponent(pp[i].split("@")[1]).replace(/atsymbol/g,"@").replace(/plussymbol/g,"+").replace(/\,/g,","); + p1 = new RegExp(p1, "gmi"); + cnt0 = cnt0.map(item => item.replace(p1, p2)); + //$notify(p1,p2,cnt0) + } + //$notify("1","",cnt0) + return cnt0//.split("\n") +} + +//混合订阅类型,用于未整体进行 base64 encode 的类型 +function Subs2QX(subs, Pudp, Ptfo, Pcert0, PTls13) { + var list0 = subs.split("\n"); + var QuanXK = ["shadowsocks=", "trojan=", "vmess=", "http="]; + var SurgeK = ["=ss,", "=vmess,", "=trojan,", "=http,", "=custom,"]; + var LoonK = ["=shadowsocks", "=shadowsocksr"] + var QXlist = []; + var failedList = []; + for (var i = 0; i < list0.length; i++) { + var node = "" + if (list0[i].trim().length > 3 && !/\;|\/|\#/.test(list0[i][0])) { + var type = list0[i].split("://")[0].trim() + var listi = list0[i].replace(/ /g, "") + var tag0 = list0[i].indexOf("tag=")!=-1 ? list0[i].split(/\&*(emoji|udp|tf0|cert|rename|replace)\=/)[0].split("tag=")[1] : "" + list0[i] = (type == "vmess" || type=="ssr") ? list0[i].split("#")[0] : list0[i] + const NodeCheck = (item) => listi.toLowerCase().indexOf(item) != -1; + try { + if (type == "vmess" && list0[i].indexOf("remarks=") == -1) { + var bnode = Base64.decode(list0[i].split("vmess://")[1]) + if (bnode.indexOf("over-tls=") == -1) { //v2rayN + node = V2QX(list0[i], Pudp, Ptfo, Pcert0, PTls13) + } else { //quantumult 类型 + node = VQ2QX(list0[i], Pudp, Ptfo, Pcert0, PTls13) + } + node = tag0 != "" ? URI_TAG(node, tag0) : node + } else if (type == "vmess" && list0[i].indexOf("remarks=") != -1) { //shadowrocket 类型 + node = VR2QX(list0[i], Pudp, Ptfo, Pcert0, PTls13) + node = tag0 != "" ? URI_TAG(node, tag0) : node + } else if (type == "ssr") { + node = SSR2QX(list0[i], Pudp, Ptfo) + node = tag0 != "" ? URI_TAG(node, tag0) : node + } else if (type == "ss") { + node = SS2QX(list0[i], Pudp, Ptfo) + node = tag0 != "" ? URI_TAG(node, tag0) : node + } else if (type == "ssd") { + node = SSD2QX(list0[i], Pudp, Ptfo) + } else if (type == "trojan") { + node = TJ2QX(list0[i], Pudp, Ptfo, Pcert0, PTls13) + node = tag0 != "" ? URI_TAG(node, tag0) : node + } else if (type == "https" && list0[i].indexOf(",") == -1) { + if (listi.indexOf("@") != -1) { + node = HPS2QX(list0[i], Ptfo, Pcert0, PTls13) + node = tag0 != "" ? URI_TAG(node, tag0) : node + } else { + var listh = Base64.decode(listi.split("https://")[1].split("#")[0]) + listh = "https://" + listh + "#" + listi.split("https://")[1].split("#")[1] + node = HPS2QX(listh, Ptfo, Pcert0, PTls13) + node = tag0 != "" ? URI_TAG(node, tag0) : node + } + } else if (QuanXK.some(NodeCheck)) { + node = QX_TLS(isQuanX(list0[i])[0], Pcert0, PTls13) + } else if (SurgeK.some(NodeCheck)) { + node = QX_TLS(Surge2QX(list0[i])[0], Pcert0, PTls13) + } else if (LoonK.some(NodeCheck)) { + node = Loon2QX(list0[i]) + } + } catch (e) { + failedList.push(`<<<\nContent: ${list0[i]}\nError: ${e}`) + } + if (node instanceof Array) { + for (var j in node) { + node[j] = Pudp != 0 ? XUDP(node[j],Pudp) : node[j] + node[j] = Ptfo != 0 ? XTFO(node[j],Ptfo) : node[j] + QXlist.push(node[j]) + } + } else if (node != "" && node) { + node = Pudp != 0 ? XUDP(node,Pudp) : node + node = Ptfo != 0 ? XTFO(node,Ptfo) : node + QXlist.push(node) + } + } + } + if (failedList.length > 0 && Pntf0 != 0) { + $notify(`⚠️ 有 ${failedList.length} 条数据解析失败, 已忽略`, "出错内容👇", failedList.join("\n")); + } + return QXlist; +} + +// qx 类型 tls/udp 验证问题 +function QX_TLS(cnt,Pcert0,PTls13) { + var cert0 = Pcert0 == 1? "tls-verification=true, " : "tls-verification=false, " + if(cnt.indexOf("tls-verification") != -1){ + cnt = cnt.replace(RegExp("tls\-verification.*?\,", "gmi"), cert0) + }else if(cnt.indexOf("obfs=over-tls")!=-1 || cnt.indexOf("obfs=wss")!=-1){ + cnt = cnt.replace(new RegExp("tag.*?\=", "gmi"), cert0+"tag=") + } + if (cnt.trim().indexOf("shadowsocks")!=0) { //关闭非 ss/ssr 类型的 udp + udp = "udp-relay=false, " + if(cnt.indexOf("udp-relay") != -1){ + var cnt = cnt.replace(RegExp("udp\-relay.*?\,", "gmi"), udp) + }else{ + var cnt = cnt.replace(new RegExp("tag.*?\=", "gmi"), udp+"tag=") + } + } + return cnt +} + +//将sip008格式的订阅转换成quanx格式 +function SIP2QuanX (cnt) { + cnt = JSON.parse(cnt) + ll =cnt.length + nodes =[] + for (i=0; i name.indexOf(item.toUpperCase()) != -1; + if (params.every(checkpara)) { + flag = 1 + } + }//for + return flag + } else { //if param + return 2 + } +} + +//节点过滤,使用+连接多个关键词(逻辑"或"):in 为保留,out 为排除, "与"逻辑请用符号"."连接 +function Filter(servers, Pin, Pout) { + var Nlist = []; + var Delist = []; + var Nname = []; + servers = servers.filter(Boolean) + for (var i = 0; i < servers.length; i++) { + if (Scheck(servers[i], Pin) != 0 && Scheck(servers[i], Pout) != 1) { + Nlist.push(servers[i]) + Nname.push(servers[i].replace(/ /g, "").split("tag=")[1]) + } else { Delist.push(servers[i].replace(/ /g, "").split("tag=")[1]) } //记录未被保留节点 + }//for + var no = Delist.length <= 10 ? emojino[Delist.length] : Delist.length; + var no1 = Nlist.length <= 10 ? emojino[Nlist.length] : Nlist.length; + if (Pntf0 == 1 && Delist.length >= 1) {//通知部分 + if (Pin && no1 > 0) { //有 in 参数就通知保留部分 + $notify("👥 引用" + "⟦" + subtag + "⟧" + " 开始节点筛选", "🕹 筛选关键字: " + pfi + pfo, "☠️ 已保留以下 " + no1 + "个节点\n" + Nname.join(", "), sub_link); + } else if (Pout && no > 0) { + $notify("👥 引用" + "⟦" + subtag + "⟧" + " 开始节点筛选", "🕹 筛选关键字: " + pfi + pfo, "☠️ 已删除以下 " + no + "个节点\n" + Delist.join(", "), sub_link); + } + } else if (no1 == 0 || no1 == null) { //无剩余节点时强制通知 + $notify("‼️ ⟦" + subtag + "⟧" + "筛选后节点数为0️⃣", "⚠️ 请自行检查原始链接以及筛选参数", link0, sub_link); + } + return Nlist +} + +function FilterScript(servers, script) { + $notify("🤖 启用脚本进行筛选", "", script); + try { + const $ = Tools(); + eval(script); + // extract server tags + const nodes = Tools().getNodeInfo(servers); + const IN = filter(nodes); + const res = servers.filter((_, i) => IN[i]); + if (res.length === 0) { + $notify("‼️ ⟦" + subtag + "⟧" + "筛选后节点数为0️⃣", "⚠️ 请自行检查原始链接以及筛选参数", link0, sub_link); + } + return res; + } catch (err) { + $notify("❌ 脚本筛选出现错误", "", err); + return servers; + } +} + +//SSR 类型 URI 转换 quanx 格式 +function SSR2QX(subs, Pudp, Ptfo) { + var nssr = [] + var cnt = Base64.decode(subs.split("ssr://")[1].replace(/-/g, "+").replace(/_/g, "/")).split("\u0000")[0] + var obfshost = ''; + var oparam = ''; + if (cnt.split(":").length <= 6) { //排除难搞的 ipv6 节点 + type = "shadowsocks="; + ip = cnt.split(":")[0] + ":" + cnt.split(":")[1]; + pwd = "password=" + Base64.decode(cnt.split("/?")[0].split(":")[5].replace(/-/g, "+").replace(/_/g, "/")).split("\u0000")[0]; + mtd = "method=" + cnt.split(":")[3]; + obfs = "obfs=" + cnt.split(":")[4] + ", "; + ssrp = "ssr-protocol=" + cnt.split(":")[2]; + if (cnt.indexOf("obfsparam=") != -1) { + obfshost = cnt.split("obfsparam=")[1].split("&")[0] != "" ? "obfs-host=" + Base64.decode(cnt.split("obfsparam=")[1].split("&")[0].replace(/-/g, "+").replace(/_/g, "/")).split(",")[0].split("\u0000")[0] + ", " : "" + } + if (cnt.indexOf("protoparam=") != -1) { + oparam = cnt.split("protoparam=")[1].split("&")[0] != "" ? "ssr-protocol-param=" + Base64.decode(cnt.split("protoparam=")[1].split("&")[0].replace(/-/g, "+").replace(/_/g, "/")).split(",")[0].split("\u0000")[0] + ", " : "" + } + tag = "tag=" + (Base64.decode(cnt.split("remarks=")[1].split("&")[0].replace(/-/g, "+").replace(/_/g, "/"))).split("\u0000")[0] + pudp = Pudp == 1 ? "udp-relay=true" : "udp-relay=false"; + ptfo = Ptfo == 1 ? "fast-open=true" : "fast-open=false"; + nssr.push(type + ip, pwd, mtd, obfs + obfshost + oparam + ssrp, pudp, ptfo, tag) + QX = nssr.join(", ") + } else { QX = "" } + return QX; +} + +//Trojan 类型 URI 转换成 QX +function TJ2QX(subs, Pudp, Ptfo, Pcert0, PTls13) { + var ntrojan = [] + var cnt = subs.split("trojan://")[1] + type = "trojan="; + if (cnt.indexOf(":443") != -1) { + ip = cnt.split("@")[1].split(":443")[0] + ":443"; + } else { + ip = cnt.split("@")[1].split("?")[0].split("\n")[0].split("#")[0].trim(); //非 443 端口的奇葩机场? + } + pwd = cnt.split("@")[0]? "password=" + decodeURIComponent(cnt.split("@")[0]):""; + obfs = "over-tls=true"; + pcert = cnt.indexOf("allowInsecure=0") != -1 ? "tls-verification=true" : "tls-verification=false"; + thost = cnt.indexOf("sni=") != -1? "tls-host="+cnt.split("sni=")[1].split(/&|#/)[0]:"" + ptls13 = PTls13 == 1 ? "tls13=true" : "tls13=false" + if (Pcert0 == 0) { + pcert = "tls-verification=false" + } else if (Pcert0 == 1) { + pcert = "tls-verification=true" + } + pudp = Pudp == 1 ? "udp-relay=false" : "udp-relay=false"; + ptfo = Ptfo == 1 ? "fast-open=true" : "fast-open=false"; + tag = cnt.indexOf("#") != -1 ? "tag=" + decodeURIComponent(cnt.split("#")[1]) : "tag= [trojan]" + ip + ntrojan.push(type + ip, pwd, obfs, pcert, thost, ptls13, pudp, ptfo, tag) + QX = ntrojan.filter(Boolean).join(", "); + return QX; +} + +//SS 类型 URI 转换 quanx 格式 +function SS2QX(subs, Pudp, Ptfo) { + var nssr = [] + var cnt = subs.split("ss://")[1] + if (cnt.split(":").length <= 6) { //排除难搞的 ipv6 节点 + type = "shadowsocks="; + let cntt = cnt.split("#")[0] + if (cntt.indexOf("@") != -1 && cntt.indexOf(":") != -1) { + ip = cnt.split("@")[1].split("#")[0].split("/")[0]; + pwdmtd = Base64.decode(cnt.split("@")[0].replace(/-/g, "+").replace(/_/g, "/")).split("\u0000")[0].split(":") + } else { + var cnt0 = Base64.decode(cnt.split("#")[0].replace(/-/g, "+").replace(/_/g, "/").split("\u0000")[0]); + ip = cnt0.split("@")[1].split("#")[0].split("/")[0]; + pwdmtd = cnt0.split("@")[0].split(":") + } + pwd = "password=" + pwdmtd[1]; + mtd = "method=" + pwdmtd[0]; + obfs = cnt.split("obfs%3D")[1] != null ? ", obfs=" + cnt.split("obfs%3D")[1].split("%3B")[0].split("#")[0] : ""; + obfshost = cnt.split("obfs-host%3D")[1] != null ? ", obfs-host=" + cnt.split("obfs-host%3D")[1].split("&")[0].split("#")[0] : ""; + tag = "tag=" + decodeURIComponent(cnt.split("#")[1]) + pudp = Pudp == 1 ? "udp-relay=true" : "udp-relay=false"; + ptfo = Ptfo == 1 ? "fast-open=true" : "fast-open=false"; + nssr.push(type + ip, pwd, mtd + obfs + obfshost, pudp, ptfo, tag) + QX = nssr.join(", ") + return QX; + } +} + +//SSD 类型 URI 转换 quanx 格式 +function SSD2QX(subs, Pudp, Ptfo) { + var j = 0 + var QX = [] + var cnt = JSON.parse(Base64.decode(subs.split("ssd://")[1])) + var type = "shadowsocks="; + var pwd = "password=" + cnt.password; + var mtd = "method=" + cnt.encryption; + var obfs = "" + var obfshost = "" + var port = cnt.port ? ":" + cnt.port : "" + if (cnt.plugin_options) { + obfs = cnt.plugin_options.split(";")[0] != null ? ", " + cnt.plugin_options.split(";")[0] : ""; + obfshost = cnt.plugin_options.split(";")[1] != null ? ", " + cnt.plugin_options.split(";")[1] : ""; + } + pudp = Pudp == 1 ? "udp-relay=true" : "udp-relay=false"; + ptfo = Ptfo == 1 ? "fast-open=true" : "fast-open=false"; + for (var i in cnt.servers) { + ip = cnt.servers[i].server; + if (cnt.servers[i].plugin_options) { + obfs = cnt.servers[i].plugin_options.split(";")[0] != null ? ", " + cnt.servers[i].plugin_options.split(";")[0] : ""; + obfshost = cnt.servers[i].plugin_options.split(";")[1] != null ? ", " + cnt.servers[i].plugin_options.split(";")[1] : ""; + } + if (cnt.servers[i].encryption) { //独立的加密方式 + mtd = "method=" + cnt.servers[i].encryption + } + if (cnt.servers[i].password) { //独立的密码 + pwd = "password=" + cnt.servers[i].password + } + if (ip.indexOf(".") > 0) { //排除难搞的 ipv6 节点 + port = cnt.servers[i].port ? ":" + cnt.servers[i].port : port; + tag = "tag=" + cnt.servers[i].remarks; + QX[j] = type + ip + port + ", " + pwd + ", " + mtd + obfs + obfshost + ", " + pudp + ", " + ptfo + ", " + tag; + var j = j + 1; + } + } + return QX; +} + +// 纠正部分不规范的写法(没有把 tag 写在最后) +function QXFix(cntf) { + //console.log("hh"+cntf) + var cnti = cntf.replace(/tag\s+\=/,"tag=").replace("chacha20-poly","chacha20-ietf-poly") + var hd = cnti.split("tag=")[0] + var tag = "tag="+cnti.split("tag=")[1].split(",")[0] + var tail = cnti.split(tag+",") + cnti = tail.length<=1? cntf : String(hd + tail[1].split("\r")[0] +"," + tag) + return cnti +} + +// 用于过滤非节点部分(比如整份配置中其它内容),同时纠正部分不规范的写法(没有把 tag 写在最后) +function isQuanX(content) { + var cnts = content.split("\n"); + var nlist = [] + for (var i = 0; i < cnts.length; i++) { + var cnti = cnts[i]; + if (cnti.indexOf("=") != -1 && cnti.indexOf("tag") != -1) { + var cnt = cnti.split("=")[0].trim() + if (cnt == "http" || cnt == "shadowsocks" || cnt == "trojan" || cnt == "vmess") { + nlist.push(QXFix(cnti)) + } + } + } + return nlist +} + +//surge script/rewrite - > quanx +function isQuanXRewrite(content) { + cnt = content + cnt0=[] + var RuleK = ["host,", "-suffix,", "domain,", "-keyword,", "ip-cidr,", "ip-cidr6,", "geoip,", "user-agent,", "ip6-cidr,","force-http"]; + + for (var i = 0; i< cnt.length; i++){ + if(cnt[i]){ + var cnti = cnt[i] + const RuleCheck = (item) => cnti.toLowerCase().indexOf(item) != -1; + + if (cnti.indexOf("pattern")!=-1 && cnti.indexOf("type")!=-1 || cnti.indexOf("http-r")!=-1) { + cnti=SGMD2QX(cnti)[0]? SGMD2QX(cnti)[0]:"" + //console.log(cnti) + }else if ((cnti.indexOf(" 302")!=-1 || cnti.indexOf(" 307")!=-1) && cnti.indexOf(" url ")==-1){ + cnti=SGMD2QX(cnti)[0]? SGMD2QX(cnti)[0]:"" + //console.log("sss",cnti) + }else if(cnti.indexOf("URL-REGEX")!=-1 || cnti.indexOf(" header")!=-1 || cnti.replace(/ /g,"").indexOf("hostname=")!=-1){ + cnti=SGMD2QX(cnti)[0]? SGMD2QX(cnti)[0]:"" + }else if(cnti.indexOf(" data=")!=-1){ + cnti=cnti.replace(/ /g, "").split("data=")[0] + " url " + "reject-dict" + } + if (cnti.trim()[0]!="[" && cnti.indexOf("RULE-SET")==-1 && !/cronexp\=|type\=cron/.test(cnti.replace(/ /g,"")) && !RuleK.some(RuleCheck)) { + cnt0.push(cnti) // 排除其它项目后写入 + } + } + } + //console.log(cnt0) + return cnt0 +} + + + +//根据节点名排序(不含emoji 部分) +function QXSort(content, para) { + var nlist = content;//.split("\n"); + if (para == 1) { + return nlist.sort(ToTag) + } else if (para == -1) { + return nlist.sort(ToTagR) + } else if(para == "x") { + return shuffle(nlist) + } else if(para == "0") { + return nlist + } else { + return Sort_KWD (nlist,para) //关键词排序 + } +} +//正序 +function ToTag(elem1, elem2) { + var tag1 = elem1.split("tag")[1].split("=")[1].trim() + var tag2 = elem2.split("tag")[1].split("=")[1].trim() + res = tag1 > tag2 ? 1 : -1 + return res +} +//逆序 +function ToTagR(elem1, elem2) { + var tag1 = elem1.split("tag")[1].split("=")[1].trim() + var tag2 = elem2.split("tag")[1].split("=")[1].trim() + res = tag1 > tag2 ? -1 : 1 + return res +} +// 随机洗牌排序 +function shuffle(arr) { + var input = arr; + for (var i = input.length - 1; i >= 0; i--) { + var randomIndex = Math.floor(Math.random() * (i + 1)); + var itemAtIndex = input[randomIndex]; + input[randomIndex] = input[i]; + input[i] = itemAtIndex; + } + return input; +} + +//根据指定规则排序 +function Sort_KWD (cnt,strs) { + strlist = strs.indexOf("<") != -1 ? strs.split("<"):strs.split(">") + regj = strlist.map(item => RegExp(item, "i")) + //dir = PsortX + dir = strs.indexOf("<") != -1 ? -1:1 + var arr = new Array(strlist.length+1); //表格有n行 + for(var i = 0;i < arr.length; i++){ + arr[i] = []; //每行有列 + } + for (var i =0; i item.sort(ToTagR)):arr + arr = PsortX == 1? arr.map(item => item.sort(ToTag)):arr + newarr = MixArr(arr,dir) + return newarr +} + +function MixArr(cnt,dir){ + var cnt0=[] + for (i=0; i s.split("tag=")[0] + "tag=" + newNames[i]); + } catch (err) { + $notify("❌ 脚本重命名出现错误", "", err); + return servers; + } + +} + +//删除 emoji +function emoji_del(str) { + return str.replace(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/g, "").trim();//unescape(escape(str).replace(/\%uD.{3}/g, '')); +} + +//为节点名添加 emoji +function get_emoji(emojip, sname) { + var Lmoji = { + "🏳️‍🌈": ["流量", "时间", "应急", "过期", "Bandwidth", "expire"], + "🇦🇿": ["阿塞拜疆"], + "🇦🇹": ["奥地利", "奧地利", "Austria", "维也纳"], + "🇦🇺": ["AU", "Australia", "Sydney", "澳大利亚", "澳洲", "墨尔本", "悉尼" ,"土澳", "京澳","廣澳","滬澳","沪澳","广澳"], + "🇧🇪": ["BE", "比利時","比利时"], + "🇧🇬": ["保加利亚", "保加利亞","Bulgaria"], + "🇵🇰": ["巴基斯坦"], + "🇰🇭": ["柬埔寨"], + "🇺🇦": ["烏克蘭","乌克兰"], + "🇭🇷": ["克罗地亚","HR","克羅地亞"], + "🇨🇦": ["CA", "Canada","CANADA", "CAN", "Waterloo", "加拿大", "蒙特利尔", "温哥华", "楓葉", "枫叶", "滑铁卢", "多伦多"], + "🇨🇭": ["瑞士", "苏黎世", "Switzerland"], + "🇳🇬": ["尼日利亚", "NG", "尼日利亞","拉各斯"], + "🇨🇿": ["Czechia", "捷克"], + "🇸🇰": ["斯洛伐克", "SK"], + "🇸🇮": ["斯洛文尼亚"], + "🇷🇸": ["RS", "塞尔维亚"], + "🇲🇩": ["摩爾多瓦","MD","摩尔多瓦"], + "🇩🇪": ["DE", "German", "GERMAN", "德国", "德國", "法兰克福","京德","滬德","廣德","沪德","广德"], + "🇩🇰": ["DK","DNK","丹麦","丹麥"], + "🇪🇸": ["ES", "西班牙", "Spain"], + "🇪🇺": ["EU", "欧盟", "欧罗巴"], + "🇫🇮": ["Finland", "芬兰","芬蘭","赫尔辛基"], + "🇫🇷": ["FR", "France", "法国", "法國", "巴黎"], + "🇬🇧": ["UK", "GB", "England", "United Kingdom", "英国", "伦敦", "英"], + "🇲🇴": ["MO", "Macao","Macau", "MAC", "澳门", "澳門", "CTM"], + "🇰🇿": ["哈萨克斯坦"], + "🇭🇺": ["匈牙利", "Hungary"], + "🇱🇹": ["立陶宛"], + "🇭🇰": ["HK", "Hongkong", "Hong Kong", "HongKong", "HONG KONG","香港", "深港", "沪港", "呼港", "HKT", "HKBN", "HGC", "WTT", "CMI", "穗港", "京港", "港"], + "🇮🇩": ["ID", "Indonesia", "印尼", "印度尼西亚", "雅加达"], + "🇮🇪": ["Ireland", "IRELAND", "爱尔兰", "愛爾蘭", "都柏林"], + "🇮🇱": ["Israel", "以色列"], + "🇮🇳": ["India", "IND", "INDIA","印度", "孟买", "Mumbai","IN "], + "🇮🇸": ["IS","ISL", "冰岛","冰島"], + "🇰🇵": ["KP", "朝鲜"], + "🇰🇷": ["KR", "Korea", "KOR", "韩国", "首尔", "韩", "韓","春川"], + "🇱🇺": ["卢森堡"], + "🇱🇻": ["Latvia", "Latvija", "拉脱维亚"], + "🇲🇽️": ["MEX", "MX", "墨西哥"], + "🇲🇾": ["MY", "Malaysia","MALAYSIA", "马来西亚", "大馬", "馬來西亞", "吉隆坡"], + "🇳🇱": ["NL", "Netherlands", "荷兰", "荷蘭", "尼德蘭", "阿姆斯特丹"], + "🇵🇭": ["PH", "Philippines", "菲律宾", "菲律賓"], + "🇷🇴": [" RO ", "罗马尼亚"], + "🇧🇾": ["BY","白俄罗斯","白俄羅斯"], + "🇷🇺": ["RU", "Russia", "俄罗斯", "俄国", "俄羅斯", "伯力", "莫斯科", "圣彼得堡", "西伯利亚", "新西伯利亚", "京俄", "杭俄","廣俄","滬俄","广俄","沪俄"], + "🇸🇦": ["沙特", "迪拜"], + "🇸🇪": ["SE", "Sweden","瑞典"], + "🇸🇬": ["SG", "Singapore","SINGAPORE", "新加坡", "狮城", "沪新", "京新", "泉新", "穗新", "深新", "杭新", "广新","廣新","滬新"], + "🇹🇭": ["TH", "Thailand", "泰国", "泰國", "曼谷"], + "🇹🇷": ["TR", "Turkey", "土耳其", "伊斯坦布尔"], + "🇹🇼": ["TW", "Taiwan","TAIWAN", "台湾", "台北", "台中", "新北", "彰化", "CHT", "台", "HINET"], + "🇺🇸": ["US", "USA", "America", "United States", "美国", "美", "京美", "波特兰", "达拉斯", "俄勒冈", "凤凰城", "费利蒙", "硅谷", "矽谷", "拉斯维加斯", "洛杉矶", "圣何塞", "圣克拉拉", "西雅图", "芝加哥", "沪美", "哥伦布", "纽约"], + "🇻🇳": ["VN", "越南", "胡志明市"], + "🇮🇹": ["Italy", "IT", "Nachash", "意大利", "米兰", "義大利"], + "🇿🇦": ["South Africa", "南非"], + "🇦🇪": ["United Arab Emirates", "阿联酋","AE "], + "🇧🇷": ["BR", "Brazil", "巴西", "圣保罗"], + "🇯🇵": ["JP", "Japan","JAPAN", "日本", "东京", "大阪", "埼玉", "沪日", "穗日", "川日", "中日", "泉日", "杭日", "深日", "辽日", "广日"], + "🇦🇷": ["AR", "阿根廷"], + "🇳🇴": ["Norway", "挪威", "NO"], + "🇵🇱": ["PL", "POL", "波兰","波蘭"], + "🇨🇱": ["智利"], + "🇳🇿": ["新西蘭","新西兰"], + "🇬🇷": ["希腊","希臘"], + "🇪🇬": ["埃及"], + "🇮🇲": ["马恩岛","馬恩島"], + "🇵🇹": ["葡萄牙"], + "🇲🇳": ["蒙古"], + "🇵🇪": ["秘鲁","祕魯"], + "🇨🇴": ["哥伦比亚"], + "🇪🇪": ["爱沙尼亚"], + "🇲🇰": ["马其顿","馬其頓"], + "🇲🇹": ["马耳他"], + "🇻🇪": ["委内瑞拉"], + "🇧🇦": ["波黑共和国","波黑"], + "🇬🇪": ["格魯吉亞","格鲁吉亚"], + "🇦🇱": ["阿爾巴尼亞","阿尔巴尼亚"], + "🇨🇾": ["CY","塞浦路斯"], + "🇨🇷": ["哥斯达黎加"], + "🇹🇳": ["突尼斯"], + "🇵🇦": ["巴拿马","巴拿馬"], + "🇮🇷": ["伊朗"], + "🇯🇴": ["约旦"], + "🇺🇾": ["乌拉圭"], + "🇰🇪": ["肯尼亚"], + "🇰🇬": ["吉尔吉斯坦","吉尔吉斯斯坦"], + "🇳🇵": ["尼泊尔"], + "🇨🇳": ["CN", "China", "回国", "中国","中國", "江苏", "北京", "上海", "广州", "深圳", "杭州", "徐州", "青岛", "宁波", "镇江", "back"], + } + str1 = JSON.stringify(Lmoji) + aa = JSON.parse(str1) + bb = JSON.parse(str1.replace(/🇹🇼/g, " 🇨🇳")) + var cnt = emojip ==1? aa:bb; + var flag = 0; + for (var key in cnt) { + dd = cnt[key] + for (i in dd) { + if (sname.indexOf(dd[i]) != -1) { + flag = 1; + nname = key + " " + sname.replace(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/g, "").trim(); // use regex to remove the original flag + return [nname,key] + } + } + } + if (flag == 0) { return ["🏴‍☠️ " + sname.replace(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/g, "").trim(), "🏴‍☠️"] } +} + +//emoji 处理 +function emoji_handle(servers, Pemoji) { + var nlist = [] + var ser0 = servers + + for (var i = 0; i < ser0.length; i++) { + if (ser0[i].indexOf("tag=") != -1) { + var oname = ser0[i].split("tag=")[1].trim(); + var hd = ser0[i].split("tag=")[0]; + var nname = oname;//emoji_del(oname); + // Code: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2, Emoji: https://emojipedia.org/flags/ + if (Pemoji == 1) { + var nname = get_emoji(1, nname)[0] + } else if (Pemoji == 2) { + var nname = get_emoji(2, nname)[0] + } else if (Pemoji == -1) { + nname = emoji_del(oname); + } + var nserver = hd + "tag=" + nname.replace(" ", " ").trim() + nlist.push(nserver) + } + } + return nlist +} + +//Surge2QX 转换主函数 +function Surge2QX(conf) { + var QXlist = conf.split("\n").map(isSurge).filter(Boolean) + var Nlist = [] + var node="" + for (var i = 0; i < QXlist.length; i++) { + var cnt = QXlist[i]; + if (cnt.split("=")[1].split(",")[0].indexOf("trojan") != -1) { + node = Strojan2QX(cnt)//surge 3的trojan + } else if (cnt.split("=")[1].split(",")[0].indexOf("http") != -1) { + node = Shttp2QX(cnt) //surge 3的http + } else if (cnt.split("=")[1].split(",")[0].indexOf("vmess") != -1) { + node = SVmess2QX(cnt) //surge 3的Vmess + } else if (cnt.split("=")[1].split(",")[0].indexOf("ss") != -1) { + node = SSS2QX(cnt) //surge 3的SS + } else if (cnt.split("=")[1].split(",")[0].indexOf("custom") != -1) { + node = SCT2QX(cnt) //surge2写法 + } + node = Pudp0 != 0 ? XUDP(node,Pudp0) : node + node = Ptfo0 != 0 ? XTFO(node,Ptfo0) : node + if (cnt.indexOf("test-url") !=-1) { + var checkurl = ", server_check_url" + cnt.split("test-url")[1].split(",")[0] + node = node.replace(/\,(\s)*tag/, checkurl + ", tag") + } + Nlist.push(node) + } + return (Nlist) +} + + +// surge2 中的 SS 类型写法(custom) +//🇷🇺 俄罗斯 GIA = custom, ip, 152, aes-128-gcm, password123, https://xxx/download/SSEncrypt.module, obfs=tls, obfs-host=xxx.windows.com, udp-relay=true +function SCT2QX(content) { + var cnt = content; + var tag = "tag=" + cnt.split("=")[0].trim(); + var ipport = cnt.split(",")[1].trim() + ":" + cnt.split(",")[2].trim(); + var pmtd = "method=" + cnt.split(",")[3].trim(); + var pwd = "password=" + cnt.split(",")[4].trim(); + if (cnt.indexOf("obfs") != -1) { + pobfs = "obfs=" + cnt.replace(/obfs-host/, "").split("obfs")[1].split(",")[0].split("=")[1] + } else { pobfs = "" } + var phost = cnt.indexOf("obfs-host") != -1 ? "obfs-host" + cnt.split("obfs-host")[1].split(",")[0].trim() : ""; + if (phost != "") { + pobfs = pobfs + ", " + phost + } + var ptfo = paraCheck(cnt, "tfo") == "true" ? "fast-open=true" : "fast-open=false"; + var pudp = paraCheck(cnt, "udp-relay") == "true" ? "udp-relay=true" : "udp-relay=false"; + var nserver = pobfs != "" ? "shadowsocks= " + [ipport, pmtd, pwd, pobfs, ptfo, pudp, tag].join(", ") : "shadowsocks= " + [ipport, pmtd, pwd, ptfo, pudp, tag].join(", "); + return nserver +} + + +// surge3 中的 SS 类型 +function SSS2QX(content) { + var cnt = content; + var tag = "tag=" + cnt.split("=")[0].trim(); + var ipport = cnt.split(",")[1].trim() + ":" + cnt.split(",")[2].trim(); + var pmtd = "method=" + cnt.split("encrypt-method")[1].split(",")[0].split("=")[1]; + var pwd = "password=" + cnt.split("password")[1].split(",")[0].split("=")[1]; + if (cnt.indexOf("obfs") != -1) { + pobfs = "obfs=" + cnt.replace(/obfs-host/, "").split("obfs")[1].split(",")[0].split("=")[1] + } else { pobfs = "" } + var phost = cnt.indexOf("obfs-host") != -1 ? "obfs-host" + cnt.split("obfs-host")[1].split(",")[0].trim() : ""; + if (phost != "") { + pobfs = pobfs + ", " + phost + } + var ptfo = paraCheck(cnt, "tfo") == "true" ? "fast-open=true" : "fast-open=false"; + var pudp = paraCheck(cnt, "udp-relay") == "true" ? "udp-relay=true" : "udp-relay=false"; + var nserver = pobfs != "" ? "shadowsocks= " + [ipport, pmtd, pwd, pobfs, ptfo, pudp, tag].join(", ") : "shadowsocks= " + [ipport, pmtd, pwd, ptfo, pudp, tag].join(", "); + return nserver +} + +// surge 中的 Vmess 类型 +function SVmess2QX(content) { + var cnt = content; + var tag = "tag=" + cnt.split("=")[0].trim(); + var ipport = cnt.split(",")[1].trim() + ":" + cnt.split(",")[2].trim(); + var puname = cnt.indexOf("username") != -1 ? "password=" + cnt.split("username")[1].split(",")[0].split("=")[1].trim() : ""; + var pmtd = "method=aes-128-gcm"; + var ptls13 = paraCheck(cnt, "tls13") == "true" ? "tls13=true" : "tls13=false"; + var pverify = cnt.replace(/ /g,"").indexOf("skip-cert-verify=false") != -1 ? "tls-verification=true" : "tls-verification=false"; + pvefify = Pcert0 == 1? "tls-verification=true" : pverify ; + if (paraCheck(cnt.replace(/tls13/, ""), "tls") == "true" && paraCheck(cnt.replace(/ws-header/, ""), "ws") == "true") { + pobfs = "obfs=wss" + ", " + ptls13 + ", " + pverify + } else if (paraCheck(cnt.replace(/ws-header/, ""), "ws") == "true") { + pobfs = "obfs=ws" + } else if (paraCheck(cnt.replace(/tls13/, ""), "tls") != "false") { + pobfs = "obfs=over-tls" + ", " + ptls13 + ", " + pverify + } else if (paraCheck(cnt.replace(/ws-header/, ""), "ws") == "false") { + pobfs = "" + } + var puri = paraCheck(cnt, "ws-path") != "false" ? "obfs-uri=" + cnt.split("ws-path")[1].split(",")[0].split("=")[1].trim() : "obfs-uri=/" + var phost = cnt.indexOf("ws-headers") != -1 ? "obfs-host=" + cnt.split("ws-headers")[1].split(",")[0].split("=")[1].split(":")[1].trim() : ""; + if (pobfs.indexOf("ws" || "wss") != -1) { + if (phost != "") { + pobfs = pobfs + ", " + puri + ", " + phost + } else { pobfs = pobfs + ", " + puri } + } + var ptfo = paraCheck(cnt, "tfo") == "true" ? "fast-open=true" : "fast-open=false"; + var nserver = pobfs != "" ? "vmess= " + [ipport, puname, pmtd, pobfs, ptfo, tag].join(", ") : "vmess= " + [ipport, puname, pmtd, ptfo, tag].join(", "); + return nserver +} + +// 用于过滤非节点部分(比如整份配置中其它内容) +function isSurge(content) { + if (content.indexOf("=") != -1) { + cnt = content.split("=")[1].split(",")[0].trim() + if (cnt == "http" || cnt == "ss" || cnt == "trojan" || cnt == "vmess" || cnt == "custom") { + return content + } + } +} +// 用于参数检查 +function paraCheck(content, para) { + content=content.replace(/ /g,"") + if (content.indexOf(para+"=") == -1) { + return "false" + } else { + //console.log(para) + return content.split(para+"=")[1].split(",")[0].trim() + } +} +//surge中 trojan 类型转换 +function Strojan2QX(content) { + var cnt = content; + var tag = "tag=" + cnt.split("=")[0].trim(); + var ipport = cnt.split(",")[1].trim() + ":" + cnt.split(",")[2].trim(); + var pwd = "password=" + cnt.split("password")[1].split(",")[0].split("=")[1].trim(); + var ptls = "over-tls=true"; + var ptfo = paraCheck(cnt, "tfo") == "true" ? "fast-open=true" : "fast-open=false"; + var pverify = cnt.replace(/ /g,"").indexOf("skip-cert-verify=false") != -1 ? "tls-verification=true" : "tls-verification=false"; + pvefify = Pcert0 == 1? "tls-verification=true" : pverify ; + var ptls13 = paraCheck(cnt, "tls13") == "true" ? "tls13=true" : "tls13=false"; + var nserver = "trojan= " + [ipport, pwd, ptls, ptfo, ptls13, pverify, tag].join(", "); + return nserver +} +// surge 中的 http 类型 +function Shttp2QX(content) { + var cnt = content; + var tag = "tag=" + cnt.split("=")[0].trim(); + var ipport = cnt.split(",")[1].trim() + ":" + cnt.split(",")[2].trim(); + var puname = cnt.indexOf("username") != -1 ? "username=" + cnt.split("username")[1].split(",")[0].split("=")[1].trim() : ""; + var pwd = cnt.indexOf("password") != -1 ? "password=" + cnt.split("password")[1].split(",")[0].split("=")[1].trim() : ""; + var ptls = cnt.split("=")[1].split(",")[0].trim() == "https" ? "over-tls=true" : "over-tls=false"; + var ptfo = paraCheck(cnt, "tfo") == "true" ? "fast-open=true" : "fast-open=false"; + if (ptls == "over-tls=true") { + var pverify = cnt.replace(/ /g,"").indexOf("skip-cert-verify=false") != -1 ? "tls-verification=true" : "tls-verification=false"; + pvefify = Pcert0 == 1? "tls-verification=true" : pverify ; + var ptls13 = paraCheck(cnt, "tls13") == "true" ? "tls13=true" : "tls13=false"; + ptls = ptls + ", " + pverify + ", " + ptls13 + } + var nserver = puname != "" ? "http= " + [ipport, puname, pwd, ptls, ptfo, tag].join(", ") : "http= " + [ipport, ptls, ptfo, tag].join(", "); + return nserver +} + +function Loon2QX(cnt) { + var type = cnt.split("=")[1].split(",")[0].trim() + var node = "" + if (type == "Shadowsocks") { //ss 类型 + node = LoonSS2QX(cnt) + } else if (type == "ShadowsocksR") { //ssr 类型 + node = LoonSSR2QX(cnt) + } + return node +} +//Loon 的 ss 部分 +function LoonSS2QX(cnt) { + var node = "shadowsocks=" + var ip = [cnt.split(",")[1].trim(), cnt.split(",")[2].trim()].join(":") + var mtd = "method=" + cnt.split(",")[3].trim() + var pwd = "password=" + cnt.split(",")[4].trim().split("\"")[1] + var obfs = cnt.split(",").length == 7 ? ", " + ["obfs=" + cnt.split(",")[5].trim(), "obfs-host=" + cnt.split(",")[6].trim()].join(",") : "" + var tag = ", tag=" + cnt.split("=")[0].trim() + node = node + [ip, mtd, pwd].join(", ") + obfs + tag + return node +} + +//Loon 的 ssr 部分 +//# SSR 格式:名称=协议类型,地址,端口,加密方式,密码,协议类型,{协议参数},混淆类型,{混淆参数} +//3 = ShadowsocksR, 1.2.3.4, 443, aes-256-cfb,"password",auth_aes128_md5,{},tls1.2_ticket_auth,{} +function LoonSSR2QX(cnt) { + var node = "shadowsocks=" + var ip = [cnt.split(",")[1].trim(), cnt.split(",")[2].trim()].join(":") + var mtd = "method=" + cnt.split(",")[3].trim() + var pwd = "password=" + cnt.split(",")[4].trim().split("\"")[1] + var ssrp = "ssr-protocol=" + cnt.split(",")[5].trim() + var ssrpara = "ssr-protocol-param=" + cnt.split(",")[6].replace(/\{|\}/g, "").trim() + var obfs = "obfs=" + cnt.split(",")[7].trim() + var obfshost = "obfs-host=" + cnt.split(",")[8].replace(/\{|\}/g, "").trim() + var tag = ", tag=" + cnt.split("=")[0].trim() + node = node + [ip, mtd, pwd, ssrp, ssrpara, obfs, obfshost].join(", ") + tag + return node +} + + +// fix yaml parse mistakes +function YAMLFix(cnt){ + cnt = cnt.replace(/\[/g,"yaml@bug1") + if (cnt.indexOf("{") != -1){ + cnt = cnt.replace(/(^|\n)- /g, "$1 - ").replace(/ - /g," - ").replace(/:(?!\s)/g,": ").replace(/\,\"/g,", \"").replace(/: {/g, ": {, ").replace(/, (Host|host|path|tls|mux|skip)/g,", $1") + //console.log(cnt) + cnt = cnt.replace(/{\s*name: /g,"{name: \"").replace(/, server:/g,"\", server:") + cnt = cnt.replace(/{|}/g,"").replace(/,/g,"\n ") + } + cnt = cnt.replace(/ -\n.*name/g," - name").replace(/\$|\`/g,"").split("proxy-providers:")[0].split("proxy-groups:")[0].replace(/\"(name|type|server|port|cipher|password|)(\"*)/g,"$1") + console.log(cnt) + cnt = cnt.indexOf("proxies:") == -1? "proxies:\n" + cnt :"proxies:"+cnt.split("proxies:")[1] + cnt = cnt.replace(/name\:(.*?)\:(.*?)\n/gmi,"name:$1冒号$2\n").replace(/\s{6}Host\:/g," Host:") //罕见bug情况 修复 + items=cnt.split("\n").map(yamlcheck) + cnt=items.join("\n") + //console.log(cnt.replace(/name\:(.*?)\:(.*?)\n/gmi,"name:$1冒号$2")) + console.log("after-fix"+cnt) + return cnt +} + + +function yamlcheck(cnt){ + if (cnt.indexOf("name") !=-1){ //名字以某些数字结尾时,解析有 bug + for (var i=0;i<10;i++) { + cnt = cnt.replace(new RegExp(patn[0][i], "gmi"),patn[4][i]) + } + + } + if (cnt.indexOf(":")!=-1) { + return cnt + } +} + +// Clash parser +function Clash2QX(cnt) { + const yaml = new YAML() + var aa = JSON.stringify(yaml.parse(YAMLFix(cnt))).replace(/yaml@bug𝟙/g,"[").replace(/冒号/gmi,":") + for (var i=0;i<10;i++) { + aa = aa.replace(new RegExp(patn[4][i], "gmi"),patn[0][i]) + } + var bb = JSON.parse(aa).proxies + //$notify("YAML Parse", "content", JSON.stringify(bb)) + //console.log(bb) + var nl = bb.length + var nodelist=[] + var node="" + for (i=0; i").replace(/</g, "<"); + }; +} + + +/* +YAML parser for Javascript +Author: Diogo Costa + +This program is released under the MIT License as follows: + +Copyright (c) 2011 Diogo Costa (costa.h4evr@gmail.com) + +*/ + +function YAML() { + var errors = [], + reference_blocks = [], + processing_time = 0, + regex = + { + "regLevel" : new RegExp("^([\\s\\-]+)"), + "invalidLine" : new RegExp("^\\-\\-\\-|^\\.\\.\\.|^\\s*#.*|^\\s*$"), + "dashesString" : new RegExp("^\\s*\\\"([^\\\"]*)\\\"\\s*$"), + "quotesString" : new RegExp("^\\s*\\\'([^\\\']*)\\\'\\s*$"), + "float" : new RegExp("^[+-]?[0-9]+\\.[0-9]+(e[+-]?[0-9]+(\\.[0-9]+)?)?$"), + "integer" : new RegExp("^[+-]?[0-9]+$"), + "array" : new RegExp("\\[\\s*(.*)\\s*\\]"), + "map" : new RegExp("\\{\\s*(.*)\\s*\\}"), + "key_value" : new RegExp("([a-z0-9_-][ a-z0-9_-]*):( .+)", "i"), + "single_key_value" : new RegExp("^([a-z0-9_-][ a-z0-9_-]*):( .+?)$", "i"), + "key" : new RegExp("([a-z0-9_-][ a-z0-9_-]+):( .+)?", "i"), + "item" : new RegExp("^-\\s+"), + "trim" : new RegExp("^\\s+|\\s+$"), + "comment" : new RegExp("([^\\\'\\\"#]+([\\\'\\\"][^\\\'\\\"]*[\\\'\\\"])*)*(#.*)?") + }; + + /** + * @class A block of lines of a given level. + * @param {int} lvl The block's level. + * @private + */ + function Block(lvl) { + return { + /* The block's parent */ + parent: null, + /* Number of children */ + length: 0, + /* Block's level */ + level: lvl, + /* Lines of code to process */ + lines: [], + /* Blocks with greater level */ + children : [], + /* Add a block to the children collection */ + addChild : function(obj) { + this.children.push(obj); + obj.parent = this; + ++this.length; + } + }; + } + + // function to create an XMLHttpClient in a cross-browser manner + + function fromURL(src, ondone) { + var client = createXMLHTTPRequest(); + client.onreadystatechange = function() { + if (this.readyState == 4 || this.status == 200) { + var txt = this.responseText; + ondone(YAML.eval0(txt)); + } + }; + client.open('GET', src); + client.send(); + } + + function parser(str) { + var regLevel = regex["regLevel"]; + var invalidLine = regex["invalidLine"]; + var lines = str.split("\n"); + var m; + var level = 0, curLevel = 0; + + var blocks = []; + + var result = new Block(-1); + var currentBlock = new Block(0); + result.addChild(currentBlock); + var levels = []; + var line = ""; + + blocks.push(currentBlock); + levels.push(level); + + for(var i = 0, len = lines.length; i < len; ++i) { + line = lines[i]; + + if(line.match(invalidLine)) { + continue; + } + + if(m = regLevel.exec(line)) { + level = m[1].length; + } else + level = 0; + + if(level > curLevel) { + var oldBlock = currentBlock; + currentBlock = new Block(level); + oldBlock.addChild(currentBlock); + blocks.push(currentBlock); + levels.push(level); + } else if(level < curLevel) { + var added = false; + + var k = levels.length - 1; + for(; k >= 0; --k) { + if(levels[k] == level) { + currentBlock = new Block(level); + blocks.push(currentBlock); + levels.push(level); + if(blocks[k].parent!= null) + blocks[k].parent.addChild(currentBlock); + added = true; + break; + } + } + + if(!added) { + errors.push("Error: Invalid indentation at line " + i + ": " + line); + return; + } + } + + currentBlock.lines.push(line.replace(regex["trim"], "")); + curLevel = level; + } + + return result; + } + + function processValue(val) { + val = val.replace(regex["trim"], ""); + var m = null; + + if(val == 'true') { + return true; + } else if(val == 'false') { + return false; + } else if(val == '.NaN') { + return Number.NaN; + } else if(val == 'null') { + return null; + } else if(val == '.inf') { + return Number.POSITIVE_INFINITY; + } else if(val == '-.inf') { + return Number.NEGATIVE_INFINITY; + } else if(m = val.match(regex["dashesString"])) { + return m[1]; + } else if(m = val.match(regex["quotesString"])) { + return m[1]; + } else if(m = val.match(regex["float"])) { + return parseFloat(m[0]); + } else if(m = val.match(regex["integer"])) { + return parseInt(m[0]); + } else if( !isNaN(m = Date.parse(val))) { + return new Date(m); + } else if(m = val.match(regex["single_key_value"])) { + var res = {}; + res[m[1]] = processValue(m[2]); + return res; + } else if(m = val.match(regex["array"])){ + var count = 0, c = ' '; + var res = []; + var content = ""; + var str = false; + for(var j = 0, lenJ = m[1].length; j < lenJ; ++j) { + c = m[1][j]; + if(c == '\'' || c == '"') { + if(str === false) { + str = c; + content += c; + continue; + } else if((c == '\'' && str == '\'') || (c == '"' && str == '"')) { + str = false; + content += c; + continue; + } + } else if(str === false && (c == '[' || c == '{')) { + ++count; + } else if(str === false && (c == ']' || c == '}')) { + --count; + } else if(str === false && count == 0 && c == ',') { + res.push(processValue(content)); + content = ""; + continue; + } + + content += c; + } + + if(content.length > 0) + res.push(processValue(content)); + return res; + } else if(m = val.match(regex["map"])){ + var count = 0, c = ' '; + var res = []; + var content = ""; + var str = false; + for(var j = 0, lenJ = m[1].length; j < lenJ; ++j) { + c = m[1][j]; + if(c == '\'' || c == '"') { + if(str === false) { + str = c; + content += c; + continue; + } else if((c == '\'' && str == '\'') || (c == '"' && str == '"')) { + str = false; + content += c; + continue; + } + } else if(str === false && (c == '[' || c == '{')) { + ++count; + } else if(str === false && (c == ']' || c == '}')) { + --count; + } else if(str === false && count == 0 && c == ',') { + res.push(content); + content = ""; + continue; + } + + content += c; + } + + if(content.length > 0) + res.push(content); + + var newRes = {}; + for(var j = 0, lenJ = res.length; j < lenJ; ++j) { + if(m = res[j].match(regex["key_value"])) { + newRes[m[1]] = processValue(m[2]); + } + } + + return newRes; + } else + return val; + } + + function processFoldedBlock(block) { + var lines = block.lines; + var children = block.children; + var str = lines.join(" "); + var chunks = [str]; + for(var i = 0, len = children.length; i < len; ++i) { + chunks.push(processFoldedBlock(children[i])); + } + return chunks.join("\n"); + } + + function processLiteralBlock(block) { + var lines = block.lines; + var children = block.children; + var str = lines.join("\n"); + for(var i = 0, len = children.length; i < len; ++i) { + str += processLiteralBlock(children[i]); + } + return str; + } + + function processBlock(blocks) { + var m = null; + var res = {}; + var lines = null; + var children = null; + var currentObj = null; + + var level = -1; + + var processedBlocks = []; + + var isMap = true; + + for(var j = 0, lenJ = blocks.length; j < lenJ; ++j) { + + if(level != -1 && level != blocks[j].level) + continue; + + processedBlocks.push(j); + + level = blocks[j].level; + lines = blocks[j].lines; + children = blocks[j].children; + currentObj = null; + + for(var i = 0, len = lines.length; i < len; ++i) { + var line = lines[i]; + + if(m = line.match(regex["key"])) { + var key = m[1]; + + if(key[0] == '-') { + key = key.replace(regex["item"], ""); + if (isMap) { + isMap = false; + if (typeof(res.length) === "undefined") { + res = []; + } + } + if(currentObj != null) res.push(currentObj); + currentObj = {}; + isMap = true; + } + + if(typeof m[2] != "undefined") { + var value = m[2].replace(regex["trim"], ""); + if(value[0] == '&') { + var nb = processBlock(children); + if(currentObj != null) currentObj[key] = nb; + else res[key] = nb; + reference_blocks[value.substr(1)] = nb; + } else if(value[0] == '|') { + if(currentObj != null) currentObj[key] = processLiteralBlock(children.shift()); + else res[key] = processLiteralBlock(children.shift()); + } else if(value[0] == '*') { + var v = value.substr(1); + var no = {}; + + if(typeof reference_blocks[v] == "undefined") { + errors.push("Reference '" + v + "' not found!"); + } else { + for(var k in reference_blocks[v]) { + no[k] = reference_blocks[v][k]; + } + + if(currentObj != null) currentObj[key] = no; + else res[key] = no; + } + } else if(value[0] == '>') { + if(currentObj != null) currentObj[key] = processFoldedBlock(children.shift()); + else res[key] = processFoldedBlock(children.shift()); + } else { + if(currentObj != null) currentObj[key] = processValue(value); + else res[key] = processValue(value); + } + } else { + if(currentObj != null) currentObj[key] = processBlock(children); + else res[key] = processBlock(children); + } + } else if(line.match(/^-\s*$/)) { + if (isMap) { + isMap = false; + if (typeof(res.length) === "undefined") { + res = []; + } + } + if(currentObj != null) res.push(currentObj); + currentObj = {}; + isMap = true; + continue; + } else if(m = line.match(/^-\s*(.*)/)) { + if(currentObj != null) + currentObj.push(processValue(m[1])); + else { + if (isMap) { + isMap = false; + if (typeof(res.length) === "undefined") { + res = []; + } + } + res.push(processValue(m[1])); + } + continue; + } + } + + if(currentObj != null) { + if (isMap) { + isMap = false; + if (typeof(res.length) === "undefined") { + res = []; + } + } + res.push(currentObj); + } + } + + for(var j = processedBlocks.length - 1; j >= 0; --j) { + blocks.splice.call(blocks, processedBlocks[j], 1); + } + + return res; + } + + function semanticAnalysis(blocks) { + var res = processBlock(blocks.children); + return res; + } + + function preProcess(src) { + var m; + var lines = src.split("\n"); + + var r = regex["comment"]; + + for(var i in lines) { + if(m = lines[i].match(r)) { +/* var cmt = ""; + if(typeof m[3] != "undefined") + lines[i] = m[1]; + else if(typeof m[3] != "undefined") + lines[i] = m[3]; + else + lines[i] = ""; + */ + if(typeof m[3] !== "undefined") { + lines[i] = m[0].substr(0, m[0].length - m[3].length); + } + } + } + + return lines.join("\n"); + } + + this.parse = function eval0(str) { + errors = []; + reference_blocks = []; + processing_time = (new Date()).getTime(); + var pre = preProcess(str) + var doc = parser(pre); + var res = semanticAnalysis(doc); + processing_time = (new Date()).getTime() - processing_time; + + return res; + } + +}; + + +/***********************************************************************************************/ +function Tools() { + const filter = (src, ...regex) => { + const initial = [...Array(src.length).keys()].map(() => false); + return regex.reduce((a, expr) => OR(a, src.map(item => expr.test(item))), initial) + } + + const rename = { + replace: (src, old, now) => { + return src.map(item => item.replace(old, now)); + }, + + delete: (src, ...args) => { + return src.map(item => args.reduce((now, expr) => now.replace(expr, ''), item)); + }, + + trim: (src) => { + return src.map(item => item.trim().replace(/[^\S\r\n]{2,}/g, ' ')); + } + } + + const getNodeInfo = servers => { + const nodes = { + names: servers.map(s => s.split("tag=")[1]), + types: servers.map(s => { + const type = s.match(/^(vmess|trojan|shadowsocks|http)=/); + return type ? type[1] : 'unknown'; + }) + }; + return nodes; + } + + + return { + filter, rename, getNodeInfo + } +} + +function AND(...args) { + return args.reduce((a, b) => a.map((c, i) => b[i] && c)); +} + +function OR(...args) { + return args.reduce((a, b) => a.map((c, i) => b[i] || c)) +} + +function NOT(array) { + return array.map(c => !c); +} \ No newline at end of file