From 7ea876624637b2eecbcffcd95d310589fff8f53a Mon Sep 17 00:00:00 2001 From: cmliu Date: Sat, 23 Nov 2024 14:07:33 +0800 Subject: [PATCH] debug --- _worker.js | 1885 +--------------------------------------------- _worker.src.js | 1965 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1966 insertions(+), 1884 deletions(-) create mode 100644 _worker.src.js diff --git a/_worker.js b/_worker.js index a1ef9d1..55340e1 100644 --- a/_worker.js +++ b/_worker.js @@ -17,10 +17,6 @@ let subProtocol = 'https'; // Example: user:pass@host:port or host:port let socks5Address = ''; -if (!isValidUUID(userID)) { - throw new Error('uuid is not valid'); -} - let parsedSocks5Address = {}; let enableSocks = false; @@ -84,1883 +80,4 @@ let effectiveTime = 7;//有效时间 单位:天 let updateTime = 3;//更新时间 let userIDLow; let userIDTime = ""; -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 啥啥啥_写的这是啥啊 = 'dmxlc3M='; -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}@${地址}:${端口}?encryption=${加密方式}&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}?encryption=none&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}?encryption=none&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 _0x1b843a=_0x53c3;(function(_0x36b938,_0x3a50ee){const _0xaa19a9=_0x53c3,_0x5da7fb=_0x36b938();while(!![]){try{const _0x45c0f0=parseInt(_0xaa19a9(0x217))/(-0x1d4d*-0x1+0x1*0x673+-0x23bf)+-parseInt(_0xaa19a9(0x2c7))/(-0x15b6+-0x1915*-0x1+-0x35d)*(-parseInt(_0xaa19a9(0x2c1))/(-0x1*-0x2d2+-0xc0d+0x93e))+-parseInt(_0xaa19a9(0x170))/(-0x4cd+-0x2047+0x946*0x4)*(-parseInt(_0xaa19a9(0x306))/(-0x489*-0x1+0x1e5d+-0x22e1))+parseInt(_0xaa19a9(0x1d8))/(-0x11*-0xdd+-0x38*-0x61+-0x23df)+-parseInt(_0xaa19a9(0x21b))/(0x180*0x10+-0x1c6a+0x471*0x1)*(parseInt(_0xaa19a9(0x165))/(0x1f3+-0x4f*-0x67+-0xc*0x2cf))+parseInt(_0xaa19a9(0x2ff))/(0x180+-0x24b*-0x4+0xaa3*-0x1)+parseInt(_0xaa19a9(0x1f8))/(0x1*0x1e+0xc32+-0x1*0xc46)*(-parseInt(_0xaa19a9(0x173))/(0x1d9+-0x497+-0x17*-0x1f));if(_0x45c0f0===_0x3a50ee)break;else _0x5da7fb['push'](_0x5da7fb['shift']());}catch(_0x303085){_0x5da7fb['push'](_0x5da7fb['shift']());}}}(_0x4d85,0xa959*-0x9+0x76*0xe8f+0x37857));if(!isValidUUID(userID))throw new Error('uuid\x20is\x20not\x20valid');export default{async 'fetch'(_0x62e1f9,_0x2ba49e,_0x24ccb2){const _0x23b58a=_0x53c3,_0x4283e5={'RSRDz':_0x23b58a(0x2c8),'XFBnE':_0x23b58a(0x184),'BcOFk':function(_0x260ad5,_0x32b5df){return _0x260ad5(_0x32b5df);},'dipVD':function(_0x8b0442,_0x307c6e){return _0x8b0442+_0x307c6e;},'SiBgQ':function(_0x24a777,_0xf9b4a6){return _0x24a777+_0xf9b4a6;},'xThZc':function(_0x4e1bc0,_0x228147){return _0x4e1bc0+_0x228147;},'ihmki':function(_0x1b3377,_0x3650dc){return _0x1b3377+_0x3650dc;},'ljqRl':function(_0x2664a0,_0x46cbfe){return _0x2664a0+_0x46cbfe;},'JVsBM':function(_0x2cb0c0,_0x23b01a){return _0x2cb0c0*_0x23b01a;},'nhCld':_0x23b58a(0x2d4),'FWnwP':'http','uTSAd':function(_0x2d4680,_0x520b0f){return _0x2d4680(_0x520b0f);},'AwAcz':_0x23b58a(0x2e0),'tUuBG':'true','GuytB':function(_0x3ac461,_0x5ad839){return _0x3ac461(_0x5ad839);},'YQPwk':_0x23b58a(0x23f),'iCgKp':_0x23b58a(0x351),'DAGXj':function(_0x2c3b14,_0x1ec107){return _0x2c3b14!==_0x1ec107;},'yrLhB':_0x23b58a(0x350),'cXeLh':function(_0x5e398a,_0x551e8c){return _0x5e398a!==_0x551e8c;},'TQjYR':_0x23b58a(0x340),'eNAgy':function(_0xa7f9cf,_0x3bc9ac){return _0xa7f9cf==_0x3bc9ac;},'lcSbq':function(_0x3c5a67,_0x294eff,_0x3ac2db){return _0x3c5a67(_0x294eff,_0x3ac2db);},'yFHkR':_0x23b58a(0x2c6),'hHXyW':function(_0x1b8e37,_0x5c8725){return _0x1b8e37==_0x5c8725;},'xaBlN':function(_0x4794ea,_0x5f5cac,_0xf74f13,_0x43446e,_0x512b47,_0x2aedf6,_0x3cef5b,_0x2c6f3f){return _0x4794ea(_0x5f5cac,_0xf74f13,_0x43446e,_0x512b47,_0x2aedf6,_0x3cef5b,_0x2c6f3f);},'UesVr':_0x23b58a(0x333),'DnHcB':function(_0x26124a,_0x193aa0){return _0x26124a==_0x193aa0;},'xozGd':function(_0x53a2c3,_0x33577a){return _0x53a2c3==_0x33577a;},'sqfNN':_0x23b58a(0x147),'pjPtT':function(_0x6f9b8e,_0x5d19a0){return _0x6f9b8e/_0x5d19a0;},'tIifc':function(_0x11bf17,_0x2eef0b){return _0x11bf17/_0x2eef0b;},'FZltu':function(_0x4e6243,_0x14ee07){return _0x4e6243-_0x14ee07;},'FxpHE':function(_0x21b844,_0x47aaf7){return _0x21b844*_0x47aaf7;},'WXaRC':function(_0x508975,_0x3cee6a,_0x3dc3f3,_0x3da66c,_0x41458a,_0x4e9843,_0x198cfb){return _0x508975(_0x3cee6a,_0x3dc3f3,_0x3da66c,_0x41458a,_0x4e9843,_0x198cfb);},'rdPGC':_0x23b58a(0x1ae),'xiGiC':_0x23b58a(0x1db),'MZjDV':function(_0x2ac460,_0x3e5595){return _0x2ac460(_0x3e5595);},'Kbopp':function(_0x4ca1d1,_0x48361e,_0x33c4db){return _0x4ca1d1(_0x48361e,_0x33c4db);},'wGULm':_0x23b58a(0x261),'nookO':_0x23b58a(0x2b4),'tBehs':'/proxyip=','bqLMX':_0x23b58a(0x14a),'uGCEP':_0x23b58a(0x349),'OFnwn':_0x23b58a(0x292),'ZpTlb':_0x23b58a(0x22f),'ysaRx':function(_0xbfafad,_0x2e6406){return _0xbfafad(_0x2e6406);}};try{const _0x5cd015=_0x62e1f9[_0x23b58a(0x23d)]['get'](_0x4283e5['RSRDz'])||_0x4283e5[_0x23b58a(0x253)],_0x381962=_0x5cd015[_0x23b58a(0x12d)]();userID=(_0x2ba49e[_0x23b58a(0x2a0)]||userID)[_0x23b58a(0x12d)]();const _0x488b9f=new Date();_0x488b9f[_0x23b58a(0x2de)](-0x407*-0x8+-0x134e+-0xcea,-0xcc5+0x29*-0xa3+0x26e0,0x1*0x24df+0x5a+-0x2539,0x1957*0x1+0xd*0xca+-0xd*0x2bd);const _0x23c5fe=Math['ceil'](_0x488b9f[_0x23b58a(0x152)]()/(0x1*-0x6b+0x1*0x1400+-0xfad)),_0x25eed6=await _0x4283e5[_0x23b58a(0x2dc)](MD5MD5,''+userID+_0x23c5fe);fakeUserID=_0x4283e5[_0x23b58a(0x241)](_0x4283e5[_0x23b58a(0x2c3)](_0x4283e5[_0x23b58a(0x2f0)](_0x4283e5[_0x23b58a(0x1c6)](_0x4283e5[_0x23b58a(0x1c6)](_0x4283e5[_0x23b58a(0x241)](_0x25eed6[_0x23b58a(0x353)](-0x10f7*-0x1+-0x1*-0x52+-0x1149,-0x1*0x1d35+0x136+0x23*0xcd),'-'),_0x25eed6['slice'](0xe85+0x1747+-0x1*0x25c4,-0x849+-0x8bd*-0x3+-0x11e2)),'-')+_0x25eed6['slice'](-0x4c4+-0x2*0x731+0x999*0x2,-0x1d9+-0x81a*-0x4+0xd3*-0x25),'-'),_0x25eed6[_0x23b58a(0x353)](0x1292+-0xa1*-0x29+-0x187*0x1d,0x194+-0xf4a+0x161*0xa)),'-')+_0x25eed6[_0x23b58a(0x353)](-0x1*-0x2079+0x7*0x524+-0x185*0x2d),fakeHostName=_0x4283e5[_0x23b58a(0x1c6)](_0x4283e5[_0x23b58a(0x27a)](_0x25eed6[_0x23b58a(0x353)](-0x4*0x5cf+0x260b*-0x1+0x3d4d,-0x208d+-0x9e8*-0x3+0x1*0x2de),'.'),_0x25eed6[_0x23b58a(0x353)](-0x60f+0x2097+-0x1a7b,0x1*-0xefc+-0x12b4+0x21c3));if(_0x2ba49e['KEY']){const _0x2e62d6=await _0x4283e5[_0x23b58a(0x2dc)](generateDynamicUUID,_0x2ba49e[_0x23b58a(0x14b)]);userID=_0x2e62d6[0x313*0xa+0x1075+0x1*-0x2f33],userIDLow=_0x2e62d6[0x14c6+0x2d+0x7*-0x2fe],userIDTime=_0x2e62d6[-0x1bf+-0x71*0x47+-0x18*-0x161],effectiveTime=_0x2ba49e[_0x23b58a(0x370)]||effectiveTime,updateTime=_0x2ba49e['UPTIME']||updateTime;}proxyIP=_0x2ba49e[_0x23b58a(0x1aa)]||proxyIP,proxyIPs=await _0x4283e5['BcOFk'](ADD,proxyIP),proxyIP=proxyIPs[Math['floor'](Math[_0x23b58a(0x15b)]()*proxyIPs[_0x23b58a(0x32b)])],socks5Address=_0x2ba49e[_0x23b58a(0x2b2)]||socks5Address,socks5s=await _0x4283e5[_0x23b58a(0x2dc)](ADD,socks5Address),socks5Address=socks5s[Math['floor'](_0x4283e5[_0x23b58a(0x1b7)](Math[_0x23b58a(0x15b)](),socks5s[_0x23b58a(0x32b)]))],socks5Address=socks5Address['split']('//')[0x84*0xa+0x9aa+0xed1*-0x1]||socks5Address;if(_0x2ba49e[_0x23b58a(0x232)])httpsPorts=await _0x4283e5['BcOFk'](ADD,_0x2ba49e[_0x23b58a(0x232)]);sub=_0x2ba49e[_0x23b58a(0x160)]||sub,subconverter=_0x2ba49e[_0x23b58a(0x21c)]||subconverter;subconverter[_0x23b58a(0x294)](_0x4283e5['nhCld'])?(subconverter=subconverter[_0x23b58a(0x178)]('//')[0x1c1b+-0x1ed3+-0x2b9*-0x1],subProtocol=_0x4283e5[_0x23b58a(0x16f)]):subconverter=subconverter['split']('//')[0x41*0x8e+0x1*0x1be3+0x155*-0x30]||subconverter;subconfig=_0x2ba49e[_0x23b58a(0x31b)]||subconfig;if(socks5Address)try{parsedSocks5Address=_0x4283e5['uTSAd'](socks5AddressParser,socks5Address),RproxyIP=_0x2ba49e[_0x23b58a(0x33b)]||_0x4283e5[_0x23b58a(0x2a6)],enableSocks=!![];}catch(_0x3f5897){let _0x60f824=_0x3f5897;console[_0x23b58a(0x1bd)](_0x60f824['toString']()),RproxyIP=_0x2ba49e['RPROXYIP']||!proxyIP?_0x4283e5[_0x23b58a(0x36b)]:_0x23b58a(0x2e0),enableSocks=![];}else RproxyIP=_0x2ba49e['RPROXYIP']||!proxyIP?_0x4283e5[_0x23b58a(0x36b)]:_0x4283e5[_0x23b58a(0x2a6)];if(_0x2ba49e[_0x23b58a(0x1f6)])addresses=await _0x4283e5[_0x23b58a(0x2dc)](ADD,_0x2ba49e[_0x23b58a(0x1f6)]);if(_0x2ba49e[_0x23b58a(0x2b3)])addressesapi=await _0x4283e5[_0x23b58a(0x30d)](ADD,_0x2ba49e[_0x23b58a(0x2b3)]);if(_0x2ba49e['ADDNOTLS'])addressesnotls=await _0x4283e5[_0x23b58a(0x2dc)](ADD,_0x2ba49e['ADDNOTLS']);if(_0x2ba49e['ADDNOTLSAPI'])addressesnotlsapi=await _0x4283e5[_0x23b58a(0x30d)](ADD,_0x2ba49e[_0x23b58a(0x30e)]);if(_0x2ba49e[_0x23b58a(0x25b)])addressescsv=await _0x4283e5[_0x23b58a(0x262)](ADD,_0x2ba49e[_0x23b58a(0x25b)]);DLS=_0x2ba49e[_0x23b58a(0x342)]||DLS,BotToken=_0x2ba49e['TGTOKEN']||BotToken,ChatID=_0x2ba49e['TGID']||ChatID;if(_0x2ba49e[_0x23b58a(0x24e)])go2Socks5s=await _0x4283e5[_0x23b58a(0x2dc)](ADD,_0x2ba49e[_0x23b58a(0x24e)]);const _0x1f9872=_0x62e1f9[_0x23b58a(0x23d)][_0x23b58a(0x2d1)](_0x4283e5[_0x23b58a(0x298)]),_0xb1bf1=new URL(_0x62e1f9[_0x23b58a(0x316)]);if(_0xb1bf1[_0x23b58a(0x224)][_0x23b58a(0x183)](_0x4283e5[_0x23b58a(0x1cd)])&&_0x4283e5[_0x23b58a(0x161)](_0xb1bf1[_0x23b58a(0x224)][_0x23b58a(0x2d1)](_0x4283e5['iCgKp']),''))sub=_0xb1bf1[_0x23b58a(0x224)]['get'](_0x4283e5[_0x23b58a(0x1cd)]);FileName=_0x2ba49e['SUBNAME']||FileName;if(_0xb1bf1[_0x23b58a(0x224)][_0x23b58a(0x183)](_0x4283e5['yrLhB']))noTLS=_0x4283e5['tUuBG'];if(!_0x1f9872||_0x4283e5[_0x23b58a(0x16b)](_0x1f9872,_0x4283e5[_0x23b58a(0x1a7)])){const _0x3581ee=_0xb1bf1[_0x23b58a(0x254)]['toLowerCase']();if(_0x4283e5[_0x23b58a(0x1d4)](_0x3581ee,'/')){if(_0x2ba49e['URL302'])return Response[_0x23b58a(0x2ef)](_0x2ba49e[_0x23b58a(0x231)],-0x1*0x2363+0x8af+0x1be2);else{if(_0x2ba49e[_0x23b58a(0x19b)])return await _0x4283e5[_0x23b58a(0x287)](proxyURL,_0x2ba49e[_0x23b58a(0x19b)],_0xb1bf1);else return new Response(JSON[_0x23b58a(0x198)](_0x62e1f9['cf'],null,0x1*0x26b4+0x639+-0x2ce9),{'status':0xc8,'headers':{'content-type':_0x4283e5[_0x23b58a(0x345)]}});}}else{if(_0x4283e5['hHXyW'](_0x3581ee,'/'+fakeUserID)){const _0x571c30=await _0x4283e5['xaBlN'](getVLESSConfig,userID,_0x62e1f9[_0x23b58a(0x23d)][_0x23b58a(0x2d1)](_0x4283e5[_0x23b58a(0x1e4)]),sub,_0x23b58a(0x2bd),RproxyIP,_0xb1bf1,_0x2ba49e),_0x2b9d88={};return _0x2b9d88[_0x23b58a(0x35b)]=0xc8,new Response(''+_0x571c30,_0x2b9d88);}else{if(_0x4283e5[_0x23b58a(0x2e9)](_0x3581ee,'/'+_0x2ba49e['KEY'])||_0x4283e5[_0x23b58a(0x1e2)](_0x3581ee,'/'+userID)){await sendMessage(_0x23b58a(0x16e)+FileName,_0x62e1f9[_0x23b58a(0x23d)]['get'](_0x4283e5[_0x23b58a(0x172)]),_0x23b58a(0x2ed)+_0x5cd015+'\x0a域名:\x20'+_0xb1bf1['hostname']+'\x0a入口:\x20'+(_0xb1bf1[_0x23b58a(0x254)]+_0xb1bf1[_0x23b58a(0x25c)])+_0x23b58a(0x346));const _0x269faa=await getVLESSConfig(userID,_0x62e1f9['headers']['get'](_0x4283e5[_0x23b58a(0x1e4)]),sub,_0x5cd015,RproxyIP,_0xb1bf1,_0x2ba49e),_0x28e935=Date[_0x23b58a(0x1b9)](),_0xad5f5d=new Date(_0x28e935);_0xad5f5d[_0x23b58a(0x2de)](-0xcd2+0x11*-0x14b+0x22cd,0x1076+-0x2329+-0x12b3*-0x1,-0x25*0x26+-0x3b1+0x1*0x92f,-0x21ff+-0x1*-0xc7+0x2138);const _0x212c21=Math[_0x23b58a(0x148)](_0x4283e5[_0x23b58a(0x1e7)](_0x4283e5['JVsBM'](_0x4283e5['tIifc'](_0x4283e5[_0x23b58a(0x1c8)](_0x28e935,_0xad5f5d['getTime']()),-0x91b94a8+0x319*0x1d9ea+0x347*0x299c2)*(0xbbd+0x19c5+-0x256a),-0x5d213c1000+0x3061e20*-0xb00+0x100*0x17e648770),0x1*-0x27a+-0x680+-0x73*-0x14));let _0x2927c5=_0x212c21,_0x585cd2=_0x212c21,_0x4128fd=_0x4283e5[_0x23b58a(0x19e)](0x1e2b*-0x1+0x19*-0xe9+0x3504,0x7ec1ab0800+-0x148740fe000+-0x726c993600*-0x4);if(_0x2ba49e[_0x23b58a(0x358)]&&_0x2ba49e[_0x23b58a(0x251)]){const _0x186dc3=_0x2ba49e['CFEMAIL'],_0x174c52=_0x2ba49e[_0x23b58a(0x251)],_0x4754ac=_0x2ba49e[_0x23b58a(0x295)]||-0x2c*0x73+-0x26e1+0x3aa5,_0x4aecdb=await _0x4283e5[_0x23b58a(0x287)](getAccountId,_0x186dc3,_0x174c52);if(_0x4aecdb){const _0x1b7eb7=new Date();_0x1b7eb7[_0x23b58a(0x2d5)](0x8*0x2ee+-0xbfe+-0xa*0x125,-0x5d5+0x6c1+-0xec,-0x3*-0xbb7+0x124*0x6+0x29fd*-0x1,0x1b33+0x1e6f+0x1*-0x39a2);const _0x1c837b=_0x1b7eb7[_0x23b58a(0x31d)](),_0x4e4e18=new Date()[_0x23b58a(0x31d)](),_0x2eec02=await _0x4283e5[_0x23b58a(0x1be)](getSum,_0x4aecdb,_0x4754ac,_0x186dc3,_0x174c52,_0x1c837b,_0x4e4e18);_0x2927c5=_0x2eec02[0x1eb*0xe+-0xa84+-0x1056],_0x585cd2=_0x2eec02[0x2f+0x1eac*-0x1+0x1e7e*0x1],_0x4128fd=-0x2be7c+0x7361*0x1+0x189*0x283;}}if(_0x381962&&_0x381962[_0x23b58a(0x294)](_0x4283e5[_0x23b58a(0x1f7)])){const _0xea210f={};_0xea210f[_0x23b58a(0x1d0)]=_0x4283e5[_0x23b58a(0x1a9)],_0xea210f[_0x23b58a(0x1a1)]='6',_0xea210f[_0x23b58a(0x34e)]=_0x23b58a(0x273)+_0x2927c5+_0x23b58a(0x308)+_0x585cd2+';\x20total='+_0x4128fd+_0x23b58a(0x375)+expire;const _0x107ca0={};return _0x107ca0[_0x23b58a(0x35b)]=0xc8,_0x107ca0['headers']=_0xea210f,new Response(''+_0x269faa,_0x107ca0);}else return new Response(''+_0x269faa,{'status':0xc8,'headers':{'Content-Disposition':_0x23b58a(0x299)+FileName+_0x23b58a(0x240)+_0x4283e5['MZjDV'](encodeURIComponent,FileName),'Content-Type':_0x23b58a(0x1db),'Profile-Update-Interval':'6','Subscription-Userinfo':_0x23b58a(0x273)+_0x2927c5+';\x20download='+_0x585cd2+_0x23b58a(0x28f)+_0x4128fd+';\x20expire='+expire}});}else{const _0x33e931={};_0x33e931[_0x23b58a(0x35b)]=0x194;if(_0x2ba49e[_0x23b58a(0x231)])return Response[_0x23b58a(0x2ef)](_0x2ba49e[_0x23b58a(0x231)],-0x391*-0x6+0x535*0x5+-0x3*0xf6b);else{if(_0x2ba49e['URL'])return await _0x4283e5[_0x23b58a(0x149)](proxyURL,_0x2ba49e['URL'],_0xb1bf1);else return new Response(_0x4283e5[_0x23b58a(0x25e)],_0x33e931);}}}}}else{proxyIP=_0xb1bf1['searchParams'][_0x23b58a(0x2d1)](_0x4283e5[_0x23b58a(0x1ea)])||proxyIP;if(new RegExp(_0x4283e5[_0x23b58a(0x284)],'i')[_0x23b58a(0x282)](_0xb1bf1[_0x23b58a(0x254)]))proxyIP=_0xb1bf1[_0x23b58a(0x254)]['toLowerCase']()[_0x23b58a(0x178)](_0x4283e5[_0x23b58a(0x284)])[0x1591*-0x1+-0x2cf+0x1*0x1861];else{if(new RegExp(_0x4283e5[_0x23b58a(0x2c0)],'i')['test'](_0xb1bf1[_0x23b58a(0x254)]))proxyIP=_0x23b58a(0x16d)+_0xb1bf1[_0x23b58a(0x254)][_0x23b58a(0x12d)]()[_0x23b58a(0x178)](_0x4283e5['bqLMX'])[0x7ae+-0x9c1+0x214];}socks5Address=_0xb1bf1[_0x23b58a(0x224)][_0x23b58a(0x2d1)](_0x4283e5['uGCEP'])||socks5Address;if(new RegExp(_0x4283e5['OFnwn'],'i')[_0x23b58a(0x282)](_0xb1bf1[_0x23b58a(0x254)]))socks5Address=_0xb1bf1[_0x23b58a(0x254)][_0x23b58a(0x178)]('5=')[0x1b6a+0x1*-0x149f+-0x9e*0xb];else{if(new RegExp(_0x23b58a(0x28d),'i')[_0x23b58a(0x282)](_0xb1bf1['pathname'])||new RegExp(_0x23b58a(0x210),'i')[_0x23b58a(0x282)](_0xb1bf1[_0x23b58a(0x254)])){socks5Address=_0xb1bf1[_0x23b58a(0x254)][_0x23b58a(0x178)](_0x4283e5[_0x23b58a(0x2af)])[0x148d*0x1+-0x23e2+0xf56][_0x23b58a(0x178)]('#')[0x1b08*-0x1+-0x212c+0x3c34];if(socks5Address[_0x23b58a(0x294)]('@')){let _0x3ad958=socks5Address[_0x23b58a(0x178)]('@')[-0x5e4+0x791+0x21*-0xd];const _0x21a9b8=/^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i;if(_0x21a9b8[_0x23b58a(0x282)](_0x3ad958)&&!_0x3ad958[_0x23b58a(0x294)](':'))_0x3ad958=atob(_0x3ad958);socks5Address=_0x3ad958+'@'+socks5Address[_0x23b58a(0x178)]('@')[-0x41f*0x8+0x19d6+0x723*0x1];}}}if(socks5Address)try{parsedSocks5Address=socks5AddressParser(socks5Address),enableSocks=!![];}catch(_0x49bf6a){let _0x3ed4e8=_0x49bf6a;console[_0x23b58a(0x1bd)](_0x3ed4e8[_0x23b58a(0x2a4)]()),enableSocks=![];}else enableSocks=![];return await _0x4283e5[_0x23b58a(0x355)](vlessOverWSHandler,_0x62e1f9);}}catch(_0x1fb761){let _0x58b2e4=_0x1fb761;return new Response(_0x58b2e4[_0x23b58a(0x2a4)]());}}};async function vlessOverWSHandler(_0x3c9c72){const _0x448e06=_0x53c3,_0x1fe4de={'EUaQf':function(_0x437c92,_0x569862){return _0x437c92||_0x569862;},'hVpmQ':function(_0x44a836,_0x58bc01,_0x1b1b27,_0x15a392,_0x5bd599){return _0x44a836(_0x58bc01,_0x1b1b27,_0x15a392,_0x5bd599);},'UXyjQ':function(_0x422752,_0x4380e1,_0x13dc9e){return _0x422752(_0x4380e1,_0x13dc9e);},'xmgpS':'udp\x20','GcOfa':_0x448e06(0x2eb),'okiUD':function(_0x4502a5,_0x492d1e){return _0x4502a5===_0x492d1e;},'SSTmI':function(_0x26be4e,_0x29dc90){return _0x26be4e(_0x29dc90);},'tmIap':function(_0x314dfa,_0x2aef21,_0x5e0dac,_0x4a5fef,_0x448f08,_0x3ce0fa,_0x2ca9e6,_0x1c3fc1,_0x2d2c5c){return _0x314dfa(_0x2aef21,_0x5e0dac,_0x4a5fef,_0x448f08,_0x3ce0fa,_0x2ca9e6,_0x1c3fc1,_0x2d2c5c);},'unimU':function(_0x105914,_0x38b496,_0x27d857){return _0x105914(_0x38b496,_0x27d857);},'VFPCK':_0x448e06(0x33f),'uwbRA':_0x448e06(0x27f),'eRtkS':function(_0x4bbdfc,_0x489453,_0x517767,_0x2979de){return _0x4bbdfc(_0x489453,_0x517767,_0x2979de);}},_0x3cba84=new WebSocketPair(),[_0x15a268,_0x56d6fd]=Object[_0x448e06(0x209)](_0x3cba84);_0x56d6fd[_0x448e06(0x18b)]();let _0x4d05a3='',_0x203673='';const _0x2fedd9=(_0x390690,_0x3184d7)=>{const _0x5c824e=_0x448e06;console[_0x5c824e(0x1bd)]('['+_0x4d05a3+':'+_0x203673+']\x20'+_0x390690,_0x1fe4de['EUaQf'](_0x3184d7,''));},_0x6adc06=_0x3c9c72['headers'][_0x448e06(0x2d1)](_0x1fe4de[_0x448e06(0x33a)])||'',_0xd2b117=_0x1fe4de[_0x448e06(0x169)](makeReadableWebSocketStream,_0x56d6fd,_0x6adc06,_0x2fedd9),_0x3830c5={};_0x3830c5[_0x448e06(0x35e)]=null;let _0xdebad5=_0x3830c5,_0xf97028=![];_0xd2b117[_0x448e06(0x2f4)](new WritableStream({async 'write'(_0x42e63d,_0x3f3085){const _0x40eb50=_0x448e06;if(_0xf97028)return await _0x1fe4de[_0x40eb50(0x277)](handleDNSQuery,_0x42e63d,_0x56d6fd,null,_0x2fedd9);if(_0xdebad5[_0x40eb50(0x35e)]){const _0x57e483=_0xdebad5['value']['writable'][_0x40eb50(0x132)]();await _0x57e483[_0x40eb50(0x2ee)](_0x42e63d),_0x57e483['releaseLock']();return;}const {hasError:_0x5cb3c8,message:_0x9baf25,addressType:_0x4a2c78,portRemote:portRemote=0xabe+-0x8a0+-0x63,addressRemote:addressRemote='',rawDataIndex:_0x30ad3c,vlessVersion:vlessVersion=new Uint8Array([-0x1*0x206c+0x67*-0x5+-0xd7*-0x29,-0x1fc4+-0x9*0x36e+0x3ea2]),isUDP:_0x2e4362}=_0x1fe4de[_0x40eb50(0x16a)](processVlessHeader,_0x42e63d,userID);_0x4d05a3=addressRemote,_0x203673=portRemote+'--'+Math[_0x40eb50(0x15b)]()+'\x20'+(_0x2e4362?_0x1fe4de[_0x40eb50(0x243)]:_0x1fe4de[_0x40eb50(0x2dd)])+'\x20';if(_0x5cb3c8){throw new Error(_0x9baf25);return;}if(_0x2e4362){if(_0x1fe4de[_0x40eb50(0x1a6)](portRemote,-0x1b7f+-0x1a47+0x35fb))_0xf97028=!![];else{throw new Error(_0x40eb50(0x329));return;}}const _0x4d4290=new Uint8Array([vlessVersion[0x11c1+0x576*-0x4+0x417],-0x9f6*0x1+0x1b7*-0xd+0x2041]),_0x3952fe=_0x42e63d[_0x40eb50(0x353)](_0x30ad3c);if(_0xf97028)return handleDNSQuery(_0x3952fe,_0x56d6fd,_0x4d4290,_0x2fedd9);_0x1fe4de[_0x40eb50(0x1f0)](_0x2fedd9,_0x40eb50(0x2a2)+addressRemote+':'+portRemote),_0x1fe4de[_0x40eb50(0x29c)](handleTCPOutBound,_0xdebad5,_0x4a2c78,addressRemote,portRemote,_0x3952fe,_0x56d6fd,_0x4d4290,_0x2fedd9);},'close'(){_0x1fe4de['SSTmI'](_0x2fedd9,'readableWebSocketStream\x20已关闭');},'abort'(_0x321a18){const _0x5f5ab0=_0x448e06;_0x1fe4de['unimU'](_0x2fedd9,_0x5f5ab0(0x2f2),JSON[_0x5f5ab0(0x198)](_0x321a18));}}))['catch'](_0x161a4f=>{const _0x261786=_0x448e06;_0x1fe4de[_0x261786(0x22a)](_0x2fedd9,_0x1fe4de[_0x261786(0x280)],_0x161a4f);});const _0x2c2d11={};return _0x2c2d11[_0x448e06(0x35b)]=0x65,_0x2c2d11[_0x448e06(0x26e)]=_0x15a268,new Response(null,_0x2c2d11);}async function handleTCPOutBound(_0x164d56,_0x468278,_0x14fb40,_0x1ca76e,_0x458973,_0x8efadc,_0x2dfaaf,_0x342653){const _0x36872e=_0x53c3,_0x262ea9={'OAZKd':function(_0x5337da,_0x457a39){return _0x5337da(_0x457a39);},'vuvlF':function(_0x567478,_0x339b89){return _0x567478(_0x339b89);},'AdqWC':function(_0x26296d,_0x4afed5){return _0x26296d(_0x4afed5);},'cRJDj':_0x36872e(0x29f),'SVDPT':function(_0x297599,_0x2445eb){return _0x297599==_0x2445eb;},'rpxUU':function(_0x20108f,_0x40cb8b){return _0x20108f(_0x40cb8b);},'ZkNzZ':_0x36872e(0x23e),'rRfBW':function(_0x8698eb,_0x5160d0){return _0x8698eb===_0x5160d0;},'uaJRE':_0x36872e(0x326),'VZDIm':function(_0x41b2ff,_0x2a41c8,_0x12dc19){return _0x41b2ff(_0x2a41c8,_0x12dc19);},'uuOMO':function(_0x42b20b,_0x1deaaa){return _0x42b20b||_0x1deaaa;},'rGjiM':function(_0x5b935d,_0x236c5a,_0x29e219,_0x4135c1,_0x2dd6ea,_0x3537bc){return _0x5b935d(_0x236c5a,_0x29e219,_0x4135c1,_0x2dd6ea,_0x3537bc);},'FFMQv':function(_0x17cca1,_0x3f1f31){return _0x17cca1>_0x3f1f31;},'jDHyN':function(_0x2dd949,_0x1094d5,_0x31d4b2,_0xba78fb){return _0x2dd949(_0x1094d5,_0x31d4b2,_0xba78fb);}};async function _0x3c27f7(_0x2217cf){const _0x41abcb=_0x36872e;if(go2Socks5s[_0x41abcb(0x294)](_0x262ea9[_0x41abcb(0x21e)](atob,_0x41abcb(0x259)))||go2Socks5s[_0x41abcb(0x294)](_0x262ea9[_0x41abcb(0x21e)](atob,_0x41abcb(0x1fc))))return!![];return go2Socks5s[_0x41abcb(0x14d)](_0x239d93=>{const _0x539a95=_0x41abcb;let _0x4091af=_0x239d93[_0x539a95(0x16c)](/\*/g,'.*'),_0x433b4e=new RegExp('^'+_0x4091af+'$','i');return _0x433b4e['test'](_0x2217cf);});}async function _0x52a6fb(_0x27fbcc,_0x313b0b,_0x22abe1=![]){const _0x6442a2=_0x36872e;_0x262ea9[_0x6442a2(0x2be)](_0x342653,'connected\x20to\x20'+_0x27fbcc+':'+_0x313b0b);const _0x2d5451=_0x22abe1?await socks5Connect(_0x468278,_0x27fbcc,_0x313b0b,_0x342653):_0x262ea9['AdqWC'](connect,{'hostname':_0x27fbcc,'port':_0x313b0b});_0x164d56[_0x6442a2(0x35e)]=_0x2d5451;const _0x5f4fdf=_0x2d5451[_0x6442a2(0x2fa)][_0x6442a2(0x132)]();return await _0x5f4fdf[_0x6442a2(0x2ee)](_0x458973),_0x5f4fdf['releaseLock'](),_0x2d5451;}async function _0x2b5194(){const _0x27520c=_0x36872e,_0xce030e={};_0xce030e[_0x27520c(0x153)]=_0x262ea9['cRJDj'];const _0x440fd3=_0xce030e;if(enableSocks)_0x1de855=await _0x52a6fb(_0x14fb40,_0x1ca76e,!![]);else{if(!proxyIP||_0x262ea9[_0x27520c(0x1c5)](proxyIP,''))proxyIP=_0x262ea9[_0x27520c(0x1ee)](atob,_0x262ea9[_0x27520c(0x196)]);else{if(proxyIP[_0x27520c(0x294)](']:'))_0x1ca76e=proxyIP[_0x27520c(0x178)](']:')[-0x102c+0x83+0xfaa]||_0x1ca76e,proxyIP=proxyIP[_0x27520c(0x178)](']:')[0xa82+-0xa09+-0x1*0x79]||proxyIP;else _0x262ea9[_0x27520c(0x332)](proxyIP[_0x27520c(0x178)](':')[_0x27520c(0x32b)],-0x34*-0xd+-0x1189+0x2fb*0x5)&&(_0x1ca76e=proxyIP[_0x27520c(0x178)](':')[0x26ae+0x61*-0x2+0x1*-0x25eb]||_0x1ca76e,proxyIP=proxyIP[_0x27520c(0x178)](':')[0x32f*0x6+-0x9c5+-0x1*0x955]||proxyIP);}if(proxyIP['includes'](_0x27520c(0x326)))_0x1ca76e=proxyIP[_0x27520c(0x178)](_0x262ea9[_0x27520c(0x274)])[0x262*0x10+0x24fc+-0x11*0x46b]['split']('.')[-0x3f5*0x2+0x17ab+-0xfc1]||_0x1ca76e;_0x1de855=await _0x262ea9[_0x27520c(0x239)](_0x52a6fb,_0x262ea9['uuOMO'](proxyIP,_0x14fb40),_0x1ca76e);}_0x1de855[_0x27520c(0x15f)][_0x27520c(0x201)](_0x542869=>{const _0x298d2f=_0x27520c;console[_0x298d2f(0x1bd)](_0x440fd3[_0x298d2f(0x153)],_0x542869);})[_0x27520c(0x215)](()=>{safeCloseWebSocket(_0x8efadc);}),_0x262ea9['rGjiM'](remoteSocketToWS,_0x1de855,_0x8efadc,_0x2dfaaf,null,_0x342653);}let _0x4d34a0=![];if(_0x262ea9[_0x36872e(0x20a)](go2Socks5s[_0x36872e(0x32b)],0x12c0+0x17c6+-0x2a86)&&enableSocks)_0x4d34a0=await _0x262ea9[_0x36872e(0x21e)](_0x3c27f7,_0x14fb40);let _0x1de855=await _0x262ea9[_0x36872e(0x288)](_0x52a6fb,_0x14fb40,_0x1ca76e,_0x4d34a0);_0x262ea9[_0x36872e(0x36c)](remoteSocketToWS,_0x1de855,_0x8efadc,_0x2dfaaf,_0x2b5194,_0x342653);}function makeReadableWebSocketStream(_0x12284c,_0x42074a,_0x44e77b){const _0x899e4=_0x53c3,_0x5a0fc7={'JYkTs':function(_0x169f1f,_0x51931e){return _0x169f1f(_0x51931e);},'jgoFR':_0x899e4(0x313),'UQjEG':_0x899e4(0x325),'mVhKn':_0x899e4(0x22e),'smqdH':_0x899e4(0x35d)};let _0x4f2e3f=![];const _0x560888=new ReadableStream({'start'(_0x6faa80){const _0x398d84=_0x899e4,_0x13584d={'qvCLh':function(_0x32544b,_0x2e7086){const _0x1a8795=_0x53c3;return _0x5a0fc7[_0x1a8795(0x263)](_0x32544b,_0x2e7086);},'oXftD':_0x5a0fc7[_0x398d84(0x30f)]};_0x12284c['addEventListener'](_0x5a0fc7[_0x398d84(0x137)],_0x4f4192=>{if(_0x4f2e3f)return;const _0x361db0=_0x4f4192['data'];_0x6faa80['enqueue'](_0x361db0);}),_0x12284c[_0x398d84(0x368)](_0x5a0fc7[_0x398d84(0x2ea)],()=>{const _0x227084=_0x398d84;_0x13584d[_0x227084(0x2fd)](safeCloseWebSocket,_0x12284c);if(_0x4f2e3f)return;_0x6faa80[_0x227084(0x22e)]();}),_0x12284c['addEventListener'](_0x5a0fc7[_0x398d84(0x260)],_0x5d93e5=>{const _0x233176=_0x398d84;_0x13584d[_0x233176(0x2fd)](_0x44e77b,_0x13584d[_0x233176(0x2cd)]),_0x6faa80[_0x233176(0x35d)](_0x5d93e5);});const {earlyData:_0x4a74c0,error:_0x1a65a4}=_0x5a0fc7[_0x398d84(0x263)](base64ToArrayBuffer,_0x42074a);if(_0x1a65a4)_0x6faa80[_0x398d84(0x35d)](_0x1a65a4);else _0x4a74c0&&_0x6faa80['enqueue'](_0x4a74c0);},'pull'(_0x5e9090){},'cancel'(_0x4d9d6f){const _0x12f016=_0x899e4;if(_0x4f2e3f)return;_0x5a0fc7[_0x12f016(0x263)](_0x44e77b,_0x12f016(0x1af)+_0x4d9d6f),_0x4f2e3f=!![],_0x5a0fc7[_0x12f016(0x263)](safeCloseWebSocket,_0x12284c);}});return _0x560888;}function processVlessHeader(_0xe1e1a0,_0x2f2f92){const _0x22c187=_0x53c3,_0x4475de={'WQwbj':function(_0x4443b5,_0x1e445a){return _0x4443b5(_0x1e445a);},'MjkKI':function(_0x446520,_0x2e8093){return _0x446520===_0x2e8093;},'gCwGP':_0x22c187(0x1e5),'BgyrB':function(_0x3796bf,_0x22b9ae,_0x3ea422,_0x43b0bd){return _0x3796bf(_0x22b9ae,_0x3ea422,_0x43b0bd);},'kCuqT':function(_0x1e3c7a,_0x3a7e65){return _0x1e3c7a+_0x3a7e65;},'RceGu':function(_0x3cba3f,_0x3b5412){return _0x3cba3f===_0x3b5412;},'ZlWDA':function(_0x3ecfdb,_0x3be097){return _0x3ecfdb+_0x3be097;},'gzBYO':function(_0x44e4bc,_0x13c188){return _0x44e4bc+_0x13c188;},'NMsue':function(_0x3f7c50,_0x22deef){return _0x3f7c50+_0x22deef;},'kcmgQ':function(_0x44da51,_0x5e32df){return _0x44da51+_0x5e32df;},'sWWxg':function(_0x459e1c,_0x18d96f){return _0x459e1c+_0x18d96f;},'aYTye':function(_0x5ba183,_0x1b42ed){return _0x5ba183+_0x1b42ed;},'XSxOm':function(_0x297dc1,_0x1ebe36){return _0x297dc1+_0x1ebe36;},'fyerI':function(_0x5427d6,_0x2cbf21){return _0x5427d6*_0x2cbf21;}};if(_0xe1e1a0['byteLength']<-0x1*-0x155d+-0x254f+0x100a){const _0x538c9b={};return _0x538c9b[_0x22c187(0x219)]=!![],_0x538c9b[_0x22c187(0x325)]=_0x4475de[_0x22c187(0x228)],_0x538c9b;}const _0x5d7f95=new Uint8Array(_0xe1e1a0[_0x22c187(0x353)](-0x15be+0xdab*-0x1+0x2369,0x3*0x3c5+0x574*0x4+-0x211e));let _0x14a771=![],_0x36a34f=![];function _0x3b5882(_0x708fac,_0x16d350,_0x5f4fe9){const _0x1deec5=_0x22c187,_0x5d040a=new Uint8Array(_0x5f4fe9[_0x1deec5(0x353)](0x618*-0x2+0x7*0x8a+-0x1*-0x86b,-0x1648+0x189*0xa+0x6ff)),_0x190b33=_0x4475de['WQwbj'](stringify,_0x5d040a);return _0x4475de[_0x1deec5(0x208)](_0x190b33,_0x708fac)||_0x4475de[_0x1deec5(0x208)](_0x190b33,_0x16d350);}_0x14a771=_0x4475de['BgyrB'](_0x3b5882,_0x2f2f92,userIDLow,_0xe1e1a0);if(!_0x14a771)return{'hasError':!![],'message':_0x22c187(0x1b0)+new Uint8Array(_0xe1e1a0[_0x22c187(0x353)](0x20d8+0x101f+-0x30f6,0x1f*-0x3+-0x1bec*-0x1+-0x1b7e))};const _0x1da7ad=new Uint8Array(_0xe1e1a0[_0x22c187(0x353)](-0x2*-0x4be+-0x19c9+0x105e,-0x1a1*-0xf+0x4d*-0x4f+-0x9a))[-0x404+0x61c+-0x218],_0x523ca4=new Uint8Array(_0xe1e1a0['slice'](-0x136e+0x1*0x947+0x1*0xa39+_0x1da7ad,_0x4475de[_0x22c187(0x31c)](_0x4475de[_0x22c187(0x31c)](-0x1fb3+0x1*0x243d+-0x478,_0x1da7ad),-0x1*0x2345+0x1627+0x1*0xd1f)))[-0xaf7+-0x1adc+0x1a5*0x17];if(_0x4475de[_0x22c187(0x208)](_0x523ca4,0x1fe7*0x1+-0x1d00+-0x2e6)){}else{if(_0x4475de[_0x22c187(0x205)](_0x523ca4,-0xbeb+-0x5a*-0x9+0x8c3))_0x36a34f=!![];else{const _0x231bd4={};return _0x231bd4[_0x22c187(0x219)]=!![],_0x231bd4['message']='command\x20'+_0x523ca4+_0x22c187(0x24a),_0x231bd4;}}const _0x5248a8=_0x4475de['ZlWDA'](0x171*0xe+-0x165b+0x5*0x73+_0x1da7ad,0xcbe+-0x8*0x425+0x1*0x146b),_0x151c01=_0xe1e1a0['slice'](_0x5248a8,_0x4475de[_0x22c187(0x233)](_0x5248a8,0x2d7+-0x1482+0x389*0x5)),_0x1f35d1=new DataView(_0x151c01)[_0x22c187(0x189)](0x1*0x1693+0xd85+0x6*-0x604);let _0x1ac646=_0x4475de[_0x22c187(0x318)](_0x5248a8,-0x2*-0xc79+0x89*-0x39+0x591);const _0x1b8ee8=new Uint8Array(_0xe1e1a0[_0x22c187(0x353)](_0x1ac646,_0x4475de[_0x22c187(0x15d)](_0x1ac646,0x7f7*-0x3+0x20b0+-0x4b*0x1e))),_0xb670d8=_0x1b8ee8[0x5*0x49b+-0x19*-0xb0+-0x2837];let _0x5842ae=0xec*-0x14+0x1*0x18b5+-0x645,_0x10c711=_0x4475de[_0x22c187(0x363)](_0x1ac646,-0x1bfb+-0x422+0x1*0x201e),_0x5eaa48='';switch(_0xb670d8){case-0x1472+0xb*-0x18e+0x258d:_0x5842ae=-0x4e7*-0x6+0x1aaf+-0x3815,_0x5eaa48=new Uint8Array(_0xe1e1a0[_0x22c187(0x353)](_0x10c711,_0x4475de['aYTye'](_0x10c711,_0x5842ae)))['join']('.');break;case 0x1833+-0x2531+0xd00:_0x5842ae=new Uint8Array(_0xe1e1a0[_0x22c187(0x353)](_0x10c711,_0x10c711+(0x1f9*-0x7+0x1027+-0x1*0x257)))[0x552+0x1e*0x65+-0x1128],_0x10c711+=0x4*-0x1d+0x9*-0x61+0x3de,_0x5eaa48=new TextDecoder()['decode'](_0xe1e1a0[_0x22c187(0x353)](_0x10c711,_0x4475de['gzBYO'](_0x10c711,_0x5842ae)));break;case-0x3a9+-0x85*-0x19+-0x951:_0x5842ae=-0xf06*0x1+0x6d3+0x843;const _0x41e369=new DataView(_0xe1e1a0[_0x22c187(0x353)](_0x10c711,_0x4475de[_0x22c187(0x2cf)](_0x10c711,_0x5842ae))),_0x471a6f=[];for(let _0xca6be1=-0xa12*0x2+0x11*-0x3b+0x3*0x805;_0xca6be1<-0x381+-0x10e0+0x1469;_0xca6be1++){_0x471a6f['push'](_0x41e369[_0x22c187(0x189)](_0x4475de[_0x22c187(0x344)](_0xca6be1,-0xfc7+0x2*0x9ab+-0x9*0x65))['toString'](-0x1*-0xc37+0x14fc+-0x2123));}_0x5eaa48=_0x471a6f[_0x22c187(0x14f)](':');break;default:const _0x3179cc={};_0x3179cc['hasError']=!![],_0x3179cc[_0x22c187(0x325)]='invild\x20addressType\x20is\x20'+_0xb670d8;return _0x3179cc;}if(!_0x5eaa48){const _0x5395e0={};return _0x5395e0[_0x22c187(0x219)]=!![],_0x5395e0[_0x22c187(0x325)]=_0x22c187(0x1e1)+_0xb670d8,_0x5395e0;}return{'hasError':![],'addressRemote':_0x5eaa48,'addressType':_0xb670d8,'portRemote':_0x1f35d1,'rawDataIndex':_0x4475de[_0x22c187(0x31c)](_0x10c711,_0x5842ae),'vlessVersion':_0x5d7f95,'isUDP':_0x36a34f};}async function remoteSocketToWS(_0x301005,_0x138cfd,_0x5ecd0e,_0x4efdef,_0x4beac6){const _0x595b34=_0x53c3,_0x54a136={'egkmR':function(_0x516d5b,_0x341f45){return _0x516d5b!==_0x341f45;},'PEkGZ':'webSocket.readyState\x20is\x20not\x20open,\x20maybe\x20close','uNBuu':function(_0x378afe,_0x48e41d){return _0x378afe(_0x48e41d);},'JDwFU':function(_0x199577,_0x4a9b23){return _0x199577(_0x4a9b23);},'ypgao':function(_0x39b5bc){return _0x39b5bc();}};let _0x20bc15=-0x1*-0x258c+0x1*-0x1bd7+-0x9b5,_0x3c7b6c=[],_0xe9ef40=_0x5ecd0e,_0xd18f6d=![];await _0x301005['readable'][_0x595b34(0x2f4)](new WritableStream({'start'(){},async 'write'(_0x2b6b1f,_0x2502c0){const _0x24a289=_0x595b34;_0xd18f6d=!![],_0x54a136[_0x24a289(0x167)](_0x138cfd[_0x24a289(0x218)],WS_READY_STATE_OPEN)&&_0x2502c0['error'](_0x54a136['PEkGZ']),_0xe9ef40?(_0x138cfd[_0x24a289(0x26b)](await new Blob([_0xe9ef40,_0x2b6b1f])['arrayBuffer']()),_0xe9ef40=null):_0x138cfd[_0x24a289(0x26b)](_0x2b6b1f);},'close'(){_0x4beac6('remoteConnection!.readable\x20is\x20close\x20with\x20hasIncomingData\x20is\x20'+_0xd18f6d);},'abort'(_0x425115){const _0x3c01fb=_0x595b34;console['error'](_0x3c01fb(0x2c2),_0x425115);}}))['catch'](_0x33ab12=>{const _0x466168=_0x595b34;console[_0x466168(0x35d)](_0x466168(0x293),_0x33ab12['stack']||_0x33ab12),_0x54a136['uNBuu'](safeCloseWebSocket,_0x138cfd);}),_0xd18f6d===![]&&_0x4efdef&&(_0x54a136['JDwFU'](_0x4beac6,_0x595b34(0x247)),_0x54a136[_0x595b34(0x323)](_0x4efdef));}function base64ToArrayBuffer(_0x4b1ae7){const _0x167462=_0x53c3,_0x539d58={'npYWE':function(_0xdd4caf,_0x5ae286){return _0xdd4caf(_0x5ae286);}};if(!_0x4b1ae7){const _0x453cd1={};return _0x453cd1[_0x167462(0x35d)]=null,_0x453cd1;}try{_0x4b1ae7=_0x4b1ae7[_0x167462(0x16c)](/-/g,'+')[_0x167462(0x16c)](/_/g,'/');const _0x455c3c=_0x539d58[_0x167462(0x179)](atob,_0x4b1ae7),_0x5b9710=Uint8Array[_0x167462(0x1cc)](_0x455c3c,_0x3cc61f=>_0x3cc61f[_0x167462(0x2c5)](0x11d1*-0x1+0x101f+0x1b2)),_0x4c8fd1={};return _0x4c8fd1[_0x167462(0x2e4)]=_0x5b9710[_0x167462(0x356)],_0x4c8fd1['error']=null,_0x4c8fd1;}catch(_0x33f26c){const _0x3c737b={};return _0x3c737b[_0x167462(0x35d)]=_0x33f26c,_0x3c737b;}}function isValidUUID(_0xb063da){const _0x357e19=_0x53c3,_0x5b1d0b=/^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;return _0x5b1d0b[_0x357e19(0x282)](_0xb063da);}function _0x53c3(_0x54859a,_0x16cb75){const _0xe21f35=_0x4d85();return _0x53c3=function(_0x91a24d,_0x1e0c67){_0x91a24d=_0x91a24d-(0x1d1*0xc+-0xf0a+0x1*-0x59a);let _0x29053d=_0xe21f35[_0x91a24d];return _0x29053d;},_0x53c3(_0x54859a,_0x16cb75);}const WS_READY_STATE_OPEN=-0x259f+0x1*-0x246a+0x4a0a,WS_READY_STATE_CLOSING=0x85a+-0x44f*-0x4+-0x1994;function safeCloseWebSocket(_0x358d8a){const _0x3cccbd=_0x53c3,_0x56cf2c={};_0x56cf2c['WZmpd']=function(_0x119719,_0x529ddc){return _0x119719===_0x529ddc;};const _0x4fe69c=_0x56cf2c;try{(_0x4fe69c['WZmpd'](_0x358d8a[_0x3cccbd(0x218)],WS_READY_STATE_OPEN)||_0x358d8a[_0x3cccbd(0x218)]===WS_READY_STATE_CLOSING)&&_0x358d8a[_0x3cccbd(0x22e)]();}catch(_0x489c31){console['error'](_0x3cccbd(0x1c1),_0x489c31);}}const byteToHex=[];for(let i=0x152*0xf+-0x103*0x25+0x11a1;i<0xe5*0x14+-0x171*0x17+0x1043;++i){byteToHex[_0x1b843a(0x335)]((i+(-0x469*0x5+-0x25e5*-0x1+-0x28*0x5f))['toString'](-0x1461+0x2*0x1019+0xb1*-0x11)[_0x1b843a(0x353)](0x14bb+0x264f+-0x3b09));}function unsafeStringify(_0x18eb46,_0x2a0785=-0x1e5d+0xb9e+-0x1*-0x12bf){const _0x4232a9=_0x1b843a,_0x2b9953={};_0x2b9953[_0x4232a9(0x13f)]=function(_0x2107a7,_0x1bb5ea){return _0x2107a7+_0x1bb5ea;},_0x2b9953['nGduq']=function(_0x4a6587,_0x27083f){return _0x4a6587+_0x27083f;},_0x2b9953['vwnOq']=function(_0x4c00c5,_0x34c516){return _0x4c00c5+_0x34c516;},_0x2b9953[_0x4232a9(0x36a)]=function(_0x4f022a,_0x34622c){return _0x4f022a+_0x34622c;},_0x2b9953[_0x4232a9(0x1f4)]=function(_0x42698e,_0x3a53a8){return _0x42698e+_0x3a53a8;},_0x2b9953[_0x4232a9(0x2fc)]=function(_0x5262e5,_0x416564){return _0x5262e5+_0x416564;},_0x2b9953[_0x4232a9(0x283)]=function(_0x828b9d,_0x42642d){return _0x828b9d+_0x42642d;},_0x2b9953[_0x4232a9(0x267)]=function(_0x463a09,_0x43c6e2){return _0x463a09+_0x43c6e2;},_0x2b9953['qNMrC']=function(_0x5e5e72,_0x1e1d7c){return _0x5e5e72+_0x1e1d7c;},_0x2b9953[_0x4232a9(0x1ac)]=function(_0x3f73cd,_0x4caa60){return _0x3f73cd+_0x4caa60;},_0x2b9953['yiubn']=function(_0x512e23,_0x9513cf){return _0x512e23+_0x9513cf;},_0x2b9953['HPNyn']=function(_0x5331da,_0x2d2cf6){return _0x5331da+_0x2d2cf6;},_0x2b9953[_0x4232a9(0x2fe)]=function(_0x24b84f,_0x2ecea0){return _0x24b84f+_0x2ecea0;},_0x2b9953['xTXrc']=function(_0x141c70,_0x4bd4e3){return _0x141c70+_0x4bd4e3;},_0x2b9953['oPNmB']=function(_0x3aa10c,_0x68729a){return _0x3aa10c+_0x68729a;},_0x2b9953['WdHaC']=function(_0x291379,_0x5dd6c0){return _0x291379+_0x5dd6c0;},_0x2b9953[_0x4232a9(0x1fb)]=function(_0x1ca9f6,_0x9f9db4){return _0x1ca9f6+_0x9f9db4;};const _0xe8dfb9=_0x2b9953;return _0xe8dfb9[_0x4232a9(0x13f)](_0xe8dfb9[_0x4232a9(0x336)](_0xe8dfb9[_0x4232a9(0x2b0)](_0xe8dfb9[_0x4232a9(0x13f)](_0xe8dfb9[_0x4232a9(0x36a)](_0xe8dfb9[_0x4232a9(0x1f4)](_0xe8dfb9[_0x4232a9(0x1f4)](_0xe8dfb9[_0x4232a9(0x336)](_0xe8dfb9[_0x4232a9(0x1f4)](_0xe8dfb9[_0x4232a9(0x2fc)](_0xe8dfb9[_0x4232a9(0x13f)](_0xe8dfb9[_0x4232a9(0x283)](_0xe8dfb9[_0x4232a9(0x267)](_0xe8dfb9[_0x4232a9(0x24f)](_0xe8dfb9[_0x4232a9(0x1ac)](_0xe8dfb9[_0x4232a9(0x2fc)](byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x146)](_0x2a0785,-0x1270+0xb0e+0x762)]],byteToHex[_0x18eb46[_0x2a0785+(-0x2246*-0x1+0x2f0+-0x27b*0xf)]]),byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x283)](_0x2a0785,-0x746+0x134+0x614)]])+byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x2e1)](_0x2a0785,0x10fb+0x15a*0xf+0x15*-0x1c6)]],'-'),byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x2fe)](_0x2a0785,0x2*-0xe0b+-0xf21*0x1+0x2b3b)]]),byteToHex[_0x18eb46[_0x2a0785+(-0x637*0x4+-0x108b+-0xa5b*-0x4)]]),'-'),byteToHex[_0x18eb46[_0xe8dfb9['HPNyn'](_0x2a0785,-0xe*-0x2b+-0x19a0+-0x7*-0x354)]])+byteToHex[_0x18eb46[_0x2a0785+(-0x1234+-0x2294+0x34cf)]],'-'),byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x176)](_0x2a0785,-0x10d*-0x7+0x1d1a+-0x246d)]]),byteToHex[_0x18eb46[_0xe8dfb9['FbOiK'](_0x2a0785,0x1c9b+0xba7*-0x2+-0x544)]]),'-'),byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x221)](_0x2a0785,0x127d+0x7*-0x2cd+0x128)]]),byteToHex[_0x18eb46[_0xe8dfb9['WdHaC'](_0x2a0785,-0x13ff+0x1*-0x17d2+-0x4*-0xaf7)]])+byteToHex[_0x18eb46[_0x2a0785+(0x164c+-0x1*-0xce+-0x170e)]],byteToHex[_0x18eb46[_0xe8dfb9['HPNyn'](_0x2a0785,-0x2222+0x682+0x1bad)]]),byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x1fb)](_0x2a0785,-0x66d*0x3+-0x10c5+0x241a)]]),byteToHex[_0x18eb46[_0xe8dfb9[_0x4232a9(0x2fe)](_0x2a0785,-0xe69+-0x698*-0x4+-0xbe8)]])[_0x4232a9(0x12d)]();}function stringify(_0x3f1a2d,_0x4a378f=-0x1*-0x751+-0x2*0x10bd+-0x1a29*-0x1){const _0x159a69=_0x1b843a,_0x5c3732={'RGZgH':function(_0x5c876f,_0x1fea6e){return _0x5c876f(_0x1fea6e);}},_0x5bcaba=unsafeStringify(_0x3f1a2d,_0x4a378f);if(!_0x5c3732[_0x159a69(0x1f9)](isValidUUID,_0x5bcaba))throw TypeError(_0x159a69(0x141)+_0x5bcaba);return _0x5bcaba;}async function handleDNSQuery(_0x2f79ac,_0xe14b35,_0x23d95d,_0x1d55e4){const _0x4482e1=_0x1b843a,_0x41f5b1={'KWbuH':function(_0x2bf039,_0x2df26b){return _0x2bf039===_0x2df26b;},'HwxcR':function(_0x3133fd,_0x1b7b7e){return _0x3133fd(_0x1b7b7e);},'avNXT':_0x4482e1(0x192)};try{const _0x1f20d1=_0x41f5b1[_0x4482e1(0x2f7)],_0x34baf6=0x228d+0x144e+0x1*-0x36a6;let _0xc72bc8=_0x23d95d;const _0x3003cb={};_0x3003cb[_0x4482e1(0x1a3)]=_0x1f20d1,_0x3003cb[_0x4482e1(0x32d)]=_0x34baf6;const _0x226ce6=_0x41f5b1['HwxcR'](connect,_0x3003cb);_0x41f5b1[_0x4482e1(0x1df)](_0x1d55e4,_0x4482e1(0x1b1)+_0x1f20d1+':'+_0x34baf6);const _0x452426=_0x226ce6[_0x4482e1(0x2fa)]['getWriter']();await _0x452426['write'](_0x2f79ac),_0x452426[_0x4482e1(0x1b8)](),await _0x226ce6[_0x4482e1(0x361)][_0x4482e1(0x2f4)](new WritableStream({async 'write'(_0x23d09c){const _0x46261e=_0x4482e1;_0x41f5b1[_0x46261e(0x281)](_0xe14b35[_0x46261e(0x218)],WS_READY_STATE_OPEN)&&(_0xc72bc8?(_0xe14b35[_0x46261e(0x26b)](await new Blob([_0xc72bc8,_0x23d09c])[_0x46261e(0x157)]()),_0xc72bc8=null):_0xe14b35[_0x46261e(0x26b)](_0x23d09c));},'close'(){const _0x1a2f9c=_0x4482e1;_0x41f5b1['HwxcR'](_0x1d55e4,_0x1a2f9c(0x222)+_0x1f20d1+_0x1a2f9c(0x21d));},'abort'(_0x54ca5b){const _0x2115d5=_0x4482e1;console[_0x2115d5(0x35d)](_0x2115d5(0x222)+_0x1f20d1+_0x2115d5(0x296),_0x54ca5b);}}));}catch(_0x7de310){console[_0x4482e1(0x35d)](_0x4482e1(0x2fb)+_0x7de310[_0x4482e1(0x325)]);}}async function socks5Connect(_0x3ab5fa,_0x3ec73b,_0x26e4e6,_0x2433c9){const _0x1ddfe9=_0x1b843a,_0x283b3b={'SUcKn':function(_0x39f504,_0x3f26d9){return _0x39f504(_0x3f26d9);},'szJEk':function(_0x42942b,_0xb2e8dd){return _0x42942b(_0xb2e8dd);},'uCeTS':_0x1ddfe9(0x185),'vtZou':function(_0xb3b22d,_0x93bba3){return _0xb3b22d!==_0x93bba3;},'bAzQK':function(_0x57f339,_0x2ae6f3){return _0x57f339===_0x2ae6f3;},'piVRQ':_0x1ddfe9(0x204),'SjdKQ':function(_0x124584,_0x51b0e0){return _0x124584(_0x51b0e0);},'dYVQC':_0x1ddfe9(0x2bc),'SUPbF':function(_0x313a40,_0x3b06a5){return _0x313a40||_0x3b06a5;},'kQJZq':_0x1ddfe9(0x1dd),'vsdbL':function(_0x449dfc,_0x5afb5e){return _0x449dfc!==_0x5afb5e;},'GcRfz':function(_0x59039d,_0x24f748){return _0x59039d(_0x24f748);},'jAGJG':'SOCKS5\x20服务器认证失败','rwvee':function(_0x503371,_0x251343){return _0x503371(_0x251343);},'NmskP':function(_0x4d08e1,_0x3b2f87){return _0x4d08e1>>_0x3b2f87;},'xLUNw':function(_0x544c0b,_0x4ac765){return _0x544c0b&_0x4ac765;},'Idfob':function(_0x662da1,_0x131adc){return _0x662da1(_0x131adc);},'uiHZW':_0x1ddfe9(0x2c4),'Sjotv':function(_0xcc7b3a,_0x5c6ad0){return _0xcc7b3a===_0x5c6ad0;},'TiwIJ':function(_0x2b4535,_0x364f18){return _0x2b4535(_0x364f18);},'lLNvk':_0x1ddfe9(0x158),'KJNua':_0x1ddfe9(0x34d)},{username:_0x3e006e,password:_0xa76435,hostname:_0x59f49e,port:_0x1cb19a}=parsedSocks5Address,_0x369c55={};_0x369c55[_0x1ddfe9(0x1a3)]=_0x59f49e,_0x369c55['port']=_0x1cb19a;const _0x116e39=_0x283b3b[_0x1ddfe9(0x19a)](connect,_0x369c55),_0x14bca4=new Uint8Array([-0xd96+0x6e*-0x23+-0x1ca5*-0x1,0x1c94+-0x1b38*-0x1+-0x37ca,-0x1e11+-0x2*-0x362+0x174d,0x10f1*-0x1+-0x26d9+0x37cc]),_0x306229=_0x116e39[_0x1ddfe9(0x2fa)][_0x1ddfe9(0x132)]();await _0x306229[_0x1ddfe9(0x2ee)](_0x14bca4),_0x283b3b[_0x1ddfe9(0x328)](_0x2433c9,_0x283b3b[_0x1ddfe9(0x1b6)]);const _0x2395f7=_0x116e39[_0x1ddfe9(0x361)][_0x1ddfe9(0x1c3)](),_0x24396e=new TextEncoder();let _0x2403cf=(await _0x2395f7[_0x1ddfe9(0x2df)]())[_0x1ddfe9(0x35e)];if(_0x283b3b[_0x1ddfe9(0x2e3)](_0x2403cf[-0x64b+-0x1*-0x1181+-0xb36],-0x2d*-0x62+0x3*-0xc51+0x13be)){_0x2433c9(_0x1ddfe9(0x1ca)+_0x2403cf[-0x34*-0x58+-0x2*-0x471+-0x1ac2]+_0x1ddfe9(0x17b));return;}if(_0x283b3b[_0x1ddfe9(0x190)](_0x2403cf[0x56*-0x56+0x11d1+0xb14],0xe0d+-0x14ba+0x1*0x7ac)){_0x283b3b[_0x1ddfe9(0x19a)](_0x2433c9,_0x283b3b[_0x1ddfe9(0x235)]);return;}if(_0x2403cf[-0x73f+-0x165c+0x17b*0x14]===-0x903+0x1*-0xb93+0x1498){_0x283b3b[_0x1ddfe9(0x374)](_0x2433c9,_0x283b3b['dYVQC']);if(_0x283b3b[_0x1ddfe9(0x1a4)](!_0x3e006e,!_0xa76435)){_0x2433c9(_0x283b3b[_0x1ddfe9(0x286)]);return;}const _0x28d7ea=new Uint8Array([0x7*0x44f+-0x1*0x83d+-0x15eb,_0x3e006e['length'],..._0x24396e[_0x1ddfe9(0x21a)](_0x3e006e),_0xa76435[_0x1ddfe9(0x32b)],..._0x24396e[_0x1ddfe9(0x21a)](_0xa76435)]);await _0x306229[_0x1ddfe9(0x2ee)](_0x28d7ea),_0x2403cf=(await _0x2395f7[_0x1ddfe9(0x2df)]())[_0x1ddfe9(0x35e)];if(_0x283b3b[_0x1ddfe9(0x142)](_0x2403cf[-0x1329+0x7b+0x6*0x31d],0x3*-0xc89+0x1dff*-0x1+0x439b)||_0x283b3b[_0x1ddfe9(0x2e3)](_0x2403cf[0xc4+-0x14d8+0x1415],0x1*0x25b1+-0xf61+0x330*-0x7)){_0x283b3b[_0x1ddfe9(0x249)](_0x2433c9,_0x283b3b[_0x1ddfe9(0x159)]);return;}}let _0x3aa1af;switch(_0x3ab5fa){case-0xa29+0x1dd2+-0x13a8:_0x3aa1af=new Uint8Array([0x22ec+0x10d5*-0x1+-0x1*0x1216,..._0x3ec73b[_0x1ddfe9(0x178)]('.')[_0x1ddfe9(0x28c)](Number)]);break;case-0x1d35+-0x667+0x2*0x11cf:_0x3aa1af=new Uint8Array([-0x1c*-0x39+-0x45*-0xc+-0x975,_0x3ec73b[_0x1ddfe9(0x32b)],..._0x24396e['encode'](_0x3ec73b)]);break;case-0x22d+-0x3c*-0x55+0x5*-0x38c:_0x3aa1af=new Uint8Array([-0x1*-0x101f+-0x1c91+0xc76,..._0x3ec73b[_0x1ddfe9(0x178)](':')[_0x1ddfe9(0x35c)](_0x13cf36=>[parseInt(_0x13cf36[_0x1ddfe9(0x353)](-0x1e9b+-0xf0f*-0x1+0xf8c,-0x1*-0x469+0x1*-0x134c+0xee5),0xb22+-0xecc+0x3ba),parseInt(_0x13cf36[_0x1ddfe9(0x353)](-0x1fb*-0x5+0x1135+-0xd8d*0x2),-0x23*-0xd7+0x118c+-0x2ee1*0x1)])]);break;default:_0x283b3b[_0x1ddfe9(0x337)](_0x2433c9,_0x1ddfe9(0x1cf)+_0x3ab5fa);return;}const _0x72cea=new Uint8Array([-0x3*-0x81b+-0x2c*-0xa6+-0x8ce*0x6,-0x1020+0x162d+0x3*-0x204,-0x14c5*0x1+-0x1a*0x155+-0x3767*-0x1,..._0x3aa1af,_0x283b3b[_0x1ddfe9(0x34b)](_0x26e4e6,0x473+-0x248b+-0x1010*-0x2),_0x283b3b['xLUNw'](_0x26e4e6,-0xddd+0x1*0x18bc+-0x9e0)]);await _0x306229[_0x1ddfe9(0x2ee)](_0x72cea),_0x283b3b['Idfob'](_0x2433c9,_0x283b3b[_0x1ddfe9(0x163)]),_0x2403cf=(await _0x2395f7[_0x1ddfe9(0x2df)]())['value'];if(_0x283b3b[_0x1ddfe9(0x1f1)](_0x2403cf[-0x1afc+0x1747+-0x19*-0x26],0x268a*-0x1+-0x1174+-0x1bff*-0x2))_0x283b3b['TiwIJ'](_0x2433c9,_0x283b3b['lLNvk']);else{_0x283b3b['szJEk'](_0x2433c9,_0x283b3b[_0x1ddfe9(0x278)]);return;}return _0x306229[_0x1ddfe9(0x1b8)](),_0x2395f7['releaseLock'](),_0x116e39;}function socks5AddressParser(_0x1bd900){const _0x4ccff3=_0x1b843a,_0x38b905={};_0x38b905[_0x4ccff3(0x2f6)]=function(_0x3ab0b0,_0x4ef899){return _0x3ab0b0!==_0x4ef899;},_0x38b905[_0x4ccff3(0x145)]=_0x4ccff3(0x214),_0x38b905[_0x4ccff3(0x25a)]=_0x4ccff3(0x1a8),_0x38b905[_0x4ccff3(0x2f5)]='无效的\x20SOCKS\x20地址格式:IPv6\x20地址必须用方括号括起来,如\x20[2001:db8::1]';const _0x12b348=_0x38b905;let [_0x408960,_0x3b10ae]=_0x1bd900[_0x4ccff3(0x178)]('@')[_0x4ccff3(0x1c7)](),_0x1a9f6a,_0x1b3153,_0x52357b,_0x208e10;if(_0x3b10ae){const _0x11ac10=_0x3b10ae[_0x4ccff3(0x178)](':');if(_0x12b348['MOXFe'](_0x11ac10[_0x4ccff3(0x32b)],0x990+-0x23ab+0x1a1d))throw new Error(_0x12b348['IVnac']);[_0x1a9f6a,_0x1b3153]=_0x11ac10;}const _0x50128f=_0x408960[_0x4ccff3(0x178)](':');_0x208e10=Number(_0x50128f[_0x4ccff3(0x13e)]());if(isNaN(_0x208e10))throw new Error(_0x12b348[_0x4ccff3(0x25a)]);_0x52357b=_0x50128f['join'](':');const _0x539b2f=/^\[.*\]$/;if(_0x52357b[_0x4ccff3(0x294)](':')&&!_0x539b2f['test'](_0x52357b))throw new Error(_0x12b348['JmcYe']);const _0x3b9207={};return _0x3b9207[_0x4ccff3(0x2e7)]=_0x1a9f6a,_0x3b9207[_0x4ccff3(0x12f)]=_0x1b3153,_0x3b9207[_0x4ccff3(0x1a3)]=_0x52357b,_0x3b9207[_0x4ccff3(0x32d)]=_0x208e10,_0x3b9207;}function revertFakeInfo(_0x312e07,_0x4f9dac,_0x5106d3,_0x1705df){const _0x476a64=_0x1b843a,_0x216144={'JISoi':function(_0x21eea9,_0x5217d2){return _0x21eea9(_0x5217d2);},'BFLHi':function(_0x414748,_0x19cfa3){return _0x414748(_0x19cfa3);}};if(_0x1705df)_0x312e07=_0x216144[_0x476a64(0x248)](atob,_0x312e07);_0x312e07=_0x312e07[_0x476a64(0x16c)](new RegExp(fakeUserID,'g'),_0x4f9dac)[_0x476a64(0x16c)](new RegExp(fakeHostName,'g'),_0x5106d3);if(_0x1705df)_0x312e07=_0x216144[_0x476a64(0x2ac)](btoa,_0x312e07);return _0x312e07;}async function MD5MD5(_0x28ff12){const _0x17db4b=_0x1b843a,_0x589e8b={};_0x589e8b['ihqty']='MD5';const _0x43ebb9=_0x589e8b,_0x14c1cc=new TextEncoder(),_0x5422d1=await crypto['subtle'][_0x17db4b(0x1bf)]('MD5',_0x14c1cc[_0x17db4b(0x21a)](_0x28ff12)),_0x323067=Array[_0x17db4b(0x1cc)](new Uint8Array(_0x5422d1)),_0x235834=_0x323067['map'](_0x159be1=>_0x159be1[_0x17db4b(0x2a4)](0x2552+-0x21f6+-0x4*0xd3)[_0x17db4b(0x2ad)](-0x439*0x5+0x1b*-0x12f+0x3514,'0'))[_0x17db4b(0x14f)](''),_0x153a09=await crypto[_0x17db4b(0x20b)][_0x17db4b(0x1bf)](_0x43ebb9['ihqty'],_0x14c1cc[_0x17db4b(0x21a)](_0x235834[_0x17db4b(0x353)](0x1d7e+0x159d*0x1+-0x3314,0x112*0x1+0x1e43+-0x1f3a))),_0x31e3d0=Array[_0x17db4b(0x1cc)](new Uint8Array(_0x153a09)),_0x2bb84a=_0x31e3d0[_0x17db4b(0x28c)](_0x1a9035=>_0x1a9035[_0x17db4b(0x2a4)](-0x1*0xd22+-0x20da+0x2e0c)[_0x17db4b(0x2ad)](0x23d5*0x1+-0x89*-0xd+0x1564*-0x2,'0'))[_0x17db4b(0x14f)]('');return _0x2bb84a[_0x17db4b(0x12d)]();}async function ADD(_0x482478){const _0x2e6e59=_0x1b843a,_0xd8c508={};_0xd8c508['JyaVE']=function(_0x4f0c3b,_0x23ed82){return _0x4f0c3b==_0x23ed82;},_0xd8c508[_0x2e6e59(0x32e)]=function(_0x3a8b7f,_0x315f83){return _0x3a8b7f==_0x315f83;},_0xd8c508[_0x2e6e59(0x2e8)]=function(_0x1b4544,_0x5e660f){return _0x1b4544-_0x5e660f;};const _0x226556=_0xd8c508;var _0x319b21=_0x482478[_0x2e6e59(0x16c)](/[ |"'\r\n]+/g,',')['replace'](/,+/g,',');if(_0x226556['JyaVE'](_0x319b21['charAt'](0x94e+0xbb1+-0x14ff),','))_0x319b21=_0x319b21[_0x2e6e59(0x353)](0x474+0x13ee*0x1+-0x1*0x1861);if(_0x226556['nDXCF'](_0x319b21[_0x2e6e59(0x155)](_0x319b21[_0x2e6e59(0x32b)]-(-0x778*-0x3+0x1cc9+0x2a*-0x138)),','))_0x319b21=_0x319b21[_0x2e6e59(0x353)](-0x146*0x1+0x1*0x12c1+-0x117b,_0x226556[_0x2e6e59(0x2e8)](_0x319b21[_0x2e6e59(0x32b)],0x2675+0x2*-0x3a+-0x2600));const _0x1510e5=_0x319b21[_0x2e6e59(0x178)](',');return _0x1510e5;}async function proxyURL(_0x50eb1a,_0x445ad1){const _0xd0e1d=_0x1b843a,_0x53e993={'RGqpd':function(_0x1836aa,_0x213ce1){return _0x1836aa(_0x213ce1);},'zfpEi':function(_0x3d6115,_0x1e03b4){return _0x3d6115*_0x1e03b4;},'ldUJW':function(_0x5a8705,_0x3857ba){return _0x5a8705==_0x3857ba;},'wGmWm':function(_0x3597a2,_0x2ebb06){return _0x3597a2-_0x2ebb06;},'mZJrQ':function(_0x19eeb0,_0x517e4a){return _0x19eeb0(_0x517e4a);},'iNgzH':_0xd0e1d(0x354)},_0x46972e=await _0x53e993['RGqpd'](ADD,_0x50eb1a),_0x36c4a6=_0x46972e[Math['floor'](_0x53e993[_0xd0e1d(0x186)](Math['random'](),_0x46972e[_0xd0e1d(0x32b)]))];let _0x5f39ce=new URL(_0x36c4a6);console[_0xd0e1d(0x1bd)](_0x5f39ce);let _0x4f6678=_0x5f39ce[_0xd0e1d(0x18f)][_0xd0e1d(0x353)](0xfa7+0x82+-0x1029,-(-0x7f*-0x48+0x13b3+0xad*-0x52))||_0xd0e1d(0x21f),_0x1bfe8e=_0x5f39ce['hostname'],_0x2a1c49=_0x5f39ce[_0xd0e1d(0x254)],_0x2ac7e7=_0x5f39ce[_0xd0e1d(0x25c)];_0x53e993['ldUJW'](_0x2a1c49[_0xd0e1d(0x155)](_0x53e993['wGmWm'](_0x2a1c49[_0xd0e1d(0x32b)],-0xd*0x1ac+0x17*0x25+0x126a)),'/')&&(_0x2a1c49=_0x2a1c49[_0xd0e1d(0x353)](-0x8*0x336+-0xccf*-0x1+0x9d*0x15,-(0x2026*0x1+0xb6b+0x148*-0x22)));_0x2a1c49+=_0x445ad1['pathname'];let _0x106d2a=_0x4f6678+'://'+_0x1bfe8e+_0x2a1c49+_0x2ac7e7,_0x41251b=await _0x53e993[_0xd0e1d(0x187)](fetch,_0x106d2a),_0x3e6153=new Response(_0x41251b[_0xd0e1d(0x360)],{'status':_0x41251b[_0xd0e1d(0x35b)],'statusText':_0x41251b[_0xd0e1d(0x279)],'headers':_0x41251b[_0xd0e1d(0x23d)]});return _0x3e6153[_0xd0e1d(0x23d)][_0xd0e1d(0x195)](_0x53e993['iNgzH'],_0x106d2a),_0x3e6153;}function checkSUB(_0x3902bd){const _0x18a5e5=_0x1b843a,_0x351769={};_0x351769[_0x18a5e5(0x151)]=function(_0x50bf75,_0x16b027){return _0x50bf75==_0x16b027;},_0x351769['lIfjc']=function(_0x1e0845,_0x2b89f5){return _0x1e0845+_0x2b89f5;},_0x351769[_0x18a5e5(0x2cb)]=function(_0x4af5b0,_0x2a076a){return _0x4af5b0+_0x2a076a;},_0x351769[_0x18a5e5(0x32a)]=_0x18a5e5(0x2d6),_0x351769['uSMHR']=_0x18a5e5(0x29d),_0x351769[_0x18a5e5(0x23b)]=_0x18a5e5(0x29b),_0x351769[_0x18a5e5(0x150)]=_0x18a5e5(0x1bb),_0x351769[_0x18a5e5(0x2ba)]=_0x18a5e5(0x1ab),_0x351769[_0x18a5e5(0x12e)]=_0x18a5e5(0x364),_0x351769[_0x18a5e5(0x36f)]='www.gov.ua:2096',_0x351769['yvkYj']='www.gco.gov.qa:8443',_0x351769['RKbjG']=_0x18a5e5(0x220),_0x351769[_0x18a5e5(0x20f)]=_0x18a5e5(0x27d),_0x351769[_0x18a5e5(0x245)]=_0x18a5e5(0x15a),_0x351769[_0x18a5e5(0x26d)]=_0x18a5e5(0x1dc),_0x351769[_0x18a5e5(0x1d3)]=_0x18a5e5(0x1ec),_0x351769[_0x18a5e5(0x236)]=_0x18a5e5(0x2d3),_0x351769[_0x18a5e5(0x1ba)]=_0x18a5e5(0x2f1);const _0x584025=_0x351769;if((!sub||sub=='')&&_0x584025[_0x18a5e5(0x151)](_0x584025[_0x18a5e5(0x188)](_0x584025[_0x18a5e5(0x2cb)](_0x584025['IqULp'](addresses[_0x18a5e5(0x32b)],addressesapi[_0x18a5e5(0x32b)]),addressesnotls[_0x18a5e5(0x32b)])+addressesnotlsapi[_0x18a5e5(0x32b)],addressescsv[_0x18a5e5(0x32b)]),0x220f+-0xe9*0xa+0x1*-0x18f5)){addresses=[_0x584025[_0x18a5e5(0x32a)],_0x584025[_0x18a5e5(0x216)],_0x584025[_0x18a5e5(0x23b)],_0x18a5e5(0x272),_0x584025[_0x18a5e5(0x150)],_0x584025[_0x18a5e5(0x2ba)],_0x584025[_0x18a5e5(0x12e)],_0x584025[_0x18a5e5(0x36f)],_0x584025[_0x18a5e5(0x168)],_0x18a5e5(0x131),_0x584025['RKbjG'],_0x18a5e5(0x12a),_0x584025[_0x18a5e5(0x20f)],_0x18a5e5(0x255),_0x584025[_0x18a5e5(0x245)]];if(_0x3902bd['includes'](_0x584025['uBZww']))addressesnotls=[_0x18a5e5(0x22c),_0x18a5e5(0x207),_0x18a5e5(0x25f),_0x584025[_0x18a5e5(0x1d3)],_0x584025['pCvGR'],_0x584025[_0x18a5e5(0x1ba)]];}}const 啥啥啥_写的这是啥啊=_0x1b843a(0x19d);function 配置信息(_0x1b118d,_0x263558){const _0x555b0e=_0x1b843a,_0xb9368={'tDJcq':function(_0x421d62,_0x21f095){return _0x421d62(_0x21f095);},'wmTtW':_0x555b0e(0x156),'kegLA':_0x555b0e(0x164),'riAlO':_0x555b0e(0x1dc),'XcfAn':_0x555b0e(0x203),'OdFuJ':function(_0x30c4d8,_0x4fda6a){return _0x30c4d8(_0x4fda6a);}},_0x36cf56=_0xb9368[_0x555b0e(0x1f2)](atob,啥啥啥_写的这是啥啊),_0x1cd0e0=FileName;let _0x562ac8=_0x263558,_0x27ba90=-0xa63*0x3+-0x24e0+0x5f*0xbc;const _0x29f3ad=_0x1b118d,_0x4af47a=_0xb9368[_0x555b0e(0x2ca)],_0x661411='ws',_0x502e05=_0x263558,_0x5c2a48=_0x555b0e(0x366);let _0x425db3=['tls',!![]];const _0x46480c=_0x263558,_0x1c5b50=_0xb9368['kegLA'];_0x263558['includes'](_0xb9368[_0x555b0e(0x1c9)])&&(_0x562ac8=_0xb9368['XcfAn'],_0x27ba90=-0x1336+0x192b+-0x5a5,_0x425db3=['',![]]);const _0x4d4d73=_0x36cf56+'://'+_0x29f3ad+'@'+_0x562ac8+':'+_0x27ba90+_0x555b0e(0x237)+_0x4af47a+_0x555b0e(0x180)+_0x425db3[-0x1e2*0x1+-0x8e*0x20+0xe*0x167]+_0x555b0e(0x1e3)+_0x46480c+_0x555b0e(0x2a7)+_0x1c5b50+_0x555b0e(0x1b5)+_0x661411+_0x555b0e(0x32c)+_0x502e05+'&path='+_0xb9368['tDJcq'](encodeURIComponent,_0x5c2a48)+'#'+_0xb9368[_0x555b0e(0x14e)](encodeURIComponent,_0x1cd0e0),_0x24ff24=_0x555b0e(0x31a)+_0x36cf56+_0x555b0e(0x191)+FileName+'\x0a\x20\x20server:\x20'+_0x562ac8+'\x0a\x20\x20port:\x20'+_0x27ba90+_0x555b0e(0x29e)+_0x29f3ad+_0x555b0e(0x2e2)+_0x661411+_0x555b0e(0x246)+_0x425db3[-0x215*0x3+0x9db*0x2+0x6bb*-0x2]+'\x0a\x20\x20udp:\x20false\x0a\x20\x20sni:\x20'+_0x46480c+'\x0a\x20\x20client-fingerprint:\x20'+_0x1c5b50+'\x0a\x20\x20ws-opts:\x0a\x20\x20\x20\x20path:\x20\x22'+_0x5c2a48+_0x555b0e(0x26f)+_0x502e05;return[_0x4d4d73,_0x24ff24];}let subParams=['sub',_0x1b843a(0x138),_0x1b843a(0x264),_0x1b843a(0x31e),_0x1b843a(0x26a),'sb'];async function getVLESSConfig(_0x133680,_0xf0c391,_0x1e362a,_0x8ea612,_0x5cd8a6,_0x3b125c,_0x17aad8){const _0x17c437=_0x1b843a,_0x3fcb97={'DVSep':function(_0x1cc6de,_0x2c2788){return _0x1cc6de==_0x2c2788;},'lZIhb':function(_0x3ee661,_0x2f67b8,_0x524292){return _0x3ee661(_0x2f67b8,_0x524292);},'pgDQU':_0x17c437(0x1dc),'WKzOn':function(_0x3cd1b3,_0x12c7ce){return _0x3cd1b3==_0x12c7ce;},'uZGzE':_0x17c437(0x310),'OIxuV':function(_0x5111c1,_0x380233){return _0x5111c1+_0x380233;},'KcehH':function(_0x3b1007,_0x9f719e){return _0x3b1007*_0x9f719e;},'zdPMa':_0x17c437(0x1ae),'MlEQb':function(_0x5ac4d8,_0x3ab71f){return _0x5ac4d8>_0x3ab71f;},'Tqopg':function(_0x466d9d,_0x50f63d){return _0x466d9d(_0x50f63d);},'jWJXi':_0x17c437(0x1fc),'TRDqg':_0x17c437(0x343),'NQxZl':'\x0a\x20\x20','ITDxX':function(_0x40cac9,_0x2fd7cc){return _0x40cac9!=_0x2fd7cc;},'lRVWg':function(_0x5b4e76,_0x22afe1){return _0x5b4e76>_0x22afe1;},'HTmdN':function(_0x377267,_0x8e0a9a){return _0x377267>_0x8e0a9a;},'eorQK':function(_0x2275cc,_0x1774ef){return _0x2275cc>_0x1774ef;},'GyycJ':function(_0x13ee90,_0x5d86eb){return _0x13ee90>_0x5d86eb;},'qJsoR':function(_0x35f95d,_0x4fc5f3){return _0x35f95d!=_0x4fc5f3;},'HSykR':'true','PVvCx':function(_0x641932,_0x48f1d8){return _0x641932!==_0x48f1d8;},'snZAj':_0x17c437(0x303),'Kskqm':_0x17c437(0x1e8),'FqSBk':_0x17c437(0x275),'vNbvy':_0x17c437(0x23c),'Hyrre':_0x17c437(0x350),'mUnEp':function(_0x2c74fb,_0x38c1ff){return _0x2c74fb(_0x38c1ff);},'lqGNV':function(_0x50973c,_0x5a3b9d){return _0x50973c==_0x5a3b9d;},'XAoYI':_0x17c437(0x1ef),'aYXLp':function(_0x184d67,_0x16970a){return _0x184d67==_0x16970a;},'edOTv':'TRUE','XzoMk':function(_0x126e36,_0x10cc40){return _0x126e36==_0x10cc40;},'xNYdS':_0x17c437(0x25d),'PXrDI':_0x17c437(0x2bd),'Eqlhc':_0x17c437(0x31e),'IqrCL':_0x17c437(0x289),'BbPoR':function(_0x152cb,_0x5007e5){return _0x152cb(_0x5007e5);},'cNMjh':_0x17c437(0x1c4),'jjkxE':_0x17c437(0x26a),'OuNZY':function(_0x31b258,_0xcfde4a){return _0x31b258(_0xcfde4a);},'pGuXl':function(_0x2ae172,_0x55ac76){return _0x2ae172==_0x55ac76;},'mDZzA':function(_0x2bee13,_0x27e8f6,_0x4ddbf2,_0x1e6878,_0x2a0232,_0x447984,_0x553aa2,_0x23082d){return _0x2bee13(_0x27e8f6,_0x4ddbf2,_0x1e6878,_0x2a0232,_0x447984,_0x553aa2,_0x23082d);},'LUNFq':function(_0x2f1382,_0x2e4c55){return _0x2f1382==_0x2e4c55;},'SebJF':'Error\x20fetching\x20content:'},_0x5536f6=_0x3fcb97['DVSep'](_0x3b125c[_0x17c437(0x254)],'/'+_0x17aad8[_0x17c437(0x14b)])?_0x17aad8[_0x17c437(0x14b)]:_0x133680;checkSUB(_0xf0c391);const _0x502c49=_0x8ea612[_0x17c437(0x12d)](),_0x4e6f43=_0x3fcb97[_0x17c437(0x22d)](配置信息,_0x133680,_0xf0c391),_0x909991=_0x4e6f43[0xbb1+0xc0a+-0x17bb],_0xd43959=_0x4e6f43[-0xfc+-0xefb+0xff8];let _0x36a2fe='';if(_0xf0c391[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x27e)])||_0xf0c391[_0x17c437(0x294)](_0x17c437(0x275))){if(proxyhostsURL&&(!proxyhosts||_0x3fcb97[_0x17c437(0x1d6)](proxyhosts[_0x17c437(0x32b)],-0x2612+-0xa53+-0xd*-0x3b9)))try{const _0x119bc1=await fetch(proxyhostsURL);if(!_0x119bc1['ok']){console[_0x17c437(0x35d)](_0x3fcb97[_0x17c437(0x30a)],_0x119bc1['status'],_0x119bc1[_0x17c437(0x279)]);return;}const _0x2fd7c0=await _0x119bc1[_0x17c437(0x250)](),_0x4dff78=_0x2fd7c0['split']('\x0a'),_0x447628=_0x4dff78['filter'](_0x336b2a=>_0x336b2a[_0x17c437(0x321)]()!=='');proxyhosts=proxyhosts[_0x17c437(0x338)](_0x447628);}catch(_0x3954c4){}if(proxyhosts[_0x17c437(0x32b)]!=-0x1*0x8bf+0x1e0b*-0x1+0x6*0x677)_0x36a2fe=_0x3fcb97[_0x17c437(0x2d7)](proxyhosts[Math[_0x17c437(0x148)](_0x3fcb97[_0x17c437(0x28b)](Math[_0x17c437(0x15b)](),proxyhosts[_0x17c437(0x32b)]))],'/');}if(_0x502c49[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x339)])&&!subParams[_0x17c437(0x14d)](_0x4ee799=>_0x3b125c[_0x17c437(0x224)][_0x17c437(0x183)](_0x4ee799))){const _0x41cba6=socks5s[_0x17c437(0x28c)](_0x563ca1=>{const _0x28ca56=_0x17c437;if(_0x563ca1[_0x28ca56(0x294)]('@'))return _0x563ca1[_0x28ca56(0x178)]('@')[0x427*-0x7+0x139*0xd+-0x1*-0xd2d];else{if(_0x563ca1[_0x28ca56(0x294)]('//'))return _0x563ca1[_0x28ca56(0x178)]('//')[-0x1*0x1253+-0x739*-0x3+0x1*-0x357];else return _0x563ca1;}});let _0x1d9c2e='';if(_0x3fcb97[_0x17c437(0x17d)](go2Socks5s['length'],0x2e7+0xca+-0x3b1)&&enableSocks){_0x1d9c2e=''+_0x3fcb97[_0x17c437(0x15c)](decodeURIComponent,_0x17c437(0x341));if(go2Socks5s['includes'](_0x3fcb97[_0x17c437(0x15c)](atob,'YWxsIGlu'))||go2Socks5s[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x15c)](atob,_0x3fcb97[_0x17c437(0x242)])))_0x1d9c2e+=_0x3fcb97['Tqopg'](decodeURIComponent,_0x3fcb97[_0x17c437(0x2b7)])+'\x0a';else _0x1d9c2e+=_0x17c437(0x1b4)+go2Socks5s[_0x17c437(0x14f)](_0x3fcb97['NQxZl'])+'\x0a';}let _0x43d387='\x0a';if(!_0x1e362a||_0x3fcb97[_0x17c437(0x1d6)](_0x1e362a,'')){if(enableSocks)_0x43d387+='CFCDN(访问方式):\x20Socks5\x0a\x20\x20'+_0x41cba6[_0x17c437(0x14f)](_0x17c437(0x1b4))+'\x0a'+_0x1d9c2e;else{if(proxyIP&&_0x3fcb97[_0x17c437(0x362)](proxyIP,''))_0x43d387+='CFCDN(访问方式):\x20ProxyIP\x0a\x20\x20'+proxyIPs[_0x17c437(0x14f)](_0x3fcb97['NQxZl'])+'\x0a';else _0x43d387+=_0x17c437(0x331);}_0x43d387+=_0x17c437(0x376);if(_0x3fcb97['lRVWg'](addresses[_0x17c437(0x32b)],0x13f1+0x337*0x9+-0x170*0x22))_0x43d387+=_0x17c437(0x1bc)+addresses[_0x17c437(0x14f)](_0x3fcb97[_0x17c437(0x2aa)])+'\x0a';if(_0x3fcb97['HTmdN'](addressesnotls[_0x17c437(0x32b)],-0x25d1+-0x6ee*-0x5+0x32b))_0x43d387+=_0x17c437(0x1d9)+addressesnotls[_0x17c437(0x14f)](_0x3fcb97[_0x17c437(0x2aa)])+'\x0a';if(_0x3fcb97[_0x17c437(0x307)](addressesapi[_0x17c437(0x32b)],-0x127*0x5+-0xc6d*-0x1+-0x6aa))_0x43d387+=_0x17c437(0x166)+addressesapi[_0x17c437(0x14f)](_0x3fcb97['NQxZl'])+'\x0a';if(_0x3fcb97[_0x17c437(0x311)](addressesnotlsapi[_0x17c437(0x32b)],0x4*0x2ab+0x17*-0x5e+-0x23a))_0x43d387+='ADDNOTLSAPI(noTLS优选域名&IP\x20的\x20API):\x20\x0a\x20\x20'+addressesnotlsapi['join'](_0x3fcb97[_0x17c437(0x2aa)])+'\x0a';if(_0x3fcb97[_0x17c437(0x1d7)](addressescsv[_0x17c437(0x32b)],0x16d6+0x1*-0x1b08+0x432))_0x43d387+=_0x17c437(0x1f3)+DLS+_0x17c437(0x227)+addressescsv['join'](_0x17c437(0x1b4))+'\x0a';}else{if(enableSocks)_0x43d387+=_0x17c437(0x322)+_0x41cba6[_0x17c437(0x14f)](_0x3fcb97['NQxZl'])+'\x0a'+_0x1d9c2e;else{if(proxyIP&&_0x3fcb97[_0x17c437(0x230)](proxyIP,''))_0x43d387+=_0x17c437(0x18c)+proxyIPs[_0x17c437(0x14f)](_0x17c437(0x1b4))+'\x0a';else{if(_0x3fcb97[_0x17c437(0x1e6)](_0x5cd8a6,_0x3fcb97['HSykR']))_0x43d387+=_0x17c437(0x2c9);else _0x43d387+=_0x17c437(0x331);}}_0x43d387+='\x0aSUB(优选订阅生成器):\x20'+_0x1e362a;}if(_0x17aad8[_0x17c437(0x14b)]&&_0x3fcb97[_0x17c437(0x312)](_0x3b125c[_0x17c437(0x254)],'/'+_0x17aad8['KEY']))_0x43d387='';else _0x43d387+='\x0aSUBAPI(订阅转换后端):\x20'+subProtocol+_0x17c437(0x22f)+subconverter+'\x0aSUBCONFIG(订阅转换配置文件):\x20'+subconfig;const _0xbb86c9=_0x5536f6!=_0x133680?'TOKEN:\x20'+_0x5536f6+_0x17c437(0x177)+_0x133680+_0x17c437(0x130)+userIDLow+'\x0a'+userIDTime+_0x17c437(0x265)+effectiveTime+_0x17c437(0x269)+updateTime+_0x17c437(0x347):''+userIDTime;return _0x17c437(0x327)+_0x36a2fe+_0xf0c391+'/'+_0x5536f6+_0x17c437(0x211)+_0x36a2fe+_0xf0c391+'/'+_0x5536f6+'?sub\x0a\x0aBase64订阅地址:\x0ahttps://'+_0x36a2fe+_0xf0c391+'/'+_0x5536f6+_0x17c437(0x367)+_0x36a2fe+_0xf0c391+'/'+_0x5536f6+_0x17c437(0x244)+_0x36a2fe+_0xf0c391+'/'+_0x5536f6+_0x17c437(0x1f5)+_0x36a2fe+_0xf0c391+'/'+_0x5536f6+'?sb\x0ahttps://'+_0x36a2fe+_0xf0c391+'/'+_0x5536f6+'?singbox\x0a---------------------------------------------------------------\x0a################################################################\x0a'+FileName+_0x17c437(0x319)+_0xbb86c9+_0x17c437(0x252)+_0xf0c391+_0x17c437(0x2ae)+_0x133680+_0x17c437(0x258)+fakeUserID+_0x17c437(0x17e)+_0x8ea612+'\x0a'+_0x43d387+_0x17c437(0x290)+_0x909991+_0x17c437(0x2da)+_0xd43959+'\x0a---------------------------------------------------------------\x0a################################################################\x0atelegram\x20交流群\x20技术大佬~在线发牌!\x0ahttps://t.me/CMLiussss\x0a---------------------------------------------------------------\x0agithub\x20项目地址\x20Star!Star!Star!!!\x0ahttps://github.com/cmliu/edgetunnel\x0a---------------------------------------------------------------\x0a################################################################\x0a';}else{if(typeof fetch!=_0x3fcb97['snZAj'])return _0x17c437(0x1d2);let _0x189f5a=[],_0x5eb9aa=[],_0x1fc40a=[],_0x42f1b6=[];if(_0xf0c391[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x27e)]))noTLS=_0x17c437(0x369),fakeHostName=fakeHostName+_0x17c437(0x1dc),_0x1fc40a=await getAddressesapi(addressesnotlsapi),_0x42f1b6=await getAddressescsv(_0x3fcb97['Kskqm']);else{if(_0xf0c391['includes'](_0x3fcb97['FqSBk']))fakeHostName=fakeHostName+'.pages.dev';else _0xf0c391[_0x17c437(0x294)](_0x3fcb97['vNbvy'])||_0xf0c391[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x226)])||_0x3fcb97[_0x17c437(0x1d6)](noTLS,_0x3fcb97[_0x17c437(0x2ce)])?(noTLS=_0x3fcb97['HSykR'],fakeHostName=_0x17c437(0x350)+fakeHostName+_0x17c437(0x28a),_0x1fc40a=await _0x3fcb97[_0x17c437(0x15c)](getAddressesapi,addressesnotlsapi),_0x42f1b6=await _0x3fcb97[_0x17c437(0x1a0)](getAddressescsv,_0x3fcb97[_0x17c437(0x2b1)])):fakeHostName=fakeHostName+'.xyz';}console[_0x17c437(0x1bd)](_0x17c437(0x1ed)+fakeHostName);let _0x5c0dfc=subProtocol+_0x17c437(0x22f)+_0x1e362a+_0x17c437(0x238)+fakeHostName+_0x17c437(0x2b6)+fakeUserID+_0x17c437(0x304)+_0x5cd8a6,_0x22c77a=!![];if(!_0x1e362a||_0x3fcb97[_0x17c437(0x270)](_0x1e362a,'')){if(_0xf0c391[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x2bf)])||_0xf0c391[_0x17c437(0x294)](_0x17c437(0x206))){if(proxyhostsURL&&(!proxyhosts||_0x3fcb97['aYXLp'](proxyhosts[_0x17c437(0x32b)],-0x11*-0x151+0x15*0xc3+-0x2660)))try{const _0x1ceec6=await _0x3fcb97['Tqopg'](fetch,proxyhostsURL);if(!_0x1ceec6['ok']){console[_0x17c437(0x35d)](_0x17c437(0x310),_0x1ceec6[_0x17c437(0x35b)],_0x1ceec6[_0x17c437(0x279)]);return;}const _0x533989=await _0x1ceec6[_0x17c437(0x250)](),_0x35d116=_0x533989['split']('\x0a'),_0x46ff75=_0x35d116[_0x17c437(0x27c)](_0x4e50ee=>_0x4e50ee[_0x17c437(0x321)]()!=='');proxyhosts=proxyhosts[_0x17c437(0x338)](_0x46ff75);}catch(_0x1b0865){console[_0x17c437(0x35d)](_0x3fcb97[_0x17c437(0x30a)],_0x1b0865);}proxyhosts=[...new Set(proxyhosts)];}_0x189f5a=await _0x3fcb97['Tqopg'](getAddressesapi,addressesapi),_0x5eb9aa=await _0x3fcb97[_0x17c437(0x15c)](getAddressescsv,_0x3fcb97[_0x17c437(0x36e)]),_0x5c0dfc=_0x17c437(0x1fa)+_0xf0c391+'/'+fakeUserID;if(_0xf0c391[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x181)])||_0xf0c391['includes'](_0x3fcb97[_0x17c437(0x226)])||_0x3fcb97['XzoMk'](noTLS,_0x3fcb97[_0x17c437(0x2ce)]))_0x5c0dfc+=_0x3fcb97['xNYdS'];console[_0x17c437(0x1bd)](_0x17c437(0x2f8)+_0x5c0dfc);}if(!_0x502c49[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x199)][_0x17c437(0x12d)]())){if(_0x502c49[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x174)])&&!_0x502c49[_0x17c437(0x294)]('nekobox')||_0x3b125c['searchParams'][_0x17c437(0x183)](_0x3fcb97['Eqlhc'])&&!_0x502c49[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x144)]))_0x5c0dfc=subProtocol+_0x17c437(0x22f)+subconverter+'/sub?target=clash&url='+_0x3fcb97['BbPoR'](encodeURIComponent,_0x5c0dfc)+_0x17c437(0x2d8)+_0x3fcb97['BbPoR'](encodeURIComponent,subconfig)+_0x17c437(0x1c2),_0x22c77a=![];else(_0x502c49['includes'](_0x3fcb97[_0x17c437(0x24c)])||_0x502c49[_0x17c437(0x294)](_0x3fcb97['jjkxE'])||(_0x3b125c['searchParams'][_0x17c437(0x183)](_0x3fcb97[_0x17c437(0x314)])||_0x3b125c[_0x17c437(0x224)][_0x17c437(0x183)]('sb'))&&!_0x502c49[_0x17c437(0x294)](_0x3fcb97[_0x17c437(0x144)]))&&(_0x5c0dfc=subProtocol+_0x17c437(0x22f)+subconverter+_0x17c437(0x1de)+_0x3fcb97[_0x17c437(0x14c)](encodeURIComponent,_0x5c0dfc)+_0x17c437(0x2d8)+_0x3fcb97[_0x17c437(0x15c)](encodeURIComponent,subconfig)+_0x17c437(0x1c2),_0x22c77a=![]);}try{let _0x46619b;if((!_0x1e362a||_0x3fcb97['aYXLp'](_0x1e362a,''))&&_0x3fcb97[_0x17c437(0x309)](_0x22c77a,!![]))_0x46619b=await _0x3fcb97['mDZzA'](subAddresses,fakeHostName,fakeUserID,noTLS,_0x189f5a,_0x5eb9aa,_0x1fc40a,_0x42f1b6);else{const _0x166b83={};_0x166b83[_0x17c437(0x2c8)]=_0x8ea612+_0x17c437(0x194);const _0x5ba426={};_0x5ba426[_0x17c437(0x23d)]=_0x166b83;const _0x5e4ccb=await fetch(_0x5c0dfc,_0x5ba426);_0x46619b=await _0x5e4ccb[_0x17c437(0x250)]();}if(_0x3fcb97[_0x17c437(0x140)](_0x3b125c[_0x17c437(0x254)],'/'+fakeUserID))return _0x46619b;return revertFakeInfo(_0x46619b,_0x133680,_0xf0c391,_0x22c77a);}catch(_0x26dbf9){return console['error'](_0x3fcb97['SebJF'],_0x26dbf9),_0x17c437(0x334)+_0x26dbf9[_0x17c437(0x325)];}}}async function getAccountId(_0x34704e,_0x41e746){const _0x2dcacd=_0x1b843a,_0x455c4c={};_0x455c4c['xwQmq']=_0x2dcacd(0x15e);const _0x362a88=_0x455c4c;try{const _0x42781b=_0x362a88['xwQmq'],_0x1c1730={};_0x1c1730[_0x2dcacd(0x143)]=_0x34704e,_0x1c1730[_0x2dcacd(0x271)]=_0x41e746;const _0xc1f04d=new Headers(_0x1c1730),_0x2deb9a={};_0x2deb9a[_0x2dcacd(0x23d)]=_0xc1f04d;const _0x4485e8=await fetch(_0x42781b,_0x2deb9a),_0x174c92=await _0x4485e8[_0x2dcacd(0x30b)]();return _0x174c92[_0x2dcacd(0x1b2)][-0xd21+0x1ea3*-0x1+0x15e2*0x2]['id'];}catch(_0x330615){return![];}}async function getSum(_0x457d29,_0x416b9e,_0x52961e,_0x5973ea,_0x407669,_0xcc67de){const _0x44caac=_0x1b843a,_0x3808a4={'zOxty':_0x44caac(0x2c6),'MaxAE':function(_0x4636b4,_0x351b66,_0x304576){return _0x4636b4(_0x351b66,_0x304576);},'QKXcc':_0x44caac(0x2e5),'yLDOX':function(_0x5a1dd5,_0x28b047){return _0x5a1dd5&&_0x28b047;}};try{const _0x3b8224=new Date(_0x407669)['toISOString'](),_0xae0502=new Date(_0xcc67de)['toISOString'](),_0x4c1f8e={};_0x4c1f8e[_0x44caac(0x1d1)]=_0x3b8224,_0x4c1f8e[_0x44caac(0x225)]=_0xae0502;const _0x53e6b0={};_0x53e6b0[_0x44caac(0x171)]=_0x457d29,_0x53e6b0[_0x44caac(0x27c)]=_0x4c1f8e;const _0x573e06={};_0x573e06[_0x44caac(0x1ce)]=_0x44caac(0x17a),_0x573e06['variables']=_0x53e6b0;const _0x3efe0a=JSON[_0x44caac(0x198)](_0x573e06),_0x14ff55={};_0x14ff55[_0x44caac(0x1d0)]=_0x3808a4['zOxty'],_0x14ff55['X-AUTH-EMAIL']=_0x52961e,_0x14ff55['X-AUTH-KEY']=_0x5973ea;const _0x164c01=new Headers(_0x14ff55),_0xce2b5f=await _0x3808a4['MaxAE'](fetch,'https://api.cloudflare.com/client/v4/graphql',{'method':_0x3808a4[_0x44caac(0x357)],'headers':_0x164c01,'body':_0x3efe0a});if(!_0xce2b5f['ok'])throw new Error(_0x44caac(0x18a)+_0xce2b5f['status']);const _0x59d67e=await _0xce2b5f[_0x44caac(0x30b)](),_0x1b51b1=_0x59d67e?.['data']?.[_0x44caac(0x197)]?.[_0x44caac(0x1e0)]?.[_0x416b9e]?.[_0x44caac(0x372)],_0x233680=_0x59d67e?.[_0x44caac(0x13d)]?.['viewer']?.[_0x44caac(0x1e0)]?.[_0x416b9e]?.[_0x44caac(0x23a)];if(_0x3808a4[_0x44caac(0x24d)](!_0x1b51b1,!_0x233680))throw new Error(_0x44caac(0x35a));const _0x2f1753=_0x1b51b1[_0x44caac(0x135)]((_0x2b35ed,_0x4aa8da)=>_0x2b35ed+_0x4aa8da?.[_0x44caac(0x133)][_0x44caac(0x19c)],0x89a*0x4+0x2dd+0x7*-0x553),_0x14bd6c=_0x233680[_0x44caac(0x135)]((_0x57d676,_0x5bcd1c)=>_0x57d676+_0x5bcd1c?.[_0x44caac(0x133)]['requests'],0xfa8+0x97*-0x38+-0x22c*-0x8);return[_0x2f1753,_0x14bd6c];}catch(_0x542529){return[-0xf*0xd3+-0x1e*-0x14c+-0x1a8b,0x10c7*0x1+0xbc0+-0x43*0x6d];}}let proxyIPPool=[];async function getAddressesapi(_0xf2a48f){const _0x1cb51e=_0x1b843a,_0x1217c2={'zjjbo':function(_0x414c79,_0x43263c,_0x416682){return _0x414c79(_0x43263c,_0x416682);},'mPfwP':function(_0x57ae0e,_0xc63494){return _0x57ae0e===_0xc63494;},'GInWm':'fulfilled','cihdV':'proxyip=true','hSSZf':function(_0x2deffa,_0x7feded){return _0x2deffa(_0x7feded);},'JLURe':function(_0x407bbf,_0x503bba){return _0x407bbf+_0x503bba;}};if(!_0xf2a48f||_0xf2a48f[_0x1cb51e(0x32b)]===-0x1*-0x35d+-0x2526*-0x1+0x2883*-0x1)return[];let _0x5661b2='';const _0x493609=new AbortController(),_0xa9165=_0x1217c2[_0x1cb51e(0x2b9)](setTimeout,()=>{const _0x3f8f84=_0x1cb51e;_0x493609[_0x3f8f84(0x1ad)]();},0x517+-0x1d00+0x3*0xa93);try{const _0x57230b={};_0x57230b[_0x1cb51e(0x2bb)]='text/html,application/xhtml+xml,application/xml;',_0x57230b[_0x1cb51e(0x2c8)]=_0x1cb51e(0x1fd);const _0x216125={};_0x216125[_0x1cb51e(0x373)]=_0x1cb51e(0x2d1),_0x216125['headers']=_0x57230b,_0x216125[_0x1cb51e(0x19f)]=_0x493609[_0x1cb51e(0x19f)];const _0x577c32=await Promise[_0x1cb51e(0x136)](_0xf2a48f[_0x1cb51e(0x28c)](_0x570cb2=>fetch(_0x570cb2,_0x216125)[_0x1cb51e(0x26c)](_0x490c93=>_0x490c93['ok']?_0x490c93['text']():Promise[_0x1cb51e(0x2a9)]())));for(const [_0x512206,_0x540b49]of _0x577c32[_0x1cb51e(0x154)]()){if(_0x1217c2['mPfwP'](_0x540b49[_0x1cb51e(0x35b)],_0x1217c2[_0x1cb51e(0x1b3)])){const _0x50b007=await _0x540b49[_0x1cb51e(0x35e)];_0xf2a48f[_0x512206][_0x1cb51e(0x294)](_0x1217c2[_0x1cb51e(0x229)])&&(proxyIPPool=proxyIPPool[_0x1cb51e(0x338)]((await _0x1217c2[_0x1cb51e(0x33c)](ADD,_0x50b007))[_0x1cb51e(0x28c)](_0x16ded1=>{const _0x3d5e85=_0x1cb51e,_0x29acd5=_0x16ded1[_0x3d5e85(0x178)]('#')[0x1f9f+0x1917+-0x38b6]||_0x16ded1;if(_0x29acd5['includes'](':')){const _0x10c1c2=_0x29acd5[_0x3d5e85(0x178)](':')[0x10ce+-0x1b70+0xaa3];if(!httpsPorts[_0x3d5e85(0x294)](_0x10c1c2))return _0x29acd5;}else return _0x29acd5+_0x3d5e85(0x2b8);return null;})[_0x1cb51e(0x27c)](Boolean))),_0x5661b2+=_0x1217c2[_0x1cb51e(0x134)](_0x50b007,'\x0a');}}}catch(_0x514415){console[_0x1cb51e(0x35d)](_0x514415);}finally{_0x1217c2[_0x1cb51e(0x33c)](clearTimeout,_0xa9165);}const _0x118430=await _0x1217c2[_0x1cb51e(0x33c)](ADD,_0x5661b2);return _0x118430;}async function getAddressescsv(_0x4ac672){const _0x54e3b6=_0x1b843a,_0x439de4={'dcVJr':function(_0x44837a,_0x49b4a7){return _0x44837a===_0x49b4a7;},'hnKCU':function(_0x46bebe,_0x3f0b53){return _0x46bebe(_0x3f0b53);},'zUBnO':'获取CSV地址时出错:','PZLdZ':function(_0x5768e7,_0x5263de){return _0x5768e7+_0x5263de;},'bBGFS':_0x54e3b6(0x371),'EoGwl':function(_0x1737ac,_0x4d7d0d){return _0x1737ac<_0x4d7d0d;},'prwoA':function(_0x98bfd8,_0x16efea){return _0x98bfd8-_0x16efea;},'QcxuT':function(_0x16d6d5,_0x44d618){return _0x16d6d5===_0x44d618;},'jsDIr':function(_0xcd12df,_0x278f64){return _0xcd12df>_0x278f64;},'sTFoT':function(_0x3a4689,_0x38d442){return _0x3a4689(_0x38d442);},'oEOxN':'proxyip=true','RaoYp':function(_0x2eab7e,_0x555c00){return _0x2eab7e==_0x555c00;}};if(!addressescsv||_0x439de4['dcVJr'](addressescsv[_0x54e3b6(0x32b)],0x4e*-0x19+0x1c66+-0x14c8))return[];let _0x4d5a80=[];for(const _0xc67fb0 of addressescsv){try{const _0x80b15c=await _0x439de4[_0x54e3b6(0x2cc)](fetch,_0xc67fb0);if(!_0x80b15c['ok']){console[_0x54e3b6(0x35d)](_0x439de4[_0x54e3b6(0x24b)],_0x80b15c[_0x54e3b6(0x35b)],_0x80b15c['statusText']);continue;}const _0x4083ac=await _0x80b15c[_0x54e3b6(0x250)]();let _0x2ebd51;_0x4083ac['includes']('\x0d\x0a')?_0x2ebd51=_0x4083ac[_0x54e3b6(0x178)]('\x0d\x0a'):_0x2ebd51=_0x4083ac['split']('\x0a');const _0xe578ee=_0x2ebd51[0x11f+0x1ff0+-0x28b*0xd][_0x54e3b6(0x178)](','),_0x2eb5f8=_0xe578ee['indexOf'](_0x54e3b6(0x31f)),_0x51aa62=-0x526+-0xa9c+0xfc2,_0x4f2e3e=0xd79+0x4*-0x18d+-0x744*0x1,_0x301d16=_0x439de4[_0x54e3b6(0x257)](_0x2eb5f8,0x8*-0x20+-0x510+0x611);if(_0x439de4['dcVJr'](_0x2eb5f8,-(-0x312*0x9+-0x1*-0xe82+-0x1*-0xd21))){console[_0x54e3b6(0x35d)](_0x439de4[_0x54e3b6(0x1a2)]);continue;}for(let _0x4cc7ac=0x237a+-0x1c22+-0x757;_0x439de4[_0x54e3b6(0x234)](_0x4cc7ac,_0x2ebd51[_0x54e3b6(0x32b)]);_0x4cc7ac++){const _0x340b29=_0x2ebd51[_0x4cc7ac][_0x54e3b6(0x178)](','),_0x2732bf=_0x439de4['prwoA'](_0x340b29[_0x54e3b6(0x32b)],-0x35*0x3+0xa6c+0x39*-0x2c);if(_0x439de4[_0x54e3b6(0x2f9)](_0x340b29[_0x2eb5f8][_0x54e3b6(0x30c)](),_0x4ac672)&&_0x439de4[_0x54e3b6(0x2a1)](_0x439de4[_0x54e3b6(0x213)](parseFloat,_0x340b29[_0x2732bf]),DLS)){const _0x1b4678=_0x340b29[_0x51aa62],_0x174552=_0x340b29[_0x4f2e3e],_0x13e528=_0x340b29[_0x301d16],_0x3237dc=_0x1b4678+':'+_0x174552+'#'+_0x13e528;_0x4d5a80[_0x54e3b6(0x335)](_0x3237dc),_0xc67fb0[_0x54e3b6(0x294)](_0x439de4[_0x54e3b6(0x302)])&&_0x439de4[_0x54e3b6(0x1da)](_0x340b29[_0x2eb5f8][_0x54e3b6(0x30c)](),_0x54e3b6(0x369))&&!httpsPorts['includes'](_0x174552)&&proxyIPPool[_0x54e3b6(0x335)](_0x1b4678+':'+_0x174552);}}}catch(_0x4a97d2){console[_0x54e3b6(0x35d)](_0x439de4[_0x54e3b6(0x24b)],_0x4a97d2);continue;}}return _0x4d5a80;}function _0x4d85(){const _0x42765b=['statusText','ljqRl','AHxOT','filter','fbi.gov:2087','pgDQU','sec-websocket-protocol','VFPCK','KWbuH','test','FbOiK','tBehs','2052','kQJZq','lcSbq','jDHyN','subconverter','.net','KcehH','map','/socks://','IGLBX',';\x20total=','\x0a---------------------------------------------------------------\x0a################################################################\x0av2ray\x0a---------------------------------------------------------------\x0a','cazFs','/socks5=','remoteSocketToWS\x20has\x20exception\x20','includes','CFID',')\x20TCP\x20连接异常中断','?encryption=none&security=&type=ws&host=','YQPwk','attachment;\x20filename=','https://api.telegram.org/bot','visa.cn:443','tmIap','127.0.0.1:1234#CFnat','\x0a\x20\x20uuid:\x20','retry\x20tcpSocket\x20closed\x20error','UUID','jsDIr','处理\x20TCP\x20出站连接\x20','SzUho','toString','moLJz','AwAcz','&fp=','country','reject','NQxZl','IlQzn','BFLHi','padStart','\x0aUUID:\x20','ZpTlb','vwnOq','Kskqm','SOCKS5','ADDAPI','proxyip','zbKGo','&uuid=','TRDqg',':443','zjjbo','pbDEe','Accept','SOCKS5\x20服务器需要认证','CF-Workers-SUB','vuvlF','XAoYI','bqLMX','41517DQPKka','remoteConnection!.readable\x20abort','SiBgQ','已发送\x20SOCKS5\x20请求','charCodeAt','application/json','22yxkdFU','User-Agent','CFCDN(访问方式):\x20自动获取ProxyIP\x0a','wmTtW','IqULp','hnKCU','oXftD','HSykR','XSxOm','dTyey','get','\x0a国家:\x20','shopify.com:2082','http://','setUTCHours','Join.my.Telegram.channel.CMLiussss.to.unlock.more.premium.nodes.cf.090227.xyz#加入我的频道t.me/CMLiussss解锁更多优选节点','OIxuV','&insert=false&config=','text/html,application/xhtml+xml,application/xml;','\x0a---------------------------------------------------------------\x0a################################################################\x0aclash-meta\x0a---------------------------------------------------------------\x0a','org','BcOFk','GcOfa','setHours','read','false','HPNyn','\x0a\x20\x20network:\x20','vtZou','earlyData','POST','HDjAX','username','NSqfg','DnHcB','mVhKn','tcp\x20','fkTIY','UA:\x20','write','redirect','xThZc','www.visasoutheasteurope.com:2086','readableWebSocketStream\x20已中止','rcRXM','pipeTo','JmcYe','MOXFe','avNXT','虚假订阅:\x20','QcxuT','writable','handleDNSQuery\x20函数发生异常,错误信息:\x20','yRORO','qvCLh','ArLjg','1178181ljBByt','oaIcw','\x0a','oEOxN','function','&edgetunnel=cmliu&proxyip=','cdrbN','555zyUAyk','eorQK',';\x20download=','pGuXl','uZGzE','json','toUpperCase','uTSAd','ADDNOTLSAPI','jgoFR','获取地址时出错:','HTmdN','PVvCx','WebSocket\x20服务器发生错误','jjkxE','\x0aASN:\x20','url','8880','NMsue','\x20配置信息\x0a---------------------------------------------------------------\x0a','-\x20type:\x20','SUBCONFIG','kCuqT','toISOString','clash','TLS','vjnJQ','trim','CFCDN(访问方式):\x20Socks5\x0a\x20\x20','ypgao','aOieJ','message','.tp','\x0a################################################################\x0aSubscribe\x20/\x20sub\x20订阅地址,\x20支持\x20Base64、clash-meta、sing-box\x20订阅格式\x0a---------------------------------------------------------------\x0a快速自适应订阅地址:\x0ahttps://','szJEk','UDP\x20代理仅对\x20DNS(53\x20端口)启用','qDzEv','length','&host=','port','nDXCF','gzip,\x20deflate,\x20br','\x0a城市:\x20','CFCDN(访问方式):\x20无法访问,\x20需要您设置\x20proxyIP/PROXYIP\x20!!!\x0a','rRfBW','Host','Error\x20fetching\x20content:\x20','push','nGduq','rwvee','concat','zdPMa','uwbRA','RPROXYIP','hSSZf','\x0aIP:\x20','&path=','readableWebSocketStream\x20管道错误','websocket','SOCKS5%EF%BC%88%E7%99%BD%E5%90%8D%E5%8D%95%EF%BC%89%3A%20','DLS','%E6%89%80%E6%9C%89%E6%B5%81%E9%87%8F','fyerI','yFHkR','','\x20时(北京时间)\x0a\x0a','uAMaH','socks5','PGKol','NmskP','UBYhW','SOCKS5\x20连接建立失败','Subscription-Userinfo','ZUEHg','notls','sub','TfjQf','slice','X-New-URL','ysaRx','buffer','QKXcc','CFEMAIL','2086','找不到数据','status','flatMap','error','value','DyXDB','body','readable','ITDxX','sWWxg','russia.com:2087','fmhvl','/?ed=2560','?b64\x0ahttps://','addEventListener','true','zVvwd','tUuBG','rGjiM','match','edOTv','EOdLi','TIME','CSV文件缺少必需的字段','pagesFunctionsInvocationsAdaptiveGroups','method','SjdKQ',';\x20expire=','\x0a您的订阅内容由\x20内置\x20addresses/ADD*\x20参数变量提供\x0a','nfAui','2082','\x20已启用临时域名中转服务,请尽快绑定自定义域!','www.wto.org:8443','apEkO','GuBOH','toLowerCase','OykDf','password','\x0aUUIDLow:\x20','www.gov.se','getWriter','sum','JLURe','reduce','allSettled','UQjEG','base64','sxBEY','ceil','substr','yXUio','data','pop','FnZni','LUNFq','生成的\x20UUID\x20不符合规范\x20','vsdbL','X-AUTH-EMAIL','IqrCL','IVnac','yiubn','CF-Connecting-IP','floor','Kbopp','/proxyip.','KEY','OuNZY','some','OdFuJ','join','BZiub','itDgw','getTime','ZwXiD','entries','charAt','none','arrayBuffer','SOCKS5\x20连接已建立','jAGJG','[2606:4700::]#IPv6','random','Tqopg','kcmgQ','https://api.cloudflare.com/client/v4/accounts','closed','SUB','DAGXj','\x0a组织:\x20','uiHZW','randomized','88qixcOx','ADDAPI(TLS优选域名&IP\x20的\x20API):\x20\x0a\x20\x20','egkmR','yvkYj','eRtkS','UXyjQ','cXeLh','replace','proxyip.','#获取订阅\x20','FWnwP','9764jyFOKx','accountId','sqfNN','11aRJkAl','Eqlhc','/sendMessage?chat_id=','xTXrc','\x0aUUIDNow:\x20','split','npYWE','query\x20getBillingMetrics($accountId:\x20String!,\x20$filter:\x20AccountWorkersInvocationsAdaptiveFilter_InputObject)\x20{\x0a\x09\x09\x09\x09viewer\x20{\x0a\x09\x09\x09\x09\x09accounts(filter:\x20{accountTag:\x20$accountId})\x20{\x0a\x09\x09\x09\x09\x09\x09pagesFunctionsInvocationsAdaptiveGroups(limit:\x201000,\x20filter:\x20$filter)\x20{\x0a\x09\x09\x09\x09\x09\x09\x09sum\x20{\x0a\x09\x09\x09\x09\x09\x09\x09\x09requests\x0a\x09\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09\x09workersInvocationsAdaptive(limit:\x2010000,\x20filter:\x20$filter)\x20{\x0a\x09\x09\x09\x09\x09\x09\x09sum\x20{\x0a\x09\x09\x09\x09\x09\x09\x09\x09requests\x0a\x09\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09\x09}\x0a\x09\x09\x09\x09}\x0a\x09\x09\x09}',',期望是\x205','到期时间(UTC):\x20','MlEQb','\x0aUA:\x20','8080','&security=','vNbvy','mdMtT','has','null','已发送\x20SOCKS5\x20问候消息','zfpEi','mZJrQ','lIfjc','getUint16','HTTP\x20error!\x20status:\x20','accept','CFCDN(访问方式):\x20ProxyIP\x0a\x20\x20','SiUCV','sToyv','protocol','bAzQK','\x0a\x20\x20name:\x20','8.8.4.4','jVuWT','\x20CF-Workers-edgetunnel/cmliu','set','ZkNzZ','viewer','stringify','PXrDI','SUcKn','URL','requests','dmxlc3M=','FxpHE','signal','mUnEp','Profile-Update-Interval','bBGFS','hostname','SUPbF','cJUnB','okiUD','TQjYR','无效的\x20SOCKS\x20地址格式:端口号必须是数字','xiGiC','PROXYIP','brazil.com:2083','tBNHL','abort','mozilla','可读流被取消,原因是\x20','invalid\x20user\x20','连接到\x20','result','GInWm','\x0a\x20\x20','&type=','uCeTS','JVsBM','releaseLock','now','cVHsg','japan.com:2053','ADD(TLS优选域名&IP):\x20\x0a\x20\x20','log','WXaRC','digest','ptCFr','safeCloseWebSocket\x20error','&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true','getReader','sing-box','SVDPT','ihmki','reverse','FZltu','riAlO','SOCKS5\x20服务器版本错误:\x20收到\x20','&parse_mode=HTML&text=','from','iCgKp','query','无效的地址类型:\x20','Content-Type','datetime_geq','Error:\x20fetch\x20is\x20not\x20available\x20in\x20this\x20environment.','JNqFF','eNAgy','vpbDP','WKzOn','GyycJ','2443722qCZcjG','ADDNOTLS(noTLS优选域名&IP):\x20\x0a\x20\x20','RaoYp','text/plain;charset=utf-8','.workers.dev','请提供用户名和密码','/sub?target=singbox&url=','HwxcR','accounts','addressValue\x20is\x20empty,\x20addressType\x20is\x20','xozGd','&sni=','UesVr','invalid\x20data','DVSep','pjPtT','FALSE','jNUSa','nookO','VeFfC','www.visaeurope.ch:2052','虚假HOST:\x20','rpxUU','workers.dev','SSTmI','Sjotv','tDJcq','ADDCSV(IPTest测速csv文件\x20限速\x20','qlqJV','?clash\x0a\x0asingbox订阅地址:\x0ahttps://','ADD','rdPGC','4634990BJBhWi','RGZgH','https://','XoVcP','Kg==','CF-Workers-edgetunnel/cmliu','dCnwr','ViBTW','BYqIn','catch','\x20(UTC+8):\x20','visa.cn','服务器不接受任何认证方法','RceGu','pages.dev','myanmar.visa.com:8080','MjkKI','values','FFMQv','subtle','lpdkv','setMilliseconds','KyeJB','RZvhR','/socks5://','\x0ahttps://','bdgWu','sTFoT','无效的\x20SOCKS\x20地址格式:认证部分必须是\x20\x22username:password\x22\x20的形式','finally','uSMHR','114900EaTIuN','readyState','hasError','encode','213661alsgIn','SUBAPI',')\x20TCP\x20连接已关闭','OAZKd','https','time.is','oPNmB','DNS\x20服务器(','city','searchParams','datetime_leq','Hyrre','\x20):\x20\x0a\x20\x20','gCwGP','cihdV','unimU','kLKVO','usa.visa.com:2095','lZIhb','close','://','qJsoR','URL302','CFPORTS','gzBYO','EoGwl','piVRQ','pCvGR','?encryption=','/sub?host=','VZDIm','workersInvocationsAdaptive','UUdOm','worker','headers','cHJveHlpcC50cDEuY21saXVzc3NzLmNvbQ==','Upgrade',';\x20filename*=utf-8\x27\x27','dipVD','jWJXi','xmgpS','?base64\x0a\x0aclash订阅地址:\x0ahttps://','kOYWX','\x0a\x20\x20tls:\x20','retry','JISoi','GcRfz','\x20is\x20not\x20support,\x20command\x2001-tcp,02-udp,03-mux','zUBnO','cNMjh','yLDOX','GO2SOCKS5','qNMrC','text','CFKEY','HOST:\x20','XFBnE','pathname','icook.hk','Mozilla/5.0\x20Chrome/90.0.4430.72','PZLdZ','\x0aFKID:\x20','YWxsIGlu','QokPK','ADDCSV','search','?notls','wGULm','dynadot.com:8880','smqdH','不用怀疑!你UUID就是错的!!!','GuytB','JYkTs','b64','TIME(动态UUID有效时间):\x20','find','MceDb','Ulgru','\x20天\x0aUPTIME(动态UUID更新时间):\x20','singbox','send','then','uBZww','webSocket','\x22\x0a\x20\x20\x20\x20headers:\x0a\x20\x20\x20\x20\x20\x20host:\x20','lqGNV','X-AUTH-KEY','singapore.com:8443','upload=','uaJRE','.pages.dev','&proxyip=','hVpmQ','KJNua'];_0x4d85=function(){return _0x42765b;};return _0x4d85();}function subAddresses(_0x4f82a2,_0x4307e7,_0x5e8563,_0x4f1211,_0x41a785,_0x19ad0c,_0x14ebd7){const _0x4166d8=_0x1b843a,_0x4b2728={'ptCFr':_0x4166d8(0x17f),'fVbSd':_0x4166d8(0x317),'lpdkv':_0x4166d8(0x285),'fmhvl':_0x4166d8(0x359),'KyeJB':function(_0x55e32d,_0x3a523c){return _0x55e32d(_0x3a523c);},'apEkO':function(_0x242f54,_0x343af0){return _0x242f54==_0x343af0;},'wmsYT':function(_0xa82b8e,_0x1ae658){return _0xa82b8e(_0x1ae658);},'xuUuj':function(_0x2662bb,_0x35f5f2){return _0x2662bb(_0x35f5f2);},'IlQzn':function(_0xd03546,_0x1162c9){return _0xd03546+_0x1162c9;},'aHtEu':function(_0x3c4b2d,_0x595191){return _0x3c4b2d(_0x595191);},'vpbDP':function(_0x5b1333,_0x51a90e){return _0x5b1333==_0x51a90e;},'Ulgru':function(_0x228223,_0x3d32e9){return _0x228223==_0x3d32e9;},'rcRXM':'443','vjnJQ':_0x4166d8(0x366),'YGrTc':function(_0x567b99,_0x1da91d){return _0x567b99>_0x1da91d;},'DyXDB':'.workers.dev','nfAui':_0x4166d8(0x206),'oaIcw':function(_0x211963,_0x5eb412){return _0x211963(_0x5eb412);},'cJUnB':function(_0xb42b02,_0x4e32e9){return _0xb42b02(_0x4e32e9);},'lZcOu':function(_0x200a81,_0xfefa29){return _0x200a81==_0xfefa29;},'bsbPV':'true','Leobg':function(_0x46208a,_0x206793){return _0x46208a(_0x206793);}},_0x285de3=/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[.*\]):?(\d+)?#?(.*)?$/;addresses=addresses[_0x4166d8(0x338)](_0x4f1211),addresses=addresses[_0x4166d8(0x338)](_0x41a785);let _0x44690c;if(_0x4b2728['lZcOu'](_0x5e8563,_0x4166d8(0x369))){addressesnotls=addressesnotls[_0x4166d8(0x338)](_0x19ad0c),addressesnotls=addressesnotls[_0x4166d8(0x338)](_0x14ebd7);const _0x9fe697=[...new Set(addressesnotls)];_0x44690c=_0x9fe697[_0x4166d8(0x28c)](_0x3fad26=>{const _0x1e0f50=_0x4166d8;let _0x21fe54='-1',_0x3c2cec=_0x3fad26;const _0x47a307=_0x3c2cec[_0x1e0f50(0x36d)](_0x285de3);if(!_0x47a307){if(_0x3fad26[_0x1e0f50(0x294)](':')&&_0x3fad26[_0x1e0f50(0x294)]('#')){const _0x5e0876=_0x3fad26[_0x1e0f50(0x178)](':');_0x3fad26=_0x5e0876[0x1f65+-0x267b*-0x1+-0x22f0*0x2];const _0xafe81d=_0x5e0876[-0x23d7+0x10e2+0x12f6][_0x1e0f50(0x178)]('#');_0x21fe54=_0xafe81d[0x3*0x27a+0x18d3+-0x2041],_0x3c2cec=_0xafe81d[0x512*-0x1+-0x1d9+0x6ec];}else{if(_0x3fad26['includes'](':')){const _0x3b0eb5=_0x3fad26[_0x1e0f50(0x178)](':');_0x3fad26=_0x3b0eb5[-0x1e23+0x115f+0x331*0x4],_0x21fe54=_0x3b0eb5[-0xec5+0x6f6*-0x2+0x1cb2];}else{if(_0x3fad26['includes']('#')){const _0x3b8866=_0x3fad26[_0x1e0f50(0x178)]('#');_0x3fad26=_0x3b8866[-0x1*-0x1279+-0x20d1+0xe58],_0x3c2cec=_0x3b8866[-0x1*0x1639+0xae9+0xb51];}}}_0x3c2cec['includes'](':')&&(_0x3c2cec=_0x3c2cec['split'](':')[-0x4d5*0x2+-0xd03*-0x3+-0x1d5f]);}else _0x3fad26=_0x47a307[0x3*0x23b+-0x1*-0x133b+-0x19eb*0x1],_0x21fe54=_0x47a307[-0xa9*-0x7+0x1bb5*0x1+-0x2052]||_0x21fe54,_0x3c2cec=_0x47a307[0x1311+0x1039*-0x1+0x91*-0x5]||_0x3fad26;const _0x89dfa3=[_0x4b2728[_0x1e0f50(0x1c0)],_0x4b2728['fVbSd'],_0x4b2728[_0x1e0f50(0x20c)],_0x1e0f50(0x128),_0x4b2728[_0x1e0f50(0x365)],'2095'];if(!_0x4b2728['KyeJB'](isValidIPv4,_0x3fad26)&&_0x4b2728[_0x1e0f50(0x12b)](_0x21fe54,'-1'))for(let _0x18eb38 of _0x89dfa3){if(_0x3fad26[_0x1e0f50(0x294)](_0x18eb38)){_0x21fe54=_0x18eb38;break;}}if(_0x4b2728[_0x1e0f50(0x12b)](_0x21fe54,'-1'))_0x21fe54='80';let _0x128e9f=_0x4f82a2,_0x5b955a=_0x1e0f50(0x366),_0x13a101='';const _0x34fe45=_0x4b2728['wmsYT'](atob,啥啥啥_写的这是啥啊),_0x46fcc9=_0x34fe45+'://'+_0x4307e7+'@'+_0x3fad26+':'+_0x21fe54+_0x1e0f50(0x297)+_0x128e9f+_0x1e0f50(0x33e)+_0x4b2728['xuUuj'](encodeURIComponent,_0x5b955a)+'#'+_0x4b2728[_0x1e0f50(0x20e)](encodeURIComponent,_0x4b2728['IlQzn'](_0x3c2cec,_0x13a101));return _0x46fcc9;})[_0x4166d8(0x14f)]('\x0a');}const _0x12ac71=[...new Set(addresses)],_0x15ffdb=_0x12ac71[_0x4166d8(0x28c)](_0x324170=>{const _0x534d29=_0x4166d8;let _0x2730c7='-1',_0x201a43=_0x324170;const _0xdf78e4=_0x201a43[_0x534d29(0x36d)](_0x285de3);if(!_0xdf78e4){if(_0x324170['includes'](':')&&_0x324170[_0x534d29(0x294)]('#')){const _0x1484b8=_0x324170['split'](':');_0x324170=_0x1484b8[0x152e*0x1+-0x2e*-0x76+-0x2a62];const _0x5dec49=_0x1484b8[0x4da+0x1*-0x2209+0x1d30]['split']('#');_0x2730c7=_0x5dec49[-0x161d+0x10b5+0x568],_0x201a43=_0x5dec49[0x834+-0x1256+0xa23];}else{if(_0x324170[_0x534d29(0x294)](':')){const _0x2ec01e=_0x324170[_0x534d29(0x178)](':');_0x324170=_0x2ec01e[-0x7d*-0x29+0x2*-0x41e+0x1*-0xbc9],_0x2730c7=_0x2ec01e[-0x1d69+0x83f+-0x1*-0x152b];}else{if(_0x324170[_0x534d29(0x294)]('#')){const _0x405482=_0x324170['split']('#');_0x324170=_0x405482[-0x1410+0x601*0x1+0xe0f],_0x201a43=_0x405482[-0x24bb+-0xed7*0x2+0x1*0x426a];}}}_0x201a43[_0x534d29(0x294)](':')&&(_0x201a43=_0x201a43['split'](':')[0x1*0x14ad+0x19*-0x109+0x534*0x1]);}else _0x324170=_0xdf78e4[-0x358*-0x4+-0xefe+0x19f],_0x2730c7=_0xdf78e4[-0x18d*-0x1+-0x362*0x1+-0x1d7*-0x1]||_0x2730c7,_0x201a43=_0xdf78e4[-0x1e19+0x36*-0x1d+-0x121d*-0x2]||_0x324170;if(!_0x4b2728['aHtEu'](isValidIPv4,_0x324170)&&_0x4b2728[_0x534d29(0x1d5)](_0x2730c7,'-1'))for(let _0x411389 of httpsPorts){if(_0x324170['includes'](_0x411389)){_0x2730c7=_0x411389;break;}}if(_0x4b2728[_0x534d29(0x268)](_0x2730c7,'-1'))_0x2730c7=_0x4b2728[_0x534d29(0x2f3)];let _0xd65d9a=_0x4f82a2,_0x416731=_0x4b2728[_0x534d29(0x320)],_0xe79355='';const _0x201c3b=proxyIPPool[_0x534d29(0x266)](_0x29f8ed=>_0x29f8ed[_0x534d29(0x294)](_0x324170));if(_0x201c3b)_0x416731+=_0x534d29(0x276)+_0x201c3b;_0x4b2728['YGrTc'](proxyhosts[_0x534d29(0x32b)],0xb*0x248+0x10d6+-0x1*0x29ee)&&(_0xd65d9a['includes'](_0x4b2728[_0x534d29(0x35f)])||_0xd65d9a[_0x534d29(0x294)](_0x4b2728[_0x534d29(0x377)]))&&(_0x416731='/'+_0xd65d9a+_0x416731,_0xd65d9a=proxyhosts[Math[_0x534d29(0x148)](Math[_0x534d29(0x15b)]()*proxyhosts[_0x534d29(0x32b)])],_0xe79355=_0x534d29(0x129));const _0x2c9dd6=atob(啥啥啥_写的这是啥啊),_0x41bb4d=_0x2c9dd6+_0x534d29(0x22f)+_0x4307e7+'@'+_0x324170+':'+_0x2730c7+'?encryption=none&security=tls&sni='+_0xd65d9a+'&fp=random&type=ws&host='+_0xd65d9a+_0x534d29(0x33e)+_0x4b2728[_0x534d29(0x300)](encodeURIComponent,_0x416731)+'#'+_0x4b2728[_0x534d29(0x1a5)](encodeURIComponent,_0x4b2728[_0x534d29(0x2ab)](_0x201a43,_0xe79355));return _0x41bb4d;})[_0x4166d8(0x14f)]('\x0a');let _0x3a09a2=_0x15ffdb;if(_0x5e8563==_0x4b2728['bsbPV'])_0x3a09a2+='\x0a'+_0x44690c;return _0x4b2728['Leobg'](btoa,_0x3a09a2);}async function sendMessage(_0x4a712b,_0xa3b497,_0x1663e6=''){const _0x42af1e=_0x1b843a,_0x132c7f={'SzUho':function(_0x55a034,_0xd7c845){return _0x55a034!==_0xd7c845;},'sToyv':function(_0x5a7b7e,_0x40f61b){return _0x5a7b7e!==_0x40f61b;},'AHxOT':function(_0x529894,_0x823a20){return _0x529894(_0x823a20);},'ioPJd':function(_0xff91f9,_0x2cd4c1){return _0xff91f9==_0x2cd4c1;},'UBYhW':function(_0x1a7ec4,_0x45cdc1){return _0x1a7ec4+_0x45cdc1;},'pjGyN':function(_0x36ba40,_0x2c173b){return _0x36ba40+_0x2c173b;},'yXUio':function(_0x5727d1,_0xa79a40){return _0x5727d1+_0xa79a40;},'Dyiol':_0x42af1e(0x29a),'HDjAX':_0x42af1e(0x175),'dTyey':_0x42af1e(0x1cb),'sxBEY':function(_0x165e3c,_0x92d846){return _0x165e3c(_0x92d846);},'dCnwr':function(_0xdd39a1,_0x2d3ec1,_0x246f7e){return _0xdd39a1(_0x2d3ec1,_0x246f7e);},'cdrbN':'get','jVuWT':_0x42af1e(0x2d9),'PGKol':_0x42af1e(0x32f),'SiUCV':_0x42af1e(0x256)};if(_0x132c7f[_0x42af1e(0x2a3)](BotToken,'')&&_0x132c7f[_0x42af1e(0x18e)](ChatID,'')){let _0x5e2e43='';const _0x4f5973=await _0x132c7f[_0x42af1e(0x27b)](fetch,'http://ip-api.com/json/'+_0xa3b497+'?lang=zh-CN');if(_0x132c7f['ioPJd'](_0x4f5973[_0x42af1e(0x35b)],-0xba*-0x25+-0x2381+0x967)){const _0x4f7e97=await _0x4f5973[_0x42af1e(0x30b)]();_0x5e2e43=_0x4a712b+_0x42af1e(0x33d)+_0xa3b497+_0x42af1e(0x2d2)+_0x4f7e97[_0x42af1e(0x2a8)]+_0x42af1e(0x330)+_0x4f7e97[_0x42af1e(0x223)]+_0x42af1e(0x162)+_0x4f7e97[_0x42af1e(0x2db)]+_0x42af1e(0x315)+_0x4f7e97['as']+'\x0a'+_0x1663e6;}else _0x5e2e43=_0x4a712b+_0x42af1e(0x33d)+_0xa3b497+_0x42af1e(0x301)+_0x1663e6;let _0xd6ea2b=_0x132c7f[_0x42af1e(0x34c)](_0x132c7f[_0x42af1e(0x34c)](_0x132c7f['pjGyN'](_0x132c7f[_0x42af1e(0x13c)](_0x132c7f['Dyiol'],BotToken),_0x132c7f[_0x42af1e(0x2e6)]),ChatID),_0x132c7f[_0x42af1e(0x2d0)])+_0x132c7f[_0x42af1e(0x139)](encodeURIComponent,_0x5e2e43);return _0x132c7f[_0x42af1e(0x1fe)](fetch,_0xd6ea2b,{'method':_0x132c7f[_0x42af1e(0x305)],'headers':{'Accept':_0x132c7f[_0x42af1e(0x193)],'Accept-Encoding':_0x132c7f[_0x42af1e(0x34a)],'User-Agent':_0x132c7f[_0x42af1e(0x18d)]}});}}function isValidIPv4(_0x15e2c2){const _0x175ff8=_0x1b843a,_0x6bd601=/^(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 _0x6bd601[_0x175ff8(0x282)](_0x15e2c2);}function generateDynamicUUID(_0xac39c4){const _0xc45ac7=_0x1b843a,_0x25c4e6={'GuBOH':function(_0x2e5482,_0x18b61b){return _0x2e5482+_0x18b61b;},'ViBTW':function(_0x42c80e,_0x4f5319){return _0x42c80e-_0x4f5319;},'fkTIY':function(_0x747b60,_0x393279){return _0x747b60*_0x393279;},'hqSCt':function(_0x59cbc,_0x1aeba9){return _0x59cbc*_0x1aeba9;},'bdgWu':function(_0x36c4ca,_0x326e6b){return _0x36c4ca*_0x326e6b;},'mdMtT':function(_0x1f2553,_0x5124de){return _0x1f2553*_0x5124de;},'Pynyk':function(_0x4b3477,_0x27e625){return _0x4b3477/_0x27e625;},'VeFfC':function(_0x16dceb,_0x319322){return _0x16dceb+_0x319322;},'IGLBX':function(_0x4b58cc,_0x57326d){return _0x4b58cc|_0x57326d;},'dqXcg':function(_0x4c387c,_0x59a13f){return _0x4c387c&_0x59a13f;},'aOieJ':'SHA-256','ELLzz':function(_0x1bd71f){return _0x1bd71f();},'uAMaH':function(_0x32eee4,_0x1e2275){return _0x32eee4*_0x1e2275;},'moLJz':function(_0x420e25,_0x4aca23){return _0x420e25(_0x4aca23);},'ZUEHg':function(_0x295e3a,_0x1647c1){return _0x295e3a+_0x1647c1;},'kLKVO':function(_0x1b289d,_0x13709b){return _0x1b289d-_0x13709b;},'jvTbx':function(_0x18272b,_0x52a427){return _0x18272b-_0x52a427;},'cazFs':function(_0x1bb48d,_0xeea8c4){return _0x1bb48d*_0xeea8c4;}};function _0x157eb4(){const _0x52915b=_0x53c3,_0xb7834c=new Date(),_0x1708d4=-0x2*-0x1+0x2*-0x84f+0x10a4,_0x407b55=new Date(_0x25c4e6[_0x52915b(0x12c)](_0xb7834c['getTime'](),_0x1708d4*(0x254d+0x1bd7*0x1+-0x40e8)*(-0x1*-0x14e1+0x1e01+0x6*-0x871)*(-0x1c09*0x1+-0xe28+0x2e19))),_0x1e6303=new Date(0x1528+0xa2c*-0x1+0x17*-0x23,-0x231*-0xb+0x506*0x2+0x2221*-0x1,0x1*-0x14bc+-0xcb4+-0x293*-0xd,updateTime,0x1e70+-0x31*0x2b+0x1*-0x1635,-0x2302+0x3d*-0x3d+-0xb*-0x481),_0x4b884a=_0x25c4e6[_0x52915b(0x1ff)](_0x407b55,_0x1e6303),_0x3f33b5=_0x25c4e6[_0x52915b(0x2ec)](_0x25c4e6['hqSCt'](_0x25c4e6[_0x52915b(0x212)](_0x25c4e6[_0x52915b(0x182)](-0x11b*0x8+0x1b6a+0x2*-0x755,0x1f5b+0x1*-0x132d+-0xbf2),-0x25af+0x1cd1*0x1+0x91a*0x1),0x13ef+-0x914+-0xac3),effectiveTime);return Math[_0x52915b(0x13a)](_0x25c4e6['Pynyk'](_0x4b884a,_0x3f33b5));}const _0x17a781=_0x25c4e6['ELLzz'](_0x157eb4),_0x572e55=new Date(-0x19*0x155+-0x1*-0x1a87+0xe9d,-0x5*0x394+-0xaf9+0x1ce3*0x1,-0xf5f+0x26a5+-0xb*0x21d,updateTime,-0x7f*0x42+-0x12e8+0x33a6,0x11*0xe2+0x18e3+-0x27e5);_0x572e55[_0xc45ac7(0x20d)](_0x25c4e6['VeFfC'](_0x572e55['getMilliseconds'](),_0x25c4e6[_0xc45ac7(0x2ec)](_0x25c4e6[_0xc45ac7(0x348)](_0x17a781,-0xef3+-0x107b+-0x1*-0x2356)*(0xe95+-0x6e1+0x4*-0x1de),0x24ab+-0xd*-0x26f+-0x4412*0x1)*(0x274+0x137b*0x1+0x1*-0x15d7)*effectiveTime));function _0x1d37bb(_0x3e980e){const _0x3780c3=_0xc45ac7,_0x54e44a={'zbKGo':function(_0x2c25c3,_0x5b503b){const _0x19e837=_0x53c3;return _0x25c4e6[_0x19e837(0x1eb)](_0x2c25c3,_0x5b503b);},'jNUSa':function(_0x417231,_0x15a2ff){const _0x369869=_0x53c3;return _0x25c4e6[_0x369869(0x1eb)](_0x417231,_0x15a2ff);},'BYqIn':function(_0x8f056d,_0x2ad203){const _0x2e893f=_0x53c3;return _0x25c4e6[_0x2e893f(0x12c)](_0x8f056d,_0x2ad203);},'TfjQf':function(_0x520221,_0x33a946){const _0x512b6a=_0x53c3;return _0x25c4e6[_0x512b6a(0x28e)](_0x520221,_0x33a946);},'ummeO':function(_0x3d51ee,_0x383197){return _0x25c4e6['dqXcg'](_0x3d51ee,_0x383197);}},_0x34375d=new TextEncoder()[_0x3780c3(0x21a)](_0x3e980e);return crypto[_0x3780c3(0x20b)][_0x3780c3(0x1bf)](_0x25c4e6[_0x3780c3(0x324)],_0x34375d)['then'](_0x2ecde0=>{const _0x49b083=_0x3780c3,_0x2c5b42=Array[_0x49b083(0x1cc)](new Uint8Array(_0x2ecde0)),_0x396b99=_0x2c5b42[_0x49b083(0x28c)](_0x20cb0a=>_0x20cb0a['toString'](0x4*0x70e+0x3b*0x26+0x546*-0x7)[_0x49b083(0x2ad)](0x8cd*-0x3+-0x21e5+0x3c4e,'0'))['join']('');let _0x17ad4e=_0x54e44a[_0x49b083(0x2b5)](_0x54e44a[_0x49b083(0x2b5)](_0x54e44a[_0x49b083(0x2b5)](_0x54e44a[_0x49b083(0x2b5)](_0x54e44a[_0x49b083(0x2b5)](_0x54e44a[_0x49b083(0x2b5)](_0x54e44a[_0x49b083(0x1e9)](_0x54e44a[_0x49b083(0x200)](_0x396b99['substr'](0x1f13+0xeca+-0x2ddd,-0x1018+-0x1*-0x2103+-0x10e3),'-'),_0x396b99[_0x49b083(0x13b)](0x1a5*0x16+-0x57b*0x5+-0x8bf,-0x1*0x10cf+0xd*0x1a6+-0x49b)),'-4'),_0x396b99[_0x49b083(0x13b)](0x2*-0x133d+0x943*0x4+0x17b,-0x5*-0x45d+-0x806+-0xdc8)),'-'),_0x54e44a[_0x49b083(0x352)](_0x54e44a['ummeO'](parseInt(_0x396b99[_0x49b083(0x13b)](0x2*0x5d2+0xc7e+-0x1812,0x5e2+-0xf00+0x920),-0xce*-0x2+0x1*0x2519+-0x26a5*0x1),0x2261+0x42a+-0x264c),0x1096+-0x1*-0x162b+-0x2641)[_0x49b083(0x2a4)](-0x2*-0x473+-0x2d5+-0x601)),_0x396b99[_0x49b083(0x13b)](0xdb9*-0x1+-0x1*-0xea5+-0xda,0x1*0x1a91+-0x17e4+-0x2ab)),'-')+_0x396b99[_0x49b083(0x13b)](-0x13b+0x15d3+-0x1484,-0x84c+0xfa9+-0x751);return _0x17ad4e;});}const _0x2695a7=_0x25c4e6[_0xc45ac7(0x2a5)](_0x1d37bb,_0x25c4e6[_0xc45ac7(0x1eb)](_0xac39c4,_0x17a781)),_0x25180f=_0x25c4e6[_0xc45ac7(0x2a5)](_0x1d37bb,_0x25c4e6[_0xc45ac7(0x34f)](_0xac39c4,_0x25c4e6[_0xc45ac7(0x22b)](_0x17a781,0x912+0x15be+-0x1ecf))),_0x5ddd52=new Date(_0x25c4e6['jvTbx'](_0x572e55[_0xc45ac7(0x152)](),_0x25c4e6['bdgWu'](_0x25c4e6[_0xc45ac7(0x291)](-0x2*0xb8+0xc3+0xb5,-0x62e+0x121d+-0xbb3),-0x2297+-0x12f4*-0x2+-0x315)*(-0x48b*0x4+0x13df+-0x5*-0x71))),_0x5b5470=_0xc45ac7(0x17c)+_0x5ddd52[_0xc45ac7(0x31d)]()[_0xc45ac7(0x353)](0xb*-0x1+0x11d7*0x2+-0x23a3,-0x1cc8+0x9af*-0x2+-0x337*-0xf)[_0xc45ac7(0x16c)]('T','\x20')+_0xc45ac7(0x202)+_0x572e55[_0xc45ac7(0x31d)]()['slice'](0x2248+-0x1a9a+-0x7ae,-0x1daa*0x1+-0xe4*-0x29+-0x6c7)['replace']('T','\x20')+'\x0a';return Promise['all']([_0x2695a7,_0x25180f,_0x5b5470]);} \ No newline at end of file diff --git a/_worker.src.js b/_worker.src.js new file mode 100644 index 0000000..c31fa51 --- /dev/null +++ b/_worker.src.js @@ -0,0 +1,1965 @@ +// 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 = ""; +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 啥啥啥_写的这是啥啊 = 'dmxlc3M='; +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}@${地址}:${端口}?encryption=${加密方式}&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}?encryption=none&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}?encryption=none&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