@@ -279,7 +279,7 @@ export default {
279279 return new Response ( '优选订阅生成器异常:' + error . message , { status : 403 } ) ;
280280 }
281281 }
282-
282+ const ECHLINK参数 = config_JSON . ECH ? '&ech=' + encodeURIComponent ( await getECH ( config_JSON . HOST ) ) : '' ;
283283 订阅内容 = 其他节点LINK + 完整优选IP . map ( 原始地址 => {
284284 // 统一正则: 匹配 域名/IPv4/IPv6地址 + 可选端口 + 可选备注
285285 // 示例:
@@ -301,7 +301,7 @@ export default {
301301 return null ;
302302 }
303303
304- return `${ 协议类型 } ://00000000-0000-4000-8000-000000000000@${ 节点地址 } :${ 节点端口 } ?security=tls&type=${ config_JSON . 传输协议 } &host=example.com&sni=example.com&path=${ encodeURIComponent ( config_JSON . 随机路径 ? 随机路径 ( ) + 节点路径 : 节点路径 ) + TLS分片参数 } &encryption=none${ config_JSON . 跳过证书验证 ? '&allowInsecure=1' : '' } #${ encodeURIComponent ( 节点备注 ) } ` ;
304+ return `${ 协议类型 } ://00000000-0000-4000-8000-000000000000@${ 节点地址 } :${ 节点端口 } ?security=tls&type=${ config_JSON . 传输协议 + ECHLINK参数 } &host=example.com&fp= ${ config_JSON . Fingerprint } & sni=example.com&path=${ encodeURIComponent ( config_JSON . 随机路径 ? 随机路径 ( ) + 节点路径 : 节点路径 ) + TLS分片参数 } &encryption=none${ config_JSON . 跳过证书验证 ? '&insecure=1 &allowInsecure=1' : '' } #${ encodeURIComponent ( 节点备注 ) } ` ;
305305 } ) . filter ( item => item !== null ) . join ( '\n' ) ;
306306 } else { // 订阅转换
307307 const 订阅转换URL = `${ config_JSON . 订阅转换配置 . SUBAPI } /sub?target=${ 订阅类型 } &url=${ encodeURIComponent ( url . protocol + '//' + url . host + '/sub?target=mixed&token=' + 订阅TOKEN + ( url . searchParams . has ( 'sub' ) && url . searchParams . get ( 'sub' ) != '' ? `&sub=${ url . searchParams . get ( 'sub' ) } ` : '' ) ) } &config=${ encodeURIComponent ( config_JSON . 订阅转换配置 . SUBCONFIG ) } &emoji=${ config_JSON . 订阅转换配置 . SUBEMOJI } &scv=${ config_JSON . 跳过证书验证 } ` ;
@@ -316,12 +316,12 @@ export default {
316316 }
317317 }
318318
319- if ( ! ua . includes ( 'subconverter' ) ) 订阅内容 = 批量替换域名 ( 订阅内容 . replace ( / 0 0 0 0 0 0 0 0 - 0 0 0 0 - 4 0 0 0 - 8 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 / g, config_JSON . UUID ) , config_JSON . HOSTS )
319+ if ( ! ua . includes ( 'subconverter' ) ) 订阅内容 = await 批量替换域名 ( 订阅内容 . replace ( / 0 0 0 0 0 0 0 0 - 0 0 0 0 - 4 0 0 0 - 8 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 / g, config_JSON . UUID ) , config_JSON . HOSTS )
320320
321321 if ( 订阅类型 === 'mixed' && ( ! ua . includes ( 'mozilla' ) || url . searchParams . has ( 'b64' ) || url . searchParams . has ( 'base64' ) ) ) 订阅内容 = btoa ( 订阅内容 ) ;
322322
323323 if ( 订阅类型 === 'singbox' ) {
324- 订阅内容 = Singbox订阅配置文件热补丁 ( 订阅内容 ) ;
324+ 订阅内容 = Singbox订阅配置文件热补丁 ( 订阅内容 , config_JSON . UUID , config_JSON . Fingerprint , config_JSON . ECH ? await getECH ( host ) : null ) ;
325325 responseHeaders [ "content-type" ] = 'application/json; charset=utf-8' ;
326326 } else if ( 订阅类型 === 'clash' ) {
327327 responseHeaders [ "content-type" ] = 'application/x-yaml; charset=utf-8' ;
@@ -784,7 +784,7 @@ async function httpConnect(targetHost, targetPort, initialData) {
784784 }
785785}
786786//////////////////////////////////////////////////功能性函数///////////////////////////////////////////////
787- function Singbox订阅配置文件热补丁 ( sb_json_text ) {
787+ function Singbox订阅配置文件热补丁 ( sb_json_text , uuid = null , fingerprint = "chrome" , ech_config = null ) {
788788 try {
789789 let config = JSON . parse ( sb_json_text ) ;
790790
@@ -929,6 +929,35 @@ function Singbox订阅配置文件热补丁(sb_json_text) {
929929 }
930930 } ) ;
931931
932+ // --- 4. UUID 匹配节点的 TLS 热补丁 (utls & ech) ---
933+ if ( uuid ) {
934+ config . outbounds . forEach ( outbound => {
935+ // 仅处理包含 uuid 且匹配的节点
936+ if ( outbound . uuid && outbound . uuid === uuid ) {
937+ // 确保 tls 对象存在
938+ if ( ! outbound . tls ) {
939+ outbound . tls = { enabled : true } ;
940+ }
941+
942+ // 添加/更新 utls 配置
943+ if ( fingerprint ) {
944+ outbound . tls . utls = {
945+ enabled : true ,
946+ fingerprint : fingerprint
947+ } ;
948+ }
949+
950+ // 如果提供了 ech_config,添加/更新 ech 配置
951+ if ( ech_config ) {
952+ outbound . tls . ech = {
953+ enabled : true ,
954+ config : `-----BEGIN ECH CONFIGS-----\n${ ech_config } \n-----END ECH CONFIGS-----`
955+ } ;
956+ }
957+ }
958+ } ) ;
959+ }
960+
932961 return JSON . stringify ( config , null , 2 ) ;
933962 } catch ( e ) {
934963 console . error ( "Singbox热补丁执行失败:" , e ) ;
@@ -1071,6 +1100,38 @@ function 批量替换域名(内容, hosts, 每组数量 = 2) {
10711100 } ) ;
10721101}
10731102
1103+ async function getECH ( host ) {
1104+ try {
1105+ const res = await fetch ( `https://1.1.1.1/dns-query?name=${ encodeURIComponent ( host ) } &type=65` , { headers : { 'accept' : 'application/dns-json' } } ) ;
1106+ const data = await res . json ( ) ;
1107+ if ( ! data . Answer ?. length ) return '' ;
1108+ for ( let ans of data . Answer ) {
1109+ if ( ans . type !== 65 || ! ans . data ) continue ;
1110+ const match = ans . data . match ( / e c h = ( [ ^ \s ] + ) / ) ;
1111+ if ( match ) return match [ 1 ] . replace ( / " / g, '' ) ;
1112+ if ( ans . data . startsWith ( '\\#' ) ) {
1113+ const hex = ans . data . split ( ' ' ) . slice ( 2 ) . join ( '' ) ;
1114+ const bytes = new Uint8Array ( hex . match ( / .{ 1 , 2 } / g) . map ( b => parseInt ( b , 16 ) ) ) ;
1115+ let offset = 2 ;
1116+ while ( offset < bytes . length && bytes [ offset ++ ] !== 0 )
1117+ offset += bytes [ offset - 1 ] ;
1118+
1119+ while ( offset + 4 <= bytes . length ) {
1120+ const key = ( bytes [ offset ] << 8 ) | bytes [ offset + 1 ] ;
1121+ const len = ( bytes [ offset + 2 ] << 8 ) | bytes [ offset + 3 ] ;
1122+ offset += 4 ;
1123+
1124+ if ( key === 5 ) return btoa ( String . fromCharCode ( ...bytes . slice ( offset , offset + len ) ) ) ;
1125+ offset += len ;
1126+ }
1127+ }
1128+ }
1129+ return '' ;
1130+ } catch {
1131+ return '' ;
1132+ }
1133+ }
1134+
10741135async function 读取config_JSON ( env , hostname , userID , path , 重置配置 = false ) {
10751136 //const host = 随机替换通配符(hostname);
10761137 const host = hostname ;
@@ -1086,6 +1147,8 @@ async function 读取config_JSON(env, hostname, userID, path, 重置配置 = fal
10861147 启用0RTT : false ,
10871148 TLS分片 : null ,
10881149 随机路径 : false ,
1150+ ECH : false ,
1151+ Fingerprint : "chrome" ,
10891152 优选订阅生成 : {
10901153 local : true , // true: 基于本地的优选地址 false: 优选订阅生成器
10911154 本地IP库 : {
@@ -1152,7 +1215,10 @@ async function 读取config_JSON(env, hostname, userID, path, 重置配置 = fal
11521215 config_JSON . UUID = userID ;
11531216 config_JSON . PATH = path ? ( path . startsWith ( '/' ) ? path : '/' + path ) : ( config_JSON . 反代 . SOCKS5 . 启用 ? ( '/' + config_JSON . 反代 . SOCKS5 . 启用 + ( config_JSON . 反代 . SOCKS5 . 全局 ? '://' : '=' ) + config_JSON . 反代 . SOCKS5 . 账号 ) : ( config_JSON . 反代 . PROXYIP === 'auto' ? '/' : `/proxyip=${ config_JSON . 反代 . PROXYIP } ` ) ) ;
11541217 const TLS分片参数 = config_JSON . TLS分片 == 'Shadowrocket' ? `&fragment=${ encodeURIComponent ( '1,40-60,30-50,tlshello' ) } ` : config_JSON . TLS分片 == 'Happ' ? `&fragment=${ encodeURIComponent ( '3,1,tlshello' ) } ` : '' ;
1155- config_JSON . LINK = `${ config_JSON . 协议类型 } ://${ userID } @${ host } :443?security=tls&type=${ config_JSON . 传输协议 } &host=${ host } &sni=${ host } &path=${ encodeURIComponent ( config_JSON . 启用0RTT ? config_JSON . PATH + '?ed=2560' : config_JSON . PATH ) + TLS分片参数 } &encryption=none${ config_JSON . 跳过证书验证 ? '&allowInsecure=1' : '' } #${ encodeURIComponent ( config_JSON . 优选订阅生成 . SUBNAME ) } ` ;
1218+ if ( ! config_JSON . Fingerprint ) config_JSON . Fingerprint = "chrome" ;
1219+ if ( ! config_JSON . ECH ) config_JSON . ECH = false ;
1220+ const ECHLINK参数 = config_JSON . ECH ? '&ech=' + encodeURIComponent ( await getECH ( config_JSON . HOST ) ) : '' ;
1221+ config_JSON . LINK = `${ config_JSON . 协议类型 } ://${ userID } @${ host } :443?security=tls&type=${ config_JSON . 传输协议 + ECHLINK参数 } &host=${ host } &fp=${ config_JSON . Fingerprint } &sni=${ host } &path=${ encodeURIComponent ( config_JSON . 启用0RTT ? config_JSON . PATH + '?ed=2560' : config_JSON . PATH ) + TLS分片参数 } &encryption=none${ config_JSON . 跳过证书验证 ? '&insecure=1&allowInsecure=1' : '' } #${ encodeURIComponent ( config_JSON . 优选订阅生成 . SUBNAME ) } ` ;
11561222 config_JSON . 优选订阅生成 . TOKEN = await MD5MD5 ( hostname + userID ) ;
11571223
11581224 const 初始化TG_JSON = { BotToken : null , ChatID : null } ;
0 commit comments