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();
}