您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
动态管理页面
// ==UserScript== // @name 动态管理 // @namespace mscststs // @version 0.31 // @description 动态管理页面 // @author mscststs // @match https://space.bilibili.com/* // @match http://space.bilibili.com/* // @require https://greasyfork.org/scripts/38220-mscststs-tools/code/MSCSTSTS-TOOLS.js?version=713767 // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js // @icon https://static.hdslb.com/images/favicon.ico // @license MIT // @grant none // ==/UserScript== (function () { 'use strict'; // 全局状态 let appState = { showModal: false, loading: false, loadCount: 20, dynamics: [], offset: '', uid: '' }; // 等待页面加载完成 document.addEventListener('DOMContentLoaded', init); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } async function init() { try { const shijiao = await mscststs.wait(".view-switcher__trigger", false, 100); if (!shijiao || !~shijiao.innerText.indexOf("我自己")) { console.log('当前不是自己的个人动态'); return; } await Promise.all([ mscststs.wait(".space-dynamic__right") ]); // 获取用户ID appState.uid = getCurrentUid(); const node = createControlPanel(); document.querySelector("body").append(node); // 绑定事件 bindEvents(); } catch (error) { console.error('初始化失败:', error); } } // 获取当前用户ID function getCurrentUid() { const match = window.location.pathname.match(/\/(\d+)/); return match ? match[1] : ''; } // 创建控制面板DOM结构 function createControlPanel() { const panelHtml = ` <div id="dynamic-manager" class="msc_panel" style=" position: fixed; bottom: 20px; right: 20px; z-index: 9999; "> <button id="open-manager-btn" style=" background: linear-gradient(135deg, #00b4d8, #0077b6); color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(0, 180, 216, 0.3); "> 📊 动态管理 </button> <!-- 管理面板弹窗 --> <div id="manager-modal" style=" position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 10000; display: none; align-items: center; justify-content: center; "> <div style=" background: white; border-radius: 12px; width: 95%; max-width: 1400px; max-height: 90%; overflow: hidden; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); "> <!-- 头部 --> <div style=" display: flex; justify-content: space-between; align-items: center; padding: 20px 30px; border-bottom: 1px solid #e1e1e1; background: linear-gradient(135deg, #f8f9fa, #e9ecef); "> <h2 style="margin: 0; color: #333;">动态管理面板</h2> <button id="close-modal-btn" style=" background: none; border: none; font-size: 24px; cursor: pointer; color: #666; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: all 0.2s; ">×</button> </div> <!-- 操作区域 --> <div style="padding: 20px 30px; border-bottom: 1px solid #e1e1e1;"> <div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;"> <div style="display: flex; align-items: center; gap: 10px;"> <label style="font-weight: 500; color: #333;">加载数量:</label> <input id="load-count-input" type="number" min="1" max="100" value="20" style=" width: 80px; padding: 6px 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; " > </div> <button id="load-dynamics-btn" style=" background: #28a745; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: all 0.2s; " > 加载动态 </button> <button id="select-all-forward-btn" style=" background: #17a2b8; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 10px; " > 勾选所有转发 </button> <button id="select-all-lottery-btn" style=" background: #fd7e14; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 10px; " > 勾选所有抽奖 </button> <button id="batch-delete-btn" style=" background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 10px; " > 批量删除 </button> <button id="batch-delete-unfollow-btn" style=" background: #e74c3c; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 10px; " > 删除并取关 </button> <button id="clear-data-btn" style=" background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; " > 清空数据 </button> <div id="dynamics-count" style="margin-left: auto; color: #666;"> 已加载: 0 条动态 </div> </div> </div> <!-- 表格区域 --> <div style="padding: 0; max-height: 600px; overflow-y: auto;"> <table id="dynamics-table" style="width: 100%; border-collapse: collapse; table-layout: fixed;"> <thead style="background: #f8f9fa; position: sticky; top: 0;"> <tr> <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd; font-weight: 600; width: 80px;"> <input type="checkbox" id="select-all-checkbox" style="margin-right: 8px;" > 选择 </th> <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd; font-weight: 600; width: 80px;">类型</th> <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd; font-weight: 600; width: 500px;">内容</th> <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd; font-weight: 600; width: 120px;">发布时间</th> <th style="padding: 12px; text-align: left; border-bottom: 1px solid #ddd; font-weight: 600; width: 120px;">操作</th> </tr> </thead> <tbody id="dynamics-tbody"> <tr> <td colspan="5" style="padding: 40px; text-align: center; color: #999;"> 暂无数据,请点击"加载动态"获取数据 </td> </tr> </tbody> </table> </div> </div> </div> </div> `; const div = document.createElement('div'); div.innerHTML = panelHtml; return div.firstElementChild; } // 绑定事件 function bindEvents() { // 打开弹窗 document.getElementById('open-manager-btn').addEventListener('click', showModal); // 关闭弹窗 document.getElementById('close-modal-btn').addEventListener('click', hideModal); // 点击遮罩关闭弹窗 document.getElementById('manager-modal').addEventListener('click', function(e) { if (e.target === this) { hideModal(); } }); // 加载动态 document.getElementById('load-dynamics-btn').addEventListener('click', loadDynamics); // 勾选所有转发 document.getElementById('select-all-forward-btn').addEventListener('click', selectAllForward); // 勾选所有抽奖 document.getElementById('select-all-lottery-btn').addEventListener('click', selectAllLottery); // 批量删除 document.getElementById('batch-delete-btn').addEventListener('click', batchDeleteDynamics); // 批量删除并取关 document.getElementById('batch-delete-unfollow-btn').addEventListener('click', batchDeleteAndUnfollowDynamics); // 清空数据 document.getElementById('clear-data-btn').addEventListener('click', clearData); // 全选 document.getElementById('select-all-checkbox').addEventListener('change', toggleAllSelection); // 加载数量输入 document.getElementById('load-count-input').addEventListener('input', function() { appState.loadCount = parseInt(this.value) || 20; }); // 按钮悬停效果 const openBtn = document.getElementById('open-manager-btn'); openBtn.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-1px)'; this.style.boxShadow = '0 4px 12px rgba(0, 180, 216, 0.4)'; }); openBtn.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 2px 8px rgba(0, 180, 216, 0.3)'; }); const closeBtn = document.getElementById('close-modal-btn'); closeBtn.addEventListener('mouseenter', function() { this.style.background = '#f0f0f0'; }); closeBtn.addEventListener('mouseleave', function() { this.style.background = 'none'; }); // 绑定查看按钮事件 document.querySelectorAll('.view-btn').forEach(btn => { btn.addEventListener('click', function() { const index = parseInt(this.dataset.index); viewDynamic(appState.dynamics[index]); }); }); // 绑定删除按钮事件 document.querySelectorAll('.delete-btn').forEach(btn => { btn.addEventListener('click', function() { const index = parseInt(this.dataset.index); deleteDynamic(appState.dynamics[index]); }); }); // 绑定删除并取关按钮事件 document.querySelectorAll('.delete-unfollow-btn').forEach(btn => { btn.addEventListener('click', function() { const index = parseInt(this.dataset.index); deleteAndUnfollowDynamic(appState.dynamics[index]); }); }); } // 显示弹窗 function showModal() { appState.showModal = true; document.getElementById('manager-modal').style.display = 'flex'; } // 隐藏弹窗 function hideModal() { appState.showModal = false; document.getElementById('manager-modal').style.display = 'none'; } // 加载动态 async function loadDynamics() { if (!appState.uid) { alert('无法获取用户ID'); return; } const loadBtn = document.getElementById('load-dynamics-btn'); loadBtn.disabled = true; loadBtn.style.opacity = '0.6'; loadBtn.style.cursor = 'not-allowed'; const targetCount = appState.loadCount; let currentOffset = appState.offset; let totalLoadedInThisSession = 0; try { // 循环加载直到达到目标数量或没有更多数据 while (totalLoadedInThisSession < targetCount) { // 更新按钮文本显示进度 loadBtn.textContent = `加载中... (${totalLoadedInThisSession}/${targetCount})`; const response = await spaceHistory(currentOffset); if (response.code !== 0) { alert('加载失败: ' + response.message); break; } const items = response.data.items || []; if (items.length === 0 || !response.data.has_more) { console.log('没有更多动态数据'); break; } // 计算本次需要添加的数量 const remainingCount = targetCount - totalLoadedInThisSession; const itemsToAdd = items.slice(0, remainingCount).map(item => ({ ...item, selected: false })); appState.dynamics = [...appState.dynamics, ...itemsToAdd]; currentOffset = response.data.offset || ''; appState.offset = currentOffset; totalLoadedInThisSession += itemsToAdd.length; console.log(`本次加载 ${itemsToAdd.length} 条动态,累计加载 ${totalLoadedInThisSession} 条`); // 如果没有更多数据(offset为空)或者API返回的数据少于预期,说明已经到底了 if (!currentOffset || !response.data.has_more) { console.log('已加载所有可用的动态数据'); break; } // 如果已经达到目标数量,退出循环 if (totalLoadedInThisSession >= targetCount) { break; } // 添加短暂延迟避免请求过于频繁 await new Promise(resolve => setTimeout(resolve, 500)); } console.log(`加载完成,目标: ${targetCount} 条,实际加载: ${totalLoadedInThisSession} 条,总计: ${appState.dynamics.length} 条`); // 更新UI updateDynamicsTable(); updateDynamicsCount(); // 显示加载结果 if (totalLoadedInThisSession > 0) { const message = totalLoadedInThisSession < targetCount ? `成功加载 ${totalLoadedInThisSession} 条动态(已加载完所有可用数据)` : `成功加载 ${totalLoadedInThisSession} 条动态`; console.log(message); } else { alert('没有新的动态数据'); } } catch (error) { console.error('加载动态失败:', error); alert('加载失败,请检查网络连接'); } finally { loadBtn.disabled = false; loadBtn.textContent = '加载动态'; loadBtn.style.opacity = '1'; loadBtn.style.cursor = 'pointer'; } } // API调用 async function spaceHistory(offset = "") { const url = `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?offset=${offset}&host_mid=${appState.uid}&timezone_offset=-480&platform=web`; try { const response = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Referer': 'https://space.bilibili.com/' }, withCredentials: true }); return response.data; } catch (error) { if (error.response && error.response.status === 429) { // 429错误重试机制 await new Promise(resolve => setTimeout(resolve, 2000)); return spaceHistory(offset); } throw error; } } // 清空数据 function clearData() { if (confirm('确定要清空所有数据吗?')) { appState.dynamics = []; appState.offset = ''; updateDynamicsTable(); updateDynamicsCount(); } } // 全选/取消全选 function toggleAllSelection() { const checkbox = document.getElementById('select-all-checkbox'); const isChecked = checkbox.checked; appState.dynamics.forEach(item => { item.selected = isChecked; }); // 更新表格中的复选框 const itemCheckboxes = document.querySelectorAll('.item-checkbox'); itemCheckboxes.forEach(cb => { cb.checked = isChecked; }); } // 更新动态表格 function updateDynamicsTable() { const tbody = document.getElementById('dynamics-tbody'); if (appState.dynamics.length === 0) { tbody.innerHTML = ` <tr> <td colspan="5" style="padding: 40px; text-align: center; color: #999;"> 暂无数据,请点击"加载动态"获取数据 </td> </tr> `; return; } tbody.innerHTML = appState.dynamics.map((item, index) => ` <tr style="border-bottom: 1px solid #eee;"> <td style="padding: 12px;"> <input type="checkbox" class="item-checkbox" data-index="${index}" ${item.selected ? 'checked' : ''} style="margin-right: 8px;" > ${index + 1} </td> <td style="padding: 12px;"> <span style=" display: inline-block; padding: 2px 8px; background: #e3f2fd; color: #1976d2; border-radius: 12px; font-size: 12px; white-space: nowrap; "> ${getTypeLabel(item)} </span> </td> <td style="padding: 12px; word-wrap: break-word; overflow: hidden;"> <div style="display: flex; align-items: center; gap: 10px;"> ${item.modules.module_dynamic.major && item.modules.module_dynamic.major.archive ? `<img src="${item.modules.module_dynamic.major.archive.cover}" style="width: 60px; height: 40px; object-fit: cover; border-radius: 4px; flex-shrink: 0;">` : '' } <div style="overflow: hidden; flex: 1; min-width: 0;"> <div style="font-weight: 500; margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${getContentTitle(item)}"> ${getContentTitle(item)} </div> <div style="font-size: 12px; color: #666; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;" title="${getContentDesc(item)}"> ${getContentDesc(item)} </div> </div> </div> </td> <td style="padding: 12px; color: #666; font-size: 14px; white-space: nowrap;"> ${item.modules.module_author.pub_time} </td> <td style="padding: 12px;"> <div style="display: flex; gap: 8px;"> <button class="view-btn" data-index="${index}" style=" background: #007bff; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 12px; white-space: nowrap; " > 查看 </button> <button class="delete-btn" data-index="${index}" style=" background: #dc3545; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 12px; white-space: nowrap; " > 删除 </button> <button class="delete-unfollow-btn" data-index="${index}" style=" background: #e74c3c; color: white; border: none; padding: 4px 6px; border-radius: 3px; cursor: pointer; font-size: 12px; white-space: nowrap; " > 删除并取关 </button> </div> </td> </tr> `).join(''); // 绑定表格内的事件 bindTableEvents(); } // 绑定表格内的事件 function bindTableEvents() { // 绑定复选框事件 document.querySelectorAll('.item-checkbox').forEach(checkbox => { checkbox.addEventListener('change', function() { const index = parseInt(this.dataset.index); appState.dynamics[index].selected = this.checked; // 更新全选复选框状态 const allSelected = appState.dynamics.every(item => item.selected); const someSelected = appState.dynamics.some(item => item.selected); const selectAllCheckbox = document.getElementById('select-all-checkbox'); selectAllCheckbox.checked = allSelected; selectAllCheckbox.indeterminate = someSelected && !allSelected; }); }); // 绑定查看按钮事件 document.querySelectorAll('.view-btn').forEach(btn => { btn.addEventListener('click', function() { const index = parseInt(this.dataset.index); viewDynamic(appState.dynamics[index]); }); }); // 绑定删除按钮事件 document.querySelectorAll('.delete-btn').forEach(btn => { btn.addEventListener('click', function() { const index = parseInt(this.dataset.index); deleteDynamic(appState.dynamics[index]); }); }); // 绑定删除并取关按钮事件 document.querySelectorAll('.delete-unfollow-btn').forEach(btn => { btn.addEventListener('click', function() { const index = parseInt(this.dataset.index); deleteAndUnfollowDynamic(appState.dynamics[index]); }); }); } // 更新动态数量显示 function updateDynamicsCount() { document.getElementById('dynamics-count').textContent = `已加载: ${appState.dynamics.length} 条动态`; } // 勾选所有转发动态 function selectAllForward() { let forwardCount = 0; appState.dynamics.forEach(item => { if (item.type === 'DYNAMIC_TYPE_FORWARD') { item.selected = true; forwardCount++; } }); if (forwardCount === 0) { alert('暂无转发动态'); return; } // 更新表格中的复选框 const itemCheckboxes = document.querySelectorAll('.item-checkbox'); itemCheckboxes.forEach((cb, index) => { if (appState.dynamics[index] && appState.dynamics[index].type === 'DYNAMIC_TYPE_FORWARD') { cb.checked = true; } }); // 更新全选复选框状态 const allSelected = appState.dynamics.every(item => item.selected); const someSelected = appState.dynamics.some(item => item.selected); const selectAllCheckbox = document.getElementById('select-all-checkbox'); selectAllCheckbox.checked = allSelected; selectAllCheckbox.indeterminate = someSelected && !allSelected; alert(`已勾选 ${forwardCount} 条转发动态`); } // 勾选所有抽奖动态 function selectAllLottery() { let lotteryCount = 0; appState.dynamics.forEach(item => { // 检查是否是转发抽奖动态 if (item.type === 'DYNAMIC_TYPE_FORWARD' && item.orig && item.orig.modules && item.orig.modules.module_dynamic && item.orig.modules.module_dynamic.additional && item.orig.modules.module_dynamic.additional.type === 'ADDITIONAL_TYPE_UPOWER_LOTTERY') { item.selected = true; lotteryCount++; } }); if (lotteryCount === 0) { alert('暂无抽奖动态'); return; } // 更新表格中的复选框 const itemCheckboxes = document.querySelectorAll('.item-checkbox'); itemCheckboxes.forEach((cb, index) => { const item = appState.dynamics[index]; if (item && item.type === 'DYNAMIC_TYPE_FORWARD' && item.orig && item.orig.modules && item.orig.modules.module_dynamic && item.orig.modules.module_dynamic.additional && item.orig.modules.module_dynamic.additional.type === 'ADDITIONAL_TYPE_UPOWER_LOTTERY') { cb.checked = true; } }); // 更新全选复选框状态 const allSelected = appState.dynamics.every(item => item.selected); const someSelected = appState.dynamics.some(item => item.selected); const selectAllCheckbox = document.getElementById('select-all-checkbox'); selectAllCheckbox.checked = allSelected; selectAllCheckbox.indeterminate = someSelected && !allSelected; alert(`已勾选 ${lotteryCount} 条抽奖动态`); } // 获取类型标签 function getTypeLabel(item) { const type = item.type; // 处理转发动态 if (type === 'DYNAMIC_TYPE_FORWARD') { if (item.orig && item.orig.modules && item.orig.modules.module_dynamic) { const origDynamic = item.orig.modules.module_dynamic; // 检查是否是转发抽奖 if (origDynamic.additional && origDynamic.additional.type === 'ADDITIONAL_TYPE_UPOWER_LOTTERY') { return '转发抽奖'; } } return '转发'; } const typeMap = { 'DYNAMIC_TYPE_AV': '视频', 'DYNAMIC_TYPE_WORD': '文字', 'DYNAMIC_TYPE_DRAW': '图片', 'DYNAMIC_TYPE_ARTICLE': '文章', 'DYNAMIC_TYPE_MUSIC': '音频', 'DYNAMIC_TYPE_COMMON_SQUARE': '分享', 'DYNAMIC_TYPE_LIVE': '直播', 'DYNAMIC_TYPE_LIVE_RCMD': '直播推荐' }; return typeMap[type] || '其他'; } // 获取内容标题 function getContentTitle(item) { // 处理转发动态 if (item.type === 'DYNAMIC_TYPE_FORWARD') { if (item.modules.module_dynamic.desc && item.modules.module_dynamic.desc.text) { return `${item.modules.module_dynamic.desc.text}`; } // 如果转发没有文字,显示转发的原动态标题 if (item.orig) { const origTitle = getOriginalContentTitle(item.orig); return `转发:${origTitle}`; } return '转发动态'; } // 处理视频动态 if (item.modules.module_dynamic.major && item.modules.module_dynamic.major.archive) { return item.modules.module_dynamic.major.archive.title; } // 处理图片动态 if (item.modules.module_dynamic.major && item.modules.module_dynamic.major.opus) { if (item.modules.module_dynamic.major.opus.summary) { return item.modules.module_dynamic.major.opus.summary.text || '图片动态'; } } // 处理文字动态 if (item.modules.module_dynamic.desc && item.modules.module_dynamic.desc.text) { return item.modules.module_dynamic.desc.text; } return '无标题'; } // 获取原动态的标题(用于转发) function getOriginalContentTitle(origItem) { if (origItem.modules.module_dynamic.major && origItem.modules.module_dynamic.major.archive) { return origItem.modules.module_dynamic.major.archive.title; } if (origItem.modules.module_dynamic.major && origItem.modules.module_dynamic.major.opus) { if (origItem.modules.module_dynamic.major.opus.summary) { return origItem.modules.module_dynamic.major.opus.summary.text || '图片动态'; } } if (origItem.modules.module_dynamic.desc && origItem.modules.module_dynamic.desc.text) { return origItem.modules.module_dynamic.desc.text; } return '动态内容'; } // 获取内容描述 function getContentDesc(item) { // 处理转发动态 if (item.type === 'DYNAMIC_TYPE_FORWARD' && item.orig) { return getOriginalContentDesc(item.orig); } // 处理视频动态 if (item.modules.module_dynamic.major && item.modules.module_dynamic.major.archive) { return item.modules.module_dynamic.major.archive.desc || ''; } // 处理图片动态描述 if (item.modules.module_dynamic.major && item.modules.module_dynamic.major.opus) { return '图片动态'; } return ''; } // 获取原动态的描述(用于转发) function getOriginalContentDesc(origItem) { if (origItem.modules.module_dynamic.major && origItem.modules.module_dynamic.major.archive) { return origItem.modules.module_dynamic.major.archive.desc || ''; } if (origItem.modules.module_dynamic.major && origItem.modules.module_dynamic.major.opus) { return '图片动态'; } return ''; } // 查看动态 function viewDynamic(item) { // 处理视频动态 if (item.modules.module_dynamic.major && item.modules.module_dynamic.major.archive) { window.open(item.modules.module_dynamic.major.archive.jump_url, '_blank'); return; } // 处理转发动态 if (item.type === 'DYNAMIC_TYPE_FORWARD' && item.orig) { // 如果原动态是视频,跳转到视频 if (item.orig.modules.module_dynamic.major && item.orig.modules.module_dynamic.major.archive) { window.open(item.orig.modules.module_dynamic.major.archive.jump_url, '_blank'); return; } // 如果原动态是图片,跳转到图文 if (item.orig.modules.module_dynamic.major && item.orig.modules.module_dynamic.major.opus) { window.open(item.orig.modules.module_dynamic.major.opus.jump_url, '_blank'); return; } // 跳转到原动态 window.open(`https://t.bilibili.com/${item.orig.id_str}`, '_blank'); return; } // 处理图片动态 if (item.modules.module_dynamic.major && item.modules.module_dynamic.major.opus) { window.open(item.modules.module_dynamic.major.opus.jump_url, '_blank'); return; } // 默认跳转到动态页面 window.open(`https://t.bilibili.com/${item.id_str}`, '_blank'); } // 获取CSRF token function getCSRFToken() { const cookies = document.cookie.split(';'); for (let cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'bili_jct') { return value; } } return ''; } // 删除动态 async function deleteDynamic(item) { const title = getContentTitle(item); if (!confirm(`确定要删除这条动态吗?\n${title}`)) { return; } try { // 获取删除参数 const deleteParams = item.modules.module_more.three_point_items.find( item => item.type === 'THREE_POINT_DELETE' ); if (!deleteParams || !deleteParams.params) { alert('无法获取删除参数'); return; } const { dyn_id_str, dyn_type, rid_str } = deleteParams.params; const csrf = getCSRFToken(); if (!csrf) { alert('未登录或获取CSRF token失败,请先登录B站'); return; } // 调用删除API const response = await fetch( `https://api.bilibili.com/x/dynamic/feed/operate/remove?platform=web&csrf=${csrf}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': '*/*', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }, credentials: 'include', body: JSON.stringify({ dyn_id_str, dyn_type, rid_str }) } ); const result = await response.json(); if (result.code === 0) { alert('删除成功!'); // 从本地数据中移除该动态 const index = appState.dynamics.findIndex(d => d.id_str === item.id_str); if (index > -1) { appState.dynamics.splice(index, 1); updateDynamicsTable(); updateDynamicsCount(); } } else { alert(`删除失败: ${result.message || '未知错误'}`); } } catch (error) { console.error('删除失败:', error); if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) { alert('删除失败:网络连接错误或跨域问题'); } else { alert(`删除失败: ${error.message}`); } } } // 批量删除动态 async function batchDeleteDynamics() { const selectedItems = appState.dynamics.filter(item => item.selected); if (selectedItems.length === 0) { alert('请先选择要删除的动态'); return; } if (!confirm(`确定要删除选中的 ${selectedItems.length} 条动态吗?此操作无法撤销!`)) { return; } const csrf = getCSRFToken(); if (!csrf) { alert('未登录或获取CSRF token失败,请先登录B站'); return; } const batchBtn = document.getElementById('batch-delete-btn'); const originalText = batchBtn.textContent; let successCount = 0; let failCount = 0; try { batchBtn.disabled = true; batchBtn.style.opacity = '0.6'; for (let i = 0; i < selectedItems.length; i++) { const item = selectedItems[i]; batchBtn.textContent = `删除中... (${i + 1}/${selectedItems.length})`; try { // 获取删除参数 const deleteParams = item.modules.module_more.three_point_items.find( item => item.type === 'THREE_POINT_DELETE' ); if (!deleteParams || !deleteParams.params) { console.warn(`动态 ${item.id_str} 无法获取删除参数`); failCount++; continue; } const { dyn_id_str, dyn_type, rid_str } = deleteParams.params; // 调用删除API const response = await fetch( `https://api.bilibili.com/x/dynamic/feed/operate/remove?platform=web&csrf=${csrf}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': '*/*', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }, credentials: 'include', body: JSON.stringify({ dyn_id_str, dyn_type, rid_str }) } ); const result = await response.json(); if (result.code === 0) { successCount++; // 从本地数据中移除该动态 const index = appState.dynamics.findIndex(d => d.id_str === item.id_str); if (index > -1) { appState.dynamics.splice(index, 1); } } else { console.error(`删除动态 ${item.id_str} 失败:`, result.message); failCount++; } } catch (error) { console.error(`删除动态 ${item.id_str} 出错:`, error); failCount++; } // 添加延迟避免请求过于频繁 if (i < selectedItems.length - 1) { await new Promise(resolve => setTimeout(resolve, 1000)); } } // 更新UI updateDynamicsTable(); updateDynamicsCount(); // 显示结果 let message = `批量删除完成!\n成功: ${successCount} 条`; if (failCount > 0) { message += `\n失败: ${failCount} 条`; } alert(message); } catch (error) { console.error('批量删除失败:', error); alert(`批量删除失败: ${error.message}`); } finally { batchBtn.disabled = false; batchBtn.style.opacity = '1'; batchBtn.textContent = originalText; } } // 批量删除并取关 async function batchDeleteAndUnfollowDynamics() { const selectedItems = appState.dynamics.filter(item => item.selected); if (selectedItems.length === 0) { alert('请先选择要删除的动态'); return; } if (!confirm(`确定要删除选中的 ${selectedItems.length} 条动态并取关对应的用户吗?此操作无法撤销!`)) { return; } const csrf = getCSRFToken(); if (!csrf) { alert('未登录或获取CSRF token失败,请先登录B站'); return; } const batchBtn = document.getElementById('batch-delete-unfollow-btn'); const originalText = batchBtn.textContent; let deleteSuccessCount = 0; let unfollowSuccessCount = 0; let failCount = 0; const processedUsers = new Set(); // 记录已处理的用户,避免重复取关 try { batchBtn.disabled = true; batchBtn.style.opacity = '0.6'; for (let i = 0; i < selectedItems.length; i++) { const item = selectedItems[i]; batchBtn.textContent = `处理中... (${i + 1}/${selectedItems.length})`; try { // 获取删除参数 const deleteParams = item.modules.module_more.three_point_items.find( item => item.type === 'THREE_POINT_DELETE' ); if (!deleteParams || !deleteParams.params) { console.warn(`动态 ${item.id_str} 无法获取删除参数`); failCount++; continue; } const { dyn_id_str, dyn_type, rid_str } = deleteParams.params; // 调用删除API const deleteResponse = await fetch( `https://api.bilibili.com/x/dynamic/feed/operate/remove?platform=web&csrf=${csrf}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': '*/*', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }, credentials: 'include', body: JSON.stringify({ dyn_id_str, dyn_type, rid_str }) } ); const deleteResult = await deleteResponse.json(); if (deleteResult.code === 0) { deleteSuccessCount++; // 删除成功,尝试取关目标作者 let targetAuthorUid = item.modules.module_author.mid; let targetAuthorName = item.modules.module_author.name; // 如果是转发动态,获取原动态的作者信息 if (item.type === 'DYNAMIC_TYPE_FORWARD' && item.orig && item.orig.modules && item.orig.modules.module_author) { targetAuthorUid = item.orig.modules.module_author.mid; targetAuthorName = item.orig.modules.module_author.name; } // 检查是否是自己的动态或者已经处理过这个用户 if (targetAuthorUid.toString() !== appState.uid && !processedUsers.has(targetAuthorUid)) { processedUsers.add(targetAuthorUid); const unfollowResult = await unfollowUser(targetAuthorUid); if (unfollowResult.success) { unfollowSuccessCount++; console.log(`成功取关用户 ${targetAuthorName} (${targetAuthorUid})`); } else { console.warn(`取关用户 ${targetAuthorName} (${targetAuthorUid}) 失败: ${unfollowResult.message}`); } // 添加取关操作间的延迟 await new Promise(resolve => setTimeout(resolve, 500)); } // 从本地数据中移除该动态 const index = appState.dynamics.findIndex(d => d.id_str === item.id_str); if (index > -1) { appState.dynamics.splice(index, 1); } } else { console.error(`删除动态 ${item.id_str} 失败:`, deleteResult.message); failCount++; } } catch (error) { console.error(`处理动态 ${item.id_str} 出错:`, error); failCount++; } // 添加延迟避免请求过于频繁 if (i < selectedItems.length - 1) { await new Promise(resolve => setTimeout(resolve, 1000)); } } // 更新UI updateDynamicsTable(); updateDynamicsCount(); // 显示结果 let message = `批量操作完成!\n删除动态成功: ${deleteSuccessCount} 条\n取关用户成功: ${unfollowSuccessCount} 个`; if (failCount > 0) { message += `\n失败: ${failCount} 条`; } alert(message); } catch (error) { console.error('批量删除并取关失败:', error); alert(`批量操作失败: ${error.message}`); } finally { batchBtn.disabled = false; batchBtn.style.opacity = '1'; batchBtn.textContent = originalText; } } // 取关用户 async function unfollowUser(uid) { const csrf = getCSRFToken(); if (!csrf) { throw new Error('未登录或获取CSRF token失败'); } try { const formData = new URLSearchParams(); formData.append('act', '2'); // 2表示取消关注 formData.append('fid', uid.toString()); formData.append('spmid', '333.1365'); formData.append('re_src', '0'); formData.append('csrf', csrf); const response = await fetch( 'https://api.bilibili.com/x/relation/modify?statistics=%7B%22appId%22:100,%22platform%22:5%7D', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': '*/*', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }, credentials: 'include', body: formData } ); const result = await response.json(); if (result.code === 0) { return { success: true }; } else { return { success: false, message: result.message || '取关失败' }; } } catch (error) { return { success: false, message: error.message }; } } // 删除并取关动态 async function deleteAndUnfollowDynamic(item) { const title = getContentTitle(item); let targetAuthorName = item.modules.module_author.name; let targetAuthorUid = item.modules.module_author.mid; // 如果是转发动态,获取原动态的作者信息 if (item.type === 'DYNAMIC_TYPE_FORWARD' && item.orig && item.orig.modules && item.orig.modules.module_author) { targetAuthorName = item.orig.modules.module_author.name; targetAuthorUid = item.orig.modules.module_author.mid; } if (!confirm(`确定要删除这条动态并取关 "${targetAuthorName}" 吗?\n动态:${title}`)) { return; } try { // 先删除动态 const deleteParams = item.modules.module_more.three_point_items.find( item => item.type === 'THREE_POINT_DELETE' ); if (!deleteParams || !deleteParams.params) { alert('无法获取删除参数'); return; } const { dyn_id_str, dyn_type, rid_str } = deleteParams.params; const csrf = getCSRFToken(); if (!csrf) { alert('未登录或获取CSRF token失败,请先登录B站'); return; } // 调用删除API const deleteResponse = await fetch( `https://api.bilibili.com/x/dynamic/feed/operate/remove?platform=web&csrf=${csrf}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': '*/*', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }, credentials: 'include', body: JSON.stringify({ dyn_id_str, dyn_type, rid_str }) } ); const deleteResult = await deleteResponse.json(); if (deleteResult.code === 0) { // 删除成功,尝试取关目标作者 // 检查是否是自己的动态,如果是则跳过取关 if (targetAuthorUid.toString() === appState.uid) { alert('删除成功!(跳过取关自己)'); } else { const unfollowResult = await unfollowUser(targetAuthorUid); if (unfollowResult.success) { alert(`删除动态并取关 "${targetAuthorName}" 成功!`); } else { alert(`删除动态成功,但取关失败: ${unfollowResult.message}`); } } // 从本地数据中移除该动态 const index = appState.dynamics.findIndex(d => d.id_str === item.id_str); if (index > -1) { appState.dynamics.splice(index, 1); updateDynamicsTable(); updateDynamicsCount(); } } else { alert(`删除失败: ${deleteResult.message || '未知错误'}`); } } catch (error) { console.error('删除并取关失败:', error); if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) { alert('操作失败:网络连接错误或跨域问题'); } else { alert(`操作失败: ${error.message}`); } } } })();