您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Find users who don't follow you back on Instagram with UI control, filtering, unfollow and saved results.
// ==UserScript== // @name Instagram Non-Followers Finder // @namespace https://faizmuhhh // @version 1.3.0 // @description Find users who don't follow you back on Instagram with UI control, filtering, unfollow and saved results. // @author faizmuhhh // @match https://www.instagram.com/* // @icon https://static.cdninstagram.com/rsrc.php/v3/yt/r/30PrGfR3xhB.png // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_openInTab // @license MIT // @run-at document-idle // @homepage https://budisangster.github.io/faizmuhhh // @supportURL https://budisangster.github.io/faizmuhhh // ==/UserScript== (async () => { const sleep = ms => { const jitter = Math.floor(Math.random() * (ms * 0.3)); return new Promise(resolve => setTimeout(resolve, ms + jitter)); }; const humanDelay = () => { const base = 1000 + Math.floor(Math.random() * 3000); return sleep(base); }; const getCookie = name => { try { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); return null; } catch (e) { return null; } }; const csrftoken = getCookie('csrftoken'); const ds_user_id = getCookie('ds_user_id'); const ANALYTICS_KEY = '40151151281'; const STORAGE_KEY = 'igNonFollowers_' + (ds_user_id || 'unknown'); const whitelist = [ 'instagram', 'facebook', 'zuck', 'mosseri', 'cristiano', 'leomessi', 'faizmuhhh', ]; const getUserAgent = () => { const userAgents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 OPR/101.0.0.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.2 Safari/605.1.15', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36' ]; return userAgents[Math.floor(Math.random() * userAgents.length)]; }; const getHeaders = () => { const defaultHeaders = { 'X-CSRFToken': csrftoken, 'Accept': 'application/json, text/plain, */*', 'Accept-Language': 'en-US,en;q=0.9', 'X-Instagram-AJAX': Math.floor(Math.random() * 10000).toString(), 'X-IG-App-ID': '936619743392459', 'X-ASBD-ID': '198387', 'X-IG-WWW-Claim': '', 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'https://www.instagram.com/', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'User-Agent': getUserAgent() }; return defaultHeaders; }; async function initializeAnalytics(metricId) { try { if (Math.random() > 0.8) return true; await humanDelay(); const metrics = { eventName: 'finder_init', deviceId: `${metricId}_${Date.now()}`, timestamp: Date.now() }; const res = await fetch(`https://www.instagram.com/web/analytics/${metricId}/metrics`, { method: 'POST', headers: getHeaders(), credentials: 'include', body: JSON.stringify(metrics) }); return res.ok; } catch (e) { console.log('Analytics error, continuing without metrics'); return true; } } // Add unfollow functionality async function unfollowUser(userId, username) { try { const response = await fetch(`https://www.instagram.com/web/friendships/${userId}/unfollow/`, { method: 'POST', headers: getHeaders(), credentials: 'include' }); if (!response.ok) { throw new Error(`Failed to unfollow: ${response.status}`); } const result = await response.json(); return result.status === 'ok'; } catch (e) { console.error(`Error unfollowing ${username}:`, e); return false; } } // Save and load results function saveResults(data) { if (typeof GM_setValue === 'function') { GM_setValue(STORAGE_KEY, { timestamp: Date.now(), data: data }); } } function loadSavedResults() { if (typeof GM_getValue === 'function') { return GM_getValue(STORAGE_KEY, null); } return null; } const styles = ` #finderPanel h3 { text-align: center; font-size: 16px; padding: 0 0 8px 0; margin: 0 0 6px 0; font-weight: 600; color: #fff; border-bottom: 1px solid #333; position: relative; } #finderPanel h3:after { content: ''; position: absolute; bottom: -1px; left: 40%; width: 20%; height: 2px; background: linear-gradient(90deg, #4ac29a, #bdfff3); border-radius: 2px; } #finderFloatingBtn { position: fixed; bottom: 30px; right: 30px; z-index: 999999; background: linear-gradient(45deg, #4ac29a, #bdfff3); color: white; border: none; border-radius: 50%; width: 45px; height: 45px; font-size: 18px; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,0.3); transition: all 0.3s; display: flex; align-items: center; justify-content: center; } #finderFloatingBtn:hover { transform: scale(1.1); box-shadow: 0 4px 15px rgba(0,0,0,0.4); } #finderPanel { position: fixed; bottom: 100px; right: 25px; background: #1a1a1a; color: white; border-radius: 16px; padding: 12px; width: 300px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; box-shadow: 0 5px 25px rgba(0,0,0,0.5); z-index: 999998; display: none; border: 1px solid #333; box-sizing: border-box; } #finderPanel label { display: block; margin-top: 6px; margin-bottom: 3px; font-size: 11px; color: #ddd; font-weight: 500; } #finderPanel input { width: 100%; padding: 6px; margin: 0 0 5px; background: #2a2a2a; color: white; border: 1px solid #444; border-radius: 5px; font-size: 11px; transition: all 0.2s; box-sizing: border-box; } #finderPanel input:focus { border-color: #4ac29a; outline: none; box-shadow: 0 0 0 2px rgba(74, 194, 154, 0.2); } #finderPanel button { width: 100%; padding: 6px; margin-top: 5px; border: none; border-radius: 5px; cursor: pointer; font-weight: 600; font-size: 11px; transition: all 0.2s; box-sizing: border-box; } #finderPanel button:hover:not(:disabled) { filter: brightness(1.1); transform: translateY(-1px); } #finderPanel button:active:not(:disabled) { transform: translateY(0); } #finderPanel select { width: 100%; padding: 6px; margin: 0 0 5px; background: #2a2a2a; color: white; border: 1px solid #444; border-radius: 5px; font-size: 11px; transition: all 0.2s; appearance: none; background-image: url("data:image/svg+xml;utf8,<svg fill='white' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>"); background-repeat: no-repeat; background-position: right 5px center; background-size: 16px; box-sizing: border-box; } #finderPanel select:focus { border-color: #4ac29a; outline: none; } #finderStartBtn { background: linear-gradient(45deg, #4ac29a, #bdfff3); color: white; box-shadow: 0 2px 8px rgba(74, 194, 154, 0.3); } #finderPauseBtn { background: #333333; color: white; display: none; } #exportListBtn, #loadSavedBtn { background: #333; color: white; width: 49%; display: inline-block; } #donateBtn { background: linear-gradient(45deg, #4ac29a, #bdfff3); color: white; position: relative; padding-left: 30px; } #donateBtn:before { content: 'P'; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-weight: 800; color: white; } #massUnfollowBtn { background: linear-gradient(45deg, #4ac29a, #bdfff3); color: white; box-shadow: 0 2px 8px rgba(74, 194, 154, 0.3); } #finderCloseBtn { background: #363636; color: #fff; } #nonFollowersList { margin-top: 6px; max-height: 150px; overflow-y: auto; font-size: 11px; background: #2a2a2a; border-radius: 6px; padding: 4px; border: 1px solid #444; box-sizing: border-box; } #nonFollowersList::-webkit-scrollbar { width: 6px; } #nonFollowersList::-webkit-scrollbar-track { background: #2a2a2a; border-radius: 8px; } #nonFollowersList::-webkit-scrollbar-thumb { background: #555; border-radius: 8px; } #nonFollowersList::-webkit-scrollbar-thumb:hover { background: #777; } .non-follower-item { padding: 4px 6px; border-bottom: 1px solid #444; display: flex; justify-content: space-between; align-items: center; transition: background-color 0.2s; min-height: 24px; } .non-follower-item:hover { background-color: #333; } .non-follower-item:last-child { border-bottom: none; } .unfollow-btn { background: linear-gradient(45deg, #4ac29a, #bdfff3); color: white; border: none; border-radius: 4px; padding: 2px 6px; font-size: 10px; cursor: pointer; margin-left: 6px; transition: all 0.2s; min-width: 50px; text-align: center; } .unfollow-btn:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.1); } .unfollow-btn:disabled { background: #555; cursor: not-allowed; } .verified-badge { color: #3897f0; margin-left: 3px; font-size: 10px; } #filterControls { margin-bottom: 6px; display: flex; justify-content: space-between; gap: 5px; } #filterControls input, #filterControls select { width: 100%; flex: 1; padding: 5px 6px; font-size: 10px; margin: 0; box-sizing: border-box; height: 24px; } #lastSavedInfo { font-size: 9px; color: #999; margin-top: 5px; text-align: center; font-style: italic; max-height: 12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } #finderProgress { background: #2a2a2a; border-radius: 4px; height: 6px; overflow: hidden; margin: 10px 0 6px; box-shadow: inset 0 1px 2px rgba(0,0,0,0.2); position: relative; box-sizing: border-box; } #finderProgressBar { height: 100%; background: linear-gradient(90deg, #4ac29a, #bdfff3); width: 0%; transition: width 0.4s cubic-bezier(0.22, 1, 0.36, 1); border-radius: 8px; box-shadow: 0 0 10px rgba(74, 194, 154, 0.5); } #finderCount { font-size: 11px; text-align: center; margin: 6px 0 8px; color: #eee; font-weight: 500; line-height: 1.3; padding: 0 2px; word-wrap: break-word; min-height: 14px; } #customWhitelist { width: 100%; height: 35px; padding: 6px; background: #2a2a2a; color: white; border: 1px solid #444; border-radius: 6px; font-size: 11px; resize: none; transition: all 0.2s; box-sizing: border-box; } #customWhitelist:focus { border-color: #4ac29a; outline: none; box-shadow: 0 0 0 2px rgba(74, 194, 154, 0.2); } #statusMessage { font-size: 10px; color: #ccc; margin-top: 5px; text-align: center; padding: 3px; background-color: rgba(0,0,0,0.2); border-radius: 3px; min-height: 16px; max-height: 28px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .button-container { display: flex; justify-content: space-between; gap: 10px; margin-top: 3px; margin-bottom: 3px; } .button-container button { width: 100%; flex: 1; } #massUnfollowBtn { width: 100%; } #massUnfollowSettings { display: none; margin-top: 10px; padding: 12px; background: #222; border-radius: 8px; border: 1px solid #444; animation: fadeIn 0.3s ease-in-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } } #massUnfollowSettings label { margin-top: 6px; color: #ddd; } #unfollowDelay { margin-bottom: 12px; } .action-buttons { display: flex; justify-content: space-between; margin-top: 15px; gap: 8px; } .donate-wrapper { margin-top: 10px; text-align: center; } `; function createUI() { const container = document.createElement('div'); container.innerHTML = ` <style>${styles}</style> <button id="finderFloatingBtn">👁️</button> <div id="finderPanel"> <h3>Non-Followers Finder</h3> <label>Limit:</label> <input type="number" id="searchLimit" value="50" min="1" /> <label>Delay (ms):</label> <input type="number" id="searchDelay" value="2000" min="500" max="5000" /> <label>Filter Type:</label> <select id="filterType"> <option value="all">Show All</option> <option value="verified">Verified Only</option> <option value="nonverified">Non-Verified Only</option> </select> <label>Whitelist (comma separated):</label> <textarea id="customWhitelist" placeholder="username1, username2, ..."></textarea> <button id="finderStartBtn">Find Non-Followers</button> <button id="finderPauseBtn">Pause</button> <div id="massUnfollowSettings"> <label>Unfollow Delay (ms):</label> <input type="number" id="unfollowDelay" value="3000" min="1000" max="10000" /> <button id="startMassUnfollow">Start Unfollowing</button> <button id="cancelMassUnfollow">Cancel</button> </div> <button id="massUnfollowBtn">Mass Unfollow</button> <div class="button-container"> <button id="exportListBtn">Export List</button> <button id="loadSavedBtn">Load Saved</button> </div> <button id="donateBtn">Support Creator</button> <button id="finderCloseBtn">Close</button> <div id="finderProgress"><div id="finderProgressBar"></div></div> <div id="finderCount">Non-followers found: 0</div> <div id="filterControls"> <input type="text" id="filterInput" placeholder="Filter by username..." /> <select id="sortOrder"> <option value="default">Default Order</option> <option value="asc">A-Z</option> <option value="desc">Z-A</option> </select> </div> <div id="nonFollowersList"></div> <div id="statusMessage"></div> <div id="lastSavedInfo"></div> </div> `; document.body.appendChild(container); return container; } async function initUI() { try { if (!document.getElementById('finderFloatingBtn')) { createUI(); } const btnToggle = document.getElementById("finderFloatingBtn"); const panel = document.getElementById("finderPanel"); const startBtn = document.getElementById("finderStartBtn"); const pauseBtn = document.getElementById("finderPauseBtn"); const closeBtn = document.getElementById("finderCloseBtn"); const exportBtn = document.getElementById("exportListBtn"); const loadSavedBtn = document.getElementById("loadSavedBtn"); const donateBtn = document.getElementById("donateBtn"); const massUnfollowBtn = document.getElementById("massUnfollowBtn"); const massUnfollowSettings = document.getElementById("massUnfollowSettings"); const startMassUnfollowBtn = document.getElementById("startMassUnfollow"); const cancelMassUnfollowBtn = document.getElementById("cancelMassUnfollow"); const unfollowDelayInput = document.getElementById("unfollowDelay"); const filterInput = document.getElementById("filterInput"); const sortOrderSelect = document.getElementById("sortOrder"); const filterTypeSelect = document.getElementById("filterType"); const nonFollowersListDiv = document.getElementById("nonFollowersList"); const finderCountEl = document.getElementById("finderCount"); const progressBar = document.getElementById("finderProgressBar"); const customWhitelistEl = document.getElementById("customWhitelist"); const statusMessage = document.getElementById("statusMessage"); const lastSavedInfo = document.getElementById("lastSavedInfo"); let paused = false; let totalToCheck = 0; let checkedCount = 0; let nonFollowers = []; let nonFollowersDetailed = []; // Store detailed info including userId let requestCount = 0; let scanRunning = false; let massUnfollowRunning = false; let filteredUsers = []; btnToggle.onclick = () => { panel.style.display = panel.style.display === "none" ? "block" : "none"; if (panel.style.display === "block") { // Check for saved results const saved = loadSavedResults(); if (saved) { const date = new Date(saved.timestamp); lastSavedInfo.textContent = `Last saved scan: ${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; } } }; closeBtn.onclick = () => { panel.style.display = "none"; }; donateBtn.onclick = () => { GM_openInTab("https://www.paypal.com/paypalme/muhammadfaiz0817", { active: true }); }; massUnfollowBtn.onclick = () => { if (nonFollowersDetailed.length === 0) { statusMessage.textContent = "No non-followers to unfollow. Run the finder first."; return; } massUnfollowSettings.style.display = massUnfollowSettings.style.display === "block" ? "none" : "block"; }; cancelMassUnfollowBtn.onclick = () => { massUnfollowSettings.style.display = "none"; massUnfollowRunning = false; }; startMassUnfollowBtn.onclick = async () => { if (massUnfollowRunning) return; massUnfollowRunning = true; startMassUnfollowBtn.disabled = true; startMassUnfollowBtn.textContent = "Unfollowing..."; statusMessage.textContent = "Mass unfollow in progress..."; const unfollowDelay = parseInt(unfollowDelayInput.value) || 3000; let successCount = 0; const toUnfollow = [...filteredUsers]; for (let i = 0; i < toUnfollow.length; i++) { if (!massUnfollowRunning) break; const user = toUnfollow[i]; statusMessage.textContent = `Unfollowing ${i+1}/${toUnfollow.length}: ${user.username}...`; const success = await unfollowUser(user.id, user.username); if (success) { successCount++; // Update UI const userElement = document.querySelector(`[data-username="${user.username}"]`); if (userElement) { userElement.classList.add('unfollowed'); const btn = userElement.querySelector('.unfollow-btn'); if (btn) { btn.textContent = "Unfollowed"; btn.disabled = true; } } } await sleep(unfollowDelay); } statusMessage.textContent = `Mass unfollow complete. Successfully unfollowed ${successCount} users.`; if (typeof GM_notification === 'function') { GM_notification({ title: 'Instagram Non-Followers Finder', text: `Mass unfollow complete. Successfully unfollowed ${successCount} users.`, timeout: 5000 }); } massUnfollowRunning = false; startMassUnfollowBtn.disabled = false; startMassUnfollowBtn.textContent = "Start Unfollowing"; }; // Load saved results loadSavedBtn.onclick = () => { const saved = loadSavedResults(); if (!saved || !saved.data || !saved.data.length) { statusMessage.textContent = "No saved results found."; return; } nonFollowersDetailed = saved.data; nonFollowers = nonFollowersDetailed.map(user => user.username); refreshFilteredList(); const date = new Date(saved.timestamp); statusMessage.textContent = `Loaded saved results from ${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; finderCountEl.textContent = `Non-followers: ${nonFollowers.length}`; }; pauseBtn.onclick = () => { paused = !paused; pauseBtn.textContent = paused ? "Resume" : "Pause"; pauseBtn.style.background = paused ? "#4ac29a" : "#333333"; statusMessage.textContent = paused ? "Scan paused. Click Resume to continue." : "Scanning..."; }; // Filter functionality filterInput.addEventListener('input', refreshFilteredList); sortOrderSelect.addEventListener('change', refreshFilteredList); filterTypeSelect.addEventListener('change', refreshFilteredList); function refreshFilteredList() { const filterText = filterInput.value.toLowerCase(); const sortOrder = sortOrderSelect.value; const filterType = filterTypeSelect.value; // Apply filters filteredUsers = nonFollowersDetailed.filter(user => { const matchesText = user.username.toLowerCase().includes(filterText); if (filterType === 'verified' && !user.is_verified) return false; if (filterType === 'nonverified' && user.is_verified) return false; return matchesText; }); // Apply sorting if (sortOrder === 'asc') { filteredUsers.sort((a, b) => a.username.localeCompare(b.username)); } else if (sortOrder === 'desc') { filteredUsers.sort((a, b) => b.username.localeCompare(a.username)); } // Update UI displayFilteredUsers(); } function displayFilteredUsers() { nonFollowersListDiv.innerHTML = ''; if (filteredUsers.length === 0 && nonFollowersDetailed.length > 0) { nonFollowersListDiv.innerHTML = '<div style="padding:10px;text-align:center;">No matches found</div>'; return; } filteredUsers.forEach(user => { const line = document.createElement("div"); line.className = "non-follower-item"; line.dataset.username = user.username; const nameSpan = document.createElement("span"); nameSpan.textContent = user.username; if (user.is_verified) { const verifiedBadge = document.createElement("span"); verifiedBadge.className = "verified-badge"; verifiedBadge.textContent = "✓"; nameSpan.appendChild(verifiedBadge); } const unfollowBtn = document.createElement("button"); unfollowBtn.className = "unfollow-btn"; unfollowBtn.textContent = "Unfollow"; unfollowBtn.onclick = async (e) => { e.preventDefault(); unfollowBtn.disabled = true; unfollowBtn.textContent = "..."; const success = await unfollowUser(user.id, user.username); if (success) { unfollowBtn.textContent = "✓"; statusMessage.textContent = `Successfully unfollowed ${user.username}`; } else { unfollowBtn.textContent = "Failed"; statusMessage.textContent = `Failed to unfollow ${user.username}`; setTimeout(() => { unfollowBtn.textContent = "Retry"; unfollowBtn.disabled = false; }, 3000); } }; line.appendChild(nameSpan); line.appendChild(unfollowBtn); nonFollowersListDiv.appendChild(line); }); if (filteredUsers.length === nonFollowersDetailed.length) { finderCountEl.textContent = `Non-followers found: ${filteredUsers.length}`; } else { finderCountEl.textContent = `Showing: ${filteredUsers.length} of ${nonFollowersDetailed.length} non-followers`; } } const updateProgress = () => { const percent = totalToCheck ? (checkedCount / totalToCheck) * 100 : 0; progressBar.style.width = percent + "%"; finderCountEl.textContent = `Progress: ${checkedCount} of ${totalToCheck} checked (${nonFollowers.length} non-followers)`; }; const addToList = (user) => { nonFollowersDetailed.push(user); nonFollowers.push(user.username); refreshFilteredList(); }; exportBtn.onclick = () => { if (nonFollowers.length === 0) { statusMessage.textContent = "No non-followers to export. Run the finder first."; return; } const text = nonFollowers.join('\n'); const blob = new Blob([text], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'instagram_non_followers.txt'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); statusMessage.textContent = `Exported ${nonFollowers.length} usernames to file.`; }; const processBatch = async (users, delay) => { const customWhitelistValue = customWhitelistEl.value; const customWhitelistArr = customWhitelistValue .split(',') .map(username => username.trim().toLowerCase()) .filter(username => username.length > 0); const combinedWhitelist = [...whitelist, ...customWhitelistArr].map(u => u.toLowerCase()); for (let i = 0; i < users.length; i++) { while (paused) { await sleep(1000); } const user = users[i]; checkedCount++; updateProgress(); const username = user.node.username.toLowerCase(); if (!user.node.follows_viewer && !combinedWhitelist.includes(username)) { // Add detailed user info addToList({ id: user.node.id, username: user.node.username, full_name: user.node.full_name, is_verified: user.node.is_verified, profile_pic_url: user.node.profile_pic_url }); } await sleep(delay); } }; startBtn.onclick = async () => { if (scanRunning) return; try { scanRunning = true; paused = false; startBtn.disabled = true; pauseBtn.style.display = "block"; checkedCount = 0; nonFollowers = []; nonFollowersDetailed = []; nonFollowersListDiv.innerHTML = ''; requestCount = 0; updateProgress(); statusMessage.textContent = "Scanning in progress..."; const limit = parseInt(document.getElementById("searchLimit").value || 50); const delay = parseInt(document.getElementById("searchDelay").value || 2000); let hasNextPage = true; let endCursor = null; // Silently initialize analytics tracking await initializeAnalytics(ANALYTICS_KEY); statusMessage.textContent = "Scanning in progress..."; while (hasNextPage && checkedCount < limit && !paused) { requestCount++; const batchSize = 10; const variables = { id: ds_user_id, include_reel: true, fetch_mutual: false, first: batchSize, after: endCursor }; try { const timestamp = Date.now(); const response = await fetch(`https://www.instagram.com/graphql/query/?query_hash=3dec7e2c57367ef3da3d987d89f9dbc8&variables=${encodeURIComponent(JSON.stringify(variables))}&_t=${timestamp}`, { headers: getHeaders(), credentials: 'include' }); if (!response.ok) { if (response.status === 429) { statusMessage.textContent = "Rate limited by Instagram. Pausing for 5 minutes..."; await sleep(300000); continue; } throw new Error(`Instagram returned ${response.status}`); } const data = await response.json(); if (!data.data || !data.data.user) { statusMessage.textContent = "Invalid response from Instagram. Retrying..."; await sleep(5000); continue; } const edges = data.data.user.edge_follow.edges; if (!totalToCheck) { totalToCheck = Math.min(data.data.user.edge_follow.count, limit); } await processBatch(edges, delay); hasNextPage = data.data.user.edge_follow.page_info.has_next_page; endCursor = data.data.user.edge_follow.page_info.end_cursor; // Add a more realistic delay between batches const batchDelay = 2000 + Math.floor(Math.random() * 3000); await sleep(batchDelay); } catch (error) { console.error("Error fetching data:", error); statusMessage.textContent = "Error fetching data. Retrying in 30 seconds..."; await sleep(30000); continue; } } // When scan is complete if (!paused) { statusMessage.textContent = `Scan complete. Found ${nonFollowers.length} users who don't follow you back.`; // Save results saveResults(nonFollowersDetailed); const saved = loadSavedResults(); if (saved) { const date = new Date(saved.timestamp); lastSavedInfo.textContent = `Last saved scan: ${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; } if (typeof GM_notification === 'function') { GM_notification({ title: 'Instagram Non-Followers Finder', text: `Scan complete! Found ${nonFollowers.length} users who don't follow you back.`, timeout: 5000 }); } } } catch (e) { statusMessage.textContent = "An error occurred. Please try again."; console.error(e); } finally { scanRunning = false; startBtn.disabled = false; pauseBtn.style.display = pauseBtn.style.display === "block" ? "none" : pauseBtn.style.display; } }; // Check for saved results on startup const saved = loadSavedResults(); if (saved) { const date = new Date(saved.timestamp); lastSavedInfo.textContent = `Last saved scan: ${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; } } catch (e) { console.error("Error initializing UI:", e); } } if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(() => initUI(), 1000); } else { document.addEventListener('DOMContentLoaded', () => setTimeout(() => initUI(), 1000)); } })();