1- <!DOCTYPE html>
1+ <!DOCTYPE html>
22< html lang ="zh-CN ">
33< head >
44 < meta charset ="UTF-8 ">
55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6- < title > Clash 配置生成器 (全协议修复版 )</ title >
6+ < title > Clash 配置生成器 (最终完美版 )</ title >
77 < style >
88 /* --- 极简设计风 --- */
99 body { font-family : -apple-system, BlinkMacSystemFont, "SF Pro Text" , "Helvetica Neue" , Arial, sans-serif; background-color : # f5f5f7 ; color : # 1d1d1f ; display : flex; justify-content : center; padding-top : 40px ; min-height : 100vh ; margin : 0 ; }
@@ -138,7 +138,8 @@ <h1>汤盛碗的Clash 配置生成器</h1>
138138 if ( ! input ) { alert ( "⚠️ 请粘贴内容!" ) ; return ; }
139139
140140 // 1. 尝试作为链接下载
141- if ( input . startsWith ( 'http' ) ) {
141+ // 排除掉明确是单行节点链接的协议,避免误入下载逻辑
142+ if ( input . startsWith ( 'http' ) && ! input . startsWith ( 'hysteria2://' ) && ! input . startsWith ( 'vless://' ) && ! input . startsWith ( 'vmess://' ) && ! input . startsWith ( 'ss://' ) ) {
142143 const btn = document . querySelector ( '#mode-batch .btn' ) ;
143144 const originalText = btn . innerText ;
144145 btn . innerText = "⏳ 解析中..." ;
@@ -173,10 +174,14 @@ <h1>汤盛碗的Clash 配置生成器</h1>
173174 proxies = yamlProxies ;
174175 } else {
175176 // 策略 B: 如果不是 YAML,尝试解析 Base64 或 vless:// 链接
176- if ( ! content . includes ( 'proxies:' ) && ! content . includes ( '- {' ) ) {
177+ // 【重要修复】增加判断,如果是单行协议链接,绝对不要尝试 Base64 解码,否则会报错导致脚本中断
178+ const isProtocolLink = content . startsWith ( 'vless://' ) || content . startsWith ( 'hysteria2://' ) || content . startsWith ( 'vmess://' ) || content . startsWith ( 'ss://' ) ;
179+
180+ if ( ! content . includes ( 'proxies:' ) && ! content . includes ( '- {' ) && ! isProtocolLink ) {
177181 const decoded = safeBase64Decode ( content ) ;
178182 if ( decoded ) content = decoded ;
179183 }
184+
180185 const lines = content . split ( / [ \r \n ] + / ) ;
181186 let nameCount = { } ;
182187 lines . forEach ( line => {
@@ -185,7 +190,7 @@ <h1>汤盛碗的Clash 配置生成器</h1>
185190 if ( line . startsWith ( 'vless://' ) ) result = parseVless ( line ) ;
186191 else if ( line . startsWith ( 'hysteria2://' ) ) result = parseHysteria2 ( line ) ;
187192 else if ( line . startsWith ( 'vmess://' ) ) result = parseVmess ( line ) ;
188- else if ( line . startsWith ( 'ss://' ) ) result = parseSS ( line ) ; // 新增 ss 支持 (虽然简单正则)
193+ else if ( line . startsWith ( 'ss://' ) ) result = parseSS ( line ) ;
189194
190195 if ( result ) {
191196 let finalName = result . name ;
@@ -206,7 +211,7 @@ <h1>汤盛碗的Clash 配置生成器</h1>
206211 const configStr = p . config . toLowerCase ( ) ;
207212 const n = p . name . toLowerCase ( ) ;
208213
209- // 1. [关键] 剔除策略组类型 (这是导致 unsupported proxy type 的元凶)
214+ // 1. [关键] 剔除策略组类型
210215 if ( configStr . includes ( 'type: select' ) ||
211216 configStr . includes ( 'type: url-test' ) ||
212217 configStr . includes ( 'type: fallback' ) ||
@@ -242,9 +247,7 @@ <h1>汤盛碗的Clash 配置生成器</h1>
242247
243248 lines . forEach ( line => {
244249 line = line . trim ( ) ;
245- // 核心特征:以 "- {" 开头,且包含 "name:"
246250 if ( line . startsWith ( '- {' ) && line . includes ( 'name:' ) ) {
247-
248251 let name = "" ;
249252 const quoteMatch = line . match ( / n a m e : \s * [ ' " ] ( .* ?) [ ' " ] / ) ;
250253 if ( quoteMatch ) {
@@ -272,16 +275,89 @@ <h1>汤盛碗的Clash 配置生成器</h1>
272275 } catch ( e ) { console . error ( e ) ; return [ ] ; }
273276 }
274277
275- // --- 辅助工具 ---
278+ // --- [修复] 辅助工具:增加错误捕获,防止解码失败导致程序崩溃 ---
276279 function safeBase64Decode ( str ) {
277- str = str . replace ( / \s / g, '' ) ;
278- return decodeURIComponent ( escape ( atob ( str . replace ( / - / g, '+' ) . replace ( / _ / g, '/' ) ) ) ) ;
280+ try {
281+ str = str . replace ( / \s / g, '' ) ;
282+ return decodeURIComponent ( escape ( atob ( str . replace ( / - / g, '+' ) . replace ( / _ / g, '/' ) ) ) ) ;
283+ } catch ( e ) {
284+ // 如果解码失败,返回 null 或者原字符串,保证程序继续运行
285+ console . warn ( "Base64 Decode Failed, treating as raw text." ) ;
286+ return null ;
287+ }
279288 }
280289
281- function parseVless ( line ) { /* 简化保留 */ return null ; }
282- function parseHysteria2 ( line ) { return null ; }
283- function parseVmess ( line ) { return null ; }
284- function parseSS ( line ) { return null ; } // 占位
290+ // --- 解析函数 ---
291+ function parseVless ( line ) {
292+ try {
293+ const u = new URL ( line ) ;
294+ const params = u . searchParams ;
295+ const name = decodeURIComponent ( u . hash . slice ( 1 ) ) || u . hostname ;
296+ const type = params . get ( 'type' ) || 'tcp' ;
297+ let config = ` - name: "${ name } "
298+ type: vless
299+ server: ${ u . hostname }
300+ port: ${ u . port }
301+ uuid: ${ u . username }
302+ tls: true
303+ udp: true
304+ skip-cert-verify: true
305+ servername: ${ params . get ( 'sni' ) || u . hostname }
306+ network: ${ type } ` ;
307+ if ( type === 'ws' ) config += `\n ws-opts:\n path: "${ params . get ( 'path' ) || '/' } "\n headers:\n Host: ${ params . get ( 'sni' ) || u . hostname } ` ;
308+ if ( params . get ( 'flow' ) ) config += `\n flow: ${ params . get ( 'flow' ) } ` ;
309+ return { name : name , config : config } ;
310+ } catch ( e ) { return null ; }
311+ }
312+
313+ function parseHysteria2 ( line ) {
314+ try {
315+ const u = new URL ( line ) ;
316+ const params = u . searchParams ;
317+ const name = decodeURIComponent ( u . hash . slice ( 1 ) ) || u . hostname ;
318+ let config = ` - name: "${ name } "
319+ type: hysteria2
320+ server: ${ u . hostname }
321+ port: ${ u . port }
322+ password: ${ u . username }
323+ sni: ${ params . get ( 'sni' ) || u . hostname }
324+ skip-cert-verify: true
325+ up: 100 Mbps
326+ down: 100 Mbps` ;
327+ if ( params . get ( 'obfs' ) && params . get ( 'obfs' ) !== 'none' ) {
328+ config += `\n obfs: ${ params . get ( 'obfs' ) } \n obfs-password: "${ params . get ( 'obfs-password' ) || '' } "` ;
329+ }
330+ return { name : name , config : config } ;
331+ } catch ( e ) { return null ; }
332+ }
333+
334+ function parseVmess ( line ) {
335+ try {
336+ const b64 = line . replace ( 'vmess://' , '' ) ;
337+ const jsonStr = safeBase64Decode ( b64 ) ;
338+ if ( ! jsonStr ) return null ;
339+ const c = JSON . parse ( jsonStr ) ;
340+ const name = c . ps || c . add ;
341+ return {
342+ name : name ,
343+ config : ` - name: "${ name } "
344+ type: vmess
345+ server: ${ c . add }
346+ port: ${ c . port }
347+ uuid: ${ c . id }
348+ alterId: ${ c . aid || 0 }
349+ cipher: ${ c . scy || 'auto' }
350+ udp: true
351+ tls: ${ c . tls === 'tls' }
352+ skip-cert-verify: true
353+ servername: ${ c . host || '' }
354+ network: ${ c . net || 'tcp' }
355+ ws-opts: { path: "${ c . path || '/' } ", headers: { Host: ${ c . host || '' } } }`
356+ } ;
357+ } catch ( e ) { return null ; }
358+ }
359+
360+ function parseSS ( line ) { return null ; }
285361
286362 // 手动组装逻辑 (简化版,仅作展示)
287363 function addNode ( ) {
@@ -352,5 +428,4 @@ <h1>汤盛碗的Clash 配置生成器</h1>
352428 }
353429</ script >
354430</ body >
355- </ html >
356-
431+ </ html >
0 commit comments