Greasy Fork

来自缓存

Greasy Fork is available in English.

微博批量删除助手|Weibo Bulk Deleter(含节奏设置 + 状态提示)

在微博个人主页/高级搜索页批量删除微博,支持开始/结束控制、自定义删除节奏、实时状态提示

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         微博批量删除助手|Weibo Bulk Deleter(含节奏设置 + 状态提示)
// @namespace    http://greasyfork.icu/zh-CN/users/umanic
// @version      1.1
// @description  在微博个人主页/高级搜索页批量删除微博,支持开始/结束控制、自定义删除节奏、实时状态提示
// @author       umanic
// @match        https://weibo.com/*
// @match        https://www.weibo.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';
    let isDeleting = false;
    let delay = 3000;  // 默认节奏 3 秒
    let statusDiv;     // 状态区域引用
    let delayInput;    // 节奏输入框引用

    function simulateClick(elem) {
        const evt = new MouseEvent('click', { bubbles: true, cancelable: true, view: window });
        elem.dispatchEvent(evt);
    }

    function deleteNext() {
        if (!isDeleting) return;

        // 删除逻辑封装为函数,便于在主文档和 iframe 中调用
        function tryDeleteInDoc(doc) {
            const moreBtns = doc.querySelectorAll('[title="更多"], [aria-label="更多"]');
            if (!moreBtns || moreBtns.length === 0) return false;

            simulateClick(moreBtns[0]);
            setTimeout(() => {
                const menuItems = Array.from(doc.querySelectorAll('div[role="button"], a[role="menuitem"]'));
                const deleteItem = menuItems.find(item => item.textContent.trim().includes('删除'));
                if (deleteItem) {
                    simulateClick(deleteItem);
                    setTimeout(() => {
                        const confirmBtns = Array.from(doc.querySelectorAll('button, a'));
                        const confirmBtn = confirmBtns.find(btn => {
                            const txt = btn.textContent.trim();
                            return txt.includes('确定') || txt.includes('删除');
                        });
                        if (confirmBtn) {
                            const targetArticle = moreBtns[0].closest('article');
                            simulateClick(confirmBtn);
                            const observer = new MutationObserver((mutations, obs) => {
                                if (!doc.body.contains(targetArticle)) {
                                    statusDiv.textContent = '删除成功';
                                    obs.disconnect();
                                    setTimeout(deleteNext, delay);
                                }
                            });
                            observer.observe(doc.body, { childList: true, subtree: true });
                            setTimeout(() => {
                                observer.disconnect();
                                if (doc.body.contains(targetArticle)) {
                                    statusDiv.textContent = '删除失败或卡住,稍后重试';
                                    setTimeout(deleteNext, delay);
                                }
                            }, 5000);
                        } else {
                            statusDiv.textContent = '未找到确认按钮';
                            setTimeout(deleteNext, delay);
                        }
                    }, 400);
                } else {
                    statusDiv.textContent = '未找到删除菜单项';
                    setTimeout(deleteNext, delay);
                }
            }, 300);
            return true;
        }

        // 先尝试在主文档删除
        let handled = tryDeleteInDoc(document);
        // 如果主文档没有找到,再尝试在同源 iframe 删除
        if (!handled) {
            const iframes = document.querySelectorAll('iframe');
            for (const frame of iframes) {
                try {
                    const doc = frame.contentDocument;
                    if (doc && tryDeleteInDoc(doc)) {
                        handled = true;
                        break;
                    }
                } catch (e) {}
            }
            if (!handled) {
                statusDiv.textContent = '未找到可删除的微博';
                // 此处可自行选择自动刷新或停止
            }
        }
    }

    function insertPanel() {
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.top = '10px';
        container.style.left = '10px';
        container.style.zIndex = '100000';
        container.style.background = '#fff';
        container.style.padding = '8px';
        container.style.border = '1px solid #ccc';
        container.style.borderRadius = '4px';
        container.style.fontSize = '14px';
        container.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';

        const startBtn = document.createElement('button');
        startBtn.textContent = '开始';
        startBtn.style.marginRight = '8px';
        startBtn.onclick = () => {
            if (!isDeleting) {
                isDeleting = true;
                statusDiv.textContent = '开始删除...';
                const val = parseInt(delayInput.value, 10);
                if (!isNaN(val) && val > 0) delay = val;
                setTimeout(deleteNext, delay);
            }
        };

        const stopBtn = document.createElement('button');
        stopBtn.textContent = '结束';
        stopBtn.style.marginRight = '8px';
        stopBtn.onclick = () => {
            isDeleting = false;
            statusDiv.textContent = '已停止';
        };

        const delayLabel = document.createElement('span');
        delayLabel.textContent = '节奏(ms):';
        delayLabel.style.marginRight = '4px';

        // 将节奏输入框赋给外层变量 delayInput
        delayInput = document.createElement('input');
        delayInput.type = 'number';
        delayInput.min = '500';
        delayInput.value = delay;
        delayInput.style.width = '80px';
        delayInput.onchange = () => {
            const val = parseInt(delayInput.value, 10);
            if (!isNaN(val) && val > 0) delay = val;
        };

        // 将状态区域赋给外层变量 statusDiv
        statusDiv = document.createElement('div');
        statusDiv.style.marginTop = '6px';
        statusDiv.style.fontSize = '12px';
        statusDiv.style.color = '#555';
        statusDiv.textContent = '未开始';

        container.appendChild(startBtn);
        container.appendChild(stopBtn);
        container.appendChild(delayLabel);
        container.appendChild(delayInput);
        container.appendChild(statusDiv);

        document.body.appendChild(container);
    }

    window.addEventListener('load', insertPanel);
})();