您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
萌娘百科UserStatus订阅
- // ==UserScript==
- // @name UserStatusSub
- // @namespace https://github.com/gui-ying233/UserStatusSub
- // @version 1.3.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==
- (() => {
- "use strict";
- if (new URLSearchParams(window.location.search).get("safemode")) return;
- $(() =>
- (async () => {
- window.addEventListener("storage", e => {
- if (e.key === "userStatusSub") {
- if (e.newValue) {
- localStorage.setItem("userStatusSub", e.newValue);
- } else {
- localStorage.removeItem("userStatusSub");
- }
- }
- });
- 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: `//commons.moegirl.org.cn/extensions/Avatar/avatar.php?user=${u.title}`,
- });
- }
- });
- }, waitTime);
- await (() => {
- return new Promise(resolve =>
- setTimeout(
- resolve,
- waitTime / userStatusGetLocalStorage().length
- )
- );
- })();
- }
- })();
- })()
- );
- })();