// ==UserScript== // @name B站动态批量删除助手精简版 // @version 1.0 // @description 简洁高效的B站动态批量删除工具 // @author 浮沉㗊 // @match https://space.bilibili.com/* // @match http://space.bilibili.com/* // @icon https://static.hdslb.com/images/favicon.ico // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; // 动态类型映射 const DYNAMIC_TYPES = { REPOST: 1, IMAGE: 2, TEXT: 2, VIDEO: 8, SHORT_VIDEO: 16, ARTICLE: 64 }; // 获取UID和CSRF Token const uid = window.location.pathname.split("/")[1]; function getCSRFToken() { const match = document.cookie.match(/bili_jct=([^;]+)/); return match ? match[1] : ''; } const csrfToken = getCSRFToken(); // API类 class DynamicAPI { constructor() { this.baseUrl = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr'; } // 获取动态历史 async getSpaceHistory(offset = 0) { const url = `${this.baseUrl}/space_history?visitor_uid=${uid}&host_uid=${uid}&offset_dynamic_id=${offset}`; try { const response = await fetch(url, { credentials: 'include' }); return await response.json(); } catch (error) { console.error('获取动态失败:', error); throw error; } } // 删除动态 async deleteDynamic(dynamicId) { const url = `${this.baseUrl}/rm_dynamic`; const formData = new URLSearchParams(); formData.append('dynamic_id', dynamicId); formData.append('csrf_token', csrfToken); try { const response = await fetch(url, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData }); return await response.json(); } catch (error) { console.error('删除动态失败:', error); throw error; } } // 延时函数 sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } const api = new DynamicAPI(); let isProcessing = false; let deleteCount = 0; // UI管理器 class UIManager { constructor() { this.panel = null; this.resultBox = null; this.activeButtons = new Set(); } // 创建控制面板 createPanel() { const panel = document.createElement('div'); panel.className = 'bili-dynamic-cleaner-panel'; panel.innerHTML = `

动态清理助手

就绪
执行结果
等待执行操作...
注意:删除操作不可逆,请谨慎操作
`; // 样式 const style = document.createElement('style'); style.textContent = ` .bili-dynamic-cleaner-panel { background: linear-gradient(135deg, #e5f2ff 0%, #f0f7ff 100%); border: 2px solid #00a1d6; border-radius: 12px; padding: 20px; margin: 0 0 20px 0; box-shadow: 0 4px 20px rgba(0, 161, 214, 0.1); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; } .cleaner-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 2px solid rgba(0, 161, 214, 0.2); } .cleaner-header h3 { margin: 0; color: #00a1d6; font-size: 18px; font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } .status-indicator { padding: 6px 12px; background: #f6f7f8; border-radius: 20px; font-size: 12px; color: #666; border: 1px solid #ddd; font-weight: 500; } .status-indicator.processing { background: #fff9e6; color: #f6a500; border-color: #f6a500; animation: pulse 1.5s infinite; } .action-buttons { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 20px; } .cleaner-btn { background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); border: 2px solid #e5e7eb; border-radius: 10px; padding: 15px 12px; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); display: flex; flex-direction: column; align-items: center; gap: 6px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); position: relative; overflow: hidden; } .cleaner-btn::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(135deg, #00a1d6 0%, #0091c6 100%); opacity: 0; transition: opacity 0.3s; z-index: 1; } .cleaner-btn:hover { border-color: #00a1d6; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0, 161, 214, 0.15); } .cleaner-btn:hover::before { opacity: 0.05; } .cleaner-btn.active { background: #000000; border-color: #000000; color: #00ff9d; } .cleaner-btn.active .btn-text { color: #00ff9d; font-weight: 600; } .cleaner-btn.active .btn-desc { color: #66ffc2; } .btn-text { font-size: 15px; font-weight: 500; color: #18191c; position: relative; z-index: 2; transition: color 0.3s; } .btn-desc { font-size: 12px; color: #666; position: relative; z-index: 2; transition: color 0.3s; } .cleaner-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none !important; } .result-box { background: rgba(255, 255, 255, 0.9); border: 1px solid #e3e5e7; border-radius: 8px; padding: 15px; margin-bottom: 15px; min-height: 80px; backdrop-filter: blur(10px); } .result-title { font-size: 14px; font-weight: 600; color: #00a1d6; margin-bottom: 8px; display: flex; align-items: center; gap: 8px; } .result-title::before { content: '📊'; font-size: 16px; } .result-content { font-size: 14px; color: #18191c; line-height: 1.5; min-height: 40px; } .info-tip { font-size: 12px; color: #f6a500; padding: 10px; background: rgba(246, 165, 0, 0.1); border-radius: 6px; text-align: center; border: 1px solid rgba(246, 165, 0, 0.2); } /* 确认弹窗 */ .confirm-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.7); display: flex; align-items: center; justify-content: center; z-index: 99999; backdrop-filter: blur(5px); } .confirm-dialog { background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); border: 2px solid #00a1d6; border-radius: 16px; padding: 30px; max-width: 450px; width: 90%; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); animation: dialogSlide 0.4s cubic-bezier(0.4, 0, 0.2, 1); } .confirm-header { text-align: center; margin-bottom: 20px; } .confirm-icon { font-size: 48px; color: #ff4d4d; margin-bottom: 15px; display: block; } .confirm-title { font-size: 20px; font-weight: 600; color: #18191c; margin-bottom: 10px; } .confirm-message { font-size: 15px; color: #666; line-height: 1.5; text-align: center; margin-bottom: 25px; padding: 0 10px; } .confirm-message strong { color: #ff4d4d; } .confirm-footer { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; } .confirm-btn { padding: 14px 20px; border: none; border-radius: 10px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s; text-align: center; } .confirm-btn.cancel { background: #f6f7f8; color: #666; } .confirm-btn.cancel:hover { background: #e9ecef; } .confirm-btn.execute { background: linear-gradient(135deg, #ff4d4d 0%, #e63946 100%); color: white; box-shadow: 0 4px 15px rgba(255, 77, 77, 0.3); } .confirm-btn.execute:hover { background: linear-gradient(135deg, #e63946 0%, #cc2e3b 100%); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255, 77, 77, 0.4); } /* 动画 */ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } @keyframes dialogSlide { from { opacity: 0; transform: translateY(-30px) scale(0.9); } to { opacity: 1; transform: translateY(0) scale(1); } } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* 刷新倒计时 */ .refresh-countdown { display: inline-flex; align-items: center; gap: 5px; background: rgba(0, 161, 214, 0.1); padding: 4px 8px; border-radius: 12px; margin-left: 10px; font-size: 12px; } .countdown-number { font-weight: 600; color: #00a1d6; } `; document.head.appendChild(style); return panel; } // 更新结果框 updateResult(message, isError = false) { if (!this.resultBox) return; const content = this.resultBox.querySelector('.result-content'); if (content) { content.innerHTML = message; content.style.color = isError ? '#ff4d4d' : '#18191c'; content.style.animation = 'fadeIn 0.3s'; } } // 更新状态指示器 updateStatus(status, isProcessing = false) { const indicator = document.getElementById('statusIndicator'); if (indicator) { indicator.textContent = status; if (isProcessing) { indicator.classList.add('processing'); } else { indicator.classList.remove('processing'); } } } // 设置按钮状态 setButtonActive(buttonId, active = true) { const button = document.getElementById(buttonId); if (button) { if (active) { button.classList.add('active'); this.activeButtons.add(buttonId); } else { button.classList.remove('active'); this.activeButtons.delete(buttonId); } } } // 禁用所有按钮 disableAllButtons(disabled = true) { const buttons = document.querySelectorAll('.cleaner-btn'); buttons.forEach(btn => { btn.disabled = disabled; }); } // 显示确认弹窗 showConfirmDialog(actionType, callback) { const overlay = document.createElement('div'); overlay.className = 'confirm-overlay'; overlay.innerHTML = `
⚠️
危险操作确认
请三思而后行,一旦执行无法找回,确认执行则为同意无人有责。

--浮沉㗊

即将执行:${this.getActionName(actionType)}
`; document.body.appendChild(overlay); // 事件监听 overlay.querySelector('.cancel').addEventListener('click', () => { overlay.remove(); this.setButtonActive(this.getButtonIdByAction(actionType), false); }); overlay.querySelector('.execute').addEventListener('click', () => { overlay.remove(); callback(); }); } getActionName(actionType) { const names = { 'all': '全部删除', 'lottery': '删除抽奖动态', 'repost': '删除转发动态' }; return names[actionType] || '未知操作'; } getButtonIdByAction(actionType) { const ids = { 'all': 'btnDeleteAll', 'lottery': 'btnDeleteLottery', 'repost': 'btnDeleteRepost' }; return ids[actionType]; } // 显示刷新倒计时 showRefreshCountdown(seconds) { const resultContent = this.resultBox?.querySelector('.result-content'); if (!resultContent) return; const countdownDiv = document.createElement('div'); countdownDiv.className = 'refresh-countdown'; countdownDiv.innerHTML = `${seconds}秒后刷新页面`; resultContent.innerHTML = ''; resultContent.appendChild(countdownDiv); const interval = setInterval(() => { seconds--; countdownDiv.innerHTML = `${seconds}秒后刷新页面`; if (seconds <= 0) { clearInterval(interval); window.location.reload(); } }, 1000); } } // 核心功能函数 async function deleteAllDynamics() { if (isProcessing) return; isProcessing = true; deleteCount = 0; const uiManager = window.uiManager; try { uiManager.updateStatus('处理中...', true); uiManager.updateResult('开始获取动态数据...'); let hasMore = true; let offset = 0; while (hasMore && isProcessing) { const response = await api.getSpaceHistory(offset); if (!response || !response.data) { uiManager.updateResult('获取数据失败', true); break; } const { data } = response; hasMore = data.has_more; if (!data.cards || data.cards.length === 0) { break; } // 处理当前页的所有动态 for (const card of data.cards) { if (!isProcessing) break; offset = card.desc.dynamic_id_str; try { const result = await api.deleteDynamic(card.desc.dynamic_id_str); if (result.code === 0) { deleteCount++; uiManager.updateResult(`已删除 ${deleteCount} 条动态...`); } await api.sleep(50); // 防止请求过快 } catch (error) { console.error('删除失败:', error); } } } uiManager.updateStatus('完成'); uiManager.updateResult(`执行完成!已删除 ${deleteCount} 条动态`); // 显示倒计时并刷新 uiManager.showRefreshCountdown(5); } catch (error) { console.error('删除全部动态失败:', error); uiManager.updateStatus('错误'); uiManager.updateResult('操作失败:' + error.message, true); } finally { isProcessing = false; uiManager.disableAllButtons(false); } } async function deleteLotteryDynamics() { if (isProcessing) return; isProcessing = true; deleteCount = 0; const uiManager = window.uiManager; try { uiManager.updateStatus('处理中...', true); uiManager.updateResult('开始获取抽奖动态...'); let hasMore = true; let offset = 0; while (hasMore && isProcessing) { const response = await api.getSpaceHistory(offset); if (!response || !response.data) { uiManager.updateResult('获取数据失败', true); break; } const { data } = response; hasMore = data.has_more; if (!data.cards || data.cards.length === 0) { break; } // 处理当前页的动态 for (const card of data.cards) { if (!isProcessing) break; offset = card.desc.dynamic_id_str; // 检查是否为转发动态 if (card.desc.orig_dy_id != 0 || card.desc.type === DYNAMIC_TYPES.REPOST) { try { const content = JSON.parse(card.card); const originExtendJson = JSON.parse(content.origin_extend_json || '{}'); // 判断是否为抽奖动态(检查lott字段) if (originExtendJson.lott) { const result = await api.deleteDynamic(card.desc.dynamic_id_str); if (result.code === 0) { deleteCount++; uiManager.updateResult(`已删除 ${deleteCount} 条抽奖动态...`); } await api.sleep(50); } } catch (parseError) { // 解析失败时跳过 continue; } } } } uiManager.updateStatus('完成'); uiManager.updateResult(`执行完成!已删除 ${deleteCount} 条抽奖动态`); // 显示倒计时并刷新 uiManager.showRefreshCountdown(5); } catch (error) { console.error('删除抽奖动态失败:', error); uiManager.updateStatus('错误'); uiManager.updateResult('操作失败:' + error.message, true); } finally { isProcessing = false; uiManager.disableAllButtons(false); } } async function deleteRepostDynamics() { if (isProcessing) return; isProcessing = true; deleteCount = 0; const uiManager = window.uiManager; try { uiManager.updateStatus('处理中...', true); uiManager.updateResult('开始获取转发动态...'); let hasMore = true; let offset = 0; while (hasMore && isProcessing) { const response = await api.getSpaceHistory(offset); if (!response || !response.data) { uiManager.updateResult('获取数据失败', true); break; } const { data } = response; hasMore = data.has_more; if (!data.cards || data.cards.length === 0) { break; } // 处理当前页的动态 for (const card of data.cards) { if (!isProcessing) break; offset = card.desc.dynamic_id_str; // 检查是否为转发动态 if (card.desc.orig_dy_id != 0 || card.desc.type === DYNAMIC_TYPES.REPOST) { try { const result = await api.deleteDynamic(card.desc.dynamic_id_str); if (result.code === 0) { deleteCount++; uiManager.updateResult(`已删除 ${deleteCount} 条转发动态...`); } await api.sleep(50); } catch (error) { console.error('删除转发失败:', error); } } } } uiManager.updateStatus('完成'); uiManager.updateResult(`执行完成!已删除 ${deleteCount} 条转发动态`); // 显示倒计时并刷新 uiManager.showRefreshCountdown(5); } catch (error) { console.error('删除转发动态失败:', error); uiManager.updateStatus('错误'); uiManager.updateResult('操作失败:' + error.message, true); } finally { isProcessing = false; uiManager.disableAllButtons(false); } } // 初始化 async function init() { // 等待页面加载 await new Promise(resolve => setTimeout(resolve, 1000)); // 创建UI管理器 const uiManager = new UIManager(); window.uiManager = uiManager; // 创建控制面板 const panel = uiManager.createPanel(); // 尝试插入到页面 const insertPanel = () => { // 尝试新版界面 const newVersionContainer = document.querySelector("#app > main > div.space-dynamic > div.space-dynamic__right"); if (newVersionContainer) { const firstChild = newVersionContainer.querySelector("div:nth-child(1)"); if (firstChild) { newVersionContainer.insertBefore(panel, firstChild); } else { newVersionContainer.appendChild(panel); } return true; } // 尝试旧版界面 const oldVersionContainer = document.querySelector("#page-dynamic .col-2"); if (oldVersionContainer) { oldVersionContainer.appendChild(panel); return true; } return false; }; if (!insertPanel()) { console.log('未找到合适的位置插入面板'); return; } // 获取UI元素 uiManager.resultBox = document.getElementById('resultBox'); // 绑定按钮事件 const buttonHandlers = { 'btnDeleteAll': () => { uiManager.setButtonActive('btnDeleteAll', true); uiManager.showConfirmDialog('all', deleteAllDynamics); }, 'btnDeleteLottery': () => { uiManager.setButtonActive('btnDeleteLottery', true); uiManager.showConfirmDialog('lottery', deleteLotteryDynamics); }, 'btnDeleteRepost': () => { uiManager.setButtonActive('btnDeleteRepost', true); uiManager.showConfirmDialog('repost', deleteRepostDynamics); } }; // 为所有按钮添加事件监听 Object.keys(buttonHandlers).forEach(buttonId => { const button = document.getElementById(buttonId); if (button) { button.addEventListener('click', buttonHandlers[buttonId]); } }); // 添加停止处理的功能(按ESC键停止) document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && isProcessing) { isProcessing = false; uiManager.updateResult('操作已停止', true); uiManager.disableAllButtons(false); } }); console.log('B站动态删除助手已加载'); } // 启动脚本 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();