ChatGPT Bulk Chat Remover (2025 DOM, FULLY WORKING)

Массовое удаление чатов: автопрокрутка, выбор, чекбоксы, удаление, индикатор, логи. Полностью рабочий DOM май 2025!

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         ChatGPT Bulk Chat Remover (2025 DOM, FULLY WORKING)
// @namespace    https://chat.openai.com
// @version      2.0
// @description  Массовое удаление чатов: автопрокрутка, выбор, чекбоксы, удаление, индикатор, логи. Полностью рабочий DOM май 2025!
// @author       GPT, user fixes
// @match        https://chatgpt.com/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const EXCLUDED_CHATS = [
    'Правила игры в тринку',
    'Ответственность при повреждении машины',
  ].map((s) => s.toLowerCase());

  // сюда запишем токен, когда он впервые встретится в headers
  window.__GPT_ACCESS_TOKEN = null;

  // запомним оригинальный fetch
  const ___origFetch = window.fetch;

  // перехватываем
  window.fetch = async function (resource, config) {
    // если в конфиге есть заголовок Authorization — достанем оттуда токен
    if (config && config.headers && config.headers.Authorization) {
      const m = config.headers.Authorization.match(/Bearer\s+(.+)/i);
      if (m) {
        window.__GPT_ACCESS_TOKEN = m[1];
        console.log('🗝️ Захвачен GPT access token:', window.__GPT_ACCESS_TOKEN);
      }
    }
    // далее отдадим выполнение штатному fetch
    return ___origFetch.call(this, resource, config);
  };

  let UI_ADDED = false;

  /** Utility */
  function wait(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  /** Индикатор статуса */
  function createOrUpdateStatus(text) {
    let status = document.querySelector('#gpt-bulk-status');
    if (!status) {
      status = document.createElement('div');
      status.id = 'gpt-bulk-status';
      status.style.marginTop = '6px';
      status.style.fontStyle = 'italic';
      status.style.color = '#169800';
      status.style.fontWeight = 'bold';
      document.body.prepend(status);
    }
    status.textContent = text;
    return status;
  }

  /** Основной блок навигации */
  function waitForSidebar() {
    const interval = setInterval(() => {
      console.log('🔍 Поиск <nav.group/scrollport>...');
      const navBlock = document.querySelector(
        'nav.group\\/scrollport, nav.group\\/scrollport.relative'
      );
      if (navBlock) {
        console.log('✅ Найден nav-блок:', navBlock);
      } else {
        console.warn('❌ nav-блок не найден');
      }
      if (navBlock && !UI_ADDED) {
        UI_ADDED = true;
        addUI(navBlock);
        clearInterval(interval);
      }
    }, 1000);
  }

  /** UI-меню */
  function addUI(container) {
    const wrapper = document.createElement('div');
    wrapper.style.padding = '10px';
    wrapper.style.margin = '10px 0 10px 0';
    wrapper.style.background = '#f2f2f2';
    wrapper.style.border = '1px solid #ccc';
    wrapper.style.borderRadius = '5px';
    wrapper.style.fontSize = '14px';
    wrapper.style.display = 'flex';
    wrapper.style.gap = '6px';

    const scrollBtn = document.createElement('button');
    scrollBtn.textContent = '📜 Прокрутити всі чати';
    scrollBtn.onclick = scrollToBottom;

    const selectBtn = document.createElement('button');
    selectBtn.textContent = '✅ Виділити всі';
    selectBtn.onclick = () => {
      document.querySelectorAll('.gpt-chat-checkbox').forEach((cb) => {
        const link = cb.closest('a[draggable="true"]');
        // Находим текст заголовка внутри <a>
        const titleEl = link.querySelector('.truncate');
        const title = titleEl?.textContent.trim().toLowerCase() || '';

        cb.checked = !EXCLUDED_CHATS.includes(title);
      });
    };

    const deleteBtn = document.createElement('button');
    deleteBtn.textContent = '🗑 Видалити обрані';
    deleteBtn.onclick = deleteSelectedChats;

    wrapper.appendChild(scrollBtn);
    wrapper.appendChild(selectBtn);
    wrapper.appendChild(deleteBtn);

    container.prepend(wrapper);

    // Статус под меню
    const status = document.createElement('div');
    status.id = 'gpt-bulk-status';
    status.style.marginTop = '6px';
    status.style.fontStyle = 'italic';
    status.style.color = '#169800';
    status.style.fontWeight = 'bold';
    container.prepend(status);
  }

  /** Прокрутка */
  async function scrollToBottom() {
    const scrollable = document.querySelector(
      'nav.group\\/scrollport, nav.group\\/scrollport.relative'
    );
    if (!scrollable) return;

    const status = createOrUpdateStatus('⏳ Завантаження...');
    let prevHeight = 0;
    let sameCount = 0;

    for (let i = 0; i < 50 && sameCount < 5; i++) {
      scrollable.scrollTo({ top: scrollable.scrollHeight, behavior: 'smooth' });
      await wait(500);

      const newHeight = scrollable.scrollHeight;
      if (newHeight === prevHeight) sameCount++;
      else sameCount = 0;

      prevHeight = newHeight;
    }

    addCheckboxes();
    status.textContent = '✅ Усі чати завантажено!';
  }

  /** Чекбоксы */
  function addCheckboxes() {
    const chatLinks = document.querySelectorAll(
      'aside[aria-labelledby] a[draggable="true"]'
    );
    chatLinks.forEach((link) => {
      // пропускаем, если чекбокс уже есть
      if (link.querySelector('.gpt-chat-checkbox')) return;

      // создаём чекбокс
      const checkbox = document.createElement('input');
      checkbox.type = 'checkbox';
      checkbox.className = 'gpt-chat-checkbox';
      checkbox.style.marginRight = '5px';

      // Останавливаем всплытие, чтобы клик по чекбоксу не кликал сам <a>
      checkbox.addEventListener('click', (e) => {
        e.stopPropagation();
        // больше ничего не останавливаем — дефолтное действие (переключение) остаётся
      });

      // вставляем перед ссылкой
      link.prepend(checkbox);
    });
  }

  /** Удаление чатов */
  async function deleteSelectedChats() {
    const selected = document.querySelectorAll('.gpt-chat-checkbox:checked');
    if (!selected.length) return alert('❗ Оберіть чати для видалення');
    if (!confirm(`Видалити ${selected.length} чат(и)?`)) return;

    createOrUpdateStatus('⏳ Видалення...');
    let countDeleted = 0;

    for (const cb of selected) {
      // 1. Находим ссылку и вытаскиваем ID
      const link = cb.closest('a[draggable="true"]');
      if (!link) {
        console.warn('❌ Не нашли <a> для чекбокса', cb);
        continue;
      }
      const href = link.getAttribute('href') || '';
      const m = href.match(/\/c\/([a-f0-9\-]+)/);
      if (!m) {
        console.warn('❌ Не смогли распарсить ID из', href);
        continue;
      }
      const id = m[1];

      // берём токен, который мы уже «поймали»
      const token = window.__GPT_ACCESS_TOKEN;
      if (!token) {
        console.error(
          '❌ Токен ещё не захвачен — сначала выполните в UI любое действие, вызывающее fetch с Authorization.'
        );
        alert(
          'Токен ещё не получен. Сначала откройте любое меню удаления вручную, чтобы скрипт успел перехватить fetch.'
        );
        return;
      }

      console.log('🗑 Удаляем (PATCH) conversation:', id);

      try {
        const res = await fetch(`/backend-api/conversation/${id}`, {
          method: 'PATCH',
          credentials: 'same-origin',
          headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${token}`,
          },
          body: JSON.stringify({ is_visible: false }),
        });
        if (res.ok) {
          console.log(`✅ Удалён ${id}`);
          countDeleted++;
        } else {
          console.warn(`⚠️ Ошибка ${res.status}`, await res.text());
        }
      } catch (err) {
        console.error('❌ Fetch failed for', id, err);
      }
    }

    createOrUpdateStatus(`✅ Видалено ${countDeleted} чат(и)!`);
    alert('✅ Видалення завершено!');
  }

  console.log('🚀 Скрипт Tampermonkey запущен');
  waitForSidebar();
})();