From 715a0b05bac14d8e417eae0c116c256987c85856 Mon Sep 17 00:00:00 2001 From: CMLiussss <24787744+cmliu@users.noreply.github.com> Date: Sat, 11 May 2024 17:59:57 +0800 Subject: [PATCH] Update _worker.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完全重写, 优化逻辑 --- _worker.js | 355 +++++++++++++++++++++++++++++------------------------ 1 file changed, 195 insertions(+), 160 deletions(-) diff --git a/_worker.js b/_worker.js index a843cbd..7edf642 100644 --- a/_worker.js +++ b/_worker.js @@ -15,9 +15,7 @@ https://sub.xf.free.hr/auto https://hy2sub.pages.dev ` -//请将机场订阅链接填入上方 -let urls = [];// https://subs.zeabur.app/clash , https://neko-warp.nloli.xyz/neko_warp.yaml - +let urls = []; let subconverter = "apiurl.v1.mk"; //在线订阅转换后端,目前使用肥羊的订阅转换功能。支持自建psub 可自行搭建https://github.com/bulianglin/psub let subconfig = "https://raw.githubusercontent.com/cmliu/ACL4SSR/main/Clash/config/ACL4SSR_Online_MultiCountry.ini"; //订阅配置文件 @@ -37,169 +35,203 @@ export default { MainData = env.LINK || MainData; if(env.LINKSUB) urls = await ADD(env.LINKSUB); - let links = await ADD(MainData + '\n' + urls.join('\n')); - let link = ""; - let linksub = ""; - - for (let x of links) { + const currentDate = new Date(); + currentDate.setHours(0, 0, 0, 0); + const timestamp = Math.ceil(currentDate.getTime() / 1000); + const fakeToken = await MD5MD5(`${mytoken}${timestamp}`); + //console.log(`${fakeUserID}\n${fakeHostName}`); // 打印fakeID + + let 重新汇总所有链接 = await ADD(MainData + '\n' + urls.join('\n')); + let 自建节点 =""; + let 订阅链接 =""; + for (let x of 重新汇总所有链接) { if (x.toLowerCase().startsWith('http')) { - linksub += x + '\n'; + 订阅链接 += x + '\n'; } else { - link += x + '\n'; + 自建节点 += x + '\n'; } } - MainData = link; - urls = await ADD(linksub); - let sublinks = request.url ; - //console.log(MainData,urls,sublinks); - - let warp = env.WARP ; - if(warp && warp != "") sublinks += '|' + (await ADD(warp)).join('|'); + MainData = 自建节点; + urls = await ADD(订阅链接); - if ( !(token == mytoken || url.pathname == ("/"+ mytoken) || url.pathname.includes("/"+ mytoken + "?")) ) { - if ( TG == 1 && url.pathname !== "/" && url.pathname !== "/favicon.ico" ) await sendMessage("#异常访问", request.headers.get('CF-Connecting-IP'), `UA: ${userAgent}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); - //首页改成一个nginx伪装页 - return new Response(` - - - - Welcome to nginx! - - - -

Welcome to nginx!

-

If you see this page, the nginx web server is successfully installed and - working. Further configuration is required.

- -

For online documentation and support please refer to - nginx.org.
- Commercial support is available at - nginx.com.

- -

Thank you for using nginx.

- - - `, { + if ( !(token == mytoken || token == fakeToken || url.pathname == ("/"+ mytoken) || url.pathname.includes("/"+ mytoken + "?")) ) { + if ( TG == 1 && url.pathname !== "/" && url.pathname !== "/favicon.ico" ) await sendMessage(`#异常访问 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${userAgent}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); + const envKey = env.URL302 ? 'URL302' : (env.URL ? 'URL' : null); + if (envKey) { + const URLs = await ADD(env[envKey]); + const URL = URLs[Math.floor(Math.random() * URLs.length)]; + return envKey === 'URL302' ? Response.redirect(URL, 302) : fetch(new Request(URL, request)); + } + return new Response(await nginx(), { + status: 200 , headers: { 'Content-Type': 'text/html; charset=UTF-8', }, }); - } else if ( TG == 1 || !userAgent.includes('subconverter') || !userAgent.includes('null')){ - await sendMessage("#获取订阅", request.headers.get('CF-Connecting-IP'), `UA: ${userAgentHeader}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); - } - - let req_data = MainData; - // 创建一个AbortController对象,用于控制fetch请求的取消 - const controller = new AbortController(); - - const timeout = setTimeout(() => { - controller.abort(); // 取消所有请求 - }, 1618); // 1.618秒后触发 - - try { - const responses = await Promise.allSettled(urls.map(url => - fetch(url, { - method: 'get', - headers: { - 'Accept': 'text/html,application/xhtml+xml,application/xml;', - 'User-Agent': `v2rayn/6.42 cmliu/CF-Workers-SUB ${userAgentHeader}` - }, - signal: controller.signal // 将AbortController的信号量添加到fetch请求中,以便于需要时可以取消请求 - }).then(response => { - if (response.ok) { - return response.text().then(content => { - // 这里可以顺便做内容检查 - if (content.includes('dns') && content.includes('proxies') && content.includes('proxy-groups') && content.includes('rules')) { - //console.log("clashsub: " + url); - sublinks += "|" + url; - } else if (content.includes('dns') && content.includes('outbounds') && content.includes('inbounds')){ - //console.log("singboxsub: " + url); - sublinks += "|" + url; - } else { - return content; // 保证链式调用中的下一个then可以接收到文本内容 - } - }); - } else { - return ""; // 如果response.ok为false,返回空字符串 - } - }) - )); - //console.log(responses); - for (const response of responses) { - if (response.status === 'fulfilled') { - const content = await response.value; - req_data += base64Decode(content) + '\n'; - } - } - } catch (error) { - //console.error(error); - } finally { - // 无论成功或失败,最后都清除设置的超时定时器 - clearTimeout(timeout); - } - //修复中文错误 - const utf8Encoder = new TextEncoder(); - const encodedData = utf8Encoder.encode(req_data); - const text = String.fromCharCode.apply(null, encodedData); - - //去重 - const uniqueLines = new Set(text.split('\n')); - const result = [...uniqueLines].join('\n'); - //console.log(result); - - const base64Data = btoa(result); - //console.log(base64Data); - - //console.log("自建节点: " + MainData,"订阅链接: " + urls,"转换链接: " + sublinks); - - let target = "v2ray"; - if (userAgent.includes('clash') && !userAgent.includes('nekobox')) { - target = "clash"; - } else if (userAgent.includes('sing-box') || userAgent.includes('singbox')) { - target = "singbox"; } else { - return new Response(base64Data ,{ - headers: { - "content-type": "text/plain; charset=utf-8", - "Profile-Update-Interval": `${SUBUpdateTime}`, - } - }); - } - - const subconverterUrl = `https://${subconverter}/sub?target=${target}&url=${encodeURIComponent(sublinks)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; - - try { - const subconverterResponse = await fetch(subconverterUrl); - - if (!subconverterResponse.ok) { - throw new Error(`Error fetching subconverterUrl: ${subconverterResponse.status} ${subconverterResponse.statusText}`); + await sendMessage(`#获取订阅 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${userAgentHeader}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); + let 订阅格式 = 'base64'; + if (userAgent.includes('null') || userAgent.includes('subconverter') || userAgent.includes('nekobox') || userAgent.includes(('CF-Workers-SUB').toLowerCase())){ + 订阅格式 = 'base64'; + } else if (userAgent.includes('clash') || ( url.searchParams.has('clash') && !userAgent.includes('subconverter'))){ + 订阅格式 = 'clash'; + } else if (userAgent.includes('sing-box') || userAgent.includes('singbox') || ( (url.searchParams.has('sb') || url.searchParams.has('singbox')) && !userAgent.includes('subconverter'))){ + 订阅格式 = 'singbox'; } - - const subconverterContent = await subconverterResponse.text(); - - return new Response(subconverterContent ,{ - headers: { - "Content-Disposition": `attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName}`, - "content-type": "text/plain; charset=utf-8", - "Profile-Update-Interval": `${SUBUpdateTime}`, - } - }); - } catch (error) { - return new Response(`Error: ${error.message}`, { - status: 500, - headers: { 'content-type': 'text/plain; charset=utf-8' }, - }); - } + let subconverterUrl ; + let 订阅转换URL = `${url.origin}/${await MD5MD5(fakeToken)}?token=${fakeToken}`; + //console.log(订阅转换URL); + let req_data = MainData; + // 创建一个AbortController对象,用于控制fetch请求的取消 + const controller = new AbortController(); + + const timeout = setTimeout(() => { + controller.abort(); // 取消所有请求 + }, 1618); // 1.618秒后触发 + + try { + const responses = await Promise.allSettled(urls.map(url => + fetch(url, { + method: 'get', + headers: { + 'Accept': 'text/html,application/xhtml+xml,application/xml;', + 'User-Agent': `v2rayn cmliu/CF-Workers-SUB ${userAgentHeader}` + }, + signal: controller.signal // 将AbortController的信号量添加到fetch请求中,以便于需要时可以取消请求 + }).then(response => { + if (response.ok) { + return response.text().then(content => { + // 这里可以顺便做内容检查 + if (content.includes('dns') && content.includes('proxies') && content.includes('proxy-groups') && content.includes('rules')) { + //console.log("clashsub: " + url); + 订阅转换URL += "|" + url; + } else if (content.includes('dns') && content.includes('outbounds') && content.includes('inbounds')){ + //console.log("singboxsub: " + url); + 订阅转换URL += "|" + url; + } else { + return content; // 保证链式调用中的下一个then可以接收到文本内容 + } + }); + } else { + return ""; // 如果response.ok为false,返回空字符串 + } + }) + )); + //console.log(responses); + for (const response of responses) { + if (response.status === 'fulfilled') { + const content = await response.value; + req_data += base64Decode(content) + '\n'; + } + } + + } catch (error) { + //console.error(error); + } finally { + // 无论成功或失败,最后都清除设置的超时定时器 + clearTimeout(timeout); + } + + //修复中文错误 + const utf8Encoder = new TextEncoder(); + const encodedData = utf8Encoder.encode(req_data); + const text = String.fromCharCode.apply(null, encodedData); + + //去重 + const uniqueLines = new Set(text.split('\n')); + const result = [...uniqueLines].join('\n'); + console.log(result); + const base64Data = btoa(result); + + if (订阅格式 == 'base64'){ + return new Response(base64Data ,{ + headers: { + "content-type": "text/plain; charset=utf-8", + "Profile-Update-Interval": `${SUBUpdateTime}`, + } + }); + } else if (订阅格式 == 'clash'){ + subconverterUrl = `https://${subconverter}/sub?target=clash&url=${encodeURIComponent(订阅转换URL)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; + } else if (订阅格式 == 'singbox'){ + subconverterUrl = `https://${subconverter}/sub?target=singbox&url=${encodeURIComponent(订阅转换URL)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; + } + + try { + const subconverterResponse = await fetch(subconverterUrl); + + if (!subconverterResponse.ok) { + return new Response(base64Data ,{ + headers: { + "content-type": "text/plain; charset=utf-8", + "Profile-Update-Interval": `${SUBUpdateTime}`, + } + }); + //throw new Error(`Error fetching subconverterUrl: ${subconverterResponse.status} ${subconverterResponse.statusText}`); + } + let subconverterContent = await subconverterResponse.text(); + + return new Response(subconverterContent, { + headers: { + "Content-Disposition": `attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName}`, + "content-type": "text/plain; charset=utf-8", + "Profile-Update-Interval": `${SUBUpdateTime}`, + + }, + }); + } catch (error) { + return new Response(base64Data ,{ + headers: { + "content-type": "text/plain; charset=utf-8", + "Profile-Update-Interval": `${SUBUpdateTime}`, + } + }); + } + } } }; +async function ADD(envadd) { + var addtext = envadd.replace(/[ "'|\r\n]+/g, ',').replace(/,+/g, ','); // 将空格、双引号、单引号和换行符替换为逗号 + //console.log(addtext); + if (addtext.charAt(0) == ',') addtext = addtext.slice(1); + if (addtext.charAt(addtext.length -1) == ',') addtext = addtext.slice(0, addtext.length - 1); + const add = addtext.split(','); + //console.log(add); + return add ; +} + +async function nginx() { + const text = ` + + + + Welcome to nginx! + + + +

Welcome to nginx!

+

If you see this page, the nginx web server is successfully installed and + working. Further configuration is required.

+ +

For online documentation and support please refer to + nginx.org.
+ Commercial support is available at + nginx.com.

+ +

Thank you for using nginx.

+ + + ` + return text ; +} + async function sendMessage(type, ip, add_data = "") { if ( BotToken !== '' && ChatID !== ''){ let msg = ""; @@ -223,19 +255,22 @@ async function sendMessage(type, ip, add_data = "") { } } -// 将 base64 编码的字符串转换为 utf-8 编码的字符 function base64Decode(str) { const bytes = new Uint8Array(atob(str).split('').map(c => c.charCodeAt(0))); const decoder = new TextDecoder('utf-8'); return decoder.decode(bytes); } -async function ADD(envadd) { - var addtext = envadd.replace(/[ "'|\r\n]+/g, ',').replace(/,+/g, ','); // 将空格、双引号、单引号和换行符替换为逗号 - //console.log(addtext); - if (addtext.charAt(0) == ',') addtext = addtext.slice(1); - if (addtext.charAt(addtext.length -1) == ',') addtext = addtext.slice(0, addtext.length - 1); - const add = addtext.split(','); - //console.log(add); - return add ; +async function MD5MD5(text) { + const encoder = new TextEncoder(); + + const firstPass = await crypto.subtle.digest('MD5', encoder.encode(text)); + const firstPassArray = Array.from(new Uint8Array(firstPass)); + const firstHex = firstPassArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + const secondPass = await crypto.subtle.digest('MD5', encoder.encode(firstHex.slice(7, 27))); + const secondPassArray = Array.from(new Uint8Array(secondPass)); + const secondHex = secondPassArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + return secondHex.toLowerCase(); }