Greasy Fork

Greasy Fork is available in English.

HHCLUB-收件箱批量已读处理

自动加载所有未读消息并标记为已读

当前为 2025-08-15 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         HHCLUB-收件箱批量已读处理
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  自动加载所有未读消息并标记为已读
// @author       You
// @match        https://hhanclub.top/messages.php*
// @grant        none
// @license     MIT
// ==/UserScript==

(function() {
    'use strict';

    let isProcessing = false;
    let shouldStop = false;
    const STORAGE_KEY = 'autoReadState';

    // 状态管理
    function saveState(data) {
        localStorage.setItem(STORAGE_KEY, JSON.stringify({...data, time: Date.now()}));
    }

    function getSavedState() {
        const saved = localStorage.getItem(STORAGE_KEY);
        if (saved) {
            const data = JSON.parse(saved);
            return (Date.now() - data.time < 300000) ? data : null;
        }
        return null;
    }

    function clearState() {
        localStorage.removeItem(STORAGE_KEY);
    }

    // 创建控制面板
    function createPanel() {
        const existing = document.getElementById('msgPanel');
        if (existing) existing.remove();

        const panel = document.createElement('div');
        panel.id = 'msgPanel';
        panel.innerHTML = `
            <div id="header" style="cursor:move;font-weight:bold;text-align:center;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,0.3)">📬 消息处理助手</div>
            <div id="count" style="margin-bottom:10px;padding:8px;background:rgba(255,255,255,0.2);border-radius:5px;text-align:center">检测中...</div>
            <div id="status" style="margin-bottom:10px;padding:6px;background:rgba(0,0,0,0.2);border-radius:5px;font-size:12px">点击开始处理</div>
            <div id="progress" style="display:none;margin-bottom:10px">
                <div id="progressText" style="font-size:11px;margin-bottom:3px">准备中...</div>
                <div style="background:rgba(255,255,255,0.3);height:6px;border-radius:3px;margin:5px 0">
                    <div id="progressBar" style="background:#4CAF50;height:100%;width:0%;border-radius:3px;transition:width 0.3s"></div>
                </div>
            </div>
            <div style="display:flex;gap:8px;margin-top:10px">
                <button id="startBtn" style="background:#4CAF50;color:white;border:none;padding:8px 12px;border-radius:5px;cursor:pointer;flex:1;font-size:12px">🚀 开始</button>
                <button id="stopBtn" style="display:none;background:#f44336;color:white;border:none;padding:8px 12px;border-radius:5px;cursor:pointer;flex:1;font-size:12px">⏹️ 停止</button>
            </div>
        `;

        panel.style.cssText = `
            position: fixed !important;
            top: 50% !important;
            right: 20px !important;
            transform: translateY(-50%) !important;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
            color: white !important;
            padding: 15px !important;
            border-radius: 10px !important;
            font-size: 13px !important;
            width: 250px !important;
            box-shadow: 0 8px 20px rgba(0,0,0,0.5) !important;
            z-index: 999999 !important;
            font-family: Arial, sans-serif !important;
            border: 2px solid rgba(255,255,255,0.3) !important;
        `;

        document.body.appendChild(panel);
        setTimeout(() => makeDraggable(panel), 100);
        return panel;
    }

    // 拖拽功能
    function makeDraggable(el) {
        let pos1=0,pos2=0,pos3=0,pos4=0;
        const header = document.getElementById('header');
        if (header) {
            header.onmousedown = (e) => {
                e.preventDefault();
                pos3 = e.clientX; pos4 = e.clientY;
                document.onmouseup = () => { document.onmouseup = document.onmousemove = null; };
                document.onmousemove = (e) => {
                    pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY;
                    el.style.top = Math.max(0, Math.min(window.innerHeight-el.offsetHeight, el.offsetTop-pos2)) + "px";
                    el.style.left = Math.max(0, Math.min(window.innerWidth-el.offsetWidth, el.offsetLeft-pos1)) + "px";
                    el.style.right = 'auto'; el.style.transform = 'none';
                };
            };
        }
    }

    // 更新显示函数
    function updateStatus(msg) {
        const statusEl = document.getElementById('status');
        if (statusEl) statusEl.textContent = msg;
        console.log(`[消息处理] ${msg}`);
    }

    function updateCount() {
        const el = document.querySelector('div[style="margin:auto auto;"]');
        const count = el ? parseInt(el.textContent) || 0 : 0;
        const countEl = document.getElementById('count');
        if (countEl) countEl.innerHTML = `📩 未读:<b>${count}</b> 条`;
        return count;
    }

    function updateProgress(current, total, text) {
        const container = document.getElementById('progress');
        const bar = document.getElementById('progressBar');
        const textEl = document.getElementById('progressText');

        if (container && bar && textEl) {
            if (total > 0) {
                container.style.display = 'block';
                textEl.textContent = text;
                bar.style.width = (current/total*100) + '%';
            } else {
                container.style.display = 'none';
            }
        }
    }

    // 点击未读短讯
    async function clickUnreadMessages() {
        const labels = document.querySelectorAll('label');
        for (let label of labels) {
            if (label.textContent.includes('未读短讯')) {
                updateStatus('点击未读短讯...');
                label.click();
                return true;
            }
        }
        return false;
    }

    // 点击加载更多
    async function clickLoadMore() {
        if (shouldStop) return false;

        const btn = document.querySelector('button[onclick="loadMail()"]');
        if (btn && !btn.disabled) {
            updateStatus('点击加载更多...');
            btn.click();

            return new Promise(resolve => {
                const check = setInterval(() => {
                    if (shouldStop) {
                        clearInterval(check);
                        resolve(false);
                        return;
                    }

                    const loading = document.getElementById('mail-loading');
                    if (!loading || loading.style.display === 'none') {
                        clearInterval(check);
                        setTimeout(() => resolve(true), 300);
                    }
                }, 100);
            });
        }
        return false;
    }

    // 正确的全选功能
    async function clickSelectAll() {
        if (shouldStop) return false;

        updateStatus('正在查找全选按钮...');

        // 根据提供的元素信息查找全选按钮
        const selectAllBtn = document.querySelector('input[type="button"][value="全选"][onclick*="check(form"]');

        if (!selectAllBtn) {
            updateStatus('❌ 未找到全选按钮');
            return false;
        }

        console.log('找到全选按钮:', selectAllBtn);
        updateStatus('找到全选按钮,当前文字: ' + selectAllBtn.value);

        // 检查按钮当前状态
        if (selectAllBtn.value === '全选') {
            updateStatus('点击全选按钮...');
            selectAllBtn.click();

            // 等待按钮状态改变
            await new Promise(resolve => setTimeout(resolve, 1000));

            // 检查按钮文字是否变成了"全不选"
            if (selectAllBtn.value === '全不选') {
                updateStatus('✅ 全选成功!按钮已变为: ' + selectAllBtn.value);
                return true;
            } else {
                updateStatus('❌ 全选可能失败,按钮文字仍为: ' + selectAllBtn.value);
                // 再次尝试点击
                selectAllBtn.click();
                await new Promise(resolve => setTimeout(resolve, 1000));

                if (selectAllBtn.value === '全不选') {
                    updateStatus('✅ 第二次尝试全选成功!');
                    return true;
                } else {
                    updateStatus('❌ 全选失败,按钮文字: ' + selectAllBtn.value);
                    return false;
                }
            }
        } else if (selectAllBtn.value === '全不选') {
            updateStatus('✅ 已经是全选状态 (按钮显示: 全不选)');
            return true;
        } else {
            updateStatus('❌ 未知的按钮状态: ' + selectAllBtn.value);
            return false;
        }
    }

    // 等待并显示倒计时
    async function waitWithCountdown(seconds, message) {
        for (let i = seconds; i > 0; i--) {
            if (shouldStop) return false;
            updateStatus(`${message} (${i}秒)`);
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
        return true;
    }

    // 继续处理
    async function continueProcess(count) {
        const clickTimes = Math.ceil(count / 10);
        updateStatus(`开始处理 ${count} 条消息,需加载 ${clickTimes} 次`);

        // 加载更多消息
        for (let i = 0; i < clickTimes; i++) {
            if (shouldStop) return;
            updateProgress(i + 1, clickTimes + 2, `加载 ${i+1}/${clickTimes}`);
            const success = await clickLoadMore();
            if (!success) break;
        }

        if (shouldStop) return;

        // 等待页面稳定
        updateStatus('等待页面稳定...');
        await new Promise(resolve => setTimeout(resolve, 2000));

        // 全选消息 - 使用正确的全选逻辑
        updateProgress(clickTimes + 1, clickTimes + 2, '全选消息');
        const selectAllSuccess = await clickSelectAll();

        if (!selectAllSuccess) {
            updateStatus('❌ 全选失败,无法继续');
            return;
        }

        // 等待3秒确保全选完全生效
        if (shouldStop) return;
        if (!(await waitWithCountdown(3, '等待全选完全生效'))) return;

        // 设为已读
        updateProgress(clickTimes + 2, clickTimes + 2, '标记已读');
        const markReadBtn = document.querySelector('input[name="markread"][value="设为已读"]');
        if (markReadBtn) {
            updateStatus('点击设为已读...');
            markReadBtn.click();
            await new Promise(resolve => setTimeout(resolve, 1000));

            clearState();
            updateStatus('✅ 处理完成!');
            setTimeout(updateCount, 2000);
        } else {
            updateStatus('❌ 未找到设为已读按钮');
        }
    }

    // 主处理函数
    async function processMessages() {
        if (isProcessing) return;
        isProcessing = true;
        shouldStop = false;

        const startBtn = document.getElementById('startBtn');
        const stopBtn = document.getElementById('stopBtn');
        if (startBtn) startBtn.style.display = 'none';
        if (stopBtn) stopBtn.style.display = 'block';

        try {
            const count = updateCount();
            if (count === 0) {
                updateStatus('✅ 没有未读消息');
                return;
            }

            saveState({phase: 'afterUnread', count});
            updateProgress(1, 4, '点击未读短讯');

            if (await clickUnreadMessages()) {
                updateStatus('页面刷新中...');
                return;
            }

            await continueProcess(count);

        } catch(e) {
            updateStatus('❌ 错误: ' + e.message);
            console.error('处理错误:', e);
        } finally {
            resetUI();
        }
    }

    function stopProcess() {
        shouldStop = true;
        clearState();
        updateStatus('❌ 用户停止操作');
        resetUI();
    }

    function resetUI() {
        isProcessing = false;
        shouldStop = false;
        setTimeout(() => {
            const startBtn = document.getElementById('startBtn');
            const stopBtn = document.getElementById('stopBtn');
            if (startBtn) startBtn.style.display = 'block';
            if (stopBtn) stopBtn.style.display = 'none';
            updateProgress(0, 0, '');
        }, 2000);
    }

    // 初始化
    function init() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', init);
            return;
        }

        createPanel();

        const saved = getSavedState();
        if (saved && saved.phase === 'afterUnread') {
            isProcessing = true;
            const startBtn = document.getElementById('startBtn');
            const stopBtn = document.getElementById('stopBtn');
            if (startBtn) startBtn.style.display = 'none';
            if (stopBtn) stopBtn.style.display = 'block';
            updateStatus('🔄 页面刷新后继续处理...');
            setTimeout(() => continueProcess(saved.count), 2000);
        }

        setTimeout(() => {
            const startBtn = document.getElementById('startBtn');
            const stopBtn = document.getElementById('stopBtn');
            if (startBtn) startBtn.onclick = processMessages;
            if (stopBtn) stopBtn.onclick = stopProcess;
            updateCount();
            updateStatus('🎯 准备就绪');
        }, 100);
    }

    init();
})();