33< head >
44 < meta charset ="UTF-8 ">
55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6- < title > Clash Config Generator (Full Protocol Fix )</ title >
6+ < title > Clash Config Generator (Final Perfect Version )</ title >
77 < style >
88 /* --- Minimalist Design --- */
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>TangShengWan's Clash Config Generator</h1>
138138 if ( ! input ) { alert ( "⚠️ Please paste content!" ) ; return ; }
139139
140140 // 1. Try as a link download
141- if ( input . startsWith ( 'http' ) ) {
141+ // [Safety Check] Skip download logic for protocol links to prevent crash
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 = "⏳ Parsing..." ;
@@ -173,10 +174,14 @@ <h1>TangShengWan's Clash Config Generator</h1>
173174 proxies = yamlProxies ;
174175 } else {
175176 // Strategy B: If not YAML, try parsing Base64 or vless:// links
176- if ( ! content . includes ( 'proxies:' ) && ! content . includes ( '- {' ) ) {
177+ // [Critical Fix] Do NOT try Base64 decode if it looks like a protocol link
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>TangShengWan's Clash Config Generator</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 ) ; // Added ss support
193+ else if ( line . startsWith ( 'ss://' ) ) result = parseSS ( line ) ;
189194
190195 if ( result ) {
191196 let finalName = result . name ;
@@ -206,16 +211,15 @@ <h1>TangShengWan's Clash Config Generator</h1>
206211 const configStr = p . config . toLowerCase ( ) ;
207212 const n = p . name . toLowerCase ( ) ;
208213
209- // 1. [Critical] Remove Proxy Group Types (Cause of "unsupported proxy type" error)
214+ // 1. [Critical] Remove Proxy Group Types
210215 if ( configStr . includes ( 'type: select' ) ||
211216 configStr . includes ( 'type: url-test' ) ||
212217 configStr . includes ( 'type: fallback' ) ||
213218 configStr . includes ( 'type: load-balance' ) ) {
214219 return false ;
215220 }
216221
217- // 2. Remove Ads/Invalid Nodes
218- // (Keeping Chinese keywords as they filter common ad nodes from providers)
222+ // 2. Remove Ads/Invalid Nodes (Keeping Chinese keywords as most providers are CN based)
219223 if ( n . includes ( '剩余' ) || n . includes ( '流量' ) || n . includes ( '到期' ) ||
220224 n . includes ( '官网' ) || n . includes ( 'expire' ) || n . includes ( 'reset' ) ||
221225 n . includes ( '🪧' ) || n . includes ( '不可用' ) ) {
@@ -273,16 +277,88 @@ <h1>TangShengWan's Clash Config Generator</h1>
273277 } catch ( e ) { console . error ( e ) ; return [ ] ; }
274278 }
275279
276- // --- Helper Tools ---
280+ // --- [Fixed] Safe Base64 Decode with try-catch ---
277281 function safeBase64Decode ( str ) {
278- str = str . replace ( / \s / g, '' ) ;
279- return decodeURIComponent ( escape ( atob ( str . replace ( / - / g, '+' ) . replace ( / _ / g, '/' ) ) ) ) ;
282+ try {
283+ str = str . replace ( / \s / g, '' ) ;
284+ return decodeURIComponent ( escape ( atob ( str . replace ( / - / g, '+' ) . replace ( / _ / g, '/' ) ) ) ) ;
285+ } catch ( e ) {
286+ console . warn ( "Base64 Decode Failed, treating as raw text." ) ;
287+ return null ;
288+ }
289+ }
290+
291+ // --- Parsers (Full Logic) ---
292+ function parseVless ( line ) {
293+ try {
294+ const u = new URL ( line ) ;
295+ const params = u . searchParams ;
296+ const name = decodeURIComponent ( u . hash . slice ( 1 ) ) || u . hostname ;
297+ const type = params . get ( 'type' ) || 'tcp' ;
298+ let config = ` - name: "${ name } "
299+ type: vless
300+ server: ${ u . hostname }
301+ port: ${ u . port }
302+ uuid: ${ u . username }
303+ tls: true
304+ udp: true
305+ skip-cert-verify: true
306+ servername: ${ params . get ( 'sni' ) || u . hostname }
307+ network: ${ type } ` ;
308+ if ( type === 'ws' ) config += `\n ws-opts:\n path: "${ params . get ( 'path' ) || '/' } "\n headers:\n Host: ${ params . get ( 'sni' ) || u . hostname } ` ;
309+ if ( params . get ( 'flow' ) ) config += `\n flow: ${ params . get ( 'flow' ) } ` ;
310+ return { name : name , config : config } ;
311+ } catch ( e ) { return null ; }
312+ }
313+
314+ function parseHysteria2 ( line ) {
315+ try {
316+ const u = new URL ( line ) ;
317+ const params = u . searchParams ;
318+ const name = decodeURIComponent ( u . hash . slice ( 1 ) ) || u . hostname ;
319+ let config = ` - name: "${ name } "
320+ type: hysteria2
321+ server: ${ u . hostname }
322+ port: ${ u . port }
323+ password: ${ u . username }
324+ sni: ${ params . get ( 'sni' ) || u . hostname }
325+ skip-cert-verify: true
326+ up: 100 Mbps
327+ down: 100 Mbps` ;
328+ if ( params . get ( 'obfs' ) && params . get ( 'obfs' ) !== 'none' ) {
329+ config += `\n obfs: ${ params . get ( 'obfs' ) } \n obfs-password: "${ params . get ( 'obfs-password' ) || '' } "` ;
330+ }
331+ return { name : name , config : config } ;
332+ } catch ( e ) { return null ; }
333+ }
334+
335+ function parseVmess ( line ) {
336+ try {
337+ const b64 = line . replace ( 'vmess://' , '' ) ;
338+ const jsonStr = safeBase64Decode ( b64 ) ;
339+ if ( ! jsonStr ) return null ;
340+ const c = JSON . parse ( jsonStr ) ;
341+ const name = c . ps || c . add ;
342+ return {
343+ name : name ,
344+ config : ` - name: "${ name } "
345+ type: vmess
346+ server: ${ c . add }
347+ port: ${ c . port }
348+ uuid: ${ c . id }
349+ alterId: ${ c . aid || 0 }
350+ cipher: ${ c . scy || 'auto' }
351+ udp: true
352+ tls: ${ c . tls === 'tls' }
353+ skip-cert-verify: true
354+ servername: ${ c . host || '' }
355+ network: ${ c . net || 'tcp' }
356+ ws-opts: { path: "${ c . path || '/' } ", headers: { Host: ${ c . host || '' } } }`
357+ } ;
358+ } catch ( e ) { return null ; }
280359 }
281360
282- function parseVless ( line ) { /* Simplified placeholder */ return null ; }
283- function parseHysteria2 ( line ) { return null ; }
284- function parseVmess ( line ) { return null ; }
285- function parseSS ( line ) { return null ; } // Placeholder
361+ function parseSS ( line ) { return null ; }
286362
287363 // Manual Assembly Logic (Simplified for Demo)
288364 function addNode ( ) {
0 commit comments