WorkerVless2sub/_worker.js

同步来源:树洞剪藏 源站剪藏 ID:369 原文地址:https://github.com/zzzhhh1/WorkerVless2sub1/blob/main/_worker.js
WorkerVless2sub1/_worker.js at main · zzzhhh1/WorkerVless2sub1
/ /blob/show" data-turbo-transient=true>
Skip to content
Navigation Menu
zzzhhh1
/
WorkerVless2sub1
Type / to search
Create new...
Issues
Pull requests
You have unread notifications
Code
Issues 0
Pull requests 0
Actions
Projects 0
Security
Insights
Additional navigation options
{{ message }}
Open in github.dev ,>" target=_blank rel="noopener noreferrer">Open in a new github.dev tab Open in codespace
Files
main
t
README.md
_worker.js
addressesapi.txt
addressescsv.csv
Breadcrumbs WorkerVless2sub1 / _worker.js
Latest commit
zzzhhh1
Update _worker.js
789eaaf · 9 months ago Mar 17, 2024
History History
Breadcrumbs WorkerVless2sub1 / _worker.js Top
File metadata and controls
Code
Blame
448 lines (365 loc) · 14.8 KB Code 55% faster with GitHub Copilot " data-hotkey-scope=read-only-cursor-text-area class=sf-hidden> " class=sf-hidden>
Raw ,>"> ,>" class=sf-hidden>
// 部署完成后在网址后面加上这个,获取订阅器默认节点,/auto
let mytoken= 'auto';//快速订阅访问入口, 留空则不启动快速订阅
// 设置优选地址,不带端口号默认443,不支持非TLS订阅生成 let addresses = [ 'icook.tw:2053#YouTube关注科技共享免费翻墙不迷路,优选域名', 'cloudflare.cfgo.cc#优选官方线路', 'skk.moe:443#优选官方线路', 'icook.hk.443#优选官方线路', 'icook.tw:443#优选官方线路', 'cf.090227.xyz:443#优选官方线路-CM提供', 'cfip.xxxxxxxx.tk:443#优选官方线路-OTC提供', ];
// 设置优选地址api接口 let addressesapi = [ 'https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressesapi.txt' //可参考内容格式 自行搭建。 ];
let DLS = 4;//速度下限 let addressescsv = [ //'https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressescsv.csv' //iptest测速结果文件。 ];
let subconverter = "api.v1.mk"; //在线订阅转换后端,目前使用肥羊的订阅转换功能。支持自建psub 可自行搭建https://github.com/bulianglin/psub let subconfig = "https://raw.githubusercontent.com/cmliu/ACL4SSR/main/Clash/config/ACL4SSR_Online_Full_MultiMode.ini"; //订阅配置文件
let link = ''; let edgetunnel = 'ed'; let RproxyIP = 'false'; let proxyIPs = [ 'proxyip.aliyun.fxxk.dedyn.io', 'proxyip.multacom.fxxk.dedyn.io', 'proxyip.vultr.fxxk.dedyn.io', ]; let CMproxyIPs = [ { proxyIP: "proxyip.fxxk.dedyn.io", type: "HK" }, ]; let BotToken =''; let ChatID =''; let proxyhosts = [//本地代理域名池 //'ppfv2tl9veojd-maillazy.pages.dev', ]; let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts';//在线代理域名池URL let EndPS = '';//节点名备注内容
let FileName = 'WorkerVless2sub'; let SUBUpdateTime = 6; let total = 99;//PB //let timestamp = now; let timestamp = 4102329600000;//2099-12-31
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} IP: ${ip} 国家: ${ipInfo.country} <tg-spoiler>城市: ${ipInfo.city} 组织: ${ipInfo.org} ASN: ${ipInfo.as} ${add_data};
} else {
msg = ${type} IP: ${ip} <tg-spoiler>${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' } }); } }
async function getAddressesapi() { if (!addressesapi || addressesapi.length === 0) { return []; }
let newAddressesapi = [];
for (const apiUrl of addressesapi) { try { const response = await fetch(apiUrl);
if (!response.ok) { console.error('获取地址时出错:', response.status, response.statusText); continue; }
const text = await response.text(); let lines; if (text.includes('\r ')){ lines = text.split('\r '); } else { lines = text.split(' '); } const regex = /^(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})(:\d+)?(#.*)?$/;
const apiAddresses = lines.map(line => { const match = line.match(regex); return match ? match[0] : null; }).filter(Boolean);
newAddressesapi = newAddressesapi.concat(apiAddresses); } catch (error) { console.error('获取地址时出错:', error); continue; } }
return newAddressesapi; }
async function getAddressescsv() { 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 ')){ lines = text.split('\r '); } else { lines = text.split(' '); }
// 检查CSV头部是否包含必需字段 const header = lines[0].split(','); const tlsIndex = header.indexOf('TLS'); const speedIndex = header.length - 1; // 最后一个字段
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(',');
// 检查TLS是否为"TRUE"且速度大于DLS if (columns[tlsIndex].toUpperCase() === 'TRUE' && parseFloat(columns[speedIndex]) > DLS) { const ipAddress = columns[ipAddressIndex]; const port = columns[portIndex]; const dataCenter = columns[dataCenterIndex];
const formattedAddress = ${ipAddress}:${port}#${dataCenter};
newAddressescsv.push(formattedAddress);
}
}
} catch (error) {
console.error('获取CSV地址时出错:', error);
continue;
}
}
return newAddressescsv; }
let protocol; export default { async fetch (request, env) { mytoken = env.TOKEN || mytoken; BotToken = env.TGTOKEN || BotToken; ChatID = env.TGID || ChatID; subconverter = env.SUBAPI || subconverter; subconfig = env.SUBCONFIG || subconfig; const userAgentHeader = request.headers.get('User-Agent'); const userAgent = userAgentHeader ? userAgentHeader.toLowerCase() : "null"; const url = new URL(request.url); const format = url.searchParams.get('format') ? url.searchParams.get('format').toLowerCase() : "null"; let host = ""; let uuid = ""; let path = ""; let UD = Math.floor(((timestamp - Date.now())/timestamp * 99 * 1099511627776 * 1024)/2); total = total * 1099511627776 * 1024; let expire= Math.floor(timestamp / 1000) ;
if (mytoken !== '' && url.pathname.includes(mytoken)) { host = env.HOST || "edgetunnel-2z2.pages.dev"; uuid = env.UUID || "30e9c5c8-ed28-4cd9-b008-dc67277f8b02"; path = env.PATH || "/?ed=2048"; edgetunnel = env.ED || edgetunnel; RproxyIP = env.RPROXYIP || RproxyIP;
const hasSos = url.searchParams.has('sos'); if (hasSos) { const hy2Url = "https://hy2sub.pages.dev"; try { const subconverterResponse = await fetch(hy2Url);
if (!subconverterResponse.ok) {
throw new Error(Error fetching lzUrl: ${subconverterResponse.status} ${subconverterResponse.statusText});
}
const base64Text = await subconverterResponse.text(); link = atob(base64Text); // 进行 Base64 解码
} catch (error) {
// 错误处理
}
}
await sendMessage("#获取订阅", request.headers.get('CF-Connecting-IP'), UA: ${userAgent}</tg-spoiler> 域名: ${url.hostname} <tg-spoiler>入口: ${url.pathname + url.search}</tg-spoiler>);
} else {
host = url.searchParams.get('host');
uuid = url.searchParams.get('uuid');
path = url.searchParams.get('path');
edgetunnel = url.searchParams.get('edgetunnel') || edgetunnel;
RproxyIP = url.searchParams.get('proxyip') || RproxyIP;
if (!url.pathname.includes("/sub")) { const responseText = ` 路径必须包含 "/sub" The path must contain "/sub" مسیر باید شامل "/sub" باشد
${url.origin}/sub?host=[your host]&uuid=[your uuid]&path=[your path]
https://github.com/cmliu/WorkerVless2sub `;
return new Response(responseText, { status: 400, headers: { 'content-type': 'text/plain; charset=utf-8' }, }); }
if (!host || !uuid) { const responseText = ` 缺少必填参数:host 和 uuid Missing required parameters: host and uuid پارامترهای ضروری وارد نشده: هاست و یوآیدی
${url.origin}/sub?host=[your host]&uuid=[your uuid]&path=[your path]
https://github.com/cmliu/WorkerVless2sub `;
return new Response(responseText, { status: 400, headers: { 'content-type': 'text/plain; charset=utf-8' }, }); }
if (!path || path.trim() === '') { path = '/?ed=2048'; } else { // 如果第一个字符不是斜杠,则在前面添加一个斜杠 path = (path[0] === '/') ? path : '/' + path; } }
if (userAgent.includes('telegram') || userAgent.includes('twitter') || userAgent.includes('miaoko')) {
return new Response('Hello World!');
} else if (userAgent.includes('clash') || (format === 'clash' && !userAgent.includes('subconverter'))) {
const 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;
try { const subconverterResponse = await fetch(subconverterUrl);
if (!subconverterResponse.ok) {
throw new Error(Error fetching subconverterUrl: ${subconverterResponse.status} ${subconverterResponse.statusText});
}
const subconverterContent = await subconverterResponse.text();
return new Response(subconverterContent, {
headers: {
"Content-Disposition": attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName},
"content-type": "text/plain; charset=utf-8",
"Profile-Update-Interval": ${SUBUpdateTime},
"Subscription-Userinfo": upload=${UD}; download=${UD}; total=${total}; expire=${expire},
},
});
} catch (error) {
return new Response(Error: ${error.message}, {
status: 500,
headers: { 'content-type': 'text/plain; charset=utf-8' },
});
}
} else if (userAgent.includes('sing-box') || userAgent.includes('singbox') || (format === 'singbox' && !userAgent.includes('subconverter'))){
const 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;
try { const subconverterResponse = await fetch(subconverterUrl);
if (!subconverterResponse.ok) {
throw new Error(Error fetching subconverterUrl: ${subconverterResponse.status} ${subconverterResponse.statusText});
}
const subconverterContent = await subconverterResponse.text();
return new Response(subconverterContent, {
headers: {
"Content-Disposition": attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName},
"content-type": "text/plain; charset=utf-8",
"Profile-Update-Interval": ${SUBUpdateTime},
"Subscription-Userinfo": upload=${UD}; download=${UD}; total=${total}; expire=${expire},
},
});
} catch (error) {
return new Response(Error: ${error.message}, {
status: 500,
headers: { 'content-type': 'text/plain; charset=utf-8' },
});
}
} else {
if(host.includes('workers.dev') || host.includes('pages.dev')) {
if (proxyhostsURL) {
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(' '); // 过滤掉空行或只包含空白字符的行 const nonEmptyLines = lines.filter(line => line.trim() !== '');
proxyhosts = proxyhosts.concat(nonEmptyLines); } catch (error) { console.error('获取地址时出错:', error); } } // 使用Set对象去重 proxyhosts = [...new Set(proxyhosts)]; }
const newAddressesapi = await getAddressesapi(); const newAddressescsv = await getAddressescsv(); addresses = addresses.concat(newAddressesapi); addresses = addresses.concat(newAddressescsv);
// 使用Set对象去重 const uniqueAddresses = [...new Set(addresses)];
const responseBody = uniqueAddresses.map(address => { let port = "443"; let addressid = address;
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]; }
if (edgetunnel.trim() === 'cmliu' && RproxyIP.trim() === 'true') { // 将addressid转换为小写 let lowerAddressid = addressid.toLowerCase(); // 初始化找到的proxyIP为null let foundProxyIP = null;
// 遍历CMproxyIPs数组查找匹配项 for (let item of CMproxyIPs) { if (lowerAddressid.includes(item.type.toLowerCase())) { foundProxyIP = item.proxyIP; break; // 找到匹配项,跳出循环 } }
if (foundProxyIP) {
// 如果找到匹配的proxyIP,赋值给path
path = /proxyIP=${foundProxyIP};
} else {
// 如果没有找到匹配项,随机选择一个proxyIP
const randomProxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)];
path = /proxyIP=${randomProxyIP};
}
}
let 伪装域名 = host ;
let 最终路径 = path ;
let 节点备注 = EndPS ;
if(proxyhosts && (host.includes('workers.dev') || host.includes('pages.dev'))) {
最终路径 = /${host}${path};
伪装域名 = proxyhosts[Math.floor(Math.random() * proxyhosts.length)];
节点备注 = ${EndPS} 已启用临时域名中转服务,请尽快绑定自定义域!;
}
const vlessLink = vless://${uuid}@${address}:${port}?encryption=none&security=tls&sni=${伪装域名}&fp=random&type=ws&host=${伪装域名}&path=${encodeURIComponent(最终路径)}#${encodeURIComponent(addressid + 节点备注)};
return vlessLink; }).join(' ');
const combinedContent = responseBody + ' ' + link; // 合并内容 const base64Response = btoa(combinedContent); // 重新进行 Base64 编码
const response = new Response(base64Response, {
headers: {
//"Content-Disposition": attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName},
"content-type": "text/plain; charset=utf-8",
"Profile-Update-Interval": ${SUBUpdateTime},
"Subscription-Userinfo": upload=${UD}; download=${UD}; total=${total}; expire=${expire},
},
});
return response; } } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
// 部署完成后在网址后面加上这个,获取订阅器默认节点,/auto
let mytoken = 'auto' ; //快速订阅访问入口, 留空则不启动快速订阅
// 设置优选地址,不带端口号默认443,不支持非TLS订阅生成
let addresses = [
'icook.tw:2053#YouTube关注科技共享免费翻墙不迷路,优选域名' ,
'cloudflare.cfgo.cc#优选官方线路' ,
'skk.moe:443#优选官方线路' ,
'icook.hk.443#优选官方线路' ,
'icook.tw:443#优选官方线路' ,
'cf.090227.xyz:443#优选官方线路-CM提供' ,
'cfip.xxxxxxxx.tk:443#优选官方线路-OTC提供' ,
] ;
// 设置优选地址api接口
let addressesapi = [
'https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressesapi.txt' //可参考内容格式 自行搭建。
] ;
let DLS = 4 ; //速度下限
let addressescsv = [
//'https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressescsv.csv' //iptest测速结果文件。
] ;
let subconverter = "api.v1.mk" ; //在线订阅转换后端,目前使用肥羊的订阅转换功能。支持自建psub 可自行搭建https://github.com/bulianglin/psub
let subconfig = "https://raw.githubusercontent.com/cmliu/ACL4SSR/main/Clash/config/ACL4SSR_Online_Full_MultiMode.ini" ; //订阅配置文件
let link = '' ;
let edgetunnel = 'ed' ;
let RproxyIP = 'false' ;
let proxyIPs = [
'proxyip.aliyun.fxxk.dedyn.io' ,
'proxyip.multacom.fxxk.dedyn.io' ,
'proxyip.vultr.fxxk.dedyn.io' ,
] ;
let CMproxyIPs = [
{ proxyIP : "proxyip.fxxk.dedyn.io" , type : "HK" } ,
] ;
let BotToken = '' ;
let ChatID = '' ;
let proxyhosts = [ //本地代理域名池
//'ppfv2tl9veojd-maillazy.pages.dev',
] ;
let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts' ; //在线代理域名池URL
let EndPS = '' ; //节点名备注内容
let FileName = 'WorkerVless2sub' ;
let SUBUpdateTime = 6 ;
let total = 99 ; //PB
//let timestamp = now;
let timestamp = 4102329600000 ; //2099-12-31
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 } IP: ${ ip } 国家: ${ ipInfo . country } <tg-spoiler>城市: ${ ipInfo . city } 组织: ${ ipInfo . org } ASN: ${ ipInfo . as } ${ add_data } ;
} else {
msg = ${ type } IP: ${ ip } <tg-spoiler> ${ 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'
}
} ) ;
}
}
async function getAddressesapi ( ) {
if ( ! addressesapi || addressesapi . length === 0 ) {
return [ ] ;
}
let newAddressesapi = [ ] ;
for ( const apiUrl of addressesapi ) {
try {
const response = await fetch ( apiUrl ) ;
if ( ! response . ok ) {
console . error ( '获取地址时出错:' , response . status , response . statusText ) ;
continue ;
}
const text = await response . text ( ) ;
let lines ;
if ( text . includes ( '\r ' ) ) {
lines = text . split ( '\r ' ) ;
} else {
lines = text . split ( ' ' ) ;
}
const regex = / ^(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})(:\d+)?(#.*)?$ / ;
const apiAddresses = lines . map ( line => {
const match = line . match ( regex ) ;
return match ? match [ 0 ] : null ;
} ) . filter ( Boolean ) ;
newAddressesapi = newAddressesapi . concat ( apiAddresses ) ;
} catch ( error ) {
console . error ( '获取地址时出错:' , error ) ;
continue ;
}
}
return newAddressesapi ;
}
async function getAddressescsv ( ) {
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 ' ) ) {
lines = text . split ( '\r ' ) ;
} else {
lines = text . split ( ' ' ) ;
}
// 检查CSV头部是否包含必需字段
const header = lines [ 0 ] . split ( ',' ) ;
const tlsIndex = header . indexOf ( 'TLS' ) ;
const speedIndex = header . length - 1 ; // 最后一个字段
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 ( ',' ) ;
// 检查TLS是否为"TRUE"且速度大于DLS
if ( columns [ tlsIndex ] . toUpperCase ( ) === 'TRUE' && parseFloat ( columns [ speedIndex ] ) > DLS ) {
const ipAddress = columns [ ipAddressIndex ] ;
const port = columns [ portIndex ] ;
const dataCenter = columns [ dataCenterIndex ] ;
const formattedAddress = ${ ipAddress } : ${ port } # ${ dataCenter } ;
newAddressescsv . push ( formattedAddress ) ;
}
}
} catch ( error ) {
console . error ( '获取CSV地址时出错:' , error ) ;
continue ;
}
}
return newAddressescsv ;
}
let protocol ;
export default {
async fetch ( request , env ) {
mytoken = env . TOKEN || mytoken ;
BotToken = env . TGTOKEN || BotToken ;
ChatID = env . TGID || ChatID ;
subconverter = env . SUBAPI || subconverter ;
subconfig = env . SUBCONFIG || subconfig ;
const userAgentHeader = request . headers . get ( 'User-Agent' ) ;
const userAgent = userAgentHeader ? userAgentHeader . toLowerCase ( ) : "null" ;
const url = new URL ( request . url ) ;
const format = url . searchParams . get ( 'format' ) ? url . searchParams . get ( 'format' ) . toLowerCase ( ) : "null" ;
let host = "" ;
let uuid = "" ;
let path = "" ;
let UD = Math . floor ( ( ( timestamp - Date . now ( ) ) / timestamp * 99 * 1099511627776 * 1024 ) / 2 ) ;
total = total * 1099511627776 * 1024 ;
let expire = Math . floor ( timestamp / 1000 ) ;
if ( mytoken !== '' && url . pathname . includes ( mytoken ) ) {
host = env . HOST || "edgetunnel-2z2.pages.dev" ;
uuid = env . UUID || "30e9c5c8-ed28-4cd9-b008-dc67277f8b02" ;
path = env . PATH || "/?ed=2048" ;
edgetunnel = env . ED || edgetunnel ;
RproxyIP = env . RPROXYIP || RproxyIP ;
const hasSos = url . searchParams . has ( 'sos' ) ;
if ( hasSos ) {
const hy2Url = "https://hy2sub.pages.dev" ;
try {
const subconverterResponse = await fetch ( hy2Url ) ;
if ( ! subconverterResponse . ok ) {
throw new Error ( Error fetching lzUrl: ${ subconverterResponse . status } ${ subconverterResponse . statusText } ) ;
}
const base64Text = await subconverterResponse . text ( ) ;
link = atob ( base64Text ) ; // 进行 Base64 解码
} catch ( error ) {
// 错误处理
}
}
await sendMessage ( "#获取订阅" , request . headers . get ( 'CF-Connecting-IP' ) , UA: ${ userAgent } </tg-spoiler> 域名: ${ url . hostname } <tg-spoiler>入口: ${ url . pathname + url . search } </tg-spoiler> ) ;
} else {
host = url . searchParams . get ( 'host' ) ;
uuid = url . searchParams . get ( 'uuid' ) ;
path = url . searchParams . get ( 'path' ) ;
edgetunnel = url . searchParams . get ( 'edgetunnel' ) || edgetunnel ;
RproxyIP = url . searchParams . get ( 'proxyip' ) || RproxyIP ;
if ( ! url . pathname . includes ( "/sub" ) ) {
const responseText = `
路径必须包含 "/sub"
The path must contain "/sub"
مسیر باید شامل "/sub" باشد
${ url . origin } /sub?host=[your host]&uuid=[your uuid]&path=[your path]
https://github.com/cmliu/WorkerVless2sub
` ;
return new Response ( responseText , {
status : 400 ,
headers : { 'content-type' : 'text/plain; charset=utf-8' } ,
} ) ;
}
if ( ! host || ! uuid ) {
const responseText = `
缺少必填参数:host 和 uuid
Missing required parameters: host and uuid
پارامترهای ضروری وارد نشده: هاست و یوآیدی
${ url . origin } /sub?host=[your host]&uuid=[your uuid]&path=[your path]
https://github.com/cmliu/WorkerVless2sub
` ;
return new Response ( responseText , {
status : 400 ,
headers : { 'content-type' : 'text/plain; charset=utf-8' } ,
} ) ;
}
if ( ! path || path . trim ( ) === '' ) {
path = '/?ed=2048' ;
} else {
// 如果第一个字符不是斜杠,则在前面添加一个斜杠
path = ( path [ 0 ] === '/' ) ? path : '/' + path ;
}
}
if ( userAgent . includes ( 'telegram' ) || userAgent . includes ( 'twitter' ) || userAgent . includes ( 'miaoko' ) ) {
return new Response ( 'Hello World!' ) ;
} else if ( userAgent . includes ( 'clash' ) || ( format === 'clash' && ! userAgent . includes ( 'subconverter' ) ) ) {
const 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 ;
try {
const subconverterResponse = await fetch ( subconverterUrl ) ;
if ( ! subconverterResponse . ok ) {
throw new Error ( Error fetching subconverterUrl: ${ subconverterResponse . status } ${ subconverterResponse . statusText } ) ;
}
const subconverterContent = await subconverterResponse . text ( ) ;
return new Response ( subconverterContent , {
headers : {
"Content-Disposition" : attachment; filename*=utf-8'' ${ encodeURIComponent ( FileName ) } ; filename= ${ FileName } ,
"content-type" : "text/plain; charset=utf-8" ,
"Profile-Update-Interval" : ${ SUBUpdateTime } ,
"Subscription-Userinfo" : upload= ${ UD } ; download= ${ UD } ; total= ${ total } ; expire= ${ expire } ,
} ,
} ) ;
} catch ( error ) {
return new Response ( Error: ${ error . message } , {
status : 500 ,
headers : { 'content-type' : 'text/plain; charset=utf-8' } ,
} ) ;
}
} else if ( userAgent . includes ( 'sing-box' ) || userAgent . includes ( 'singbox' ) || ( format === 'singbox' && ! userAgent . includes ( 'subconverter' ) ) ) {
const 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 ;
try {
const subconverterResponse = await fetch ( subconverterUrl ) ;
if ( ! subconverterResponse . ok ) {
throw new Error ( Error fetching subconverterUrl: ${ subconverterResponse . status } ${ subconverterResponse . statusText } ) ;
}
const subconverterContent = await subconverterResponse . text ( ) ;
return new Response ( subconverterContent , {
headers : {
"Content-Disposition" : attachment; filename*=utf-8'' ${ encodeURIComponent ( FileName ) } ; filename= ${ FileName } ,
"content-type" : "text/plain; charset=utf-8" ,
"Profile-Update-Interval" : ${ SUBUpdateTime } ,
"Subscription-Userinfo" : upload= ${ UD } ; download= ${ UD } ; total= ${ total } ; expire= ${ expire } ,
} ,
} ) ;
} catch ( error ) {
return new Response ( Error: ${ error . message } , {
status : 500 ,
headers : { 'content-type' : 'text/plain; charset=utf-8' } ,
} ) ;
}
} else {
if ( host . includes ( 'workers.dev' ) || host . includes ( 'pages.dev' ) ) {
if ( proxyhostsURL ) {
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 ( ' ' ) ;
// 过滤掉空行或只包含空白字符的行
const nonEmptyLines = lines . filter ( line => line . trim ( ) !== '' ) ;
proxyhosts = proxyhosts . concat ( nonEmptyLines ) ;
} catch ( error ) {
console . error ( '获取地址时出错:' , error ) ;
}
}
// 使用Set对象去重
proxyhosts = [ ... new Set ( proxyhosts ) ] ;
}
const newAddressesapi = await getAddressesapi ( ) ;
const newAddressescsv = await getAddressescsv ( ) ;
addresses = addresses . concat ( newAddressesapi ) ;
addresses = addresses . concat ( newAddressescsv ) ;
// 使用Set对象去重
const uniqueAddresses = [ ... new Set ( addresses ) ] ;
const responseBody = uniqueAddresses . map ( address => {
let port = "443" ;
let addressid = address ;
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 ] ;
}
if ( edgetunnel . trim ( ) === 'cmliu' && RproxyIP . trim ( ) === 'true' ) {
// 将addressid转换为小写
let lowerAddressid = addressid . toLowerCase ( ) ;
// 初始化找到的proxyIP为null
let foundProxyIP = null ;
// 遍历CMproxyIPs数组查找匹配项
for ( let item of CMproxyIPs ) {
if ( lowerAddressid . includes ( item . type . toLowerCase ( ) ) ) {
foundProxyIP = item . proxyIP ;
break ; // 找到匹配项,跳出循环
}
}
if ( foundProxyIP ) {
// 如果找到匹配的proxyIP,赋值给path
path = /proxyIP= ${ foundProxyIP } ;
} else {
// 如果没有找到匹配项,随机选择一个proxyIP
const randomProxyIP = proxyIPs [ Math . floor ( Math . random ( ) * proxyIPs . length ) ] ;
path = /proxyIP= ${ randomProxyIP } ;
}
}
let 伪装域名 = host ;
let 最终路径 = path ;
let 节点备注 = EndPS ;
if ( proxyhosts && ( host . includes ( 'workers.dev' ) || host . includes ( 'pages.dev' ) ) ) {
最终路径 = / ${ host } ${ path } ;
伪装域名 = proxyhosts [ Math . floor ( Math . random ( ) * proxyhosts . length ) ] ;
节点备注 = ${ EndPS } 已启用临时域名中转服务,请尽快绑定自定义域! ;
}
const vlessLink = vless:// ${ uuid } @ ${ address } : ${ port } ?encryption=none&security=tls&sni= ${ 伪装域名 } &fp=random&type=ws&host= ${ 伪装域名 } &path= ${ encodeURIComponent ( 最终路径 ) } # ${ encodeURIComponent ( addressid + 节点备注 ) } ;
return vlessLink ;
} ) . join ( ' ' ) ;
const combinedContent = responseBody + ' ' + link ; // 合并内容
const base64Response = btoa ( combinedContent ) ; // 重新进行 Base64 编码
const response = new Response ( base64Response , {
headers : {
//"Content-Disposition": attachment; filename*=utf-8''${encodeURIComponent(FileName)}; filename=${FileName},
"content-type" : "text/plain; charset=utf-8" ,
"Profile-Update-Interval" : ${ SUBUpdateTime } ,
"Subscription-Userinfo" : upload= ${ UD } ; download= ${ UD } ; total= ${ total } ; expire= ${ expire } ,
} ,
} ) ;
return response ;
}
}
} ;
You can’t perform that action at this time.
set 限制解除