Greasy Fork

Greasy Fork is available in English.

NodeSeek X

【原NodeSeek增强】自动签到、自动滚动翻页

当前为 2024-03-12 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         NodeSeek X
// @namespace    http://www.nodeseek.com/
// @version      0.3-beta.3
// @description  【原NodeSeek增强】自动签到、自动滚动翻页
// @author       dabao
// @match        *://www.nodeseek.com/*
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACz0lEQVR4Ae3B32tVdQAA8M85u7aVHObmzJVD0+ssiphstLEM62CBlCBEIAYhUoGGD/kiRUo+9CIEElFZgZJFSApBVhCUX2WFrVQKf5Qy26SgdK4pN7eZu+cbtyfJ/gLx83HD9SAhlEyXupiPhUSTeonRfNw1ws2aRJeN5jHcolFhJJ9M8Zj99piDTnv12SjzfzIb9dmrC7Pttt8ykjDVLsu8ZZ1GH1oqeDofJLtJh4fMEw3Y72jlCuEO2+W+sNJFr3vOZ1YIi8NIGA29hDWhGgZDJ2Rt2ZvZSBazmMUsZsPZ1qwVQmcYDNWwhtAbRsNIWJx6WLPDfgxNVkm9nR8hm+XduLba7F9RtcXztmUzyY/YJrUqNPvBYc0eSS3CwXxMl4WG7CarsyEuvU2HOkRNujSw3PosxR6DFurKxx3E/akFohPo0aDfEO61os5LdrtLVWG1TzxokifdiSH9GnTjuGhBqsWE39GOo3kVi8wsmeVW00SJ200zA9r0kFcdQzv+MKElVW/S+L5EE86pmUth3BV/SzCOCUjMVXMWzfsSYybVl1SlSlESkagpuOI1nzshFX1gyAF1UKhJEKOkJFVNXVBv+pJoBK1qBkh86z1/SaR+9o5zEgoDaloxsiSart6F1Bkl83ESHWEKvvEbqZJETaokgSH9hCk6cBLtSs6kDqEb/cZ0K+MnO0X/VdhRGUBZjzH9uA+HUl+a0BvmO+J7bVZSKWz1kehqhfe9oWalNoccDmW9JnyV+toxsy3PK3aY9Gx4gMp567ziV4WawpCXra+MEhZ5xqTtecVycxzXlxA22OK4ZYbt9LjvrM5PkNUp6zVPdNpBv1QKwt126Paxp8zwqXu8kG8pYZdHlT2Rvxo2aVG2ObyYn65UnXLKVULZZrP02ZRfCms1OmAXCSHRYqrLzuZFaDFV6s/8omuERs0Kl/LzITVTvTHDeXTD9eAftAsSYhXYOWUAAAAASUVORK5CYII=
// @require      https://cdn.staticfile.org/notie/4.3.1/notie.min.js
// @resource     notieStyle https://cdn.staticfile.org/notie/4.3.1/notie.min.css
// @resource     highlightStyle https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_notification
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getResourceText
// @grant        GM_addElement
// @grant        GM_addStyle
// @grant        GM_openInTab
// @grant        unsafeWindow
// @run-at       document-end
// @license      GPL-3.0 License
// @supportURL   https://www.nodeseek.com/post-36263-1
// @homepageURL  https://www.nodeseek.com/post-36263-1
// ==/UserScript==

(function () {
    'use strict';

    const scriptInfo = GM_info.script;
    const version = scriptInfo.version;
    const author = scriptInfo.author;
    const name = scriptInfo.name;
    const icon = scriptInfo.icon;

    const util = {
        clog(c) {
            console.group(`%c %c [${name}]-v${version} by ${author}`, `background:url(${icon}) center center no-repeat;background-size:12px;padding:3px`, "");
            console.log(c);
            console.groupEnd();
        },
        getValue(name) {
            return GM_getValue(name);
        },
        setValue(name, value) {
            GM_setValue(name, value);
        },
        sleep(time) {
            return new Promise((resolve) => setTimeout(resolve, time));
        },
        addStyle(id, tag, css) {
            tag = tag || 'style';
            let doc = document, styleDom = doc.getElementById(id);
            if (styleDom) return;
            let style = doc.createElement(tag);
            style.rel = 'stylesheet';
            style.id = id;
            tag === 'style' ? style.innerHTML = css : style.href = css;
            document.head.appendChild(style);
        },
        getAttributesByPrefix(element, prefix) {
            const attributes = element.attributes;
            let matchingAttributes = {};
            for (let attribute of attributes) {
                const attributeName = attribute.name;
                const attributeValue = attribute.value;

                if (attributeName.startsWith(prefix)) {
                    matchingAttributes[attributeName] = attributeValue;
                }
            }
            return matchingAttributes;
        },
        data(element, key, value) {
            if (arguments.length < 2) {
                return undefined;
            }
            if (value != undefined) {
                element.dataset[key] = value;
            }
            return element.dataset[key];
        },
        post(url, data, headers, type) {
            if (typeof data === 'object') {
                data = JSON.stringify(data);
            }
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: "POST", url, headers, data,
                    responseType: type || 'json',
                    onload: (res) => resolve(res.response || res.responseText),
                    onerror: (err) => reject(err)
                });
            });
        },
        get(url, headers, type) {
            return new Promise((resolve, reject) => {
                const requestObj = GM_xmlhttpRequest({
                    method: "GET", url, headers,
                    responseType: type || 'json',
                    onload: (res) => {
                        if (res.status === 204) {
                            requestObj.abort();
                        }
                        resolve(res.response || res.responseText);
                    },
                    onerror: (err) => reject(err)
                });
            });
        },
        openLinkInNewTab(selector) {
            const allLinks = document.querySelectorAll(selector);

            allLinks.forEach(function (link) {
                link.setAttribute('target', '_blank');
            });
        },
        getCurrentDate() {
            const localTimezoneOffset = (new Date()).getTimezoneOffset();
            const beijingOffset = 8 * 60;
            const beijingTime = new Date(Date.now() + (localTimezoneOffset + beijingOffset) * 60 * 1000);
            const timeNow = `${beijingTime.getFullYear()}/${(beijingTime.getMonth() + 1)}/${beijingTime.getDate()}`;
            return timeNow;
        }
    };

    const opts = {
        post: {
            pathPattern: /^\/(categories\/|page|award|search|$)/,
            scrollThreshold: 200,
            nextPagerSelector: '.nsk-pager a.pager-next',
            postListSelector: 'ul.post-list',
            topPagerSelector: 'div.nsk-pager.pager-top',
            bottomPagerSelector: 'div.nsk-pager.pager-bottom',
        },
        comment: {
            pathPattern: /^\/post-/,
            scrollThreshold: 690,
            nextPagerSelector: '.nsk-pager a.pager-next',
            postListSelector: 'ul.comments',
            topPagerSelector: 'div.nsk-pager.post-top-pager',
            bottomPagerSelector: 'div.nsk-pager.post-bottom-pager',
        },
        setting: {
            SETTING_SIGN_IN_STATUS: 'setting_sign_in_status',
            SETTING_SIGN_IN_LAST_DATE: 'setting_sign_in_last_date',
            SETTING_SIGN_IN_IGNORE_DATE: 'setting_sign_in_ignore_date'
        }
    };

    const message = {
        info: (text) => notie.alert({ type: 'info', text: text }),
        success: (text) => notie.alert({ type: 'success', text: text, time: 2 }),
        warning: (text) => notie.alert({ type: 'warning', text: text }),
        error: (text) => notie.alert({ type: 'error', text: text })
    };

    const main = {
        // 初始化配置数据
        initValue() {
            const value = [
                { name: opts.setting.SETTING_SIGN_IN_STATUS, defaultValue: 0 },
                { name: opts.setting.SETTING_SIGN_IN_LAST_DATE, defaultValue: '1753/1/1' },
                { name: opts.setting.SETTING_SIGN_IN_IGNORE_DATE, defaultValue: '1753/1/1' }
            ];
            this.upgradeConfig();
            value.forEach((v) => util.getValue(v.name) === undefined && util.setValue(v.name, v.defaultValue));
        },
        // 升级配置项
        upgradeConfig() {
            const upgradeConfItem = (oldConfKey, newConfKey) => {
                if (util.getValue(oldConfKey) && util.getValue(newConfKey) === undefined) {
                    util.clog(`升级配置项 ${oldConfKey} 为 ${newConfKey}`);
                    util.setValue(newConfKey, util.getValue(oldConfKey));
                    GM_deleteValue(oldConfKey);
                }
            };
            upgradeConfItem('menu_signInTime', opts.setting.SETTING_SIGN_IN_LAST_DATE);
        },
        loginStatus: false,
        //检查是否登陆
        checkLogin() {
            if (unsafeWindow.meCard && unsafeWindow.meCard.logined) {
                this.loginStatus = true;
                util.clog(`当前登录用户 ${unsafeWindow.meCard.user.member_name} (ID ${unsafeWindow.meCard.user.member_id})`);
            }
        },
        // 自动签到
        autoSignIn(rand) {
            if (!this.loginStatus) return
            if (util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) === 0) return;

            let timeNow = util.getCurrentDate(),
                timeOld = util.getValue(opts.setting.SETTING_SIGN_IN_LAST_DATE);
            if (!timeOld || timeOld != timeNow) { // 是新的一天
                util.setValue(opts.setting.SETTING_SIGN_IN_LAST_DATE, timeNow); // 写入签到时间以供后续比较
                this.signInRequest(rand);
            }
        },
        // 重新签到
        reSignIn() {
            if (!this.loginStatus) return;
            if (util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) === 0) {
                unsafeWindow.mscAlert('提示', this.getMenuStateText(this._menus[0], 0) + ' 状态时不支持重新签到!');
                return;
            }

            util.setValue(opts.setting.SETTING_SIGN_IN_LAST_DATE, '1753/1/1');
            location.reload();
        },
        addSignTips() {
            if (!this.loginStatus) return
            if (util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) !== 0) return;

            const timeNow = util.getCurrentDate();
            const { SETTING_SIGN_IN_IGNORE_DATE, SETTING_SIGN_IN_LAST_DATE } = opts.setting;
            const timeIgnore = util.getValue(SETTING_SIGN_IN_IGNORE_DATE);
            const timeOld = util.getValue(SETTING_SIGN_IN_LAST_DATE);

            if (timeNow === timeIgnore || timeNow === timeOld) return;

            const _this = this;
            let tip = document.createElement('div');
            tip.className = "nsplus-tip";
            let tip_p = document.createElement('p');
            tip_p.innerHTML = '今天你还没有签到哦!&emsp;【<a class="sign_in_btn" data-rand="true" href="javascript:;">随机抽个鸡腿</a>】&emsp;【<a class="sign_in_btn" data-rand="false" href="javascript:;">只要5个鸡腿</a>】&emsp;【<a id="sign_in_ignore" href="javascript:;">今天不再提示</a>】';
            tip.appendChild(tip_p);
            tip.querySelectorAll('.sign_in_btn').forEach(function (item) {
                item.addEventListener("click", function (e) {
                    const rand = util.data(this, 'rand');
                    _this.signInRequest(rand);
                    tip.remove();
                    util.setValue(opts.setting.SETTING_SIGN_IN_LAST_DATE, timeNow); // 写入签到时间以供后续比较
                })
            });
            tip.querySelector('#sign_in_ignore').addEventListener("click", function (e) {
                tip.remove();
                util.setValue(opts.setting.SETTING_SIGN_IN_IGNORE_DATE, timeNow);
            });

            document.querySelector('#nsk-frame').before(tip);
        },
        signInRequest(rand) {
            util.post('/api/attendance?random=' + (rand || true), {}, { "Content-Type": "application/json" }, '').then(function (json) {
                if (json.success) {
                    message.success('签到成功!今天午饭+' + json.gain + '个鸡腿; 积攒了' + json.current + '个鸡腿了');
                    //GM_notification({ text: '签到成功!今天午饭+' + json.gain + '个鸡腿; 积攒了' + json.current + '个鸡腿了', timeout: 3500 });
                }
                else {
                    message.info(json.message);
                    //GM_notification({ text: json.message, timeout: 3500 });
                }
            }).catch(function (err) {
                util.clog(err)
            });
            util.clog(`[${name}] 签到完成`);
        },
        is_show_quick_comment: false,
        quickComment() {
            if (!this.loginStatus) return

            if (!opts.comment.pathPattern.test(location.pathname)) return;
            const _this = this;
            const onClick = (e) => {
                if (_this.is_show_quick_comment) {
                    return;
                }
                e.preventDefault();
                const md = document.querySelector('.md-editor');
                md.style.position = 'fixed'; md.style.top = '50%'; md.style.left = '50%'; md.style.transform = 'translate(-50%, -50%)'; md.style.width = '100%'; md.style.maxWidth = '720px'; md.style.zIndex = '999'; _this.addEditorCloseButton();

                _this.is_show_quick_comment = true;
            };
            const commentDiv = document.querySelector('#fast-nav-button-group #back-to-parent').cloneNode(true);
            commentDiv.id = 'back-to-comment';
            commentDiv.innerHTML = '<svg class="iconpark-icon" style="width: 24px; height: 24px;"><use href="#comments"></use></svg>';
            commentDiv.addEventListener("click", onClick);
            document.querySelector('#back-to-parent').before(commentDiv);
            document.querySelectorAll('div.comment-menu > div:nth-last-child(1),div.comment-menu > div:nth-last-child(2) ').forEach(function (item) { item.addEventListener("click", onClick, { capture: true, once: false, passive: false }); })
        },
        addEditorCloseButton() {
            const _this = this;
            const fullScreenToolbar = document.querySelector('#editor-body .window_header > :last-child');
            const cloneToolbar = fullScreenToolbar.cloneNode(true);
            cloneToolbar.setAttribute('title', '关闭');
            cloneToolbar.querySelector('span').classList.replace('i-icon-full-screen-one', 'i-icon-close');
            cloneToolbar.querySelector('span').innerHTML = '<svg width="16" height="16" viewBox="0 0 48 48" fill="none"><path d="M8 8L40 40" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8 40L40 8" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
            cloneToolbar.addEventListener("click", function (e) {
                document.querySelector('.md-editor').style = "";
                this.remove();
                _this.is_show_quick_comment = false;
            });
            fullScreenToolbar.after(cloneToolbar);
        },
        //新窗口打开帖子
        openPostInNewTab() {
            util.openLinkInNewTab('.post-title>a[href]');
        },
        //自动点击跳转页链接
        autoJump() {
            if (!/^\/jump/.test(location.pathname)) return;
            document.querySelector('.btn').click();
        },
        blockPost(ele) {
            ele = ele || document;
            ele.querySelectorAll('.post-title>a[href]').forEach(function (item) {
                if (item.textContent.toLowerCase().includes("__keys__")) {
                    item.closest(".post-list-item").classList.add('blocked-post')
                }
            });
        },
        //拉黑用户
        blockMemberDOMInsert() {
            if (!this.loginStatus) return;

            const _this = this;
            Array.from(document.querySelectorAll(".post-list .post-list-item,.content-item")).forEach((function (t, n) {
                var r = t.querySelector('.avatar-normal');
                r.addEventListener("click", (function (n) {
                    n.preventDefault();
                    let intervalId = setInterval(async () => {
                        const userCard = document.querySelector('div.user-card.hover-user-card');
                        const pmButton = document.querySelector('div.user-card.hover-user-card a.btn');
                        if (userCard && pmButton) {
                            clearInterval(intervalId);
                            const dataVAttrs = util.getAttributesByPrefix(userCard, 'data-v');
                            const userName = userCard.querySelector('a.Username').textContent;
                            const blockBtn = document.createElement("a");
                            for (let k in dataVAttrs) {
                                blockBtn.setAttribute(k, dataVAttrs[k]);
                            };
                            blockBtn.onclick = function (e) {
                                e.preventDefault();
                                unsafeWindow.mscConfirm(`确定要拉黑“${userName}”吗?`, '你可以在本站的 设置=>屏蔽用户 中解除屏蔽', function () { _this.blockMember(userName); });
                            };
                            blockBtn.className = "btn";
                            blockBtn.style.float = "left";
                            blockBtn.style.backgroundColor = 'rgba(0,0,0,.3)';
                            blockBtn.textContent = "拉黑";
                            pmButton.after(blockBtn);
                        }
                    }, 50);
                }))
            }))
        },
        // 黑名单
        blockMember(userName) {
            util.post("/api/block-list/add", { "block_member_name": userName }, { "Content-Type": "application/json" }, '').then(function (data) {
                if (data.success) {
                    let msg = '屏蔽用户【' + userName + '】成功!';
                    unsafeWindow.mscAlert(msg);
                    util.clog(msg);
                } else {
                    let msg = '屏蔽用户【' + userName + '】失败!' + data.message;
                    unsafeWindow.mscAlert(msg);
                    util.clog(msg);
                }
            }).catch(function (err) {
                util.clog(err);
            });
        },
        addLevelTag() {//添加等级标签
            if (!this.loginStatus) return;
            if (!opts.comment.pathPattern.test(location.pathname)) return;

            this.getUserInfo(unsafeWindow.__config__.postData.op.uid, function (user) {
                const span = document.createElement('span');
                span.setAttribute('title', 'Joined ' + user.created_at_str);
                span.innerHTML = `<span>Lv ${user.rank}</span>`;
                span.classList = `nsk-badge role-tag user-level user-lv${user.rank}`;

                const authorLink = document.querySelector('#nsk-body .nsk-post .nsk-content-meta-info .author-info>a');
                if (authorLink != null) {
                    authorLink.after(span);
                }
            });
        },
        getUserInfo(uid, callback) {
            util.get(`/api/account/getInfo/${uid}`, {}, 'json').then(function (data) {
                if (!data.success) {
                    util.clog(data);
                    return;
                }
                callback && callback(data.detail);
            });
        },
        UserCardEx() {
            const updateNotificationElement = (element, href, iconHref, text, count) => {
                element.querySelector("a").setAttribute("href", `${href}`);
                element.querySelector("a > svg > use").setAttribute("href", `${iconHref}`)
                element.querySelector("a > :nth-child(2)").textContent = `${text} `;
                element.querySelector("a > :last-child").textContent = count;
                if (count > 0) {
                    element.querySelector("a > :last-child").classList.add("notify-count");
                }
                return element;
            };

            const userCard = document.querySelector(".user-card .user-stat");
            const lastElement = userCard.querySelector(".stat-block:first-child > :last-child");
            const unViewedCount = unsafeWindow.__config__.user.unViewedCount;

            if (lastElement.querySelector("a > .notify-count:last-child")) {
                lastElement.querySelector("a > .notify-count:last-child").classList.remove("notify-count");
            }

            const atMeElement = lastElement.cloneNode(true);
            updateNotificationElement(atMeElement, "/notification#/atMe", "#at-sign", "我", unViewedCount.atMe);
            lastElement.after(atMeElement);

            const msgElement = lastElement.cloneNode(true);
            updateNotificationElement(msgElement, "/notification#/message?mode=list", "#envelope-one", "私信", unViewedCount.message);
            userCard.querySelector(".stat-block:last-child").append(msgElement);

            updateNotificationElement(lastElement, "/notification#/reply", "#remind-6nce9p47", "回复", unViewedCount.reply);
        },
        // 自动翻页
        autoLoading() {
            let opt = {};
            if (opts.post.pathPattern.test(location.pathname)) { opt = opts.post; }
            else if (opts.comment.pathPattern.test(location.pathname)) { opt = opts.comment; }
            else { return; }
            let is_requesting = false;
            let _this = this;
            this.windowScroll(function (direction, e) {
                if (direction === 'down') { // 下滑才准备翻页
                    let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
                    if (document.documentElement.scrollHeight <= document.documentElement.clientHeight + scrollTop + opt.scrollThreshold && !is_requesting) {
                        if (!document.querySelector(opt.nextPagerSelector)) return;
                        let nextUrl = document.querySelector(opt.nextPagerSelector).attributes.href.value;
                        is_requesting = true;
                        util.get(nextUrl, {}, 'text').then(function (data) {
                            let doc = new DOMParser().parseFromString(data, "text/html");
                            _this.blockPost(doc);//过滤帖子
                            document.querySelector(opt.postListSelector).append(...doc.querySelector(opt.postListSelector).childNodes);
                            document.querySelector(opt.topPagerSelector).innerHTML = doc.querySelector(opt.topPagerSelector).innerHTML;
                            document.querySelector(opt.bottomPagerSelector).innerHTML = doc.querySelector(opt.bottomPagerSelector).innerHTML;
                            history.pushState(null, null, nextUrl);
                            is_requesting = false;
                        }).catch(function (err) {
                            is_requesting = false;
                            util.clog(err);
                        });
                    }
                }
            });
        },
        // 滚动条事件
        windowScroll(fn1) {
            let beforeScrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
                fn = fn1 || function () { };
            setTimeout(function () { // 延时执行,避免刚载入到页面就触发翻页事件
                window.addEventListener('scroll', function (e) {
                    const afterScrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
                        delta = afterScrollTop - beforeScrollTop;
                    if (delta == 0) return false;
                    fn(delta > 0 ? 'down' : 'up', e);
                    beforeScrollTop = afterScrollTop;
                }, false);
            }, 1000)
        },
        switchMultiState(stateName, states) {//多态顺序切换
            let currState = util.getValue(stateName);
            currState = (currState + 1) % states.length;
            util.setValue(stateName, currState);
            this.registerMenus();
        },
        getMenuStateText(menu, stateVal) {
            return `${menu.states[stateVal].s1} ${menu.text}(${menu.states[stateVal].s2})`;
        },
        _menus: [
            { name: opts.setting.SETTING_SIGN_IN_STATUS, callback: (name, states) => main.switchMultiState(name, states), accessKey: '', text: '自动签到', states: [{ s1: '❌', s2: '关闭' }, { s1: '🎲', s2: '随机🍗' }, { s1: '📌', s2: '5个🍗' }] },
            { name: 're_sign_in', callback: (name, states) => main.reSignIn(), accessKey: '', text: '🔂 重新签到', states: [] },
            { name: 'advanced_settings', callback: (name, states) => unsafeWindow.mscAlert('全力施工中...'), accessKey: '', text: '⚙️ 高级设置', states: [] },
            { name: 'feedback', callback: (name, states) => GM_openInTab('http://greasyfork.icu/zh-CN/scripts/479426/feedback', { active: true, insert: true, setParent: true }), accessKey: '', text: '💬 反馈 & 建议', states: [] }
        ],
        _menuIds: [],
        registerMenus() {
            this._menuIds.forEach(function (id) {
                GM_unregisterMenuCommand(id);
            });
            this._menuIds = [];

            const _this = this;
            this._menus.forEach(function (menu) {
                let k = menu.text;
                if (menu.states.length > 0) {
                    k = _this.getMenuStateText(menu, util.getValue(menu.name));
                }
                let menuId = GM_registerMenuCommand(k, function () { menu.callback(menu.name, menu.states) });
                _this._menuIds.push(menuId);
            });
        },
        addPluginStyle() {
            let style = `
                .notie-container{ opacity: 0.8; }
            .nsplus-tip { background-color: rgba(255, 217, 0, 0.8); border: 0px solid black;  padding: 10px; text-align: center;animation: blink 5s cubic-bezier(.68,.05,.46,.96) infinite;}
            /* @keyframes blink{ 0%{background-color: red;} 25%{background-color: yellow;} 50%{background-color: blue;} 75%{background-color: green;} 100%{background-color: red;} } */
            .nsplus-tip p,.nsplus-tip p a { color: #f00 }
            .nsplus-tip p a:hover {color: #0ff}
            #back-to-comment{display:flex;}
            #fast-nav-button-group .nav-item-btn:nth-last-child(4){bottom:120px;}
            .post-list .post-title a:visited{color:#681da8}
            .role-tag.user-level.user-lv0 {background-color: rgb(199 194 194); border: 1px solid rgb(199 194 194); color: #fafafa;}
            .role-tag.user-level.user-lv1 {background-color: #ff9400; border: 1px solid #ff9400; color: #fafafa;}
            .role-tag.user-level.user-lv2 {background-color: #ff9400; border: 1px solid #ff9400; color: #fafafa;}
            .role-tag.user-level.user-lv3 {background-color: #ff3a55; border: 1px solid #ff3a55; color: #fafafa;}
            .role-tag.user-level.user-lv4 {background-color: #ff3a55; border: 1px solid #ff3a55; color: #fafafa;}
            .role-tag.user-level.user-lv5 {background-color: #de00ff; border: 1px solid #de00ff; color: #fafafa;}
            .role-tag.user-level.user-lv6 {background-color: #de00ff; border: 1px solid #de00ff; color: #fafafa;}
            .role-tag.user-level.user-lv7 {background-color: #ff0000; border: 1px solid #ff0000; color: #fafafa;}
            .role-tag.user-level.user-lv8 {background-color: #3478f7; border: 1px solid #3478f7; color: #fafafa;}
            `;

            if (document.head) {
                util.addStyle('notie-style', 'style', GM_getResourceText('notieStyle'));
                util.addStyle('nsplus-style', 'style', style);
            }

            const headObserver = new MutationObserver(() => {
                util.addStyle('notie-style', 'style', GM_getResourceText('notieStyle'));
                util.addStyle('nsplus-style', 'style', style);
            });
            headObserver.observe(document.head, { childList: true, subtree: true });
        },
        addPluginScript() {
            GM_addElement('script', {
                src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'
            });
            GM_addElement('script', {
                textContent: 'window.onload = function(){hljs.highlightAll();}'
            });
            GM_addElement("script", { textContent: `!function(e){var t,n,d,o,i,a,r='<svg><symbol id="envelope-one" viewBox="0 0 48 48" fill="none"><path stroke-linejoin="round" stroke-linecap="round" stroke-width="4" stroke="currentColor" d="M36 16V8H4v24h8" data-follow-stroke="currentColor"/><path stroke-linejoin="round" stroke-width="4" stroke="currentColor" d="M12 40h32V16H12v24Z" data-follow-stroke="currentColor"/><path stroke-linejoin="round" stroke-linecap="round" stroke-width="4" stroke="currentColor" d="m12 16 16 12 16-12" data-follow-stroke="currentColor"/><path stroke-linejoin="round" stroke-linecap="round" stroke-width="4" stroke="currentColor" d="M32 16H12v15" data-follow-stroke="currentColor"/><path stroke-linejoin="round" stroke-linecap="round" stroke-width="4" stroke="currentColor" d="M44 31V16H24" data-follow-stroke="currentColor"/></symbol><symbol id="at-sign" viewBox="0 0 48 48" fill="none"><path stroke-linejoin="round" stroke-linecap="round" stroke-width="4" stroke="currentColor" d="M44 24c0-11.046-8.954-20-20-20S4 12.954 4 24s8.954 20 20 20v0c4.989 0 9.55-1.827 13.054-4.847" data-follow-stroke="currentColor"/><path stroke-linejoin="round" stroke-width="4" stroke="currentColor" d="M24 32a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" data-follow-stroke="currentColor"/><path stroke-linejoin="round" stroke-linecap="round" stroke-width="4" stroke="currentColor" d="M32 24a6 6 0 0 0 6 6v0a6 6 0 0 0 6-6m-12 1v-9" data-follow-stroke="currentColor"/></symbol></svg>';function c(){i||(i=!0,d())}t=function(){var e,t,n;(n=document.createElement("div")).innerHTML=r,r=null,(t=n.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(n=document.body).firstChild?(t=n.firstChild).parentNode.insertBefore(e,t):n.appendChild(e))},document.addEventListener?["complete","loaded","interactive"].indexOf(document.readyState)>-1?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(d=t,o=e.document,i=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(a,50)}c()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,c())})}(window);` });
        },
        init() {
            this.initValue();
            this.addPluginStyle();
            this.checkLogin();
            this.autoSignIn();//自动签到
            this.addSignTips();//签到提示
            this.autoJump();//自动点击跳转页
            this.autoLoading();//无缝加载帖子和评论
            this.openPostInNewTab();//在新标签页打开帖子
            this.blockMemberDOMInsert();//拉黑用户
            this.blockPost();//屏蔽帖子
            this.quickComment();//快捷评论
            this.addLevelTag();//添加等级标签
            this.UserCardEx();//用户卡片扩展
            this.registerMenus();
            const css = GM_getResourceText("highlightStyle");
            GM_addStyle(css);
            this.addPluginScript();
        }
    }
    main.init();
})();