NodeSeek增强

自动签到、自动滚动翻页

目前為 2024-02-21 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         NodeSeek增强
// @namespace    http://www.nodeseek.com/
// @version      0.3-alpha
// @description  自动签到、自动滚动翻页
// @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_notification
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getResourceText
// @grant        GM_addElement
// @grant        GM_addStyle
// @grant        unsafeWindow
// @run-at       document-end
// @license      GPL-3.0 License
// @supportURL   https://www.nodeseek.com/notification#/message?mode=talk&to=8110
// @homepageURL  https://www.nodeseek.com/post-36263-1
// ==/UserScript==

(function () {
    'use strict';

    const util = {
        clog(c) {
            console.group("%c %c [NodeSeek增强]", `background:url(${GM_info.script.icon}) center center no-repeat;background-size:12px;padding:3px`, "");
            console.log(c);
            console.groupEnd();
        },
        parseQuery(name) {
            let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
            let r = location.search.substr(1).match(reg);
            if (r != null) return (r[2]);
            return null;
        },
        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);
        },
        isHidden(el) {
            try {
                return el.offsetParent === null;
            } catch (e) {
                return false;
            }
        },
        query(selector) {
            if (Array.isArray(selector)) {
                let obj = null;
                for (let i = 0; i < selector.length; i++) {
                    let o = document.querySelector(selector[i]);
                    if (o) {
                        obj = o;
                        break;
                    }
                }
                return obj;
            }
            return document.querySelector(selector);
        },
        getAttributesByPrefix(element, prefix) {
            var attributes = element.attributes;
            var matchingAttributes = {};
            for (var attribute of attributes) {
                var attributeName = attribute.name;
                var attributeValue = attribute.value;

                if (attributeName.startsWith(prefix)) {
                    matchingAttributes[attributeName] = attributeValue;
                }
            }
            return matchingAttributes;
        },
        openLinkInNewTab(selector) {
            var allLinks = document.querySelectorAll(selector);

            allLinks.forEach(function (link) {
                link.setAttribute('target', '_blank');
            });
        }
    };

    const opts = {
        post: {
            pathPattern: /^\/(categories\/|page|award|$)/,
            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'
        }
    };

    let main = {
        // 初始化配置数据
        initValue() {
            let value = [{
                name: opts.setting.SETTING_SIGN_IN_STATUS,
                value: 0
            }];

            value.forEach((v) => {
                if (util.getValue(v.name) === undefined) {
                    util.setValue(v.name, v.value);
                }
            });
        },
        loginStatus: false,
        //检查是否登陆
        checkLogin() {
            if (document.querySelector('#nsk-right-panel-container>.user-card')) {
                this.loginStatus = true;
                util.clog('已登录');
            }
        },
        // 自动签到
        autoSignIn(rand) {
            if (!this.loginStatus) return

            let localTimezoneOffset = (new Date()).getTimezoneOffset();
            let beijingOffset = 8 * 60;
            let beijingTime = new Date(Date.now() + (localTimezoneOffset + beijingOffset) * 60 * 1000);
            let timeNow = `${beijingTime.getFullYear()}/${(beijingTime.getMonth() + 1)}/${beijingTime.getDate()}`,
                timeOld = util.getValue('menu_signInTime');
            if (!timeOld || timeOld != timeNow) { // 是新的一天
                util.setValue('menu_signInTime', timeNow); // 写入签到时间以供后续比较

                GM_xmlhttpRequest({
                    url: '/api/attendance?random=' + (rand || true),
                    method: 'POST',
                    timeout: 4000
                    , onload: function (res) {
                        if (res.status === 200) {
                            let json = JSON.parse(res.responseText);
                            if (json.success) {
                                GM_notification({ text: '签到成功!今天午饭+' + json.gain + '个鸡腿; 积攒了' + json.current + '个鸡腿了', timeout: 3500 });
                            }
                            else {
                                GM_notification({ text: '签到失败!' + json.message, timeout: 3500 });
                            }
                        }
                    }, onerror: function (err) {
                        util.clog('error');
                        util.clog(err)
                    }
                });
                util.clog(`[NodeSeek] 签到完成`);
            }
        },
        addSignTips() {
            let tip = document.createElement('div');
            tip.className = "nsplus-tip";
            let tip_p = document.createElement('p');
            tip_p.innerHTML = '今天你还没有签到哦!&emsp;【<a href="">随机抽个鸡腿<svg data-v-372de460="" class="iconpark-icon"><use data-v-372de460="" href="#chicken-leg"></use></svg></a>】&emsp;【<a href="">只要5个鸡腿</a>】&emsp;【<a href="">今天不再提示</a>】';
            tip.appendChild(tip_p);
            document.querySelector('#nsk-frame').before(tip);
        },
        quickComment() {
            let _this = this;
            document.querySelectorAll('div.comment-menu > div:nth-child(4) ').forEach(function (item) { item.onclick = function (e) { var md = document.querySelector('.md-editor'); md.style.position = 'fixed'; md.style.bottom = 0; md.style.width = '100%'; md.style.maxWidth = '720px'; md.style.zIndex = '999'; _this.addEditorCloseButton() } })
        },
        addEditorCloseButton() {
            var linkElement = document.createElement('a');

            // 设置属性
            linkElement.setAttribute('data-v-f5a54ae2', '');
            linkElement.setAttribute('href', 'javascript:void(0)');
            linkElement.setAttribute('title', '关闭');
            linkElement.setAttribute('class', 'editor-top-button');

            // 创建 <span> 元素
            var spanElement = document.createElement('span');
            spanElement.setAttribute('data-v-f5a54ae2', '');
            spanElement.setAttribute('class', 'i-icon i-icon-close');
            spanElement.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>';

            // 将元素组合起来
            linkElement.appendChild(spanElement);
            linkElement.addEventListener("click", function (e) {
                var md = document.querySelector('.md-editor'); md.style.position = ""; md.style.bottom = ""; md.style.maxWidth = "";
                this.remove();
            });
            document.querySelector('#editor-body > div.tab-select.window_header > a[title=全屏]').after(linkElement);
        },
        //新窗口打开帖子
        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("__key__")) {
                    item.closest(".post-list-item").remove()
                }
            });
        },
        //拉黑用户
        blockMemberDOMInsert() {
            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').innerText;
                            const blockBtn = document.createElement("a");
                            for (let k in dataVAttrs) {
                                blockBtn.setAttribute(k, dataVAttrs[k]);
                            };
                            blockBtn.onclick = function (e) { e.preventDefault(); main.blockMember(userName) };
                            blockBtn.className = "btn";
                            blockBtn.style.float = "left";
                            blockBtn.innerText = "拉黑";
                            pmButton.after(blockBtn);
                        }
                    }, 50);
                }))
            }))
        },
        // 黑名单
        blockMember(userName) {
            GM_xmlhttpRequest({
                url: "/api/block-list/add",
                method: 'POST',
                headers: {
                    "Content-Type": "application/json"
                },
                data: JSON.stringify({ "block_member_name": userName }),
                onload: function (res) {
                    if (res.status === 200) {
                        let result = JSON.parse(res.responseText);
                        if (result.success) {
                            let msg = '屏蔽用户【' + userName + '】成功!';
                            unsafeWindow.mscAlert(msg);
                            util.clog(msg);
                        } else {
                            let msg = '屏蔽用户【' + userName + '】失败!' + result.message;
                            unsafeWindow.mscAlert(msg);
                            util.clog(msg);
                        }
                    }
                }, onerror: function (err) {
                    util.clog(err);
                }
            });
        },

        // 自动翻页
        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;
                        GM_xmlhttpRequest({
                            url: nextUrl,
                            method: 'GET',
                            onload: function (res) {
                                if (res.status === 200) {
                                    let doc = new DOMParser().parseFromString(res.responseText, "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;
                            },
                            onerror: function (err) {
                                is_requesting = false;
                                util.clog(err);
                            }
                        });
                    }
                }
            });
        },
        // 滚动条事件
        windowScroll(fn1) {
            var beforeScrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
                fn = fn1 || function () { };
            setTimeout(function () { // 延时执行,避免刚载入到页面就触发翻页事件
                window.addEventListener('scroll', function (e) {
                    var 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].k} ${menu.text}(${menu.states[stateVal].v})`;
        },
        _menus: [{ name: opts.setting.SETTING_SIGN_IN_STATUS, callback: (name,states)=>main.switchMultiState(name,states), accessKey: '', text: '自动签到', states: [{ k: '❌', v: '关闭' }, { k: '🎲', v: '随机🍗' }, { k: '5️⃣', v: '5个🍗' }] }],//type:"b"--boolean;type:"m"--multi state
        _menuIds: [],
        registerMenus() {
            this._menuIds.forEach(function (id) {
                GM_unregisterMenuCommand(id);
            });
            this._menuIds = [];

            const _this = this;
            this._menus.forEach(function (menu) {
                let k = menu.name;
                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}
            `;

            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 });
        },
        init() {
            this.initValue();
            this.addPluginStyle();
            this.checkLogin();
            this.autoSignIn();//自动签到
            this.autoJump();//自动点击跳转页
            this.autoLoading();//无缝加载帖子和评论
            this.openPostInNewTab();//在新标签页打开帖子
            this.blockMemberDOMInsert();//拉黑用户
            this.blockPost();//屏蔽帖子
            this.quickComment();//快捷评论
            util.getValue(opts.setting.SETTING_SIGN_IN_STATUS) === 0 && this.addSignTips();//签到提示
            this.registerMenus();
            const css = GM_getResourceText("highlightStyle");
            GM_addStyle(css);
            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();}'
            });
        }
    }
    main.init();
})();