├── README.md └── _worker.js /README.md: -------------------------------------------------------------------------------- 1 | ### 变量说明 2 | | 变量名 | 示例 | 备注 | 3 | |--------|---------|-----| 4 | | TOKEN | auto | 快速订阅内置节点的订阅路径地址 /auto (支持多元素, 元素之间使用`,`作间隔)| 5 | | HOST | edgetunnel-2z2.pages.dev | 快速订阅内置节点的伪装域名 | 6 | | PASSWORD | CMLiussss | 快速订阅内置节点的密码 | 7 | | PATH | /?ed=2560 | 快速订阅内置节点的路径信息 | 8 | | ADD | icook.tw:2053#官方优选域名 | 对应`addresses`字段 (支持多元素, 元素之间使用`,`作间隔) | 9 | | ADDAPI | [https://raw.github.../addressesapi.txt](https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressesapi.txt) | 对应`addressesapi`字段 (支持多元素, 元素之间使用`,`作间隔) | 10 | | ADDCSV | [https://raw.github.../addressescsv.csv](https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressescsv.csv) | 对应`addressescsv`字段 (支持多元素, 元素之间使用`,`作间隔) | 11 | | DLS | 8 |`addressescsv`测速结果满足速度下限 | 12 | | TGTOKEN | 6894123456:XXXXXXXXXX0qExVsBPUhHDAbXXXXXqWXgBA | 发送TG通知的机器人token | 13 | | TGID | 6946912345 | 接收TG通知的账户数字ID | 14 | | SUBAPI | api.v1.mk | clash、singbox等 订阅转换后端 | 15 | | SUBCONFIG | [https://raw.github.../ACL4SSR_Online_Full_MultiMode.ini](https://raw.githubusercontent.com/cmliu/ACL4SSR/main/Clash/config/ACL4SSR_Online_Full_MultiMode.ini) | clash、singbox等 订阅转换配置文件 | 16 | | SUBNAME | WorkerTrojan2sub | 订阅生成器名称 | 17 | | PS | 【请勿测速】 | 节点名备注消息 | 18 | | PROXYIP | proxyip.fxxk.dedyn.io | 默认分配的ProxyIP, 多ProxyIP将随机分配(支持多元素, 元素之间使用`,`作间隔) | 19 | | CMPROXYIPS | proxyip.aliyun.fxxk.dedyn.io:HK | 识别HK后分配对应的ProxyIP(支持多元素, 元素之间使用`,`作间隔) | 20 | -------------------------------------------------------------------------------- /_worker.js: -------------------------------------------------------------------------------- 1 | 2 | // 部署完成后在网址后面加上这个,获取订阅器默认节点,/auto 3 | 4 | let mytoken= ['auto'];//快速订阅访问入口, 留空则不启动快速订阅 5 | 6 | // 设置优选地址,不带端口号默认443,TLS订阅生成 7 | let addresses = [ 8 | 'cf.090227.xyz:443#加入我的频道t.me/CMLiussss解锁更多优选节点', 9 | ]; 10 | 11 | // 设置优选地址api接口 12 | let addressesapi = [ 13 | //'https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressesapi.txt', //可参考内容格式 自行搭建。 14 | //'https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressesipv6api.txt', //IPv6优选内容格式 自行搭建。 15 | ]; 16 | 17 | let DLS = 8;//速度下限 18 | let addressescsv = [ 19 | //'https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressescsv.csv', //iptest测速结果文件。 20 | ]; 21 | 22 | let subconverter = "url.v1.mk"; //在线订阅转换后端,目前使用肥羊的订阅转换功能。支持自建psub 可自行搭建https://github.com/bulianglin/psub 23 | let subconfig = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Mini.ini"; //订阅转换配置文件 24 | 25 | let link = ''; 26 | let epeius = 'ed'; 27 | let RproxyIP = 'false'; 28 | let proxyIPs = [ 29 | 'proxyip.aliyun.fxxk.dedyn.io', 30 | ]; 31 | let CMproxyIPs = [ 32 | //'proxyip.aliyun.fxxk.dedyn.io:HK', 33 | ]; 34 | let BotToken =''; 35 | let ChatID =''; 36 | let proxyhosts = [//本地代理域名池 37 | //'ppfv2tl9veojd-maillazy.pages.dev', 38 | ]; 39 | let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts';//在线代理域名池URL 40 | let EndPS = '';//节点名备注内容 41 | 42 | let FileName = 'WorkerTrojan2sub'; 43 | let SUBUpdateTime = 6; 44 | let total = 99;//PB 45 | //let timestamp = now; 46 | let timestamp = 4102329600000;//2099-12-31 47 | const regex = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[.*\]):?(\d+)?#?(.*)?$/; 48 | async function sendMessage(type, ip, add_data = "") { 49 | if ( BotToken !== '' && ChatID !== ''){ 50 | let msg = ""; 51 | const response = await fetch(`http://ip-api.com/json/${ip}?lang=zh-CN`); 52 | if (response.status == 200) { 53 | const ipInfo = await response.json(); 54 | msg = `${type}\nIP: ${ip}\n国家: ${ipInfo.country}\n城市: ${ipInfo.city}\n组织: ${ipInfo.org}\nASN: ${ipInfo.as}\n${add_data}`; 55 | } else { 56 | msg = `${type}\nIP: ${ip}\n${add_data}`; 57 | } 58 | 59 | let url = "https://api.telegram.org/bot"+ BotToken +"/sendMessage?chat_id=" + ChatID + "&parse_mode=HTML&text=" + encodeURIComponent(msg); 60 | return fetch(url, { 61 | method: 'get', 62 | headers: { 63 | 'Accept': 'text/html,application/xhtml+xml,application/xml;', 64 | 'Accept-Encoding': 'gzip, deflate, br', 65 | 'User-Agent': 'Mozilla/5.0 Chrome/90.0.4430.72' 66 | } 67 | }); 68 | } 69 | } 70 | 71 | let MamaJustKilledAMan = ['telegram','twitter','miaoko']; 72 | async function getAddressesapi(api) { 73 | if (!api || api.length === 0) { 74 | return []; 75 | } 76 | 77 | let newapi = ""; 78 | 79 | // 创建一个AbortController对象,用于控制fetch请求的取消 80 | const controller = new AbortController(); 81 | 82 | const timeout = setTimeout(() => { 83 | controller.abort(); // 取消所有请求 84 | }, 2000); // 2秒后触发 85 | 86 | try { 87 | // 使用Promise.allSettled等待所有API请求完成,无论成功或失败 88 | // 对api数组进行遍历,对每个API地址发起fetch请求 89 | const responses = await Promise.allSettled(api.map(apiUrl => fetch(apiUrl, { 90 | method: 'get', 91 | headers: { 92 | 'Accept': 'text/html,application/xhtml+xml,application/xml;', 93 | 'User-Agent': 'cmliu/WorkerTrojan2sub' 94 | }, 95 | signal: controller.signal // 将AbortController的信号量添加到fetch请求中,以便于需要时可以取消请求 96 | }).then(response => response.ok ? response.text() : Promise.reject()))); 97 | 98 | // 遍历所有响应 99 | for (const response of responses) { 100 | // 检查响应状态是否为'fulfilled',即请求成功完成 101 | if (response.status === 'fulfilled') { 102 | // 获取响应的内容 103 | const content = await response.value; 104 | newapi += content + '\n'; 105 | } 106 | } 107 | } catch (error) { 108 | console.error(error); 109 | } finally { 110 | // 无论成功或失败,最后都清除设置的超时定时器 111 | clearTimeout(timeout); 112 | } 113 | 114 | const newAddressesapi = await ADD(newapi); 115 | 116 | // 返回处理后的结果 117 | return newAddressesapi; 118 | } 119 | 120 | async function getAddressescsv(tls) { 121 | if (!addressescsv || addressescsv.length === 0) { 122 | return []; 123 | } 124 | 125 | let newAddressescsv = []; 126 | 127 | for (const csvUrl of addressescsv) { 128 | try { 129 | const response = await fetch(csvUrl); 130 | 131 | if (!response.ok) { 132 | console.error('获取CSV地址时出错:', response.status, response.statusText); 133 | continue; 134 | } 135 | 136 | const text = await response.text();// 使用正确的字符编码解析文本内容 137 | let lines; 138 | if (text.includes('\r\n')){ 139 | lines = text.split('\r\n'); 140 | } else { 141 | lines = text.split('\n'); 142 | } 143 | 144 | // 检查CSV头部是否包含必需字段 145 | const header = lines[0].split(','); 146 | const tlsIndex = header.indexOf('TLS'); 147 | const speedIndex = header.length - 1; // 最后一个字段 148 | 149 | const ipAddressIndex = 0;// IP地址在 CSV 头部的位置 150 | const portIndex = 1;// 端口在 CSV 头部的位置 151 | const dataCenterIndex = tlsIndex + 1; // 数据中心是 TLS 的后一个字段 152 | 153 | if (tlsIndex === -1) { 154 | console.error('CSV文件缺少必需的字段'); 155 | continue; 156 | } 157 | 158 | // 从第二行开始遍历CSV行 159 | for (let i = 1; i < lines.length; i++) { 160 | const columns = lines[i].split(','); 161 | 162 | // 检查TLS是否为"TRUE"且速度大于DLS 163 | if (columns[tlsIndex].toUpperCase() === tls && parseFloat(columns[speedIndex]) > DLS) { 164 | const ipAddress = columns[ipAddressIndex]; 165 | const port = columns[portIndex]; 166 | const dataCenter = columns[dataCenterIndex]; 167 | 168 | const formattedAddress = `${ipAddress}:${port}#${dataCenter}`; 169 | newAddressescsv.push(formattedAddress); 170 | } 171 | } 172 | } catch (error) { 173 | console.error('获取CSV地址时出错:', error); 174 | continue; 175 | } 176 | } 177 | 178 | return newAddressescsv; 179 | } 180 | 181 | async function ADD(envadd) { 182 | var addtext = envadd.replace(/[ |"'\r\n]+/g, ',').replace(/,+/g, ','); // 将空格、双引号、单引号和换行符替换为逗号 183 | //console.log(addtext); 184 | if (addtext.charAt(0) == ',') addtext = addtext.slice(1); 185 | if (addtext.charAt(addtext.length -1) == ',') addtext = addtext.slice(0, addtext.length - 1); 186 | const add = addtext.split(','); 187 | //console.log(add); 188 | return add ; 189 | } 190 | 191 | async function nginx() { 192 | const text = ` 193 | 194 | 195 | 196 | Welcome to nginx! 197 | 204 | 205 | 206 |

Welcome to nginx!

207 |

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

209 | 210 |

For online documentation and support please refer to 211 | nginx.org.
212 | Commercial support is available at 213 | nginx.com.

214 | 215 |

Thank you for using nginx.

216 | 217 | 218 | ` 219 | return text ; 220 | } 221 | 222 | export default { 223 | async fetch (request, env) { 224 | if (env.TOKEN) mytoken = await ADD(env.TOKEN); 225 | //mytoken = env.TOKEN.split(',') || mytoken; 226 | BotToken = env.TGTOKEN || BotToken; 227 | ChatID = env.TGID || ChatID; 228 | subconverter = env.SUBAPI || subconverter; 229 | subconfig = env.SUBCONFIG || subconfig; 230 | FileName = env.SUBNAME || FileName; 231 | if (env.CMPROXYIPS) CMproxyIPs = await ADD(env.CMPROXYIPS);; 232 | //console.log(CMproxyIPs); 233 | EndPS = env.PS || EndPS; 234 | const userAgentHeader = request.headers.get('User-Agent'); 235 | const userAgent = userAgentHeader ? userAgentHeader.toLowerCase() : "null"; 236 | const url = new URL(request.url); 237 | const format = url.searchParams.get('format') ? url.searchParams.get('format').toLowerCase() : "null"; 238 | let host = ""; 239 | let pw = ""; 240 | //let uuid = ""; 241 | let path = ""; 242 | let sni = ""; 243 | let UD = Math.floor(((timestamp - Date.now())/timestamp * 99 * 1099511627776 * 1024)/2); 244 | if (env.UA) MamaJustKilledAMan = MamaJustKilledAMan.concat(await ADD(env.UA)); 245 | total = total * 1099511627776 * 1024; 246 | let expire= Math.floor(timestamp / 1000) ; 247 | 248 | link = env.LINK || link; 249 | const links = await ADD(link); 250 | link = links.join('\n'); 251 | 252 | if (env.ADD) addresses = await ADD(env.ADD); 253 | if (env.ADDAPI) addressesapi = await ADD(env.ADDAPI); 254 | if (env.ADDCSV) addressescsv = await ADD(env.ADDCSV); 255 | DLS = env.DLS || DLS; 256 | 257 | /* 258 | console.log(` 259 | addresses: ${addresses} 260 | addressesapi: ${addressesapi} 261 | addressescsv: ${addressescsv} 262 | DLS: ${DLS} 263 | `); 264 | */ 265 | 266 | if (env.PROXYIP) proxyIPs = await ADD(env.PROXYIP); 267 | //console.log(proxyIPs); 268 | 269 | if (mytoken.length > 0 && mytoken.some(token => url.pathname.includes(token))) { 270 | host = "null"; 271 | if (env.HOST) { 272 | const hosts = await ADD(env.HOST); 273 | host = hosts[Math.floor(Math.random() * hosts.length)]; 274 | } 275 | pw = env.PASSWORD || "null"; 276 | path = env.PATH || "/?ed=2560"; 277 | sni = env.SNI || host; 278 | epeius = env.ED || epeius; 279 | RproxyIP = env.RPROXYIP || RproxyIP; 280 | 281 | if (host == "null" || pw == "null" ){ 282 | let 空字段; 283 | if (host == "null" && pw == "null") 空字段 = "HOST/PASSWORD"; 284 | else if (host == "null") 空字段 = "HOST"; 285 | else if (pw == "null") 空字段 = "PASSWORD"; 286 | EndPS += ` 订阅器内置节点 ${空字段} 未设置!!!`; 287 | } 288 | 289 | await sendMessage("#Trojan订阅", request.headers.get('CF-Connecting-IP'), `UA: ${userAgentHeader}
\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); 290 | } else { 291 | host = url.searchParams.get('host'); 292 | pw = url.searchParams.get('pw') || url.searchParams.get('password'); 293 | path = url.searchParams.get('path'); 294 | sni = url.searchParams.get('sni') || host; 295 | epeius = url.searchParams.get('epeius') || epeius; 296 | RproxyIP = url.searchParams.get('proxyip') || RproxyIP; 297 | 298 | if (!url.pathname.includes("/sub")) { 299 | const envKey = env.URL302 ? 'URL302' : (env.URL ? 'URL' : null); 300 | if (envKey) { 301 | const URLs = await ADD(env[envKey]); 302 | const URL = URLs[Math.floor(Math.random() * URLs.length)]; 303 | return envKey === 'URL302' ? Response.redirect(URL, 302) : fetch(new Request(URL, request)); 304 | } 305 | //首页改成一个nginx伪装页 306 | return new Response(await nginx(), { 307 | headers: { 308 | 'Content-Type': 'text/html; charset=UTF-8', 309 | }, 310 | }); 311 | } 312 | 313 | if (!host || !pw) { 314 | const responseText = ` 315 | 缺少必填参数:host 和 pw 316 | Missing required parameters: host and uuid 317 | پارامترهای ضروری وارد نشده: هاست و یوآی‌دی 318 | 319 | ${url.origin}/sub?host=[your host]&pw=[your password]&path=[your path] 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | https://github.com/cmliu/WorkerTrojan2sub 328 | `; 329 | 330 | return new Response(responseText, { 331 | status: 400, 332 | headers: { 'content-type': 'text/plain; charset=utf-8' }, 333 | }); 334 | } 335 | 336 | if (!path || path.trim() === '') { 337 | path = '/?ed=2560'; 338 | } else { 339 | // 如果第一个字符不是斜杠,则在前面添加一个斜杠 340 | path = (path[0] === '/') ? path : '/' + path; 341 | } 342 | } 343 | 344 | let subconverterUrl = ''; 345 | 346 | if (!userAgent.includes('subconverter') && MamaJustKilledAMan.some(PutAGunAgainstHisHeadPulledMyTriggerNowHesDead => userAgent.includes(PutAGunAgainstHisHeadPulledMyTriggerNowHesDead)) && MamaJustKilledAMan.length > 0) { 347 | //首页改成一个nginx伪装页 348 | return new Response(await nginx(), { 349 | headers: { 350 | 'Content-Type': 'text/html; charset=UTF-8', 351 | }, 352 | }); 353 | } else if ( (userAgent.includes('clash') || (format === 'clash' && !userAgent.includes('subconverter')) ) && !userAgent.includes('nekobox') && !userAgent.includes('cf-workers-sub')) { 354 | subconverterUrl = `https://${subconverter}/sub?target=clash&url=${encodeURIComponent(request.url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; 355 | } else if ( (userAgent.includes('sing-box') || userAgent.includes('singbox') || (format === 'singbox' && !userAgent.includes('subconverter')) ) && !userAgent.includes('cf-workers-sub')) { 356 | subconverterUrl = `https://${subconverter}/sub?target=singbox&url=${encodeURIComponent(request.url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; 357 | } else { 358 | if(host.includes('workers.dev') || host.includes('pages.dev')) { 359 | if (proxyhostsURL) { 360 | try { 361 | const response = await fetch(proxyhostsURL); 362 | 363 | if (!response.ok) { 364 | console.error('获取地址时出错:', response.status, response.statusText); 365 | return; // 如果有错误,直接返回 366 | } 367 | 368 | const text = await response.text(); 369 | const lines = text.split('\n'); 370 | // 过滤掉空行或只包含空白字符的行 371 | const nonEmptyLines = lines.filter(line => line.trim() !== ''); 372 | 373 | proxyhosts = proxyhosts.concat(nonEmptyLines); 374 | } catch (error) { 375 | console.error('获取地址时出错:', error); 376 | } 377 | } 378 | // 使用Set对象去重 379 | proxyhosts = [...new Set(proxyhosts)]; 380 | } 381 | 382 | const newAddressesapi = await getAddressesapi(addressesapi); 383 | const newAddressescsv = await getAddressescsv('TRUE'); 384 | addresses = addresses.concat(newAddressesapi); 385 | addresses = addresses.concat(newAddressescsv); 386 | 387 | // 使用Set对象去重 388 | const uniqueAddresses = [...new Set(addresses)]; 389 | 390 | const responseBody = uniqueAddresses.map(address => { 391 | let port = "443"; 392 | let addressid = address; 393 | 394 | const match = addressid.match(regex); 395 | if (!match) { 396 | if (address.includes(':') && address.includes('#')) { 397 | const parts = address.split(':'); 398 | address = parts[0]; 399 | const subParts = parts[1].split('#'); 400 | port = subParts[0]; 401 | addressid = subParts[1]; 402 | } else if (address.includes(':')) { 403 | const parts = address.split(':'); 404 | address = parts[0]; 405 | port = parts[1]; 406 | } else if (address.includes('#')) { 407 | const parts = address.split('#'); 408 | address = parts[0]; 409 | addressid = parts[1]; 410 | } 411 | 412 | if (addressid.includes(':')) { 413 | addressid = addressid.split(':')[0]; 414 | } 415 | } else { 416 | address = match[1]; 417 | port = match[2] || port; 418 | addressid = match[3] || address; 419 | } 420 | 421 | //console.log(address, port, addressid); 422 | 423 | if (epeius.trim() === 'cmliu' && RproxyIP.trim() === 'true') { 424 | // 将addressid转换为小写 425 | let lowerAddressid = addressid.toLowerCase(); 426 | // 初始化找到的proxyIP为null 427 | let foundProxyIP = null; 428 | 429 | // 遍历CMproxyIPs数组查找匹配项 430 | for (let item of CMproxyIPs) { 431 | if (lowerAddressid.includes(item.split(':')[1].toLowerCase())) { 432 | foundProxyIP = item.split(':')[0]; 433 | break; // 找到匹配项,跳出循环 434 | } 435 | } 436 | 437 | if (foundProxyIP) { 438 | // 如果找到匹配的proxyIP,赋值给path 439 | path = `/proxyIP=${foundProxyIP}`; 440 | } else { 441 | // 如果没有找到匹配项,随机选择一个proxyIP 442 | const randomProxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)]; 443 | path = `/proxyIP=${randomProxyIP}`; 444 | } 445 | } 446 | 447 | let 伪装域名 = host ; 448 | let 最终路径 = path ; 449 | let 节点备注 = EndPS ; 450 | if(proxyhosts && (host.includes('.workers.dev') || host.includes('pages.dev'))) { 451 | 最终路径 = `/${host}${path}?ed=2560`; 452 | 伪装域名 = proxyhosts[Math.floor(Math.random() * proxyhosts.length)]; 453 | 节点备注 = `${EndPS} 已启用临时域名中转服务,请尽快绑定自定义域!`; 454 | sni = 伪装域名; 455 | } 456 | 457 | let 密码 = pw; 458 | if (!userAgent.includes('subconverter')){ 459 | 密码 = encodeURIComponent(pw); 460 | } 461 | const trojanLink = `trojan://${密码}@${address}:${port}?security=tls&sni=${sni}&alpn=http%2F1.1&fp=randomized&type=ws&host=${伪装域名}&path=${encodeURIComponent(最终路径)}#${encodeURIComponent(addressid + 节点备注)}`; 462 | 463 | return trojanLink; 464 | }).join('\n'); 465 | 466 | let combinedContent = responseBody; // 合并内容 467 | 468 | if (link) { 469 | combinedContent += '\n' + link; 470 | console.log("link: " + link) 471 | } 472 | 473 | if ((userAgent.includes('surge') || (format === 'surge' && !userAgent.includes('subconverter')) ) && !userAgent.includes('cf-workers-sub')) { 474 | const TrojanLinks = combinedContent.split('\n'); 475 | subconverterUrl = `https://${subconverter}/sub?target=surge&ver=4&url=${encodeURIComponent(TrojanLinks.join('|'))}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&xudp=false&udp=false&tfo=false&expand=true&scv=true&fdn=false`; 476 | } else { 477 | const base64Response = btoa(combinedContent); // 重新进行 Base64 编码 478 | 479 | const response = new Response(base64Response, { 480 | headers: { 481 | "Content-Disposition": `attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName}`, 482 | "content-type": "text/plain; charset=utf-8", 483 | "Profile-Update-Interval": `${SUBUpdateTime}`, 484 | "Subscription-Userinfo": `upload=${UD}; download=${UD}; total=${total}; expire=${expire}`, 485 | }, 486 | }); 487 | 488 | return response; 489 | } 490 | } 491 | 492 | try { 493 | const subconverterResponse = await fetch(subconverterUrl); 494 | 495 | if (!subconverterResponse.ok) { 496 | throw new Error(`Error fetching subconverterUrl: ${subconverterResponse.status} ${subconverterResponse.statusText}`); 497 | } 498 | 499 | let subconverterContent = await subconverterResponse.text(); 500 | 501 | if (( userAgent.includes('surge') || (format === 'surge' && !userAgent.includes('subconverter')) ) && !userAgent.includes('cf-workers-sub')){ 502 | subconverterContent = surge(subconverterContent, url); 503 | } 504 | 505 | return new Response(subconverterContent, { 506 | headers: { 507 | "Content-Disposition": `attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName}`, 508 | "content-type": "text/plain; charset=utf-8", 509 | "Profile-Update-Interval": `${SUBUpdateTime}`, 510 | "Subscription-Userinfo": `upload=${UD}; download=${UD}; total=${total}; expire=${expire}`, 511 | }, 512 | }); 513 | } catch (error) { 514 | return new Response(`Error: ${error.message}`, { 515 | status: 500, 516 | headers: { 'content-type': 'text/plain; charset=utf-8' }, 517 | }); 518 | } 519 | 520 | } 521 | }; 522 | 523 | function surge(content, url) { 524 | let 每行内容; 525 | if (content.includes('\r\n')){ 526 | 每行内容 = content.split('\r\n'); 527 | } else { 528 | 每行内容 = content.split('\n'); 529 | } 530 | 531 | let 输出内容 = ""; 532 | for (let x of 每行内容) { 533 | if (x.includes('= trojan,') && !x.includes('Warp')) { 534 | const host = x.split("sni=")[1].split(",")[0]; 535 | const 备改内容 = `skip-cert-verify=true, tfo=false, udp-relay=false`; 536 | const 正确内容 = `skip-cert-verify=true, ws=true, ws-path=/?ed=2560, ws-headers=Host:"${host}", tfo=false, udp-relay=false`; 537 | 输出内容 += x.replace(new RegExp(备改内容, 'g'), 正确内容).replace("[", "").replace("]", "") + '\n'; 538 | } else { 539 | 输出内容 += x + '\n'; 540 | } 541 | } 542 | 543 | 输出内容 = `#!MANAGED-CONFIG ${url.href} interval=86400 strict=false` + 输出内容.substring(输出内容.indexOf('\n')); 544 | return 输出内容; 545 | } 546 | --------------------------------------------------------------------------------