AutoDarkMode

Automatically switch to Dark Mode

2025-04-09 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         AutoDarkMode
// @namespace    airbash/Rocy-June/AutoDarkMode
// @homepage     https://github.com/AirBashX/UserScript
// @version      25.04.07.01
// @description  Automatically switch to Dark Mode
// @author       airbash / Rocy-June
// @match        *://*/*
// @icon
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license      GPL-3.0
// ==/UserScript==

(function () {
  "use strict";

  // 日志函数
  const log = console.log.bind(console, "[AutoDarkMode Script]");
  const error = console.error.bind(console, "[AutoDarkMode Script]");

  log("脚本开始运行");

  // 设置对象
  const settings = {
    light_time: "08:00",
    dark_time: "18:00",
    light_menu_id: null,
    dark_menu_id: null,
  };

  // 初始化设置
  initSettings();

  // 所有适配的站点
  /* 
    {
      "domain (站点域名匹配, 防止后续匹配项过多, 提高加载速度)": [{
        url: 匹配站点正则,
        check: 检查当前主题函数(返回 "dark" 或 "light"),
        toggle: 切换主题函数(可选, 为 null 时 toLight 和 toDark 函数会被调用),
        toLight: 切换到明亮主题函数(可选, toggle 为 null 时必需设定),
        toDark: 切换到黑夜主题函数(可选, toggle 为 null 时必需设定),
        checkTime: 检查主题间隔时间(可选, 为 null 时默认 5 秒),
      }]
    }
  */
  const sites = {
    "pixiv.net": [
      {
        url: /^https?:\/\/.*?pixiv\.net.*/,
        check: () =>
          document.documentElement.getAttribute("data-theme") === "dark"
            ? "dark"
            : "light",
        toggle: () => {
          const button = document.querySelector(
            "button.ccl__sc-1lxyknw-0.hZvyDT.sc-pkfh0q-1.ikiFYU"
          );
          button.click();
          nextTick(() => {
            document
              .querySelector("div.sc-1o6692m-0.lerGVa.sc-gmfqyv-1.jdCrQO")
              .click();
            button.click();
          });
        },
      },
    ],
    "fengchedmp.com": [
      {
        url: /^https?:\/\/.*?fengchedmp\.com.*/,
        check: () =>
          document
            .querySelector("#cssFile")
            .getAttribute("href")
            .includes("black")
            ? "dark"
            : "light",
        toLight: () => {
          document.querySelector("i.icon-rijian").click();
        },
        toDark: () => {
          document.querySelector("i.icon-yejian").click();
        },
      },
    ],
  };

  // 匹配到的域名设置
  const no_www_domain = location.hostname.replace(/^www\./, "");

  const domain_setting = sites[no_www_domain];
  if (!domain_setting) {
    return;
  }

  // 匹配到的网站设置
  const site_setting = domain_setting.find((s) => s.url.test(location.href));
  if (!site_setting) {
    return;
  }

  // 加载完成后开始检查主题
  addEventListener("load", () => {
    // 创建一个定时器, 每隔 5 秒检查当前时间是否已超过时间设置
    setInterval(checkTheme, site_setting.checkTime || 5000);

    // 立刻检查
    checkTheme();
  });

  // 初始化设置
  function initSettings() {
    log("初始化设置");

    settings.light_time = GM_getValue("light_time", settings.light_time);
    settings.dark_time = GM_getValue("dark_time", settings.dark_time);

    refreshMenuCommand();
  }

  // 检查当前时间是否需要切换主题
  function checkTheme() {
    log("开始检查主题");

    const light_minutes = timeToMinutes(settings.light_time);
    const dark_minutes = timeToMinutes(settings.dark_time);

    const now = nowMinutes();

    const current_theme = site_setting.check();
    log(`当前主题:${current_theme}`);

    if (now >= light_minutes && now < dark_minutes) {
      if (current_theme === "light") {
        log("当前时间为明亮主题时间, 无需切换");
        return;
      }
      if (site_setting.toggle) {
        log("切换到明亮主题 - toggle");
        site_setting.toggle();
        return;
      }
      if (site_setting.toLight) {
        log("切换到明亮主题 - toLight");
        site_setting.toLight();
        return;
      }

      error("切换到明亮主题 - 未指定切换函数");
    } else {
      if (current_theme === "dark") {
        log("当前时间为黑夜主题时间, 无需切换");
        return;
      }
      if (site_setting.toggle) {
        log("切换到黑夜主题 - toggle");
        site_setting.toggle();
        return;
      }
      if (site_setting.toDark) {
        log("切换到黑夜主题 - toDark");
        site_setting.toDark();
        return;
      }

      error("切换到黑夜主题 - 未指定切换函数");
    }
  }

  // 刷新菜单
  function refreshMenuCommand() {
    log("刷新脚本菜单");

    if (settings.light_menu_id) {
      GM_unregisterMenuCommand(settings.light_menu_id);
    }
    if (settings.dark_menu_id) {
      GM_unregisterMenuCommand(settings.dark_menu_id);
    }

    settings.light_menu_id = GM_registerMenuCommand(
      `设置明亮时间 (${settings.light_time})`,
      () => setTimePrompt("light_time", "明亮时间")
    );
    settings.dark_menu_id = GM_registerMenuCommand(
      `设置黑夜时间 (${settings.dark_time})`,
      () => setTimePrompt("dark_time", "黑夜时间")
    );
  }

  // 设置时间提示框
  function setTimePrompt(key, label) {
    log(`设置${label}提示框`);

    const old_val = GM_getValue(key, settings[key]);

    log(`旧${label}:${old_val}`);

    const new_val = prompt(`设置${label}(格式 HH:mm):`, old_val);
    log(`用户输入: ${new_val}`);
    if (!new_val) {
      return;
    }
    if (
      !new_val ||
      !/^(?:[0-9]|1[0-9]|2[0-3])[::](?:[0-9]|[0-5][0-9])$/.test(new_val)
    ) {
      alert('格式不正确, 时间格式为 "08:00"');
      return;
    }
    const standard_new_val = new_val.replace(":", ":");

    log(`新${label}:${standard_new_val}`);

    const tmp = standard_new_val;
    settings[key] = standard_new_val;
    if (
      timeToMinutes(settings.light_time) > timeToMinutes(settings.dark_time)
    ) {
      log("黑夜时间不能早于明亮时间");

      settings[key] = tmp;
      alert("黑夜时间不能早于明亮时间");
      return;
    }

    GM_setValue(key, standard_new_val);

    log(`${label} 已设置为:${standard_new_val}`);

    alert(`${label} 已设置为:${standard_new_val}`);

    refreshMenuCommand();
  }

  // 将时间字符串转为分钟
  function timeToMinutes(time) {
    const [hour, minute] = time.split(":").map(Number);
    return hour * 60 + minute;
  }

  // 获取当前时间的分钟数
  function nowMinutes() {
    const now = new Date();
    return now.getHours() * 60 + now.getMinutes();
  }

  // 将函数加入下一轮事件循环
  function nextTick(func) {
    Promise.resolve().then(func);
  }
})();