🚀 GitHub Follower & Unfollower Tracker | Find Non-Followers & Bulk Unfollow Console Script (Beni Geri Takip Etmeyenleri Bulma ve Toplu Takipten Çıkma Konsol Kodu)

🚀 GitHub Follower & Unfollower Tracker (Console Script)

Easily track who isn't following you back on GitHub and bulk unfollow with an interactive dashboard right in your browser console!

GitHub'da sizi geri takip etmeyenleri veya sizin takip etmediğiniz takipçilerinizi tarayıcı konsolundan tek tıkla listeleyin ve yönetin.

✨ Features / Özellikler

  • Fullscreen Dashboard: Dark-mode UI matching GitHub's design. (GitHub temalı tam ekran kontrol paneli)
  • Non-Followers List: See who doesn't follow you back. (Geri takip etmeyenleri listeleme)
  • Fans / Not Followed Back: Discover followers you don't follow back. (Takip etmediğiniz takipçileri listeleme)
  • Bulk Unfollow: Safely unfollow non-followers in sequence. (Geri takip etmeyenleri toplu takipten çıkma)
  • One-Click Actions: Follow/unfollow directly from the list. (Tek tıkla takip et / takipten çık)

🛠️ How to Use / Nasıl Kullanılır?

  1. Go to github.com and log in. (github.com'a gidin ve giriş yapın)
  2. Press F12 (or Cmd+Option+I on Mac) to open DevTools and switch to the Console tab. (F12 ile Console sekmesini açın)
  3. Copy and paste the script from tracker.js and hit Enter. (Kodu yapıştırıp Enter'a basın)
  4. To unfollow/follow users, generate a GitHub Personal Access Token (Classic) with the user:follow scope: 👉 Generate Token
(async () => {
const existingApp = document.getElementById('gh-tracker-fullscreen-app');
if (existingApp) existingApp.remove();
let username = document.querySelector('meta[name="user-login"]')?.content;
if (!username) {
username = prompt("GitHub kullanıcı adınızı girin:");
}
if (!username) return alert("Kullanıcı adı bulunamadı.");
let ghToken = localStorage.getItem('gh_unfollow_pat') || '';
// Tam Ekran Container
const overlay = document.createElement('div');
overlay.id = 'gh-tracker-fullscreen-app';
overlay.style.cssText = `
position: fixed; inset: 0; background: #0d1117; color: #c9d1d9; z-index: 9999999;
display: flex; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
`;
overlay.innerHTML = `

GitHub Takip Yönetim Paneli

@${username}
</div>
PAT Token:
Token Al
</div>
</div>
</div>
Geri Takip Etmeyenler (0)
</button>
Benim Takip Etmediklerim (0)
</button>
</div>
Kullanıcı verileri taranıyor, lütfen bekleyin...
</div>
</div>
`;
document.body.appendChild(overlay);
const patInput = document.getElementById('gh-pat-input');
patInput.addEventListener('input', (e) => {
ghToken = e.target.value.trim();
localStorage.setItem('gh_unfollow_pat', ghToken);
});
document.getElementById('gh-close-btn').onclick = () => overlay.remove();
// API Takip/Takipten Çıkma Fonksiyonları (REST API v3)
const ensureToken = () => {
if (!ghToken) {
const promptToken = prompt("Takipten çıkma/takip etme API isteği için 'user:follow' yetkili GitHub Personal Access Token (classic) girin:");
if (promptToken) {
ghToken = promptToken.trim();
patInput.value = ghToken;
localStorage.setItem('gh_unfollow_pat', ghToken);
return true;
}
return false;
}
return true;
};
const unfollowUser = async (targetUser) => {
if (!ensureToken()) throw new Error("Token girilmedi.");
const res = await fetch(`https://api.github.com/user/following/${targetUser}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${ghToken}`,
'Accept': 'application/vnd.github.v3+json'
}
});
if (res.status === 204 || res.ok) return true;
if (res.status === 401) throw new Error("Token geçersiz veya yetkisiz (user:follow seçilmeli).");
throw new Error(`İşlem başarısız: ${res.status}`);
};
const followUser = async (targetUser) => {
if (!ensureToken()) throw new Error("Token girilmedi.");
const res = await fetch(`https://api.github.com/user/following/${targetUser}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${ghToken}`,
'Accept': 'application/vnd.github.v3+json',
'Content-Length': '0'
}
});
if (res.status === 204 || res.ok) return true;
if (res.status === 401) throw new Error("Token geçersiz veya yetkisiz (user:follow seçilmeli).");
throw new Error(`İşlem başarısız: ${res.status}`);
};
// Kullanıcı Listelerini Çekme
const fetchAllUsers = async (endpoint) => {
let results = [];
let page = 1;
const perPage = 100;
while (true) {
const headers = { 'Accept': 'application/vnd.github.v3+json' };
if (ghToken) headers['Authorization'] = `Bearer ${ghToken}`;
const res = await fetch(`https://api.github.com/users/${username}/${endpoint}?per_page=${perPage}&page=${page}`, { headers });
if (!res.ok) throw new Error("Kullanıcı listesi alınamadı.");
const data = await res.json();
if (!data || data.length === 0) break;
results = results.concat(data.map(u => ({ username: u.login, avatar: u.avatar_url, profileUrl: u.html_url })));
if (data.length < perPage) break;
page++;
}
return results;
};
try {
const [following, followers] = await Promise.all([
fetchAllUsers('following'),
fetchAllUsers('followers')
]);
const followingMap = new Map(following.map(u => [u.username.toLowerCase(), u]));
const followerMap = new Map(followers.map(u => [u.username.toLowerCase(), u]));
let notFollowingBack = following.filter(u => !followerMap.has(u.username.toLowerCase()));
let notFollowedBack = followers.filter(u => !followingMap.has(u.username.toLowerCase()));
document.getElementById('count-not-me').textContent = notFollowingBack.length;
document.getElementById('count-not-them').textContent = notFollowedBack.length;
document.getElementById('gh-loading').style.display = 'none';
const grid = document.getElementById('gh-grid');
grid.style.display = 'grid';
let currentTab = 'not-following-me';
const render = () => {
grid.innerHTML = '';
const bulkActions = document.getElementById('gh-bulk-actions');
bulkActions.innerHTML = '';
if (currentTab === 'not-following-me') {
if (notFollowingBack.length > 0) {
const bulkBtn = document.createElement('button');
bulkBtn.textContent = `Hepsini Takipten Çıkar (${notFollowingBack.length})`;
bulkBtn.style.cssText = `background: #da3633; color: #ffffff; border: 1px solid rgba(240,246,252,0.1); padding: 6px 14px; border-radius: 6px; font-weight: 600; font-size: 14px; cursor: pointer;`;
bulkBtn.onclick = async () => {
if (!ensureToken()) return;
if (!confirm(`Geri takip etmeyen ${notFollowingBack.length} kişiyi takipten çıkarmak istediğine emin misin?`)) return;
bulkBtn.disabled = true;
bulkBtn.style.opacity = '0.6';
for (let i = notFollowingBack.length - 1; i >= 0; i--) {
const target = notFollowingBack[i];
bulkBtn.textContent = `Çıkarılıyor: ${target.username} (${i + 1} kaldı)...`;
try {
await unfollowUser(target.username);
notFollowingBack.splice(i, 1);
document.getElementById('count-not-me').textContent = notFollowingBack.length;
render();
} catch (e) {
alert(`Hata: ${e.message}`);
break;
}
// Rate limit yememek için 400ms bekleme
await new Promise(r => setTimeout(r, 400));
}
render();
};
bulkActions.appendChild(bulkBtn);
}
if (notFollowingBack.length === 0) {
grid.innerHTML = 'Geri takip etmeyen kimse kalmadı! 🎉';
return;
}
notFollowingBack.forEach(user => {
const card = document.createElement('div');
card.style.cssText = `background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 16px; display: flex; align-items: center; justify-content: space-between;`;
card.innerHTML = `
${user.username}
</div>
</div>
`;
const btn = card.querySelector('.unfollow-btn');
btn.onclick = async () => {
if (!ensureToken()) return;
btn.disabled = true;
btn.textContent = '...';
try {
await unfollowUser(user.username);
notFollowingBack = notFollowingBack.filter(u => u.username !== user.username);
document.getElementById('count-not-me').textContent = notFollowingBack.length;
render();
} catch (e) {
alert(`Hata: ${e.message}`);
btn.disabled = false;
btn.textContent = 'Takipten Çık';
}
};
grid.appendChild(card);
});
} else {
if (notFollowedBack.length === 0) {
grid.innerHTML = 'Takip etmediğin takipçin yok.';
return;
}
notFollowedBack.forEach(user => {
const card = document.createElement('div');
card.style.cssText = `background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 16px; display: flex; align-items: center; justify-content: space-between;`;
card.innerHTML = `
${user.username}
</div>
</div>
`;
const btn = card.querySelector('.follow-btn');
btn.onclick = async () => {
if (!ensureToken()) return;
btn.disabled = true;
btn.textContent = '...';
try {
await followUser(user.username);
notFollowedBack = notFollowedBack.filter(u => u.username !== user.username);
document.getElementById('count-not-them').textContent = notFollowedBack.length;
render();
} catch (e) {
alert(`Hata: ${e.message}`);
btn.disabled = false;
btn.textContent = 'Takip Et';
}
};
grid.appendChild(card);
});
}
};
const tab1 = document.getElementById('tab-not-following-me');
const tab2 = document.getElementById('tab-i-dont-follow');
tab1.onclick = () => {
currentTab = 'not-following-me';
tab1.style.background = '#21262d';
tab1.style.color = '#f0f6fc';
tab1.style.borderColor = '#30363d';
tab2.style.background = 'transparent';
tab2.style.color = '#8b949e';
tab2.style.borderColor = 'transparent';
render();
};
tab2.onclick = () => {
currentTab = 'i-dont-follow';
tab2.style.background = '#21262d';
tab2.style.color = '#f0f6fc';
tab2.style.borderColor = '#30363d';
tab1.style.background = 'transparent';
tab1.style.color = '#8b949e';
tab1.style.borderColor = 'transparent';
render();
};
render();
} catch (err) {
document.getElementById('gh-loading').innerHTML = `Hata oluştu: ${err.message}`;
}
})();
添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论