Script for automatic daily

// ==UserScript==
// @name         MS Rewards Auto Daily Set • 02:00 WITA
// @namespace    io.eternal.msr.autodaily.0200
// @version      2.0.0
// @description  Jalan otomatis tiap hari setelah 02:00 (Asia/Makassar). Auto-buka & tutup Daily Set tasks (tanpa klik).
// @author       you
// @match        https://bing.com/*
// @icon         https://www.bing.com/sa/simg/favicon-2x.ico
// @run-at       document-idle
// @grant        GM_openInTab
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// ==/UserScript==

(function () {
  'use strict';

  /*** KONFIGURASI ***/
  const TZ_OFFSET_MINUTES = 8 * 60; // Asia/Makassar (UTC+8)
  const TARGET_HOUR = 2;            // 02:00
  const TARGET_MIN = 0;             // 02:00
  const SAFETY_DELAY_MS = 60 * 1000; // buffer 1 menit setelah jam target
  const DELAY_BETWEEN_OPENS_MS = 3000;
  const TAB_LIFETIME_MS = 22000;     // tahan tab 8 detik, tweak 10-15s jika perlu
  const MAX_CARDS = 4;              // Daily Set biasanya 3

  const LS_KEY_LAST_RUN = 'msr-auto-daily-last-run-v2';

  // ---------- Util waktu (force ke UTC+8 lokal) ----------
  function nowLocal() {
    // Kita pakai waktu lokal mesin; kalau OS kamu sudah UTC+8, ini tepat.
    // Untuk berjaga-jaga, kita geser ke UTC+8 manual.
    const now = new Date();
    const utc = now.getTime() + now.getTimezoneOffset() * 60000;
    return new Date(utc + TZ_OFFSET_MINUTES * 60000);
  }

  function ymd(d) {
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, '0');
    const day = String(d.getDate()).padStart(2, '0');
    return `${y}-${m}-${day}`;
  }

  function targetToday() {
    const d = nowLocal();
    d.setHours(TARGET_HOUR, TARGET_MIN, 0, 0);
    return d;
  }

  function msUntilNextRun() {
    const n = nowLocal();
    const tgt = targetToday();
    if (n.getTime() <= tgt.getTime() + SAFETY_DELAY_MS) {
      // Belum lewat 02:01 => tunggu ke 02:01 hari ini
      return tgt.getTime() + SAFETY_DELAY_MS - n.getTime();
    }
    // Sudah lewat => jadwalkan besok 02:01
    const t2 = new Date(tgt.getTime() + 24 * 3600 * 1000 + SAFETY_DELAY_MS);
    return t2.getTime() - n.getTime();
  }

  function alreadyRanToday() {
    const last = GM_getValue(LS_KEY_LAST_RUN, '');
    return last === ymd(nowLocal());
  }

  function markRanToday() {
    GM_setValue(LS_KEY_LAST_RUN, ymd(nowLocal()));
  }

  // ---------- DOM / aksi ----------
  function findDailySetLinks() {
    // Cari anchor klik yang kamu kirimkan: a.ds-card-sec di dalam mee-rewards-daily-set-item-content
    const anchors = Array.from(
      document.querySelectorAll('mee-rewards-daily-set-item-content a.ds-card-sec[href]')
    );
    const seen = new Set();
    const unique = [];
    for (const a of anchors) {
      const href = a.getAttribute('href');
      if (!href) continue;
      if (seen.has(href)) continue;
      seen.add(href);
      unique.push(a);
      if (unique.length >= MAX_CARDS) break;
    }
    return unique;
  }

  function toast(msg) {
    try {
      GM_notification({ text: msg, title: 'MS Rewards', timeout: 3000 });
    } catch {
      const el = document.createElement('div');
      el.textContent = msg;
      Object.assign(el.style, {
        position: 'fixed', bottom: '60px', right: '16px',
        padding: '10px 12px', borderRadius: '10px',
        background: 'rgba(0,0,0,0.85)', color: '#fff',
        fontSize: '12px', zIndex: 999999
      });
      document.body.appendChild(el);
      setTimeout(() => el.remove(), 2800);
    }
  }

  async function openAndClose(href) {
    const tab = GM_openInTab(href, { active: false, insert: true, setParent: true });
    await new Promise(r => setTimeout(r, TAB_LIFETIME_MS));
    if (tab && typeof tab.close === 'function') { try { tab.close(); } catch {} }
  }

  async function runDailySet() {
    if (alreadyRanToday()) return;

    // Pastikan kartu sudah render
    let links = findDailySetLinks();
    if (links.length < 2) {
      // tunggu sebentar biar yang ke-3 ikut tampil
      await new Promise(r => setTimeout(r, 1200));
      links = findDailySetLinks();
    }
    if (links.length === 0) {
      // Kadang dashboard butuh scroll / network; coba lagi sebentar
      await new Promise(r => setTimeout(r, 2000));
      links = findDailySetLinks();
    }
    if (links.length === 0) {
      toast('Daily Set tidak ditemukan (DOM belum siap). Akan dicoba lagi otomatis.');
      // Biarkan observer memicu nanti
      return;
    }

    toast(`Membuka ${links.length} tugas Daily Set…`);
    for (let i = 0; i < links.length; i++) {
      const href = links[i].href;
      if (!href || href.startsWith('javascript:')) continue;
      openAndClose(href); // non-blocking
      await new Promise(r => setTimeout(r, DELAY_BETWEEN_OPENS_MS));
    }

    markRanToday();
    toast('Daily Set dibuka di background; tab akan tertutup otomatis.');
  }

  // ---------- Scheduler dalam halaman ----------
  // 1) Kalau sekarang sudah lewat 02:01 & belum jalan → eksekusi segera (setelah kartu muncul)
  // 2) Selain itu, set timer menuju 02:01 run berikutnya
  function armTimeScheduler() {
    const waitMs = msUntilNextRun();
    setTimeout(() => {
      // Pada saat timer pecah: kalau halaman terbuka, kita jalankan
      // (Observer di bawah memastikan nunggu kartu render)
      forceRunWhenReady = true;
      maybeKick();
      // Jadwalkan hari berikutnya juga
      armTimeScheduler();
    }, waitMs);
  }

  // Amati DOM agar jalan otomatis saat kartu muncul / reload
  let autoTriggered = false;
  let forceRunWhenReady = false;

  const obs = new MutationObserver(() => {
    if (alreadyRanToday()) return;
    const ready = document.querySelector('mee-rewards-daily-set-item-content a.ds-card-sec[href]');
    if (!ready) return;

    // Jika waktu sudah lewat target (atau dipaksa oleh timer), jalankan sekali
    const n = nowLocal();
    const tgt = targetToday().getTime() + SAFETY_DELAY_MS;
    if ((n.getTime() >= tgt || forceRunWhenReady) && !autoTriggered) {
      autoTriggered = true;
      setTimeout(runDailySet, 400); // beri jeda kecil
    }
  });

  function maybeKick() {
    // Coba trigger kalau DOM sudah siap
    const hasCards = document.querySelector('mee-rewards-daily-set-item-content a.ds-card-sec[href]');
    if (hasCards) {
      const n = nowLocal().getTime();
      const tgt = targetToday().getTime() + SAFETY_DELAY_MS;
      if (!alreadyRanToday() && (n >= tgt || forceRunWhenReady)) {
        autoTriggered = true;
        runDailySet();
      }
    }
  }

  // Start
  obs.observe(document.documentElement, { childList: true, subtree: true });
  armTimeScheduler();       // pasang timer menuju 02:01 berikutnya
  maybeKick();              // coba langsung kalau kondisi sudah memenuhi

  // Menu manual (opsional)
  try {
    GM_registerMenuCommand('Jalankan sekarang', () => {
      forceRunWhenReady = true;
      autoTriggered = false;
      maybeKick();
      toast('Menjalankan Daily Set sekarang…');
    });
  } catch {}
})();