动态管理

动态管理页面

目前為 2025-06-16 提交的版本,檢視 最新版本

// ==UserScript==
// @name         动态管理
// @namespace    mscststs
// @version      0.31
// @description  动态管理页面
// @author       mscststs
// @match        https://space.bilibili.com/*
// @match        http://space.bilibili.com/*
// @require      https://gf.qytechs.cn/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}`);
      }
    }
  }

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址