SPXXKLP

Minecraft.net & X.com blog article to BBCode converter, adapted to KLPBBS

Εγκατάσταση αυτού του κώδικαΒοήθεια
Κώδικας προτεινόμενος από τον δημιιουργό

Μπορεί, επίσης, να σας αρέσει ο κώδικας 移除 Minecraft.net 网易弹窗.

Εγκατάσταση αυτού του κώδικα
// ==UserScript==
// @name        SPXXKLP
// @description Minecraft.net & X.com blog article to BBCode converter, adapted to KLPBBS
// @namespace   npmjs.com/package/@spxxklp/userscript
// @author      Cinder & SPGoding & SPX Fellow
// @connect     *
// @connect     feedback.minecraft.com
// @connect     help.minecraft.net
// @connect     raw.githubusercontent.com
// @homepage    https://github.com/cinder0601/SPXXKLP
// @match       https://www.minecraft.net/en-us/article/*
// @match       https://www.minecraft.net/zh-hans/article/*
// @match       https://x.com/*/status/*
// @match       https://feedback.minecraft.net/hc/en-us/articles/*
// @match       https://help.minecraft.net/hc/en-us/articles/*
// @require     https://fastly.jsdelivr.net/gh/sizzlemctwizzle/GM_config@2207c5c1322ebb56e401f03c2e581719f909762a/gm_config.js
// @icon        https://www.minecraft.net/etc.clientlibs/minecraft/clientlibs/main/resources/favicon.ico
// @version     3.2.4
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_setClipboard
// @grant       GM_xmlhttpRequest
// @grant       GM_registerMenuCommand
// @license     MIT
// ==/UserScript==
(function () {
  "use strict";

  var GM_config = new GM_configStruct();
  GM_config.init({
    id: "spxxklp",
    title: "SPXXKLP 用户脚本",
    fields: {
      translator: {
        label: "译者名",
        type: "text",
        default: "<默认译者>",
      },
      bugSource: {
        label: "选择翻译源",
        type: "select",
        options: ["Github", "自定义"],
        default: "Github",
      },
      bugCenterTranslation: {
        label: "漏洞翻译源",
        type: "text",
        default:
          "https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/crowdin/zh-CN/zh_CN.json",
      },
      bugCenterTranslator: {
        label: "漏洞译者源",
        type: "text",
        default:
          "https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/translator.json",
      },
      bugCenterColor: {
        label: "漏洞颜色源",
        type: "text",
        default:
          "https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/color.json",
      },
    },
  });
  GM_registerMenuCommand("编辑配置", () => GM_config.open());
  const src = GM_config.get("bugSource");
  let tr = "";
  let tor = "";
  let c = "";

  if (src == "Github") {
    console.log("[SPXXKLP] 正在使用 Github 漏洞中心");
    tr =
      "https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/crowdin/zh-CN/zh_CN.json";
    tor =
      "https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/translator.json";
    c =
      "https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/color.json";
  } else if (src == "自定义") {
    console.log("[SPXXKLP] 正在使用自定义漏洞中心");
    tr = GM_config.get("bugCenterTranslation");
    tor = GM_config.get("bugCenterTranslator");
    c = GM_config.get("bugCenterColor");
  }

  const config = {
    translator: GM_config.get("translator"),
    bugCenter: {
      translation: tr,
      translator: tor,
      color: c,
    },
  };

  var version = "3.2.4";

  function getVersionType(url) {
    const lowerUrl = url.toLowerCase();
    if (lowerUrl.includes("snapshot")) {
      return VersionType.Snapshot;
    } else if (lowerUrl.includes("pre-release")) {
      return VersionType.PreRelease;
    } else if (lowerUrl.includes("release-candidate")) {
      return VersionType.ReleaseCandidate;
    } else if (
      lowerUrl.includes("minecraft-java-edition") &&
      !lowerUrl.includes("snapshot")
    ) {
      return VersionType.Release;
    } else if (
      lowerUrl.includes("minecraft-preview") ||
      lowerUrl.includes("minecraft-beta-preview") ||
      lowerUrl.includes("minecraft-beta")
    ) {
      return VersionType.BedrockBeta;
    } else if (lowerUrl.includes("bedrock")) {
      return VersionType.BedrockRelease;
    } else {
      return VersionType.Normal;
    }
  }

  const bugsCenter = config.bugCenter.translation;
  const bugsTranslatorsTable = config.bugCenter.translator;
  const translatorColorTable = config.bugCenter.color;
  const spxxklpVersion = version;
  const url1 = window.location.href;

  function getReleaseVersionCode(url) {
    const lowerUrl = url.toLowerCase();
    if (lowerUrl.includes("pre-release")) {
      const versionRegex = /(\d+)-(\d+)-(\d*)(.+)-release/;
      const match = lowerUrl.match(versionRegex);
      if (match && match.length >= 3) {
        const Version1 = match[1];
        const Version2 = match[2];
        const Version3 = match[3];
        if (Version3 == "") {
          const formattedVersion = `${Version1}.${Version2}`;
          return formattedVersion;
        } else {
          const formattedVersion = `${Version1}.${Version2}.${Version3}`;
          return formattedVersion;
        }
      }
    } else if (lowerUrl.includes("release-candidate")) {
      const versionRegex = /(\d+)-(\d+)-(\d*)(.+)-candidate/;
      const match = lowerUrl.match(versionRegex);
      if (match && match.length >= 3) {
        const Version1 = match[1];
        const Version2 = match[2];
        const Version3 = match[3];
        if (Version3 == "") {
          const formattedVersion = `${Version1}.${Version2}`;
          return formattedVersion;
        } else {
          const formattedVersion = `${Version1}.${Version2}.${Version3}`;
          return formattedVersion;
        }
      }
    }
  }

  function getVersionCode(url) {
    const lowerUrl = url.toLowerCase();
    if (lowerUrl.includes("snapshot")) {
      const versionRegex = /-([0-9a-zA-Z]+)$/;
      const match = url.match(versionRegex);
      if (match && match[1]) {
        return match[1];
      }
    } else if (lowerUrl.includes("pre-release")) {
      const versionRegex = /(\d+)-(\d+)-(\d*)(.+)-release-(\d+)/;
      const match = lowerUrl.match(versionRegex);
      if (match && match.length >= 4) {
        const Version1 = match[1];
        const Version2 = match[2];
        const Version3 = match[3];
        const Version4 = match[5];
        if (Version3 == "") {
          const formattedVersion = `${Version1}.${Version2}-pre${Version4}`;
          return formattedVersion;
        } else {
          const formattedVersion = `${Version1}.${Version2}.${Version3}-pre${Version4}`;
          return formattedVersion;
        }
      }
    } else if (lowerUrl.includes("release-candidate")) {
      const versionRegex = /(\d+)-(\d+)-(\d*)(.+)-candidate-(\d+)/;
      const match = lowerUrl.match(versionRegex);
      if (match && match.length >= 4) {
        const Version1 = match[1];
        const Version2 = match[2];
        const Version3 = match[3];
        const Version4 = match[5];
        if (Version3 == "") {
          const formattedVersion = `${Version1}.${Version2}-rc${Version4}`;
          return formattedVersion;
        } else {
          const formattedVersion = `${Version1}.${Version2}.${Version3}-rc${Version4}`;
          return formattedVersion;
        }
      }
    } else if (lowerUrl.includes("minecraft-beta-preview")) {
      const versionRegex = /-beta-preview-(\d+)-(\d+)-(\d+)-(\d+)/;
      const match = lowerUrl.match(versionRegex);
      if (match && match.length >= 5) {
        const Version1 = match[1];
        const Version2 = match[2];
        const Version3 = match[3];
        const Version4 = match[4];
        const formattedVersion = `${Version1}.${Version2}.${Version3}.${Version4}`;
        return formattedVersion;
      }
    } else if (
      lowerUrl.includes("minecraft-preview") &&
      !lowerUrl.includes("beta")
    ) {
      const versionRegex = /-preview-(\d+)-(\d+)-(\d+)-(\d+)/;
      const match = lowerUrl.match(versionRegex);
      if (match && match.length >= 5) {
        const Version1 = match[1];
        const Version2 = match[2];
        const Version3 = match[3];
        const Version4 = match[4];
        const formattedVersion = `${Version1}.${Version2}.${Version3}.${Version4}`;
        return formattedVersion;
      }
    } else if (
      lowerUrl.includes("minecraft-beta") &&
      !lowerUrl.includes("preview")
    ) {
      const versionRegex = /-beta-(\d+)-(\d+)-(\d+)-(\d+)/;
      const match = lowerUrl.match(versionRegex);
      if (match && match.length >= 5) {
        const Version1 = match[1];
        const Version2 = match[2];
        const Version3 = match[3];
        const Version4 = match[4];
        const formattedVersion = `${Version1}.${Version2}.${Version3}.${Version4}`;
        return formattedVersion;
      }
    }
  }

  function getVersionCount(url) {
    const lowerUrl = url.toLowerCase();
    if (lowerUrl.includes("pre-release")) {
      const versionRegex = /-release-(\d+)/;
      const match = lowerUrl.match(versionRegex);
      if (match && match[1]) {
        return match[1];
      }
    } else if (lowerUrl.includes("release-candidate")) {
      const versionRegex = /-candidate-(\d+)/;
      const match = lowerUrl.match(versionRegex);
      if (match && match[1]) {
        return match[1];
      }
    }
  }

  const link = document.createElement("link");
  link.rel = "stylesheet";
  link.href =
    "https://www.minecraft.net/etc.clientlibs/minecraftnet/clientlibs/clientlib-site/resources/fonts/MinecraftTen.woff";
  document.head.appendChild(link);

  let releaseversioncode = getReleaseVersionCode(url1);
  let versioncode = getVersionCode(url1);
  let versioncount = getVersionCount(url1);
  function getHeader(articleType, type) {
    if (articleType.toLowerCase() !== "news") {
      return `[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;
    }

    switch (type) {
      case VersionType.Snapshot:
        return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4][b]每周快照[/b]是 Minecraft Java 版的测试机制,主要用于下一个正式版的特性预览。[/size]
[color=#f44336][size=5]|[/size][/color][size=4]然而,[b]每周快照[/b]主要用于新特性展示,通常存在大量漏洞。因此对于普通玩家建议仅做[color=Red][b]测试尝鲜[/b][/color]用。在快照中打开存档前请务必[color=Red][b]进行备份[/b][/color]。[b]适用于正式版的 Mod 不兼容快照,且大多数 Mod 都不对每周快照提供支持[/b]。 [/size]
[color=#f44336][size=5]|[/size][/color][size=4]Minecraft Java 版 <正式版版本号> 仍未发布,${versioncode} 为其第 <计数> 个预览版。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;

      case VersionType.PreRelease:
        return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4][b]预发布版[/b]是 Minecraft Java 版的测试机制,如果该版本作为正式版发布,那么预发布版的游戏文件将与启动器推送的正式版完全相同。[/size]
[color=#f44336][size=5]|[/size][/color][size=4]然而,预发布版主要用于服主和 Mod 制作者的预先体验,如果发现重大漏洞,该预发布版会被新的预发布版代替。因此建议普通玩家[color=Red]持观望态度[/color]。 [/size]
[color=#f44336][size=5]|[/size][/color][size=4]Minecraft Java 版 ${releaseversioncode} 仍未发布,${versioncode} 为其第 ${versioncount} 个预发布版。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;

      case VersionType.ReleaseCandidate:
        return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指运行在 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4][b]候选版[/b]是 Minecraft Java 版正式版的候选版本,如果发现重大漏洞,该候选版会被新的候选版代替。如果一切正常,该版本将会作为正式版发布。[/size]
[color=#f44336][size=5]|[/size][/color][size=4]候选版已可供普通玩家进行抢鲜体验,但仍需当心可能存在的漏洞。[/size]
[color=#f44336][size=5]|[/size][/color][size=4]Minecraft Java 版 ${releaseversioncode} 仍未发布,${versioncode} 为其第 ${versioncount} 个候选版。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;

      case VersionType.Release:
        return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指运行在 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size]
[color=#f44336][size=5]|[/size][/color][size=4][b]正式版[/b]是 Minecraft Java 版经过一段时间的预览版测试后得到的稳定版本,也是众多纹理、Mod 与服务器插件会逐渐跟进的版本。官方启动器也会第一时间进行推送。 [/size]
[color=#f44336][size=5]|[/size][/color][size=4]建议玩家与服主关注其相关服务端、Mod 与插件的更新,迎接新的正式版吧!专注于单人原版游戏的玩家可立即更新,多人游戏玩家请关注您所在服务器的通知。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;

      case VersionType.BedrockRelease:
        return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft 基岩版[/b]是指运行在移动平台(Android、iOS)、Windows 10/11、主机(Xbox One、Switch、PlayStation 4/5)上,使用「基岩引擎」(C++语言)开发的 Minecraft 版本。[/size]
[color=#f44336][size=5]|[/size][/color][size=4][b]正式版[/b]是 Minecraft 基岩版经过一段时间的测试版测试之后得到的稳定版本,也是众多纹理、附加包和 Realms 会逐渐跟进的版本。与此同时 Google Play、Microsoft Store 等官方软件商店也会推送此次更新。 [/size]
[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;

      case VersionType.BedrockBeta:
        return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft 基岩版[/b]是指运行在移动平台(Android、iOS)、Windows 10/11、主机(Xbox One、Switch、PlayStation 4/5)上,使用「基岩引擎」(C++语言)开发的 Minecraft 版本。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4][b]测试版[/b]是 Minecraft 基岩版的测试机制,主要用于下一个正式版的特性预览。[/size]
[color=#f44336][size=5]|[/size][/color][size=4][b]然而,测试版主要用于新特性展示,通常存在大量漏洞。因此对于普通玩家建议仅做测试尝鲜用。使用测试版打开存档前请务必备份。适用于正式版的领域服务器与测试版不兼容。[/b] [/size]
[color=#f44336][size=5]|[/size][/color][size=4]如果在测试版中遇到旧版存档无法使用的问题,测试版将允许你将存档上传以供开发团队查找问题。[/size]
[color=#f44336][size=5]|[/size][/color][size=4]Minecraft 基岩版 <正式版版本号> 仍未发布,Beta & Preview ${versioncode} 为其第 <计数> 个测试版。[/size]
[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;

      case VersionType.Normal:
      default:
        return `[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n`;
    }
  }
  function getFooter(articleType, type) {
    const time = new Date();

    function padTime(time) {
      return time.toString().padStart(2, "0");
    }

    function toHoursAndMinutes(totalMinutes) {
      const m = Math.abs(totalMinutes);
      const minutes = m % 60;
      const hours = Math.floor(m / 60);
      return `${totalMinutes < 0 ? "+" : "-"}${padTime(hours)}${padTime(
        minutes
      )}`;
    }

    const poweredBy = `\n[align=center][size=1][color=Silver]Powered by SPXXKLP ${spxxklpVersion} with love
Converted at ${time.getFullYear()}-${
      padTime(time.getMonth() + 1) // why +1 javascript
    }-${padTime(time.getDate())} ${padTime(time.getHours())}:${padTime(
      time.getMinutes()
    )} ${toHoursAndMinutes(time.getTimezoneOffset())}[/color][/size][/align]`;

    /*Same contents,change if necessary.**/

    switch (type) {
      case VersionType.Snapshot:
        return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list]`;

      case VersionType.PreRelease:
        return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list]`;

      case VersionType.ReleaseCandidate:
        return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list]`;

      case VersionType.Release:
        return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list]`;

      case VersionType.BedrockRelease:
        return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list]`;

      case VersionType.BedrockBeta:
        return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list]`;

      case VersionType.Normal:
        return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list]`;
    }
  }
  let VersionType;

  (function (VersionType) {
    VersionType[(VersionType["Snapshot"] = 1)] = "Snapshot";
    VersionType[(VersionType["PreRelease"] = 2)] = "PreRelease";
    VersionType[(VersionType["ReleaseCandidate"] = 3)] = "ReleaseCandidate";
    VersionType[(VersionType["Release"] = 4)] = "Release";
    VersionType[(VersionType["Normal"] = 5)] = "Normal";
    VersionType[(VersionType["BedrockBeta"] = 6)] = "BedrockBeta";
    VersionType[(VersionType["BedrockRelease"] = 7)] = "BedrockRelease";
  })(VersionType || (VersionType = {}));

  const translators = {
    headings: (input, ctx) => {
      return translator(input, ctx, [
        // Minecraft.net titles
        [/Block of the Week: /gi, "本周方块:"],
        [/Taking Inventory: /gi, "背包盘点:"],
        [/Around the Block: /gi, "群系漫游:"],
        [/A Minecraft Java Snapshot/gi, "Minecraft Java版 快照"],
        [/A Minecraft Java Pre-Release/gi, "Minecraft Java版 预发布版"],
        [/A Minecraft Java Release Candidate/gi, "Minecraft Java版 候选版本"], // Bedrock Edition titles
        [
          /Minecraft Beta (?:-|——) (.*?) \((.*?)\)/gi,
          "Minecraft 基岩版 Beta $1($2)",
        ],
        [
          /Minecraft Beta & Preview - (.*?)/g,
          "Minecraft 基岩版 Beta & Preview $1",
        ],
        [/Minecraft (?:-|——) (.*?) \(Bedrock\)/gi, "Minecraft 基岩版 $1"],
        [
          /Minecraft (?:-|——) (.*?) \((.*?) Only\)/gi,
          "Minecraft 基岩版 $1(仅$2)",
        ],
        [/Minecraft (?:-|——) (.*?) \((.*?)\)/gi, "Minecraft 基岩版 $1(仅$2)"], // BE subheadings
        [/Marketplace/gi, "市场"],
        [/Data-Driven/gi, "数据驱动"],
        [/Graphical/gi, "图像"],
        [/Vanilla /gi, "原版"],
        [/Player/gi, "玩家"],
        [/Experimental /gi, "实验性"],
        [/Mobs/gi, "生物"],
        [/Features and Bug Fixes/gi, "特性和漏洞修复"],
        [/ADVANCEMENTS/gi, "进度"],
        [/Accessibility/gi, "辅助功能"],
        [/Gameplay/gi, "玩法"],
        [/Items/gi, "物品"],
        [/Blocks/gi, "方块"],
        [/User Interface/gi, "用户界面"],
        [/Commands/gi, "命令"],
        [/Known Issues/gi, "已知问题"],
        [/Character Creator/gi, "角色创建器"],
        [/ Components/gi, "组件"],
        [/General/gi, "通用"],
        [/Technical Experimental Updates/gi, "实验性技术性更新"],
        [/Gametest Framework/gi, "Gametest 框架"],
        [/Gametest Framework (experimental)/gi, "Gametest 框架(实验性)"], // JE subheadings
        [/Minecraft Snapshot /gi, "Minecraft 快照 "],
        [/ Pre-Release /gi, "-pre"],
        [/ Release Candidate /gi, "-rc"],
        [/Get the Release Candidate/gi, "获取预发布版本"],
        [/Get the Release/gi, "获取正式版"],
        [/Get the Pre-Release/gi, "获取候选版本"],
        [/Get the Snapshot/gi, "获取快照版本"],
        [/New Features in ([^\r\n]+)/gi, "$1 的新增特性"],
        [/Technical changes in ([^\r\n]+)/gi, "$1 的技术性修改"],
        [/Changes in ([^\r\n]+)/gi, "$1 的修改内容"],
        [/Fixed bugs in ([^\r\n]+)/gi, "$1 修复的漏洞"],
        [/STABILITY AND PERFORMANCE/gi, "性能与稳定性"],
        [/FEATURES AND BUG FIXES/gi, "特性和漏洞修复"],
        [/LOOT/gi, "战利品"],
        [/PARITY/gi, "趋同"],
        [/Components/gi, "组件"],
        [/ADD-ONS AND SCRIPT ENGINE/gi, "附加包和脚本引擎"],
        [/DRESSING ROOM/gi, "更衣室"],
        [/Item/gi, "物品"],
        [/CHANGES/gi, "改动"],
        [/SOUNDS/gi, "音效"],
        [/DATA PACK VERSION/gi, "数据包版本"],
        [/PREDICATES/gi, "谓词"],
        [/ PREDICATE/gi, "谓词"],
        [/EFFECT/gi, "效果"],
        [/COMMAND/gi, "命令"],
        [/ATTRIBUTE/gi, "属性"],
        [/BLOCK/gi, "方块"],
        [/ENTITY/gi, "实体"],
        [/ENCHANTMENTS/gi, "附魔"],
        [/ TAGS/gi, "标签"],
        [/TAGS/gi, "标签"],
        [/TYPE/gi, "类型"],
        [/MUSIC/gi, "音乐"],
        [/GAME TIPS/gi, "游戏提示"],
        [/NEW FEATURES/gi, "新特性"],
        [/NEW /gi, "新的"],
        [/USER INTERFACE/gi, "用户界面"],
        [/EDITOR/gi, "编辑器"],
        [/FIXES/gi, "修复"],
        [/IMPROVEMENTS/gi, "改进"],
        [/RESOURCE PACK VERSION/gi, "资源包版本"],
        [/SHADERS/gi, "着色器"],
        [/PARTICLES/gi, "粒子效果"],
        [/TOUCH CONTROLS/gi, "触控"],
        [/TECHNICAL UPDATES/gi, "技术性更新"],
        [/ TABLES/gi, "表"],
        [/PROJECTILES/gi, "弹射物"],
        [/STRUCTURES/gi, "结构"],
        [/ENTITIES/gi, "实体"],
        [/FUNCTIONS/gi, "函数"],
      ]);
    },
    imgCredits: (input, ctx) => {
      return translator(input, ctx, [
        // Creative Commons image credits
        [/Image credit:/gi, "图片来源:"],
        [/CC BY-NC-ND/gi, "知识共享 署名-非商业性使用-禁止演绎"],
        [/CC BY-NC-SA/gi, "知识共享 署名-非商业性使用-相同方式共享"],
        [/CC BY-NC/gi, "知识共享 署名-非商业性使用"],
        [/CC BY-ND/gi, "知识共享 署名-禁止演绎"],
        [/CC BY-SA/gi, "知识共享 署名-相同方式共享"],
        [/CC BY/gi, "知识共享 署名"],
        [/Public Domain/gi, "公有领域"],
      ]);
    },
    punctuation: (input, ctx) => {
      return translator(
        input,
        ctx,
        [
          [/\[i]/gi, "[font=楷体]"],
          [/\[\/i]/g, "[/font]"],
          ...(ctx.disablePunctuationConverter
            ? []
            : [
                [/,( |$)/g, ","],
                [/!( |$)/g, "!"],
                [/\.\.\.( |$)/g, "…"],
                [/\.( |$)/g, "。"],
                [/\?( |$)/g, "?"],
                [/( |^)-( |$)/g, " —— "],
              ]),
        ],
        (input) => {
          return quoteTreatment(input, [["“", "”", /"/]]);
        }
      );
    },
    code: (input, ctx) => {
      return quoteTreatment(input, [
        [
          '[backcolor=#f1edec][color=Silver][font=SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace][/font][/color][/backcolor]',
          "`",
          /`/,
        ],
      ]);
    },
  };
  function translate(input, ctx, type) {
    if (typeof type === "string") {
      type = [type];
    }

    for (const t of type) {
      input = translators[t](input, ctx);
    }

    return input;
  }

  function quoteTreatment(input, quoteArrays) {
    for (const quoteArray of quoteArrays) {
      const split = input.split(quoteArray[2]);
      input = "";

      for (let i = 0; i < split.length - 1; i++) {
        const element = split[i];
        input += element + quoteArray[i % 2];
      }

      input += split[split.length - 1];
    }

    return input;
  }

  function translator(input, ctx, mappings, treatment = (input) => input) {
    // REPLACE!!!!1
    for (const mapping of mappings) {
      input = input.replace(mapping[0], mapping[1]);
    }

    treatment(input, ctx);
    return input;
  }

  const converters = {
    /**
     * Converts a ChildNode to a BBCode string according to the type of the node.
     */
    convert: async (node, ctx) => {
      if (node.classList?.contains("spxxklp-userscript-ignored")) {
        return "";
      } // Listing all possible elements in the document

      switch (node.nodeName) {
        case "A":
          return converters.a(node, ctx);

        case "B":
        case "STRONG":
          return converters.strong(node, ctx);

        case "BLOCKQUOTE":
          return converters.blockquote(node, ctx);

        case "BR":
          return converters.br();

        case "CITE":
          return converters.cite(node, ctx);

        case "CODE":
          return converters.code(node, ctx);

        case "DIV":
        case "SECTION":
          return converters.div(node, ctx);

        case "DD":
          return converters.dd(node, ctx);

        case "DL":
          return converters.dl(node, ctx);

        case "DT":
          return converters.dt();

        case "EM":
          return converters.em(node, ctx);

        case "H1":
          return converters.h1(node, ctx);

        case "H2":
          return converters.h2(node, ctx);

        case "H3":
          return converters.h3(node, ctx);

        case "H4":
          return converters.h4(node, ctx);

        case "I":
          return converters.i(node, ctx);

        case "IMG":
          return converters.img(node);

        case "LI":
          return converters.li(node, ctx);

        case "OL":
          return converters.ol(node, ctx);

        case "P":
          return converters.p(node, ctx);

        case "PICTURE":
          return converters.picture(node, ctx);

        case "PRE":
          return converters.pre(node, ctx);

        case "SPAN":
          return converters.span(node, ctx);

        case "TABLE":
          return converters.table(node, ctx);

        case "TBODY":
          return converters.tbody(node, ctx);

        case "TH":
        case "TD":
          return converters.td(node, ctx);

        case "TR":
          return converters.tr(node, ctx);

        case "UL":
          return converters.ul(node, ctx);

        case "#text":
          if (node) {
            if (ctx.multiLineCode) {
              return node.textContent ? node.textContent : "";
            } else
              return node.textContent
                .replace(/[\n\r\t]+/g, "")
                .replace(/\s{2,}/g, "");
          } else {
            return "";
          }

        case "H5":
          return converters.h5(node, ctx);

        case "BUTTON":
        case "NAV":
        case "svg":
        case "SCRIPT":
          if (node) {
            return node.textContent ? node.textContent : "";
          } else {
            return "";
          }
        case "FIGURE":
          return converters.figure(node, ctx);

        default:
          console.warn(`Unknown type: '${node.nodeName}'.`);

          if (node) {
            return node.textContent ? node.textContent : "";
          } else {
            return "";
          }
      }
    },

    /**
     * Convert child nodes of an HTMLElement to a BBCode string.
     */
    recurse: async (ele, ctx) => {
      let ans = "";

      if (!ele) {
        return ans;
      }

      for (const child of Array.from(ele.childNodes)) {
        ans += await converters.convert(child, ctx);
      }

      return ans;
    },
    a: async (anchor, ctx) => {
      const url = resolveUrl(anchor.href);
      let ans;

      if (url) {
        ans = `[url=${url}][color=#388d40][u]${await converters.recurse(
          anchor,
          ctx
        )}[/u][/color][/url]`;
      } else {
        ans = await converters.recurse(anchor, ctx);
      }

      return ans;
    },
    blockquote: async (ele, ctx) => {
      const prefix = "";
      const suffix = "";
      const ans = `${prefix}${await converters.recurse(ele, ctx)}${suffix}`;
      return ans;
    },
    br: async () => {
      const ans = "\n";
      return ans;
    },
    cite: async (ele, ctx) => {
      const prefix = "—— ";
      const suffix = "";
      const ans = `${prefix}${await converters.recurse(ele, ctx)}${suffix}`;
      return ans;
    },
    code: async (ele, ctx) => {
      const prefix = ctx.multiLineCode
        ? "[code]"
        : "[backcolor=#f1edec][color=#7824c5][font=SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace]";
      const suffix = ctx.multiLineCode
        ? "[/code]"
        : "[/font][/color][/backcolor]";
      const ans = `${prefix}${await converters.recurse(ele, {
        ...ctx,
        disablePunctuationConverter: true,
      })}${suffix}`;
      return ans;
    },
    div: async (ele, ctx) => {
      let ans = await converters.recurse(ele, ctx);

      if (ele.classList.contains("text-center")) {
        ans = `[align=center]${ans}[/align]\n`;
      } else if (ele.classList.contains("article-image-carousel")) {
        /*const prefix = `[/indent][/indent][album]\n`;
        const suffix = `\n[/album][indent][indent]\n`;*/

        const slides = [];
        const findSlides = async (ele) => {
          if (ele.classList.contains("slick-cloned")) {
            return;
          }

          if (
            ele.nodeName === "IMG" &&
            ele.classList.contains("article-image-carousel__image")
          ) {
            slides.push([resolveUrl(ele.src), " "]);
          } else if (
            ele.nodeName === "DIV" &&
            ele.classList.contains("article-image-carousel__caption")
          ) {
            if (slides.length > 0) {
              slides[slides.length - 1][1] = `[b]${await converters.recurse(
                ele,
                ctx
              )}[/b]`;
            }
          } else {
            for (const child of Array.from(ele.childNodes)) {
              if (child.nodeName === "DIV" || child.nodeName === "IMG") {
                await findSlides(child);
              }
            }
          }
        };

        await findSlides(ele);

        /*if (shouldUseAlbum(slides)) {
          ans = `${prefix}${slides.map(([url, caption]) => `[aimg=${url}]${caption}[/aimg]`).join('\n')}${suffix}`;
        } else */ if (slides.length > 0) {
          ans = `[align=center]${slides
            .map(([url, caption]) => `[img]${url}[/img]\n${caption}`)
            .join("\n")}[/align]\n`;
        } else {
          ans = "";
        }
      } else if (ele.classList.contains("video")) {
        // Video.
        ans =
          "\n[align=center]<无法获取的视频,如有可用视频源,请在此处插入>\n<对于B站视频,可使用 [bilibili] 代码>[/align]\n";
      } else if (
        ele.classList.contains("quote") ||
        ele.classList.contains("attributed-quote")
      ) {
        ans = `\n[quote]\n${ans}\n[/quote]\n`;
      } else if (ele.classList.contains("article-social")) {
        // End of the content.
        ans = "";
      } else if (ele.classList.contains("modal")) {
        // Unknown useless content
        ans = "";
      }

      return ans;
    },
    dt: async () => {
      // const ans = `${converters.rescure(ele)}:`
      // return ans
      return "";
    },
    dl: async (ele, ctx) => {
      const ans = `\n\n${await converters.recurse(
        ele,
        ctx
      )}\n【本文排版借助了:[url=https://github.com/cinder0601/SPXXKLP][color=#388d40][u]SPXXKLP[/u][/color][/url] 用户脚本 v${spxxklpVersion}】\n\n`;
      return ans;
    },
    dd: async (ele, ctx) => {
      let ans = "";

      if (ele.classList.contains("pubDate")) {
        // Published:
        // `pubDate` is like '2019-03-08T10:00:00.876+0000'.
        const date = ele.attributes.getNamedItem("data-value");

        if (date) {
          ans = `[b]【${ctx.translator} 译自[url=${
            ctx.url
          }][color=#388d40][u]官网 ${date.value.slice(
            0,
            4
          )} 年 ${date.value.slice(5, 7)} 月 ${date.value.slice(
            8,
            10
          )} 日发布的 ${ctx.title}[/u][/color][/url];原作者 ${
            ctx.author
          }】[/b]`;
        } else {
          ans = `[b]【${ctx.translator} 译自[url=${ctx.url}][color=#388d40][u]官网 * 年 * 月 * 日发布的 ${ctx.title}[/u][/color][/url]】[/b]`;
        }
      } else {
        // Written by:
        ctx.author = await converters.recurse(ele, ctx);
      }

      return ans;
    },
    em: async (ele, ctx) => {
      const ans = `[i]${await converters.recurse(ele, ctx)}[/i]`;
      return ans;
    },
    h1: async (ele, ctx) => {
      const prefix = "[size=6][b]";
      const suffix = "[/b][/size]";
      const processNode = async (node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          return await converters[node.tagName.toLowerCase()](node, ctx);
        } else if (node.nodeType === Node.TEXT_NODE) {
          return node.nodeValue;
        }
      };
      const rawInnerArray = await Promise.all(
        Array.from(ele.childNodes).map(processNode)
      );
      const rawInner = rawInnerArray.join("");
      const inner = makeUppercaseHeader(rawInner);
      const ans = `${prefix}[color=Silver]${usingSilver(inner).replace(
        /[\n\r]+/g,
        " "
      )}[/color]${suffix}\n${prefix}${translate(`${inner}`, ctx, [
        "headings",
        "punctuation",
      ]).replace(/[\n\r]+/g, " ")}${suffix}\n\n`;
      return ans;
    },
    h2: async (ele, ctx) => {
      if (isBlocklisted(ele.textContent)) return "";
      const prefix = "[size=5][b]";
      const suffix = "[/b][/size]";
      const processNode = async (node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          return await converters[node.tagName.toLowerCase()](node, ctx);
        } else if (node.nodeType === Node.TEXT_NODE) {
          return node.nodeValue;
        }
      };
      const rawInnerArray = await Promise.all(
        Array.from(ele.childNodes).map(processNode)
      );
      const rawInner = rawInnerArray.join("");
      const inner = makeUppercaseHeader(rawInner);
      const ans = `\n${prefix}[color=Silver]${usingSilver(inner).replace(
        /[\n\r]+/g,
        " "
      )}[/color]${suffix}\n${prefix}${translate(`${inner}`, ctx, [
        "headings",
        "punctuation",
      ]).replace(/[\n\r]+/g, " ")}${suffix}\n\n`;
      return ans;
    },
    h3: async (ele, ctx) => {
      const prefix = "[size=4][b]";
      const suffix = "[/b][/size]";
      const processNode = async (node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          return await converters[node.tagName.toLowerCase()](node, ctx);
        } else if (node.nodeType === Node.TEXT_NODE) {
          return node.nodeValue;
        }
      };
      const rawInnerArray = await Promise.all(
        Array.from(ele.childNodes).map(processNode)
      );
      const rawInner = rawInnerArray.join("");
      const inner = makeUppercaseHeader(rawInner);
      const ans = `\n${prefix}[color=Silver]${usingSilver(inner).replace(
        /[\n\r]+/g,
        " "
      )}[/color]${suffix}\n${prefix}${translate(`${inner}`, ctx, [
        "headings",
        "punctuation",
      ]).replace(/[\n\r]+/g, " ")}${suffix}\n\n`;
      return ans;
    },
    h4: async (ele, ctx) => {
      const prefix = "[size=3][b]";
      const suffix = "[/b][/size]";
      const processNode = async (node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          return await converters[node.tagName.toLowerCase()](node, ctx);
        } else if (node.nodeType === Node.TEXT_NODE) {
          return node.nodeValue;
        }
      };
      const rawInnerArray = await Promise.all(
        Array.from(ele.childNodes).map(processNode)
      );
      const rawInner = rawInnerArray.join("");
      const inner = makeUppercaseHeader(rawInner);
      const ans = `\n${prefix}[color=Silver]${usingSilver(inner).replace(
        /[\n\r]+/g,
        " "
      )}[/color]${suffix}\n${prefix}${inner}${suffix}\n\n`;
      return ans;
    },
    h5: async (ele, ctx) => {
      const prefix = "[size=2][b]";
      const suffix = "[/b][/size]";
      const processNode = async (node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          return await converters[node.tagName.toLowerCase()](node, ctx);
        } else if (node.nodeType === Node.TEXT_NODE) {
          return node.nodeValue;
        }
      };
      const rawInnerArray = await Promise.all(
        Array.from(ele.childNodes).map(processNode)
      );
      const rawInner = rawInnerArray.join("");
      const inner = makeUppercaseHeader(rawInner);
      const ans = `\n${prefix}[color=Silver]${usingSilver(inner).replace(
        /[\n\r]+/g,
        " "
      )}[/color]${suffix}\n${prefix}${inner}${suffix}\n\n`;
      return ans;
    },
    i: async (ele, ctx) => {
      const ans = `[i]${await converters.recurse(ele, ctx)}[/i]`;
      return ans;
    },
    img: async (img) => {
      let host = location.host;

      let w;
      let h;

      if (img.classList.contains("attributed-quote__image")) {
        // for in-quote avatar image
        h = 92;
        w = 53;
      } else if (img.classList.contains("mr-3")) {
        // for attributor avatar image
        h = 121;
        w = 82;
      }

      const prefix = w && h ? `[img=${w},${h}]` : "[img]";
      const imgUrl = resolveUrl(img.src);
      if (imgUrl === "") return ""; // in case of empty image

      let ans;
      if (host == "www.minecraft.net") {
        ans = `[align=center]${prefix}${imgUrl}[/img][/align]\n`; //Left aligning is too ugly.
      } else {
        ans = `[align=center]${prefix}${imgUrl}[/img][/align]\n`;
      }

      return ans;
    },
    li: async (ele, ctx) => {
      let ans;
      let nestedList = false;

      for (const child of ele.childNodes) {
        if (child.nodeName === "OL" || child.nodeName === "UL") {
          nestedList = true;
        }
      }

      if (nestedList) {
        // Nested lists.
        let theParagragh = "";
        let theList = "";
        let addingList = false;

        for (let i = 0; i < ele.childNodes.length - 1; i++) {
          let nodeName = ele.childNodes[i].nodeName;

          if (nodeName === "OL" || nodeName === "UL") {
            addingList = true;
          }

          if (!addingList) {
            const paragraghNode = await converters.convert(ele.childNodes[i], {
              ...ctx,
              inList: true,
            });
            theParagragh = `${theParagragh}${paragraghNode}`;
          } else {
            const listNode = await converters.convert(ele.childNodes[i], {
              ...ctx,
              inList: true,
            });
            theList = `${theList}${listNode}`;
          }
        }

        ans = `[*][color=Silver]${usingSilver(
          theParagragh
        )}[/color]\n[*]${translate(
          translateBugs(theParagragh, ctx),
          ctx,
          "code"
        )}\n${theList}`;
      } else if (isBlocklisted(ele.textContent)) {
        return "";
      } else {
        const inner = await converters.recurse(ele, { ...ctx, inList: true });
        ans = `[*][color=Silver]${usingSilver(inner)}[/color]\n[*]${translate(
          translateBugs(inner, ctx),
          ctx,
          "code"
        )}\n`;
      }

      return ans;
    },
    ol: async (ele, ctx) => {
      const inner = await converters.recurse(ele, ctx);
      const ans = `[list=1]\n${inner}[/list]\n`;
      return ans;
    },
    p: async (ele, ctx) => {
      const processNode = async (node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          const converter = converters[node.tagName.toLowerCase()];
          if (converter) {
            return await converter(node, ctx);
          }
        } else if (node.nodeType === Node.TEXT_NODE) {
          return node.nodeValue;
        }
      };

      let inner = await converters.recurse(ele, ctx);
      inner = inner ? inner.trim() : "";

      let ans;

      if (inner === "") {
        return "";
      }

      if (ele.style.textAlign === "center") {
        ans = `[align=center][size=2][color=Silver]${usingSilver(
          inner
        )}[/color][/size]\n${translate(inner, ctx, [
          "punctuation",
          "imgCredits",
        ])}[/align]\n`;
      } else if (ele.classList.contains("lead")) {
        ans = `[size=4][b][size=2][color=Silver]${inner}[/color][/size][/b][/size]\n[size=4][b]${translate(
          inner,
          ctx,
          "headings"
        )}[/b][/size]\n`;
      } else if (
        ele.querySelector("strong") !== null &&
        ele.querySelector("strong").textContent === "Posted:"
      ) {
        return "";
      } else if (isBlocklisted(inner)) {
        return "";
      } else if (ele.innerHTML.trim() === "&nbsp;") {
        return "";
      } else if (
        /\s{0,}/.test(inner) &&
        ele.querySelectorAll("img").length === 1
      ) {
        return inner;
      } else {
        if (ctx.inList) {
          ans = inner;
        } else {
          ans = `[size=2][color=Silver]${usingSilver(
            inner
          )}[/color][/size]\n${translate(inner, ctx, [
            "punctuation",
            "imgCredits",
          ])}\n\n`;
        }
      }

      return ans;
    },
    picture: async (ele, ctx) => {
      const ans = await converters.recurse(ele, ctx);
      return ans;
    },
    figure: async (ele, ctx) => {
      const ans = await converters.recurse(ele, ctx);
      return ans;
    },
    pre: async (ele, ctx) => {
      const ans = await converters.recurse(ele, {
        ...ctx,
        multiLineCode: true,
      });
      return ans;
    },
    span: async (ele, ctx) => {
      const ans = await converters.recurse(ele, ctx);

      if (ele.classList.contains("MC_Effect_TextHighlightA")) {
        // Special for MC_Effect_TextHighlightA element.
        const textContent = await converters.recurse(ele, ctx);
        const prefix =
          "[backcolor=#f1edec][color=#7824c5][font=SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace]";
        const suffix = "[/font][/color][/backcolor]";
        return `${prefix}${textContent}${suffix}`;
      } else if (ele.classList.contains("MC_Effect_TextHighlightB")) {
        // Special for MC_Effect_TextHighlightB element.
        const textContent = await converters.recurse(ele, ctx);
        const prefix =
          "[backcolor=#f1edec][color=#7824c5][font=SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace]";
        const suffix = "[/font][/color][/backcolor]";
        return `${prefix}${textContent}${suffix}`;
      } else if (ele.classList.contains("bedrock-server")) {
        // Inline code.
        const prefix =
          "[backcolor=#f1edec][color=#7824c5][font=SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace]";
        const suffix = "[/font][/color][/backcolor]";
        return `${prefix}${await converters.recurse(ele, {
          ...ctx,
          disablePunctuationConverter: true,
        })}${suffix}`;
      } else if (ele.classList.contains("strikethrough")) {
        // Strikethrough text.
        const prefix = "[s]";
        const suffix = "[/s]";
        return `${prefix}${ans}${suffix}`;
      } else if (
        ele.childElementCount === 1 &&
        ele.firstElementChild.nodeName === "IMG"
      ) {
        // Image.
        const img = ele.firstElementChild;
        return await converters.img(img);
      }

      return ans;
    },
    strong: async (ele, ctx) => {
      const ans = `[b]${await converters.recurse(ele, ctx)}[/b]`;
      return ans;
    },
    table: async (ele, ctx) => {
      const ans = `\n[table]\n${await converters.recurse(ele, ctx)}[/table]\n`;
      return ans;
    },
    tbody: async (ele, ctx) => {
      const ans = await converters.recurse(ele, ctx);
      return ans;
    },
    td: async (ele, ctx) => {
      const ans = `[td]${await converters.recurse(ele, ctx)}[/td]`;
      return ans;
    },
    tr: async (ele, ctx) => {
      const ans = `[tr]${await converters.recurse(ele, ctx)}[/tr]\n`;
      return ans;
    },
    ul: async (ele, ctx) => {
      const inner = await converters.recurse(ele, ctx);
      const ans = `[list]\n${inner}[/list]\n`;
      return ans;
    },
  };
  /**
   * Resolve relative URLs.
   */

  function resolveUrl(url) {
    if (url[0] === "/") {
      return `https://${location.host}${url}`;
    } else {
      return url;
    }
  }
  function usingSilver(text) {
    return text.replace(/#388d40/g, "Silver").replace(/#7824c5/g, "Silver");
  }
  function makeUppercaseHeader(header) {
    let retStr = "";
    let idx = 0;
    let bracket = 0;

    for (let i = 0; i < header.length; i++) {
      if (header[i] == "[") {
        if (bracket == 0) {
          retStr = retStr.concat(header.substring(idx, i).toUpperCase());
          idx = i;
        }

        bracket++;
      } else if (header[i] == "]") {
        if (bracket <= 1) {
          retStr = retStr.concat(header.substring(idx, i + 1));
          idx = i + 1;
        }

        bracket = Math.max(0, bracket - 1);
      }
    }

    if (bracket > 0) {
      console.error("bracket not closed!");
      retStr = retStr.concat(header.substring(idx, header.length));
    } else {
      retStr = retStr.concat(
        header.substring(idx, header.length).toUpperCase()
      );
    }

    return retStr;
  }
  /**
   * Get bugs from BugCenter.
   * Guangyao and GitHub source are down, so I deleted them.
   */

  async function getBugs() {
    return new Promise((rs, rj) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: bugsCenter,
        fetch: true,
        nocache: true,
        timeout: 7_000,
        onload: (r) => {
          try {
            rs(JSON.parse(r.responseText));
          } catch (e) {
            rj(e);
          }
        },
        onabort: () => rj(new Error("Aborted")),
        onerror: (e) => rj(e),
        ontimeout: () => rj(new Error("Time out")),
      });
    });
  }
  async function getBugsTranslators() {
    return new Promise((rs, rj) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: bugsTranslatorsTable,
        fetch: true,
        nocache: true,
        timeout: 7_000,
        onload: (r) => {
          try {
            rs(JSON.parse(r.responseText));
          } catch (e) {
            rj(e);
          }
        },
        onabort: () => rj(new Error("Aborted")),
        onerror: (e) => rj(e),
        ontimeout: () => rj(new Error("Time out")),
      });
    });
  }
  async function getTranslatorColor() {
    return new Promise((rs, rj) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: translatorColorTable,
        fetch: true,
        nocache: true,
        timeout: 7_000,
        onload: (r) => {
          try {
            rs(JSON.parse(r.responseText));
          } catch (e) {
            rj(e);
          }
        },
        onabort: () => rj(new Error("Aborted")),
        onerror: (e) => rj(e),
        ontimeout: () => rj(new Error("Time out")),
      });
    });
  }

  function markdownToBbcode(value) {
    return value.replace(
      /`([^`]+)`/g,
      "[backcolor=#f1edec][color=#7824c5][font=SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace]$1[/font][/color][/backcolor]"
    );
  }
  /**
   * Replace untranslated bugs.
   */

  function translateBugs(str, ctx) {
    if (
      str.startsWith("[url=https://bugs.mojang.com/browse/MC-") &&
      ctx.bugs != null // nullish
    ) {
      const id = str.slice(36, str.indexOf("]"));
      const data = ctx.bugs[id];

      if (data) {
        let bugColor = "#388d40";

        if (ctx.bugsTranslators[id]) {
          const bugTranslator = ctx.bugsTranslators[id];

          if (ctx.translatorColor[bugTranslator]) {
            bugColor = ctx.translatorColor[bugTranslator];
          }
        }

        const bbcode = markdownToBbcode(data);
        return `[url=https://bugs.mojang.com/browse/${id}][b][color=${bugColor}]${id}[/color][/b][/url]- ${bbcode}`;
      } else {
        return str;
      }
    } else {
      return str;
    }
  }
  /**
   * KLPBBS does NOT support [album], the shouldUseAlbum function is removed temporarily.
   */

  /*function shouldUseAlbum(slides) {
    return slides.length > 1 && slides.every(([_, caption]) => caption === ' ')
    ;
  }*/

  function isBlocklisted(text) {
    const blocklist = [
      "Information on the Minecraft Preview and Beta:",
      "While the version numbers between Preview and Beta are different, there is no difference in game content",
      "These work-in-progress versions can be unstable and may not be representative of final version quality",
      "Minecraft Preview is available on Xbox, Windows 10/11, and iOS devices. More information can be found at aka.ms/PreviewFAQ",
      "The beta is available on Android (Google Play). To join or leave the beta, see aka.ms/JoinMCBeta for detailed instructions",
    ];
    return blocklist
      .map((i) => {
        return i.replace(/\p{General_Category=Space_Separator}*/, "");
      })
      .some((block) =>
        text
          .trim()
          .trim()
          .replace(/\p{General_Category=Space_Separator}*/, "")
          .includes(block)
      );
  }

  async function minecraftNet() {
    const url = document.location.toString();

    if (url.match(/^https:\/\/www\.minecraft\.net\/(?:[a-z-]+)\/article\//)) {
      const authorContainer = document.querySelector(
        ".MC_articleHeroA_attribution_author"
      );
      const dateElement = authorContainer.querySelector("dd:nth-child(4)"); // 获取发布日期的 dd 元素

      const button = document.createElement("button");
      button.classList.add("spxxklp-userscript-ignored");
      button.innerText = "复制 BBCode";
      // 按钮样式设置
      button.style.backgroundColor = "#3C8527";
      button.style.color = "#FFFFFF";
      button.style.border = "none";
      button.style.padding = "10px 20px";
      button.style.borderRadius = "5px";
      button.style.fontSize = "16px";
      button.style.cursor = "pointer";
      button.style.transition = "background-color 0.3s ease";
      button.style.fontFamily = "MinecraftTen, sans-serif";

      button.style.width = "140px";
      button.style.height = "45px";
      button.style.textAlign = "center";
      button.style.marginLeft = "auto";

      button.onmouseover = () => {
        button.style.backgroundColor = "#52A535";
      };
      button.onmouseout = () => {
        button.style.backgroundColor = "#3C8527";
      };
      button.onclick = async () => {
        button.innerText = "处理中...";
        const bbcode = await convertMCArticleToBBCode(document, url);
        GM_setClipboard(bbcode, {
          type: "text",
          mimetype: "text/plain",
        });
        button.innerText = "已复制!";
        setTimeout(() => (button.innerText = "复制 BBCode"), 5000);
      };
      const container = document.createElement("div");
      container.id = "spxxklp-buttons";
      container.style.display = "flex";
      container.style.flexDirection = "column";
      container.style.alignItems = "flex-end";
      container.style.width = "100%";
      container.style.padding = "10px";
      container.style.boxSizing = "border-box";
      container.append(button);
      // 将按钮插入到日期下方
      dateElement.insertAdjacentElement("afterend", button);
    }
  }

  async function convertMCArticleToBBCode(
    html,
    articleUrl,
    translator = config.translator
  ) {
    const articleType = getArticleType(html);
    const versionType = getVersionType(articleUrl);
    let bugs;

    try {
      bugs = await getBugs();
    } catch (e) {
      bugs = {};
      console.error("[convertMCArticleToBBCode#getBugs]", e);
    }

    let bugsTranslators;

    try {
      bugsTranslators = await getBugsTranslators();
    } catch (e) {
      bugsTranslators = {};
      console.error("[convertMCArticleToBBCode#getBugs]", e);
    }

    let translatorColor;

    try {
      translatorColor = await getTranslatorColor();
    } catch (e) {
      translatorColor = {};
      console.error("[convertMCArticleToBBCode#getBugs]", e);
    }

    const header = getHeader(articleType, versionType);
    const heroImage = getHeroImage(html, articleType);
    const maintitle = await getMainTitle(html);
    const subtitle = await getSubTitle(html);
    let content = await getContent(html, {
      bugs,
      bugsTranslators,
      translatorColor,
      title: html.title.split(" | ").slice(0, -1).join(" | "),
      date: null,
      translator,
      url: articleUrl,
    });
    const footer = getFooter(articleType, versionType);
    const author = await getAuthor(html);
    const ans = `${header}${heroImage}\n[align=center][color=silver][size=6][b]${maintitle}[/b][/size][/color][/align][align=center][size=6][b]${maintitle}[/b][/size][/align]\n[align=center][color=silver][size=2]${subtitle}[/size][/color][/align][align=center][size=2]${subtitle}[/size][/align]\n\n${content}[b]${author}\n\n${footer}`;
    return ans;
  }
  /**
   * Returns the type of the article.
   */

  function getArticleType(html) {
    try {
      const type =
        html.getElementsByClassName("MC_articleHeroA_category")?.[0]
          ?.textContent ?? "";
      return type.toUpperCase();
    } catch (e) {
      console.error("[getArticleType]", e);
    }

    return "INSIDER";
  }

  /**
   * Get the hero image (head image) of an article as the form of a BBCode string.
   * @param html An HTML Document.
   */

  function getHeroImage(html, articleType) {
    const category = articleType
      ? `\n[backcolor=Black][color=White][font="Noto Sans",sans-serif][b]${articleType}[/b][/font][/color][/backcolor][/align]`
      : "";
    const img = html.getElementsByClassName("article-head__image")[0];

    if (!img) {
      return `\n[align=center]${category}[/align]\n`;
    }

    const src = img.src;
    const ans = `[align=center][img=1200,513]${resolveUrl(
      src
    )}[/img]\n${category}[/align]\n`;
    return ans;
  }
  /**
   * Get the content of an article as the form of a BBCode string.
   * @param html An HTML Document.
   */

  async function getSubTitle(html) {
    let con = html.getElementsByClassName(
      "MC_articleHeroA_header_container"
    )[0];
    let subtitle = con.getElementsByClassName(
      "MC_articleHeroA_header_subheadline"
    )[0].innerText;
    return subtitle;
  }
  async function getMainTitle(html) {
    let con = html.getElementsByClassName(
      "MC_articleHeroA_header_container"
    )[0];
    let maintitle = con.getElementsByClassName("MC_Heading_1")[0].innerText;
    return maintitle;
  }

  async function getAuthor(html, translator = config.translator) {
    let rawauthor = html.getElementsByClassName(
      "MC_articleHeroA_attribution_author"
    )[0];
    let authorImgUrl = rawauthor.getElementsByTagName("img")[0].src;
    let authorName = rawauthor.getElementsByTagName("dd")[0].innerText;
    let publishDate = rawauthor.getElementsByTagName("dd")[1].innerText;
    let [year, month, day] = publishDate.split("/");
    let url = window.location.href;
    let title = await getMainTitle(html);
    let ans = `\n[float=left][img]${authorImgUrl}[/img][/float]\n\n\n【${translator} 译自[url=${url}][color=#388d40][u]${authorName} ${year} 年 ${month} 月 ${day} 日发布的 ${title}[/u][/color][/url]】[/b]\n【本文排版借助了:[url=https://github.com/cinder0601/SPXXKLP][color=#388d40][u]SPXXKLP[/u][/color][/url] 用户脚本 v${spxxklpVersion}】`;
    return ans;
  }

  async function getContent(html, ctx) {
    let results = [];
    let elements = document.querySelectorAll(
      ".MC_articleGridA_container.MC_articleGridA_grid, .MC_Carousel_track_slide.MC_Theme_Vanilla.MC_Carousel_track_slide__active, .MC_Carousel_track_slide.MC_Theme_Vanilla:not(.MC_Carousel_track_slide__copy)"
    );
    let container = document.createElement("div");

    let seenElements = new Set();

    Array.from(elements).forEach((element) => {
      let identifier = element.outerHTML;
      if (!seenElements.has(identifier)) {
        seenElements.add(identifier);
        container.appendChild(element.cloneNode(true)); // cloneNode(true) 深拷贝元素
      }
    });

    let containerElements = Array.from(container.children);

    for (let i = 0; i < containerElements.length; i++) {
      let rootDiv = containerElements[i];

      let rootDivHTML = rootDiv.outerHTML.replace(
        /<h[1-5]>(?:&nbsp;|\s)*<\/h[1-5]>/g,
        ""
      );
      rootDiv = document.createElement("div");
      rootDiv.innerHTML = rootDivHTML;

      let spanElements = rootDiv.querySelectorAll("span");
      spanElements.forEach((spanElement) => {
        spanElement.innerHTML = spanElement.innerHTML.replace(/\n/g, " ");
      });

      let ans = await converters.recurse(rootDiv, ctx);
      ans = ans
        .replace(/([a-zA-Z0-9\-._])(\[[A-Za-z])/g, "$1 $2")
        .replace(/(\[\/[^\]]+?])([a-zA-Z0-9\-._])/g, "$1 $2");
      results.push(ans);
    }

    return results.join("\n\n");
  }

  function getZendesk(controlDOM, titleSlice, contentClass, versionType) {
    const button = document.createElement("a");
    button.classList.add("spxxklp-userscript-ignored", "navLink");
    button.innerText = "复制 BBCode";
    // 按钮样式设置
    button.style.backgroundColor = "#3C8527";
    button.style.color = "#FFFFFF";
    button.style.border = "none";
    button.style.padding = "5px 10px";
    button.style.borderRadius = "5px";
    button.style.fontSize = "15px";
    button.style.cursor = "pointer";
    button.style.transition = "background-color 0.3s ease";

    button.style.width = "120px";
    button.style.height = "30px";
    button.style.textAlign = "center";
    button.style.marginLeft = "auto";

    button.onmouseover = () => {
      button.style.backgroundColor = "#52A535";
    };
    button.onmouseout = () => {
      button.style.backgroundColor = "#3C8527";
    };
    button.onclick = async () => {
      button.innerText = "处理中...";
      const bbcode = await convertZendeskArticleToBBCode(
        document,
        location.href,
        config.translator,
        titleSlice,
        contentClass,
        versionType
      );
      GM_setClipboard(bbcode, {
        type: "text",
        mimetype: "text/plain",
      });
      button.innerText = "已复制!";
      setTimeout(() => (button.innerText = "复制 BBCode"), 5_000);
    };
    const container = document.createElement("div");
    container.id = "spxxklp-buttons";
    container.style.display = "flex";
    container.style.flexDirection = "column";
    container.style.alignItems = "flex-end";
    container.style.width = "100%";
    container.style.padding = "10px";
    container.style.boxSizing = "border-box";
    container.append(button);

    controlDOM(button);
  }

  async function converthelpElementsToBBCode(elements, ctx) {
    let bbcode = "";
    const seenImages = new Set();

    for (let element of elements) {
      try {
        let converted = await converters.recurse(element, ctx);
        let imgTags = converted.match(/\[img](.*?)\[\/img]/g);
        if (imgTags) {
          for (let imgTag of imgTags) {
            let imgUrl = imgTag.match(/\[img](.*?)\[\/img]/)[1];
            if (seenImages.has(imgUrl)) {
              converted = converted.replace(imgTag, "");
            } else {
              seenImages.add(imgUrl);
            }
          }
        }

        bbcode += converted + "\n";
      } catch (error) {
        console.error("Error converting content to BBCode:", error);
      }
    }
    return bbcode;
  }

  function getHelpContent(controlDOM) {
    const ctx = {
      multiLineCode: false,
      disablePunctuationConverter: false,
      translator: config.translator,
      url: window.location.href,
      inList: false,
    };
    const heading = document.getElementsByClassName("article-page-heading");
    const content = document.getElementsByClassName("article-page-body");

    const button = document.createElement("a");
    button.classList.add("spxxklp-userscript-ignored", "navLink");
    button.innerText = "复制 BBCode";
    // 按钮样式设置
    button.style.backgroundColor = "#3C8527";
    button.style.color = "#FFFFFF";
    button.style.border = "none";
    button.style.padding = "5px 10px";
    button.style.borderRadius = "5px";
    button.style.fontSize = "15px";
    button.style.cursor = "pointer";
    button.style.transition = "background-color 0.3s ease";

    button.style.width = "120px";
    button.style.height = "30px";
    button.style.textAlign = "center";
    button.style.marginLeft = "auto";

    button.onmouseover = () => {
      button.style.backgroundColor = "#52A535";
    };
    button.onmouseout = () => {
      button.style.backgroundColor = "#3C8527";
    };
    button.onclick = async () => {
      button.innerText = "处理中...";
      let bbcode = await converthelpElementsToBBCode(heading, ctx);
      let title = bbcode;
      title = title.replace(/\n/g, "");
      bbcode = `[color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][hr]\n[size=6][b][color=silver]${bbcode}[/color][/b][/size][size=6][b]${bbcode}[/b][/size]\n`;
      bbcode += await converthelpElementsToBBCode(content, ctx);
      bbcode += `[b]【${ctx.translator} 译自[url=${ctx.url}][color=#388d40][u] help.minecraft.net 上的 ${title}[/u][/color][/url]】[/b]\n【本文排版借助了:[url=https://github.com/cinder0601/SPXXKLP][color=#388d40][u]SPXXKLP[/u][/color][/url] 用户脚本 v${version}】\n`;
      bbcode += getFooter("INSIDER", VersionType.Normal);
      GM_setClipboard(bbcode, {
        type: "text",
        mimetype: "text/plain",
      });
      button.innerText = "已复制!";
      setTimeout(() => (button.innerText = "复制 BBCode"), 5000);
    };
    const container = document.createElement("div");
    container.id = "spxxklp-buttons";
    container.style.display = "flex";
    container.style.flexDirection = "column";
    container.style.alignItems = "flex-end";
    container.style.width = "100%";
    container.style.padding = "10px";
    container.style.boxSizing = "border-box";
    container.append(button);

    controlDOM(button);
  }

  async function convertZendeskArticleToBBCode(
    html,
    articleUrl,
    translator = config.translator,
    titleSlice,
    contentClass,
    versionType
  ) {
    const title = html.title.slice(0, html.title.lastIndexOf(titleSlice));
    const ctx = {
      bugs: {},
      title: title,
      date: null,
      translator,
      url: articleUrl,
    };
    const content = await getZendeskContent(html, ctx, contentClass);
    const posted = await getZendeskDate(location.href);
    const header = versionType ? getHeader("news", versionType) : "";
    const footer = versionType ? getFooter("news", versionType) : "";
    const ans = `${header}[align=center][size=6][b][color=Silver]${title}[/color][/b][/size]
${translate(
  `[size=6][b]${title}[/b][/size]`,
  ctx,
  "headings"
)}[/align]\n\n${content}\n
[b]【${ctx.translator} 译自[url=${ctx.url}][color=#388d40][u]${
      ctx.url.match(/https:\/\/(.*?)\//)[1]
    } ${posted.year} 年 ${posted.month} 月 ${posted.day} 日发布的 ${
      ctx.title
    }[/u][/color][/url]】[/b]
【本文排版借助了:[url=https://github.com/cinder0601/SPXXKLP][color=#388d40][u]SPXXKLP[/u][/color][/url] 用户脚本 v${spxxklpVersion}】\n\n${footer}`;
    return ans;
  }

  async function getZendeskContent(html, ctx, contentClass) {
    const rootSection = html.getElementsByClassName(contentClass)[0]; // Yep, this is the only difference.

    let ans = await converters.recurse(rootSection, ctx); // Add spaces between texts and '[x'.

    ans = ans.replace(/([a-zA-Z0-9\-._])(\[[A-Za-z])/g, "$1 $2"); // Add spaces between '[/x]' and texts.

    ans = ans.replace(/(\[\/[^\]]+?])([a-zA-Z0-9\-._])/g, "$1 $2");
    return ans;
  }

  async function getZendeskDate(url) {
    const req = new Promise((rs, rj) => {
      GM_xmlhttpRequest({
        method: "GET",
        url:
          "/api/v2/help_center/en-us/articles/" +
          url.match(/\/articles\/(\d+)/)[1],
        fetch: true,
        nocache: true,
        timeout: 7_000,
        onload: (r) => {
          try {
            rs(r.responseText);
          } catch (e) {
            rj(e);
          }
        },
        onabort: () => rj(new Error("Aborted")),
        onerror: (e) => rj(e),
        ontimeout: () => rj(new Error("Time out")),
      });
    });
    let res;
    await req.then((value) => {
      const rsp = JSON.parse(value);
      res = new Date(rsp.article.created_at);
    });
    const year = res.getFullYear();
    const month = res.getMonth() + 1;
    const day = res.getDate();
    return {
      year,
      month,
      day,
    };
  }

  function feedback() {
    let url = window.location.href; // 获取当前页面的URL
    let versionType = getVersionType(url); // 调用getVersionType函数确定versionType

    getZendesk(
      (button) => {
        document.querySelector(".topNavbar nav").append(button);
      },
      " – Minecraft Feedback",
      "article-info",
      versionType
    );
  }

  function help() {
    getHelpContent((button) => {
      document.querySelector(".mc-globalbanner").append(button);
    });
  }
  function twitter() {
    const ProfilePictures = new Map([
      ["Mojang", "https://s2.loli.net/2024/05/27/tMZKe4BmldgDv2P.jpg"],
      ["MojangSupport", "https://s2.loli.net/2024/05/27/tMZKe4BmldgDv2P.jpg"],
      ["MojangStatus", "https://s2.loli.net/2024/05/27/tMZKe4BmldgDv2P.jpg"],
      ["Minecraft", "https://s2.loli.net/2024/05/27/6QsES9CwgKMLv7I.jpg"],
      ["henrikkniberg", "https://s2.loli.net/2024/05/27/h2KGZEBks4XMTFq.png"],
      ["_LadyAgnes", "https://s2.loli.net/2024/05/27/ZoJsth1i8randl9.png"],
      ["kingbdogz", "https://s2.loli.net/2024/05/27/IH7aVTepiDXBZb2.png"],
      ["JasperBoerstra", "https://s2.loli.net/2024/05/27/Jh2doD1eG7A56TR.png"],
      ["adrian_ivl", "https://s2.loli.net/2024/05/27/itAL8hsGqk67cxS.png"],
      ["slicedlime", "https://s2.loli.net/2024/05/27/DY6gQscuX8HiA9e.jpg"],
      ["Cojomax99", "https://s2.loli.net/2024/05/27/DxeZ3rINFgTildA.png"],
      ["Mojang_Ined", "https://s2.loli.net/2024/05/27/dzX3pYy8TSa7uJt.png"],
      ["SeargeDP", "https://s2.loli.net/2024/05/27/nf7EKltTYXLgsxN.png"],
      ["Dinnerbone", "https://s2.loli.net/2024/05/27/Q4ebCE29vFwPmxn.png"],
      ["Marc_IRL", "https://s2.loli.net/2024/05/27/bcW5zXfQ84r9IO6.png"],
      ["Mega_Spud", "https://s2.loli.net/2024/05/27/TZwzsJBRhnLyuFx.png"],
      ["CornerHardMC", "https://s2.loli.net/2024/05/28/o4wLCvuRGi9Yxh7.png"],
      ["MinecraftWikiEN", "https://s2.loli.net/2024/06/23/fn1SeWpdmit86lQ.png"],
    ]); //More pictures can be added manually.
    function getTweetMetadata() {
      const tweetMetadata = {
        date: "",
        source: "",
        text: "",
        rawtext: "",
        tweetLink: "",
        urls: "",
        userName: "",
        userTag: "",
        lang: "",
      };
      const url = window.location.href;
      const regex = /https:\/\/x\.com\/([^/]+)\/status\/\d+/;
      const match = url.match(regex);
      tweetMetadata.userTag = match[1];
      let posterNameContent = [];
      const posterNameElements = document
        .querySelector('div[data-testid="User-Name"] a span')
        .querySelectorAll("span, img[alt]");
      for (const element of posterNameElements) {
        if (element.tagName.toLowerCase() === "span") {
          posterNameContent.push(element.textContent);
        } else if (element.tagName.toLowerCase() === "img" && element.alt) {
          posterNameContent.push(element.alt);
        }
      }
      tweetMetadata.userName = posterNameContent.join("");
      let texts = [];
      let rawTexts = [];

      const articleDivs = document
        .querySelector("article div[lang]")
        .querySelectorAll("a, span, img[alt]");

      for (const element of articleDivs) {
        let textContent = "";
        let rawContent = "";

        if (element.tagName.toLowerCase() === "a") {
          const url = element.href;
          let linkText = element.textContent.trim();
          const span = element.querySelector("span");
          if (span) {
            let spanContent = span.textContent.trim();
            linkText = linkText.replace(spanContent, "").trim();
          }
          textContent = `[url=${url}][color=#00bfff][u]${linkText}[/u][/color][/url]`;
          rawContent = linkText;
        } else if (element.tagName.toLowerCase() === "span") {
          if (
            !element.closest("a") &&
            element.querySelectorAll("a").length === 0
          ) {
            textContent = element.innerHTML;
            rawContent = element.textContent;
          }
        } else if (element.tagName.toLowerCase() === "img" && element.alt) {
          textContent = element.alt;
          rawContent = element.alt;
        }
        if (textContent.trim()) {
          texts.push(textContent);
          rawTexts.push(rawContent.replace(/<a.*?>(.*?)<\/a>/g, "$1"));
        }
      }
      tweetMetadata.text = texts.join("");
      tweetMetadata.rawtext = rawTexts.join("");
      //I have tried my best but failed, if it still returns 'http://' or 'https://', please add the link manually.
      tweetMetadata.lang = document
        .querySelector("article div[lang]")
        .getAttribute("lang");
      tweetMetadata.date = document.querySelector("time").innerHTML;
      tweetMetadata.source = document.querySelector(
        'article a[role="link"] span'
      ).innerText;
      tweetMetadata.tweetLink = window.location.href;

      return tweetMetadata;
    }

    function getTweetBbcode(tweet, mode) {
      const attributeColor = "#5B7083";
      const backgroundColor = mode === "dark" ? "#000000" : "#FFFFFF";
      const foregroundColor = mode === "dark" ? "#D9D9D9" : "#0F1419";
      const dateString = `${tweet.date} · ${tweet.source} · SPXXKLP v${spxxklpVersion} · 转载请注明原作者及本帖地址`;
      const content = tweet.text;
      const content1 = tweet.rawtext;

      return `[align=center][table=560,${backgroundColor}]
[tr][td][indent][font=-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif]
  [float=left][img=44,44]${
    ProfilePictures.get(tweet.userTag) || "<不支持的头像,请手动添加图片链接>"
  }[/img][/float][size=15px][b][color=${foregroundColor}]${
        tweet.userName
      }[/color][/b]
[color=${attributeColor}]@${tweet.userTag}[/color][/size]

[color=Silver][size=23px]${content1}[/color][/size]

[size=15px][color=${attributeColor}]由 ${GM_config.get("translator")} 翻译自${
        tweet.lang.startsWith("en") ? "英语" : ` ${tweet.lang}`
      }[/color][/size]

[color=${foregroundColor}][size=23px]${content}[/size]
  [/size][/color][/indent][align=center]<如有配图,请在此处添加>[/align]
  [indent][size=15px][url=${
    tweet.tweetLink
  }][color=${attributeColor}][u]${dateString}[/u][/color][/url][/size][/indent][/td][/tr]
  [/table][/align]`;
    }

    function x() {
      console.info("[SPXXKLP] Activated");

      const buttonLight = document.createElement("button");
      buttonLight.style.backgroundColor = "rgb(255, 255, 255)";
      buttonLight.style.color = "#000000";
      buttonLight.style.border = "none";
      buttonLight.style.padding = "5px 10px";
      buttonLight.style.borderRadius = "5px";
      buttonLight.style.fontSize = "15px";
      buttonLight.style.cursor = "pointer";
      buttonLight.style.transition = "background-color 0.3s ease";
      buttonLight.style.width = "180px";
      buttonLight.style.height = "30px";
      buttonLight.style.textAlign = "center";
      buttonLight.style.marginLeft = "auto";

      buttonLight.onmouseover = () => {
        buttonLight.style.backgroundColor = "rgb(223, 223, 223)";
      };
      buttonLight.onmouseout = () => {
        buttonLight.style.backgroundColor = "rgb(255, 255, 255)";
      };
      buttonLight.innerText = "复制 BBCode (浅色)";
      buttonLight.onclick = async () => {
        buttonLight.innerText = "处理中...";
        try {
          const bbcode = getTweetBbcode(getTweetMetadata(), "light");
          GM_setClipboard(bbcode, { type: "text", mimetype: "text/plain" });
          buttonLight.innerText = "已复制!";
        } catch (error) {
          console.error("Error processing BBCode (Light):", error);
          buttonLight.innerText = "错误!";
        }
        setTimeout(() => (buttonLight.innerText = "复制 BBCode (浅色)"), 5000);
      };

      const buttonDark = document.createElement("button");
      buttonDark.style.backgroundColor = "rgb(32, 32, 32)";
      buttonDark.style.color = "#FFFFFF";
      buttonDark.style.border = "none";
      buttonDark.style.padding = "5px 10px";
      buttonDark.style.borderRadius = "5px";
      buttonDark.style.fontSize = "15px";
      buttonDark.style.cursor = "pointer";
      buttonDark.style.transition = "background-color 0.3s ease";
      buttonDark.style.width = "180px";
      buttonDark.style.height = "30px";
      buttonDark.style.textAlign = "center";
      buttonDark.style.marginLeft = "auto";

      buttonDark.onmouseover = () => {
        buttonDark.style.backgroundColor = "rgb(42, 42, 42)";
      };
      buttonDark.onmouseout = () => {
        buttonDark.style.backgroundColor = "rgb(32, 32, 32)";
      };
      buttonDark.innerText = "复制 BBCode (深色)";
      buttonDark.onclick = async () => {
        buttonDark.innerText = "处理中...";
        try {
          const bbcode = getTweetBbcode(getTweetMetadata(), "dark");
          GM_setClipboard(bbcode, { type: "text", mimetype: "text/plain" });
          buttonDark.innerText = "已复制!";
        } catch (error) {
          console.error("Error processing BBCode (Dark):", error);
          buttonDark.innerText = "错误!";
        }
        setTimeout(() => (buttonDark.innerText = "复制 BBCode (深色)"), 5000);
      };

      const checkLoaded = setInterval(() => {
        const targetDiv = document.querySelector("article div[lang]");
        if (targetDiv && !document.querySelector("#spxxklp-buttons")) {
          const container = document.createElement("div");
          container.id = "spxxklp-buttons";
          container.style.display = "flex";
          container.style.flexDirection = "column";
          container.style.alignItems = "flex-end";
          container.style.width = "100%";
          container.style.padding = "10px";
          container.style.boxSizing = "border-box";
          container.append(buttonLight);
          container.append(buttonDark);
          targetDiv.parentElement.append(container);
          clearInterval(checkLoaded);
        }
      }, 300);
    }

    x();
  }
  switch (location.host) {
    case "www.minecraft.net": //Fuck minecraft.net what the heck are you doing.
      minecraftNet();
      break;

    case "x.com":
      twitter();
      break;

    case "feedback.minecraft.net":
      feedback();
      break;

    case "help.minecraft.net":
      help();
      break;
  }
})();
//# sourceMappingURL=bundle.user.js.map