// ==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();
}
})();