From c0444e13cfd6454638624a114de998899cf3b50e Mon Sep 17 00:00:00 2001 From: cmliu Date: Wed, 27 Nov 2024 23:56:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=8F=E8=AF=95=E7=89=9B=E5=88=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _worker.src.js | 1967 +----------------------------------------------- 1 file changed, 1 insertion(+), 1966 deletions(-) diff --git a/_worker.src.js b/_worker.src.js index 7e50c9c7a..ddfe33d74 100644 --- a/_worker.src.js +++ b/_worker.src.js @@ -1,1966 +1 @@ -// version base on commit 43fad05dcdae3b723c53c226f8181fc5bd47223e, time is 2024-10-21 14:06:14 UTC. -// @ts-ignore -import { connect } from 'cloudflare:sockets'; - -// How to generate your own UUID: -// [Windows] Press "Win + R", input cmd and run: Powershell -NoExit -Command "[guid]::NewGuid()" -let userID = '90cd4a77-141a-43c9-991b-08263cfe9c10'; - -let proxyIP = '';// 小白勿动,该地址并不影响你的网速,这是给CF代理使用的。'cdn.xn--b6gac.eu.org, cdn-all.xn--b6gac.eu.org' - -let sub = '';// 避免项目被滥用,现已取消内置订阅器 -let subconverter = 'SUBAPI.fxxk.dedyn.io';// clash订阅转换后端,目前使用CM的订阅转换功能。自带虚假uuid和host订阅。 -let subconfig = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Mini_MultiMode.ini"; //订阅配置文件 -let subProtocol = 'https'; -// The user name and password do not contain special characters -// Setting the address will ignore proxyIP -// Example: user:pass@host:port or host:port -let socks5Address = ''; - -let parsedSocks5Address = {}; -let enableSocks = false; - -// 虚假uuid和hostname,用于发送给配置生成服务 -let fakeUserID ; -let fakeHostName ; -let noTLS = 'false'; -const expire = 4102329600;//2099-12-31 -let proxyIPs; -let socks5s; -let go2Socks5s = [ - '*ttvnw.net', - '*tapecontent.net', - '*cloudatacdn.com', - '*.loadshare.org', -]; -let addresses = [ - //当sub为空时启用本地优选域名/优选IP,若不带端口号 TLS默认端口为443,#号后为备注别名 - /* - 'Join.my.Telegram.channel.CMLiussss.to.unlock.more.premium.nodes.cf.090227.xyz#加入我的频道t.me/CMLiussss解锁更多优选节点', - 'visa.cn:443', - 'www.visa.com:8443', - 'cis.visa.com:2053', - 'africa.visa.com:2083', - 'www.visa.com.sg:2087', - 'www.visaeurope.at:2096', - 'www.visa.com.mt:8443', - 'qa.visamiddleeast.com', - 'time.is', - 'www.wto.org:8443', - 'chatgpt.com:2087', - 'icook.hk', - '104.17.0.0#IPv4', - '[2606:4700::]#IPv6' - */ -]; -let addressesapi = []; -let addressesnotls = [ - //当sub为空且域名带有"worker"字样时启用本地优选域名/优选IP,若不带端口号 noTLS默认端口为80,#号后为备注别名 - /* - 'usa.visa.com', - 'myanmar.visa.com:8080', - 'www.visa.com.tw:8880', - 'www.visaeurope.ch:2052', - 'www.visa.com.br:2082', - 'www.visasoutheasteurope.com:2086', - '[2606:4700::1]:2095#IPv6' - */ -]; -let addressesnotlsapi = []; -let addressescsv = []; -let DLS = 8; -let FileName = 'edgetunnel'; -let BotToken =''; -let ChatID =''; -let proxyhosts = [];//本地代理域名池 -let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts';//在线代理域名池URL -let RproxyIP = 'false'; -let httpsPorts = ["2053","2083","2087","2096","8443"]; -let effectiveTime = 7;//有效时间 单位:天 -let updateTime = 3;//更新时间 -let userIDLow; -let userIDTime = ""; -/*Obfuscate-cmliu*/ -if (!isValidUUID(userID)) { - throw new Error('uuid is not valid'); -} -export default { - /** - * @param {import("@cloudflare/workers-types").Request} request - * @param {{UUID: string, PROXYIP: string}} env - * @param {import("@cloudflare/workers-types").ExecutionContext} ctx - * @returns {Promise} - */ - async fetch(request, env, ctx) { - try { - const UA = request.headers.get('User-Agent') || 'null'; - const userAgent = UA.toLowerCase(); - userID = (env.UUID || userID).toLowerCase(); - - const currentDate = new Date(); - currentDate.setHours(0, 0, 0, 0); - const timestamp = Math.ceil(currentDate.getTime() / 1000); - const fakeUserIDMD5 = await MD5MD5(`${userID}${timestamp}`); - fakeUserID = fakeUserIDMD5.slice(0, 8) + "-" + fakeUserIDMD5.slice(8, 12) + "-" + fakeUserIDMD5.slice(12, 16) + "-" + fakeUserIDMD5.slice(16, 20) + "-" + fakeUserIDMD5.slice(20); - fakeHostName = fakeUserIDMD5.slice(6, 9) + "." + fakeUserIDMD5.slice(13, 19); - //console.log(`虚假UUID: ${fakeUserID}`); // 打印fakeID - if (env.KEY) { - const userIDs = await generateDynamicUUID(env.KEY); - userID = userIDs[0]; - userIDLow = userIDs[1]; - userIDTime = userIDs[2]; - //console.log(`启用动态UUID\n秘钥KEY: ${env.KEY}\nUUIDNow: ${userID}\nUUIDLow: ${userIDLow}`); - effectiveTime = env.TIME || effectiveTime; - updateTime = env.UPTIME || updateTime; - } - proxyIP = env.PROXYIP || proxyIP; - proxyIPs = await ADD(proxyIP); - proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)]; - //console.log(proxyIP); - socks5Address = env.SOCKS5 || socks5Address; - socks5s = await ADD(socks5Address); - socks5Address = socks5s[Math.floor(Math.random() * socks5s.length)]; - socks5Address = socks5Address.split('//')[1] || socks5Address; - if (env.CFPORTS) httpsPorts = await ADD(env.CFPORTS); - sub = env.SUB || sub; - subconverter = env.SUBAPI || subconverter; - if( subconverter.includes("http://") ){ - subconverter = subconverter.split("//")[1]; - subProtocol = 'http'; - } else { - subconverter = subconverter.split("//")[1] || subconverter; - } - subconfig = env.SUBCONFIG || subconfig; - if (socks5Address) { - try { - parsedSocks5Address = socks5AddressParser(socks5Address); - RproxyIP = env.RPROXYIP || 'false'; - enableSocks = true; - } catch (err) { - /** @type {Error} */ - let e = err; - console.log(e.toString()); - RproxyIP = env.RPROXYIP || !proxyIP ? 'true' : 'false'; - enableSocks = false; - } - } else { - RproxyIP = env.RPROXYIP || !proxyIP ? 'true' : 'false'; - } - if (env.ADD) addresses = await ADD(env.ADD); - if (env.ADDAPI) addressesapi = await ADD(env.ADDAPI); - if (env.ADDNOTLS) addressesnotls = await ADD(env.ADDNOTLS); - if (env.ADDNOTLSAPI) addressesnotlsapi = await ADD(env.ADDNOTLSAPI); - if (env.ADDCSV) addressescsv = await ADD(env.ADDCSV); - DLS = env.DLS || DLS; - BotToken = env.TGTOKEN || BotToken; - ChatID = env.TGID || ChatID; - if(env.GO2SOCKS5) go2Socks5s = await ADD(env.GO2SOCKS5); - const upgradeHeader = request.headers.get('Upgrade'); - const url = new URL(request.url); - if (url.searchParams.has('sub') && url.searchParams.get('sub') !== '') sub = url.searchParams.get('sub'); - FileName = env.SUBNAME || FileName; - if (url.searchParams.has('notls')) noTLS = 'true'; - if (!upgradeHeader || upgradeHeader !== 'websocket') { - const 路径 = url.pathname.toLowerCase(); - if (路径 == '/') { - if (env.URL302) return Response.redirect(env.URL302, 302); - else if (env.URL) return await proxyURL(env.URL, url); - else return new Response(JSON.stringify(request.cf, null, 4), { - status: 200, - headers: { - 'content-type': 'application/json', - }, - }); - } else if (路径 == `/${fakeUserID}`) { - const fakeConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, 'CF-Workers-SUB', RproxyIP, url, env); - return new Response(`${fakeConfig}`, { status: 200 }); - } else if (路径 == `/${env.KEY}` || 路径 == `/${userID}`) { - await sendMessage(`#获取订阅 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${UA}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); - const vlessConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, UA, RproxyIP, url, env); - const now = Date.now(); - //const timestamp = Math.floor(now / 1000); - const today = new Date(now); - today.setHours(0, 0, 0, 0); - const UD = Math.floor(((now - today.getTime())/86400000) * 24 * 1099511627776 / 2); - let pagesSum = UD; - let workersSum = UD; - let total = 24 * 1099511627776 ; - if (env.CFEMAIL && env.CFKEY){ - const email = env.CFEMAIL; - const key = env.CFKEY; - const accountIndex = env.CFID || 0; - const accountId = await getAccountId(email, key); - if (accountId){ - const now = new Date() - now.setUTCHours(0, 0, 0, 0) - const startDate = now.toISOString() - const endDate = new Date().toISOString(); - const Sum = await getSum(accountId, accountIndex, email, key, startDate, endDate); - pagesSum = Sum[0]; - workersSum = Sum[1]; - total = 102400 ; - } - } - //console.log(`pagesSum: ${pagesSum}\nworkersSum: ${workersSum}\ntotal: ${total}`); - if (userAgent && userAgent.includes('mozilla')){ - return new Response(`${vlessConfig}`, { - status: 200, - headers: { - "Content-Type": "text/plain;charset=utf-8", - "Profile-Update-Interval": "6", - "Subscription-Userinfo": `upload=${pagesSum}; download=${workersSum}; total=${total}; expire=${expire}`, - } - }); - } else { - return new Response(`${vlessConfig}`, { - status: 200, - headers: { - "Content-Disposition": `attachment; filename=${FileName}; filename*=utf-8''${encodeURIComponent(FileName)}`, - "Content-Type": "text/plain;charset=utf-8", - "Profile-Update-Interval": "6", - "Subscription-Userinfo": `upload=${pagesSum}; download=${workersSum}; total=${total}; expire=${expire}`, - } - }); - } - } else { - if (env.URL302) return Response.redirect(env.URL302, 302); - else if (env.URL) return await proxyURL(env.URL, url); - else return new Response('不用怀疑!你UUID就是错的!!!', { status: 404 }); - } - } else { - proxyIP = url.searchParams.get('proxyip') || proxyIP; - if (new RegExp('/proxyip=', 'i').test(url.pathname)) proxyIP = url.pathname.toLowerCase().split('/proxyip=')[1]; - else if (new RegExp('/proxyip.', 'i').test(url.pathname)) proxyIP = `proxyip.${url.pathname.toLowerCase().split("/proxyip.")[1]}`; - - socks5Address = url.searchParams.get('socks5') || socks5Address; - if (new RegExp('/socks5=', 'i').test(url.pathname)) socks5Address = url.pathname.split('5=')[1]; - else if (new RegExp('/socks://', 'i').test(url.pathname) || new RegExp('/socks5://', 'i').test(url.pathname)) { - socks5Address = url.pathname.split('://')[1].split('#')[0]; - if (socks5Address.includes('@')){ - let userPassword = socks5Address.split('@')[0]; - const base64Regex = /^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i; - if (base64Regex.test(userPassword) && !userPassword.includes(':')) userPassword = atob(userPassword); - socks5Address = `${userPassword}@${socks5Address.split('@')[1]}`; - } - } - if (socks5Address) { - try { - parsedSocks5Address = socks5AddressParser(socks5Address); - enableSocks = true; - } catch (err) { - /** @type {Error} */ - let e = err; - console.log(e.toString()); - enableSocks = false; - } - } else { - enableSocks = false; - } - - return await vlessOverWSHandler(request); - } - } catch (err) { - /** @type {Error} */ let e = err; - return new Response(e.toString()); - } - }, -}; - -/** - * 处理 VLESS over WebSocket 的请求 - * @param {import("@cloudflare/workers-types").Request} request - */ -async function vlessOverWSHandler(request) { - - /** @type {import("@cloudflare/workers-types").WebSocket[]} */ - // @ts-ignore - const webSocketPair = new WebSocketPair(); - const [client, webSocket] = Object.values(webSocketPair); - - // 接受 WebSocket 连接 - webSocket.accept(); - - let address = ''; - let portWithRandomLog = ''; - // 日志函数,用于记录连接信息 - const log = (/** @type {string} */ info, /** @type {string | undefined} */ event) => { - console.log(`[${address}:${portWithRandomLog}] ${info}`, event || ''); - }; - // 获取早期数据头部,可能包含了一些初始化数据 - const earlyDataHeader = request.headers.get('sec-websocket-protocol') || ''; - - // 创建一个可读的 WebSocket 流,用于接收客户端数据 - const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log); - - /** @type {{ value: import("@cloudflare/workers-types").Socket | null}}*/ - // 用于存储远程 Socket 的包装器 - let remoteSocketWapper = { - value: null, - }; - // 标记是否为 DNS 查询 - let isDns = false; - - // WebSocket 数据流向远程服务器的管道 - readableWebSocketStream.pipeTo(new WritableStream({ - async write(chunk, controller) { - if (isDns) { - // 如果是 DNS 查询,调用 DNS 处理函数 - return await handleDNSQuery(chunk, webSocket, null, log); - } - if (remoteSocketWapper.value) { - // 如果已有远程 Socket,直接写入数据 - const writer = remoteSocketWapper.value.writable.getWriter() - await writer.write(chunk); - writer.releaseLock(); - return; - } - - // 处理 VLESS 协议头部 - const { - hasError, - message, - addressType, - portRemote = 443, - addressRemote = '', - rawDataIndex, - vlessVersion = new Uint8Array([0, 0]), - isUDP, - } = processVlessHeader(chunk, userID); - // 设置地址和端口信息,用于日志 - address = addressRemote; - portWithRandomLog = `${portRemote}--${Math.random()} ${isUDP ? 'udp ' : 'tcp '} `; - if (hasError) { - // 如果有错误,抛出异常 - throw new Error(message); - return; - } - // 如果是 UDP 且端口不是 DNS 端口(53),则关闭连接 - if (isUDP) { - if (portRemote === 53) { - isDns = true; - } else { - throw new Error('UDP 代理仅对 DNS(53 端口)启用'); - return; - } - } - // 构建 VLESS 响应头部 - const vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]); - // 获取实际的客户端数据 - const rawClientData = chunk.slice(rawDataIndex); - - if (isDns) { - // 如果是 DNS 查询,调用 DNS 处理函数 - return handleDNSQuery(rawClientData, webSocket, vlessResponseHeader, log); - } - // 处理 TCP 出站连接 - log(`处理 TCP 出站连接 ${addressRemote}:${portRemote}`); - handleTCPOutBound(remoteSocketWapper, addressType, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log); - }, - close() { - log(`readableWebSocketStream 已关闭`); - }, - abort(reason) { - log(`readableWebSocketStream 已中止`, JSON.stringify(reason)); - }, - })).catch((err) => { - log('readableWebSocketStream 管道错误', err); - }); - - // 返回一个 WebSocket 升级的响应 - return new Response(null, { - status: 101, - // @ts-ignore - webSocket: client, - }); -} - -/** - * 处理出站 TCP 连接。 - * - * @param {any} remoteSocket 远程 Socket 的包装器,用于存储实际的 Socket 对象 - * @param {number} addressType 要连接的远程地址类型(如 IP 类型:IPv4 或 IPv6) - * @param {string} addressRemote 要连接的远程地址 - * @param {number} portRemote 要连接的远程端口 - * @param {Uint8Array} rawClientData 要写入的原始客户端数据 - * @param {import("@cloudflare/workers-types").WebSocket} webSocket 用于传递远程 Socket 的 WebSocket - * @param {Uint8Array} vlessResponseHeader VLESS 响应头部 - * @param {function} log 日志记录函数 - * @returns {Promise} 异步操作的 Promise - */ -async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portRemote, rawClientData, webSocket, vlessResponseHeader, log,) { - async function useSocks5Pattern(address) { - if ( go2Socks5s.includes(atob('YWxsIGlu')) || go2Socks5s.includes(atob('Kg==')) ) return true; - return go2Socks5s.some(pattern => { - let regexPattern = pattern.replace(/\*/g, '.*'); - let regex = new RegExp(`^${regexPattern}$`, 'i'); - return regex.test(address); - }); - } - /** - * 连接远程服务器并写入数据 - * @param {string} address 要连接的地址 - * @param {number} port 要连接的端口 - * @param {boolean} socks 是否使用 SOCKS5 代理连接 - * @returns {Promise} 连接后的 TCP Socket - */ - async function connectAndWrite(address, port, socks = false) { - /** @type {import("@cloudflare/workers-types").Socket} */ - log(`connected to ${address}:${port}`); - //if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(address)) address = `${atob('d3d3Lg==')}${address}${atob('LmlwLjA5MDIyNy54eXo=')}`; - // 如果指定使用 SOCKS5 代理,则通过 SOCKS5 协议连接;否则直接连接 - const tcpSocket = socks ? await socks5Connect(addressType, address, port, log) - : connect({ - hostname: address, - port: port, - }); - remoteSocket.value = tcpSocket; - //log(`connected to ${address}:${port}`); - const writer = tcpSocket.writable.getWriter(); - // 首次写入,通常是 TLS 客户端 Hello 消息 - await writer.write(rawClientData); - writer.releaseLock(); - return tcpSocket; - } - - /** - * 重试函数:当 Cloudflare 的 TCP Socket 没有传入数据时,我们尝试重定向 IP - * 这可能是因为某些网络问题导致的连接失败 - */ - async function retry() { - if (enableSocks) { - // 如果启用了 SOCKS5,通过 SOCKS5 代理重试连接 - tcpSocket = await connectAndWrite(addressRemote, portRemote, true); - } else { - // 否则,尝试使用预设的代理 IP(如果有)或原始地址重试连接 - if (!proxyIP || proxyIP == '') { - proxyIP = atob('cHJveHlpcC50cDEuY21saXVzc3NzLmNvbQ=='); - } else if (proxyIP.includes(']:')) { - portRemote = proxyIP.split(']:')[1] || portRemote; - proxyIP = proxyIP.split(']:')[0] || proxyIP; - } else if (proxyIP.split(':').length === 2) { - portRemote = proxyIP.split(':')[1] || portRemote; - proxyIP = proxyIP.split(':')[0] || proxyIP; - } - if (proxyIP.includes('.tp')) portRemote = proxyIP.split('.tp')[1].split('.')[0] || portRemote; - tcpSocket = await connectAndWrite(proxyIP || addressRemote, portRemote); - } - // 无论重试是否成功,都要关闭 WebSocket(可能是为了重新建立连接) - tcpSocket.closed.catch(error => { - console.log('retry tcpSocket closed error', error); - }).finally(() => { - safeCloseWebSocket(webSocket); - }) - // 建立从远程 Socket 到 WebSocket 的数据流 - remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, null, log); - } - - let useSocks = false; - if( go2Socks5s.length > 0 && enableSocks ) useSocks = await useSocks5Pattern(addressRemote); - // 首次尝试连接远程服务器 - let tcpSocket = await connectAndWrite(addressRemote, portRemote, useSocks); - - // 当远程 Socket 就绪时,将其传递给 WebSocket - // 建立从远程服务器到 WebSocket 的数据流,用于将远程服务器的响应发送回客户端 - // 如果连接失败或无数据,retry 函数将被调用进行重试 - remoteSocketToWS(tcpSocket, webSocket, vlessResponseHeader, retry, log); -} - -/** - * 将 WebSocket 转换为可读流(ReadableStream) - * @param {import("@cloudflare/workers-types").WebSocket} webSocketServer 服务器端的 WebSocket 对象 - * @param {string} earlyDataHeader WebSocket 0-RTT(零往返时间)的早期数据头部 - * @param {(info: string)=> void} log 日志记录函数,用于记录 WebSocket 0-RTT 相关信息 - * @returns {ReadableStream} 由 WebSocket 消息组成的可读流 - */ -function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) { - // 标记可读流是否已被取消 - let readableStreamCancel = false; - - // 创建一个新的可读流 - const stream = new ReadableStream({ - // 当流开始时的初始化函数 - start(controller) { - // 监听 WebSocket 的消息事件 - webSocketServer.addEventListener('message', (event) => { - // 如果流已被取消,不再处理新消息 - if (readableStreamCancel) { - return; - } - const message = event.data; - // 将消息加入流的队列中 - controller.enqueue(message); - }); - - // 监听 WebSocket 的关闭事件 - // 注意:这个事件意味着客户端关闭了客户端 -> 服务器的流 - // 但是,服务器 -> 客户端的流仍然打开,直到在服务器端调用 close() - // WebSocket 协议要求在每个方向上都要发送单独的关闭消息,以完全关闭 Socket - webSocketServer.addEventListener('close', () => { - // 客户端发送了关闭信号,需要关闭服务器端 - safeCloseWebSocket(webSocketServer); - // 如果流未被取消,则关闭控制器 - if (readableStreamCancel) { - return; - } - controller.close(); - }); - - // 监听 WebSocket 的错误事件 - webSocketServer.addEventListener('error', (err) => { - log('WebSocket 服务器发生错误'); - // 将错误传递给控制器 - controller.error(err); - }); - - // 处理 WebSocket 0-RTT(零往返时间)的早期数据 - // 0-RTT 允许在完全建立连接之前发送数据,提高了效率 - const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader); - if (error) { - // 如果解码早期数据时出错,将错误传递给控制器 - controller.error(error); - } else if (earlyData) { - // 如果有早期数据,将其加入流的队列中 - controller.enqueue(earlyData); - } - }, - - // 当使用者从流中拉取数据时调用 - pull(controller) { - // 这里可以实现反压机制 - // 如果 WebSocket 可以在流满时停止读取,我们就可以实现反压 - // 参考:https://streams.spec.whatwg.org/#example-rs-push-backpressure - }, - - // 当流被取消时调用 - cancel(reason) { - // 流被取消的几种情况: - // 1. 当管道的 WritableStream 有错误时,这个取消函数会被调用,所以在这里处理 WebSocket 服务器的关闭 - // 2. 如果 ReadableStream 被取消,所有 controller.close/enqueue 都需要跳过 - // 3. 但是经过测试,即使 ReadableStream 被取消,controller.error 仍然有效 - if (readableStreamCancel) { - return; - } - log(`可读流被取消,原因是 ${reason}`); - readableStreamCancel = true; - // 安全地关闭 WebSocket - safeCloseWebSocket(webSocketServer); - } - }); - - return stream; -} - -// https://xtls.github.io/development/protocols/vless.html -// https://github.com/zizifn/excalidraw-backup/blob/main/v2ray-protocol.excalidraw - -/** - * 解析 VLESS 协议的头部数据 - * @param { ArrayBuffer} vlessBuffer VLESS 协议的原始头部数据 - * @param {string} userID 用于验证的用户 ID - * @returns {Object} 解析结果,包括是否有错误、错误信息、远程地址信息等 - */ -function processVlessHeader(vlessBuffer, userID) { - // 检查数据长度是否足够(至少需要 24 字节) - if (vlessBuffer.byteLength < 24) { - return { - hasError: true, - message: 'invalid data', - }; - } - - // 解析 VLESS 协议版本(第一个字节) - const version = new Uint8Array(vlessBuffer.slice(0, 1)); - - let isValidUser = false; - let isUDP = false; - - // 验证用户 ID(接下来的 16 个字节) - function isUserIDValid(userID, userIDLow, buffer) { - const userIDArray = new Uint8Array(buffer.slice(1, 17)); - const userIDString = stringify(userIDArray); - return userIDString === userID || userIDString === userIDLow; - } - - // 使用函数验证 - isValidUser = isUserIDValid(userID, userIDLow, vlessBuffer); - - // 如果用户 ID 无效,返回错误 - if (!isValidUser) { - return { - hasError: true, - message: `invalid user ${(new Uint8Array(vlessBuffer.slice(1, 17)))}`, - }; - } - - // 获取附加选项的长度(第 17 个字节) - const optLength = new Uint8Array(vlessBuffer.slice(17, 18))[0]; - // 暂时跳过附加选项 - - // 解析命令(紧跟在选项之后的 1 个字节) - // 0x01: TCP, 0x02: UDP, 0x03: MUX(多路复用) - const command = new Uint8Array( - vlessBuffer.slice(18 + optLength, 18 + optLength + 1) - )[0]; - - // 0x01 TCP - // 0x02 UDP - // 0x03 MUX - if (command === 1) { - // TCP 命令,不需特殊处理 - } else if (command === 2) { - // UDP 命令 - isUDP = true; - } else { - // 不支持的命令 - return { - hasError: true, - message: `command ${command} is not support, command 01-tcp,02-udp,03-mux`, - }; - } - - // 解析远程端口(大端序,2 字节) - const portIndex = 18 + optLength + 1; - const portBuffer = vlessBuffer.slice(portIndex, portIndex + 2); - // port is big-Endian in raw data etc 80 == 0x005d - const portRemote = new DataView(portBuffer).getUint16(0); - - // 解析地址类型和地址 - let addressIndex = portIndex + 2; - const addressBuffer = new Uint8Array( - vlessBuffer.slice(addressIndex, addressIndex + 1) - ); - - // 地址类型:1-IPv4(4字节), 2-域名(可变长), 3-IPv6(16字节) - const addressType = addressBuffer[0]; - let addressLength = 0; - let addressValueIndex = addressIndex + 1; - let addressValue = ''; - - switch (addressType) { - case 1: - // IPv4 地址 - addressLength = 4; - // 将 4 个字节转为点分十进制格式 - addressValue = new Uint8Array( - vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) - ).join('.'); - break; - case 2: - // 域名 - // 第一个字节是域名长度 - addressLength = new Uint8Array( - vlessBuffer.slice(addressValueIndex, addressValueIndex + 1) - )[0]; - addressValueIndex += 1; - // 解码域名 - addressValue = new TextDecoder().decode( - vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) - ); - break; - case 3: - // IPv6 地址 - addressLength = 16; - const dataView = new DataView( - vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength) - ); - // 每 2 字节构成 IPv6 地址的一部分 - const ipv6 = []; - for (let i = 0; i < 8; i++) { - ipv6.push(dataView.getUint16(i * 2).toString(16)); - } - addressValue = ipv6.join(':'); - // seems no need add [] for ipv6 - break; - default: - // 无效的地址类型 - return { - hasError: true, - message: `invild addressType is ${addressType}`, - }; - } - - // 确保地址不为空 - if (!addressValue) { - return { - hasError: true, - message: `addressValue is empty, addressType is ${addressType}`, - }; - } - - // 返回解析结果 - return { - hasError: false, - addressRemote: addressValue, // 解析后的远程地址 - addressType, // 地址类型 - portRemote, // 远程端口 - rawDataIndex: addressValueIndex + addressLength, // 原始数据的实际起始位置 - vlessVersion: version, // VLESS 协议版本 - isUDP, // 是否是 UDP 请求 - }; -} - - -/** - * 将远程 Socket 的数据转发到 WebSocket - * - * @param {import("@cloudflare/workers-types").Socket} remoteSocket 远程服务器的 Socket 连接 - * @param {import("@cloudflare/workers-types").WebSocket} webSocket 客户端的 WebSocket 连接 - * @param {ArrayBuffer} vlessResponseHeader VLESS 协议的响应头部 - * @param {(() => Promise) | null} retry 重试函数,当没有数据时调用 - * @param {*} log 日志函数 - */ -async function remoteSocketToWS(remoteSocket, webSocket, vlessResponseHeader, retry, log) { - // 将数据从远程服务器转发到 WebSocket - let remoteChunkCount = 0; - let chunks = []; - /** @type {ArrayBuffer | null} */ - let vlessHeader = vlessResponseHeader; - let hasIncomingData = false; // 检查远程 Socket 是否有传入数据 - - // 使用管道将远程 Socket 的可读流连接到一个可写流 - await remoteSocket.readable - .pipeTo( - new WritableStream({ - start() { - // 初始化时不需要任何操作 - }, - /** - * 处理每个数据块 - * @param {Uint8Array} chunk 数据块 - * @param {*} controller 控制器 - */ - async write(chunk, controller) { - hasIncomingData = true; // 标记已收到数据 - // remoteChunkCount++; // 用于流量控制,现在似乎不需要了 - - // 检查 WebSocket 是否处于开放状态 - if (webSocket.readyState !== WS_READY_STATE_OPEN) { - controller.error( - 'webSocket.readyState is not open, maybe close' - ); - } - - if (vlessHeader) { - // 如果有 VLESS 响应头部,将其与第一个数据块一起发送 - webSocket.send(await new Blob([vlessHeader, chunk]).arrayBuffer()); - vlessHeader = null; // 清空头部,之后不再发送 - } else { - // 直接发送数据块 - // 以前这里有流量控制代码,限制大量数据的发送速率 - // 但现在 Cloudflare 似乎已经修复了这个问题 - // if (remoteChunkCount > 20000) { - // // cf one package is 4096 byte(4kb), 4096 * 20000 = 80M - // await delay(1); - // } - webSocket.send(chunk); - } - }, - close() { - // 当远程连接的可读流关闭时 - log(`remoteConnection!.readable is close with hasIncomingData is ${hasIncomingData}`); - // 不需要主动关闭 WebSocket,因为这可能导致 HTTP ERR_CONTENT_LENGTH_MISMATCH 问题 - // 客户端无论如何都会发送关闭事件 - // safeCloseWebSocket(webSocket); - }, - abort(reason) { - // 当远程连接的可读流中断时 - console.error(`remoteConnection!.readable abort`, reason); - }, - }) - ) - .catch((error) => { - // 捕获并记录任何异常 - console.error( - `remoteSocketToWS has exception `, - error.stack || error - ); - // 发生错误时安全地关闭 WebSocket - safeCloseWebSocket(webSocket); - }); - - // 处理 Cloudflare 连接 Socket 的特殊错误情况 - // 1. Socket.closed 将有错误 - // 2. Socket.readable 将关闭,但没有任何数据 - if (hasIncomingData === false && retry) { - log(`retry`); - retry(); // 调用重试函数,尝试重新建立连接 - } -} - -/** - * 将 Base64 编码的字符串转换为 ArrayBuffer - * - * @param {string} base64Str Base64 编码的输入字符串 - * @returns {{ earlyData: ArrayBuffer | undefined, error: Error | null }} 返回解码后的 ArrayBuffer 或错误 - */ -function base64ToArrayBuffer(base64Str) { - // 如果输入为空,直接返回空结果 - if (!base64Str) { - return { error: null }; - } - try { - // Go 语言使用了 URL 安全的 Base64 变体(RFC 4648) - // 这种变体使用 '-' 和 '_' 来代替标准 Base64 中的 '+' 和 '/' - // JavaScript 的 atob 函数不直接支持这种变体,所以我们需要先转换 - base64Str = base64Str.replace(/-/g, '+').replace(/_/g, '/'); - - // 使用 atob 函数解码 Base64 字符串 - // atob 将 Base64 编码的 ASCII 字符串转换为原始的二进制字符串 - const decode = atob(base64Str); - - // 将二进制字符串转换为 Uint8Array - // 这是通过遍历字符串中的每个字符并获取其 Unicode 编码值(0-255)来完成的 - const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0)); - - // 返回 Uint8Array 的底层 ArrayBuffer - // 这是实际的二进制数据,可以用于网络传输或其他二进制操作 - return { earlyData: arryBuffer.buffer, error: null }; - } catch (error) { - // 如果在任何步骤中出现错误(如非法 Base64 字符),则返回错误 - return { error }; - } -} - -/** - * 这不是真正的 UUID 验证,而是一个简化的版本 - * @param {string} uuid 要验证的 UUID 字符串 - * @returns {boolean} 如果字符串匹配 UUID 格式则返回 true,否则返回 false - */ -function isValidUUID(uuid) { - // 定义一个正则表达式来匹配 UUID 格式 - const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; - - // 使用正则表达式测试 UUID 字符串 - return uuidRegex.test(uuid); -} - -// WebSocket 的两个重要状态常量 -const WS_READY_STATE_OPEN = 1; // WebSocket 处于开放状态,可以发送和接收消息 -const WS_READY_STATE_CLOSING = 2; // WebSocket 正在关闭过程中 - -/** - * 安全地关闭 WebSocket 连接 - * 通常,WebSocket 在关闭时不会抛出异常,但为了以防万一,我们还是用 try-catch 包裹 - * @param {import("@cloudflare/workers-types").WebSocket} socket 要关闭的 WebSocket 对象 - */ -function safeCloseWebSocket(socket) { - try { - // 只有在 WebSocket 处于开放或正在关闭状态时才调用 close() - // 这避免了在已关闭或连接中的 WebSocket 上调用 close() - if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) { - socket.close(); - } - } catch (error) { - // 记录任何可能发生的错误,虽然按照规范不应该有错误 - console.error('safeCloseWebSocket error', error); - } -} - -// 预计算 0-255 每个字节的十六进制表示 -const byteToHex = []; -for (let i = 0; i < 256; ++i) { - // (i + 256).toString(16) 确保总是得到两位数的十六进制 - // .slice(1) 删除前导的 "1",只保留两位十六进制数 - byteToHex.push((i + 256).toString(16).slice(1)); -} - -/** - * 快速地将字节数组转换为 UUID 字符串,不进行有效性检查 - * 这是一个底层函数,直接操作字节,不做任何验证 - * @param {Uint8Array} arr 包含 UUID 字节的数组 - * @param {number} offset 数组中 UUID 开始的位置,默认为 0 - * @returns {string} UUID 字符串 - */ -function unsafeStringify(arr, offset = 0) { - // 直接从查找表中获取每个字节的十六进制表示,并拼接成 UUID 格式 - // 8-4-4-4-12 的分组是通过精心放置的连字符 "-" 实现的 - // toLowerCase() 确保整个 UUID 是小写的 - return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + - byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + - byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + - byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + - byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + - byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); -} - -/** - * 将字节数组转换为 UUID 字符串,并验证其有效性 - * 这是一个安全的函数,它确保返回的 UUID 格式正确 - * @param {Uint8Array} arr 包含 UUID 字节的数组 - * @param {number} offset 数组中 UUID 开始的位置,默认为 0 - * @returns {string} 有效的 UUID 字符串 - * @throws {TypeError} 如果生成的 UUID 字符串无效 - */ -function stringify(arr, offset = 0) { - // 使用不安全的函数快速生成 UUID 字符串 - const uuid = unsafeStringify(arr, offset); - // 验证生成的 UUID 是否有效 - if (!isValidUUID(uuid)) { - // 原:throw TypeError("Stringified UUID is invalid"); - throw TypeError(`生成的 UUID 不符合规范 ${uuid}`); - //uuid = userID; - } - return uuid; -} - -/** - * 处理 DNS 查询的函数 - * @param {ArrayBuffer} udpChunk - 客户端发送的 DNS 查询数据 - * @param {import("@cloudflare/workers-types").WebSocket} webSocket - 与客户端建立的 WebSocket 连接 - * @param {ArrayBuffer} vlessResponseHeader - VLESS 协议的响应头部数据 - * @param {(string)=> void} log - 日志记录函数 - */ -async function handleDNSQuery(udpChunk, webSocket, vlessResponseHeader, log) { - // 无论客户端发送到哪个 DNS 服务器,我们总是使用硬编码的服务器 - // 因为有些 DNS 服务器不支持 DNS over TCP - try { - // 选用 Google 的 DNS 服务器(注:后续可能会改为 Cloudflare 的 1.1.1.1) - const dnsServer = '8.8.4.4'; // 在 Cloudflare 修复连接自身 IP 的 bug 后,将改为 1.1.1.1 - const dnsPort = 53; // DNS 服务的标准端口 - - /** @type {ArrayBuffer | null} */ - let vlessHeader = vlessResponseHeader; // 保存 VLESS 响应头部,用于后续发送 - - /** @type {import("@cloudflare/workers-types").Socket} */ - // 与指定的 DNS 服务器建立 TCP 连接 - const tcpSocket = connect({ - hostname: dnsServer, - port: dnsPort, - }); - - log(`连接到 ${dnsServer}:${dnsPort}`); // 记录连接信息 - const writer = tcpSocket.writable.getWriter(); - await writer.write(udpChunk); // 将客户端的 DNS 查询数据发送给 DNS 服务器 - writer.releaseLock(); // 释放写入器,允许其他部分使用 - - // 将从 DNS 服务器接收到的响应数据通过 WebSocket 发送回客户端 - await tcpSocket.readable.pipeTo(new WritableStream({ - async write(chunk) { - if (webSocket.readyState === WS_READY_STATE_OPEN) { - if (vlessHeader) { - // 如果有 VLESS 头部,则将其与 DNS 响应数据合并后发送 - webSocket.send(await new Blob([vlessHeader, chunk]).arrayBuffer()); - vlessHeader = null; // 头部只发送一次,之后置为 null - } else { - // 否则直接发送 DNS 响应数据 - webSocket.send(chunk); - } - } - }, - close() { - log(`DNS 服务器(${dnsServer}) TCP 连接已关闭`); // 记录连接关闭信息 - }, - abort(reason) { - console.error(`DNS 服务器(${dnsServer}) TCP 连接异常中断`, reason); // 记录异常中断原因 - }, - })); - } catch (error) { - // 捕获并记录任何可能发生的错误 - console.error( - `handleDNSQuery 函数发生异常,错误信息: ${error.message}` - ); - } -} - -/** - * 建立 SOCKS5 代理连接 - * @param {number} addressType 目标地址类型(1: IPv4, 2: 域名, 3: IPv6) - * @param {string} addressRemote 目标地址(可以是 IP 或域名) - * @param {number} portRemote 目标端口 - * @param {function} log 日志记录函数 - */ -async function socks5Connect(addressType, addressRemote, portRemote, log) { - const { username, password, hostname, port } = parsedSocks5Address; - // 连接到 SOCKS5 代理服务器 - const socket = connect({ - hostname, // SOCKS5 服务器的主机名 - port, // SOCKS5 服务器的端口 - }); - - // 请求头格式(Worker -> SOCKS5 服务器): - // +----+----------+----------+ - // |VER | NMETHODS | METHODS | - // +----+----------+----------+ - // | 1 | 1 | 1 to 255 | - // +----+----------+----------+ - - // https://en.wikipedia.org/wiki/SOCKS#SOCKS5 - // METHODS 字段的含义: - // 0x00 不需要认证 - // 0x02 用户名/密码认证 https://datatracker.ietf.org/doc/html/rfc1929 - const socksGreeting = new Uint8Array([5, 2, 0, 2]); - // 5: SOCKS5 版本号, 2: 支持的认证方法数, 0和2: 两种认证方法(无认证和用户名/密码) - - const writer = socket.writable.getWriter(); - - await writer.write(socksGreeting); - log('已发送 SOCKS5 问候消息'); - - const reader = socket.readable.getReader(); - const encoder = new TextEncoder(); - let res = (await reader.read()).value; - // 响应格式(SOCKS5 服务器 -> Worker): - // +----+--------+ - // |VER | METHOD | - // +----+--------+ - // | 1 | 1 | - // +----+--------+ - if (res[0] !== 0x05) { - log(`SOCKS5 服务器版本错误: 收到 ${res[0]},期望是 5`); - return; - } - if (res[1] === 0xff) { - log("服务器不接受任何认证方法"); - return; - } - - // 如果返回 0x0502,表示需要用户名/密码认证 - if (res[1] === 0x02) { - log("SOCKS5 服务器需要认证"); - if (!username || !password) { - log("请提供用户名和密码"); - return; - } - // 认证请求格式: - // +----+------+----------+------+----------+ - // |VER | ULEN | UNAME | PLEN | PASSWD | - // +----+------+----------+------+----------+ - // | 1 | 1 | 1 to 255 | 1 | 1 to 255 | - // +----+------+----------+------+----------+ - const authRequest = new Uint8Array([ - 1, // 认证子协议版本 - username.length, // 用户名长度 - ...encoder.encode(username), // 用户名 - password.length, // 密码长度 - ...encoder.encode(password) // 密码 - ]); - await writer.write(authRequest); - res = (await reader.read()).value; - // 期望返回 0x0100 表示认证成功 - if (res[0] !== 0x01 || res[1] !== 0x00) { - log("SOCKS5 服务器认证失败"); - return; - } - } - - // 请求数据格式(Worker -> SOCKS5 服务器): - // +----+-----+-------+------+----------+----------+ - // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - // ATYP: 地址类型 - // 0x01: IPv4 地址 - // 0x03: 域名 - // 0x04: IPv6 地址 - // DST.ADDR: 目标地址 - // DST.PORT: 目标端口(网络字节序) - - // addressType - // 1 --> IPv4 地址长度 = 4 - // 2 --> 域名 - // 3 --> IPv6 地址长度 = 16 - let DSTADDR; // DSTADDR = ATYP + DST.ADDR - switch (addressType) { - case 1: // IPv4 - DSTADDR = new Uint8Array( - [1, ...addressRemote.split('.').map(Number)] - ); - break; - case 2: // 域名 - DSTADDR = new Uint8Array( - [3, addressRemote.length, ...encoder.encode(addressRemote)] - ); - break; - case 3: // IPv6 - DSTADDR = new Uint8Array( - [4, ...addressRemote.split(':').flatMap(x => [parseInt(x.slice(0, 2), 16), parseInt(x.slice(2), 16)])] - ); - break; - default: - log(`无效的地址类型: ${addressType}`); - return; - } - const socksRequest = new Uint8Array([5, 1, 0, ...DSTADDR, portRemote >> 8, portRemote & 0xff]); - // 5: SOCKS5版本, 1: 表示CONNECT请求, 0: 保留字段 - // ...DSTADDR: 目标地址, portRemote >> 8 和 & 0xff: 将端口转为网络字节序 - await writer.write(socksRequest); - log('已发送 SOCKS5 请求'); - - res = (await reader.read()).value; - // 响应格式(SOCKS5 服务器 -> Worker): - // +----+-----+-------+------+----------+----------+ - // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - if (res[1] === 0x00) { - log("SOCKS5 连接已建立"); - } else { - log("SOCKS5 连接建立失败"); - return; - } - writer.releaseLock(); - reader.releaseLock(); - return socket; -} - - -/** - * SOCKS5 代理地址解析器 - * 此函数用于解析 SOCKS5 代理地址字符串,提取出用户名、密码、主机名和端口号 - * - * @param {string} address SOCKS5 代理地址,格式可以是: - * - "username:password@hostname:port" (带认证) - * - "hostname:port" (不需认证) - * - "username:password@[ipv6]:port" (IPv6 地址需要用方括号括起来) - */ -function socks5AddressParser(address) { - // 使用 "@" 分割地址,分为认证部分和服务器地址部分 - // reverse() 是为了处理没有认证信息的情况,确保 latter 总是包含服务器地址 - let [latter, former] = address.split("@").reverse(); - let username, password, hostname, port; - - // 如果存在 former 部分,说明提供了认证信息 - if (former) { - const formers = former.split(":"); - if (formers.length !== 2) { - throw new Error('无效的 SOCKS 地址格式:认证部分必须是 "username:password" 的形式'); - } - [username, password] = formers; - } - - // 解析服务器地址部分 - const latters = latter.split(":"); - // 从末尾提取端口号(因为 IPv6 地址中也包含冒号) - port = Number(latters.pop()); - if (isNaN(port)) { - throw new Error('无效的 SOCKS 地址格式:端口号必须是数字'); - } - - // 剩余部分就是主机名(可能是域名、IPv4 或 IPv6 地址) - hostname = latters.join(":"); - - // 处理 IPv6 地址的特殊情况 - // IPv6 地址包含多个冒号,所以必须用方括号括起来,如 [2001:db8::1] - const regex = /^\[.*\]$/; - if (hostname.includes(":") && !regex.test(hostname)) { - throw new Error('无效的 SOCKS 地址格式:IPv6 地址必须用方括号括起来,如 [2001:db8::1]'); - } - - //if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(hostname)) hostname = `${atob('d3d3Lg==')}${hostname}${atob('LmlwLjA5MDIyNy54eXo=')}`; - // 返回解析后的结果 - return { - username, // 用户名,如果没有则为 undefined - password, // 密码,如果没有则为 undefined - hostname, // 主机名,可以是域名、IPv4 或 IPv6 地址 - port, // 端口号,已转换为数字类型 - } -} - -/** - * 恢复被伪装的信息 - * 这个函数用于将内容中的假用户ID和假主机名替换回真实的值 - * - * @param {string} content 需要处理的内容 - * @param {string} userID 真实的用户ID - * @param {string} hostName 真实的主机名 - * @param {boolean} isBase64 内容是否是Base64编码的 - * @returns {string} 恢复真实信息后的内容 - */ -function revertFakeInfo(content, userID, hostName, isBase64) { - if (isBase64) content = atob(content); // 如果内容是Base64编码的,先解码 - - // 使用正则表达式全局替换('g'标志) - // 将所有出现的假用户ID和假主机名替换为真实的值 - content = content.replace(new RegExp(fakeUserID, 'g'), userID) - .replace(new RegExp(fakeHostName, 'g'), hostName); - - if (isBase64) content = btoa(content); // 如果原内容是Base64编码的,处理完后再次编码 - - return content; -} - -/** - * 双重MD5哈希函数 - * 这个函数对输入文本进行两次MD5哈希,增强安全性 - * 第二次哈希使用第一次哈希结果的一部分作为输入 - * - * @param {string} text 要哈希的文本 - * @returns {Promise} 双重哈希后的小写十六进制字符串 - */ -async function MD5MD5(text) { - const encoder = new TextEncoder(); - - // 第一次MD5哈希 - 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(''); - - // 第二次MD5哈希,使用第一次哈希结果的中间部分(索引7到26) - 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(); // 返回小写的十六进制字符串 -} - -/** - * 解析并清理环境变量中的地址列表 - * 这个函数用于处理包含多个地址的环境变量 - * 它会移除所有的空白字符、引号等,并将地址列表转换为数组 - * - * @param {string} envadd 包含地址列表的环境变量值 - * @returns {Promise} 清理和分割后的地址数组 - */ -async function ADD(envadd) { - // 将制表符、双引号、单引号和换行符都替换为逗号 - // 然后将连续的多个逗号替换为单个逗号 - var addtext = envadd.replace(/[ |"'\r\n]+/g, ',').replace(/,+/g, ','); - - // 删除开头和结尾的逗号(如果有的话) - 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(','); - - return add; -} - -async function proxyURL(proxyURL, url) { - const URLs = await ADD(proxyURL); - const fullURL = URLs[Math.floor(Math.random() * URLs.length)]; - - // 解析目标 URL - let parsedURL = new URL(fullURL); - console.log(parsedURL); - // 提取并可能修改 URL 组件 - let URLProtocol = parsedURL.protocol.slice(0, -1) || 'https'; - let URLHostname = parsedURL.hostname; - let URLPathname = parsedURL.pathname; - let URLSearch = parsedURL.search; - - // 处理 pathname - if (URLPathname.charAt(URLPathname.length - 1) == '/') { - URLPathname = URLPathname.slice(0, -1); - } - URLPathname += url.pathname; - - // 构建新的 URL - let newURL = `${URLProtocol}://${URLHostname}${URLPathname}${URLSearch}`; - - // 反向代理请求 - let response = await fetch(newURL); - - // 创建新的响应 - let newResponse = new Response(response.body, { - status: response.status, - statusText: response.statusText, - headers: response.headers - }); - - // 添加自定义头部,包含 URL 信息 - //newResponse.headers.set('X-Proxied-By', 'Cloudflare Worker'); - //newResponse.headers.set('X-Original-URL', fullURL); - newResponse.headers.set('X-New-URL', newURL); - - return newResponse; -} - -function checkSUB(host) { - if ((!sub || sub == '') && (addresses.length + addressesapi.length + addressesnotls.length + addressesnotlsapi.length + addressescsv.length) == 0){ - addresses = [ - 'Join.my.Telegram.channel.CMLiussss.to.unlock.more.premium.nodes.cf.090227.xyz#加入我的频道t.me/CMLiussss解锁更多优选节点', - '127.0.0.1:1234#CFnat', - 'visa.cn:443', - 'singapore.com:8443', - 'japan.com:2053', - 'brazil.com:2083', - 'russia.com:2087', - 'www.gov.ua:2096', - 'www.gco.gov.qa:8443', - 'www.gov.se', - 'time.is', - 'www.wto.org:8443', - 'fbi.gov:2087', - 'icook.hk', - //'104.17.0.0#IPv4', - '[2606:4700::]#IPv6' - ]; - if (host.includes(".workers.dev")) addressesnotls = [ - 'usa.visa.com:2095', - 'myanmar.visa.com:8080', - 'dynadot.com:8880', - 'www.visaeurope.ch:2052', - 'shopify.com:2082', - 'www.visasoutheasteurope.com:2086' - ]; - } -} - -const 啥啥啥_写的这是啥啊 = `\u0064\u006d\u0078\u006c\u0063\u0033\u004d\u003d`; -function 配置信息(UUID, 域名地址) { - const 协议类型 = atob(啥啥啥_写的这是啥啊); - - const 别名 = FileName; - let 地址 = 域名地址; - let 端口 = 443; - - const 用户ID = UUID; - const 加密方式 = 'none'; - - const 传输层协议 = 'ws'; - const 伪装域名 = 域名地址; - const 路径 = '/?ed=2560'; - - let 传输层安全 = ['tls',true]; - const SNI = 域名地址; - const 指纹 = 'randomized'; - - if (域名地址.includes('.workers.dev')){ - 地址 = 'visa.cn'; - 端口 = 80 ; - 传输层安全 = ['',false]; - } - - const v2ray = `${协议类型}://${用户ID}@${地址}:${端口}\u003f\u0065\u006e\u0063\u0072\u0079\u0070\u0074\u0069\u006f\u006e\u003d${加密方式}&security=${传输层安全[0]}&sni=${SNI}&fp=${指纹}&type=${传输层协议}&host=${伪装域名}&path=${encodeURIComponent(路径)}#${encodeURIComponent(别名)}`; - const clash = `- type: ${协议类型} - name: ${FileName} - server: ${地址} - port: ${端口} - uuid: ${用户ID} - network: ${传输层协议} - tls: ${传输层安全[1]} - udp: false - sni: ${SNI} - client-fingerprint: ${指纹} - ws-opts: - path: "${路径}" - headers: - host: ${伪装域名}`; - return [v2ray,clash]; -} - -let subParams = ['sub','base64','b64','clash','singbox','sb']; - -/** - * @param {string} userID - * @param {string | null} hostName - * @param {string} sub - * @param {string} UA - * @returns {Promise} - */ -async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url, env) { - const uuid = (_url.pathname == `/${env.KEY}`) ? env.KEY : userID; - checkSUB(hostName); - const userAgent = UA.toLowerCase(); - const Config = 配置信息(userID , hostName); - const v2ray = Config[0]; - const clash = Config[1]; - let proxyhost = ""; - if(hostName.includes(".workers.dev") || hostName.includes(".pages.dev")){ - if ( proxyhostsURL && (!proxyhosts || proxyhosts.length == 0)) { - try { - const response = await fetch(proxyhostsURL); - - if (!response.ok) { - console.error('获取地址时出错:', response.status, response.statusText); - return; // 如果有错误,直接返回 - } - - const text = await response.text(); - const lines = text.split('\n'); - // 过滤掉空行或只包含空白字符的行 - const nonEmptyLines = lines.filter(line => line.trim() !== ''); - - proxyhosts = proxyhosts.concat(nonEmptyLines); - } catch (error) { - //console.error('获取地址时出错:', error); - } - } - if (proxyhosts.length != 0) proxyhost = proxyhosts[Math.floor(Math.random() * proxyhosts.length)] + "/"; - } - - if ( userAgent.includes('mozilla') && !subParams.some(_searchParams => _url.searchParams.has(_searchParams))) { - const newSocks5s = socks5s.map(socks5Address => { - if (socks5Address.includes('@')) return socks5Address.split('@')[1]; - else if (socks5Address.includes('//')) return socks5Address.split('//')[1]; - else return socks5Address; - }); - - let socks5List = ''; - if( go2Socks5s.length > 0 && enableSocks ) { - socks5List = `${decodeURIComponent('SOCKS5%EF%BC%88%E7%99%BD%E5%90%8D%E5%8D%95%EF%BC%89%3A%20')}`; - if (go2Socks5s.includes(atob('YWxsIGlu'))||go2Socks5s.includes(atob('Kg=='))) socks5List += `${decodeURIComponent('%E6%89%80%E6%9C%89%E6%B5%81%E9%87%8F')}\n`; - else socks5List += `\n ${go2Socks5s.join('\n ')}\n`; - } - - let 订阅器 = '\n'; - if (!sub || sub == '') { - if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`; - else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`; - else 订阅器 += `CFCDN(访问方式): 无法访问, 需要您设置 proxyIP/PROXYIP !!!\n`; - 订阅器 += `\n您的订阅内容由 内置 addresses/ADD* 参数变量提供\n`; - if (addresses.length > 0) 订阅器 += `ADD(TLS优选域名&IP): \n ${addresses.join('\n ')}\n`; - if (addressesnotls.length > 0) 订阅器 += `ADDNOTLS(noTLS优选域名&IP): \n ${addressesnotls.join('\n ')}\n`; - if (addressesapi.length > 0) 订阅器 += `ADDAPI(TLS优选域名&IP 的 API): \n ${addressesapi.join('\n ')}\n`; - if (addressesnotlsapi.length > 0) 订阅器 += `ADDNOTLSAPI(noTLS优选域名&IP 的 API): \n ${addressesnotlsapi.join('\n ')}\n`; - if (addressescsv.length > 0) 订阅器 += `ADDCSV(IPTest测速csv文件 限速 ${DLS} ): \n ${addressescsv.join('\n ')}\n`; - } else { - if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`; - else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`; - else if (RproxyIP == 'true') 订阅器 += `CFCDN(访问方式): 自动获取ProxyIP\n`; - else 订阅器 += `CFCDN(访问方式): 无法访问, 需要您设置 proxyIP/PROXYIP !!!\n` - 订阅器 += `\nSUB(优选订阅生成器): ${sub}`; - } - - if (env.KEY && _url.pathname !== `/${env.KEY}`) 订阅器 = ''; - else 订阅器 += `\nSUBAPI(订阅转换后端): ${subProtocol}://${subconverter}\nSUBCONFIG(订阅转换配置文件): ${subconfig}`; - const 动态UUID = (uuid != userID) ? `TOKEN: ${uuid}\nUUIDNow: ${userID}\nUUIDLow: ${userIDLow}\n${userIDTime}TIME(动态UUID有效时间): ${effectiveTime} 天\nUPTIME(动态UUID更新时间): ${updateTime} 时(北京时间)\n\n` : `${userIDTime}`; - return ` -################################################################ -Subscribe / sub 订阅地址, 支持 Base64、clash-meta、sing-box 订阅格式 ---------------------------------------------------------------- -快速自适应订阅地址: -https://${proxyhost}${hostName}/${uuid} -https://${proxyhost}${hostName}/${uuid}?sub - -Base64订阅地址: -https://${proxyhost}${hostName}/${uuid}?b64 -https://${proxyhost}${hostName}/${uuid}?base64 - -clash订阅地址: -https://${proxyhost}${hostName}/${uuid}?clash - -singbox订阅地址: -https://${proxyhost}${hostName}/${uuid}?sb -https://${proxyhost}${hostName}/${uuid}?singbox ---------------------------------------------------------------- -################################################################ -${FileName} 配置信息 ---------------------------------------------------------------- -${动态UUID}HOST: ${hostName} -UUID: ${userID} -FKID: ${fakeUserID} -UA: ${UA} -${订阅器} ---------------------------------------------------------------- -################################################################ -v2ray ---------------------------------------------------------------- -${v2ray} ---------------------------------------------------------------- -################################################################ -clash-meta ---------------------------------------------------------------- -${clash} ---------------------------------------------------------------- -################################################################ -telegram 交流群 技术大佬~在线发牌! -https://t.me/CMLiussss ---------------------------------------------------------------- -github 项目地址 Star!Star!Star!!! -https://github.com/cmliu/edgetunnel ---------------------------------------------------------------- -################################################################ -`; - } else { - if (typeof fetch != 'function') { - return 'Error: fetch is not available in this environment.'; - } - - let newAddressesapi = []; - let newAddressescsv = []; - let newAddressesnotlsapi = []; - let newAddressesnotlscsv = []; - - // 如果是使用默认域名,则改成一个workers的域名,订阅器会加上代理 - if (hostName.includes(".workers.dev")){ - noTLS = 'true'; - fakeHostName = `${fakeHostName}.workers.dev`; - newAddressesnotlsapi = await getAddressesapi(addressesnotlsapi); - newAddressesnotlscsv = await getAddressescsv('FALSE'); - } else if (hostName.includes(".pages.dev")){ - fakeHostName = `${fakeHostName}.pages.dev`; - } else if (hostName.includes("worker") || hostName.includes("notls") || noTLS == 'true'){ - noTLS = 'true'; - fakeHostName = `notls${fakeHostName}.net`; - newAddressesnotlsapi = await getAddressesapi(addressesnotlsapi); - newAddressesnotlscsv = await getAddressescsv('FALSE'); - } else { - fakeHostName = `${fakeHostName}.xyz` - } - console.log(`虚假HOST: ${fakeHostName}`); - let url = `${subProtocol}://${sub}/sub?host=${fakeHostName}&uuid=${fakeUserID}&edgetunnel=cmliu&proxyip=${RproxyIP}`; - let isBase64 = true; - - if (!sub || sub == ""){ - if(hostName.includes('workers.dev') || hostName.includes('pages.dev')) { - if (proxyhostsURL && (!proxyhosts || proxyhosts.length == 0)) { - try { - const response = await fetch(proxyhostsURL); - - if (!response.ok) { - console.error('获取地址时出错:', response.status, response.statusText); - return; // 如果有错误,直接返回 - } - - const text = await response.text(); - const lines = text.split('\n'); - // 过滤掉空行或只包含空白字符的行 - const nonEmptyLines = lines.filter(line => line.trim() !== ''); - - proxyhosts = proxyhosts.concat(nonEmptyLines); - } catch (error) { - console.error('获取地址时出错:', error); - } - } - // 使用Set对象去重 - proxyhosts = [...new Set(proxyhosts)]; - } - - newAddressesapi = await getAddressesapi(addressesapi); - newAddressescsv = await getAddressescsv('TRUE'); - url = `https://${hostName}/${fakeUserID}`; - if (hostName.includes("worker") || hostName.includes("notls") || noTLS == 'true') url += '?notls'; - console.log(`虚假订阅: ${url}`); - } - - if (!userAgent.includes(('CF-Workers-SUB').toLowerCase())){ - if ((userAgent.includes('clash') && !userAgent.includes('nekobox')) || ( _url.searchParams.has('clash') && !userAgent.includes('subconverter'))) { - url = `${subProtocol}://${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`; - isBase64 = false; - } else if (userAgent.includes('sing-box') || userAgent.includes('singbox') || (( _url.searchParams.has('singbox') || _url.searchParams.has('sb')) && !userAgent.includes('subconverter'))) { - url = `${subProtocol}://${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`; - isBase64 = false; - } - } - - try { - let content; - if ((!sub || sub == "") && isBase64 == true) { - content = await subAddresses(fakeHostName,fakeUserID,noTLS,newAddressesapi,newAddressescsv,newAddressesnotlsapi,newAddressesnotlscsv); - } else { - const response = await fetch(url ,{ - headers: { - 'User-Agent': `${UA} CF-Workers-edgetunnel/cmliu` - }}); - content = await response.text(); - } - - if (_url.pathname == `/${fakeUserID}`) return content; - - return revertFakeInfo(content, userID, hostName, isBase64); - - } catch (error) { - console.error('Error fetching content:', error); - return `Error fetching content: ${error.message}`; - } - - } -} - -async function getAccountId(email, key) { - try { - const url = 'https://api.cloudflare.com/client/v4/accounts'; - const headers = new Headers({ - 'X-AUTH-EMAIL': email, - 'X-AUTH-KEY': key - }); - const response = await fetch(url, { headers }); - const data = await response.json(); - return data.result[0].id; // 假设我们需要第一个账号ID - } catch (error) { - return false ; - } -} - -async function getSum(accountId, accountIndex, email, key, startDate, endDate) { - try { - const startDateISO = new Date(startDate).toISOString(); - const endDateISO = new Date(endDate).toISOString(); - - const query = JSON.stringify({ - query: `query getBillingMetrics($accountId: String!, $filter: AccountWorkersInvocationsAdaptiveFilter_InputObject) { - viewer { - accounts(filter: {accountTag: $accountId}) { - pagesFunctionsInvocationsAdaptiveGroups(limit: 1000, filter: $filter) { - sum { - requests - } - } - workersInvocationsAdaptive(limit: 10000, filter: $filter) { - sum { - requests - } - } - } - } - }`, - variables: { - accountId, - filter: { datetime_geq: startDateISO, datetime_leq: endDateISO } - }, - }); - - const headers = new Headers({ - 'Content-Type': 'application/json', - 'X-AUTH-EMAIL': email, - 'X-AUTH-KEY': key, - }); - - const response = await fetch(`https://api.cloudflare.com/client/v4/graphql`, { - method: 'POST', - headers: headers, - body: query - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const res = await response.json(); - - const pagesFunctionsInvocationsAdaptiveGroups = res?.data?.viewer?.accounts?.[accountIndex]?.pagesFunctionsInvocationsAdaptiveGroups; - const workersInvocationsAdaptive = res?.data?.viewer?.accounts?.[accountIndex]?.workersInvocationsAdaptive; - - if (!pagesFunctionsInvocationsAdaptiveGroups && !workersInvocationsAdaptive) { - throw new Error('找不到数据'); - } - - const pagesSum = pagesFunctionsInvocationsAdaptiveGroups.reduce((a, b) => a + b?.sum.requests, 0); - const workersSum = workersInvocationsAdaptive.reduce((a, b) => a + b?.sum.requests, 0); - - //console.log(`范围: ${startDateISO} ~ ${endDateISO}\n默认取第 ${accountIndex} 项`); - - return [pagesSum, workersSum ]; - } catch (error) { - return [ 0,0 ]; - } -} -let proxyIPPool = []; -async function getAddressesapi(api) { - if (!api || api.length === 0) return []; - - let newapi = ""; - - // 创建一个AbortController对象,用于控制fetch请求的取消 - const controller = new AbortController(); - - const timeout = setTimeout(() => { - controller.abort(); // 取消所有请求 - }, 2000); // 2秒后触发 - - try { - // 使用Promise.allSettled等待所有API请求完成,无论成功或失败 - // 对api数组进行遍历,对每个API地址发起fetch请求 - const responses = await Promise.allSettled(api.map(apiUrl => fetch(apiUrl, { - method: 'get', - headers: { - 'Accept': 'text/html,application/xhtml+xml,application/xml;', - 'User-Agent': 'CF-Workers-edgetunnel/cmliu' - }, - signal: controller.signal // 将AbortController的信号量添加到fetch请求中,以便于需要时可以取消请求 - }).then(response => response.ok ? response.text() : Promise.reject()))); - - // 遍历所有响应 - for (const [index, response] of responses.entries()) { - // 检查响应状态是否为'fulfilled',即请求成功完成 - if (response.status === 'fulfilled') { - // 获取响应的内容 - const content = await response.value; - - // 验证当前apiUrl是否带有'proxyip=true' - if (api[index].includes('proxyip=true')) { - // 如果URL带有'proxyip=true',则将内容添加到proxyIPPool - proxyIPPool = proxyIPPool.concat((await ADD(content)).map(item => { - const baseItem = item.split('#')[0] || item; - if (baseItem.includes(':')) { - const port = baseItem.split(':')[1]; - if (!httpsPorts.includes(port)) { - return baseItem; - } - } else { - return `${baseItem}:443`; - } - return null; // 不符合条件时返回 null - }).filter(Boolean)); // 过滤掉 null 值 - } - // 将内容添加到newapi中 - newapi += content + '\n'; - } - } - } catch (error) { - console.error(error); - } finally { - // 无论成功或失败,最后都清除设置的超时定时器 - clearTimeout(timeout); - } - - const newAddressesapi = await ADD(newapi); - - // 返回处理后的结果 - return newAddressesapi; -} - -async function getAddressescsv(tls) { - if (!addressescsv || addressescsv.length === 0) { - return []; - } - - let newAddressescsv = []; - - for (const csvUrl of addressescsv) { - try { - const response = await fetch(csvUrl); - - if (!response.ok) { - console.error('获取CSV地址时出错:', response.status, response.statusText); - continue; - } - - const text = await response.text();// 使用正确的字符编码解析文本内容 - let lines; - if (text.includes('\r\n')){ - lines = text.split('\r\n'); - } else { - lines = text.split('\n'); - } - - // 检查CSV头部是否包含必需字段 - const header = lines[0].split(','); - const tlsIndex = header.indexOf('TLS'); - - const ipAddressIndex = 0;// IP地址在 CSV 头部的位置 - const portIndex = 1;// 端口在 CSV 头部的位置 - const dataCenterIndex = tlsIndex + 1; // 数据中心是 TLS 的后一个字段 - - if (tlsIndex === -1) { - console.error('CSV文件缺少必需的字段'); - continue; - } - - // 从第二行开始遍历CSV行 - for (let i = 1; i < lines.length; i++) { - const columns = lines[i].split(','); - const speedIndex = columns.length - 1; // 最后一个字段 - // 检查TLS是否为"TRUE"且速度大于DLS - if (columns[tlsIndex].toUpperCase() === tls && parseFloat(columns[speedIndex]) > DLS) { - const ipAddress = columns[ipAddressIndex]; - const port = columns[portIndex]; - const dataCenter = columns[dataCenterIndex]; - - const formattedAddress = `${ipAddress}:${port}#${dataCenter}`; - newAddressescsv.push(formattedAddress); - if (csvUrl.includes('proxyip=true') && columns[tlsIndex].toUpperCase() == 'true' && !httpsPorts.includes(port)) { - // 如果URL带有'proxyip=true',则将内容添加到proxyIPPool - proxyIPPool.push(`${ipAddress}:${port}`); - } - } - } - } catch (error) { - console.error('获取CSV地址时出错:', error); - continue; - } - } - - return newAddressescsv; -} - -function subAddresses(host,UUID,noTLS,newAddressesapi,newAddressescsv,newAddressesnotlsapi,newAddressesnotlscsv) { - const regex = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[.*\]):?(\d+)?#?(.*)?$/; - addresses = addresses.concat(newAddressesapi); - addresses = addresses.concat(newAddressescsv); - let notlsresponseBody ; - if (noTLS == 'true'){ - addressesnotls = addressesnotls.concat(newAddressesnotlsapi); - addressesnotls = addressesnotls.concat(newAddressesnotlscsv); - const uniqueAddressesnotls = [...new Set(addressesnotls)]; - - notlsresponseBody = uniqueAddressesnotls.map(address => { - let port = "-1"; - let addressid = address; - - const match = addressid.match(regex); - if (!match) { - if (address.includes(':') && address.includes('#')) { - const parts = address.split(':'); - address = parts[0]; - const subParts = parts[1].split('#'); - port = subParts[0]; - addressid = subParts[1]; - } else if (address.includes(':')) { - const parts = address.split(':'); - address = parts[0]; - port = parts[1]; - } else if (address.includes('#')) { - const parts = address.split('#'); - address = parts[0]; - addressid = parts[1]; - } - - if (addressid.includes(':')) { - addressid = addressid.split(':')[0]; - } - } else { - address = match[1]; - port = match[2] || port; - addressid = match[3] || address; - } - - const httpPorts = ["8080","8880","2052","2082","2086","2095"]; - if (!isValidIPv4(address) && port == "-1") { - for (let httpPort of httpPorts) { - if (address.includes(httpPort)) { - port = httpPort; - break; - } - } - } - if (port == "-1") port = "80"; - - let 伪装域名 = host ; - let 最终路径 = '/?ed=2560' ; - let 节点备注 = ''; - const 协议类型 = atob(啥啥啥_写的这是啥啊); - - const vlessLink = `${协议类型}://${UUID}@${address}:${port}\u003f\u0065\u006e\u0063\u0072\u0079\u0070\u0074\u0069\u006f\u006e\u003dnone&security=&type=ws&host=${伪装域名}&path=${encodeURIComponent(最终路径)}#${encodeURIComponent(addressid + 节点备注)}`; - - return vlessLink; - - }).join('\n'); - - } - - // 使用Set对象去重 - const uniqueAddresses = [...new Set(addresses)]; - - const responseBody = uniqueAddresses.map(address => { - let port = "-1"; - let addressid = address; - - const match = addressid.match(regex); - if (!match) { - if (address.includes(':') && address.includes('#')) { - const parts = address.split(':'); - address = parts[0]; - const subParts = parts[1].split('#'); - port = subParts[0]; - addressid = subParts[1]; - } else if (address.includes(':')) { - const parts = address.split(':'); - address = parts[0]; - port = parts[1]; - } else if (address.includes('#')) { - const parts = address.split('#'); - address = parts[0]; - addressid = parts[1]; - } - - if (addressid.includes(':')) { - addressid = addressid.split(':')[0]; - } - } else { - address = match[1]; - port = match[2] || port; - addressid = match[3] || address; - } - - if (!isValidIPv4(address) && port == "-1") { - for (let httpsPort of httpsPorts) { - if (address.includes(httpsPort)) { - port = httpsPort; - break; - } - } - } - if (port == "-1") port = "443"; - - let 伪装域名 = host ; - let 最终路径 = '/?ed=2560' ; - let 节点备注 = ''; - const matchingProxyIP = proxyIPPool.find(proxyIP => proxyIP.includes(address)); - if (matchingProxyIP) 最终路径 += `&proxyip=${matchingProxyIP}`; - - if(proxyhosts.length > 0 && (伪装域名.includes('.workers.dev') || 伪装域名.includes('pages.dev'))) { - 最终路径 = `/${伪装域名}${最终路径}`; - 伪装域名 = proxyhosts[Math.floor(Math.random() * proxyhosts.length)]; - 节点备注 = ` 已启用临时域名中转服务,请尽快绑定自定义域!`; - } - - const 协议类型 = atob(啥啥啥_写的这是啥啊); - const vlessLink = `${协议类型}://${UUID}@${address}:${port}\u003f\u0065\u006e\u0063\u0072\u0079\u0070\u0074\u0069\u006f\u006e\u003dnone&security=tls&sni=${伪装域名}&fp=random&type=ws&host=${伪装域名}&path=${encodeURIComponent(最终路径)}#${encodeURIComponent(addressid + 节点备注)}`; - - return vlessLink; - }).join('\n'); - - let base64Response = responseBody; // 重新进行 Base64 编码 - if(noTLS == 'true') base64Response += `\n${notlsresponseBody}`; - return btoa(base64Response); -} - -async function sendMessage(type, ip, add_data = "") { - if ( BotToken !== '' && ChatID !== ''){ - let msg = ""; - const response = await fetch(`http://ip-api.com/json/${ip}?lang=zh-CN`); - if (response.status == 200) { - const ipInfo = await response.json(); - msg = `${type}\nIP: ${ip}\n国家: ${ipInfo.country}\n城市: ${ipInfo.city}\n组织: ${ipInfo.org}\nASN: ${ipInfo.as}\n${add_data}`; - } else { - msg = `${type}\nIP: ${ip}\n${add_data}`; - } - - let url = "https://api.telegram.org/bot"+ BotToken +"/sendMessage?chat_id=" + ChatID + "&parse_mode=HTML&text=" + encodeURIComponent(msg); - return fetch(url, { - method: 'get', - headers: { - 'Accept': 'text/html,application/xhtml+xml,application/xml;', - 'Accept-Encoding': 'gzip, deflate, br', - 'User-Agent': 'Mozilla/5.0 Chrome/90.0.4430.72' - } - }); - } -} - -function isValidIPv4(address) { - const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; - return ipv4Regex.test(address); -} - -function generateDynamicUUID(key) { - function getWeekOfYear() { - const now = new Date(); - const timezoneOffset = 8; // 北京时间相对于UTC的时区偏移+8小时 - const adjustedNow = new Date(now.getTime() + timezoneOffset * 60 * 60 * 1000); - const start = new Date(2007, 6, 7, updateTime, 0, 0); // 固定起始日期为2007年7月7日的凌晨3点 - const diff = adjustedNow - start; - const oneWeek = 1000 * 60 * 60 * 24 * effectiveTime; - return Math.ceil(diff / oneWeek); - } - - const passwdTime = getWeekOfYear(); // 获取当前周数 - const endTime = new Date(2007, 6, 7, updateTime, 0, 0); // 固定起始日期 - endTime.setMilliseconds(endTime.getMilliseconds() + passwdTime * 1000 * 60 * 60 * 24 * effectiveTime); - - // 生成 UUID 的辅助函数 - function generateUUID(baseString) { - const hashBuffer = new TextEncoder().encode(baseString); - return crypto.subtle.digest('SHA-256', hashBuffer).then((hash) => { - const hashArray = Array.from(new Uint8Array(hash)); - const hexHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); - let uuid = hexHash.substr(0, 8) + '-' + hexHash.substr(8, 4) + '-4' + hexHash.substr(13, 3) + '-' + (parseInt(hexHash.substr(16, 2), 16) & 0x3f | 0x80).toString(16) + hexHash.substr(18, 2) + '-' + hexHash.substr(20, 12); - return uuid; - }); - } - - // 生成两个 UUID - const currentUUIDPromise = generateUUID(key + passwdTime); - const previousUUIDPromise = generateUUID(key + (passwdTime - 1)); - - // 格式化到期时间 - const expirationDateUTC = new Date(endTime.getTime() - 8 * 60 * 60 * 1000); // UTC时间 - const expirationDateString = `到期时间(UTC): ${expirationDateUTC.toISOString().slice(0, 19).replace('T', ' ')} (UTC+8): ${endTime.toISOString().slice(0, 19).replace('T', ' ')}\n`; - - return Promise.all([currentUUIDPromise, previousUUIDPromise, expirationDateString]); -} \ No newline at end of file +const _0x452530=_0x42e2;(function(_0x4499a3,_0x36eefd){const _0x58a97b=_0x42e2,_0x29c7fd=_0x4499a3();while(!![]){try{const _0xc7c3bd=parseInt(_0x58a97b(0xba))/0x1+-parseInt(_0x58a97b(0xd4))/0x2*(-parseInt(_0x58a97b(0xbf))/0x3)+parseInt(_0x58a97b(0x1b3))/0x4+parseInt(_0x58a97b(0xb2))/0x5*(parseInt(_0x58a97b(0x117))/0x6)+parseInt(_0x58a97b(0xb7))/0x7*(parseInt(_0x58a97b(0x113))/0x8)+-parseInt(_0x58a97b(0x8a))/0x9+-parseInt(_0x58a97b(0x13f))/0xa*(parseInt(_0x58a97b(0x109))/0xb);if(_0xc7c3bd===_0x36eefd)break;else _0x29c7fd['push'](_0x29c7fd['shift']());}catch(_0x3747a0){_0x29c7fd['push'](_0x29c7fd['shift']());}}}(_0x2647,0xb117b));import{connect}from'cloudflare:sockets';let userID,proxyIP='',sub='',subconverter=_0x452530(0x106),subconfig=_0x452530(0x17c),subProtocol='https',socks5Address='',parsedSocks5Address={},enableSocks=![],fakeUserID,fakeHostName,noTLS=_0x452530(0x11d);const expire=0xf4849500;let proxyIPs,socks5s,go2Socks5s=['*ttvnw.net','*tapecontent.net',_0x452530(0xfd),'*.loadshare.org'],addresses=[],addressesapi=[],addressesnotls=[],addressesnotlsapi=[],addressescsv=[],DLS=0x8,FileName=atob('ZWRnZXR1bm5lbA=='),BotToken,ChatID,proxyhosts=[],proxyhostsURL='',RproxyIP='false',httpsPorts=[_0x452530(0xd1),'2083',_0x452530(0x195),_0x452530(0x1a1),'8443'],有效时间=0x7,更新时间=0x3,userIDLow,userIDTime='',proxyIPPool=[];export default{async 'fetch'(_0x3d2015,_0x5ee915,_0xe7eefe){const _0x422822=_0x452530;try{const _0x2f5538=_0x3d2015[_0x422822(0x1b0)][_0x422822(0xd9)]('User-Agent')||_0x422822(0xa4),_0x39cf66=_0x2f5538[_0x422822(0x170)]();if(_0x5ee915[_0x422822(0x11c)]){有效时间=_0x5ee915[_0x422822(0xad)]||有效时间,更新时间=_0x5ee915[_0x422822(0xf1)]||更新时间;const _0xb9c11d=await 生成动态UUID(_0x5ee915[_0x422822(0x11c)]);userID=_0xb9c11d[0x0];}else _0x5ee915[_0x422822(0xec)]&&(userID=_0x5ee915[_0x422822(0xec)]);const _0x196c60={};_0x196c60['status']=0x194;if(!userID)return new Response(_0x422822(0x1bf),_0x196c60);const _0x3cdbee=new Date();_0x3cdbee['setHours'](0x0,0x0,0x0,0x0);const _0x103043=Math[_0x422822(0x19c)](_0x3cdbee[_0x422822(0x189)]()/0x3e8),_0x143c02=await 双重哈希(''+userID+_0x103043);fakeUserID=[_0x143c02[_0x422822(0x98)](0x0,0x8),_0x143c02['slice'](0x8,0xc),_0x143c02[_0x422822(0x98)](0xc,0x10),_0x143c02[_0x422822(0x98)](0x10,0x14),_0x143c02[_0x422822(0x98)](0x14)][_0x422822(0xd6)]('-'),fakeHostName=_0x143c02[_0x422822(0x98)](0x6,0x9)+'.'+_0x143c02['slice'](0xd,0x13),proxyIP=_0x5ee915[_0x422822(0x16f)]||proxyIP,proxyIPs=await 整理(proxyIP),proxyIP=proxyIPs[Math[_0x422822(0x10c)](Math[_0x422822(0xe0)]()*proxyIPs['length'])],socks5Address=_0x5ee915[_0x422822(0x1a4)]||socks5Address,socks5s=await 整理(socks5Address),socks5Address=socks5s[Math[_0x422822(0x10c)](Math[_0x422822(0xe0)]()*socks5s[_0x422822(0x178)])],socks5Address=socks5Address[_0x422822(0x1ac)]('//')[0x1]||socks5Address;if(_0x5ee915[_0x422822(0x18c)])httpsPorts=await 整理(_0x5ee915[_0x422822(0x18c)]);sub=_0x5ee915[_0x422822(0xf7)]||sub,subconverter=_0x5ee915['SUBAPI']||subconverter;subconverter[_0x422822(0x15e)]('http://')?(subconverter=subconverter['split']('//')[0x1],subProtocol=_0x422822(0x10b)):subconverter=subconverter[_0x422822(0x1ac)]('//')[0x1]||subconverter;subconfig=_0x5ee915[_0x422822(0xf5)]||subconfig;if(socks5Address)try{parsedSocks5Address=socks5AddressParser(socks5Address),RproxyIP=_0x5ee915['RPROXYIP']||_0x422822(0x11d),enableSocks=!![];}catch(_0x4d4171){let _0x43df64=_0x4d4171;console[_0x422822(0x185)](_0x43df64['toString']()),RproxyIP=_0x5ee915[_0x422822(0x90)]||!proxyIP?_0x422822(0x18d):_0x422822(0x11d),enableSocks=![];}else RproxyIP=_0x5ee915['RPROXYIP']||!proxyIP?_0x422822(0x18d):_0x422822(0x11d);if(_0x5ee915[_0x422822(0xbc)])addresses=await 整理(_0x5ee915[_0x422822(0xbc)]);if(_0x5ee915[_0x422822(0x166)])addressesapi=await 整理(_0x5ee915[_0x422822(0x166)]);if(_0x5ee915['ADDNOTLS'])addressesnotls=await 整理(_0x5ee915[_0x422822(0x150)]);if(_0x5ee915['ADDNOTLSAPI'])addressesnotlsapi=await 整理(_0x5ee915[_0x422822(0x152)]);if(_0x5ee915['ADDCSV'])addressescsv=await 整理(_0x5ee915[_0x422822(0x1bd)]);DLS=_0x5ee915[_0x422822(0x157)]||DLS,BotToken=_0x5ee915[_0x422822(0x91)]||BotToken,ChatID=_0x5ee915[_0x422822(0x12a)]||ChatID;if(_0x5ee915[_0x422822(0x13d)])go2Socks5s=await 整理(_0x5ee915[_0x422822(0x13d)]);const _0x3f65b6=_0x3d2015[_0x422822(0x1b0)][_0x422822(0xd9)]('Upgrade'),_0x13c0bd=new URL(_0x3d2015[_0x422822(0x13a)]);if(_0x13c0bd[_0x422822(0x1b1)]['has'](_0x422822(0x122))&&_0x13c0bd[_0x422822(0x1b1)]['get'](_0x422822(0x122))!=='')sub=_0x13c0bd[_0x422822(0x1b1)][_0x422822(0xd9)](_0x422822(0x122));FileName=_0x5ee915['SUBNAME']||FileName;if(_0x13c0bd['searchParams'][_0x422822(0x1a0)](_0x422822(0x105)))noTLS=_0x422822(0x18d);if(!_0x3f65b6||_0x3f65b6!=='websocket'){const _0x3f37f0=_0x13c0bd['pathname']['toLowerCase']();if(_0x3f37f0=='/'){const _0x5f522f={};_0x5f522f[_0x422822(0x1bb)]=_0x422822(0x88);const _0x411164={};_0x411164[_0x422822(0x128)]=0xc8,_0x411164[_0x422822(0x1b0)]=_0x5f522f;if(_0x5ee915[_0x422822(0x19f)])return Response[_0x422822(0x112)](_0x5ee915['URL302'],0x12e);else{if(_0x5ee915[_0x422822(0xc1)])return await 代理URL(_0x5ee915[_0x422822(0xc1)],_0x13c0bd);else return new Response(JSON[_0x422822(0x15f)](_0x3d2015['cf'],null,0x4),_0x411164);}}else{if(_0x3f37f0=='/'+fakeUserID){const _0x221fd1=await 生成配置信息(userID,_0x3d2015[_0x422822(0x1b0)]['get'](_0x422822(0x172)),sub,_0x422822(0xbd),RproxyIP,_0x13c0bd,_0x5ee915),_0x488c7d={};return _0x488c7d['status']=0xc8,new Response(''+_0x221fd1,_0x488c7d);}else{if(_0x3f37f0=='/'+_0x5ee915[_0x422822(0x11c)]||_0x3f37f0=='/'+userID){await sendMessage('#获取订阅\x20'+FileName,_0x3d2015[_0x422822(0x1b0)][_0x422822(0xd9)](_0x422822(0x93)),_0x422822(0x1c2)+_0x2f5538+_0x422822(0x10f)+_0x13c0bd[_0x422822(0x14f)]+_0x422822(0x158)+(_0x13c0bd[_0x422822(0x19d)]+_0x13c0bd[_0x422822(0x1c1)])+'');const _0x2e0bc6=await 生成配置信息(userID,_0x3d2015[_0x422822(0x1b0)][_0x422822(0xd9)](_0x422822(0x172)),sub,_0x2f5538,RproxyIP,_0x13c0bd,_0x5ee915),_0x159683=Date[_0x422822(0xce)](),_0x125130=new Date(_0x159683);_0x125130['setHours'](0x0,0x0,0x0,0x0);const _0x3e4491=Math[_0x422822(0x10c)]((_0x159683-_0x125130[_0x422822(0x189)]())/0x5265c00*0x18*0x10000000000/0x2);let _0x14819e=_0x3e4491,_0x28123f=_0x3e4491,_0x37de0c=0x18*0x10000000000;if(_0x39cf66&&_0x39cf66[_0x422822(0x15e)](_0x422822(0xa3))){const _0x1e71d4={};_0x1e71d4[_0x422822(0x126)]=_0x422822(0x149),_0x1e71d4[_0x422822(0x151)]='6',_0x1e71d4[_0x422822(0x165)]='upload='+_0x14819e+_0x422822(0x1ae)+_0x28123f+_0x422822(0x1ba)+_0x37de0c+_0x422822(0x13c)+expire;const _0x2b393a={};return _0x2b393a[_0x422822(0x128)]=0xc8,_0x2b393a['headers']=_0x1e71d4,new Response(''+_0x2e0bc6,_0x2b393a);}else return new Response(''+_0x2e0bc6,{'status':0xc8,'headers':{'Content-Disposition':_0x422822(0x13e)+FileName+';\x20filename*=utf-8\x27\x27'+encodeURIComponent(FileName),'Content-Type':_0x422822(0x149),'Profile-Update-Interval':'6','Subscription-Userinfo':'upload='+_0x14819e+_0x422822(0x1ae)+_0x28123f+_0x422822(0x1ba)+_0x37de0c+_0x422822(0x13c)+expire}});}else{const _0x258337={};_0x258337[_0x422822(0x128)]=0x194;if(_0x5ee915[_0x422822(0x19f)])return Response['redirect'](_0x5ee915[_0x422822(0x19f)],0x12e);else{if(_0x5ee915[_0x422822(0xc1)])return await 代理URL(_0x5ee915[_0x422822(0xc1)],_0x13c0bd);else return new Response(_0x422822(0x104),_0x258337);}}}}}else{proxyIP=_0x13c0bd[_0x422822(0x1b1)]['get'](_0x422822(0x16e))||proxyIP;if(new RegExp(_0x422822(0xcd),'i')[_0x422822(0x1a5)](_0x13c0bd['pathname']))proxyIP=_0x13c0bd[_0x422822(0x19d)][_0x422822(0x170)]()[_0x422822(0x1ac)](_0x422822(0xcd))[0x1];else{if(new RegExp(_0x422822(0x103),'i')[_0x422822(0x1a5)](_0x13c0bd[_0x422822(0x19d)]))proxyIP=_0x422822(0x131)+_0x13c0bd['pathname']['toLowerCase']()['split'](_0x422822(0x103))[0x1];}socks5Address=_0x13c0bd[_0x422822(0x1b1)][_0x422822(0xd9)](_0x422822(0xd2))||socks5Address;if(new RegExp(_0x422822(0x134),'i')['test'](_0x13c0bd['pathname']))socks5Address=_0x13c0bd[_0x422822(0x19d)]['split']('5=')[0x1];else{if(new RegExp(_0x422822(0xf4),'i')['test'](_0x13c0bd['pathname'])||new RegExp('/socks5://','i')[_0x422822(0x1a5)](_0x13c0bd[_0x422822(0x19d)])){socks5Address=_0x13c0bd[_0x422822(0x19d)]['split']('://')[0x1][_0x422822(0x1ac)]('#')[0x0];if(socks5Address[_0x422822(0x15e)]('@')){let _0xbc8b6e=socks5Address[_0x422822(0x1ac)]('@')[0x0];const _0x2a424e=/^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i;if(_0x2a424e[_0x422822(0x1a5)](_0xbc8b6e)&&!_0xbc8b6e['includes'](':'))_0xbc8b6e=atob(_0xbc8b6e);socks5Address=_0xbc8b6e+'@'+socks5Address[_0x422822(0x1ac)]('@')[0x1];}}}if(socks5Address)try{parsedSocks5Address=socks5AddressParser(socks5Address),enableSocks=!![];}catch(_0xafc01){let _0x2c69a3=_0xafc01;console[_0x422822(0x185)](_0x2c69a3['toString']()),enableSocks=![];}else enableSocks=![];return await 维列斯WS开搞开搞(_0x3d2015);}}catch(_0x5d2e78){let _0x2cc8ab=_0x5d2e78;return new Response(_0x2cc8ab['toString']());}}};async function 维列斯WS开搞开搞(_0xdba366){const _0x367767=_0x452530,_0x25e6e7=new WebSocketPair(),[_0x4e58a7,_0x2072db]=Object[_0x367767(0xc8)](_0x25e6e7);_0x2072db['accept']();let _0x4fb92e='',_0x38ccfd='';const _0x1464d2=(_0x4adf53,_0x1192de)=>{console['log']('['+_0x4fb92e+':'+_0x38ccfd+']\x20'+_0x4adf53,_0x1192de||'');},_0x299f9c=_0xdba366[_0x367767(0x1b0)][_0x367767(0xd9)]('sec-websocket-protocol')||'',_0x41ff04=makeReadableWebSocketStream(_0x2072db,_0x299f9c,_0x1464d2),_0x3c998a={};_0x3c998a[_0x367767(0x1be)]=null;let _0x21117a=_0x3c998a,_0x17c839=![];_0x41ff04[_0x367767(0x16a)](new WritableStream({async 'write'(_0x98f9c3,_0x44d99c){const _0x16966d=_0x367767;if(_0x17c839)return await handleDNSQuery(_0x98f9c3,_0x2072db,null,_0x1464d2);if(_0x21117a[_0x16966d(0x1be)]){const _0x30b2f0=_0x21117a[_0x16966d(0x1be)]['writable']['getWriter']();await _0x30b2f0[_0x16966d(0x101)](_0x98f9c3),_0x30b2f0[_0x16966d(0x14b)]();return;}const {hasError:_0x1a1296,message:_0x3b6591,addressType:_0x46c864,portRemote:portRemote=0x1bb,addressRemote:addressRemote='',rawDataIndex:_0x1330c9,vlessVersion:vlessVersion=new Uint8Array([0x0,0x0]),isUDP:_0x35adfb}=processVlessHeader(_0x98f9c3,userID);_0x4fb92e=addressRemote,_0x38ccfd=portRemote+'--'+Math['random']()+'\x20'+(_0x35adfb?_0x16966d(0x89):_0x16966d(0x125))+'\x20';if(_0x1a1296){throw new Error(_0x3b6591);return;}if(_0x35adfb){if(portRemote===0x35)_0x17c839=!![];else{throw new Error('UDP\x20代理仅对\x20DNS(53\x20端口)启用');return;}}const _0x4fbdd5=new Uint8Array([vlessVersion[0x0],0x0]),_0x18a2d0=_0x98f9c3[_0x16966d(0x98)](_0x1330c9);if(_0x17c839)return handleDNSQuery(_0x18a2d0,_0x2072db,_0x4fbdd5,_0x1464d2);_0x1464d2('处理\x20TCP\x20出站连接\x20'+addressRemote+':'+portRemote),handleTCPOutBound(_0x21117a,_0x46c864,addressRemote,portRemote,_0x18a2d0,_0x2072db,_0x4fbdd5,_0x1464d2);},'close'(){const _0x8fe3db=_0x367767;_0x1464d2(_0x8fe3db(0xc2));},'abort'(_0x4daa30){const _0x247a28=_0x367767;_0x1464d2(_0x247a28(0xf3),JSON[_0x247a28(0x15f)](_0x4daa30));}}))[_0x367767(0x1b2)](_0x4b770f=>{const _0x3b0b59=_0x367767;_0x1464d2(_0x3b0b59(0xe3),_0x4b770f);});const _0x17a241={};return _0x17a241['status']=0x65,_0x17a241[_0x367767(0x15d)]=_0x4e58a7,new Response(null,_0x17a241);}async function handleTCPOutBound(_0x2a0abe,_0x496314,_0x57d925,_0x1d4e3a,_0x58c55d,_0x4d3e81,_0x4fd97a,_0x35b0a9){const _0x30fb35=_0x452530;async function _0xe40bb7(_0x49ec4b){const _0xda70c4=_0x42e2;if(go2Socks5s[_0xda70c4(0x15e)](atob('YWxsIGlu'))||go2Socks5s[_0xda70c4(0x15e)](atob('Kg==')))return!![];return go2Socks5s[_0xda70c4(0x92)](_0x543238=>{const _0x5dca88=_0xda70c4;let _0xcc0171=_0x543238[_0x5dca88(0xde)](/\*/g,'.*'),_0x30e0f9=new RegExp('^'+_0xcc0171+'$','i');return _0x30e0f9[_0x5dca88(0x1a5)](_0x49ec4b);});}async function _0x320e0c(_0x11f424,_0x2839bf,_0x5554cb=![]){const _0x4a50fc=_0x42e2;_0x35b0a9(_0x4a50fc(0xcb)+_0x11f424+':'+_0x2839bf);const _0x5ec9b0=_0x5554cb?await socks5Connect(_0x496314,_0x11f424,_0x2839bf,_0x35b0a9):connect({'hostname':_0x11f424,'port':_0x2839bf});_0x2a0abe[_0x4a50fc(0x1be)]=_0x5ec9b0;const _0x2d2dcf=_0x5ec9b0[_0x4a50fc(0x1c4)]['getWriter']();return await _0x2d2dcf['write'](_0x58c55d),_0x2d2dcf['releaseLock'](),_0x5ec9b0;}async function _0x14a228(){const _0x51a179=_0x42e2;if(enableSocks)_0x17c6f1=await _0x320e0c(_0x57d925,_0x1d4e3a,!![]);else{if(!proxyIP||proxyIP=='')proxyIP=atob(_0x51a179(0x147));else{if(proxyIP[_0x51a179(0x15e)](']:'))_0x1d4e3a=proxyIP[_0x51a179(0x1ac)](']:')[0x1]||_0x1d4e3a,proxyIP=proxyIP[_0x51a179(0x1ac)](']:')[0x0]||proxyIP;else proxyIP['split'](':')[_0x51a179(0x178)]===0x2&&(_0x1d4e3a=proxyIP[_0x51a179(0x1ac)](':')[0x1]||_0x1d4e3a,proxyIP=proxyIP[_0x51a179(0x1ac)](':')[0x0]||proxyIP);}if(proxyIP[_0x51a179(0x15e)](_0x51a179(0x184)))_0x1d4e3a=proxyIP[_0x51a179(0x1ac)]('.tp')[0x1]['split']('.')[0x0]||_0x1d4e3a;_0x17c6f1=await _0x320e0c(proxyIP||_0x57d925,_0x1d4e3a);}_0x17c6f1[_0x51a179(0x10e)][_0x51a179(0x1b2)](_0x48b5b9=>{const _0x3e882a=_0x51a179;console[_0x3e882a(0x185)](_0x3e882a(0x110),_0x48b5b9);})[_0x51a179(0xd7)](()=>{safeCloseWebSocket(_0x4d3e81);}),remoteSocketToWS(_0x17c6f1,_0x4d3e81,_0x4fd97a,null,_0x35b0a9);}let _0x410067=![];if(go2Socks5s[_0x30fb35(0x178)]>0x0&&enableSocks)_0x410067=await _0xe40bb7(_0x57d925);let _0x17c6f1=await _0x320e0c(_0x57d925,_0x1d4e3a,_0x410067);remoteSocketToWS(_0x17c6f1,_0x4d3e81,_0x4fd97a,_0x14a228,_0x35b0a9);}function makeReadableWebSocketStream(_0x58e2b7,_0x454118,_0x55377a){let _0x5dcdc9=![];const _0x22a889=new ReadableStream({'start'(_0xfd8433){const _0x1abbe9=_0x42e2;_0x58e2b7[_0x1abbe9(0x1b6)](_0x1abbe9(0x102),_0x392ef2=>{const _0x376909=_0x1abbe9;if(_0x5dcdc9)return;const _0x587352=_0x392ef2[_0x376909(0x16b)];_0xfd8433[_0x376909(0xed)](_0x587352);}),_0x58e2b7[_0x1abbe9(0x1b6)](_0x1abbe9(0x129),()=>{const _0x45c8c9=_0x1abbe9;safeCloseWebSocket(_0x58e2b7);if(_0x5dcdc9)return;_0xfd8433[_0x45c8c9(0x129)]();}),_0x58e2b7[_0x1abbe9(0x1b6)](_0x1abbe9(0x11a),_0xa3b1e9=>{const _0x1b168c=_0x1abbe9;_0x55377a(_0x1b168c(0x191)),_0xfd8433[_0x1b168c(0x11a)](_0xa3b1e9);});const {earlyData:_0x20f603,error:_0x1e88ca}=base64ToArrayBuffer(_0x454118);if(_0x1e88ca)_0xfd8433['error'](_0x1e88ca);else _0x20f603&&_0xfd8433[_0x1abbe9(0xed)](_0x20f603);},'pull'(_0x58ff65){},'cancel'(_0x263e9a){const _0xb0ba70=_0x42e2;if(_0x5dcdc9)return;_0x55377a(_0xb0ba70(0x153)+_0x263e9a),_0x5dcdc9=!![],safeCloseWebSocket(_0x58e2b7);}});return _0x22a889;}function processVlessHeader(_0x56cca3,_0x5c38d9){const _0x5da9b4=_0x452530;if(_0x56cca3[_0x5da9b4(0x8f)]<0x18){const _0x2d1087={};return _0x2d1087[_0x5da9b4(0xa0)]=!![],_0x2d1087[_0x5da9b4(0x102)]=_0x5da9b4(0xa8),_0x2d1087;}const _0x288fe5=new Uint8Array(_0x56cca3[_0x5da9b4(0x98)](0x0,0x1));let _0xff1702=![],_0x1c9054=![];function _0x4f46fb(_0xe053c3,_0x56e7bd,_0x41043a){const _0x3b50b7=_0x5da9b4,_0x3590cd=new Uint8Array(_0x41043a[_0x3b50b7(0x98)](0x1,0x11)),_0x16f2c6=stringify(_0x3590cd);return _0x16f2c6===_0xe053c3||_0x16f2c6===_0x56e7bd;}_0xff1702=_0x4f46fb(_0x5c38d9,userIDLow,_0x56cca3);if(!_0xff1702)return{'hasError':!![],'message':_0x5da9b4(0x8b)+new Uint8Array(_0x56cca3[_0x5da9b4(0x98)](0x1,0x11))};const _0x50deab=new Uint8Array(_0x56cca3[_0x5da9b4(0x98)](0x11,0x12))[0x0],_0x23c5f9=new Uint8Array(_0x56cca3[_0x5da9b4(0x98)](0x12+_0x50deab,0x12+_0x50deab+0x1))[0x0];if(_0x23c5f9===0x1){}else{if(_0x23c5f9===0x2)_0x1c9054=!![];else{const _0xb4833d={};return _0xb4833d['hasError']=!![],_0xb4833d[_0x5da9b4(0x102)]='command\x20'+_0x23c5f9+_0x5da9b4(0x159),_0xb4833d;}}const _0x2f154d=0x12+_0x50deab+0x1,_0x32f6c3=_0x56cca3[_0x5da9b4(0x98)](_0x2f154d,_0x2f154d+0x2),_0x38ff94=new DataView(_0x32f6c3)[_0x5da9b4(0xa2)](0x0);let _0x2f278e=_0x2f154d+0x2;const _0x4836a5=new Uint8Array(_0x56cca3[_0x5da9b4(0x98)](_0x2f278e,_0x2f278e+0x1)),_0x1c46ef=_0x4836a5[0x0];let _0xdd54a2=0x0,_0x578ee0=_0x2f278e+0x1,_0x3345db='';switch(_0x1c46ef){case 0x1:_0xdd54a2=0x4,_0x3345db=new Uint8Array(_0x56cca3['slice'](_0x578ee0,_0x578ee0+_0xdd54a2))['join']('.');break;case 0x2:_0xdd54a2=new Uint8Array(_0x56cca3[_0x5da9b4(0x98)](_0x578ee0,_0x578ee0+0x1))[0x0],_0x578ee0+=0x1,_0x3345db=new TextDecoder()[_0x5da9b4(0x111)](_0x56cca3[_0x5da9b4(0x98)](_0x578ee0,_0x578ee0+_0xdd54a2));break;case 0x3:_0xdd54a2=0x10;const _0x46ece7=new DataView(_0x56cca3[_0x5da9b4(0x98)](_0x578ee0,_0x578ee0+_0xdd54a2)),_0x2a8da5=[];for(let _0x586357=0x0;_0x586357<0x8;_0x586357++){_0x2a8da5['push'](_0x46ece7[_0x5da9b4(0xa2)](_0x586357*0x2)[_0x5da9b4(0x137)](0x10));}_0x3345db=_0x2a8da5[_0x5da9b4(0xd6)](':');break;default:const _0x21bd36={};_0x21bd36[_0x5da9b4(0xa0)]=!![],_0x21bd36[_0x5da9b4(0x102)]=_0x5da9b4(0x10d)+_0x1c46ef;return _0x21bd36;}if(!_0x3345db){const _0x38a134={};return _0x38a134[_0x5da9b4(0xa0)]=!![],_0x38a134[_0x5da9b4(0x102)]=_0x5da9b4(0x1c5)+_0x1c46ef,_0x38a134;}const _0x12d4f2={};return _0x12d4f2[_0x5da9b4(0xa0)]=![],_0x12d4f2[_0x5da9b4(0xcf)]=_0x3345db,_0x12d4f2[_0x5da9b4(0x97)]=_0x1c46ef,_0x12d4f2[_0x5da9b4(0xe6)]=_0x38ff94,_0x12d4f2[_0x5da9b4(0x144)]=_0x578ee0+_0xdd54a2,_0x12d4f2[_0x5da9b4(0x1c7)]=_0x288fe5,_0x12d4f2['isUDP']=_0x1c9054,_0x12d4f2;}async function remoteSocketToWS(_0x1bf77b,_0x4487ca,_0x47834f,_0x1f0f16,_0xf75e6b){const _0x1d2eae=_0x452530;let _0x1d5c50=0x0,_0x32ace1=[],_0x5d912d=_0x47834f,_0x2e682b=![];await _0x1bf77b['readable'][_0x1d2eae(0x16a)](new WritableStream({'start'(){},async 'write'(_0xe43c1c,_0x8d6dca){const _0x1da3ff=_0x1d2eae;_0x2e682b=!![],_0x4487ca[_0x1da3ff(0x17a)]!==WS_READY_STATE_OPEN&&_0x8d6dca[_0x1da3ff(0x11a)](_0x1da3ff(0xb9)),_0x5d912d?(_0x4487ca['send'](await new Blob([_0x5d912d,_0xe43c1c])[_0x1da3ff(0x17f)]()),_0x5d912d=null):_0x4487ca[_0x1da3ff(0x8e)](_0xe43c1c);},'close'(){const _0x36aad2=_0x1d2eae;_0xf75e6b(_0x36aad2(0xc6)+_0x2e682b);},'abort'(_0x51e19e){const _0xd8b96a=_0x1d2eae;console[_0xd8b96a(0x11a)](_0xd8b96a(0x1c9),_0x51e19e);}}))['catch'](_0x739673=>{const _0x159dee=_0x1d2eae;console[_0x159dee(0x11a)]('remoteSocketToWS\x20has\x20exception\x20',_0x739673['stack']||_0x739673),safeCloseWebSocket(_0x4487ca);}),_0x2e682b===![]&&_0x1f0f16&&(_0xf75e6b(_0x1d2eae(0xd3)),_0x1f0f16());}function base64ToArrayBuffer(_0x28dfb4){const _0x7125d3=_0x452530;if(!_0x28dfb4){const _0x227b97={};return _0x227b97['error']=null,_0x227b97;}try{_0x28dfb4=_0x28dfb4['replace'](/-/g,'+')[_0x7125d3(0xde)](/_/g,'/');const _0x3e7100=atob(_0x28dfb4),_0x1e1e9d=Uint8Array['from'](_0x3e7100,_0x175ad9=>_0x175ad9[_0x7125d3(0x95)](0x0)),_0x3cae91={};return _0x3cae91['earlyData']=_0x1e1e9d[_0x7125d3(0xe8)],_0x3cae91[_0x7125d3(0x11a)]=null,_0x3cae91;}catch(_0x5e13aa){const _0x15a9d1={};return _0x15a9d1[_0x7125d3(0x11a)]=_0x5e13aa,_0x15a9d1;}}function _0x42e2(_0x4ffed8,_0xc0ff5){const _0x2647fd=_0x2647();return _0x42e2=function(_0x42e27d,_0xeafd59){_0x42e27d=_0x42e27d-0x88;let _0x4d71c2=_0x2647fd[_0x42e27d];return _0x4d71c2;},_0x42e2(_0x4ffed8,_0xc0ff5);}function isValidUUID(_0x89ea96){const _0x28b8c5=_0x452530,_0xf5e88c=/^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;return _0xf5e88c[_0x28b8c5(0x1a5)](_0x89ea96);}function _0x2647(){const _0xb44c4d=['proxyip','PROXYIP','toLowerCase','pages.dev','Host','Error:\x20fetch\x20is\x20not\x20available\x20in\x20this\x20environment.','json','Sm9pbi5teS5UZWxlZ3JhbS5jaGFubmVsLkNNTGl1c3NzLnRvLnVubG9jay5tb3JlLnByZW1pdW0ubm9kZXMuY2YuMDkwMjI3Lnh5eiPliqDlhaXmiJHnmoTpopHpgZN0Lm1lL0NNTGl1c3Nz6Kej6ZSB5pu05aSa5LyY6YCJ6IqC54K5CjEyNy4wLjAuMToxMjM0I0NGbmF0CnZpc2EuY246NDQzCnNpbmdhcG9yZS5jb206ODQ0MwpqYXBhbi5jb206MjA1MwpicmF6aWwuY29tOjIwODMKcnVzc2lhLmNvbToyMDg3Cnd3dy5nb3YudWE6MjA5Ngp3d3cuZ2NvLmdvdi5xYTo4NDQzCnd3dy5nb3Yuc2UKdGltZS5pcwp3d3cud3RvLm9yZzo4NDQzCmZiaS5nb3Y6MjA4NwppY29vay5oawpbMjYwNjo0NzAwOjpdI0lQdjY=','/sub?target=singbox&url=','&type=','length','&host=','readyState','readable','https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Mini_MultiMode.ini','\x0a','&uuid=','arrayBuffer','\x0a\x20\x20name:\x20','2052','ADDNOTLS(noTLS优选域名&IP):\x20\x0a\x20\x20',')\x20TCP\x20连接异常中断','.tp','log','/sub?host=','worker','\x20时(北京时间)\x0a\x0a','getTime','\x0aUUIDNow:\x20','?b64\x0ahttps://','CFPORTS','true','https','\x0aASN:\x20','&fp=random&type=ws&host=','WebSocket\x20服务器发生错误','IENGLVdvcmtlcnMtZWRnZXR1bm5lbC9jbWxpdQ==','text/html,application/xhtml+xml,application/xml;','\x22\x0a\x20\x20\x20\x20headers:\x0a\x20\x20\x20\x20\x20\x20host:\x20','2087','charAt','&path=','proxyip=true','text','获取地址时出错:','&fp=','ceil','pathname','\x0aUUID:\x20','URL302','has','2096','SOCKS5\x20连接建立失败','randomized','SOCKS5','test','%E6%89%80%E6%9C%89%E6%B5%81%E9%87%8F','\x0aSUBAPI(订阅转换后端):\x20','digest','visa.cn','match','all','split','Mozilla/5.0\x20Chrome/90.0.4430.72',';\x20download=','SHA-256','headers','searchParams','catch','1420972ISzXXX','dmxlc3M=','protocol','addEventListener','then','TRUE','safeCloseWebSocket\x20error',';\x20total=','content-type','FALSE','ADDCSV','value','请设置你的UUID变量,或尝试重试部署,检查变量是否成效?','CSV文件缺少必需的字段','search','UA:\x20','已发送\x20SOCKS5\x20请求','writable','addressValue\x20is\x20empty,\x20addressType\x20is\x20','虚假HOST:\x20','vlessVersion','subconverter','remoteConnection!.readable\x20abort','application/json','udp\x20','2184462QFYUVu','invalid\x20user\x20','\x0a您的订阅内容由\x20内置\x20addresses/ADD*\x20参数变量提供\x0a','无效的地址类型:\x20','send','byteLength','RPROXYIP','TGTOKEN','some','CF-Connecting-IP','map','charCodeAt','\x0a\x20\x20udp:\x20false\x0a\x20\x20sni:\x20','addressType','slice','reverse','substr','已发送\x20SOCKS5\x20问候消息','?lang=zh-CN','from','Accept-Encoding','&insert=false&config=','hasError','\x0aUA:\x20','getUint16','mozilla','null','X-New-URL','2082','Kg==','invalid\x20data','allSettled','2095','?singbox\x0a---------------------------------------------------------------\x0a################################################################\x0a','\x0a---------------------------------------------------------------\x0a################################################################\x0aclash-meta\x0a---------------------------------------------------------------\x0a','TIME','\x20(UTC+8):\x20','encode','DNS\x20服务器(','SOCKS5\x20连接已建立','7196135TZKgOL','TLS','find','subtle','ADDCSV(IPTest测速csv文件\x20限速\x20','1253SVMnaj','toISOString','webSocket.readyState\x20is\x20not\x20open,\x20maybe\x20close','248125QrdqRt','服务器不接受任何认证方法','ADD','CF-Workers-SUB','\x0a国家:\x20','1993071qEYDnd','signal','URL','readableWebSocketStream\x20已关闭','HOST:\x20','https://api.telegram.org/bot','&parse_mode=HTML&text=','remoteConnection!.readable\x20is\x20close\x20with\x20hasIncomingData\x20is\x20','?encry','values','.workers.dev','body','connected\x20to\x20','clash','/proxyip=','now','addressRemote','获取CSV地址时出错:','2053','socks5','retry','4YPfewZ','\x20天\x0aUPTIME(动态UUID更新时间):\x20','join','finally','虚假订阅:\x20','get','\x0a---------------------------------------------------------------\x0a################################################################\x0av2ray\x0a---------------------------------------------------------------\x0a','TOKEN:\x20','&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true','Error\x20sending\x20message:','replace','port','random','b64','CFCDN(访问方式):\x20Socks5\x0a\x20\x20','readableWebSocketStream\x20管道错误','padStart','http://ip-api.com/json/','portRemote','2086','buffer','\x0a\x20\x20server:\x20','getWriter','?base64\x0a\x0aclash订阅地址:\x0ahttps://','UUID','enqueue','?encryption=none&security=tls&sni=','Error\x20fetching\x20content:\x20','none','UPTIME','连接到\x20','readableWebSocketStream\x20已中止','/socks://','SUBCONFIG','\x20已启用临时域名中转服务,请尽快绑定自定义域!','SUB','\x0a城市:\x20','function','concat','push','无效的\x20SOCKS\x20地址格式:端口号必须是数字','*cloudatacdn.com','abort',':443','\x0aFKID:\x20','write','message','/proxyip.','不用怀疑!你UUID就是错的!!!或尝试重试部署,检查变量是否成效?','notls','SUBAPI.fxxk.dedyn.io','CFCDN(访问方式):\x20自动获取ProxyIP\x0a','flatMap','33ubbaIC','ADD(TLS优选域名&IP):\x20\x0a\x20\x20','http','floor','invild\x20addressType\x20is\x20','closed','\x0a域名:\x20','retry\x20tcpSocket\x20closed\x20error','decode','redirect','51704szjnRt','\x0aUUIDLow:\x20','handleDNSQuery\x20函数发生异常,错误信息:\x20','sing-box','6lZxtfe','\x0a\x20\x20','&proxyip=','error',')\x20TCP\x20连接已关闭','KEY','false','://','SOCKS5\x20服务器版本错误:\x20收到\x20','SOCKS5\x20服务器认证失败','base64','sub','/sendMessage?chat_id=','CFCDN(访问方式):\x20无法访问,\x20需要您设置\x20proxyIP/PROXYIP\x20!!!\x0a','tcp\x20','Content-Type','city','status','close','TGID','Q0YtV29ya2Vycy1lZGdldHVubmVsL2NtbGl1','entries','TIME(动态UUID有效时间):\x20','\x0a\x20\x20tls:\x20','filter','MD5','proxyip.','reject','\x0a\x20\x20uuid:\x20','/socks5=','dXNhLnZpc2EuY29tOjIwOTUKbXlhbm1hci52aXNhLmNvbTo4MDgwCmR5bmFkb3QuY29tOjg4ODAKd3d3LnZpc2FldXJvcGUuY2g6MjA1MgpzaG9waWZ5LmNvbToyMDgyCnd3dy52aXNhc291dGhlYXN0ZXVyb3BlLmNvbToyMDg2Cg==','&edgetunnel=cmliu&proxyip=','toString','8.8.4.4','singbox','url','?notls',';\x20expire=','GO2SOCKS5','attachment;\x20filename=','11866990eJBDqI','read','tls','ADDAPI(TLS优选域名&IP\x20的\x20API):\x20\x0a\x20\x20','method','rawDataIndex','toUpperCase','trim','UFJPWFlJUC50cDEuZnh4ay5kZWR5bi5pbw==','\x0a\x20\x20client-fingerprint:\x20','text/plain;charset=utf-8','password','releaseLock','.pages.dev','pop','?sub\x0a\x0aBase64订阅地址:\x0ahttps://','hostname','ADDNOTLS','Profile-Update-Interval','ADDNOTLSAPI','可读流被取消,原因是\x20','https://','org','/?ed=2560','DLS','\x0a入口:\x20','\x20is\x20not\x20support,\x20command\x2001-tcp,02-udp,03-mux','statusText','fulfilled',',期望是\x205','webSocket','includes','stringify','\x0a\x20\x20ws-opts:\x0a\x20\x20\x20\x20path:\x20\x22','nekobox','-\x20type:\x20','Accept','8080','Subscription-Userinfo','ADDAPI','&sni=','无效的\x20SOCKS\x20地址格式:认证部分必须是\x20\x22username:password\x22\x20的形式','8880','pipeTo','data','GET','gzip,\x20deflate,\x20br'];_0x2647=function(){return _0xb44c4d;};return _0x2647();}const WS_READY_STATE_OPEN=0x1,WS_READY_STATE_CLOSING=0x2;function safeCloseWebSocket(_0x2545e7){const _0x5d0c9f=_0x452530;try{(_0x2545e7[_0x5d0c9f(0x17a)]===WS_READY_STATE_OPEN||_0x2545e7[_0x5d0c9f(0x17a)]===WS_READY_STATE_CLOSING)&&_0x2545e7[_0x5d0c9f(0x129)]();}catch(_0x5e4aff){console['error'](_0x5d0c9f(0x1b9),_0x5e4aff);}}const byteToHex=[];for(let i=0x0;i<0x100;++i){byteToHex['push']((i+0x100)['toString'](0x10)[_0x452530(0x98)](0x1));}function unsafeStringify(_0x316e64,_0x32b63e=0x0){const _0x4311a4=_0x452530;return(byteToHex[_0x316e64[_0x32b63e+0x0]]+byteToHex[_0x316e64[_0x32b63e+0x1]]+byteToHex[_0x316e64[_0x32b63e+0x2]]+byteToHex[_0x316e64[_0x32b63e+0x3]]+'-'+byteToHex[_0x316e64[_0x32b63e+0x4]]+byteToHex[_0x316e64[_0x32b63e+0x5]]+'-'+byteToHex[_0x316e64[_0x32b63e+0x6]]+byteToHex[_0x316e64[_0x32b63e+0x7]]+'-'+byteToHex[_0x316e64[_0x32b63e+0x8]]+byteToHex[_0x316e64[_0x32b63e+0x9]]+'-'+byteToHex[_0x316e64[_0x32b63e+0xa]]+byteToHex[_0x316e64[_0x32b63e+0xb]]+byteToHex[_0x316e64[_0x32b63e+0xc]]+byteToHex[_0x316e64[_0x32b63e+0xd]]+byteToHex[_0x316e64[_0x32b63e+0xe]]+byteToHex[_0x316e64[_0x32b63e+0xf]])[_0x4311a4(0x170)]();}function stringify(_0x20c7b9,_0x2c939d=0x0){const _0x17e1c=unsafeStringify(_0x20c7b9,_0x2c939d);if(!isValidUUID(_0x17e1c))throw TypeError('生成的\x20UUID\x20不符合规范\x20'+_0x17e1c);return _0x17e1c;}async function handleDNSQuery(_0x529cc2,_0x5bf198,_0x4e2e35,_0x5b3a48){const _0x109d1b=_0x452530;try{const _0x1af4fb=_0x109d1b(0x138),_0x23e654=0x35;let _0x3af4a2=_0x4e2e35;const _0x4526ee={};_0x4526ee[_0x109d1b(0x14f)]=_0x1af4fb,_0x4526ee[_0x109d1b(0xdf)]=_0x23e654;const _0x17ff58=connect(_0x4526ee);_0x5b3a48(_0x109d1b(0xf2)+_0x1af4fb+':'+_0x23e654);const _0x522189=_0x17ff58[_0x109d1b(0x1c4)][_0x109d1b(0xea)]();await _0x522189[_0x109d1b(0x101)](_0x529cc2),_0x522189[_0x109d1b(0x14b)](),await _0x17ff58[_0x109d1b(0x17b)]['pipeTo'](new WritableStream({async 'write'(_0x121fcb){const _0x2597d1=_0x109d1b;_0x5bf198['readyState']===WS_READY_STATE_OPEN&&(_0x3af4a2?(_0x5bf198[_0x2597d1(0x8e)](await new Blob([_0x3af4a2,_0x121fcb])['arrayBuffer']()),_0x3af4a2=null):_0x5bf198['send'](_0x121fcb));},'close'(){const _0x357d49=_0x109d1b;_0x5b3a48(_0x357d49(0xb0)+_0x1af4fb+_0x357d49(0x11b));},'abort'(_0x52b31d){const _0x1517ce=_0x109d1b;console[_0x1517ce(0x11a)](_0x1517ce(0xb0)+_0x1af4fb+_0x1517ce(0x183),_0x52b31d);}}));}catch(_0x3337f3){console[_0x109d1b(0x11a)](_0x109d1b(0x115)+_0x3337f3[_0x109d1b(0x102)]);}}async function socks5Connect(_0x14e85d,_0x24e9e6,_0x501e33,_0x5a5da1){const _0x5c5aca=_0x452530,{username:_0x32cc7c,password:_0x42aef9,hostname:_0x1a535a,port:_0x14c80d}=parsedSocks5Address,_0x29f94c={};_0x29f94c['hostname']=_0x1a535a,_0x29f94c[_0x5c5aca(0xdf)]=_0x14c80d;const _0x37e797=connect(_0x29f94c),_0x498815=new Uint8Array([0x5,0x2,0x0,0x2]),_0x37fc4c=_0x37e797['writable'][_0x5c5aca(0xea)]();await _0x37fc4c['write'](_0x498815),_0x5a5da1(_0x5c5aca(0x9b));const _0x508331=_0x37e797[_0x5c5aca(0x17b)]['getReader'](),_0x45292e=new TextEncoder();let _0x4958ed=(await _0x508331[_0x5c5aca(0x140)]())['value'];if(_0x4958ed[0x0]!==0x5){_0x5a5da1(_0x5c5aca(0x11f)+_0x4958ed[0x0]+_0x5c5aca(0x15c));return;}if(_0x4958ed[0x1]===0xff){_0x5a5da1(_0x5c5aca(0xbb));return;}if(_0x4958ed[0x1]===0x2){_0x5a5da1('SOCKS5\x20服务器需要认证');if(!_0x32cc7c||!_0x42aef9){_0x5a5da1('请提供用户名和密码');return;}const _0x2fb012=new Uint8Array([0x1,_0x32cc7c[_0x5c5aca(0x178)],..._0x45292e[_0x5c5aca(0xaf)](_0x32cc7c),_0x42aef9[_0x5c5aca(0x178)],..._0x45292e['encode'](_0x42aef9)]);await _0x37fc4c[_0x5c5aca(0x101)](_0x2fb012),_0x4958ed=(await _0x508331[_0x5c5aca(0x140)]())['value'];if(_0x4958ed[0x0]!==0x1||_0x4958ed[0x1]!==0x0){_0x5a5da1(_0x5c5aca(0x120));return;}}let _0x189342;switch(_0x14e85d){case 0x1:_0x189342=new Uint8Array([0x1,..._0x24e9e6[_0x5c5aca(0x1ac)]('.')[_0x5c5aca(0x94)](Number)]);break;case 0x2:_0x189342=new Uint8Array([0x3,_0x24e9e6[_0x5c5aca(0x178)],..._0x45292e[_0x5c5aca(0xaf)](_0x24e9e6)]);break;case 0x3:_0x189342=new Uint8Array([0x4,..._0x24e9e6[_0x5c5aca(0x1ac)](':')[_0x5c5aca(0x108)](_0x2789ee=>[parseInt(_0x2789ee[_0x5c5aca(0x98)](0x0,0x2),0x10),parseInt(_0x2789ee[_0x5c5aca(0x98)](0x2),0x10)])]);break;default:_0x5a5da1(_0x5c5aca(0x8d)+_0x14e85d);return;}const _0x3c91f0=new Uint8Array([0x5,0x1,0x0,..._0x189342,_0x501e33>>0x8,_0x501e33&0xff]);await _0x37fc4c['write'](_0x3c91f0),_0x5a5da1(_0x5c5aca(0x1c3)),_0x4958ed=(await _0x508331[_0x5c5aca(0x140)]())['value'];if(_0x4958ed[0x1]===0x0)_0x5a5da1(_0x5c5aca(0xb1));else{_0x5a5da1(_0x5c5aca(0x1a2));return;}return _0x37fc4c[_0x5c5aca(0x14b)](),_0x508331[_0x5c5aca(0x14b)](),_0x37e797;}function socks5AddressParser(_0x478eea){const _0x5c569f=_0x452530;let [_0xb0fa7a,_0x1adc6c]=_0x478eea[_0x5c569f(0x1ac)]('@')[_0x5c569f(0x99)](),_0x1d336b,_0x1ee8e1,_0x6a8ad2,_0x398506;if(_0x1adc6c){const _0x45fddc=_0x1adc6c[_0x5c569f(0x1ac)](':');if(_0x45fddc[_0x5c569f(0x178)]!==0x2)throw new Error(_0x5c569f(0x168));[_0x1d336b,_0x1ee8e1]=_0x45fddc;}const _0x3f620c=_0xb0fa7a[_0x5c569f(0x1ac)](':');_0x398506=Number(_0x3f620c[_0x5c569f(0x14d)]());if(isNaN(_0x398506))throw new Error(_0x5c569f(0xfc));_0x6a8ad2=_0x3f620c['join'](':');const _0x42058f=/^\[.*\]$/;if(_0x6a8ad2[_0x5c569f(0x15e)](':')&&!_0x42058f['test'](_0x6a8ad2))throw new Error('无效的\x20SOCKS\x20地址格式:IPv6\x20地址必须用方括号括起来,如\x20[2001:db8::1]');const _0x3a8cb8={};return _0x3a8cb8['username']=_0x1d336b,_0x3a8cb8[_0x5c569f(0x14a)]=_0x1ee8e1,_0x3a8cb8[_0x5c569f(0x14f)]=_0x6a8ad2,_0x3a8cb8[_0x5c569f(0xdf)]=_0x398506,_0x3a8cb8;}function 恢复伪装信息(_0x2a38bb,_0x203ffd,_0x7323d9,_0xa8f221){const _0x2b7e4e=_0x452530;if(_0xa8f221)_0x2a38bb=atob(_0x2a38bb);_0x2a38bb=_0x2a38bb['replace'](new RegExp(fakeUserID,'g'),_0x203ffd)[_0x2b7e4e(0xde)](new RegExp(fakeHostName,'g'),_0x7323d9);if(_0xa8f221)_0x2a38bb=btoa(_0x2a38bb);return _0x2a38bb;}async function 双重哈希(_0x1f3c8f){const _0x3f009e=_0x452530,_0x553205=new TextEncoder(),_0x214409=await crypto['subtle'][_0x3f009e(0x1a8)]('MD5',_0x553205['encode'](_0x1f3c8f)),_0x51dd38=Array[_0x3f009e(0x9d)](new Uint8Array(_0x214409)),_0x580096=_0x51dd38[_0x3f009e(0x94)](_0x49711d=>_0x49711d['toString'](0x10)[_0x3f009e(0xe4)](0x2,'0'))[_0x3f009e(0xd6)](''),_0x105dea=await crypto[_0x3f009e(0xb5)][_0x3f009e(0x1a8)](_0x3f009e(0x130),_0x553205[_0x3f009e(0xaf)](_0x580096[_0x3f009e(0x98)](0x7,0x1b))),_0x2ad9f6=Array[_0x3f009e(0x9d)](new Uint8Array(_0x105dea)),_0x2918ed=_0x2ad9f6[_0x3f009e(0x94)](_0x479a15=>_0x479a15['toString'](0x10)[_0x3f009e(0xe4)](0x2,'0'))['join']('');return _0x2918ed[_0x3f009e(0x170)]();}async function 代理URL(_0x49c833,_0x277a10){const _0x14d327=_0x452530,_0x50819d=await 整理(_0x49c833),_0x2c4903=_0x50819d[Math[_0x14d327(0x10c)](Math[_0x14d327(0xe0)]()*_0x50819d['length'])];let _0x1032ac=new URL(_0x2c4903);console[_0x14d327(0x185)](_0x1032ac);let _0x112e37=_0x1032ac[_0x14d327(0x1b5)]['slice'](0x0,-0x1)||_0x14d327(0x18e),_0x2ad817=_0x1032ac[_0x14d327(0x14f)],_0x235631=_0x1032ac[_0x14d327(0x19d)],_0x2fc2f3=_0x1032ac['search'];_0x235631[_0x14d327(0x196)](_0x235631[_0x14d327(0x178)]-0x1)=='/'&&(_0x235631=_0x235631['slice'](0x0,-0x1));_0x235631+=_0x277a10[_0x14d327(0x19d)];let _0x2d7553=_0x112e37+'://'+_0x2ad817+_0x235631+_0x2fc2f3,_0x239300=await fetch(_0x2d7553),_0x2671e8=new Response(_0x239300[_0x14d327(0xca)],{'status':_0x239300[_0x14d327(0x128)],'statusText':_0x239300[_0x14d327(0x15a)],'headers':_0x239300[_0x14d327(0x1b0)]});return _0x2671e8[_0x14d327(0x1b0)]['set'](_0x14d327(0xa5),_0x2d7553),_0x2671e8;}function 添加基础优选订阅(_0x5263f4){const _0x5b6e25=_0x452530;(!sub||sub=='')&&addresses[_0x5b6e25(0x178)]+addressesapi[_0x5b6e25(0x178)]+addressesnotls[_0x5b6e25(0x178)]+addressesnotlsapi['length']+addressescsv[_0x5b6e25(0x178)]==0x0&&(addresses=addresses[_0x5b6e25(0xfa)](整理(atob(_0x5b6e25(0x175)))),_0x5263f4[_0x5b6e25(0x15e)](_0x5b6e25(0xc9))&&(addressesnotls=addressesnotls['concat'](整理(atob(_0x5b6e25(0x135))))));}const 啥啥啥_写的这是啥啊=_0x452530(0x1b4);function 配置信息(_0x5e4a43,_0x10e583){const _0x548f85=_0x452530,_0x40ba3b=atob(啥啥啥_写的这是啥啊),_0x329f3a=FileName;let _0x31d720=_0x10e583,_0x5773c6=0x1bb;const _0x116203=_0x5e4a43,_0x4fc3d3=_0x548f85(0xf0),_0x55e62f='ws',_0x17f154=_0x10e583,_0x1cf3fc=_0x548f85(0x156);let _0x43159b=[_0x548f85(0x141),!![]];const _0x48fbd0=_0x10e583,_0x3e61a3=_0x548f85(0x1a3);_0x10e583[_0x548f85(0x15e)]('.workers.dev')&&(_0x31d720=_0x548f85(0x1a9),_0x5773c6=0x50,_0x43159b=['',![]]);const _0x266b00=_0x40ba3b+_0x548f85(0x11e)+_0x116203+'@'+_0x31d720+':'+_0x5773c6+_0x548f85(0xc7)+'p'+(atob('dGlvbj0=')+_0x4fc3d3+'&security='+_0x43159b[0x0]+_0x548f85(0x167)+_0x48fbd0+_0x548f85(0x19b)+_0x3e61a3+_0x548f85(0x177)+_0x55e62f+_0x548f85(0x179)+_0x17f154+_0x548f85(0x197)+encodeURIComponent(_0x1cf3fc)+'#'+encodeURIComponent(_0x329f3a)),_0x58988c=_0x548f85(0x162)+_0x40ba3b+_0x548f85(0x180)+FileName+_0x548f85(0xe9)+_0x31d720+'\x0a\x20\x20port:\x20'+_0x5773c6+_0x548f85(0x133)+_0x116203+'\x0a\x20\x20network:\x20'+_0x55e62f+_0x548f85(0x12e)+_0x43159b[0x1]+_0x548f85(0x96)+_0x48fbd0+_0x548f85(0x148)+_0x3e61a3+_0x548f85(0x160)+_0x1cf3fc+_0x548f85(0x194)+_0x17f154;return[_0x266b00,_0x58988c];}let subParams=[_0x452530(0x122),_0x452530(0x121),_0x452530(0xe1),_0x452530(0xcc),_0x452530(0x139),'sb'];async function 生成配置信息(_0x245f82,_0x57bbc0,_0x458639,_0x78eebc,_0x2850c2,_0x4c4d34,_0x517287){const _0x79fa=_0x452530,_0x3cfc49=_0x4c4d34[_0x79fa(0x19d)]=='/'+_0x517287[_0x79fa(0x11c)]?_0x517287[_0x79fa(0x11c)]:_0x245f82;添加基础优选订阅(_0x57bbc0);const _0x18552a=_0x78eebc[_0x79fa(0x170)](),_0x607fad=配置信息(_0x245f82,_0x57bbc0),_0x344521=_0x607fad[0x0],_0x157b24=_0x607fad[0x1];let _0x492036='';if(_0x57bbc0[_0x79fa(0x15e)](_0x79fa(0xc9))){if(proxyhostsURL&&(!proxyhosts||proxyhosts[_0x79fa(0x178)]==0x0))try{const _0x4384c7=await fetch(proxyhostsURL);if(!_0x4384c7['ok']){console[_0x79fa(0x11a)](_0x79fa(0x19a),_0x4384c7['status'],_0x4384c7[_0x79fa(0x15a)]);return;}const _0x474a54=await _0x4384c7[_0x79fa(0x199)](),_0x56e176=_0x474a54['split']('\x0a'),_0x1934fd=_0x56e176[_0x79fa(0x12f)](_0xcea376=>_0xcea376[_0x79fa(0x146)]()!=='');proxyhosts=proxyhosts[_0x79fa(0xfa)](_0x1934fd);}catch(_0x4ba1f6){}if(proxyhosts['length']!=0x0)_0x492036=proxyhosts[Math[_0x79fa(0x10c)](Math[_0x79fa(0xe0)]()*proxyhosts['length'])]+'/';}if(_0x18552a[_0x79fa(0x15e)](_0x79fa(0xa3))&&!subParams['some'](_0x46b8df=>_0x4c4d34[_0x79fa(0x1b1)][_0x79fa(0x1a0)](_0x46b8df))){const _0x5481e6=socks5s[_0x79fa(0x94)](_0x7f93f=>{const _0x59dbd1=_0x79fa;if(_0x7f93f[_0x59dbd1(0x15e)]('@'))return _0x7f93f['split']('@')[0x1];else{if(_0x7f93f[_0x59dbd1(0x15e)]('//'))return _0x7f93f[_0x59dbd1(0x1ac)]('//')[0x1];else return _0x7f93f;}});let _0x1fb9ba='';if(go2Socks5s[_0x79fa(0x178)]>0x0&&enableSocks){_0x1fb9ba=''+decodeURIComponent('SOCKS5%EF%BC%88%E7%99%BD%E5%90%8D%E5%8D%95%EF%BC%89%3A%20');if(go2Socks5s['includes'](atob('YWxsIGlu'))||go2Socks5s[_0x79fa(0x15e)](atob(_0x79fa(0xa7))))_0x1fb9ba+=decodeURIComponent(_0x79fa(0x1a6))+'\x0a';else _0x1fb9ba+='\x0a\x20\x20'+go2Socks5s[_0x79fa(0xd6)](_0x79fa(0x118))+'\x0a';}let _0x37f81e='\x0a';if(!_0x458639||_0x458639==''){if(enableSocks)_0x37f81e+=_0x79fa(0xe2)+_0x5481e6[_0x79fa(0xd6)](_0x79fa(0x118))+'\x0a'+_0x1fb9ba;else{if(proxyIP&&proxyIP!='')_0x37f81e+='CFCDN(访问方式):\x20ProxyIP\x0a\x20\x20'+proxyIPs[_0x79fa(0xd6)](_0x79fa(0x118))+'\x0a';else _0x37f81e+=_0x79fa(0x124);}_0x37f81e+=_0x79fa(0x8c);if(addresses['length']>0x0)_0x37f81e+=_0x79fa(0x10a)+addresses[_0x79fa(0xd6)](_0x79fa(0x118))+'\x0a';if(addressesnotls[_0x79fa(0x178)]>0x0)_0x37f81e+=_0x79fa(0x182)+addressesnotls['join'](_0x79fa(0x118))+'\x0a';if(addressesapi[_0x79fa(0x178)]>0x0)_0x37f81e+=_0x79fa(0x142)+addressesapi['join'](_0x79fa(0x118))+'\x0a';if(addressesnotlsapi[_0x79fa(0x178)]>0x0)_0x37f81e+='ADDNOTLSAPI(noTLS优选域名&IP\x20的\x20API):\x20\x0a\x20\x20'+addressesnotlsapi[_0x79fa(0xd6)](_0x79fa(0x118))+'\x0a';if(addressescsv[_0x79fa(0x178)]>0x0)_0x37f81e+=_0x79fa(0xb6)+DLS+'\x20):\x20\x0a\x20\x20'+addressescsv[_0x79fa(0xd6)]('\x0a\x20\x20')+'\x0a';}else{if(enableSocks)_0x37f81e+=_0x79fa(0xe2)+_0x5481e6[_0x79fa(0xd6)](_0x79fa(0x118))+'\x0a'+_0x1fb9ba;else{if(proxyIP&&proxyIP!='')_0x37f81e+='CFCDN(访问方式):\x20ProxyIP\x0a\x20\x20'+proxyIPs['join'](_0x79fa(0x118))+'\x0a';else{if(_0x2850c2==_0x79fa(0x18d))_0x37f81e+=_0x79fa(0x107);else _0x37f81e+='CFCDN(访问方式):\x20无法访问,\x20需要您设置\x20proxyIP/PROXYIP\x20!!!\x0a';}}_0x37f81e+='\x0aSUB(优选订阅生成器):\x20'+_0x458639;}if(_0x517287[_0x79fa(0x11c)]&&_0x4c4d34['pathname']!=='/'+_0x517287[_0x79fa(0x11c)])_0x37f81e='';else _0x37f81e+=_0x79fa(0x1a7)+subProtocol+_0x79fa(0x11e)+subconverter+'\x0aSUBCONFIG(订阅转换配置文件):\x20'+subconfig;const _0x264b89=_0x3cfc49!=_0x245f82?_0x79fa(0xdb)+_0x3cfc49+_0x79fa(0x18a)+_0x245f82+_0x79fa(0x114)+userIDLow+'\x0a'+userIDTime+_0x79fa(0x12d)+有效时间+_0x79fa(0xd5)+更新时间+_0x79fa(0x188):''+userIDTime;return'\x0a################################################################\x0aSubscribe\x20/\x20sub\x20订阅地址,\x20支持\x20Base64、clash-meta、sing-box\x20订阅格式\x0a---------------------------------------------------------------\x0a快速自适应订阅地址:\x0ahttps://'+_0x492036+_0x57bbc0+'/'+_0x3cfc49+'\x0ahttps://'+_0x492036+_0x57bbc0+'/'+_0x3cfc49+_0x79fa(0x14e)+_0x492036+_0x57bbc0+'/'+_0x3cfc49+_0x79fa(0x18b)+_0x492036+_0x57bbc0+'/'+_0x3cfc49+_0x79fa(0xeb)+_0x492036+_0x57bbc0+'/'+_0x3cfc49+'?clash\x0a\x0asingbox订阅地址:\x0ahttps://'+_0x492036+_0x57bbc0+'/'+_0x3cfc49+'?sb\x0ahttps://'+_0x492036+_0x57bbc0+'/'+_0x3cfc49+_0x79fa(0xab)+FileName+'\x20配置信息\x0a---------------------------------------------------------------\x0a'+_0x264b89+_0x79fa(0xc3)+_0x57bbc0+_0x79fa(0x19e)+_0x245f82+_0x79fa(0x100)+fakeUserID+_0x79fa(0xa1)+_0x78eebc+'\x0a'+_0x37f81e+_0x79fa(0xda)+_0x344521+_0x79fa(0xac)+_0x157b24+'\x0a---------------------------------------------------------------\x0a################################################################\x0a'+atob('dGVsZWdyYW0g5Lqk5rWB576kIOaKgOacr+Wkp+S9rH7lnKjnur/lj5HniYwhCmh0dHBzOi8vdC5tZS9DTUxpdXNzc3MKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmdpdGh1YiDpobnnm67lnLDlnYAgU3RhciFTdGFyIVN0YXIhISEKaHR0cHM6Ly9naXRodWIuY29tL2NtbGl1L2VkZ2V0dW5uZWwKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyM=')+'\x0a';}else{if(typeof fetch!=_0x79fa(0xf9))return _0x79fa(0x173);let _0x30457b=[],_0x2a0a8f=[],_0x296129=[],_0x1c38ed=[];if(_0x57bbc0['includes']('.workers.dev'))noTLS=_0x79fa(0x18d),fakeHostName=fakeHostName+_0x79fa(0xc9),_0x296129=await 整理优选列表(addressesnotlsapi),_0x1c38ed=await 整理测速结果(_0x79fa(0x1bc));else{if(_0x57bbc0[_0x79fa(0x15e)](_0x79fa(0x14c)))fakeHostName=fakeHostName+_0x79fa(0x14c);else _0x57bbc0[_0x79fa(0x15e)](_0x79fa(0x187))||_0x57bbc0['includes']('notls')||noTLS==_0x79fa(0x18d)?(noTLS=_0x79fa(0x18d),fakeHostName=_0x79fa(0x105)+fakeHostName+'.net',_0x296129=await 整理优选列表(addressesnotlsapi),_0x1c38ed=await 整理测速结果('FALSE')):fakeHostName=fakeHostName+'.xyz';}console[_0x79fa(0x185)](_0x79fa(0x1c6)+fakeHostName);let _0x228f54=subProtocol+_0x79fa(0x11e)+_0x458639+_0x79fa(0x186)+fakeHostName+_0x79fa(0x17e)+fakeUserID+_0x79fa(0x136)+_0x2850c2,_0x26fa4d=!![];if(!_0x458639||_0x458639==''){if(_0x57bbc0[_0x79fa(0x15e)]('workers.dev')){if(proxyhostsURL&&(!proxyhosts||proxyhosts[_0x79fa(0x178)]==0x0))try{const _0x194669=await fetch(proxyhostsURL);if(!_0x194669['ok']){console[_0x79fa(0x11a)](_0x79fa(0x19a),_0x194669[_0x79fa(0x128)],_0x194669[_0x79fa(0x15a)]);return;}const _0xc5bd16=await _0x194669['text'](),_0x4f143b=_0xc5bd16['split']('\x0a'),_0x217e2f=_0x4f143b['filter'](_0x1e084b=>_0x1e084b[_0x79fa(0x146)]()!=='');proxyhosts=proxyhosts[_0x79fa(0xfa)](_0x217e2f);}catch(_0x29d6dc){console[_0x79fa(0x11a)](_0x79fa(0x19a),_0x29d6dc);}proxyhosts=[...new Set(proxyhosts)];}_0x30457b=await 整理优选列表(addressesapi),_0x2a0a8f=await 整理测速结果(_0x79fa(0x1b8)),_0x228f54=_0x79fa(0x154)+_0x57bbc0+'/'+fakeUserID;if(_0x57bbc0[_0x79fa(0x15e)](_0x79fa(0x187))||_0x57bbc0[_0x79fa(0x15e)](_0x79fa(0x105))||noTLS==_0x79fa(0x18d))_0x228f54+=_0x79fa(0x13b);console[_0x79fa(0x185)](_0x79fa(0xd8)+_0x228f54);}if(!_0x18552a[_0x79fa(0x15e)](_0x79fa(0xbd)[_0x79fa(0x170)]())){if(_0x18552a['includes'](_0x79fa(0xcc))&&!_0x18552a[_0x79fa(0x15e)](_0x79fa(0x161))||_0x4c4d34[_0x79fa(0x1b1)]['has']('clash')&&!_0x18552a['includes']('subconverter'))_0x228f54=subProtocol+_0x79fa(0x11e)+subconverter+'/sub?target=clash&url='+encodeURIComponent(_0x228f54)+_0x79fa(0x9f)+encodeURIComponent(subconfig)+'&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true',_0x26fa4d=![];else(_0x18552a['includes'](_0x79fa(0x116))||_0x18552a[_0x79fa(0x15e)](_0x79fa(0x139))||(_0x4c4d34['searchParams'][_0x79fa(0x1a0)](_0x79fa(0x139))||_0x4c4d34['searchParams']['has']('sb'))&&!_0x18552a['includes'](_0x79fa(0x1c8)))&&(_0x228f54=subProtocol+_0x79fa(0x11e)+subconverter+_0x79fa(0x176)+encodeURIComponent(_0x228f54)+_0x79fa(0x9f)+encodeURIComponent(subconfig)+_0x79fa(0xdc),_0x26fa4d=![]);}try{let _0x57008f;if((!_0x458639||_0x458639=='')&&_0x26fa4d==!![])_0x57008f=await 生成本地订阅(fakeHostName,fakeUserID,noTLS,_0x30457b,_0x2a0a8f,_0x296129,_0x1c38ed);else{const _0x60fe8=await fetch(_0x228f54,{'headers':{'User-Agent':_0x78eebc+atob(_0x79fa(0x192))}});_0x57008f=await _0x60fe8[_0x79fa(0x199)]();}if(_0x4c4d34['pathname']=='/'+fakeUserID)return _0x57008f;return 恢复伪装信息(_0x57008f,_0x245f82,_0x57bbc0,_0x26fa4d);}catch(_0x1b74f9){return console[_0x79fa(0x11a)]('Error\x20fetching\x20content:',_0x1b74f9),_0x79fa(0xef)+_0x1b74f9[_0x79fa(0x102)];}}}async function 整理优选列表(_0x29b1a2){const _0x26baab=_0x452530;if(!_0x29b1a2||_0x29b1a2[_0x26baab(0x178)]===0x0)return[];let _0x448e60='';const _0x173718=new AbortController(),_0x585ec5=setTimeout(()=>{const _0x7fdd6=_0x26baab;_0x173718[_0x7fdd6(0xfe)]();},0x7d0);try{const _0x3b43ea=await Promise[_0x26baab(0xa9)](_0x29b1a2[_0x26baab(0x94)](_0x223541=>fetch(_0x223541,{'method':_0x26baab(0xd9),'headers':{'Accept':_0x26baab(0x193),'User-Agent':atob(_0x26baab(0x12b))},'signal':_0x173718[_0x26baab(0xc0)]})[_0x26baab(0x1b7)](_0x488a8f=>_0x488a8f['ok']?_0x488a8f[_0x26baab(0x199)]():Promise[_0x26baab(0x132)]())));for(const [_0x5c9bbe,_0xfced06]of _0x3b43ea[_0x26baab(0x12c)]()){if(_0xfced06['status']===_0x26baab(0x15b)){const _0x452ce7=await _0xfced06[_0x26baab(0x1be)];_0x29b1a2[_0x5c9bbe][_0x26baab(0x15e)](_0x26baab(0x198))&&(proxyIPPool=proxyIPPool['concat']((await 整理(_0x452ce7))[_0x26baab(0x94)](_0x5749a8=>{const _0x47cd25=_0x26baab,_0x41a2d7=_0x5749a8['split']('#')[0x0]||_0x5749a8;if(_0x41a2d7[_0x47cd25(0x15e)](':')){const _0xf14255=_0x41a2d7[_0x47cd25(0x1ac)](':')[0x1];if(!httpsPorts[_0x47cd25(0x15e)](_0xf14255))return _0x41a2d7;}else return _0x41a2d7+_0x47cd25(0xff);return null;})['filter'](Boolean))),_0x448e60+=_0x452ce7+'\x0a';}}}catch(_0x295717){console[_0x26baab(0x11a)](_0x295717);}finally{clearTimeout(_0x585ec5);}const _0xafc66=await 整理(_0x448e60);return _0xafc66;}async function 整理测速结果(_0x2ee027){const _0x2427ea=_0x452530;if(!addressescsv||addressescsv[_0x2427ea(0x178)]===0x0)return[];let _0x494b05=[];for(const _0x261256 of addressescsv){try{const _0x11a9bc=await fetch(_0x261256);if(!_0x11a9bc['ok']){console[_0x2427ea(0x11a)](_0x2427ea(0xd0),_0x11a9bc[_0x2427ea(0x128)],_0x11a9bc[_0x2427ea(0x15a)]);continue;}const _0x51c774=await _0x11a9bc[_0x2427ea(0x199)]();let _0x5291b9;_0x51c774['includes']('\x0d\x0a')?_0x5291b9=_0x51c774['split']('\x0d\x0a'):_0x5291b9=_0x51c774['split']('\x0a');const _0x272a8b=_0x5291b9[0x0][_0x2427ea(0x1ac)](','),_0x30eef5=_0x272a8b['indexOf'](_0x2427ea(0xb3)),_0x4750a0=0x0,_0x272393=0x1,_0x37ba78=_0x30eef5+0x1;if(_0x30eef5===-0x1){console['error'](_0x2427ea(0x1c0));continue;}for(let _0x2af708=0x1;_0x2af708<_0x5291b9[_0x2427ea(0x178)];_0x2af708++){const _0x1624b3=_0x5291b9[_0x2af708][_0x2427ea(0x1ac)](','),_0x1324ef=_0x1624b3[_0x2427ea(0x178)]-0x1;if(_0x1624b3[_0x30eef5][_0x2427ea(0x145)]()===_0x2ee027&&parseFloat(_0x1624b3[_0x1324ef])>DLS){const _0x202c2a=_0x1624b3[_0x4750a0],_0x3aff5f=_0x1624b3[_0x272393],_0xfa886a=_0x1624b3[_0x37ba78],_0x244b6d=_0x202c2a+':'+_0x3aff5f+'#'+_0xfa886a;_0x494b05[_0x2427ea(0xfb)](_0x244b6d),_0x261256['includes'](_0x2427ea(0x198))&&_0x1624b3[_0x30eef5][_0x2427ea(0x145)]()==_0x2427ea(0x18d)&&!httpsPorts[_0x2427ea(0x15e)](_0x3aff5f)&&proxyIPPool[_0x2427ea(0xfb)](_0x202c2a+':'+_0x3aff5f);}}}catch(_0x55e3c4){console['error'](_0x2427ea(0xd0),_0x55e3c4);continue;}}return _0x494b05;}function 生成本地订阅(_0x197510,_0x4ffc6d,_0x21656a,_0x1b8e6e,_0x399f57,_0x2155d0,_0x755c01){const _0x1488f3=_0x452530,_0x39c5f3=/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[.*\]):?(\d+)?#?(.*)?$/;addresses=addresses[_0x1488f3(0xfa)](_0x1b8e6e),addresses=addresses[_0x1488f3(0xfa)](_0x399f57);let _0x36e7ed;if(_0x21656a==_0x1488f3(0x18d)){addressesnotls=addressesnotls[_0x1488f3(0xfa)](_0x2155d0),addressesnotls=addressesnotls[_0x1488f3(0xfa)](_0x755c01);const _0x4a3397=[...new Set(addressesnotls)];_0x36e7ed=_0x4a3397[_0x1488f3(0x94)](_0x79abae=>{const _0x1c09b=_0x1488f3;let _0x44fbdd='-1',_0x107a84=_0x79abae;const _0x5640d0=_0x107a84['match'](_0x39c5f3);if(!_0x5640d0){if(_0x79abae[_0x1c09b(0x15e)](':')&&_0x79abae['includes']('#')){const _0x5c39f6=_0x79abae['split'](':');_0x79abae=_0x5c39f6[0x0];const _0x3e9e4c=_0x5c39f6[0x1][_0x1c09b(0x1ac)]('#');_0x44fbdd=_0x3e9e4c[0x0],_0x107a84=_0x3e9e4c[0x1];}else{if(_0x79abae[_0x1c09b(0x15e)](':')){const _0x4a8e26=_0x79abae[_0x1c09b(0x1ac)](':');_0x79abae=_0x4a8e26[0x0],_0x44fbdd=_0x4a8e26[0x1];}else{if(_0x79abae[_0x1c09b(0x15e)]('#')){const _0x47670a=_0x79abae[_0x1c09b(0x1ac)]('#');_0x79abae=_0x47670a[0x0],_0x107a84=_0x47670a[0x1];}}}_0x107a84[_0x1c09b(0x15e)](':')&&(_0x107a84=_0x107a84[_0x1c09b(0x1ac)](':')[0x0]);}else _0x79abae=_0x5640d0[0x1],_0x44fbdd=_0x5640d0[0x2]||_0x44fbdd,_0x107a84=_0x5640d0[0x3]||_0x79abae;const _0x187b2e=[_0x1c09b(0x164),_0x1c09b(0x169),_0x1c09b(0x181),_0x1c09b(0xa6),_0x1c09b(0xe7),_0x1c09b(0xaa)];if(!isValidIPv4(_0x79abae)&&_0x44fbdd=='-1')for(let _0x3eb54d of _0x187b2e){if(_0x79abae[_0x1c09b(0x15e)](_0x3eb54d)){_0x44fbdd=_0x3eb54d;break;}}if(_0x44fbdd=='-1')_0x44fbdd='80';let _0x3cb3c0=_0x197510,_0x1552a0=_0x1c09b(0x156),_0x2fb17d='';const _0x5766e6=atob(啥啥啥_写的这是啥啊),_0x10e9c2=_0x5766e6+'://'+_0x4ffc6d+'@'+_0x79abae+':'+_0x44fbdd+'?encryption=none&security=&type=ws&host='+_0x3cb3c0+_0x1c09b(0x197)+encodeURIComponent(_0x1552a0)+'#'+encodeURIComponent(_0x107a84+_0x2fb17d);return _0x10e9c2;})[_0x1488f3(0xd6)]('\x0a');}const _0x5c86a2=[...new Set(addresses)],_0x2491dd=_0x5c86a2['map'](_0x487b39=>{const _0x58ff15=_0x1488f3;let _0x18e52c='-1',_0x157fab=_0x487b39;const _0x2972cf=_0x157fab[_0x58ff15(0x1aa)](_0x39c5f3);if(!_0x2972cf){if(_0x487b39[_0x58ff15(0x15e)](':')&&_0x487b39[_0x58ff15(0x15e)]('#')){const _0x2d1aef=_0x487b39[_0x58ff15(0x1ac)](':');_0x487b39=_0x2d1aef[0x0];const _0x32db93=_0x2d1aef[0x1][_0x58ff15(0x1ac)]('#');_0x18e52c=_0x32db93[0x0],_0x157fab=_0x32db93[0x1];}else{if(_0x487b39[_0x58ff15(0x15e)](':')){const _0x142d3f=_0x487b39[_0x58ff15(0x1ac)](':');_0x487b39=_0x142d3f[0x0],_0x18e52c=_0x142d3f[0x1];}else{if(_0x487b39['includes']('#')){const _0x441aaa=_0x487b39[_0x58ff15(0x1ac)]('#');_0x487b39=_0x441aaa[0x0],_0x157fab=_0x441aaa[0x1];}}}_0x157fab[_0x58ff15(0x15e)](':')&&(_0x157fab=_0x157fab[_0x58ff15(0x1ac)](':')[0x0]);}else _0x487b39=_0x2972cf[0x1],_0x18e52c=_0x2972cf[0x2]||_0x18e52c,_0x157fab=_0x2972cf[0x3]||_0x487b39;if(!isValidIPv4(_0x487b39)&&_0x18e52c=='-1')for(let _0x5f06be of httpsPorts){if(_0x487b39['includes'](_0x5f06be)){_0x18e52c=_0x5f06be;break;}}if(_0x18e52c=='-1')_0x18e52c='443';let _0x53ce13=_0x197510,_0x1054d9='/?ed=2560',_0x41dd20='';const _0x16772f=proxyIPPool[_0x58ff15(0xb4)](_0x137fad=>_0x137fad['includes'](_0x487b39));if(_0x16772f)_0x1054d9+=_0x58ff15(0x119)+_0x16772f;proxyhosts[_0x58ff15(0x178)]>0x0&&(_0x53ce13[_0x58ff15(0x15e)](_0x58ff15(0xc9))||_0x53ce13['includes'](_0x58ff15(0x171)))&&(_0x1054d9='/'+_0x53ce13+_0x1054d9,_0x53ce13=proxyhosts[Math[_0x58ff15(0x10c)](Math[_0x58ff15(0xe0)]()*proxyhosts[_0x58ff15(0x178)])],_0x41dd20=_0x58ff15(0xf6));const _0x15e89b=atob(啥啥啥_写的这是啥啊),_0x372c3e=_0x15e89b+_0x58ff15(0x11e)+_0x4ffc6d+'@'+_0x487b39+':'+_0x18e52c+_0x58ff15(0xee)+_0x53ce13+_0x58ff15(0x190)+_0x53ce13+_0x58ff15(0x197)+encodeURIComponent(_0x1054d9)+'#'+encodeURIComponent(_0x157fab+_0x41dd20);return _0x372c3e;})[_0x1488f3(0xd6)]('\x0a');let _0x20d159=_0x2491dd;if(_0x21656a=='true')_0x20d159+='\x0a'+_0x36e7ed;return btoa(_0x20d159);}async function 整理(_0x22a9e6){const _0x540f1e=_0x452530;var _0x8d75f0=_0x22a9e6[_0x540f1e(0xde)](/[ |"'\r\n]+/g,',')[_0x540f1e(0xde)](/,+/g,',');if(_0x8d75f0['charAt'](0x0)==',')_0x8d75f0=_0x8d75f0[_0x540f1e(0x98)](0x1);if(_0x8d75f0['charAt'](_0x8d75f0[_0x540f1e(0x178)]-0x1)==',')_0x8d75f0=_0x8d75f0[_0x540f1e(0x98)](0x0,_0x8d75f0['length']-0x1);const _0x31190c=_0x8d75f0[_0x540f1e(0x1ac)](',');return _0x31190c;}async function sendMessage(_0x209b4b,_0x75b7fe,_0x2f20cf=''){const _0x5c42ff=_0x452530;if(!BotToken||!ChatID)return;try{let _0x1d3124='';const _0x50df6a=await fetch(_0x5c42ff(0xe5)+_0x75b7fe+_0x5c42ff(0x9c));if(_0x50df6a['ok']){const _0x3564ce=await _0x50df6a[_0x5c42ff(0x174)]();_0x1d3124=_0x209b4b+'\x0aIP:\x20'+_0x75b7fe+_0x5c42ff(0xbe)+_0x3564ce['country']+_0x5c42ff(0xf8)+_0x3564ce[_0x5c42ff(0x127)]+'\x0a组织:\x20'+_0x3564ce[_0x5c42ff(0x155)]+_0x5c42ff(0x18f)+_0x3564ce['as']+'\x0a'+_0x2f20cf;}else _0x1d3124=_0x209b4b+'\x0aIP:\x20'+_0x75b7fe+_0x5c42ff(0x17d)+_0x2f20cf;const _0x17cb4c=_0x5c42ff(0xc4)+BotToken+_0x5c42ff(0x123)+ChatID+_0x5c42ff(0xc5)+encodeURIComponent(_0x1d3124),_0x3b3a83={};_0x3b3a83[_0x5c42ff(0x163)]=_0x5c42ff(0x193),_0x3b3a83[_0x5c42ff(0x9e)]=_0x5c42ff(0x16d),_0x3b3a83['User-Agent']=_0x5c42ff(0x1ad);const _0x4c6273={};return _0x4c6273[_0x5c42ff(0x143)]=_0x5c42ff(0x16c),_0x4c6273[_0x5c42ff(0x1b0)]=_0x3b3a83,fetch(_0x17cb4c,_0x4c6273);}catch(_0x4c69dc){console[_0x5c42ff(0x11a)](_0x5c42ff(0xdd),_0x4c69dc);}}function isValidIPv4(_0x1c3952){const _0x15e388=/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;return _0x15e388['test'](_0x1c3952);}function 生成动态UUID(_0x56809d){const _0x14d087=_0x452530,_0x3f5f23=0x8,_0x3ccb3d=new Date(0x7d7,0x6,0x7,更新时间,0x0,0x0),_0x342311=0x3e8*0x3c*0x3c*0x18*有效时间;function _0x2ed462(){const _0xaabf57=_0x42e2,_0x19b7fe=new Date(),_0x5d8d90=new Date(_0x19b7fe[_0xaabf57(0x189)]()+_0x3f5f23*0x3c*0x3c*0x3e8),_0x3121dd=_0x5d8d90-_0x3ccb3d;return Math['ceil'](_0x3121dd/_0x342311);}function _0x4a45f2(_0x1ed7c9){const _0x1fd291=_0x42e2,_0xb210bf=new TextEncoder()[_0x1fd291(0xaf)](_0x1ed7c9);return crypto[_0x1fd291(0xb5)]['digest'](_0x1fd291(0x1af),_0xb210bf)[_0x1fd291(0x1b7)](_0x159706=>{const _0x48b359=_0x1fd291,_0x4b3fb6=Array[_0x48b359(0x9d)](new Uint8Array(_0x159706)),_0x4f34a8=_0x4b3fb6[_0x48b359(0x94)](_0x1783a7=>_0x1783a7[_0x48b359(0x137)](0x10)[_0x48b359(0xe4)](0x2,'0'))['join']('');return _0x4f34a8['substr'](0x0,0x8)+'-'+_0x4f34a8[_0x48b359(0x9a)](0x8,0x4)+'-4'+_0x4f34a8[_0x48b359(0x9a)](0xd,0x3)+'-'+(parseInt(_0x4f34a8[_0x48b359(0x9a)](0x10,0x2),0x10)&0x3f|0x80)[_0x48b359(0x137)](0x10)+_0x4f34a8[_0x48b359(0x9a)](0x12,0x2)+'-'+_0x4f34a8[_0x48b359(0x9a)](0x14,0xc);});}const _0x8a5af7=_0x2ed462(),_0x2307a2=new Date(_0x3ccb3d['getTime']()+_0x8a5af7*_0x342311),_0x1cb9a5=_0x4a45f2(_0x56809d+_0x8a5af7),_0x2b32fc=_0x4a45f2(_0x56809d+(_0x8a5af7-0x1)),_0x434edc=new Date(_0x2307a2[_0x14d087(0x189)]()-_0x3f5f23*0x3c*0x3c*0x3e8),_0x3cd502='到期时间(UTC):\x20'+_0x434edc[_0x14d087(0xb8)]()[_0x14d087(0x98)](0x0,0x13)[_0x14d087(0xde)]('T','\x20')+_0x14d087(0xae)+_0x2307a2[_0x14d087(0xb8)]()[_0x14d087(0x98)](0x0,0x13)[_0x14d087(0xde)]('T','\x20')+'\x0a';return Promise[_0x14d087(0x1ab)]([_0x1cb9a5,_0x2b32fc,_0x3cd502]);} \ No newline at end of file