妖火站内信及帖子回复提醒

自动获取新私信和帖子回复,并在页面内通过Toast弹窗提示用户

目前为 2025-01-20 提交的版本。查看 最新版本

// ==UserScript==
// @name         妖火站内信及帖子回复提醒
// @namespace    https://www.yaohuo.me/bbs/userinfo.aspx?touserid=20740
// @version      1.0.6
// @description  自动获取新私信和帖子回复,并在页面内通过Toast弹窗提示用户
// @author       SiXi
// @match        *://www.yaohuo.me/*
// @match        *://yaohuo.me/*
// @icon         https://yaohuo.me/css/favicon.ico
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 首次运行时会将所有未读消息和帖子都默认为新数据,直接无视即可,刷新页面或等待8秒后即可自动消失

    // 配置项
    const CHECK_INTERVAL = 15000;  // 15秒更新检查一次数据
    const MESSAGE_URL = 'https://www.yaohuo.me/bbs/messagelist.aspx';
    const POSTS_URL = 'https://www.yaohuo.me/bbs/book_list_search.aspx?type=pub&classid=0&key=XXXXX'; // 将XXXX替换为你的妖火ID(纯数字,不是用户名)
    const ENABLE_POSTS_NOTIFICATION = true; // 是否启用帖子回复提醒功能,true开启,false关闭

    // 本地存储的键
    const MESSAGES_STORAGE_KEY = 'yaohuoMessages';
    const POSTS_STORAGE_KEY = 'yaohuoPosts';

    // 创建Toast提醒
    function showToast(messages, isPost = false) {
        // 先移除旧的toast
        const oldToasts = document.querySelectorAll('.toast');
        oldToasts.forEach(toast => toast.remove());

        // 创建新的toast容器
        const toastContainer = document.createElement('div');
        toastContainer.className = 'toast-container';
        document.body.appendChild(toastContainer);

        // 按顺序添加每条消息的toast
        messages.forEach((msg, index) => {
            const toast = document.createElement('div');
            toast.className = 'toast';
            if (isPost) {
                toast.innerHTML = `
                    帖子<a href="https://www.yaohuo.me${msg.url}" target="_blank">${msg.title}</a>有新回复,<a href="https://www.yaohuo.me${msg.replyUrl}" target="_blank">点击查看回复列表</a>
                `;
            } else {
                toast.innerHTML = `
                    新私信:${msg.from} <small>${msg.date}</small><br>
                    内 容:<a href="https://www.yaohuo.me${msg.url}" target="_blank">${msg.text}</a>
                `;
            }
            toastContainer.appendChild(toast);

            // 设置toast动画
            setTimeout(() => {
                toast.classList.add('show');
            }, 100);

            // 自动关闭Toast
            setTimeout(() => {
                toast.classList.remove('show');
                setTimeout(() => {
                    toast.remove();
                }, 300);
            }, 8000); // Toast消息延迟几秒关闭(单位ms)
        });
    }

    // 添加CSS样式
    function addToastStyles() {
        const style = document.createElement('style');
        style.innerHTML = `
            .toast-container {
                position: fixed;
                bottom: 20px;
                right: 20px;
                z-index: 9999;
                display: flex;
                flex-direction: column-reverse;
                gap: 10px;
            }

            .toast {
                background-color: #333;
                color: white;
                padding: 10px 20px;
                border-radius: 5px;
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
                opacity: 0;
                transition: opacity 0.3s, transform 0.3s;
                transform: translateY(10px);
            }

            .toast.show {
                opacity: 1;
                transform: translateY(0);
            }

            .toast a {
                color: #4CAF50;
                text-decoration: underline;
            }
        `;
        document.head.appendChild(style);
    }

    // 本地存储获取和设置
    function saveMessages(messages) {
        GM_setValue(MESSAGES_STORAGE_KEY, messages);
    }

    function getMessages() {
        return GM_getValue(MESSAGES_STORAGE_KEY, []);
    }

    function savePosts(posts) {
        GM_setValue(POSTS_STORAGE_KEY, posts);
    }

    function getPosts() {
        return GM_getValue(POSTS_STORAGE_KEY, []);
    }

    // 获取站内信
    function fetchMessages() {
        console.log('正在请求站内信...');
        GM_xmlhttpRequest({
            method: 'GET',
            url: MESSAGE_URL,
            onload: function(response) {
                console.log('站内信请求成功');
                const data = response.responseText;
                const parser = new DOMParser();
                const doc = parser.parseFromString(data, 'text/html');
                const messageElements = doc.querySelectorAll('.listmms.line1, .listmms.line2');
                const newMessages = [];

                messageElements.forEach(element => {
                    // 检查是否有新站内信标志
                    const newIcon = element.querySelector('img[src="/NetImages/new.gif"]');// 为了简化逻辑,是通过获取是否有新消息图片标识来判断
                    if (newIcon) {
                        const linkElement = element.querySelector('a');
                        const fromElement = element.querySelector('span.laizi');
                        const dateElement = element.textContent.match(/\d{4}\/\d{1,2}\/\d{1,2} \d{1,2}:\d{2}/);

                        if (linkElement && fromElement && dateElement) {
                            const url = linkElement.getAttribute('href');
                            const text = linkElement.textContent.trim();
                            const from = fromElement.nextSibling.textContent.trim();
                            const date = dateElement[0];

                            newMessages.push({ url, text, from, date });
                        }
                    }
                });

                // 获取本地存储键值与新获取到的数据比对(如果未读消息有new.gif但是本地已经保存了这条消息,那就表示仅仅是未读消息,不进行提醒)
                const oldMessages = getMessages();
                const uniqueNewMessages = newMessages.filter(msg => !oldMessages.some(old => old.url === msg.url));

                if (uniqueNewMessages.length > 0) {
                    console.log('发现新站内信:', uniqueNewMessages);
                    // 保存新数据
                    saveMessages([...oldMessages, ...uniqueNewMessages]);

                    // 单列平铺显示所有新消息的toast
                    showToast(uniqueNewMessages);
                } else {
                    console.log('没有新站内信');
                }
            },
            onerror: function(err) {
                console.error('获取站内信失败:', err);
                showToast([{ from: '系统', date: '', url: '', text: '获取站内信失败' }]);
            }
        });
    }

    // 获取帖子回复
    function fetchPosts() {
        if (!ENABLE_POSTS_NOTIFICATION) return;

        console.log('正在请求帖子回复...');
        GM_xmlhttpRequest({
            method: 'GET',
            url: POSTS_URL,
            onload: function(response) {
                console.log('帖子回复请求成功');
                const data = response.responseText;
                const parser = new DOMParser();
                const doc = parser.parseFromString(data, 'text/html');
                const postElements = doc.querySelectorAll('.listdata.line1, .listdata.line2');
                const newPosts = [];

                postElements.forEach(element => {
                    const titleElement = element.querySelector('a.topic-link');
                    const replyElement = element.querySelector('a.topic-link[href*="book_re.aspx"]');
                    const replyCount = replyElement ? replyElement.textContent.trim() : '0';

                    if (titleElement && replyElement) {
                        const title = titleElement.textContent.trim();
                        const url = titleElement.getAttribute('href');
                        const replyUrl = replyElement.getAttribute('href');

                        newPosts.push({ title, url, replyUrl, replyCount });
                    }
                });

                // 比较新帖子回复与本地存储的帖子回复(主要是通过提取每个帖子回帖数量判断是否有增加)
                const oldPosts = getPosts();
                const updatedPosts = newPosts.filter(newPost => {
                    const oldPost = oldPosts.find(old => old.url === newPost.url);
                    return !oldPost || oldPost.replyCount !== newPost.replyCount;
                });

                if (updatedPosts.length > 0) {
                    console.log('发现新帖子回复:', updatedPosts);
                    // 保存新数据
                    savePosts([...oldPosts, ...updatedPosts]);

                    // 显示所有新回复的toast
                    showToast(updatedPosts, true);
                } else {
                    console.log('没有新帖子回复');
                }
            },
            onerror: function(err) {
                console.error('获取帖子回复失败:', err);
                showToast([{ title: '系统', url: '', replyUrl: '', replyCount: '获取帖子回复失败' }], true);
            }
        });
    }

    // 设置定时器
    setInterval(() => {
        fetchMessages();
        fetchPosts();
    }, CHECK_INTERVAL);

    // 首次加载时立即获取一次(每一次进入妖火任意页面时都会默认先请求一次)
    addToastStyles();
    fetchMessages();
    fetchPosts();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址