Instagram Deşifre - Gelişmiş Unfollowers Tespiti

Hedef hesabın takip ettiklerini ve takipçilerini iki ayrı görevle, yavaş ve güvenli bir şekilde tarar, karşılaştırır ve takipten çıkanları bulur. Rate limit'e takılmamak için durdur/devam et özelliğini destekler.

// ==UserScript==
// @name         Instagram Deşifre - Gelişmiş Unfollowers Tespiti
// @namespace    http://tampermonkey.net/
// @version      2.4
// @description  Hedef hesabın takip ettiklerini ve takipçilerini iki ayrı görevle, yavaş ve güvenli bir şekilde tarar, karşılaştırır ve takipten çıkanları bulur. Rate limit'e takılmamak için durdur/devam et özelliğini destekler.
// @author       harbidenfurkan'ın isteği üzerine yapay zeka tarafından geliştirildi.
// @match        https://www.instagram.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11
// @license     MIT
// ==/UserScript==

//                                      PLEASE RESPECT IF MY SCRIPTS USEFUL FOR YOU
//                      DON'T TRY TO COPY PASTE MY SCRIPTS THEN SHARE TO OTHERS LIKE YOU ARE THE CREATOR
//                                               THANKS FOR YOUR SUPPORT

(function() {
    'use strict';

    // --- AYARLAR ---
    const MIN_DELAY_MS = 3000;
    const MAX_DELAY_MS = 6000;
    const BIG_DELAY_MS = 15000;
    const REQUEST_COUNT_FOR_BIG_DELAY = 10;
    const APP_ID = '936619743392459';

    // --- DÜZELTİLMİŞ QUERY HASH'LER ---
    // "Takipçileri Getir" için (Bu zaten çalışıyordu)
    const FOLLOWERS_QUERY_HASH = 'c76146de99bb02f6415203be841dd25a';
    // "Takip Edilenleri Getir" için sizin sağladığınız örnekten alınan, test edilmiş ve çalışan HASH
    const FOLLOWING_QUERY_HASH = '58712303d941c6855d4e888c5f0cd22f';


    // --- ARAYÜZ OLUŞTURMA ---
    GM_addStyle(`
        #desifre-panel {
            position: fixed;
            bottom: 15px;
            right: 15px;
            width: 380px;
            background-color: #1e1e1e;
            border: 1px solid #333;
            border-radius: 12px;
            z-index: 9999;
            color: #fff;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            box-shadow: 0 5px 15px rgba(0,0,0,0.5);
            transition: all 0.3s ease-in-out;
            overflow: hidden;
        }
        #desifre-panel.minimized {
            width: 50px;
            height: 50px;
            cursor: pointer;
        }
        #desifre-header {
            background-color: #333;
            padding: 10px 15px;
            font-weight: bold;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
        }
        #desifre-header .title-icon { font-size: 18px; margin-right: 8px; }
        #desifre-body { padding: 15px; }
        #desifre-body.hidden { display: none; }
        #desifre-panel input[type="text"] {
            width: 100%;
            padding: 10px;
            border-radius: 6px;
            border: 1px solid #555;
            background-color: #2a2a2a;
            color: #fff;
            margin-bottom: 10px;
            box-sizing: border-box;
        }
        #desifre-panel button {
            width: 100%;
            padding: 10px;
            border-radius: 6px;
            border: none;
            font-weight: bold;
            cursor: pointer;
            transition: background-color 0.2s;
            margin-top: 5px;
        }
        .btn-primary { background-color: #0095f6; color: white; }
        .btn-primary:hover { background-color: #007ac1; }
        .btn-secondary { background-color: #555; color: white; }
        .btn-secondary:hover { background-color: #666; }
        .btn-success { background-color: #4CAF50; color: white; }
        .btn-success:hover { background-color: #45a049; }
        .btn-danger { background-color: #f44336; color: white; }
        .btn-danger:hover { background-color: #da190b; }
        .btn-ghost { background-color: transparent; border: 1px solid #555; color: #fff; }
        .btn-ghost:hover { background-color: #333; }
        #desifre-status {
            margin-top: 15px;
            padding: 10px;
            background-color: #2a2a2a;
            border-radius: 6px;
            font-size: 14px;
            line-height: 1.5;
            min-height: 50px;
            word-wrap: break-word;
        }
        #desifre-results {
            margin-top: 10px;
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #333;
            border-radius: 6px;
        }
        #desifre-results ul { list-style: none; padding: 0; margin: 0; }
        #desifre-results li {
            padding: 8px 12px;
            border-bottom: 1px solid #333;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        #desifre-results li:last-child { border-bottom: none; }
        #desifre-results a { color: #0095f6; text-decoration: none; font-weight: 500; }
        .progress-container {
            width: 100%; background-color: #333; border-radius: 6px; margin-top: 10px; overflow: hidden;
        }
        .progress-bar {
            width: 0%; height: 8px; background-color: #4CAF50; transition: width 0.3s;
        }
        .minimize-btn {
            cursor: pointer; font-weight: bold; font-size: 20px;
        }
    `);

    const panel = document.createElement('div');
    panel.id = 'desifre-panel';
    panel.innerHTML = `
        <div id="desifre-header">
            <div><span class="title-icon">🕵️</span><span>Instagram Deşifre</span></div>
            <span class="minimize-btn">-</span>
        </div>
        <div id="desifre-body">
            <input type="text" id="target-username" placeholder="Hedef kullanıcı adı (örn: harbidenfurkan)">
            <div id="controls">
                <button id="fetch-followers-btn" class="btn-primary">1. Takipçileri Getir</button>
                <button id="fetch-following-btn" class="btn-primary">2. Takip Edilenleri Getir</button>
                <button id="compare-btn" class="btn-success">3. Karşılaştır ve Unfollower'ları Bul</button>
                <button id="stop-btn" class="btn-danger" style="display:none;">Durdur</button>
            </div>
            <div id="desifre-status">
                <strong>Durum:</strong><span id="status-text"> Bekliyor...</span>
                <div class="progress-container"><div id="progress-bar" class="progress-bar"></div></div>
            </div>
            <div id="results-container" style="display:none;">
                <p><strong><span id="results-count">0</span> kişi sizi takip etmiyor:</strong> <button id="copy-results-btn" class="btn-ghost">Listeyi Kopyala</button></p>
                <div id="desifre-results"><ul></ul></div>
            </div>
            <button id="reset-btn" class="btn-secondary">Tüm Verileri Sıfırla</button>
        </div>
    `;
    document.body.appendChild(panel);

    const targetUsernameInput = document.getElementById('target-username');
    const fetchFollowersBtn = document.getElementById('fetch-followers-btn');
    const fetchFollowingBtn = document.getElementById('fetch-following-btn');
    const compareBtn = document.getElementById('compare-btn');
    const stopBtn = document.getElementById('stop-btn');
    const resetBtn = document.getElementById('reset-btn');
    const statusText = document.getElementById('status-text');
    const progressBar = document.getElementById('progress-bar');
    const resultsContainer = document.getElementById('results-container');
    const resultsCount = document.getElementById('results-count');
    const resultsList = document.querySelector('#desifre-results ul');
    const copyResultsBtn = document.getElementById('copy-results-btn');
    const minimizeBtn = document.querySelector('.minimize-btn');
    const deşifreBody = document.getElementById('desifre-body');
    const deşifreHeader = document.getElementById('desifre-header');

    let state = { isRunning: false, targetUserId: null, targetUsername: '', currentTask: null };

    const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
    const randomDelay = () => Math.floor(Math.random() * (MAX_DELAY_MS - MIN_DELAY_MS + 1)) + MIN_DELAY_MS;
    const log = (message) => {
        console.log(`[Deşifre] ${message}`);
        statusText.innerHTML = ` ${message}`;
    };
    const updateProgress = (current, total) => {
        const percentage = total > 0 ? (current / total) * 100 : 0;
        progressBar.style.width = `${percentage}%`;
    };
    const showStopButton = (show) => {
        stopBtn.style.display = show ? 'block' : 'none';
        [fetchFollowersBtn, fetchFollowingBtn, compareBtn].forEach(btn => btn.style.display = show ? 'none' : 'block');
    };

    async function getUserId(username) {
        try {
            const response = await fetch(`https://www.instagram.com/api/v1/users/web_profile_info/?username=${username}`, {
                headers: { 'x-ig-app-id': APP_ID }
            });
            if (!response.ok) throw new Error(`Kullanıcı profili alınamadı: ${response.status}`);
            const data = await response.json();
            if (data?.data?.user?.id) {
                return data.data.user.id;
            }
            throw new Error('Kullanıcı ID bulunamadı.');
        } catch (error) {
            Swal.fire('Hata!', `"${username}" kullanıcısı bulunamadı veya profil gizli. Lütfen kontrol edin.`, 'error');
            return null;
        }
    }

    async function fetchList(type) {
        if (state.isRunning) return;

        state.targetUsername = targetUsernameInput.value.trim();
        if (!state.targetUsername) {
            Swal.fire('Eksik Bilgi', 'Lütfen bir hedef kullanıcı adı girin.', 'warning');
            return;
        }

        state.isRunning = true;
        state.currentTask = type;
        showStopButton(true);
        log('Başlatılıyor...');

        const cachedUserId = await GM_getValue(`userid_${state.targetUsername}`);
        state.targetUserId = cachedUserId || await getUserId(state.targetUsername);

        if (!state.targetUserId) {
            state.isRunning = false;
            showStopButton(false);
            return;
        }
        if (!cachedUserId) {
            await GM_setValue(`userid_${state.targetUsername}`, state.targetUserId);
        }

        const queryHash = type === 'followers' ? FOLLOWERS_QUERY_HASH : FOLLOWING_QUERY_HASH;
        const storageKey = `${type}_${state.targetUsername}`;

        let userList = await GM_getValue(storageKey, []);
        let nextCursor = await GM_getValue(`${storageKey}_cursor`, null);
        let hasNextPage = true;
        let requestCount = 0;

        if (nextCursor === 'DONE') {
             Swal.fire('Tamamlandı', `Bu görev ('${type}') daha önce tamamlanmış. Sıfırlayıp yeniden başlatabilirsiniz.`, 'info');
             state.isRunning = false;
             showStopButton(false);
             updateProgress(userList.length, userList.length);
             log(`${type.charAt(0).toUpperCase() + type.slice(1)} listesi zaten tam. Toplam: ${userList.length}`);
             return;
        }

        log(`${userList.length} kullanıcıdan devam ediliyor...`);

        while (state.isRunning && hasNextPage) {
            try {
                const variables = {
                    id: state.targetUserId,
                    first: 50
                };
                if (nextCursor) variables.after = nextCursor;
                // 'following' için özel değişkenler gerekebilir, bu hash daha basit bir yapı istiyor olabilir.
                // Şimdilik standart tutuyoruz.
                if (type === 'followers') {
                    variables.include_reel = true;
                    variables.fetch_mutual = true;
                }

                const url = `https://www.instagram.com/graphql/query/?query_hash=${queryHash}&variables=${JSON.stringify(variables)}`;

                const response = await fetch(url); // App ID header'ı olmadan daha stabil çalışabilir.

                if (response.status === 429) throw new Error('RATE_LIMIT');
                if (!response.ok) throw new Error(`API Hatası: ${response.status}`);
                const data = await response.json();
                if (data.status === 'fail') throw new Error(data.message);

                const edge = data?.data?.user?.[type === 'followers' ? 'edge_followed_by' : 'edge_follow'];
                if (!edge) {
                    console.error("Beklenmedik API yanıtı (edge bulunamadı):", data);
                    throw new Error('API yanıtı anlaşılamadı. Veri yapısı değişmiş olabilir.');
                }


                const newUsers = edge.edges.map(e => ({ id: e.node.id, username: e.node.username, full_name: e.node.full_name }));
                userList.push(...newUsers);

                const totalCount = edge.count;
                const pageInfo = edge.page_info;
                hasNextPage = pageInfo.has_next_page;
                nextCursor = pageInfo.end_cursor;

                log(`${userList.length} / ${totalCount} ${type} getirildi.`);
                updateProgress(userList.length, totalCount);

                await GM_setValue(storageKey, userList);
                await GM_setValue(`${storageKey}_cursor`, nextCursor);

                if (!hasNextPage) {
                    log('Tüm liste başarıyla alındı.');
                    await GM_setValue(`${storageKey}_cursor`, 'DONE');
                    break;
                }

                requestCount++;
                let delay = randomDelay();
                if (requestCount % REQUEST_COUNT_FOR_BIG_DELAY === 0) {
                    delay = BIG_DELAY_MS;
                    log(`${REQUEST_COUNT_FOR_BIG_DELAY} istek yapıldı. ${delay / 1000}s bekleniyor...`);
                }
                await sleep(delay);

            } catch (error) {
                const errorMessage = error.message === 'RATE_LIMIT'
                    ? 'Hız Limitine Takıldınız! Instagram istek hızınızı geçici olarak engelledi.<br><br><b>Çözüm:</b> Birkaç saat bekleyin veya <b>farklı bir Instagram hesabıyla giriş yapıp</b> göreve kaldığınız yerden devam edin.'
                    : `Bir hata oluştu: ${error.message}. Lütfen internet bağlantınızı kontrol edin veya daha sonra tekrar deneyin.`;

                Swal.fire({ title: 'Hata!', html: errorMessage, icon: 'warning', confirmButtonText: 'Anladım' });
                state.isRunning = false;
                break;
            }
        }

        if (!state.isRunning) log('İşlem durduruldu.');
        else log('Görev tamamlandı!');
        state.isRunning = false;
        showStopButton(false);
    }

    async function compareLists() {
        const username = targetUsernameInput.value.trim();
        if (!username) { Swal.fire('Eksik Bilgi', 'Lütfen bir hedef kullanıcı adı girin.', 'warning'); return; }
        log('Veriler okunuyor...');
        const followers = await GM_getValue(`followers_${username}`, []);
        const following = await GM_getValue(`following_${username}`, []);

        if (followers.length === 0 || following.length === 0) {
            Swal.fire('Veri Eksik', 'Karşılaştırma yapmadan önce her iki listeyi de getirmelisiniz.', 'error');
            return;
        }

        log(`Takipçiler: ${followers.length}, Takip Edilenler: ${following.length}. Karşılaştırılıyor...`);
        const followerUsernames = new Set(followers.map(u => u.username));
        const nonFollowers = following.filter(u => !followerUsernames.has(u.username));

        log(`Sonuç: ${nonFollowers.length} kişi sizi takip etmiyor.`);
        resultsCount.innerText = nonFollowers.length;
        resultsList.innerHTML = '';
        nonFollowers.sort((a, b) => a.username.localeCompare(b.username)).forEach(user => {
            const li = document.createElement('li');
            li.innerHTML = `<span>${user.full_name || user.username} (<a href="https://www.instagram.com/${user.username}" target="_blank">@${user.username}</a>)</span>`;
            resultsList.appendChild(li);
        });
        resultsContainer.style.display = 'block';
    }

    async function resetData() {
        const username = targetUsernameInput.value.trim();
        if (!username) { Swal.fire('Eksik Bilgi', 'Sıfırlanacak kullanıcı adını girin.', 'warning'); return; }
        Swal.fire({
            title: 'Emin misiniz?',
            text: `"${username}" için kaydedilmiş tüm veriler silinecek! Bu işlem geri alınamaz.`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#3085d6',
            confirmButtonText: 'Evet, sil!',
            cancelButtonText: 'İptal'
        }).then(async (result) => {
            if (result.isConfirmed) {
                await GM_deleteValue(`followers_${username}`);
                await GM_deleteValue(`following_${username}`);
                await GM_deleteValue(`followers_${username}_cursor`);
                await GM_deleteValue(`following_${username}_cursor`);
                await GM_deleteValue(`userid_${username}`);
                log(`"${username}" için tüm veriler sıfırlandı.`);
                updateProgress(0, 1);
                updateProgress(0, 0);
                resultsContainer.style.display = 'none';
                Swal.fire('Silindi!', 'Veriler başarıyla sıfırlandı.', 'success');
            }
        });
    }

    function copyResultsToClipboard() {
        const listItems = resultsList.querySelectorAll('a');
        if (listItems.length === 0) return;
        const textToCopy = Array.from(listItems).map(a => a.innerText.replace('@', '')).join('\n');
        navigator.clipboard.writeText(textToCopy).then(() => Swal.fire('Kopyalandı!', 'Kullanıcı adları panoya kopyalandı.', 'success'),
            (err) => Swal.fire('Hata', 'Panoya kopyalanamadı: ' + err, 'error'));
    }

    function makeDraggable(panel, header) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        header.onmousedown = (e) => {
            e.preventDefault();
            pos3 = e.clientX; pos4 = e.clientY;
            document.onmouseup = () => { document.onmouseup = null; document.onmousemove = null; };
            document.onmousemove = (e) => {
                e.preventDefault();
                pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY;
                pos3 = e.clientX; pos4 = e.clientY;
                panel.style.top = (panel.offsetTop - pos2) + "px";
                panel.style.left = (panel.offsetLeft - pos1) + "px";
            };
        };
    }

    fetchFollowersBtn.addEventListener('click', () => fetchList('followers'));
    fetchFollowingBtn.addEventListener('click', () => fetchList('following'));
    compareBtn.addEventListener('click', compareLists);
    stopBtn.addEventListener('click', () => { state.isRunning = false; });
    resetBtn.addEventListener('click', resetData);
    copyResultsBtn.addEventListener('click', copyResultsToClipboard);
    makeDraggable(panel, deşifreHeader);

    minimizeBtn.addEventListener('click', () => {
        panel.classList.toggle('minimized');
        deşifreBody.classList.toggle('hidden');
        minimizeBtn.innerText = panel.classList.contains('minimized') ? 'O' : '-';
        deşifreHeader.querySelector('.title-icon').style.display = panel.classList.contains('minimized') ? 'none' : 'inline';
        deşifreHeader.querySelector('span:not(.minimize-btn):not(.title-icon)').style.display = panel.classList.contains('minimized') ? 'none' : 'inline';
    });

    log('Panel yüklendi. Lütfen hedef kullanıcı adını girip bir görev seçin.');
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址