|
1206 | 1206 | return btoa(links.join('\n')); |
1207 | 1207 | } |
1208 | 1208 |
|
| 1209 | + function generateV2RayConfig(links) { |
| 1210 | + return btoa(links.join('\n')); |
| 1211 | + } |
| 1212 | + |
| 1213 | + // 生成小火箭格式的订阅(支持 vless、trojan、xhttp 协议,针对ECH进行适配) |
| 1214 | + function generateShadowrocketConfig(links) { |
| 1215 | + const shadowrocketLinks = links.map(link => { |
| 1216 | + try { |
| 1217 | + // 解析 vless:// 或 trojan:// 链接 |
| 1218 | + const urlMatch = link.match(/^(vless|trojan):\/\/([^@]+)@([^?#]+)(\?[^#]*)?(#.*)?$/); |
| 1219 | + if (!urlMatch) return link; // 如果无法解析,返回原链接 |
| 1220 | + |
| 1221 | + const protocol = urlMatch[1]; |
| 1222 | + const user = urlMatch[2]; |
| 1223 | + const address = urlMatch[3]; |
| 1224 | + const queryString = urlMatch[4] || ''; |
| 1225 | + const fragment = urlMatch[5] || ''; |
| 1226 | + |
| 1227 | + // 解析地址和端口 |
| 1228 | + const addrMatch = address.match(/^\[?([^\]]+)\]?:(\d+)$/); |
| 1229 | + if (!addrMatch) return link; |
| 1230 | + const host = addrMatch[1]; |
| 1231 | + const port = addrMatch[2]; |
| 1232 | + |
| 1233 | + // 解析查询参数 |
| 1234 | + const params = new URLSearchParams(queryString.substring(1)); |
| 1235 | + const remarks = fragment.substring(1) ? decodeURIComponent(fragment.substring(1)) : ''; |
| 1236 | + |
| 1237 | + // 构建小火箭格式的用户部分(base64编码 auto:user) |
| 1238 | + // 注意:小火箭使用 base64 编码,但不需要移除 = 号,URL 会自动处理 |
| 1239 | + const shadowrocketUser = btoa(`auto:${user}`); |
| 1240 | + |
| 1241 | + // 构建小火箭格式的参数 |
| 1242 | + const shadowrocketParams = new URLSearchParams(); |
| 1243 | + |
| 1244 | + // path 参数 |
| 1245 | + const path = params.get('path') || '/?ed=2048'; |
| 1246 | + shadowrocketParams.set('path', path); |
| 1247 | + |
| 1248 | + // remarks 参数 |
| 1249 | + if (remarks) { |
| 1250 | + shadowrocketParams.set('remarks', remarks); |
| 1251 | + } |
| 1252 | + |
| 1253 | + // obfsParam 参数(从 host 参数获取) |
| 1254 | + const obfsParam = params.get('host') || host; |
| 1255 | + shadowrocketParams.set('obfsParam', obfsParam); |
| 1256 | + |
| 1257 | + // obfs 参数(根据 type 参数设置) |
| 1258 | + const type = params.get('type'); |
| 1259 | + if (type === 'ws') { |
| 1260 | + shadowrocketParams.set('obfs', 'websocket'); |
| 1261 | + } else if (type === 'xhttp') { |
| 1262 | + shadowrocketParams.set('obfs', 'http'); |
| 1263 | + } |
| 1264 | + |
| 1265 | + // tls 参数 |
| 1266 | + if (params.get('security') === 'tls') { |
| 1267 | + shadowrocketParams.set('tls', '1'); |
| 1268 | + } |
| 1269 | + |
| 1270 | + // peer 参数(从 sni 参数获取) |
| 1271 | + const peer = params.get('sni') || obfsParam; |
| 1272 | + shadowrocketParams.set('peer', peer); |
| 1273 | + |
| 1274 | + // alpn 参数(如果有,通常启用ECH时会有) |
| 1275 | + const alpn = params.get('alpn'); |
| 1276 | + if (alpn) { |
| 1277 | + shadowrocketParams.set('alpn', alpn); |
| 1278 | + } |
| 1279 | + |
| 1280 | + // fragment 和 mode 参数 |
| 1281 | + shadowrocketParams.set('fragment', 'auto'); |
| 1282 | + shadowrocketParams.set('mode', 'auto'); |
| 1283 | + |
| 1284 | + // ech 参数(只针对ECH,如果有) |
| 1285 | + const ech = params.get('ech'); |
| 1286 | + if (ech) { |
| 1287 | + shadowrocketParams.set('ech', ech); |
| 1288 | + } |
| 1289 | + |
| 1290 | + // 对于 xhttp 协议,需要添加 mode 参数(如果原链接有) |
| 1291 | + const mode = params.get('mode'); |
| 1292 | + if (mode) { |
| 1293 | + shadowrocketParams.set('mode', mode); |
| 1294 | + } |
| 1295 | + |
| 1296 | + // 构建小火箭格式的链接(保持原协议类型) |
| 1297 | + return `${protocol}://${shadowrocketUser}@${host}:${port}?${shadowrocketParams.toString()}`; |
| 1298 | + } catch (error) { |
| 1299 | + // 如果转换失败,返回原链接 |
| 1300 | + return link; |
| 1301 | + } |
| 1302 | + }); |
| 1303 | + |
| 1304 | + return btoa(shadowrocketLinks.join('\n')); |
| 1305 | + } |
| 1306 | + |
1209 | 1307 | // 全局变量存储ECH调试信息 |
1210 | 1308 | let echDebugInfo = ''; |
1211 | 1309 |
|
|
1360 | 1458 |
|
1361 | 1459 | const finalLinks = []; |
1362 | 1460 | const workerDomain = url.hostname; |
1363 | | - const target = url.searchParams.get('target') || 'base64'; |
| 1461 | + |
| 1462 | + // 检测 User-Agent 或 target 参数来确定是否为小火箭 |
| 1463 | + const userAgent = request.headers.get('User-Agent') || ''; |
| 1464 | + const isShadowrocket = userAgent.toLowerCase().includes('shadowrocket') || |
| 1465 | + url.searchParams.get('target')?.toLowerCase() === 'shadowrocket'; |
| 1466 | + |
| 1467 | + const target = isShadowrocket ? 'shadowrocket' : (url.searchParams.get('target') || 'base64'); |
1364 | 1468 |
|
1365 | 1469 | // 获取ECH配置(如果启用) |
1366 | 1470 | let echConfig = null; |
|
1512 | 1616 | case atob('bG9vbg=='): |
1513 | 1617 | subscriptionContent = generateLoonConfig(finalLinks); |
1514 | 1618 | break; |
| 1619 | + case 'shadowrocket': |
| 1620 | + subscriptionContent = generateShadowrocketConfig(finalLinks); |
| 1621 | + break; |
1515 | 1622 | default: |
1516 | 1623 | subscriptionContent = btoa(finalLinks.join('\n')); |
1517 | 1624 | } |
|
2177 | 2284 | KR: '🇰🇷 韩国', DE: '🇩🇪 德国', SE: '🇸🇪 瑞典', NL: '🇳🇱 荷兰', |
2178 | 2285 | FI: '🇫🇮 芬兰', GB: '🇬🇧 英国' |
2179 | 2286 | }, |
2180 | | - terminal: '终端 v2.9.1', |
| 2287 | + terminal: '终端 v2.9.2', |
2181 | 2288 | githubProject: 'GitHub 项目', |
2182 | 2289 | autoDetectClient: '自动识别', |
2183 | 2290 | selectionLogicText: '同地区 → 邻近地区 → 其他地区', |
|
3024 | 3131 | var finalUrl = subscriptionUrl; |
3025 | 3132 |
|
3026 | 3133 | if (clientType === atob('djJyYXk=')) { |
3027 | | - finalUrl = subscriptionUrl; |
| 3134 | + // 如果是 Shadowrocket,自动添加 target=shadowrocket 参数 |
| 3135 | + if (clientName === 'Shadowrocket') { |
| 3136 | + finalUrl = subscriptionUrl + (subscriptionUrl.includes('?') ? '&' : '?') + 'target=shadowrocket'; |
| 3137 | + } else { |
| 3138 | + finalUrl = subscriptionUrl; |
| 3139 | + } |
3028 | 3140 | var urlElement = document.getElementById("clientSubscriptionUrl"); |
3029 | 3141 | urlElement.textContent = finalUrl; |
3030 | 3142 | urlElement.style.display = "block"; |
|
0 commit comments