- // ==UserScript==
- // @name SPXXHi
- // @description Minecraft.net & X.com blog article to BBCode converter, adapted to HiMCBBS
- // @namespace npmjs.com/package/@spxxhi/userscript
- // @author Cinder & SPGoding & SPX Fellow
- // @connect *
- // @connect feedback.minecraft.com
- // @connect help.minecraft.net
- // @connect raw.githubusercontent.com
- // @homepage https://github.com/cinder0601/SPXXHi
- // @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.5
- // @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: "spxxhi",
- title: "SPXXHi 用户脚本",
- 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("[SPXXHi] 正在使用 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("[SPXXHi] 正在使用自定义漏洞中心");
- 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.5";
-
- 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 spxxhiVersion = 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[center][size=1][color=Silver]Powered by SPXXHi ${spxxhiVersion} 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][/center]`;
-
- /*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://www.himcbbs.com/forums/news/][color=#388e3c][u]HiMCBBS - 新闻资讯版块[/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://www.himcbbs.com/forums/news/][color=#388e3c][u]HiMCBBS - 新闻资讯版块[/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://www.himcbbs.com/forums/news/][color=#388e3c][u]HiMCBBS - 新闻资讯版块[/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://www.himcbbs.com/forums/news/][color=#388e3c][u]HiMCBBS - 新闻资讯版块[/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://www.himcbbs.com/forums/news/][color=#388e3c][u]HiMCBBS - 新闻资讯版块[/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://www.himcbbs.com/forums/news/][color=#388e3c][u]HiMCBBS - 新闻资讯版块[/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://www.himcbbs.com/forums/news/][color=#388e3c][u]HiMCBBS - 新闻资讯版块[/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, [
- [/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版 候选版本"],
- [/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)"],
- [/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 框架(实验性)"],
- [/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, [
- [/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, "[i]"],
- [/\[\/i]/g, "[/i]"],
- ...(ctx.disablePunctuationConverter
- ? []
- : [
- [/,( |$)/g, ","],
- [/!( |$)/g, "!"],
- [/\.\.\.( |$)/g, "…"],
- [/\.( |$)/g, "。"],
- [/\?( |$)/g, "?"],
- [/( |^)-( |$)/g, " —— "],
- ]),
- ],
- (input) => {
- return quoteTreatment(input, [["“", "”", /"/]]);
- }
- );
- },
- code: (input, ctx) => {
- return quoteTreatment(input, [
- [
- '[bgcolor=#f1edec][color=Silver][font=Unifont][/font][/color][/bgcolor]',
- "`",
- /`/,
- ],
- ]);
- },
- };
- 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("spxxhi-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) => {
- if (!ele || !await converters.recurse(ele, {
- ...ctx,
- disablePunctuationConverter: true,
- })) {
- return '';
- }
- const prefix = ctx.multiLineCode
- ? "[code]"
- : "[bgcolor=#f1edec][color=#7824c5][font=Unifont]";
- const suffix = ctx.multiLineCode
- ? "[/code]"
- : "[/font][/color][/bgcolor]";
- 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 = `[center]${ans}[/center]\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 (slides.length > 0) {
- ans = `[center]${slides
- .map(([url, caption]) => `[img]${url}[/img]\n${caption}`)
- .join("\n")}[/center]\n`;
- } else {
- ans = "";
- }
- } else if (ele.classList.contains("video")) {
- ans = "\n[center]<无法获取的视频,如有可用视频源,请在此处插入>\n<对于B站视频,可使用 [bilibili] 代码>[/center]\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")) {
- ans = "";
- } else if (ele.classList.contains("modal")) {
- 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/SPXXHi][color=#388d40][u]SPXXHi[/u][/color][/url] 用户脚本 v${spxxhiVersion}】\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 = `[center]${prefix}${imgUrl}[/img][/center]\n`; //Left aligning is too ugly.
- 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 = `[center][size=2][color=Silver]${usingSilver(
- inner
- )}[/color][/size]\n${translate(inner, ctx, [
- "punctuation",
- "imgCredits",
- ])}[/center]\n`;
- } else if (ele.classList.contains("lead")) {
- ans = `[b][size=2][color=Silver]${inner}[/color][/size][/b]\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() === " ") {
- 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 =
- "[bgcolor=#f1edec][color=#7824c5][font=Unifont]";
- const suffix = "[/font][/color][/bgcolor]";
- 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 =
- "[bgcolor=#f1edec][color=#7824c5][font=Unifont]";
- const suffix = "[/font][/color][/bgcolor]";
- return `${prefix}${textContent}${suffix}`;
- } else if (ele.classList.contains("bedrock-server")) {
- // Inline code.
- const prefix =
- "[bgcolor=#f1edec][color=#7824c5][font=Unifont]";
- const suffix = "[/font][/color][/bgcolor]";
- 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,
- "[bgcolor=#f1edec][color=#7824c5][font=Unifont]$1[/font][/color][/bgcolor]"
- );
- }
- /**
- * 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;
- }
- }
- /**
- * HiMCBBS 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("spxxhi-userscript-ignored");
- button.innerText = "复制 BBCode (HiMCBBS)";
- // 按钮样式设置
- 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 = "70px";
- 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 (HiMCBBS)"), 5000);
- };
- const container = document.createElement("div");
- container.id = "spxxhi-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[center][color=silver][size=6][b]${maintitle}[/b][/size][/color][/center]\n[center][size=6][b]${maintitle}[/b][/size][/center]\n[center][color=silver][size=2]${subtitle}[/size][/color][/center]\n[center][size=2]${subtitle}[/size][/center]\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[center][bgcolor=Black][color=White][font=Unifont][b]${articleType}[/b][/font][/color][/bgcolor][/center]`
- : "";
- const img = html.getElementsByClassName("article-head__image")[0];
-
- if (!img) {
- return `\n[center]${category}[/center]\n`;
- }
-
- const src = img.src;
- const ans = `[center][img=1200,513]${resolveUrl(
- src
- )}[/img]\n${category}[/center]\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) {
- try {
- let rawauthor = html.getElementsByClassName("MC_articleHeroA_attribution_author")[0];
- if (!rawauthor) {
- console.warn("Author attribution element not found");
- return "Unknown Author";
- }
-
- let authorImgUrl = "";
- let authorImg = rawauthor.getElementsByTagName("img")[0];
- if (authorImg && authorImg.src) {
- authorImgUrl = authorImg.src;
- }
-
- let authorName = "Unknown";
- let authorNameElement = rawauthor.getElementsByTagName("dd")[0];
- if (authorNameElement) {
- authorName = authorNameElement.innerText;
- }
-
- let publishDate = "Unknown Date";
- let publishDateElement = rawauthor.getElementsByTagName("dd")[1];
- if (publishDateElement) {
- publishDate = publishDateElement.innerText;
- }
-
- let [a, b, c] = publishDate.split("/");
- let year, month, day;
- if (a > 12) {
- year = a;
- month = b;
- day = c;
- } else {
- year = "20" + c;
- month = a;
- day = b;
- }
- let url = window.location.href;
- let title = await getMainTitle(html);
-
- let ans = `\n${authorImgUrl ? `[left][img]${authorImgUrl}[/img][/left]\n\n\n` : ''}【${translator} 译自[url=${url}][color=#388d40][u]${authorName} ${year} 年 ${month} 月 ${day} 日发布的 ${title}[/u][/color][/url]】[/b]\n【本文排版借助了:[url=https://github.com/cinder0601/SPXXHi][color=#388d40][u]SPXXHi[/u][/color][/url] 用户脚本 v${spxxhiVersion}】`;
- return ans;
- } catch (error) {
- console.error("Error in getAuthor function:", error);
- return "Error retrieving author information";
- }
- }
-
- 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), .MC_Carousel_track_slide.MC_Theme_Legends.MC_Carousel_track_slide__active, .MC_Carousel_track_slide.MC_Theme_Legends:not(.MC_Carousel_track_slide__copy)"
- );
- let container = document.createElement("div");
-
- let seenElements = new Set();
-
- Array.from(elements).forEach((element) => {
- let identifier = element.querySelector("img")?.src || element.innerHTML;
- if (!seenElements.has(identifier)) {
- seenElements.add(identifier);
- if (element.classList.contains("MC_Carousel_track_slide")) {
- let mediaDiv = element.querySelector(".MC_Carousel_track_slide_media");
- if (mediaDiv) {
- let clonedDiv = document.createElement("div");
- clonedDiv.appendChild(mediaDiv.cloneNode(true));
- container.appendChild(clonedDiv);
- }
- } else {
- container.appendChild(element.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]>(?: |\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("spxxhi-userscript-ignored", "navLink");
- button.innerText = "复制 BBCode (HiMCBBS)";
- // 按钮样式设置
- 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 = "50px";
- 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 (HiMCBBS)"), 5_000);
- };
- const container = document.createElement("div");
- container.id = "spxxhi-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("spxxhi-userscript-ignored", "navLink");
- button.innerText = "复制 BBCode (HiMCBBS)";
- // 按钮样式设置
- 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 = "50px";
- 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/SPXXHi][color=#388d40][u]SPXXHi[/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 (HiMCBBS)"), 5000);
- };
- const container = document.createElement("div");
- container.id = "spxxhi-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}[center][size=6][b][color=Silver]${title}[/color][/b][/size]
- ${translate(
- `[size=6][b]${title}[/b][/size]`,
- ctx,
- "headings"
- )}[/center]\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/HiMCBBS/SPXXHi][color=#388d40][u]SPXXHi[/u][/color][/url] 用户脚本 v${spxxhiVersion}】\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);
- });
- let year, month, day;
- if (res.getFullYear() > 12) {
- year = res.getFullYear();
- month = res.getMonth() + 1;
- day = res.getDate();
- } else {
- year = "20" + res.getDate();
- month = res.getFullYear();
- day = res.getMonth() + 1;
- }
- 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} · SPXXHi v${spxxhiVersion} · 转载请注明原作者及本帖地址`;
- const content = tweet.text;
- const content1 = tweet.rawtext;
-
- return `[center][table=560,${backgroundColor}]
- [tr][td][indent][font=Arial]
- [left][img=44,44]${
- ProfilePictures.get(tweet.userTag) || "<不支持的头像,请手动添加图片链接>"
- }[/img][/left][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][center]<如有配图,请在此处添加>[/center]
- [indent][size=15px][url=${
- tweet.tweetLink
- }][color=${attributeColor}][u]${dateString}[/u][/color][/url][/size][/indent][/td][/tr]
- [/table][/center]`;
- }
-
- function x() {
- console.info("[SPXXHi] 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 = "50px";
- 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 (HiMCBBS)(浅色)";
- 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 (HiMCBBS)(浅色)"), 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 = "50px";
- 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 (HiMCBBS)(深色)";
- 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 (HiMCBBS)(深色)"), 5000);
- };
-
- const checkLoaded = setInterval(() => {
- const targetDiv = document.querySelector("article div[lang]");
- if (targetDiv && !document.querySelector("#spxxhi-buttons")) {
- const container = document.createElement("div");
- container.id = "spxxhi-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