Greasy Fork

Greasy Fork is available in English.

UserStatusSub

萌娘百科UserStatus订阅

当前为 2023-05-21 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         UserStatusSub
// @namespace    https://github.com/gui-ying233/UserStatusSub
// @version      1.1.0
// @description  萌娘百科UserStatus订阅
// @author       鬼影233
// @contributor  BearBin@BearBin1215
// @license      MIT
// @match        *.moegirl.org.cn/*
// @icon         https://img.moegirl.org.cn/common/b/b7/%E5%A4%A7%E8%90%8C%E5%AD%97.svg
// @supportURL   https://github.com/gui-ying233/UserStatusSub/issues
// ==/UserScript==

(function () {
	"use strict";

	$(() =>
		(async () => {
			await mw.loader.using([
				"mediawiki.api",
				"mediawiki.notification",
				"mediawiki.util",
				"oojs-ui",
			]);

			const api = new mw.Api();

			mw.util.addCSS(`.userStatus {
		width: 100%;
		display: grid;
		grid-template-columns: repeat(auto-fit, 11em);
		gap: 1em;
		padding: 1em;
		justify-content: center;
	}

	.userStatusImg {
		width: 25px;
		vertical-align: middle;
	}

	.userStatusRaw {
		display: grid;
		gap: 0.5em;
		border: var(--theme-just-kidding-text-color) dashed 1px;
		padding: 0.5em;
	}

	.userStatusContent {
		max-height: 11em;
		overflow: auto;
		transition: max-height 350ms;
	}

	.userStatusContent:hover {
		max-height: 100%;
	}

	#userStatuUpdate {
		margin: 1em 0;
	}`);

			/**
			 * @return {JSON}
			 */
			function userStatusGetLocalStorage() {
				return Object.values(
					JSON.parse(localStorage.getItem("userStatusSub")) || {}
				);
			}
			async function userStatusSetLocalStorage() {
				if (
					document
						.querySelector("#userStatuSummary > textarea")
						.value.trim()
				) {
					let subList = [];
					for (const t of [
						...new Set(
							document
								.querySelector("#userStatuSummary > textarea")
								.value.trim()
								.split("\n")
								.map((str) => str.trim())
								.filter((str) => str)
						),
					]) {
						await api
							.get({
								action: "query",
								format: "json",
								prop: "revisions",
								titles: `User:${t}/Status`,
								utf8: 1,
								formatversion: 2,
								rvprop: "timestamp",
							})
							.then((d) => {
								if (d.query.pages[0].missing) {
									mw.notify(`用户${t}不存在`, {
										type: "warn",
									});
								} else {
									subList.push({
										title: t,
										timestamp:
											d.query.pages[0].revisions[0]
												.timestamp,
									});
								}
							});
					}
					localStorage.setItem(
						"userStatusSub",
						JSON.stringify(subList)
					);
					document.querySelector("#userStatuSummary > textarea");
				} else {
					localStorage.removeItem("userStatusSub");
				}
			}

			/**
			 * @param {JSON} subList
			 */
			async function updateUserStatus(subList) {
				const userStatusUpdateButton =
					document.getElementById("userStatuUpdate");
				userStatusUpdateButton.style.pointerEvents = "none";
				userStatusUpdateButton.classList.add("oo-ui-widget-disabled");
				userStatusUpdateButton.classList.remove("oo-ui-widget-enabled");
				userStatusUpdateButton.setAttribute("aria-disabled", "true");
				document.body.querySelector(".userStatus").innerHTML = "";
				userStatusSetLocalStorage();
				for (const u of subList) {
					try {
						await api
							.get({
								action: "parse",
								format: "json",
								page: `User:${u.title}/Status`,
								prop: "text",
								disabletoc: 1,
								utf8: 1,
								formatversion: 2,
							})
							.then((d) => {
								const userStatusRaw =
									document.createElement("div");
								userStatusRaw.classList.add("userStatusRaw");
								userStatusRaw.innerHTML += d.parse.text;
								switch (
									userStatusRaw.innerText.trim().toLowerCase()
								) {
									case "online":
									case "on":
										userStatusRaw.innerHTML =
											'<img class="userStatusImg" src="https://img.moegirl.org.cn/common/9/94/Symbol_support_vote.svg"> <b style="color:green;">在线</b>';
										break;
									case "busy":
										userStatusRaw.innerHTML =
											'<img class="userStatusImg" src="https://img.moegirl.org.cn/common/c/c5/Symbol_support2_vote.svg"> <b style="color:blue;">忙碌</b>';
										break;
									case "offline":
									case "off":
										userStatusRaw.innerHTML =
											'<img class="userStatusImg" src="https://img.moegirl.org.cn/common/7/7f/Symbol_oppose_vote.svg"> <b style="color:red;">离线</b>';
										break;
									case "away":
										userStatusRaw.innerHTML =
											'<img class="userStatusImg" src="https://img.moegirl.org.cn/common/6/6c/Time2wait.svg"> <b style="color:grey;">已离开</b>';
										break;
									case "sleeping":
									case "sleep":
										userStatusRaw.innerHTML =
											'<img class="userStatusImg" src="https://img.moegirl.org.cn/common/5/54/Symbol_wait.svg"> <b style="color:purple;">在睡觉</b>';
										break;
									case "wikibreak":
									case "break":
										userStatusRaw.innerHTML =
											'<img class="userStatusImg" src="https://img.moegirl.org.cn/common/6/61/Symbol_abstain_vote.svg"> <b style="color:brown;">正在放萌百假期</b>';
										break;
									case "holiday":
										userStatusRaw.innerHTML =
											'<img class="userStatusImg" src="https://img.moegirl.org.cn/common/3/30/Symbol_deferred.svg"> <b style="color:#7B68EE;">处于假期中</b>';
										break;
								}
								userStatusRaw.innerHTML = `<b class="userStatusUserName">${u.title}</b><div class="userStatusContent">${userStatusRaw.innerHTML}</div>`;
								document.body
									.querySelector(".userStatus")
									.append(userStatusRaw);
								userStatusSubDialog.updateSize();
							});
					} catch (e) {
						if (e !== "missingtitle") {
							console.error(e);
						}
					}
				}
				userStatusUpdateButton.style.pointerEvents = "auto";
				userStatusUpdateButton.classList.remove(
					"oo-ui-widget-disabled"
				);
				userStatusUpdateButton.classList.add("oo-ui-widget-enabled");
				userStatusUpdateButton.setAttribute("aria-disabled", "false");
			}

			/* 感谢BearBin@BearBin1215提供的的OOUI部分 */
			const $body = $("body");

			class userStatusSubWindow extends OO.ui.ProcessDialog {
				static static = {
					...super.static,
					tagName: "div",
					name: "userStatus",
					title: "用户状态监控",
					actions: [
						{
							action: "cancel",
							label: "取消",
							flags: ["safe", "close", "destructive"],
						},
						{
							action: "submit",
							label: "保存",
							flags: ["primary", "progressive"],
						},
					],
				};
				constructor(config) {
					super(config);
				}
				initialize() {
					super.initialize();
					this.panelLayout = new OO.ui.PanelLayout({
						scrollable: false,
						expanded: false,
						padded: true,
					});

					const $userStatus = document.createElement("div");
					$userStatus.classList.add("userStatus");

					const $label = document.createElement("p");
					$label.innerText = "订阅列表:";
					const userStatusInputBox =
						new OO.ui.MultilineTextInputWidget({
							placeholder: "仅填写用户名,每个用户名一行",
							id: "userStatuSummary",
							autosize: true,
						});
					const userStatusUpdateButton = new OO.ui.ButtonWidget({
						label: "更新列表",
						flags: ["primary"],
						id: "userStatuUpdate",
					});
					userStatusUpdateButton.on("click", async () => {
						await updateUserStatus(
							userStatusInputBox
								.getValue()
								.trim()
								.split("\n")
								.map((str) => str.trim())
								.filter((str) => str)
								.map((str) => {
									return { title: str };
								})
						);
						this.updateSize();
					});

					this.panelLayout.$element.append(
						$userStatus,
						$label,
						userStatusInputBox.$element,
						userStatusUpdateButton.$element
					);
					this.$body.append(this.panelLayout.$element);
				}

				getActionProcess(action) {
					if (action === "cancel") {
						return new OO.ui.Process(() => {
							this.close({ action });
						}, this);
					} else if (action === "submit") {
						userStatusSetLocalStorage();
						return new OO.ui.Process(() => {
							this.close({ action });
						}, this);
					}
					return super.getActionProcess(action);
				}
			}

			const windowManager = new OO.ui.WindowManager({});
			$body.append(windowManager.$element);
			const userStatusSubDialog = new userStatusSubWindow({
				size: "larger",
			});
			windowManager.addWindows([userStatusSubDialog]);

			mw.util
				.addPortletLink(
					"p-cactions",
					"javascript:void(0);",
					"用户状态监控",
					"ca-userstatussub"
				)
				.addEventListener("click", async () => {
					$("#mw-notification-area").appendTo("body");
					windowManager.openWindow(userStatusSubDialog);
					document.body.querySelector(
						"#userStatuSummary > textarea"
					).value = userStatusGetLocalStorage()
						.map((u) => u.title)
						.join("\n");
					await updateUserStatus(userStatusGetLocalStorage());
				});

			const waitTime = 300000;

			var timestamps = {};
			(async () => {
				if (Notification.permission !== "denied") {
					Notification.requestPermission();
				}
				for (const u of userStatusGetLocalStorage()) {
					timestamps[u.title] = u.timestamp;
					setInterval(() => {
						api.get({
							action: "query",
							format: "json",
							prop: "revisions",
							titles: `User:${u.title}/Status`,
							utf8: 1,
							formatversion: 2,
							rvprop: "timestamp|content",
						}).then((d) => {
							const timestamp =
								d.query.pages[0].revisions[0].timestamp;
							if (timestamp !== timestamps[u.title]) {
								localStorage.setItem(
									"userStatusSub",
									JSON.stringify(
										userStatusGetLocalStorage()
									).replace(
										`"title":"${u.title}","timestamp":"${
											timestamps[u.title]
										}"`,
										`"title":"${u.title}","timestamp":"${timestamp}"`
									)
								);
								timestamps[u.title] = timestamp;
								let status =
									d.query.pages[0].revisions[0].content
										.trim()
										.toLowerCase();
								switch (status) {
									case "online":
									case "on":
										status = "在线";
										break;
									case "busy":
										status = "忙碌";
										break;
									case "offline":
									case "off":
										status = "离线";
										break;
									case "away":
										status = "已离开";
										break;
									case "sleeping":
									case "sleep":
										status = "在睡觉";
										break;
									case "wikibreak":
									case "break":
										status = "正在放萌百假期";
										break;
									case "holiday":
										status = "处于假期中";
										break;
									default:
										const s = document.createElement("div");
										s.innerHTML = status;
										status = s.innerText;
								}
								new Notification(`${u.title}已更新:`, {
									body: status,
									icon: `https://commons.moegirl.org.cn/extensions/Avatar/avatar.php?user=${u.title}`,
								});
							}
						});
					}, waitTime);
					await (() => {
						return new Promise((resolve) =>
							setTimeout(
								resolve,
								waitTime / userStatusGetLocalStorage().length
							)
						);
					})();
				}
			})();
		})()
	);
})();