ChatGPT-批量对话删除器(修复版+跳转按钮 v1.7)

捕获 Token,批量删除 ChatGPT 对话,支持分页获取(修复100条限制)、分组全选;新增每条会话“跳转”按钮,优先同页路由跳转,失败则新标签打开;导出功能已停用

当前为 2025-10-23 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         ChatGPT-批量对话删除器(修复版+跳转按钮 v1.7)
// @namespace    https://chatgpt.com/
// @version      1.7
// @description  捕获 Token,批量删除 ChatGPT 对话,支持分页获取(修复100条限制)、分组全选;新增每条会话“跳转”按钮,优先同页路由跳转,失败则新标签打开;导出功能已停用
// @author       Your Name
// @match        https://chatgpt.com/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  let authToken = null;
  let allConversations = [];
  let isFirstClick = true;

  // =========================
  // 新增:通用前置样式(z-index 超大,保证前台)
  // =========================
  const GLOBAL_Z = 2147483647;

  // --- 拦截 fetch 以捕获 Authorization Token ---
  const originalFetch = window.fetch;
  window.fetch = async function (...args) {
    const response = await originalFetch.apply(this, args);
    const url = args[0];

    if (typeof url === 'string' && url.includes('chatgpt.com/backend-api')) {
      try {
        const requestHeaders = args[1]?.headers || {};
        if (requestHeaders.Authorization || requestHeaders.authorization) {
          const token = requestHeaders.Authorization || requestHeaders.authorization;
          if (token && token !== authToken) {
            authToken = token;
            console.log('🎯 抓到 token: 成功 (来自: ' + url + ')');
          }
        }
      } catch (err) {
        console.warn('解析 fetch 请求头时出错:', err);
      }
    }

    return response;
  };

  function getAuthHeaders() {
    return {
      'Content-Type': 'application/json',
      Authorization: authToken || '',
    };
  }

  function showUserTip(message, duration = 5000) {
    const tipBox = document.createElement('div');
    tipBox.style.cssText = `
      position: fixed;
      top: 20px;
      left: 50%;
      transform: translateX(-50%);
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      padding: 15px 25px;
      border-radius: 8px;
      box-shadow: 0 8px 32px rgba(0,0,0,0.2);
      z-index: ${GLOBAL_Z};
      font-size: 14px;
      font-weight: 500;
      animation: slideDown 0.3s ease;
      max-width: 400px;
      text-align: center;
    `;

    const style = document.createElement('style');
    style.textContent = `
      @keyframes slideDown {
        from { transform: translateX(-50%) translateY(-20px); opacity: 0; }
        to { transform: translateX(-50%) translateY(0); opacity: 1; }
      }
    `;
    document.head.appendChild(style);

    const closeBtn = document.createElement('span');
    closeBtn.textContent = '✕';
    closeBtn.style.cssText = `
      position: absolute;
      top: 5px;
      right: 10px;
      cursor: pointer;
      font-size: 18px;
      opacity: 0.8;
    `;
    closeBtn.onclick = () => tipBox.remove();

    tipBox.innerHTML = message;
    tipBox.appendChild(closeBtn);
    document.body.appendChild(tipBox);

    setTimeout(() => {
      if (tipBox.parentNode) {
        tipBox.style.animation = 'slideDown 0.3s ease reverse';
        setTimeout(() => tipBox.remove(), 300);
      }
    }, duration);
  }

  // --- 获取对话列表(分页)---
  async function fetchConversations() {
    console.log('📡 开始获取对话列表...');
    const limit = 50;
    let offset = 0;
    let allConvs = [];
    const maxIterations = 100;

    let iteration = 0;
    while (iteration < maxIterations) {
      console.log(`⏳ 正在获取对话: offset=${offset}, limit=${limit}`);

      if (document.getElementById('loading-progress')) {
        document.getElementById('loading-progress').textContent = `加载中... 已获取 ${allConvs.length} 条对话`;
      }

      try {
        const response = await fetch(
          `/backend-api/conversations?offset=${offset}&limit=${limit}&order=updated`,
          {
            method: 'GET',
            headers: getAuthHeaders(),
            credentials: 'include',
          }
        );

        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

        const data = await response.json();
        const items = data.items || [];

        if (items.length === 0) {
          console.log('✅ 没有更多对话了');
          break;
        }

        allConvs = allConvs.concat(items);
        console.log(`📦 本批次获取 ${items.length} 条,累计 ${allConvs.length} 条`);

        if (items.length < limit) {
          console.log('✅ 获取完成(返回数量少于请求数量)');
          break;
        }

        offset += limit;
        iteration++;
        await new Promise((resolve) => setTimeout(resolve, 300));
      } catch (error) {
        console.error('❌ 获取对话时出错:', error);
        break;
      }
    }

    console.log(`📊 最终获取 ${allConvs.length} 条对话`);
    return allConvs;
  }

    // =========================
    // 路由跳转辅助(优先 SPA;失败回退新标签)+ 滚动位置保存-恢复
    // =========================
    function trySpaNavigate(pathname) {
        try {
            // 1) 常见 Next.js Router 探测
            const candidate =
                  window.next?.router ||
                  window.__NEXT_ROUTER__ ||
                  window.__NUXT__?.$router;

            if (candidate && typeof candidate.push === 'function') {
                candidate.push(pathname);
                return true;
            }

            // 2) History API 回退(部分 SPA 会监听)
            history.pushState({}, '', pathname);
            window.dispatchEvent(new Event('pushstate'));
            window.dispatchEvent(new PopStateEvent('popstate'));
            return true;
        } catch (e) {
            console.debug('SPA 内部跳转失败:', e);
            return false;
        }
    }

    // ★ 新增:多帧重试恢复滚动(防止路由渲染异步导致首次设置失效)
    function restoreScrollWithRetry(targetElGetter, savedScrollTop, maxTries = 20) {
        let tries = 0;
        function tick() {
            const el = targetElGetter();
            if (el) {
                el.scrollTop = savedScrollTop;
                // 若已成功恢复或滚动位置接近目标,直接结束
                if (Math.abs(el.scrollTop - savedScrollTop) < 2) return;
            }
            if (++tries < maxTries) {
                requestAnimationFrame(tick);
            }
        }
        requestAnimationFrame(tick);
    }

    function openConversation(convId, forceNewTab = false) {
        const path = `/c/${convId}`;

        // 在跳转前记录滚动位置(若找不到容器则记 0)
        const panel = document.getElementById('chatgpt-cleaner-container');
        const contentEl = document.getElementById('chatgpt-cleaner-content');
        const savedScrollTop = contentEl ? contentEl.scrollTop : 0;

        // “强制新标签”用于中键/Ctrl+Click
        if (forceNewTab) {
            window.open(path, '_blank');
            return;
        }

        // 优先尝试同页 SPA 跳转
        const ok = trySpaNavigate(path);
        if (!ok) {
            // 回退:新标签页保证可用
            window.open(path, '_blank');
            return;
        }

        // ★ 保持面板最前,但不再 append(避免潜在滚动重置)
        if (panel) {
            panel.style.zIndex = String(GLOBAL_Z);
        }

        // ★ 路由切换后,多帧重试恢复到跳转前的滚动位置
        restoreScrollWithRetry(
            () => document.getElementById('chatgpt-cleaner-content'),
            savedScrollTop
        );
    }

  function renderUI(conversations) {
    console.log('🎉 会话总数:', conversations.length);

    const existingContainer = document.getElementById('chatgpt-cleaner-container');
    if (existingContainer) existingContainer.remove();

    const container = document.createElement('div');
    container.id = 'chatgpt-cleaner-container';
    container.style.cssText = `
      position: fixed;
      top: 50px;
      right: 20px;
      width: 450px;
      max-height: 80vh;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border-radius: 12px;
      box-shadow: 0 15px 35px rgba(0,0,0,0.3);
      z-index: ${GLOBAL_Z};
      display: flex;
      flex-direction: column;
      font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;
    `;

    // 标题栏
    const header = document.createElement('div');
    header.style.cssText = `
      padding: 20px;
      color: white;
      font-size: 18px;
      font-weight: bold;
      border-bottom: 1px solid rgba(255,255,255,0.2);
      display: flex;
      justify-content: space-between;
      align-items: center;
    `;
    header.innerHTML = `
      <span>ChatGPT 履历管理(共 ${conversations.length} 条)</span>
      <button id="close-cleaner" style="
        background: none; border: none; color: white; font-size: 24px; cursor: pointer;
        padding: 0; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
        border-radius: 50%; transition: background-color 0.3s;
      " onmouseover="this.style.backgroundColor='rgba(255,255,255,0.2)'"
         onmouseout="this.style.backgroundColor='transparent'">×</button>
    `;
    container.appendChild(header);

    // 内容区域
      const content = document.createElement('div');
      content.id = 'chatgpt-cleaner-content'; // ★ 新增:用于获取与恢复滚动
      content.style.cssText = `
      flex: 1;
      overflow-y: auto;
      background: white;
      padding: 20px;
      overscroll-behavior: contain; /* ★ 新增:防止外层滚动联动 */
    `;


    // 分组
    const grouped = {};
    conversations.forEach((conv) => {
      const category = getTimeCategory(conv.update_time);
      if (!grouped[category]) grouped[category] = [];
      grouped[category].push(conv);
    });

    const categoryOrder = ['今天', '昨天', '7天内', '30天内', '更早'];

    categoryOrder.forEach((category) => {
      if (!grouped[category] || grouped[category].length === 0) return;

      const groupDiv = document.createElement('div');
      groupDiv.style.cssText = `margin-bottom: 25px;`;

      const groupHeader = document.createElement('div');
      groupHeader.style.cssText = `
        display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;
      `;

      const groupTitle = document.createElement('h3');
      groupTitle.style.cssText = `color:#4a5568;font-size:14px;font-weight:600;margin:0;`;
      groupTitle.textContent = `${category} (${grouped[category].length})`;

      const selectAllBtn = document.createElement('button');
      selectAllBtn.className = `group-select-${category.replace(/\s+/g, '-')}`;
      selectAllBtn.style.cssText = `
        background:#4299e1;color:white;border:none;padding:4px 12px;border-radius:4px;
        font-size:12px;cursor:pointer;transition:all 0.3s;
      `;
      selectAllBtn.textContent = '全选';

      const updateGroupSelectBtn = () => {
        const groupCheckboxes = groupDiv.querySelectorAll('input[type="checkbox"]');
        const checkedCount = Array.from(groupCheckboxes).filter((cb) => cb.checked).length;
        const totalCount = groupCheckboxes.length;

        if (checkedCount === 0) {
          selectAllBtn.textContent = '全选';
          selectAllBtn.style.background = '#4299e1';
        } else if (checkedCount === totalCount) {
          selectAllBtn.textContent = '取消';
          selectAllBtn.style.background = '#e53e3e';
        } else {
          selectAllBtn.textContent = '部分';
          selectAllBtn.style.background = '#ed8936';
        }
      };

      selectAllBtn.onclick = () => {
        const groupCheckboxes = groupDiv.querySelectorAll('input[type="checkbox"]');
        const checkedCount = Array.from(groupCheckboxes).filter((cb) => cb.checked).length;
        const shouldCheck = checkedCount < groupCheckboxes.length;
        groupCheckboxes.forEach((checkbox) => (checkbox.checked = shouldCheck));
        updateGroupSelectBtn();
        updateGlobalSelectAll();
      };

      groupHeader.appendChild(groupTitle);
      groupHeader.appendChild(selectAllBtn);
      groupDiv.appendChild(groupHeader);

      // 列表
      grouped[category].forEach((conv) => {
        const item = document.createElement('div');
        item.className = 'conversation-item';
        item.style.cssText = `
          padding: 12px; margin-bottom: 8px; background:#f7fafc; border-radius: 8px;
          display:flex; align-items:center; transition: all 0.2s; cursor: pointer;
        `;

        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.value = conv.id;
        checkbox.style.cssText = `margin-right:12px;width:18px;height:18px;cursor:pointer;`;
        checkbox.onchange = () => {
          updateGroupSelectBtn();
          updateGlobalSelectAll();
        };

        const label = document.createElement('div');
        label.style.cssText = `flex:1; cursor:pointer; display:flex; flex-direction:column;`;

        const title = document.createElement('span');
        title.style.cssText = `color:#2d3748;font-size:14px;margin-bottom:4px;`;
        title.textContent = conv.title || '无标题对话';

        const time = document.createElement('span');
        time.style.cssText = `color:#718096;font-size:12px;`;
        time.textContent = new Date(conv.update_time).toLocaleString();

        label.appendChild(title);
        label.appendChild(time);

        // =========================
        // 新增:「跳转」按钮(核心)
        // =========================
        const jumpBtn = document.createElement('button');
        jumpBtn.textContent = '跳转';
        jumpBtn.title = '打开该对话(优先同页跳转,失败则新标签)';
        jumpBtn.style.cssText = `
          margin-left: 8px; background:#805ad5; color:white; border:none; padding:6px 10px;
          border-radius:6px; cursor:pointer; font-size:12px; transition:all 0.2s; flex-shrink:0;
        `;
        jumpBtn.onmouseover = () => (jumpBtn.style.background = '#6b46c1');
        jumpBtn.onmouseout = () => (jumpBtn.style.background = '#805ad5');

        // 防止点击“跳转”影响复选框/选中态
        jumpBtn.addEventListener('click', (evt) => {
          evt.preventDefault();
          evt.stopPropagation();
          // Ctrl/Meta 或 中键 => 强制新标签
          const forceNew =
            evt.ctrlKey || evt.metaKey || evt.button === 1 /* middle button */;
          openConversation(conv.id, forceNew);
        });

        // 支持中键直接新标签
        jumpBtn.addEventListener('auxclick', (evt) => {
          if (evt.button === 1) {
            evt.preventDefault();
            evt.stopPropagation();
            openConversation(conv.id, true);
          }
        });

        item.appendChild(checkbox);
        item.appendChild(label);
        item.appendChild(jumpBtn);

        // 行 hover 效果
        item.onmouseover = () => {
          item.style.background = '#e6fffa';
          item.style.transform = 'translateX(-2px)';
        };
        item.onmouseout = () => {
          item.style.background = '#f7fafc';
          item.style.transform = 'translateX(0)';
        };

        // 点击整行 = 切换复选框
        item.onclick = (e) => {
          // 如果点的是按钮,则已 stopPropagation,这里不执行
          checkbox.checked = !checkbox.checked;
          updateGroupSelectBtn();
          updateGlobalSelectAll();
        };

        groupDiv.appendChild(item);
      });

      content.appendChild(groupDiv);
    });

    container.appendChild(content);

    // 底部操作栏
    const footer = document.createElement('div');
    footer.style.cssText = `
      padding: 15px 20px; background:white; border-top:1px solid #e2e8f0;
      display:flex; justify-content:space-between; align-items:center;
    `;

    const selectAllBtn = document.createElement('button');
    selectAllBtn.id = 'select-all-btn';
    selectAllBtn.style.cssText = `
      background:#4299e1;color:white;border:none;padding:8px 16px;border-radius:6px;cursor:pointer;
      font-size:14px;font-weight:500;transition:all 0.3s;
    `;
    selectAllBtn.textContent = '全选所有';

    selectAllBtn.onclick = () => {
      const allCheckboxes = content.querySelectorAll('input[type="checkbox"]');
      const checkedCount = Array.from(allCheckboxes).filter((cb) => cb.checked).length;
      const shouldCheck = checkedCount < allCheckboxes.length;

      allCheckboxes.forEach((checkbox) => (checkbox.checked = shouldCheck));

      ['今天', '昨天', '7天内', '30天内', '更早'].forEach((category) => {
        const groupBtn = content.querySelector(`.group-select-${category.replace(/\s+/g, '-')}`);
        if (groupBtn) {
          groupBtn.textContent = shouldCheck ? '取消' : '全选';
          groupBtn.style.background = shouldCheck ? '#e53e3e' : '#4299e1';
        }
      });

      updateGlobalSelectAll();
    };

    function updateGlobalSelectAll() {
      const allCheckboxes = content.querySelectorAll('input[type="checkbox"]');
      const checkedCount = Array.from(allCheckboxes).filter((cb) => cb.checked).length;
      const totalCount = allCheckboxes.length;

      if (checkedCount === 0) {
        selectAllBtn.textContent = '全选所有';
        selectAllBtn.style.background = '#4299e1';
      } else if (checkedCount === totalCount) {
        selectAllBtn.textContent = '取消全选';
        selectAllBtn.style.background = '#e53e3e';
      } else {
        selectAllBtn.textContent = `已选 ${checkedCount}/${totalCount}`;
        selectAllBtn.style.background = '#ed8936';
      }
    }

    const exportBtn = document.createElement('button');
    exportBtn.style.cssText = `
      background:#cbd5e0;color:#a0aec0;border:none;padding:8px 16px;border-radius:6px;
      cursor:not-allowed;font-size:14px;font-weight:500;transition:all 0.3s;position:relative;
    `;
    exportBtn.textContent = '导出(暂停)';
    exportBtn.disabled = true;
    exportBtn.title = '导出功能暂时停止开放';

    const deleteBtn = document.createElement('button');
    deleteBtn.style.cssText = `
      background:#e53e3e;color:white;border:none;padding:8px 16px;border-radius:6px;cursor:pointer;
      font-size:14px;font-weight:500;transition:all 0.3s;
    `;
    deleteBtn.textContent = '删除选中';
    deleteBtn.onmouseover = () => (deleteBtn.style.background = '#c53030');
    deleteBtn.onmouseout = () => (deleteBtn.style.background = '#e53e3e');

    deleteBtn.onclick = async () => {
      const checkboxes = content.querySelectorAll('input[type="checkbox"]:checked');
      if (checkboxes.length === 0) {
        alert('请先选择要删除的对话');
        return;
      }

      if (!confirm(`确定要删除选中的 ${checkboxes.length} 条对话吗?`)) return;

      let successCount = 0;
      let failCount = 0;

      for (let i = 0; i < checkboxes.length; i++) {
        const checkbox = checkboxes[i];
        const convId = checkbox.value;

        try {
          await deleteSingleConversation(convId);
          successCount++;
          const row = checkbox.closest('.conversation-item');
          if (row) {
            row.style.opacity = '0.5';
            row.style.textDecoration = 'line-through';
          }
        } catch (error) {
          console.error(`删除对话 ${convId} 失败:`, error);
          failCount++;
        }

        deleteBtn.textContent = `删除中... (${i + 1}/${checkboxes.length})`;
        await new Promise((resolve) => setTimeout(resolve, 300));
      }

      alert(`删除完成!\n成功: ${successCount} 条\n失败: ${failCount} 条`);

      const remainingConvs = allConversations.filter(
        (c) => !Array.from(checkboxes).some((cb) => cb.value === c.id)
      );
      allConversations = remainingConvs;
      renderUI(remainingConvs);
    };

    footer.appendChild(selectAllBtn);
    footer.appendChild(exportBtn);
    footer.appendChild(deleteBtn);
    container.appendChild(footer);

    document.body.appendChild(container);

    document.getElementById('close-cleaner').onclick = () => container.remove();

    console.log('✅ UI 渲染完成');
  }

  function getTimeCategory(dateStr) {
    const date = new Date(dateStr);
    const now = new Date();
    const diffMs = now - date;
    const oneDay = 24 * 60 * 60 * 1000;

    if (date.toDateString() === now.toDateString()) return '今天';
    const yesterday = new Date(now);
    yesterday.setDate(now.getDate() - 1);
    if (date.toDateString() === yesterday.toDateString()) return '昨天';
    if (diffMs <= 7 * oneDay) return '7天内';
    if (diffMs <= 30 * oneDay) return '30天内';
    return '更早';
  }

  async function main() {
    console.log('🚀 ChatGPT 清理器启动...');

    if (!authToken) {
      showUserTip('❌ 未能获取 Authorization Token,请刷新页面重试');
      return;
    }

    const loadingDiv = document.createElement('div');
    loadingDiv.id = 'loading-indicator';
    loadingDiv.style.cssText = `
      position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); background:white;
      padding:30px; border-radius:12px; box-shadow:0 10px 25px rgba(0,0,0,0.2); z-index:${GLOBAL_Z}; text-align:center;
    `;
    loadingDiv.innerHTML = `
      <div style="font-size:16px;color:#4a5568;margin-bottom:10px;">正在加载对话列表...</div>
      <div id="loading-progress" style="font-size:14px;color:#718096;">准备中...</div>
      <div style="margin-top:20px;">
        <div style="width:200px;height:4px;background:#e2e8f0;border-radius:2px;overflow:hidden;">
          <div style="width:100%;height:100%;background:linear-gradient(90deg,#667eea 0%,#764ba2 100%);
                      animation: loading 1.5s ease-in-out infinite;"></div>
        </div>
      </div>
      <style>
        @keyframes loading {
          0% { transform: translateX(-100%); }
          100% { transform: translateX(100%); }
        }
      </style>
    `;
    document.body.appendChild(loadingDiv);

    try {
      allConversations = await fetchConversations();
      renderUI(allConversations);
    } catch (error) {
      console.error('❌ 错误:', error);
      showUserTip('❌ 获取对话列表失败,请检查控制台');
    } finally {
      loadingDiv.remove();
    }
  }

  function createToggleButton() {
    const toggleButton = document.createElement('button');
    toggleButton.id = 'chatgpt-cleaner-toggle';
    toggleButton.style.cssText = `
      position: fixed; bottom: 30px; right: 30px; width: 60px; height: 60px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white; border: none; border-radius: 50%; cursor: pointer;
      box-shadow: 0 4px 15px rgba(102,126,234,0.4);
      z-index: ${GLOBAL_Z - 1};
      font-size: 12px; font-weight: bold; transition: all 0.3s; display:flex; align-items:center; justify-content:center; text-align:center; line-height:1.2;
    `;
    toggleButton.textContent = '履历\n删除';
    toggleButton.title = '打开对话履历管理器';

    toggleButton.onmouseover = () => {
      toggleButton.style.transform = 'scale(1.1)';
      toggleButton.style.boxShadow = '0 6px 20px rgba(102,126,234,0.5)';
    };
    toggleButton.onmouseout = () => {
      toggleButton.style.transform = 'scale(1)';
      toggleButton.style.boxShadow = '0 4px 15px rgba(102,126,234,0.4)';
    };

    toggleButton.onclick = async () => {
      console.log('🖱️ 切换按钮点击:' + (isFirstClick ? '首次加载数据...' : '切换显示/隐藏'));

      if (!authToken) {
        showUserTip('💡 请先向 ChatGPT 发送一条消息,以激活对话管理功能');
        return;
      }

      const existingContainer = document.getElementById('chatgpt-cleaner-container');
      if (existingContainer) {
        existingContainer.remove();
      } else {
        if (isFirstClick || allConversations.length === 0) {
          await main();
          isFirstClick = false;
        } else {
          renderUI(allConversations);
        }
      }
    };

    document.body.appendChild(toggleButton);
    console.log('🔌 对话管理切换按钮已创建。');
  }

  async function deleteSingleConversation(conversationId) {
    if (!conversationId) throw new Error('无效的 conversation ID');

    console.log(`⏳ 准备删除对话: ${conversationId}`);
    const headers = getAuthHeaders();
    if (!headers.Authorization) throw new Error('无法获取 Authorization token');

    const url = `/backend-api/conversation/${conversationId}`;
    const body = JSON.stringify({ is_visible: false });

    try {
      const response = await fetch(url, {
        method: 'PATCH',
        headers: headers,
        body: body,
        credentials: 'include',
      });

      if (!response.ok) {
        const errorData = await response.text();
        console.error(`删除 ${conversationId} 请求失败: ${response.status} ${response.statusText}`, errorData);
        throw new Error(`API 请求失败: ${response.status} ${response.statusText}`);
      }

      const result = await response.json();
      console.log(`✅ 对话 ${conversationId} 删除成功:`, result);
      return result;
    } catch (error) {
      console.error(`❌ 执行删除对话 ${conversationId} 的 fetch 时出错:`, error);
      throw error;
    }
  }

  console.log('ChatGPT Cleaner 脚本已注入,等待 API 请求以捕获 Token...');
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', createToggleButton);
  } else {
    createToggleButton();
  }
})();