AutoBot FBMP Enhanced

Gabungan auto perbarui & relist Marketplace FB dengan logika retry final, auto start, dan UI kontrol

// ==UserScript==
// @name         AutoBot FBMP Enhanced
// @namespace    http://tampermonkey.net/
// @version      4.4
// @description  Gabungan auto perbarui & relist Marketplace FB dengan logika retry final, auto start, dan UI kontrol
// @author       Behesty
// @match        https://www.facebook.com/marketplace/selling/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  // ⚙️ Konfigurasi yang bisa diubah:
  let retryInterval = 500;        // ms antar percobaan
  let maxTriesPerRound = 60;       // jumlah percobaan per round
  let maxRetryRounds = 3;        // jumlah maksimal round
  let maxLoops = 100;               // maksimal jumlah siklus utama

  let running = true;
  let loopCounter = 0;
  let isLooping = false; // ✅ untuk mencegah mainLoop dipanggil ganda


  // 📦 Log Box
  const logBox = document.createElement('div');
  Object.assign(logBox.style, {
    position: 'fixed',
    bottom: '10px',
    right: '10px',
    background: 'rgba(0,0,0,0.8)',
    color: '#fff',
    padding: '10px',
    maxHeight: '300px',
    overflowY: 'auto',
    fontSize: '12px',
    zIndex: '9999',
    borderRadius: '8px',
    whiteSpace: 'pre-line'
  });
  logBox.innerText = '📢 FB Marketplace Auto Bot\n';
  document.body.appendChild(logBox);

  function log(msg) {
    const now = new Date();
    const time = now.toLocaleTimeString('id-ID', { hour12: false });
    const fullMsg = `[${time}] ${msg}`;
    console.log('[FBMBot] ' + fullMsg);
    logBox.innerText += fullMsg + '\n';
    logBox.scrollTop = logBox.scrollHeight;
  }

  function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  function randomDelay(min = 500, max = 1500) {
    return delay(Math.floor(Math.random() * (max - min + 1)) + min);
  }

  async function waitForElement(selector) {
    for (let round = 1; round <= maxRetryRounds; round++) {
        log(`🔄 Percobaan ${round}`);
      for (let i = 1; i <= maxTriesPerRound; i++) {
        //log(`🔄 Pengecekan tombol: "${selector}" (Round ${round}, Try ${i})`);
        const el = document.querySelector(selector);
        if (el && el.offsetHeight > 0) return el;
        await delay(retryInterval);
      }
    }
    return null;
  }

  async function waitForVisibleElements(selector) {
    for (let round = 1; round <= maxRetryRounds; round++) {
        log(`🔄 Percobaan ${round}`);
      for (let i = 1; i <= maxTriesPerRound; i++) {
        //log(`🔄 Pengecekan tombol: "${selector}" (Round ${round}, Try ${i})`);
        const els = Array.from(document.querySelectorAll(selector)).filter(e => e.offsetHeight > 0);
        if (els.length > 0) return els;
        await delay(retryInterval);
      }
    }
    return [];
  }


    async function findAndClickRelistButton() {
  log('🔎 Mencari tombol "Untuk dihapus & ditawarkan ulang"...');

  for (let round = 1; round <= maxRetryRounds; round++) {
    log(`🔍 Percobaan ${round}`);

    const links = Array.from(document.querySelectorAll('a[role="link"]')).filter(a => a.offsetHeight > 0);
    for (const link of links) {
      const text = link.innerText;
      if (text.includes('Untuk dihapus & ditawarkan ulang')) {
        const count = parseInt(text.match(/\d+/)?.[0] || '0');
        log(`ℹ️ Ditemukan "Untuk dihapus & ditawarkan ulang" (${count} listing)`);

        if (count < 2) {
          log('✅ "Untuk dihapus & ditawarkan ulang" <= 1. Lewati.');
          return false; // ⛔ Tidak perlu delay
        }

        log(`📌 Klik "Untuk dihapus & ditawarkan ulang" (${count} listing)`);
        link.scrollIntoView({ behavior: 'smooth', block: 'center' });
        link.click();
        return true;
      }
    }

    await delay(retryInterval); // ⏳ Tunggu tombol muncul di round berikutnya
  }

  log('❌ Tidak menemukan tombol "Untuk dihapus & ditawarkan ulang" setelah retry.');
  return false;
}

    async function findAndClickUpdateButton() {
  log('🔎 Mencari tombol "Untuk diperbarui"...');

  for (let round = 1; round <= maxRetryRounds; round++) {
    log(`🔍 Percobaan ${round}`);

    const links = Array.from(document.querySelectorAll('a[role="link"]')).filter(a => a.offsetHeight > 0);
    for (const link of links) {
      const text = link.innerText;
      if (text.includes('Untuk diperbarui')) {
        const count = parseInt(text.match(/\d+/)?.[0] || '0');
        log(`ℹ️ Ditemukan "Untuk diperbarui" (${count} listing)`);

        if (count < 2) {
          log('✅ "Untuk diperbarui" <= 1. Lewati.');
          return false;
        }

        log(`📌 Klik "Untuk diperbarui" (${count} listing)`);
        link.scrollIntoView({ behavior: 'smooth', block: 'center' });
        link.click();
        return true;
      }
    }

    await delay(retryInterval); // ⏳ Tunggu tombol muncul di round berikutnya
  }

  log('❌ Tidak menemukan tombol "Untuk diperbarui" setelah retry.');
  return false;
}



  async function clickRelistButtons() {
    log('🔎 Menunggu tombol "Hapus & Tawarkan Ulang"...');
    const buttons = await waitForVisibleElements('div[aria-label="Hapus & Tawarkan Ulang"]');
    if (buttons.length === 0) {
      log('❌ ERROR: Tidak menemukan tombol "Hapus & Tawarkan Ulang".');
      return false;
    }
    log(`🔧 Menemukan ${buttons.length} tombol "Hapus & Tawarkan Ulang"`);
    for (const btn of buttons) {
      btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
      log('🔁 Klik tombol "Hapus & Tawarkan Ulang"...');
      btn.click();
      await randomDelay();
    }
    return true;
  }


  async function clickUpdateButtons() {
    log('🔎 Menunggu tombol "Perbarui"...');
    const buttons = await waitForVisibleElements('div[aria-label="Perbarui"]');
    if (buttons.length === 0) {
      log('❌ ERROR: Tidak menemukan tombol "Perbarui".');
      return false;
    }
    log(`🔧 Menemukan ${buttons.length} tombol "Perbarui"`);
    for (const btn of buttons) {
      btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
      log('🔁 Klik tombol "Perbarui"...');
      btn.click();
      await randomDelay();
    }
    return true;
  }

  async function clickDoneButton() {
    log('🔎 Mencari tombol "Selesai"...');
    const btn = await waitForElement('div[aria-label="Selesai"]');
    if (btn) {
      btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
      log('✅ Klik tombol "Selesai"');
      btn.click();
      return true;
    }
    log('⚠️ Tombol "Selesai" tidak ditemukan.');
    return false;
  }

  async function countdown(seconds) {
  for (let i = seconds; i > 0; i--) {
    if (!running) {
      log('⏹ Dihentikan saat countdown.');
      return;
    }
    log(`⏳ Menunggu ${i} detik...`);
    await delay(1000);
  }
}
    async function checkErrorMessage(selector, expectedText, context) {
  log(`🔎 Mencari pesan khusus untuk ${context}...`);
  const el = Array.from(document.querySelectorAll(selector)).find(e => e.textContent.trim().includes(expectedText));
  if (el) {
    log(`⚠️ Ditemukan pesan error untuk ${context}: "${expectedText}"`);
    await countdown(5);
    window.location.href = 'https://www.facebook.com/marketplace/selling/';
    return true;
  }
  log(`ℹ️ Tidak ditemukan pesan khusus untuk ${context}`);
  return false;
}



  async function runCycle() {
  if (!running) return;
  loopCounter++;
  log(`🚀 Mulai siklus ${loopCounter}`);

  let didSomething = false;
  let relistClickCounter = 0;
  let updateClickCounter = 0;
  let shouldReload = false;

  // 1. Proses relist dulu
  const relistClicked = await findAndClickRelistButton();
  if (relistClicked) {
    await delay(2000);
    const buttons = await waitForVisibleElements('div[aria-label="Hapus & Tawarkan Ulang"]');
    if (buttons.length === 0) {
      log('❌ ERROR: Tidak menemukan tombol "Hapus & Tawarkan Ulang".');
          const found = await checkErrorMessage(
    'span[dir="auto"]',
    'Ini tidak bisa diperbarui lagi, tetapi Anda bisa menawarkan ulang dan menghapus tawaran asli.',
    'Relist'
  );
  if (found) return;

    } else {
      log(`🔧 Menemukan ${buttons.length} tombol "Hapus & Tawarkan Ulang"`);
      for (const btn of buttons) {
        btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
        log('🔁 Klik tombol "Hapus & Tawarkan Ulang"...');
        btn.click();
        relistClickCounter++;
        await randomDelay();
      }
      await clickDoneButton();
      await delay(2000);
      didSomething = true;
    }
    if (relistClickCounter < 20) {
      log(`⚠️ Tombol "Hapus & Tawarkan Ulang" yang diklik (${relistClickCounter}). Akan reload.`);
      shouldReload = true;
    }
  }

  // 2. Proses update
  const updateClicked = await findAndClickUpdateButton();
  if (updateClicked) {
    await delay(2000);
    const buttons = await waitForVisibleElements('div[aria-label="Perbarui"]');
    if (buttons.length === 0) {
      log('❌ ERROR: Tidak menemukan tombol "Perbarui".');
          const found = await checkErrorMessage(
    'span[dir="auto"]',
    'Anda tidak lagi memiliki tawaran yang memenuhi syarat untuk diperbarui.',
    'Update'
  );
  if (found) return;

    } else {
      log(`🔧 Menemukan ${buttons.length} tombol "Perbarui"`);
      for (const btn of buttons) {
        btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
        log('🔁 Klik tombol "Perbarui"...');
        btn.click();
        updateClickCounter++;
        await randomDelay();
      }
      await clickDoneButton();
      didSomething = true;
    }
    if (updateClickCounter < 20) {
      log(`⚠️ Tombol "Perbarui" yang diklik (${updateClickCounter}). Akan reload.`);
      shouldReload = true;
    }
  }

  log(`📊 Ringkasan siklus ${loopCounter} → Relist: ${relistClickCounter}x klik | Update: ${updateClickCounter}x klik`);

  if (shouldReload) {
    log('🔁 Kondisi reload terpenuhi (jumlah klik < 20). Countdown sebelum reload...');
    await countdown(20); // atau countdown(20);
    window.location.href = 'https://www.facebook.com/marketplace/selling/';
    return;
  }

  if (!didSomething) {
    log('✅ Tidak ada tombol tersisa. Proses selesai.');
    running = false;
    return;
  }
}



    async function mainLoop() {
  if (isLooping) return; // ⛔ mencegah dua loop berjalan bersamaan
  isLooping = true;

  while (running && loopCounter < maxLoops) {
    await runCycle();
    await countdown(20);
  }

  isLooping = false;

  if (loopCounter >= maxLoops) {
    log(`🔄 Maksimum ${maxLoops} siklus tercapai. Reload halaman...`);
    await delay(2000);
    window.location.href = 'https://www.facebook.com/marketplace/selling/';
  }
}



  // 🔘 UI: Tombol Start/Stop + input maxLoops
  function createUIControls() {
    const wrapper = document.createElement('div');
    Object.assign(wrapper.style, {
      position: 'fixed',
      bottom: '10px',
      left: '10px',
      zIndex: 10000,
      display: 'flex',
      gap: '10px',
      alignItems: 'center'
    });

    const btn = document.createElement('button');
    btn.innerText = '⏹ STOP';
    Object.assign(btn.style, {
      padding: '8px 12px',
      background: '#333',
      color: '#fff',
      border: 'none',
      borderRadius: '8px',
      cursor: 'pointer'
    });

    const input = document.createElement('input');
    input.type = 'number';
    input.value = maxLoops;
    input.min = 1;
    input.max = 50;
    input.style.width = '60px';
    input.title = 'Max Loop';

    input.onchange = () => {
      maxLoops = parseInt(input.value) || 10;
      log(`🔧 maxLoops diubah menjadi ${maxLoops}`);
    };

    btn.onclick = () => {
        running = !running;
        btn.innerText = running ? '⏹ STOP' : '▶️ START';
        if (running) mainLoop(); // ✅ hanya panggil mainLoop satu kali
};


    wrapper.appendChild(btn);
    wrapper.appendChild(input);
    document.body.appendChild(wrapper);
  }

  // ▶️ Mulai otomatis
  setTimeout(() => {
    createUIControls();
    mainLoop();
  }, 2000);
})();