diff --git a/README.md b/README.md index e3e162c..7ba6ba6 100644 --- a/README.md +++ b/README.md @@ -279,4 +279,5 @@ - [Stanley-baby](https://github.com/Stanley-baby) - [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR/tree/master/Clash/config) - [股神](https://t.me/CF_NAT/38889) -- [Workers/Pages Metrics](https://t.me/zhetengsha/3382) \ No newline at end of file +- [Workers/Pages Metrics](https://t.me/zhetengsha/3382) +- [白嫖哥](https://t.me/bestcfipas) \ No newline at end of file diff --git a/_worker.js b/_worker.js index e11b525..99146fe 100644 --- a/_worker.js +++ b/_worker.js @@ -2592,7 +2592,33 @@ async function resolveToIPv6(target) { async function bestIP(request, env, txt = 'ADD.txt') { const country = request.cf?.country || 'CN'; const url = new URL(request.url); - + async function getNipDomain() { + try { + const response = await fetch(atob('aHR0cHM6Ly9jbG91ZGZsYXJlLWRucy5jb20vZG5zLXF1ZXJ5P25hbWU9bmlwLjA5MDIyNy54eXomdHlwZT1UWFQ='), { + headers: { + 'Accept': 'application/dns-json' + } + }); + + if (response.ok) { + const data = await response.json(); + if (data.Status === 0 && data.Answer && data.Answer.length > 0) { + // TXT记录的值通常包含在引号中,需要去除引号 + const txtRecord = data.Answer[0].data; + // 去除首尾的引号 + const domain = txtRecord.replace(/^"(.*)"$/, '$1'); + console.log('通过DoH解析获取到域名: ' + domain); + return domain; + } + } + console.warn('DoH解析失败,使用默认域名'); + return atob('bmlwLmxmcmVlLm9yZw=='); + } catch (error) { + console.error('DoH解析出错:', error); + return atob('aXAuMDkwMjI3Lnh5eg=='); + } + } + const nipDomain = await getNipDomain(); async function GetCFIPs(ipSource = 'official', targetPort = '443') { try { let response; @@ -2635,11 +2661,11 @@ async function bestIP(request, env, txt = 'ADD.txt') { console.log(`反代IP列表解析完成,端口${targetPort}匹配到${validIps.length}个有效IP`); - // 如果超过1000个IP,随机选择1000个 - if (validIps.length > 1000) { + // 如果超过512个IP,随机选择512个 + if (validIps.length > 512) { const shuffled = [...validIps].sort(() => 0.5 - Math.random()); - const selectedIps = shuffled.slice(0, 1000); - console.log(`IP数量超过1000个,随机选择了${selectedIps.length}个IP`); + const selectedIps = shuffled.slice(0, 512); + console.log(`IP数量超过512个,随机选择了${selectedIps.length}个IP`); return selectedIps; } else { return validIps; @@ -2667,7 +2693,7 @@ async function bestIP(request, env, txt = 'ADD.txt') { const cidrs = text.split('\n').filter(line => line.trim() && !line.startsWith('#')); const ips = new Set(); // 使用Set去重 - const targetCount = 1000; + const targetCount = 512; let round = 1; // 不断轮次生成IP直到达到目标数量 @@ -2773,9 +2799,9 @@ async function bestIP(request, env, txt = 'ADD.txt') { // 构建返回格式 if (comment) { - return `${ip}:${port}#${comment}`; + return ip + ':' + port + '#' + comment; } else { - return `${ip}:${port}`; + return ip + ':' + port; } } catch (error) { @@ -3274,6 +3300,28 @@ async function bestIP(request, env, txt = 'ADD.txt') { color: #e65100; font-weight: bold; } + .region-buttons { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 10px; + } + .region-btn { + padding: 6px 12px; + background-color: #e0e0e0; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: all 0.3s; + } + .region-btn:hover { + background-color: #d5d5d5; + } + .region-btn.active { + background-color: #2196F3; + color: white; + } @@ -3325,7 +3373,6 @@ async function bestIP(request, env, txt = 'ADD.txt') { - @@ -3360,6 +3407,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {

IP列表

+
请选择端口和IP库,然后点击"开始延迟测试"加载IP列表
@@ -3373,6 +3421,7 @@ async function bestIP(request, env, txt = 'ADD.txt') { let displayedResults = []; // 新增:存储当前显示的结果 let showingAll = false; // 新增:标记是否显示全部内容 let currentDisplayType = 'loading'; // 新增:当前显示类型 'loading' | 'results' + let cloudflareLocations = {}; // 新增:存储Cloudflare位置信息 // 新增:本地存储管理 const StorageKeys = { @@ -3380,6 +3429,27 @@ async function bestIP(request, env, txt = 'ADD.txt') { IP_SOURCE: 'cf-ip-test-source' }; + // 新增:加载Cloudflare位置信息 + async function loadCloudflareLocations() { + try { + const response = await fetch('https://speed.cloudflare.com/locations'); + if (response.ok) { + const locations = await response.json(); + // 转换为以iata为key的对象,便于快速查找 + cloudflareLocations = {}; + locations.forEach(location => { + cloudflareLocations[location.iata] = location; + }); + console.log('Cloudflare位置信息加载成功:', Object.keys(cloudflareLocations).length, '个位置'); + } else { + console.warn('无法加载Cloudflare位置信息,将使用原始colo值'); + } + } catch (error) { + console.error('加载Cloudflare位置信息失败:', error); + console.warn('将使用原始colo值'); + } + } + // 初始化页面设置 function initializeSettings() { const portSelect = document.getElementById('port-select'); @@ -3414,7 +3484,12 @@ async function bestIP(request, env, txt = 'ADD.txt') { } // 页面加载完成后初始化设置 - document.addEventListener('DOMContentLoaded', initializeSettings); + document.addEventListener('DOMContentLoaded', async function() { + // 先加载Cloudflare位置信息 + await loadCloudflareLocations(); + // 然后初始化页面设置 + initializeSettings(); + }); // 新增:切换显示更多/更少 function toggleShowMore() { @@ -3524,7 +3599,17 @@ async function bestIP(request, env, txt = 'ADD.txt') { } async function saveIPs() { - if (displayedResults.length === 0) { + // 使用当前显示的结果而不是全部结果 + let ipsToSave = []; + if (document.getElementById('region-filter') && document.getElementById('region-filter').style.display !== 'none') { + // 如果地区筛选器可见,使用筛选后的结果 + ipsToSave = displayedResults; + } else { + // 否则使用全部测试结果 + ipsToSave = testResults; + } + + if (ipsToSave.length === 0) { showMessage('没有可保存的IP结果', 'error'); return; } @@ -3538,8 +3623,8 @@ async function bestIP(request, env, txt = 'ADD.txt') { try { // 只保存前16个最优IP - const saveCount = Math.min(displayedResults.length, 16); - const ips = displayedResults.slice(0, saveCount).map(result => result.display); + const saveCount = Math.min(ipsToSave.length, 16); + const ips = ipsToSave.slice(0, saveCount).map(result => result.display); const response = await fetch('?action=save', { method: 'POST', @@ -3552,7 +3637,7 @@ async function bestIP(request, env, txt = 'ADD.txt') { const data = await response.json(); if (data.success) { - showMessage(\`\${data.message}(已保存前\${saveCount}个最优IP)\`, 'success'); + showMessage(data.message + '(已保存前' + saveCount + '个最优IP)', 'success'); } else { showMessage(data.error || '保存失败', 'error'); } @@ -3566,7 +3651,17 @@ async function bestIP(request, env, txt = 'ADD.txt') { } async function appendIPs() { - if (displayedResults.length === 0) { + // 使用当前显示的结果而不是全部结果 + let ipsToAppend = []; + if (document.getElementById('region-filter') && document.getElementById('region-filter').style.display !== 'none') { + // 如果地区筛选器可见,使用筛选后的结果 + ipsToAppend = displayedResults; + } else { + // 否则使用全部测试结果 + ipsToAppend = testResults; + } + + if (ipsToAppend.length === 0) { showMessage('没有可追加的IP结果', 'error'); return; } @@ -3580,8 +3675,8 @@ async function bestIP(request, env, txt = 'ADD.txt') { try { // 只追加前16个最优IP - const saveCount = Math.min(displayedResults.length, 16); - const ips = displayedResults.slice(0, saveCount).map(result => result.display); + const saveCount = Math.min(ipsToAppend.length, 16); + const ips = ipsToAppend.slice(0, saveCount).map(result => result.display); const response = await fetch('?action=append', { method: 'POST', @@ -3594,7 +3689,7 @@ async function bestIP(request, env, txt = 'ADD.txt') { const data = await response.json(); if (data.success) { - showMessage(\`\${data.message}(已追加前\${saveCount}个最优IP)\`, 'success'); + showMessage(data.message + '(已追加前' + saveCount + '个最优IP)', 'success'); } else { showMessage(data.error || '追加失败', 'error'); } @@ -3620,7 +3715,7 @@ async function bestIP(request, env, txt = 'ADD.txt') { } async function testIP(ip, port) { - const timeout = 999; + const timeout = 5000; // 增加超时时间到5秒 // 解析IP格式 const parsedIP = parseIPFormat(ip, port); @@ -3628,46 +3723,40 @@ async function bestIP(request, env, txt = 'ADD.txt') { return null; } - // 第一次测试 - const firstResult = await singleTest(parsedIP.host, parsedIP.port, timeout); - if (!firstResult) { - return null; // 第一次测试失败,直接返回 + // 进行测试,最多重试3次 + let lastError = null; + for (let attempt = 1; attempt <= 3; attempt++) { + const result = await singleTest(parsedIP.host, parsedIP.port, timeout); + if (result) { + console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第\${attempt}次测试成功: \${result.latency}ms, colo: \${result.colo}, 类型: \${result.type}\`); + + // 根据colo字段获取国家代码 + const locationCode = cloudflareLocations[result.colo] ? cloudflareLocations[result.colo].cca2 : result.colo; + + // 生成显示格式 + const typeText = result.type === 'official' ? '官方优选' : '反代优选'; + const display = \`\${parsedIP.host}:\${parsedIP.port}#\${locationCode} \${typeText} \${result.latency}ms\`; + + return { + ip: parsedIP.host, + port: parsedIP.port, + latency: result.latency, + colo: result.colo, + type: result.type, + locationCode: locationCode, + comment: \`\${locationCode} \${typeText}\`, + display: display + }; + } else { + console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第\${attempt}次测试失败\`); + if (attempt < 3) { + // 短暂延迟后重试 + await new Promise(resolve => setTimeout(resolve, 200)); + } + } } - // 第一次测试成功,再进行第二次测试 - console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第一次测试成功: \${firstResult.latency}ms,进行第二次测试...\`); - - const results = [firstResult]; - - // 进行第二次测试 - const secondResult = await singleTest(parsedIP.host, parsedIP.port, timeout); - if (secondResult) { - results.push(secondResult); - console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第二次测试: \${secondResult.latency}ms\`); - } - - // 取最低延迟 - const bestResult = results.reduce((best, current) => - current.latency < best.latency ? current : best - ); - - const displayLatency = Math.floor(bestResult.latency / 2); - - console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 最终结果: \${displayLatency}ms (原始: \${bestResult.latency}ms, 共\${results.length}次有效测试)\`); - - // 生成显示格式 - const comment = parsedIP.comment || 'CF优选IP'; - const display = \`\${parsedIP.host}:\${parsedIP.port}#\${comment} \${displayLatency}ms\`; - - return { - ip: parsedIP.host, - port: parsedIP.port, - latency: displayLatency, - originalLatency: bestResult.latency, - testCount: results.length, - comment: comment, - display: display - }; + return null; // 所有尝试都失败 } // 新增:解析IP格式的函数 @@ -3710,38 +3799,107 @@ async function bestIP(request, env, txt = 'ADD.txt') { } async function singleTest(ip, port, timeout) { - const startTime = Date.now(); - + // 先进行预请求以缓存DNS解析结果 try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); + const parts = ip.split('.').map(part => { + const hex = parseInt(part, 10).toString(16); + return hex.length === 1 ? '0' + hex : hex; // 补零 + }); + const nip = parts.join(''); - const response = await fetch(\`https://\${ip}:\${port}/cdn-cgi/trace\`, { + // 预请求,不计入延迟时间 + await fetch('https://' + nip + '.${nipDomain}:' + port + '/cdn-cgi/trace', { signal: controller.signal, mode: 'cors' }); clearTimeout(timeoutId); - // 如果请求成功了,说明这个IP不是我们要的 + } catch (preRequestError) { + // 预请求失败可以忽略,继续进行正式测试 + console.log('预请求失败 (' + ip + ':' + port + '):', preRequestError.message); + } + + // 正式延迟测试 + const startTime = Date.now(); + + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + const parts = ip.split('.').map(part => { + const hex = parseInt(part, 10).toString(16); + return hex.length === 1 ? '0' + hex : hex; // 补零 + }); + const nip = parts.join(''); + const response = await fetch('https://' + nip + '.${nipDomain}:' + port + '/cdn-cgi/trace', { + signal: controller.signal, + mode: 'cors' + }); + + clearTimeout(timeoutId); + + // 检查响应状态 + if (response.status === 200) { + const latency = Date.now() - startTime; + const responseText = await response.text(); + + // 解析trace响应 + const traceData = parseTraceResponse(responseText); + + if (traceData && traceData.ip && traceData.colo) { + // 判断IP类型 + const responseIP = traceData.ip; + let ipType = 'official'; // 默认官方IP + + // 检查是否是IPv6(包含冒号)或者IP相等 + if (responseIP.includes(':') || responseIP === ip) { + ipType = 'proxy'; // 反代IP + } + // 如果responseIP与ip不相等且不是IPv6,则是官方IP + + return { + ip: ip, + port: port, + latency: latency, + colo: traceData.colo, + type: ipType, + responseIP: responseIP + }; + } + } + return null; } catch (error) { const latency = Date.now() - startTime; // 检查是否是真正的超时(接近设定的timeout时间) - if (latency >= timeout - 50) { + if (latency >= timeout - 100) { return null; } - // 检查是否是 Failed to fetch 错误(通常是SSL/证书错误) - if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) { - return { - ip: ip, - port: port, - latency: latency - }; + return null; + } + } + + // 新增:解析trace响应的函数 + function parseTraceResponse(responseText) { + try { + const lines = responseText.split('\\n'); + const data = {}; + + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine && trimmedLine.includes('=')) { + const [key, value] = trimmedLine.split('=', 2); + data[key] = value; + } } + return data; + } catch (error) { + console.error('解析trace响应失败:', error); return null; } } @@ -3833,11 +3991,8 @@ async function bestIP(request, env, txt = 'ADD.txt') { case 'as13335': ipSourceName = 'CF全段'; break; - case 'as35916': - ipSourceName = 'CF非官方1'; - break; case 'as209242': - ipSourceName = 'CF非官方2'; + ipSourceName = 'CF非官方'; break; case 'as24429': ipSourceName = 'Alibaba'; @@ -3852,7 +4007,7 @@ async function bestIP(request, env, txt = 'ADD.txt') { ipSourceName = '未知'; } - progressText.textContent = \`正在加载 \${ipSourceName} IP列表...\`; + progressText.textContent = '正在加载 ' + ipSourceName + ' IP列表...'; // 加载IP列表 originalIPs = await loadIPs(selectedIPSource, selectedPort); @@ -3869,21 +4024,21 @@ async function bestIP(request, env, txt = 'ADD.txt') { } // 更新IP数量显示 - ipCount.textContent = \`\${originalIPs.length} 个\`; + ipCount.textContent = originalIPs.length + ' 个'; // 显示加载的IP列表(默认显示前16个) displayLoadedIPs(); // 开始测试 testBtn.textContent = '测试中...'; - progressText.textContent = \`开始测试端口 \${selectedPort}...\`; + progressText.textContent = '开始测试端口 ' + selectedPort + '...'; currentDisplayType = 'testing'; // 切换到测试状态 // 在测试开始时隐藏显示更多按钮 showMoreSection.style.display = 'none'; - // 使用16个并发线程测试 - const results = await testIPsWithConcurrency(originalIPs, selectedPort, 16); + // 使用更高的并发数(从16增加到32)来加快测试速度 + const results = await testIPsWithConcurrency(originalIPs, selectedPort, 32); // 按延迟排序 testResults = results.sort((a, b) => a.latency - b.latency); @@ -3893,11 +4048,14 @@ async function bestIP(request, env, txt = 'ADD.txt') { showingAll = false; // 重置显示状态 displayResults(); + // 创建地区筛选器 + createRegionFilter(); + testBtn.disabled = false; testBtn.textContent = '重新测试'; portSelect.disabled = false; ipSourceSelect.disabled = false; - progressText.textContent = \`完成 - 有效IP: \${testResults.length}/\${originalIPs.length} (端口: \${selectedPort}, IP库: \${ipSourceName})\`; + progressText.textContent = '完成 - 有效IP: ' + testResults.length + '/' + originalIPs.length + ' (端口: ' + selectedPort + ', IP库: ' + ipSourceName + ')'; } // 新增:加载IP列表的函数 @@ -3942,12 +4100,12 @@ async function bestIP(request, env, txt = 'ADD.txt') { // 更新结果计数显示 if (testResults.length <= 16) { - resultCount.textContent = \`(共测试出 \${testResults.length} 个有效IP)\`; - ipDisplayInfo.textContent = \`显示全部 \${testResults.length} 个测试结果\`; + resultCount.textContent = '(共测试出 ' + testResults.length + ' 个有效IP)'; + ipDisplayInfo.textContent = '显示全部 ' + testResults.length + ' 个测试结果'; showMoreSection.style.display = 'none'; } else { - resultCount.textContent = \`(共测试出 \${testResults.length} 个有效IP)\`; - ipDisplayInfo.textContent = \`显示前 \${maxDisplayCount} 个测试结果,共 \${testResults.length} 个有效IP\`; + resultCount.textContent = '(共测试出 ' + testResults.length + ' 个有效IP)'; + ipDisplayInfo.textContent = '显示前 ' + maxDisplayCount + ' 个测试结果,共 ' + testResults.length + ' 个有效IP'; showMoreSection.style.display = 'block'; showMoreBtn.textContent = showingAll ? '显示更少' : '显示更多'; showMoreBtn.disabled = false; // 确保在结果显示时启用按钮 @@ -3958,7 +4116,107 @@ async function bestIP(request, env, txt = 'ADD.txt') { if (result.latency > 200) className = 'bad-latency'; else if (result.latency > 100) className = 'medium-latency'; - return \`
\${result.display}
\`; + return '
' + result.display + '
'; + }).join(''); + + ipList.innerHTML = resultsHTML; + updateButtonStates(); + } + + // 新增:创建地区筛选器 + function createRegionFilter() { + // 获取所有唯一的地区代码(使用cca2代码) + const uniqueRegions = [...new Set(testResults.map(result => result.locationCode))]; + uniqueRegions.sort(); // 按字母顺序排序 + + const filterContainer = document.getElementById('region-filter'); + if (!filterContainer) return; + + if (uniqueRegions.length === 0) { + filterContainer.style.display = 'none'; + return; + } + + // 创建筛选按钮 + let filterHTML = '

地区筛选:

'; + filterHTML += ''; + + uniqueRegions.forEach(region => { + const count = testResults.filter(r => r.locationCode === region).length; + filterHTML += ''; + }); + + filterHTML += '
'; + filterContainer.innerHTML = filterHTML; + filterContainer.style.display = 'block'; + + // 添加点击事件 + document.querySelectorAll('.region-btn').forEach(button => { + button.addEventListener('click', function() { + // 更新活动按钮 + document.querySelectorAll('.region-btn').forEach(btn => { + btn.classList.remove('active'); + }); + this.classList.add('active'); + + // 筛选结果 + const selectedRegion = this.getAttribute('data-region'); + if (selectedRegion === 'all') { + displayedResults = [...testResults]; + } else { + displayedResults = testResults.filter(result => result.locationCode === selectedRegion); + } + + // 重置显示状态 + showingAll = false; + displayFilteredResults(); + }); + }); + } + + // 新增:显示筛选后的结果 + function displayFilteredResults() { + const ipList = document.getElementById('ip-list'); + const resultCount = document.getElementById('result-count'); + const showMoreSection = document.getElementById('show-more-section'); + const showMoreBtn = document.getElementById('show-more-btn'); + const ipDisplayInfo = document.getElementById('ip-display-info'); + + if (displayedResults.length === 0) { + ipList.innerHTML = '
未找到有效的IP
'; + resultCount.textContent = ''; + ipDisplayInfo.textContent = ''; + showMoreSection.style.display = 'none'; + updateButtonStates(); + return; + } + + // 确定显示数量 + const maxDisplayCount = showingAll ? displayedResults.length : Math.min(displayedResults.length, 16); + const currentResults = displayedResults.slice(0, maxDisplayCount); + + // 更新结果计数显示 + const totalCount = testResults.length; + const filteredCount = displayedResults.length; + + if (filteredCount <= 16) { + resultCount.textContent = '(共测试出 ' + totalCount + ' 个有效IP,筛选出 ' + filteredCount + ' 个)'; + ipDisplayInfo.textContent = '显示全部 ' + filteredCount + ' 个筛选结果'; + showMoreSection.style.display = 'none'; + } else { + resultCount.textContent = '(共测试出 ' + totalCount + ' 个有效IP,筛选出 ' + filteredCount + ' 个)'; + ipDisplayInfo.textContent = '显示前 ' + maxDisplayCount + ' 个筛选结果,共 ' + filteredCount + ' 个'; + showMoreSection.style.display = 'block'; + showMoreBtn.textContent = showingAll ? '显示更少' : '显示更多'; + showMoreBtn.disabled = false; + } + + const resultsHTML = currentResults.map(result => { + let className = 'good-latency'; + if (result.latency > 200) className = 'bad-latency'; + else if (result.latency > 100) className = 'medium-latency'; + + return '
' + result.display + '
'; }).join(''); ipList.innerHTML = resultsHTML;