diff --git a/Scripts/resource-parser.js b/Scripts/resource-parser.js index 73abcb0..5fee7b5 100644 --- a/Scripts/resource-parser.js +++ b/Scripts/resource-parser.js @@ -472,2536 +472,4 @@ function Type_Check(subs) { // 检查节点名字(重复以及空名)等QuanX 不允许的情形,以及多个空格等“不规范”方式 function TagCheck_QX(content) { - 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 + typefix = {"shadowsocks":["𝐬𝐬","𝐒𝐒","🅢🅢","🆂🆂","ⓢⓢ","🅂🅂","SS"],"shadowsocksr":["𝐬𝐬𝐫","𝐒𝐒𝐑","🅢🅢🅡","🆂🆂🆁","ⓢⓢⓡ","🅂 \ No newline at end of file