1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#0b1020">
<title>جارو 🧹 | اسکنر آیپی تمیز کلادفلر</title>
<link rel="manifest" href="manifest.json">
<link rel="icon" href="icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="icon.svg">
<style>
* { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
:root { --txt:#eef2ff; --muted:#94a3b8; --card:rgba(255,255,255,.06); --line:rgba(255,255,255,.1); }
body { margin:0; font-family:Tahoma,'Segoe UI',sans-serif; color:var(--txt); background:#0b1020; min-height:100vh; overflow-x:hidden; }
.aurora { position:fixed; inset:0; z-index:-1; overflow:hidden; }
.blob { position:absolute; border-radius:50%; filter:blur(90px); opacity:.5; animation:drift 14s ease-in-out infinite alternate; }
.b1 { width:420px;height:420px; background:#7c3aed; top:-120px; right:-100px; }
.b2 { width:380px;height:380px; background:#0ea5e9; bottom:-120px; left:-100px; animation-delay:-5s; }
.b3 { width:260px;height:260px; background:#ec4899; top:40%; left:30%; opacity:.3; animation-delay:-9s; }
@keyframes drift { from{transform:translate(0,0) scale(1)} to{transform:translate(40px,30px) scale(1.15)} }
.wrap { max-width:720px; margin:0 auto; padding:20px 16px 60px; }
header { text-align:center; padding:26px 0 10px; }
.logo { font-size:3rem; filter:drop-shadow(0 6px 20px rgba(124,58,237,.6)); animation:float 3s ease-in-out infinite; }
@keyframes float { 50%{transform:translateY(-8px)} }
h1 { margin:8px 0 4px; font-size:1.6rem; background:linear-gradient(90deg,#c4b5fd,#7dd3fc,#f9a8d4); -webkit-background-clip:text; background-clip:text; color:transparent; }
.sub { color:var(--muted); font-size:.85rem; line-height:1.9; }
.glass { background:var(--card); border:1px solid var(--line); border-radius:20px; padding:18px; backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px); box-shadow:0 8px 32px rgba(0,0,0,.35); margin:14px 0; }
.seg { display:flex; background:rgba(0,0,0,.3); border-radius:14px; padding:4px; gap:4px; }
.seg button { flex:1; border:none; background:transparent; color:var(--muted); padding:10px 4px; border-radius:10px; font-family:inherit; font-size:.9rem; cursor:pointer; transition:.2s; }
.seg button.on { background:linear-gradient(135deg,#7c3aed,#0ea5e9); color:#fff; font-weight:bold; box-shadow:0 4px 14px rgba(124,58,237,.5); }
.big { width:100%; margin-top:12px; padding:16px; font-size:1.15rem; font-weight:bold; font-family:inherit; border:none; border-radius:16px; cursor:pointer; color:#fff; background:linear-gradient(135deg,#7c3aed,#2563eb,#0ea5e9); background-size:200% 200%; animation:sheen 4s linear infinite; box-shadow:0 8px 26px rgba(37,99,235,.45); }
@keyframes sheen { 50%{background-position:100% 100%} }
.big:disabled { opacity:.55; animation:none; }
.ghost { background:rgba(255,255,255,.07); border:1px solid var(--line); color:var(--txt); border-radius:12px; padding:10px 16px; font-family:inherit; cursor:pointer; }
.pbar { height:12px; background:rgba(0,0,0,.35); border-radius:8px; overflow:hidden; margin-top:14px; }
.pbar > div { height:100%; width:0%; border-radius:8px; background:linear-gradient(90deg,#7c3aed,#0ea5e9,#4ade80); transition:width .25s; }
.stats { display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin-top:14px; }
.stat { background:rgba(0,0,0,.25); border-radius:14px; padding:10px; text-align:center; }
.stat .v { font-size:1.25rem; font-weight:bold; }
.stat .l { font-size:.72rem; color:var(--muted); margin-top:2px; }
.live { text-align:center; color:var(--muted); font-size:.8rem; margin-top:10px; min-height:1.4em; font-family:monospace; direction:ltr; }
.rip { display:flex; align-items:center; gap:12px; background:rgba(0,0,0,.25); border:1px solid var(--line); border-radius:16px; padding:12px 14px; margin:8px 0; animation:pop .3s ease; }
@keyframes pop { from{transform:scale(.96); opacity:0} }
.rank { width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-weight:bold; font-size:.9rem; flex-shrink:0; background:linear-gradient(135deg,#f59e0b,#ec4899); }
.rip:nth-child(1) .rank { background:linear-gradient(135deg,#fbbf24,#f59e0b); box-shadow:0 0 14px rgba(251,191,36,.6); }
.rip:nth-child(2) .rank { background:linear-gradient(135deg,#e2e8f0,#94a3b8); }
.rip:nth-child(3) .rank { background:linear-gradient(135deg,#d97706,#92400e); }
.ip { font-family:monospace; font-size:1.05rem; direction:ltr; }
.rng { font-size:.7rem; color:var(--muted); font-family:monospace; direction:ltr; }
.pill { margin-inline-start:auto; font-weight:bold; font-size:.9rem; padding:6px 12px; border-radius:20px; direction:ltr; flex-shrink:0; }
.p-good { background:rgba(74,222,128,.15); color:#4ade80; border:1px solid rgba(74,222,128,.4); }
.p-mid { background:rgba(251,191,36,.12); color:#fbbf24; border:1px solid rgba(251,191,36,.4); }
.p-bad { background:rgba(248,113,113,.12); color:#f87171; border:1px solid rgba(248,113,113,.4); }
.cp { border:1px solid var(--line); background:rgba(255,255,255,.06); color:#7dd3fc; border-radius:10px; padding:8px 12px; font-family:inherit; cursor:pointer; flex-shrink:0; }
.tools { display:flex; gap:8px; margin-bottom:6px; align-items:center; }
.tools b { font-size:1rem; }
.hint { font-size:.8rem; color:var(--muted); line-height:2; }
.hint a { color:#7dd3fc; }
#installBtn { display:none; width:100%; margin-top:10px; }
.foot { text-align:center; color:var(--muted); font-size:.75rem; margin-top:20px; }
</style>
</head>
<body>
<div class="aurora"><div class="blob b1"></div><div class="blob b2"></div><div class="blob b3"></div></div>
<div class="wrap">
<header>
<div class="logo">🧹</div>
<h1>جارو — اسکنر آیپی تمیز</h1>
<div class="sub">تمیزترین آیپیهای لبهی کلادفلر، تستشده از اینترنت <b>خودت</b><br>مخصوص همین شبکه (همراه اول / ایرانسل / ...)</div>
</header>
<div class="glass">
<div class="seg" id="seg">
<button data-n="60">⚡ سریع<span style="display:block;font-size:.7rem">۶۰ آیپی</span></button>
<button data-n="150" class="on">🔍 معمولی<span style="display:block;font-size:.7rem">۱۵۰ آیپی</span></button>
<button data-n="300">🌊 عمیق<span style="display:block;font-size:.7rem">۳۰۰ آیپی</span></button>
</div>
<button class="big" id="start">شروع اسکن ✨</button>
<button class="ghost" id="installBtn">📲 نصب اپ روی گوشی / کامپیوتر</button>
<div class="pbar"><div id="barfill"></div></div>
<div class="stats">
<div class="stat"><div class="v" id="stTested">۰</div><div class="l">تستشده</div></div>
<div class="stat"><div class="v" id="stOk" style="color:#4ade80">۰</div><div class="l">سالم</div></div>
<div class="stat"><div class="v" id="stBest" style="color:#7dd3fc">—</div><div class="l">بهترین پینگ</div></div>
</div>
<div class="live" id="live"></div>
</div>
<div class="glass" id="resCard" style="display:none">
<div class="tools"><b>🏆 تمیزترینها</b><span style="flex:1"></span>
<button class="ghost" id="dlTxt">TXT</button>
<button class="ghost" id="dlCsv">CSV</button>
</div>
<div id="list"></div>
</div>
<div class="glass hint">
💡 «تمیز» = آیپیای که از شبکهی تو سریع و بدون اختلال به لبهی کلادفلر میرسه. رنجها از
<a href="https://www.cloudflare.com/ips-v4">لیست رسمی کلادفلر</a> هستن و هر روز خودکار تازه میشن.
تست با درخواست HTTP به پورت ۸۰ انجام میشه — فقط «زمان اتصال» مهمه.<br>
⚠️ این ابزار پینگ و دسترسی رو میسنجه؛ آیپی خوبِ امروز ممکنه فردا کثیف بشه.
</div>
<div class="foot">ساخته شده با 🧹 برای اینترنت آزاد</div>
</div>
<script id="cfdata" type="application/json">
{"ranges":["173.245.48.0/20","103.21.244.0/22","103.22.200.0/22","103.31.4.0/22","141.101.64.0/18","108.162.192.0/18","190.93.240.0/20","188.114.96.0/20","197.234.240.0/22","198.41.128.0/17","162.158.0.0/15","104.16.0.0/13","104.24.0.0/14","172.64.0.0/13","131.0.72.0/22"]}
</script>
<script>
const RANGES_URL='data/cf-ips.json', TIMEOUT_MS=6000, CONCURRENCY=12;
let ranges=[], stopFlag=false, results=[], sampleN=150;
const fa=n=>Number(n).toLocaleString('fa-IR'), $=id=>document.getElementById(id);
async function loadRanges(){
try{ ranges=(await (await fetch(RANGES_URL)).json()).ranges; }
catch(e){ ranges=JSON.parse(document.getElementById('cfdata').textContent).ranges; }
}
const ipToInt=ip=>ip.split('.').reduce((a,o)=>(a<<8)+ +o,0)>>>0;
const intToIp=n=>[(n>>>24)&255,(n>>>16)&255,(n>>>8)&255,n&255].join('.');
function randomIp(cidr){
const [base,p]=cidr.split('/'), bits=+p, net=ipToInt(base), hosts=Math.pow(2,32-bits);
const h = hosts<=2 ? 0 : 1+Math.floor(Math.random()*(hosts-2));
return {ip:intToIp((net+h)>>>0), range:cidr};
}
function sampleIps(n){
const out=[], per=Math.ceil(n/ranges.length);
for(const r of ranges) for(let i=0;i<per&&out.length<n;i++) out.push(randomIp(r));
for(let i=out.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[out[i],out[j]]=[out[j],out[i]];}
return out.slice(0,n);
}
function probe(ip){
return new Promise(res=>{
const t0=performance.now(), ctl=new AbortController();
const to=setTimeout(()=>{ctl.abort();res(null);},TIMEOUT_MS);
fetch('http://'+ip+'/',{mode:'no-cors',cache:'no-store',signal:ctl.signal})
.then(()=>{clearTimeout(to);res(Math.round(performance.now()-t0));})
.catch(()=>{clearTimeout(to);res(null);});
});
}
const pill=ms=>ms<300?'p-good':ms<800?'p-mid':'p-bad';
let scanning=false;
async function run(){
if(scanning){ stopFlag=true; return; } // click while scanning = stop
scanning=true; stopFlag=false; results=[];
const btn=$('start'); btn.textContent='توقف ⏹';
$('resCard').style.display='none'; $('list').innerHTML='';
const ips=sampleIps(sampleN);
let done=0, ok=0, best=Infinity;
const render=()=>{ $('barfill').style.width=(done/ips.length*100)+'%';
$('stTested').textContent=fa(done); $('stOk').textContent=fa(ok);
$('stBest').textContent=best===Infinity?'—':fa(best)+'ms'; };
render(); let idx=0;
async function worker(){
while(idx<ips.length && !stopFlag){
const s=ips[idx++];
$('live').textContent='probing '+s.ip+' ...';
const ms=await probe(s.ip); done++;
if(ms!==null){ok++; if(ms<best)best=ms; results.push({...s,ms});}
render();
}
}
await Promise.all(Array.from({length:CONCURRENCY},worker));
$('live').textContent=stopFlag?'متوقف شد.':'تمام شد ✅ '+fa(ok)+' آیپی سالم پیدا شد';
results.sort((a,b)=>a.ms-b.ms); showResults();
scanning=false; btn.textContent='اسکن دوباره 🔄';
}
function showResults(){
if(!results.length)return;
$('resCard').style.display='';
$('list').innerHTML=results.slice(0,50).map((r,i)=>
`<div class="rip"><div class="rank">${fa(i+1)}</div>
<div><div class="ip">${r.ip}</div><div class="rng">${r.range}</div></div>
<div class="pill ${pill(r.ms)}">${fa(r.ms)}ms</div>
<button class="cp" data-ip="${r.ip}">کپی</button></div>`).join('');
document.querySelectorAll('.cp').forEach(b=>b.onclick=()=>{
navigator.clipboard.writeText(b.dataset.ip);
b.textContent='✓'; setTimeout(()=>b.textContent='کپی',1200);});
}
function dl(name,text){const a=document.createElement('a');
a.href=URL.createObjectURL(new Blob([text],{type:'text/plain'}));a.download=name;a.click();
setTimeout(()=>URL.revokeObjectURL(a.href),2000);}
$('dlTxt').onclick=()=>dl('clean-ips.txt',results.map(r=>r.ip).join('\n'));
$('dlCsv').onclick=()=>dl('clean-ips.csv','ip,latency_ms,range\n'+results.map(r=>`${r.ip},${r.ms},${r.range}`).join('\n'));
$('start').onclick=()=>run();
document.querySelectorAll('#seg button').forEach(b=>b.onclick=()=>{
document.querySelectorAll('#seg button').forEach(x=>x.classList.remove('on'));
b.classList.add('on'); sampleN=+b.dataset.n; });
// PWA install
let deferredPrompt=null;
window.addEventListener('beforeinstallprompt',e=>{e.preventDefault();deferredPrompt=e;$('installBtn').style.display='';});
$('installBtn').onclick=async()=>{
if(deferredPrompt){deferredPrompt.prompt();await deferredPrompt.userChoice;deferredPrompt=null;$('installBtn').style.display='none';}
else alert('از منوی مرورگر گزینهی "Add to Home Screen" (گوشی) یا "Install" (کامپیوتر) رو بزن 📲');
};
if('serviceWorker' in navigator) window.addEventListener('load',()=>navigator.serviceWorker.register('sw.js').catch(()=>{}));
loadRanges().catch(()=>{$('live').textContent='خطا در لود رنجها!';});
</script>
</body>
</html>