b站一键删除消息中心通知(更新可选择性删除)

适配新版B站消息中心,可自定义保留顶部通知数量

// ==UserScript==
// @name         b站一键删除消息中心通知(更新可选择性删除)
// @namespace    http://tampermonkey.net/
// @version      2.1.0
// @description  适配新版B站消息中心,可自定义保留顶部通知数量
// @author       szhclear
// @match        https://message.bilibili.com/*
// @icon         https://static.hdslb.com/images/favicon.ico
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue

// ==/UserScript==

(function() {
    'use strict';

    // 自定义一次运行的最大次数
    const MAX_COUNT = 999;

    // 等待网页加载完毕
    window.addEventListener('load', function() {
        // 创建控制容器
        let container = document.createElement('div');
        container.id = "sp-ac-container";
        container.style.position = "fixed";
        container.style.right = "20px";
        container.style.top = "105px";
        container.style.zIndex = "999999";
        container.style.padding = "15px";
        container.style.borderRadius = "8px";
        container.style.background = "rgba(0, 0, 0, 0.8)";
        container.style.boxShadow = "0 4px 20px rgba(0, 0, 0, 0.5)";
        container.style.color = "#fff";
        container.style.fontFamily = "sans-serif";
        container.style.backdropFilter = "blur(10px)";
        container.style.border = "1px solid rgba(255, 255, 255, 0.15)";
        container.style.width = "280px";
        container.style.maxHeight = "90vh";
        container.style.overflowY = "auto";

        // 控制面板
        container.innerHTML =
            '<div style="text-align: center; margin-bottom: 15px;">' +
                '<div style="display: inline-flex; align-items: center; background: rgba(0, 161, 214, 0.2); padding: 5px 15px; border-radius: 20px; margin-bottom: 10px;">' +
                    '<div style="width: 24px; height: 24px; background: #00a1d6; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; margin-right: 8px;">B</div>' +
                    '<div style="font-weight: bold; color: #00a1d6; font-size: 16px;">B站通知清理工具</div>' +
                '</div>' +
            '</div>' +
            '<div style="margin-bottom: 15px;">' +
                '<label style="display: block; margin-bottom: 8px; font-size: 14px; color: #a0a0c0;">保留顶部通知数量:</label>' +
                '<div style="display: flex; align-items: center;">' +
                    '<input type="number" id="preserveCount" min="0" value="0" ' +
                        'style="flex: 1; padding: 10px 12px; border: 1px solid #2d2d4d; ' +
                        'border-radius: 6px; background: rgba(20, 20, 30, 0.7); color: #fff; font-size: 14px;">' +
                    '<div style="margin-left: 10px; font-size: 12px; color: #8888aa;">0=全部删除</div>' +
                '</div>' +
            '</div>' +
            '<div style="display: flex; justify-content: center; margin-bottom: 15px;">' +
                '<button id="deleteAllBtn" style="' +
                    'width: 100%;' +
                    'padding: 12px;' +
                    'font-size: 16px;' +
                    'background: linear-gradient(135deg, #00a1d6, #0087b4);' +
                    'color: white;' +
                    'border: none;' +
                    'border-radius: 6px;' +
                    'cursor: pointer;' +
                    'font-weight: bold;' +
                    'box-shadow: 0 4px 10px rgba(0,0,0,0.3);' +
                    'transition: all 0.3s;' +
                '">' +
                    '开始删除通知' +
                '</button>' +
            '</div>' +
            '<div id="progress" style="' +
                'padding: 12px;' +
                'background: rgba(30, 30, 45, 0.6);' +
                'border-radius: 6px;' +
                'font-size: 13px;' +
                'text-align: center;' +
                'margin-bottom: 10px;' +
            '">' +
                '<div id="statusText" style="font-size: 14px; margin-bottom: 5px;">准备就绪</div>' +
                '<div id="countText" style="font-size: 12px; color: #8888aa; margin-bottom: 10px;">保留顶部: 0 条通知</div>' +
                '<div style="height: 8px; background: rgba(0, 0, 0, 0.3); border-radius: 4px; overflow: hidden;">' +
                    '<div id="progressBar" style="height: 100%; background: linear-gradient(90deg, #00a1d6, #00c1a4); border-radius: 4px; width: 0%; transition: width 0.3s;"></div>' +
                '</div>' +
            '</div>' +
            '<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 10px;">' +
                '<div style="background: rgba(40, 40, 60, 0.5); border-radius: 6px; padding: 10px; text-align: center;">' +
                    '<div id="deletedCount" style="font-size: 20px; font-weight: bold; color: #00a1d6;">0</div>' +
                    '<div style="font-size: 11px; color: #8888aa;">已删除</div>' +
                '</div>' +
                '<div style="background: rgba(40, 40, 60, 0.5); border-radius: 6px; padding: 10px; text-align: center;">' +
                    '<div id="skippedCount" style="font-size: 20px; font-weight: bold; color: #00a1d6;">0</div>' +
                    '<div style="font-size: 11px; color: #8888aa;">已跳过</div>' +
                '</div>' +
                '<div style="background: rgba(40, 40, 60, 0.5); border-radius: 6px; padding: 10px; text-align: center;">' +
                    '<div id="errorCount" style="font-size: 20px; font-weight: bold; color: #00a1d6;">0</div>' +
                    '<div style="font-size: 11px; color: #8888aa;">错误</div>' +
                '</div>' +
            '</div>' +
            '<div style="margin-top: 15px; font-size: 10px; color: #666688; text-align: center; padding-top: 10px; border-top: 1px solid rgba(255, 255, 255, 0.05);">' +
                '设置自动保存,刷新页面后仍然有效' +
            '</div>';

        document.body.appendChild(container);

        // 添加样式
        GM_addStyle(
            '#deleteAllBtn:hover {' +
                'background: linear-gradient(135deg, #0087b4, #006e95) !important;' +
                'transform: translateY(-2px);' +
                'box-shadow: 0 6px 14px rgba(0,0,0,0.4) !important;' +
            '}' +
            '#deleteAllBtn:active {' +
                'transform: translateY(0) !important;' +
            '}' +
            '#preserveCount:focus {' +
                'outline: none;' +
                'border-color: #00a1d6 !important;' +
                'box-shadow: 0 0 0 3px rgba(0, 161, 214, 0.3) !important;' +
            '}' +
            '#sp-ac-container::-webkit-scrollbar {' +
                'width: 6px;' +
            '}' +
            '#sp-ac-container::-webkit-scrollbar-track {' +
                'background: rgba(0,0,0,0.1);' +
            '}' +
            '#sp-ac-container::-webkit-scrollbar-thumb {' +
                'background: #00a1d6;' +
                'border-radius: 3px;' +
            '}'
        );

        // 加载设置
        const preserveCountInput = document.getElementById('preserveCount');
        const savedPreserveCount = GM_getValue('biliPreserveCount', 0);
        preserveCountInput.value = savedPreserveCount;

        // 保存设置
        preserveCountInput.addEventListener('change', function() {
            const value = parseInt(this.value) || 0;
            GM_setValue('biliPreserveCount', value);
            document.getElementById('countText').textContent = `保留顶部: ${value} 条通知`;
        });

        // 初始化
        document.getElementById('countText').textContent = `保留顶部: ${savedPreserveCount} 条通知`;

        document.getElementById('deleteAllBtn').addEventListener('click', startDeletion);

        async function startDeletion() {
            const btn = document.getElementById('deleteAllBtn');
            const statusText = document.getElementById('statusText');
            const countText = document.getElementById('countText');
            const preserveCountInput = document.getElementById('preserveCount');
            const progressBar = document.getElementById('progressBar');
            const deletedCountEl = document.getElementById('deletedCount');
            const skippedCountEl = document.getElementById('skippedCount');
            const errorCountEl = document.getElementById('errorCount');

            // 获取保留数量
            let preserveCount = parseInt(preserveCountInput.value);
            if (isNaN(preserveCount)) preserveCount = 0;
            preserveCount = Math.max(0, preserveCount);

            // 保存设置
            GM_setValue('biliPreserveCount', preserveCount);
            countText.textContent = `保留顶部: ${preserveCount} 条通知`;

            // 禁用按钮
            btn.disabled = true;
            btn.textContent = "删除中...";
            btn.style.opacity = "0.7";
            btn.style.cursor = "not-allowed";

            statusText.textContent = "开始删除...";
            statusText.style.color = "#fff";

            // 重置统计
            let deletedCount = 0;
            let skippedCount = 0;
            let errorCount = 0;
            deletedCountEl.textContent = "0";
            skippedCountEl.textContent = "0";
            errorCountEl.textContent = "0";

            try {
                for (let i = 0; i < MAX_COUNT; i++) {
                    // 更新进度条
                    const progressPercent = Math.min(100, Math.floor(i / MAX_COUNT * 100));
                    progressBar.style.width = `${progressPercent}%`;

                    // 获取所有删除按钮
                    const deleteButtons = document.querySelectorAll('.interaction-item__btn.invisible.delete');

                    if (!deleteButtons || deleteButtons.length === 0) {
                        statusText.textContent = "未找到更多通知";
                        break;
                    }

                    // 计算要删除的索引(跳过前preserveCount个)
                    const indexToDelete = preserveCount;

                    if (indexToDelete >= deleteButtons.length) {
                        statusText.textContent = `已完成 (保留 ${preserveCount} 条)`;
                        break;
                    }

                    // 获取要删除的按钮
                    const deleteBtn = deleteButtons[indexToDelete];

                    // 点击删除按钮
                    try {
                        deleteBtn.click();
                        await sleep(300);

                        // 获取确认按钮
                        const confirmBtn = document.querySelector('.b-modal-confirm');
                        if (confirmBtn) {
                            confirmBtn.click();
                            deletedCount++;
                            deletedCountEl.textContent = deletedCount;
                            statusText.textContent = `正在删除通知 ${i+1}/${MAX_COUNT}`;
                            statusText.style.color = "#00a1d6";
                        } else {
                            errorCount++;
                            errorCountEl.textContent = errorCount;
                            statusText.textContent = "未找到确认按钮";
                            statusText.style.color = "#f44336";
                        }
                    } catch (e) {
                        errorCount++;
                        errorCountEl.textContent = errorCount;
                        console.error("删除过程中出错:", e);
                        statusText.textContent = `错误: ${e.message || "未知错误"}`;
                        statusText.style.color = "#f44336";
                    }

                    await sleep(500);
                }

                if (deletedCount > 0) {
                    statusText.textContent = `删除完成!`;
                    statusText.style.color = "#4caf50";
                    progressBar.style.width = "100%";
                    progressBar.style.background = "#00c853";
                } else {
                    statusText.textContent = "没有需要删除的通知";
                    statusText.style.color = "#ff9800";
                    progressBar.style.width = "100%";
                    progressBar.style.background = "#ff9800";
                }
            } catch (e) {
                console.error("删除过程中出错:", e);
                statusText.textContent = "操作出错,请重试";
                statusText.style.color = "#f44336";
                progressBar.style.background = "#f44336";
            } finally {
                // 恢复按钮状态
                btn.disabled = false;
                btn.textContent = "开始删除通知";
                btn.style.opacity = "1";
                btn.style.cursor = "pointer";
            }
        }

        function sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
    });
})();