diff --git a/Scripts/resource-parser.js b/Scripts/resource-parser.js new file mode 100644 index 0000000..5085f80 --- /dev/null +++ b/Scripts/resource-parser.js @@ -0,0 +1,372 @@ +/** + +#Quantumult X 节点资源解析器 + +本资源解析器作者: Shawn (@XIAO_KOP) , 有问题请反馈:@Shawn_KOP_bot + +#tag 2020-04-26: 09:00 + +功能:将不同格式订阅转换成 Quantumult X,并支持简单的节点过滤/emoji添加删除,udp/tfo 的开启. +- 目前支持 V2RayN/SSR/Trojan/Quanx 格式写法的节点引用; + +1⃣️ 过滤参数为 in,out, 分别为保留与排除,多个参数间用+号连接, 可直接使用中文(如 in=香港+台湾) +2⃣️ emoji 参数为 emoji=1,2 或-1,为添加或删除节点名中的emoji旗帜(国行设备请用 emoji=2) +3⃣️ udp=1,tfo=1 参数开启 udp-relay 及fast-open +4⃣️ info=0,用于关闭转换解析器的提示通知 + + */ + +/** + * 使用, +0⃣️ 在quantumult X 配置文件中[general] 部分,加入 resource_parser_url=https://raw.githubusercontent.com/KOP-XIAO/QuantumultX/master/Scripts/resource-parser.js +1⃣️ 原始订阅连接为: https://raw.githubusercontent.com/crossutility/Quantumult-X/master/server-complete.txt , +2⃣️ 想要保留的参数为 in=tls+ss, 想要过滤的参数为 out=http+2, 请注意下面订阅链接后一定要加 ”#“ 符号 +3⃣️ 则填入 quanx 的总链接为 https://raw.githubusercontent.com/crossutility/Quantumult-X/master/server-complete.txt#in=tls+ss&out=http+2 +4⃣️ 填入上述链接并打开的资源解析器开关 + */ + +var content0=$resource.content; +var para=decodeURIComponent($resource.link); +var type0=Type_Check(content0); +var Pin0=para.indexOf("in=")!=-1? para.split("#")[1].split("in=")[1].split("&")[0].split("+"):null; +var Pout0=para.indexOf("out=")!=-1? para.split("#")[1].split("out=")[1].split("&")[0].split("+"):null; +var Pemoji=para.indexOf("emoji=")!=-1? para.split("#")[1].split("emoji=")[1].split("&")[0].split("+"):null; +var Pudp0=para.indexOf("udp=")!=-1? para.split("#")[1].split("udp=")[1].split("&")[0].split("+"):0; +var Ptfo0=para.indexOf("tfo=")!=-1? para.split("#")[1].split("tfo=")[1].split("&")[0].split("+"):0; +var Pinfo=para.indexOf("info=")!=-1? para.split("#")[1].split("info=")[1].split("&")[0].split("+"):0; + +if(type0=="Vmess"){ + total=V2QX(content0,Pudp0,Ptfo0); + flag=1; +}else if(type0=="QuanX"){ + total=content0.split("\n"); + flag=1; +}else if(type0=="SSR"){ + total=SSR2QX(content0,Pudp0,Ptfo0); + flag=1; +}else if(type0=="Trojan"){ + total=TJ2QX(content0,Pudp0,Ptfo0); + flag=1; +}else{ + $notify("👻该解析器暂未支持您的订阅格式","😭太难写了", "stay tuned"); + flag=0; + $done({content : content0}); +} + +if(flag==1){ + if(Pin0||Pout0){ + if(Pinfo!=0){ + $notify("👥 开始转换节点,类型:"+type0,"🐶 您已添加节点筛选参数,如下","👍️ 保留的关键字:"+Pin0+"\n👎️ 排除的关键字:"+Pout0);} + total=filter(total,Pin0,Pout0) + } else { + if(Pinfo!=0){ + $notify("🐷 开始转换节点,类型:"+type0,"🐼️ 如需筛选节点请使用in/out及其他参数,可参考此示范:","👉 https://t.me/QuanXNews/110");} + } + if(Pemoji){ + if(Pinfo!=0){ + $notify("🏳️‍🌈 开始更改旗帜 emoji","清除emoji请用参数 -1, 国行设备添加emoji请使用参数 2","你当前所用的参数为 emoji="+Pemoji)}; + total=emoji_handle(total,Pemoji); + } + $done({content : total.join("\n")}); +} + + +//判断订阅类型 +function Type_Check(subs){ + var type="" + if (subs.indexOf("dm1lc3M6Ly")!= -1){ + type="Vmess" + } else if (subs.indexOf("tag")!=-1){ + type="QuanX" + } else if (subs.indexOf("c3NyOi8v")!= -1){ + type="SSR" + } else if (subs.indexOf("dHJvamFu")!= -1){ + type="Trojan" + } + return type +} + +//V2RayN 订阅转换成 QUANX 格式 +function V2QX(subs,Pudp,Ptfo){ + const $base64 = new Base64() + var list0=$base64.decode(subs).split("\n"); + var QXList=[] + for(var i=0;i name.indexOf(item) != -1; + const exclude = (item) => name.indexOf(item) != -1; + if(Pin){ + if(Pin.some(include)&&Pout){ + if(!Pout.some(exclude)){ + NList.push(Servers[i]) + } + } else if(Pin.some(include)&&!Pout) {NList.push(Servers[i])} + } else{ + if(!Pout.some(exclude)){ + NList.push(Servers[i]) + } + } + } + } + return NList +} + +// Vmess obfs 参数 +function Pobfs(jsonl){ + var obfsi=[] + if(jsonl.net=="ws" && jsonl.tls=="tls"){ + obfs0="obfs=wss, "; + uri0=jsonl.path!=""? "obfs-uri="+jsonl.path:"obfs-uri=/"; + host0= jsonl.host!=""? "obfs-host="+jsonl.host+",":""; + obfsi.push(obfs0+host0+uri0) + return obfsi.join(", ") + }else if(jsonl.net=="ws"){ + obfs0="obfs=ws"; + uri0=jsonl.path!=""? "obfs-uri="+jsonl.path:"obfs-uri=/"; + obfsi.push(obfs0,uri0) + return obfsi.join(", ") + }else if(jsonl.tls=="tls"){ + obfs0="obfs=over-tls"; + uri0=jsonl.path!=""? "obfs-uri="+jsonl.path:""; + host0=jsonl.host!=""? "obfs-host="+jsonl.host:""; + obfsi.push(obfs0+host0) + return obfsi.join(", ") + } +} + +//SSR 转换 quanx 格式 +function SSR2QX(subs,Pudp,Ptfo){ + const $base64 = new Base64() + var list0=$base64.decode(subs).split("\n"); + var QXList=[]; + for(var i=0;i> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + output = output + + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); + } + return output; + } + // public method for decoding + this.decode = function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + while (i < input.length) { + enc1 = _keyStr.indexOf(input.charAt(i++)); + enc2 = _keyStr.indexOf(input.charAt(i++)); + enc3 = _keyStr.indexOf(input.charAt(i++)); + enc4 = _keyStr.indexOf(input.charAt(i++)); + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + output = output + String.fromCharCode(chr1); + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + } + output = _utf8_decode(output); + return output; + } + // private method for UTF-8 encoding + _utf8_encode = function (string) { + string = string.replace(/\r\n/g, "\n"); + var utftext = ""; + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + return utftext; + } + // private method for UTF-8 decoding + _utf8_decode = function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + while (i < utftext.length) { + c = utftext.charCodeAt(i); + if (c < 128) { + string += String.fromCharCode(c); + i++; + } else if ((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i + 1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = utftext.charCodeAt(i + 1); + c3 = utftext.charCodeAt(i + 2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + return string; + } +}