Bulk delete files, share links, and deleted chats from Grok in safe batches. Auto-scrolls to load more, skips your exceptions (e.g., .pdf, "important"), and shows live progress. Fast, smart, and beautiful.
// ==UserScript==
// @name Grok Batch Deleter
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Bulk delete files, share links, and deleted chats from Grok in safe batches. Auto-scrolls to load more, skips your exceptions (e.g., .pdf, "important"), and shows live progress. Fast, smart, and beautiful.
// @author Vishwas R
// @match https://grok.com/files*
// @match https://grok.com/share-links*
// @match https://grok.com/deleted-conversations*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// UI IDs
const UI_CONTAINER_ID = 'grok-multi-deleter';
const PAGE_SELECT_ID = 'grok-page-select';
const EXT_INPUT_ID = 'grok-ext-input';
const START_BTN_ID = 'grok-start-btn';
const STOP_BTN_ID = 'grok-stop-btn';
const STATUS_ID = 'grok-status';
const PROGRESS_BAR_ID = 'grok-progress-bar';
const STATS_ID = 'grok-stats';
// Delays (ms)
const HOVER_DELAY = 600;
const CONFIRM_DELAY = 800;
const DELETE_DELAY = 1200;
const SCROLL_DELAY = 1500;
let isRunning = false;
let totalDeleted = 0;
let totalSkipped = 0;
let pageConfig = {};
// Page-specific configs
const PAGE_CONFIGS = {
files: {
name: 'Files',
icon: '📁',
itemSelector: 'li',
titleSelector: 'span.flex-1',
deleteBtnSelector: 'button[aria-label="Delete file"]',
confirmBtnSelector: 'button[aria-label="Delete"]:not([aria-label="Delete file"])',
scrollContainer: '#_r_2s_ .overflow-y-auto',
exceptionType: 'extension',
placeholder: 'pdf, md, jpg'
},
sharelinks: {
name: 'Share Links',
icon: '🔗',
itemSelector: 'li',
titleSelector: 'span.flex-1, a[href*="/share/"] span',
deleteBtnSelector: 'button[aria-label="Delete link"]',
confirmBtnSelector: 'button[aria-label="Delete"]',
scrollContainer: '#_r_2s_ .overflow-y-auto',
exceptionType: 'extension',
placeholder: 'pdf, link'
},
deletedchats: {
name: 'Deleted Chats',
icon: '💬',
itemSelector: 'li, div[data-chat-id]',
titleSelector: 'span.flex-1, .chat-title',
deleteBtnSelector: 'button[aria-label="Delete chat"], button[aria-label="Permanently delete"]',
confirmBtnSelector: 'button[aria-label="Delete"], button:has-text("Delete forever")',
scrollContainer: '#_r_2s_ .overflow-y-auto',
exceptionType: 'keyword',
placeholder: 'important, work'
}
};
// Inject custom styles
function injectStyles() {
if (document.getElementById('grok-deleter-styles')) return;
const style = document.createElement('style');
style.id = 'grok-deleter-styles';
style.textContent = `
#${UI_CONTAINER_ID} {
margin: 16px 0;
padding: 20px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 16px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.grok-deleter-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
flex-wrap: wrap;
}
.credit-link {
color: #a0a0b0;
text-decoration: none;
font-size: 12px;
font-weight: 500;
transition: all 0.3s ease;
padding: 6px 12px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.credit-link:hover {
color: #667eea;
background: rgba(102, 126, 234, 0.1);
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-1px);
}
.grok-deleter-title {
font-size: 18px;
font-weight: 700;
color: #fff;
letter-spacing: 0.3px;
}
.grok-deleter-description {
color: #fff;
}
.grok-deleter-badge {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 4px 10px;
border-radius: 12px;
font-size: 10px;
font-weight: 600;
letter-spacing: 0.5px;
}
.grok-deleter-controls {
display: grid;
grid-template-columns: auto 1fr auto;
gap: 16px;
align-items: center;
margin-bottom: 16px;
}
.grok-control-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.grok-control-label {
color: #a0a0b0;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
}
.grok-control-group {
display: flex;
flex-direction: column;
gap: 8px;
}
#${PAGE_SELECT_ID} {
padding: 12px 16px;
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
color: #fff;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
outline: none;
min-width: 180px;
}
#${PAGE_SELECT_ID}:hover {
border-color: rgba(102, 126, 234, 0.5);
background: rgba(255, 255, 255, 0.08);
}
#${PAGE_SELECT_ID}:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
#${PAGE_SELECT_ID} option {
background: #1a1a2e;
color: #fff;
padding: 12px;
}
#${EXT_INPUT_ID} {
padding: 12px 16px;
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
color: #fff;
font-size: 14px;
transition: all 0.3s ease;
outline: none;
}
#${EXT_INPUT_ID}::placeholder {
color: rgba(255, 255, 255, 0.3);
}
#${EXT_INPUT_ID}:hover {
border-color: rgba(102, 126, 234, 0.5);
background: rgba(255, 255, 255, 0.08);
}
#${EXT_INPUT_ID}:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.grok-button {
padding: 12px 24px;
border: none;
border-radius: 12px;
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
gap: 8px;
}
#${START_BTN_ID} {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
#${START_BTN_ID}:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
#${START_BTN_ID}:active {
transform: translateY(0);
}
#${STOP_BTN_ID} {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
display: none;
}
#${STOP_BTN_ID}:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
}
.grok-status-panel {
background: rgba(0, 0, 0, 0.3);
border-radius: 12px;
padding: 16px;
border: 1px solid rgba(255, 255, 255, 0.05);
}
#${STATUS_ID} {
color: #fff;
font-size: 14px;
font-weight: 500;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
background: #667eea;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.status-indicator.running {
background: #51cf66;
animation: pulse 1s ease-in-out infinite;
}
.status-indicator.stopped {
background: #ff6b6b;
animation: none;
}
#${STATS_ID} {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
margin-top: 12px;
}
.stat-box {
background: rgba(255, 255, 255, 0.05);
padding: 12px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.stat-value {
font-size: 24px;
font-weight: 700;
color: #667eea;
display: block;
}
.stat-label {
font-size: 11px;
color: #a0a0b0;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 4px;
}
.stat-box.deleted .stat-value {
color: #51cf66;
}
.stat-box.skipped .stat-value {
color: #ffd43b;
}
.progress-container {
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
margin-top: 12px;
}
#${PROGRESS_BAR_ID} {
height: 100%;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
width: 0%;
transition: width 0.3s ease;
}
`;
document.head.appendChild(style);
}
// Create UI
function createUI() {
if (document.getElementById(UI_CONTAINER_ID)) return;
const currentPath = window.location.pathname;
const autoPage = currentPath.includes('/files') ? 'files' : currentPath.includes('/share-links') ? 'sharelinks' : currentPath.includes('/deleted-conversations') ? 'deletedchats' : 'files';
const config = PAGE_CONFIGS[autoPage];
const div = document.createElement('div');
div.id = UI_CONTAINER_ID;
// Get proper label and title based on page type
const skipLabel = config.exceptionType === 'extension' ?
'Skip Extensions' :
'Skip Keywords';
const skipTitle = config.exceptionType === 'extension' ?
`Skip files with these extensions (comma-separated). Example: ${config.placeholder}` :
`Skip items containing these keywords (comma-separated). Example: ${config.placeholder}`;
div.innerHTML = `
<div class="grok-deleter-header">
<span style="font-size: 24px;">${config.icon}</span>
<div class="grok-header-content">
<div class="grok-header-top">
<span class="grok-deleter-title">Grok Batch Deleter</span>
<span class="grok-deleter-badge">v1.0</span>
</div>
<div class="grok-deleter-description">
Efficiently delete multiple items across Grok pages (Files, Share Links, Deleted Chats) with smart filtering and auto-scroll capabilities.
</div>
</div>
<div style="margin-left: auto;">
<a href="https://vishwas.me" target="_blank" class="credit-link">
Script by Vishwas R
</a>
</div>
</div>
<div class="grok-deleter-controls">
<div class="grok-control-group">
<label class="grok-control-label" title="Select which Grok page to perform batch deletion on">
Target Page
</label>
<select id="${PAGE_SELECT_ID}" title="Select which Grok page to perform batch deletion on">
<option value="files" ${autoPage === 'files' ? 'selected' : ''}>📁 Files</option>
<option value="sharelinks" ${autoPage === 'sharelinks' ? 'selected' : ''}>🔗 Share Links</option>
<option value="deletedchats" ${autoPage === 'deletedchats' ? 'selected' : ''}>💬 Deleted Chats</option>
</select>
</div>
<div class="grok-control-group">
<label class="grok-control-label" title="${skipTitle}">
${skipLabel}
</label>
<input id="${EXT_INPUT_ID}"
placeholder="${config.placeholder}"
value="${autoPage === 'files' ? 'pdf, md' : autoPage === 'sharelinks' ? 'pdf' : 'important'}"
title="${skipTitle}">
</div>
<div class="grok-control-group">
<label class="grok-control-label" style="opacity: 0;">Action</label>
<div style="display: flex; gap: 8px;">
<button id="${START_BTN_ID}" class="grok-button" title="Start the batch deletion process">
<span>▶</span> Start
</button>
<button id="${STOP_BTN_ID}" class="grok-button" title="Stop the deletion process immediately">
<span>⏹</span> Stop
</button>
</div>
</div>
</div>
<div class="grok-status-panel">
<div id="${STATUS_ID}">
<span class="status-indicator"></span>
<span>Ready to start</span>
</div>
<div id="${STATS_ID}">
<div class="stat-box deleted">
<span class="stat-value" id="deleted-count">0</span>
<span class="stat-label">Deleted</span>
</div>
<div class="stat-box skipped">
<span class="stat-value" id="skipped-count">0</span>
<span class="stat-label">Skipped</span>
</div>
</div>
<div class="progress-container">
<div id="${PROGRESS_BAR_ID}"></div>
</div>
</div>
`;
const target = document.querySelector('header, main, body');
target?.insertBefore(div, target.firstChild);
document.getElementById(PAGE_SELECT_ID).onchange = updateInputPlaceholder;
document.getElementById(START_BTN_ID).onclick = () => startBatchDeletion(div);
document.getElementById(STOP_BTN_ID).onclick = stopDeletion;
}
function updateInputPlaceholder() {
const select = document.getElementById(PAGE_SELECT_ID);
const input = document.getElementById(EXT_INPUT_ID);
const config = PAGE_CONFIGS[select.value];
const skipLabel = config.exceptionType === 'extension' ?
'Skip Extensions' :
'Skip Keywords';
const skipTitle = config.exceptionType === 'extension' ?
`Skip files with these extensions (comma-separated). Example: ${config.placeholder}` :
`Skip items containing these keywords (comma-separated). Example: ${config.placeholder}`;
// Update label
const labelEl = input.closest('.grok-control-group').querySelector('.grok-control-label');
if (labelEl) {
labelEl.textContent = skipLabel;
labelEl.title = skipTitle;
}
input.placeholder = config.placeholder;
input.value = select.value === 'files' ? 'pdf, md' : select.value === 'sharelinks' ? 'pdf' : 'important';
input.title = skipTitle;
}
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function getExceptions(inputValue, exceptionType) {
return inputValue.split(',').map(e => e.trim().toLowerCase()).filter(e => e);
}
function matchesException(title, ext, exceptions, exceptionType) {
if (exceptionType === 'extension') {
const fileExt = ext.toLowerCase();
return exceptions.includes(fileExt);
} else {
return exceptions.some(kw => title.toLowerCase().includes(kw));
}
}
function getExtension(title) {
const match = title.match(/\.([a-z0-9]+)$/i);
return match ? match[1] : '';
}
async function hoverToReveal(element) {
element.dispatchEvent(new MouseEvent('mouseover', {
bubbles: true
}));
element.dispatchEvent(new MouseEvent('mouseenter', {
bubbles: true
}));
await delay(HOVER_DELAY);
}
function getScrollable() {
let container = document.querySelector(pageConfig.scrollContainer);
if (!container) container = document.querySelector('#_r_2s_ .overflow-y-auto, .overflow-y-auto');
return container;
}
async function scrollToLoadNext(scrollable) {
if (!scrollable) return false;
const oldHeight = scrollable.scrollHeight;
const oldCount = scrollable.querySelectorAll(pageConfig.itemSelector).length;
scrollable.scrollTop = scrollable.scrollHeight;
await delay(SCROLL_DELAY);
const newHeight = scrollable.scrollHeight;
const newCount = scrollable.querySelectorAll(pageConfig.itemSelector).length;
return newHeight > oldHeight || newCount > oldCount;
}
async function deleteBatch(exceptions, statusEl, scrollable) {
const items = Array.from(scrollable.querySelectorAll(pageConfig.itemSelector));
for (let item of items) {
if (!isRunning || !document.contains(item)) continue;
const clickable = item.querySelector('a.group\\/file, a, div[role="button"]') || item;
if (!clickable) continue;
const titleEl = clickable.querySelector(pageConfig.titleSelector);
if (!titleEl) continue;
const title = titleEl.textContent.trim();
const ext = getExtension(title);
if (matchesException(title, ext, exceptions, pageConfig.exceptionType)) {
totalSkipped++;
updateStatus(`Skipping: ${title}`, true);
updateStats();
continue;
}
updateStatus(`Deleting: ${title}`, true);
await hoverToReveal(clickable);
const deleteBtn = clickable.querySelector(pageConfig.deleteBtnSelector) || item.querySelector('button[aria-label*="Delete"]');
if (!deleteBtn) continue;
deleteBtn.click();
await delay(CONFIRM_DELAY);
const confirmBtn = document.querySelector(pageConfig.confirmBtnSelector) || document.querySelector('button[aria-label="Delete"], button:has-text("Delete")');
if (confirmBtn && confirmBtn.offsetParent !== null) {
confirmBtn.click();
totalDeleted++;
updateStats();
}
await delay(DELETE_DELAY);
}
}
function updateStatus(text, running = false) {
const statusEl = document.getElementById(STATUS_ID);
const indicator = statusEl.querySelector('.status-indicator');
statusEl.querySelector('span:last-child').textContent = text;
if (running) {
indicator.className = 'status-indicator running';
} else if (text.includes('Stopped')) {
indicator.className = 'status-indicator stopped';
} else {
indicator.className = 'status-indicator';
}
}
function updateStats() {
document.getElementById('deleted-count').textContent = totalDeleted;
document.getElementById('skipped-count').textContent = totalSkipped;
const total = totalDeleted + totalSkipped;
if (total > 0) {
const progress = (totalDeleted / total) * 100;
document.getElementById(PROGRESS_BAR_ID).style.width = `${progress}%`;
}
}
function stopDeletion() {
isRunning = false;
updateStatus('Stopped by user');
resetUI();
}
function resetUI() {
isRunning = false;
document.getElementById(START_BTN_ID).style.display = 'flex';
document.getElementById(STOP_BTN_ID).style.display = 'none';
}
async function startBatchDeletion() {
const select = document.getElementById(PAGE_SELECT_ID);
pageConfig = PAGE_CONFIGS[select.value];
const input = document.getElementById(EXT_INPUT_ID);
const exceptions = getExceptions(input.value, pageConfig.exceptionType);
if (exceptions.length === 0) {
alert(`Enter ${pageConfig.exceptionType}s to skip (e.g., ${pageConfig.placeholder})`);
return;
}
isRunning = true;
totalDeleted = 0;
totalSkipped = 0;
updateStats();
document.getElementById(START_BTN_ID).style.display = 'none';
document.getElementById(STOP_BTN_ID).style.display = 'flex';
const scrollable = getScrollable();
if (!scrollable) {
updateStatus('Error: Scroll container not found');
resetUI();
return;
}
updateStatus('Starting batch deletion...', true);
let noNewFilesCount = 0;
while (isRunning && noNewFilesCount < 3) {
await deleteBatch(exceptions, document.getElementById(STATUS_ID), scrollable);
const loadedMore = await scrollToLoadNext(scrollable);
if (!loadedMore) {
noNewFilesCount++;
updateStatus(`No new items (check ${noNewFilesCount}/3)...`, true);
} else {
noNewFilesCount = 0;
updateStatus('Loaded more items...', true);
}
}
updateStatus(`✓ Complete! Deleted ${totalDeleted}, Skipped ${totalSkipped}`);
resetUI();
}
const init = () => {
if (document.getElementById('_r_2s_') || document.querySelector('.overflow-y-auto')) {
injectStyles();
createUI();
updateInputPlaceholder();
} else {
setTimeout(init, 1000);
}
};
init();
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址