Panduan aman dan simpel untuk membersihkan daftar Following Twitter/X Anda. Ikuti langkah-langkah di bawah ini.
Login ke akun Twitter/X Anda di browser laptop atau komputer. Masuk ke profil Anda, lalu klik tab Following.
Buka TwitterKlik tombol copy di bawah untuk menyalin seluruh kode.
// Mass Unfollow V.1 (BETA)
// by Keith. (@miegrains)
// ==========================================
// SAFETY EDITION
// Copy and paste this ENTIRE script into your Browser Console.
// ==========================================
(function() {
'use strict';
// --- State & Config ---
const STATE = {
stage: 'INIT',
users: [],
whitelist: new Set(),
selection: new Set(),
config: {
skipTop: 0,
safetyLevel: 'SAFE',
dailyCap: 120,
}
};
// User requested custom intervals
const PROFILES = {
ULTRA: {
min: 300000, // 5 menit
max: 600000, // 10 menit
idleFreq: 3,
idleMin: 600000, // 10 menit idle
idleMax: 1200000 // 20 menit idle
},
SAFE: {
min: 180000, // 3 menit
max: 360000, // 6 menit
idleFreq: 5,
idleMin: 420000, // 7 menit idle
idleMax: 900000 // 15 menit idle
},
RISKY: { min: 5000, max: 20000, idleFreq: 25, idleMin: 30000, idleMax: 60000 },
VERY_RISKY: { min: 1000, max: 5000, idleFreq: 50, idleMin: 10000, idleMax: 20000 }
};
// --- Elegant SaaS UI Styles ---
const STYLES = `
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
#mu-root {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
position: fixed; top: 20px; right: 20px; width: 420px;
background: #ffffff; color: #111111;
border-radius: 16px;
box-shadow: 0 10px 40px rgba(0,0,0,0.15);
z-index: 9999999; border: 1px solid #efefef;
display: flex; flex-direction: column;
font-size: 13px;
}
#mu-header {
background: #0a0a0a; color: #ffffff;
padding: 16px;
display: flex; align-items: center; justify-content: space-between;
cursor: move; border-radius: 16px 16px 0 0;
}
.mu-logo-wrap { display: flex; align-items: center; gap: 10px; }
.mu-logo {
width: 28px; height: 28px; border-radius: 8px;
background: linear-gradient(135deg, #ff8c00, #ff5000);
display: flex; align-items: center; justify-content: center;
font-weight: 800; font-size: 14px; color: #fff;
}
.mu-title-box { display: flex; flex-direction: column; }
.mu-title { font-size: 15px; font-weight: 700; line-height: 1.2; display:flex; align-items:center; gap:6px; }
.mu-beta-badge { background: #ff7300; color: #fff; font-size: 10px; font-weight: 800; padding: 2px 6px; border-radius: 4px; letter-spacing: 0.5px; }
.mu-author { font-size: 10px; color: #888; font-weight: 500; }
.mu-header-icons { display: flex; align-items: center; gap: 12px; color: #aaa; }
.mu-icon-btn { cursor: pointer; transition: color 0.2s; font-size: 18px; font-weight: bold; width: 24px; height: 24px; display:flex; align-items:center; justify-content:center; }
.mu-icon-btn:hover { color: #fff; }
.mu-pill-tabs {
background: #f4f5f7; border-radius: 12px; padding: 4px;
display: flex; margin: 16px 16px 0 16px;
}
.mu-pill {
flex: 1; text-align: center; padding: 10px 0; font-size: 12px; font-weight: 600;
color: #666; border-radius: 8px; cursor: pointer; transition: all 0.2s;
}
.mu-pill.active { background: #ffffff; color: #111; box-shadow: 0 2px 8px rgba(0,0,0,0.06); }
.mu-panel { display: none; padding: 16px; max-height: 65vh; overflow-y: auto; overflow-x: hidden; }
.mu-panel.active { display: block; animation: fadeIn 0.2s ease; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.mu-card {
border: 1px solid #efefef; border-radius: 16px; padding: 16px 20px;
background: #ffffff;
}
.mu-card-title { font-size: 15px; font-weight: 800; text-align: center; margin-bottom: 6px; color:#111; }
.mu-card-desc { font-size: 12px; color: #666; text-align: center; margin-bottom: 24px; line-height: 1.5; }
.mu-form-group { margin-bottom: 20px; text-align: left; }
.mu-label { display: block; font-size: 13px; font-weight: 700; color: #111; margin-bottom: 6px; }
.mu-subtext { font-size: 11px; color: #888; margin-top: 6px; line-height: 1.4; }
.mu-input, .mu-select, .mu-textarea {
width: 100%; box-sizing: border-box;
background: #f9f9f9; border: 1px solid #e0e0e0; border-radius: 10px;
padding: 12px; font-family: inherit; font-size: 13px; color: #111;
outline: none; transition: border 0.2s, background 0.2s;
appearance: none;
}
.mu-input:focus, .mu-select:focus, .mu-textarea:focus {
background: #fff; border-color: #ff7300; box-shadow: 0 0 0 3px rgba(255,115,0,0.1);
}
.mu-textarea { height: 75px; resize: none; }
.mu-btn-black {
background: #0a0a0a; color: #fff; border: none; border-radius: 12px;
padding: 14px; font-size: 14px; font-weight: 700; width: 100%;
cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;
transition: all 0.2s; margin-top: 8px;
}
.mu-btn-black:hover { background: #222; transform: translateY(-1px); }
.mu-btn-orange {
background: #ff7300; color: #fff; border: none; border-radius: 12px;
padding: 14px; font-size: 14px; font-weight: 700; width: 100%;
cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;
transition: all 0.2s;
}
.mu-btn-orange:hover { background: #e66800; transform: translateY(-1px); }
.mu-btn-white {
background: #ffffff; color: #111; border: 1px solid #e0e0e0; border-radius: 10px;
padding: 10px 14px; font-size: 13px; font-weight: 600; width: 100%;
cursor: pointer; transition: all 0.2s; text-align: center;
}
.mu-btn-white:hover { background: #f9f9f9; border-color: #d0d0d0; }
.mu-btn-danger { color: #f4212e; border-color: #ffe0e0; background: #fff5f5; }
.mu-btn-danger:hover { background: #ffebeb; border-color: #ffc4c4; }
.mu-list-title { font-size: 14px; font-weight: 800; margin-bottom: 12px; color: #111; }
.mu-user-item {
background: #ffffff; border: 1px solid #efefef; border-radius: 12px;
padding: 14px; display: flex; align-items: center; margin-bottom: 10px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.mu-user-item:hover { border-color: #e0e0e0; box-shadow: 0 4px 12px rgba(0,0,0,0.03); }
.mu-user-chk {
appearance: none; width: 22px; height: 22px;
border: 2px solid #ddd; border-radius: 6px;
margin-right: 14px; cursor: pointer; flex-shrink: 0;
position: relative; transition: all 0.2s; background: #fff;
}
.mu-user-chk:checked { background: #ff7300; border-color: #ff7300; }
.mu-user-chk:checked::after {
content: '✔'; color: white; position: absolute; font-size: 13px; font-weight:800;
top: 50%; left: 50%; transform: translate(-50%, -50%);
}
.mu-user-chk:disabled { background: #f4f5f7; border-color: #eee; cursor: not-allowed; }
.mu-user-chk:disabled::after { color: #ccc; }
.mu-user-info { flex: 1; overflow: hidden; }
.mu-user-handle { font-size: 14px; font-weight: 800; color: #111; text-decoration: none; display: block; width: fit-content; margin-bottom: 2px; }
.mu-user-handle:hover { text-decoration: underline; color: #ff7300; }
.mu-user-name { font-size: 12px; color: #666; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 6px; }
.mu-user-status { font-size: 11px; font-weight: 800; letter-spacing: 0.5px; text-transform:uppercase; }
.status-mutual { color: #888; }
.status-none { color: #ff7300; }
.status-skip { color: #ccc; }
.mu-monitor-box {
background: #f9f9f9; border: 1px solid #e0e0e0; border-radius: 16px;
padding: 24px; text-align: center; margin-bottom: 20px;
}
.mu-prog-text { font-size: 32px; font-weight: 800; color: #111; margin-bottom: 16px; font-variant-numeric: tabular-nums; }
.mu-prog-track { height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden; }
.mu-prog-bar { height: 100%; background: #ff7300; width: 0%; transition: width 0.3s ease-out; }
.mu-logs {
background: #ffffff; border: 1px solid #e0e0e0; border-radius: 12px;
padding: 12px; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; color: #666;
height: 120px; margin-top: 16px; overflow-y: auto; overflow-x: hidden; line-height: 1.5;
}
.log-success { color: #00ba7c; font-weight: 600; }
.log-err { color: #f4212e; font-weight: 600; }
.log-item { border-bottom: 1px solid #f0f0f0; padding-bottom: 4px; margin-bottom: 4px; }
.log-item:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
/* Modal / Popups */
.mu-modal-overlay {
display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.4); border-radius: 16px; z-index: 1000;
align-items: center; justify-content: center; backdrop-filter: blur(2px);
}
.mu-modal-box {
background: #fff; width: 85%; max-height: 85%; border-radius: 14px;
padding: 24px; display: flex; flex-direction: column;
box-shadow: 0 10px 40px rgba(0,0,0,0.2); border: 1px solid #efefef;
}
.mu-modal-title { font-size: 16px; font-weight: 800; color: #111; margin-bottom: 12px; text-transform:uppercase; letter-spacing:0.5px; }
.mu-modal-content { font-size: 13px; color: #555; overflow-y: auto; flex: 1; line-height: 1.6; padding-right: 5px; }
.mu-modal-content p { margin-top: 0; margin-bottom: 12px; }
.mu-modal-content a { color: #ff7300; text-decoration: none; font-weight: 800; }
.mu-modal-content a:hover { text-decoration: underline; }
/* Estetika Scrollbar / Ultra Sleek */
#mu-root * { scrollbar-width: thin; scrollbar-color: #c1c1c1 transparent; }
#mu-root *::-webkit-scrollbar { width: 5px; height: 5px; }
#mu-root *::-webkit-scrollbar-track { background: transparent; border-radius: 10px; }
#mu-root *::-webkit-scrollbar-thumb { background: #d0d0d0; border-radius: 10px; }
#mu-root *::-webkit-scrollbar-thumb:hover { background: #ff7300; }
`;
// --- UI Construction ---
function createUI() {
const old = document.getElementById('mu-root');
if (old) old.remove();
const root = document.createElement('div');
root.id = 'mu-root';
root.innerHTML = `
<style>${STYLES}</style>
<div id="mu-header">
<div class="mu-logo-wrap">
<div class="mu-logo">MU</div>
<div class="mu-title-box">
<div class="mu-title">Mass Unfollow <span class="mu-beta-badge">BETA</span></div>
<div class="mu-author">by Keith (@miegrains)</div>
</div>
</div>
<div class="mu-header-icons">
<div id="mu-close" class="mu-icon-btn">✕</div>
</div>
</div>
<div class="mu-pill-tabs">
<div class="mu-pill active" id="tab-scan" data-target="SCAN">Scan Setup</div>
<div class="mu-pill" id="tab-review" data-target="REVIEW">User History</div>
<div class="mu-pill" id="tab-run" data-target="RUN">Action</div>
</div>
<!-- MODAL SYSTEM -->
<div id="mu-modal" class="mu-modal-overlay">
<div class="mu-modal-box">
<div id="mu-modal-title" class="mu-modal-title">Title</div>
<div id="mu-modal-content" class="mu-modal-content mu-scroll">Content</div>
<button id="mu-modal-btn" class="mu-btn-black" style="margin-top:20px; padding:12px;">Tutup</button>
</div>
</div>
<!-- SCAN PANEL -->
<div id="panel-scan" class="mu-panel active">
<div style="display:flex; gap:8px; margin-bottom:16px;">
<button id="btn-tutorial" class="mu-btn-white" style="flex:1; font-size:12px; font-weight:700;">📖 Cara Pakai</button>
<button id="btn-disclaimer" class="mu-btn-white" style="flex:1; font-size:12px; font-weight:700;">🛡️ Disclaimer / Keamanan</button>
</div>
<div class="mu-card">
<div class="mu-card-title">Setup Configuration</div>
<div class="mu-card-desc" style="margin-bottom:16px;">Configure parameters to safely scan users you follow, filter mutuals, and prepare your unfollow list.</div>
<div class="mu-form-group">
<label class="mu-label">Whitelist</label>
<div style="position:relative;">
<textarea id="inp-whitelist" class="mu-textarea" placeholder="@family\n@friends"></textarea>
<button id="btn-save-wl" class="mu-btn-white" style="position:absolute; right:10px; bottom:10px; width:auto; padding:4px 10px; font-size:11px;">Save</button>
</div>
<div class="mu-subtext">Put specific @handles here so they are never accidentally unfollowed.</div>
<div id="wl-status" style="font-size:11px; color:#00ba7c; font-weight:700; display:none; margin-top:4px;">✔ Whitelist Saved</div>
</div>
<div class="mu-form-group">
<label class="mu-label">Skip Recent Follows (Abaikan Follow Baru)</label>
<input type="number" id="inp-skip" class="mu-input" value="0" placeholder="e.g. 50">
<div class="mu-subtext" style="color:#ff7300; font-weight:600;">Apa fungsinya?</div>
<div class="mu-subtext">Isi dengan angka (Misal: 50). Maka script ini TIDAK AKAN unfollow 50 orang paling atas yang baru saja kamu follow. Ini aman untuk menjaga koneksi barumu.</div>
</div>
<div class="mu-form-group">
<label class="mu-label">Scanning Process</label>
<div class="mu-subtext" style="margin-top:0; margin-bottom:8px;">Klik start untuk memindai akun Twittermu. (Aman, belum melakukan unfollow).</div>
<button id="btn-scan" class="mu-btn-black">
<span style="color:#ff7300; font-size:18px; font-weight:800;">›</span> Start Scanning
</button>
</div>
<div id="scan-running" style="display:none; text-align:center; padding: 15px 0;">
<div style="font-size:13px; color:#666; font-weight:600;">Scanning Profile...</div>
<div style="font-size:36px; font-weight:800; margin:10px 0; color:#ff7300;" id="scan-count">0</div>
<div style="font-size:12px; color:#888; font-weight:500; margin-bottom:20px;">Users Found</div>
<button id="btn-scan-stop" class="mu-btn-white mu-btn-danger">Stop Scan Early</button>
</div>
</div>
</div>
<!-- REVIEW PANEL -->
<div id="panel-review" class="mu-panel">
<div class="mu-card" style="padding:16px; margin-bottom:16px;">
<div style="display:flex; justify-content:space-between; align-items:center;">
<div>
<div style="font-size:11px; color:#888; font-weight:600; text-transform:uppercase;">Total Found</div>
<div style="font-weight:800; font-size:18px;" id="lbl-found">0</div>
</div>
<div style="text-align:right;">
<div style="font-size:11px; color:#666; font-weight:600; text-transform:uppercase;">Selected to Unfollow</div>
<div style="font-weight:800; font-size:20px; color:#ff7300;" id="lbl-selected">0</div>
</div>
</div>
</div>
<div class="mu-list-title">Compare History</div>
<div id="mu-list-root" style="height: 250px; overflow-y: auto; background:#fcfcfc; border: 1px solid #efefef; border-radius: 12px; padding: 10px; margin-bottom:16px;">
<div style="text-align:center; padding: 40px 20px; color:#888; font-size: 13px; font-weight:500;">List is empty. Go Scan first.</div>
</div>
<div style="display:flex; gap:10px; margin-bottom:20px;">
<button class="mu-btn-white" id="btn-select-all" style="flex:1;">Select Non-Mutuals</button>
<button class="mu-btn-white" id="btn-deselect" style="flex:1;">Deselect All</button>
</div>
<div>
<button id="btn-go-action" class="mu-btn-black">Proceed to Action ›</button>
</div>
</div>
<!-- ACTION PANEL -->
<div id="panel-run" class="mu-panel">
<div class="mu-form-group">
<label class="mu-label">Unfollow Delay (Kecepatan & Interval Jeda)</label>
<div class="mu-subtext" style="margin-top:0; margin-bottom:6px;">Berapa jeda waktu setiap 1 kali aksi unfollow?</div>
<select id="inp-safety" class="mu-select" style="font-weight:600;">
<option value="SAFE" selected>🟢 SAFE (Human-like) [ 35-60 detik ]</option>
<option value="ULTRA">🟢 ULTRA 24H AFK [ 90-180 detik ]</option>
<option value="RISKY">🔴 RISKY (Bot Farm) [ 5-20 detik ]</option>
<option value="VERY_RISKY">🚨 VERY RISKY (Auto-Flag) [ < 5 detik ]</option>
</select>
</div>
<div class="mu-form-group">
<label class="mu-label">Daily Cap (Batas Maksimal Harian)</label>
<div class="mu-subtext" style="margin-top:0; margin-bottom:6px;">Berhenti otomatis jika sudah mencapai angka ini.</div>
<select id="inp-cap" class="mu-select" style="font-weight:600;">
<option value="120" selected>🟢 Aman (Max 80-120 akun)</option>
<option value="400">🔴 Risky (Max 200-400 akun)</option>
<option value="100">🚨 Very Risky (Batas max 50-100)</option>
</select>
</div>
<div class="mu-monitor-box">
<div style="font-size:12px; font-weight:700; color:#888; margin-bottom:8px; text-transform:uppercase;">Successfully Unfollowed</div>
<div class="mu-prog-text" id="lbl-progress">0 <span style="font-size:20px; color:#ccc;">/ 0</span></div>
<div class="mu-prog-track">
<div class="mu-prog-bar" id="bar-progress"></div>
</div>
<div id="mu-status" style="font-size:12px; color:#ff7300; margin-top:12px; font-weight:700;">Ready to start</div>
</div>
<button id="btn-run" class="mu-btn-orange">Execute Unfollows</button>
<button id="btn-stop" class="mu-btn-white mu-btn-danger" style="display:none; margin-top:10px;">Stop Execution</button>
<div class="mu-logs" id="mu-logs"></div>
<div style="display:flex; gap:10px; margin-top:20px;">
<button id="btn-back-review" class="mu-btn-white" style="flex:1;">‹ Back to Review</button>
<button id="btn-export" class="mu-btn-white" style="flex:1;">Export Reports ↓</button>
</div>
</div>
`;
document.body.appendChild(root);
// --- Attach Event Listeners ---
document.getElementById('mu-close').addEventListener('click', () => root.remove());
document.getElementById('btn-scan').addEventListener('click', startScan);
document.getElementById('btn-scan-stop').addEventListener('click', stopScan);
document.getElementById('btn-save-wl').addEventListener('click', () => {
STATE.whitelist = new Set(document.getElementById('inp-whitelist').value.split('\n').map(x=>x.trim().replace('@','')).filter(x=>x));
const status = document.getElementById('wl-status');
status.style.display = 'block';
setTimeout(() => status.style.display = 'none', 3000);
});
document.getElementById('btn-select-all').addEventListener('click', selectAllNonMutuals);
document.getElementById('btn-deselect').addEventListener('click', () => {
document.querySelectorAll('.mu-user-chk').forEach(c => { if(!c.disabled) c.checked = false; });
updateCount();
});
document.getElementById('btn-go-action').addEventListener('click', () => switchTab('RUN'));
document.getElementById('btn-run').addEventListener('click', startRun);
document.getElementById('btn-stop').addEventListener('click', () => stopRun(true));
document.getElementById('btn-back-review').addEventListener('click', () => switchTab('REVIEW'));
document.getElementById('btn-export').addEventListener('click', generateReports);
document.getElementById('mu-list-root').addEventListener('change', updateCount);
// Modals
document.getElementById('btn-tutorial').addEventListener('click', () => {
showModal('📖 Cara Pakai', `
<p><b>1. Buka Tab Following</b><br>Pastikan Anda membuka halaman profil Twitter/X Anda, lalu klik bagian "Following" (Mengikuti).</p>
<p><b>2. Atur Scan Setup</b><br>Masukkan akun-akun penting ke Whitelist agar tidak ikut terhapus. Atur juga "Skip Recent Follows" jika Anda baru saja mem-follow seseorang dan tidak ingin unfollow mereka.</p>
<p><b>3. Lakukan Scan</b><br>Klik "Start Scanning". Script akan otomatis mendata siapa saja yang mutual (follow back) dan siapa yang tidak (Not Back).</p>
<p><b>4. User History</b><br>Cek hasil scan di tab User History. Gunakan tombol "Select Non-Mutuals" untuk otomatis memilih orang yang tidak follow back.</p>
<p><b>5. Eksekusi</b><br>Masuk ke tab Action, pilih batas harian dan delay keamanan. Klik "Execute Unfollows" dan biarkan tab tetap terbuka hingga selesai.</p>
`);
});
document.getElementById('btn-disclaimer').addEventListener('click', () => {
showModal('🛡️ Disclaimer Keamanan', `
<p>Alat ini ("Mass Unfollow") dirancang secara cermat untuk beroperasi se-aman mungkin dengan meniru pola jeda manusia sesungguhnya (Human-like delays).</p>
<p>Namun, pihak <b>Twitter/X</b> memiliki sistem deteksi platform otomatis yang ketat. Segala risiko yang terjadi akibat penggunaan script ini (seperti limit aksi, shadowban, atau temp-ban) sepenuhnya adalah <b>tanggung jawab pengguna</b>.</p>
<p><b>Rekomendasi Terbaik:</b> Selalu gunakan settingan 🟢 <b>SAFE</b> atau 🟢 <b>ULTRA SAFE</b>. Hindari pemakaian secara agresif dan perhatikan "Daily Cap" agar algoritma Twitter tidak mendeteksi anomali pada akun Anda.</p>
`);
});
document.getElementById('mu-modal-btn').addEventListener('click', hideModal);
// Bind pill tabs
document.querySelectorAll('.mu-pill').forEach(pill => {
pill.addEventListener('click', (e) => switchTab(e.target.dataset.target));
});
makeDraggable(root, document.getElementById('mu-header'));
}
// --- Modal Logic ---
function showModal(title, content) {
document.getElementById('mu-modal-title').innerHTML = title;
document.getElementById('mu-modal-content').innerHTML = content;
document.getElementById('mu-modal').style.display = 'flex';
}
function hideModal() {
document.getElementById('mu-modal').style.display = 'none';
}
// --- Data Logic & Tab Switching ---
function switchTab(id) {
document.querySelectorAll('.mu-panel').forEach(e => e.classList.remove('active'));
document.querySelectorAll('.mu-pill').forEach(e => e.classList.remove('active'));
document.getElementById('panel-'+id.toLowerCase()).classList.add('active');
document.getElementById('tab-'+id.toLowerCase()).classList.add('active');
}
// --- Core Logic: Scan ---
async function startScan() {
if(STATE.stage === 'SCANNING') return;
// Fully reset state so users can scan multiple times even after error parsing empty string
STATE.stage = 'SCANNING';
STATE.users = [];
STATE.selection.clear();
document.getElementById('lbl-found').innerText = '0';
document.getElementById('lbl-selected').innerText = '0';
document.getElementById('mu-list-root').innerHTML = '';
document.getElementById('lbl-progress').innerHTML = '0 <span style="font-size:20px; color:#ccc;">/ 0</span>';
document.getElementById('bar-progress').style.width = '0%';
document.getElementById('mu-logs').innerHTML = '';
setStatus('Ready to start');
STATE.whitelist = new Set(document.getElementById('inp-whitelist').value.split('\n').map(x=>x.trim().replace('@','')).filter(x=>x));
// Fix: Bug when erasing input, parsing empty string causes NaN
let skipVal = parseInt(document.getElementById('inp-skip').value);
STATE.config.skipTop = isNaN(skipVal) ? 0 : skipVal;
document.getElementById('btn-scan').parentElement.style.display = 'none';
document.getElementById('scan-running').style.display = 'block';
const countEl = document.getElementById('scan-count');
let consecutiveZero = 0;
let lastScrollH = 0;
try {
while(STATE.stage === 'SCANNING') {
const cells = document.querySelectorAll('[data-testid="UserCell"]');
for(const cell of cells) {
const link = cell.querySelector('a[href^="/"]');
if(!link) continue;
const handle = link.getAttribute('href').substring(1);
if(STATE.users.some(u=>u.handle === handle)) continue;
const followBtn = cell.querySelector('[aria-label^="Follow"]');
let isActuallyFollowing = false;
if (followBtn) {
const label = followBtn.getAttribute('aria-label') || "";
if (label.startsWith("Following")) isActuallyFollowing = true;
}
if (!isActuallyFollowing) { continue; }
const nameEl = cell.querySelector('div[dir="ltr"] > span > span');
const name = nameEl ? nameEl.innerText : handle;
const followsYou = !!cell.querySelector('[data-testid="userFollowIndicator"]');
const textCheck = cell.innerText.includes("Follows you");
const isMutal = followsYou || textCheck;
const isWhitelisted = STATE.whitelist.has(handle);
const isRecent = STATE.users.length < STATE.config.skipTop;
STATE.users.push({
handle, name, isMutal, isWhitelisted, isRecent, cell,
status: 'PENDING'
});
}
countEl.innerText = STATE.users.length;
window.scrollBy(0, 600);
await delay(800);
const h = document.body.scrollHeight;
if(h === lastScrollH) {
consecutiveZero++;
if(consecutiveZero > 4) break;
} else {
consecutiveZero = 0;
lastScrollH = h;
}
}
} catch(e) { console.error(e); }
if (STATE.stage === 'STOPPED') return;
finishScan();
}
function stopScan() {
if (STATE.stage !== 'SCANNING') return;
STATE.stage = 'STOPPED';
finishScan();
}
function finishScan() {
// Critical fix: Reset stage if it finished naturally
STATE.stage = 'INIT';
document.getElementById('btn-scan').parentElement.style.display = 'block';
document.getElementById('scan-running').style.display = 'none';
if (STATE.users.length > 0) {
renderList();
switchTab('REVIEW');
log(`Scan Complete. Found ${STATE.users.length} users.`, true);
} else {
document.getElementById('mu-list-root').innerHTML = '<div style="text-align:center; padding: 40px 20px; color:#888; font-size: 13px; font-weight:500;">No users found. Try scrolling manually or refresh.</div>';
log("Scan stopped. No users found.", false);
}
}
// --- Core Logic: Review ---
function renderList() {
const root = document.getElementById('mu-list-root');
root.innerHTML = '';
STATE.users.forEach(u => {
const row = document.createElement('div');
row.className = 'mu-user-item';
let statusHtml = '';
let disabled = false;
if (u.isWhitelisted) {
statusHtml = `<div class="mu-user-status status-skip">WHITELISTED</div>`;
disabled = true;
} else if (u.isRecent) {
statusHtml = `<div class="mu-user-status status-skip">RECENT FOLLOW</div>`;
disabled = true;
} else if (u.isMutal) {
statusHtml = `<div class="mu-user-status status-mutual">MUTUAL</div>`;
} else {
statusHtml = `<div class="mu-user-status status-none">NOT BACK</div>`;
}
row.innerHTML = `
<input type="checkbox" class="mu-user-chk"
data-handle="${u.handle}"
data-mutual="${u.isMutal}"
${disabled ? 'disabled' : ''}>
<div class="mu-user-info">
<a href="https://x.com/${u.handle}" target="_blank" class="mu-user-handle">@${u.handle}</a>
<div class="mu-user-name">${u.name}</div>
${statusHtml}
</div>
<div style="color:#ff7300; font-weight:800; font-size:18px; padding-left:10px;">›</div>
`;
root.appendChild(row);
});
document.getElementById('lbl-found').innerText = STATE.users.length;
updateCount();
}
function selectAllNonMutuals() {
const checks = document.querySelectorAll('.mu-user-chk');
checks.forEach(c => {
if (!c.disabled) {
c.checked = (c.dataset.mutual === 'false');
}
});
updateCount();
}
function updateCount() {
const checked = document.querySelectorAll('.mu-user-chk:checked').length;
document.getElementById('lbl-selected').innerText = checked;
}
// --- Core Logic: Run ---
async function startRun() {
STATE.selection.clear();
document.querySelectorAll('.mu-user-chk:checked').forEach(c => STATE.selection.add(c.dataset.handle));
if(STATE.selection.size === 0) return alert("Select users first from the User History tab!");
const cap = parseInt(document.getElementById('inp-cap').value);
const targets = STATE.users.filter(u => STATE.selection.has(u.handle));
const limit = Math.min(targets.length, cap);
if(!confirm(`Proceed to unfollow ${limit} users?\nPlease leave this tab open and do not interact.`)) return;
STATE.stage = 'RUNNING';
document.getElementById('btn-run').style.display = 'none';
document.getElementById('btn-stop').style.display = 'block';
window.scrollTo(0, 0);
setStatus('Warming up... Scanning from top');
await delay(1500);
let count = 0;
const profile = PROFILES[document.getElementById('inp-safety').value] || PROFILES.SAFE;
for(let i=0; i<limit; i++) {
if(STATE.stage !== 'RUNNING') break;
const u = targets[i];
setStatus(`Processing ${i+1}/${limit}: @${u.handle}`);
let btn = null;
let retryScan = 0;
while(!btn && retryScan < 30 && STATE.stage === 'RUNNING') {
const links = document.querySelectorAll(`a[href="/${u.handle}"]`);
if(links.length) {
const freshCell = links[0].closest('[data-testid="UserCell"]');
if(freshCell) {
btn = freshCell.querySelector('[aria-label^="Following"]');
if (btn) {
freshCell.scrollIntoView({behavior:'smooth', block:'center'});
await delay(400);
} else {
const followBtn = freshCell.querySelector('[aria-label^="Follow"]');
if (followBtn) {
log(`Skip: @${u.handle} already unfollowed.`, false);
break;
}
}
}
}
if(!btn) {
window.scrollBy(0, 350);
await delay(500);
retryScan++;
}
}
if(btn) {
await delay(random(500,1000));
btn.click();
await delay(1000);
const confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
if(confirmBtn) {
confirmBtn.click();
log(`Unfollowed @${u.handle}`, true);
u.status = 'UNFOLLOWED';
count++;
} else {
log(`Err: No Confirm window for @${u.handle}`, false);
}
} else {
log(`Skip: Can't find button for @${u.handle}`, false);
}
updateProgress(count, limit);
if(i < limit-1) {
if((i+1) % profile.idleFreq === 0) {
const pause = random(profile.idleMin, profile.idleMax);
setStatus(`Human Pause... (${Math.round(pause/1000)}s)`);
await delay(pause);
} else {
const wait = random(profile.min, profile.max);
setStatus(`Waiting intervals... (${Math.round(wait/1000)}s)`);
await delay(wait);
}
}
}
stopRun(false); // natural finish
setStatus("Execution Done.");
setTimeout(() => {
showModal('🎉 Selesai!', `
<p>Proses unfollow telah selesai berjalan!</p>
<div style="background:#fffaf5; border:1px solid #ffbc85; border-radius:10px; padding:15px; text-align:center; margin-top:20px;">
<p style="margin-bottom:8px; font-weight:600; color:#111;">Tool ini membantu dan berguna bagimu?</p>
<p style="font-size:11px;">Jangan lupa support author-nya dengan Follow link di bawah ya!</p>
<a href="https://x.com/miegrains" target="_blank" style="display:inline-block; margin-top:10px; padding:8px 16px; background:#000; color:#fff; border-radius:20px; text-decoration:none;">Follow Keith. (X)</a>
</div>
`);
}, 1000);
}
function stopRun(isManual = false) {
STATE.stage = 'DONE';
document.getElementById('btn-run').style.display = 'flex';
document.getElementById('btn-stop').style.display = 'none';
if (isManual) {
setStatus("Execution Stopped.");
}
}
function generateReports() {
const h = "Mass Unfollow Log\n------------------\n";
const txt = STATE.users.map(u => `@${u.handle} | ${u.status}`).join('\n');
save(h+txt, 'unfollow_log.txt');
}
// --- Utils ---
function setStatus(t) { document.getElementById('mu-status').innerText = t; }
function log(t, ok) {
const d = document.createElement('div');
d.className = 'log-item ' + (ok ? 'log-success' : 'log-err');
d.innerText = `> ${t}`;
document.getElementById('mu-logs').prepend(d);
}
function updateProgress(n, total) {
document.getElementById('lbl-progress').innerHTML = `${n} <span style="font-size:20px; color:#ccc;">/ ${total}</span>`;
const pct = (n/total)*100;
document.getElementById('bar-progress').style.width = pct + '%';
}
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
function random(a,b) { return Math.floor(Math.random()*(b-a+1))+a; }
function save(content, name) {
const blob = new Blob([content], {type:'text/plain'});
const a = document.createElement('a'); a.href=URL.createObjectURL(blob); a.download=name;
document.body.appendChild(a); a.click(); document.body.removeChild(a);
}
function makeDraggable(el, h) {
let p = {x:0, y:0, lx:0, ly:0};
h.onmousedown = e => { e.preventDefault(); p.x = e.clientX; p.y = e.clientY; document.onmouseup = close; document.onmousemove = drag; };
function drag(e) { e.preventDefault(); p.lx = p.x - e.clientX; p.ly = p.y - e.clientY; p.x = e.clientX; p.y = e.clientY; el.style.top = Math.max(0, el.offsetTop - p.ly) + "px"; el.style.left = Math.max(0, el.offsetLeft - p.lx) + "px"; }
function close() { document.onmouseup=null; document.onmousemove=null; }
}
createUI();
console.log("Mass Unfollow (V4.6 BETA) Loaded");
})();
Saat membuka halaman Following Twitter, buka "Developer Tools" dengan kombinasi tombol berikut:
Peringatan Keamanan Browser: Jika ini pertama kalinya, browser akan memblokir Anda dari melakukan paste kode karena alasan keamanan. Ketik "allow pasting" (lalu tekan Enter) di console tersebut, baru Anda bisa menempelkan kode script.
Paste (Tempel) kode yang sudah Anda copy tadi ke dalam baris paling bawah di Console, lalu tekan Enter. Panel antarmuka "Mass Unfollow" akan muncul secara otomatis di layar Anda!
Gunakan settingan "SAFE" pada pop-up script untuk jarak waktu unfollow. Jangan melakukan unfollow secara agresif untuk menghindari flagging oleh sistem Twitter.