Hide For You (schedule)

Hides the 'For You' tab and auto-switches to 'Following' on X.com during configured time windows

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Hide For You (schedule)
// @namespace    f_d
// @version      1.2
// @description  Hides the 'For You' tab and auto-switches to 'Following' on X.com during configured time windows
// @author       GPT-5
// @match        https://x.com/*
// @match        https://twitter.com/*
// @icon         https://abs.twimg.com/favicons/twitter.ico
// @grant        none
// ==/UserScript==

/*** ──────────────────────────────────────────────────────────────
 * SCHEDULE CONFIG
 *
 * Time is local browser time, 24h "HH:MM".
 * Days: 0=Sun, 1=Mon, ..., 6=Sat
 * The filter is ACTIVE if the current time falls into ANY range
 * listed for the current day. "default" applies when a day has no
 * explicit entry. Overnight ranges are supported (e.g. 22:00–03:00).
 *
 * My Settings:
 *   - Mon–Fri: block active all day
 *   - Sat & Sun: active until 16:00
 * ───────────────────────────────────────────────────────────────*/
const SCHEDULE = {
  // Sunday
  0: [{ start: "00:00", end: "16:00" }],
  // Monday–Friday
  1: [{ start: "00:00", end: "24:00" }],
  2: [{ start: "00:00", end: "24:00" }],
  3: [{ start: "00:00", end: "24:00" }],
  4: [{ start: "00:00", end: "24:00" }],
  5: [{ start: "00:00", end: "24:00" }],
  // Saturday
  6: [{ start: "00:00", end: "16:00" }],
  // Fallback if a day is not listed
  default: [{ start: "00:00", end: "24:00" }],
};
/*** ─────────────────────────────────────────────────────────── ***/

(function () {
  'use strict';

  // Match both English and German labels, fall back to exact text match
  const LABELS_FOR_YOU = new Set(["For you", "Für dich"]);
  const LABELS_FOLLOWING = new Set(["Following", "Folge ich"]);

  const HIDDEN_ATTR = "data-hidden-by-userscript-for-you";

  function timeToMinutes(t) {
    const [h, m] = t.split(":").map(Number);
    return h * 60 + (m || 0);
  }

  function inRange(nowMin, startMin, endMin) {
    // Handles normal and overnight (wrap-around) ranges
    if (startMin === endMin) return false; // empty range
    if (startMin < endMin) return nowMin >= startMin && nowMin < endMin;
    return nowMin >= startMin || nowMin < endMin;
  }

  function isFeatureActiveNow() {
    const now = new Date();
    const day = now.getDay(); // 0..6
    const minutes = now.getHours() * 60 + now.getMinutes();
    const ranges = SCHEDULE.hasOwnProperty(day) ? SCHEDULE[day] : SCHEDULE.default;

    if (!ranges || ranges.length === 0) return false;
    return ranges.some(({ start, end }) => inRange(minutes, timeToMinutes(start), timeToMinutes(end)));
  }

  function getTabs() {
    const tabs = Array.from(document.querySelectorAll('[role="tab"]'));
    let forYouTab = null, followingTab = null;

    tabs.forEach(tab => {
      const text = (tab.textContent || "").trim();
      if (LABELS_FOR_YOU.has(text)) forYouTab = tab;
      if (LABELS_FOLLOWING.has(text)) followingTab = tab;
    });

    return { forYouTab, followingTab };
  }

  function hideForYouAndSwitch() {
    const { forYouTab, followingTab } = getTabs();
    if (!forYouTab) return;

    // Hide the "For you" tab
    if (forYouTab.style.display !== "none") {
      forYouTab.style.display = "none";
      forYouTab.setAttribute(HIDDEN_ATTR, "true");
    }

    // If "For you" was active, switch to "Following"
    if (forYouTab.getAttribute('aria-selected') === 'true' && followingTab) {
      // Some builds require a mouse event to trigger navigation properly
      followingTab.click();
    }
  }

  function unhideForYou() {
    const { forYouTab } = getTabs();
    if (!forYouTab) return;
    if (forYouTab.getAttribute(HIDDEN_ATTR) === "true") {
      forYouTab.style.display = "";
      forYouTab.removeAttribute(HIDDEN_ATTR);
    }
  }

  function applyBehavior() {
    if (isFeatureActiveNow()) {
      hideForYouAndSwitch();
    } else {
      unhideForYou();
    }
  }

  // Run immediately
  applyBehavior();

  // Observe DOM changes (tabs re-render frequently)
  const observer = new MutationObserver(() => applyBehavior());
  observer.observe(document.body, { childList: true, subtree: true });

  // Re-check schedule every minute in case the hour changes without DOM mutations
  const intervalId = setInterval(applyBehavior, 60 * 1000);

  // Cleanup on unload
  window.addEventListener('unload', () => {
    observer.disconnect();
    clearInterval(intervalId);
  }, false);
})();