Grok-批量删除文件-油猴脚本

2026-04-11 08:191阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:

用Grok的时候,发现每次生图和上传图片/文件之后,即使删除了对应的聊天话题,文件也会遗留在 https://grok.com/files中。

所以在网上找了脚本,不过大多数都是失效了,偶然在reddit中看到了一个 帖子,提到了 grok_files_delete.js 这个脚本文件,使用后发现虽然也失效了 ,但是界面倒是挺好看,于是结合Grok修复了一下。

PS:也可能有其他更方便的方法,我没有找到,哈哈哈哈哈哈,有的话佬友可以教教我(感觉油猴脚本这个容易失效),感谢佬友。

使用流程:

  • 下载 油猴Tampermonkey 浏览器插件
  • 新建脚本,将下面的内容复制粘贴进去,ctrl+s保存。
  • https://grok.com/files 取消勾选仅模拟,开始即可。
  • 注意:这个是依次删除所有文件,佬友使用前请确认一下是否都是垃圾文件。
  • 效果图:
    image2560×639 116 KB

// ==UserScript== // @name Grok 文件批量删除 // @namespace alexds9 // @version 1.0 // @description 在 https://grok.com/files 页面添加控制面板,支持批量删除文件 // @match https://grok.com/files* // @run-at document-start // @grant GM_addStyle // ==/UserScript== (() => { "use strict"; // ----------------------------- // 配置(可自行调整) // ----------------------------- const CFG = { stepDelayMs: 60, dialogWaitMs: 3500, deleteWaitMs: 80, maxRetriesPerFile: 2, scrollIdlePassesToStop: 5, autoReloadEvery: 0, // >0 时每删除 N 个自动刷新页面(0=禁用) }; // ----------------------------- // 状态 // ----------------------------- const S = { running: false, stopRequested: false, dryRun: true, processed: new Set(), deletedOk: new Set(), countTried: 0, countDeleted: 0, countSkipped: 0, lastStatus: "", lastDeleteSignals: new Map(), deletionsSinceReload: 0, }; // ----------------------------- // 辅助函数 // ----------------------------- const sleep = ms => new Promise(r => setTimeout(r, ms)); const now = () => new Date().toISOString().replace("T", " ").replace("Z", ""); function setStatus(msg) { S.lastStatus = msg; const el = document.getElementById("gbd_status"); if (el) el.textContent = msg; } function extractFileIdFromHref(href) { if (!href) return null; const m = String(href).match(/[?&]file=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i); return m ? m[1] : null; } function isElementInDOM(el) { return !!(el && el.ownerDocument && el.ownerDocument.contains(el)); } function makeClickable(btn) { if (!btn) return; btn.classList.remove("hidden"); btn.style.display = "inline-flex"; btn.style.visibility = "visible"; btn.style.opacity = "1"; btn.style.pointerEvents = "auto"; btn.style.width = "auto"; } function getFileRows() { const anchors = Array.from(document.querySelectorAll('a[href^="/files?file="]')); const rows = []; for (const a of anchors) { const fileId = extractFileIdFromHref(a.getAttribute("href")); if (!fileId) continue; const li = a.closest("li") || a; rows.push({ fileId, a, li }); } const seen = new Set(); return rows.filter(r => seen.has(r.fileId) ? false : (seen.add(r.fileId), true)); } function findDeleteButton(row) { return ( row.li.querySelector('button[aria-label="Delete file"]') || row.li.querySelector('button[aria-label*="Delete"]') || row.a.querySelector('button[aria-label="Delete file"]') || row.a.querySelector('button[aria-label*="Delete"]') || // 垃圾桶图标常见特征 [...row.li.querySelectorAll('button')].find(b => { const svg = b.querySelector('svg'); return svg && (svg.innerHTML.includes('trash') || svg.innerHTML.includes('delete') || b.className.toLowerCase().includes('delete')); }) ) || null; } async function waitForInlineConfirmButton(row, timeoutMs) { const t0 = performance.now(); while (performance.now() - t0 < timeoutMs) { // 最高优先级:中文界面 aria-label="删除" 就是确认按钮 let btn = row.li.querySelector('button[aria-label="删除"]'); if (btn) { console.debug("[GrokDelete] 找到确认按钮 (aria-label=删除)"); return btn; } // 备选:找包含“删除”文字或类似 aria 的 btn = row.li.querySelector('button[aria-label*="删除"]') || row.li.querySelector('button[aria-label*="Confirm"]'); if (btn) return btn; // 找取消按钮 → 取旁边的那个(通常 × 左 ✓ 右) const cancelBtn = row.li.querySelector('button[aria-label="取消"]'); if (cancelBtn) { const siblings = [...cancelBtn.parentElement.querySelectorAll('button')]; const idx = siblings.indexOf(cancelBtn); if (idx >= 0 && siblings[idx + 1]) { console.debug("[GrokDelete] 通过取消按钮定位到确认按钮"); return siblings[idx + 1]; } } await sleep(80); } console.debug("[GrokDelete] 超时 - 未找到确认按钮"); return null; } async function waitForDeleteSignal(fileId, timeoutMs) { const t0 = performance.now(); while (performance.now() - t0 < timeoutMs) { const sig = S.lastDeleteSignals.get(fileId); if (sig?.ok) return sig; await sleep(100); } return null; } async function waitRowGone(row, timeoutMs) { const t0 = performance.now(); while (performance.now() - t0 < timeoutMs) { if (!isElementInDOM(row.li)) return true; await sleep(100); } return false; } function markRowVisually(row, kind) { if (!row?.li) return; if (kind === "deleting") { row.li.style.opacity = "0.55"; row.li.style.filter = "grayscale(0.6)"; } else if (kind === "deleted") { row.li.style.opacity = "0.25"; row.li.style.textDecoration = "line-through"; } else if (kind === "skipped") { row.li.style.opacity = "0.65"; row.li.style.outline = "1px solid #fb923c"; row.li.style.outlineOffset = "2px"; } } // ----------------------------- // 网络拦截 - 判断删除是否成功 // ----------------------------- function captureUuidFromUrl(url) { const m = String(url).match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i); return m ? m[1] : null; } function patchFetch() { const orig = window.fetch; window.fetch = async (...args) => { const [input, init] = args; const url = typeof input === 'string' ? input : input?.url || ''; const method = (init?.method || 'GET').toUpperCase(); const res = await orig(...args); try { const uuid = captureUuidFromUrl(url); if (uuid && (method === 'DELETE' || /\/files\b/i.test(url))) { S.lastDeleteSignals.set(uuid, { ok: res?.ok ?? false, ts: Date.now(), url, status: res?.status ?? 0, }); } } catch {} return res; }; } function patchXHR() { const origOpen = XMLHttpRequest.prototype.open; const origSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { this.__gbd_method = method.toUpperCase(); this.__gbd_url = url; return origOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function () { this.addEventListener('loadend', () => { try { const uuid = captureUuidFromUrl(this.__gbd_url); if (uuid && (this.__gbd_method === 'DELETE' || /\/files\b/i.test(this.__gbd_url))) { const ok = this.status >= 200 && this.status < 300; S.lastDeleteSignals.set(uuid, { ok, ts: Date.now(), status: this.status }); } } catch {} }); return origSend.apply(this, arguments); }; } patchFetch(); patchXHR(); // ----------------------------- // 核心删除逻辑 // ----------------------------- async function deleteOne(row) { if (!row?.fileId) return false; const fileId = row.fileId; if (S.processed.has(fileId)) return false; S.processed.add(fileId); S.countTried++; setStatus(`${now()} - 目标: ${fileId} (模拟=${S.dryRun})`); markRowVisually(row, "deleting"); if (S.dryRun) { await sleep(CFG.stepDelayMs); markRowVisually(row, "skipped"); S.countSkipped++; return true; } for (let attempt = 1; attempt <= CFG.maxRetriesPerFile; attempt++) { if (!S.running || S.stopRequested) return false; setStatus(`${now()} - 删除中 ${fileId} (第${attempt}次)`); const delBtn = findDeleteButton(row); if (!delBtn) { setStatus(`${now()} - 未找到垃圾桶 ${fileId},跳过`); markRowVisually(row, "skipped"); S.countSkipped++; return true; } makeClickable(delBtn); await sleep(40); delBtn.click(); await sleep(CFG.stepDelayMs); const confirmBtn = await waitForInlineConfirmButton(row, CFG.dialogWaitMs); if (confirmBtn) { console.debug("[GrokDelete] 即将点击确认按钮"); makeClickable(confirmBtn); await sleep(60); confirmBtn.click(); await sleep(CFG.stepDelayMs * 1.5); } else { setStatus(`${now()} - 未找到确认按钮 ${fileId},重试`); await sleep(300); continue; } const sig = await waitForDeleteSignal(fileId, CFG.deleteWaitMs * 2); if (sig?.ok) { S.countDeleted++; S.deletionsSinceReload++; markRowVisually(row, "deleted"); await waitRowGone(row, 1500); setStatus(`${now()} - 成功 ${fileId}`); return true; } if (await waitRowGone(row, 2000)) { S.countDeleted++; S.deletionsSinceReload++; setStatus(`${now()} - 成功(行消失) ${fileId}`); return true; } setStatus(`${now()} - 不确定 ${fileId},重试`); await sleep(400); } markRowVisually(row, "skipped"); S.countSkipped++; setStatus(`${now()} - 重试用尽,跳过 ${fileId}`); return true; } async function scrollToLoadMore() { const before = document.body.scrollHeight; window.scrollTo(0, document.body.scrollHeight); await sleep(800); return document.body.scrollHeight > before; } async function mainLoop() { let idle = 0; while (S.running && !S.stopRequested) { const rows = getFileRows().filter(r => !S.processed.has(r.fileId)); if (!rows.length) { const grew = await scrollToLoadMore(); idle++; setStatus(`${now()} - 无新文件(空闲${idle}/${CFG.scrollIdlePassesToStop})`); if (!grew && idle >= CFG.scrollIdlePassesToStop) break; await sleep(400); continue; } idle = 0; for (const r of rows) { if (!S.running || S.stopRequested) break; await deleteOne(r); await sleep(CFG.stepDelayMs); updateCounters(); } } S.running = false; S.stopRequested = false; updateButtons(); setStatus(`${now()} - 停止。尝试:${S.countTried} 成功:${S.countDeleted} 跳过:${S.countSkipped}`); } // ----------------------------- // UI // ----------------------------- function updateCounters() { const el = document.getElementById("gbd_counts"); if (el) { el.textContent = `已尝试: ${S.countTried} | 已删除: ${S.countDeleted} | 已跳过: ${S.countSkipped} | 仅模拟: ${S.dryRun}`; } } function updateButtons() { document.getElementById("gbd_start").disabled = S.running; document.getElementById("gbd_stop").disabled = !S.running; } function injectUI() { if (document.getElementById("gbd_panel")) return; GM_addStyle(` #gbd_panel { position: fixed; top: 16px; right: 16px; z-index: 999999; width: 360px; background: rgba(15,15,18,0.96); color: white; border: 1px solid rgba(255,255,255,0.12); border-radius: 12px; padding: 14px; font: 13.5px/1.45 system-ui, sans-serif; box-shadow: 0 10px 40px #000c; } #gbd_panel button { border-radius: 8px; border: 1px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.08); color: white; padding: 8px 16px; font-weight: 600; cursor: pointer; } #gbd_panel button:disabled { opacity: 0.45; cursor: not-allowed; } .row { display:flex; gap:14px; align-items:center; margin:12px 0 8px; } .muted { color: rgba(255,255,255,0.75); } .warn { color: #fbbf24; font-weight:700; } .status { margin-top: 12px; padding: 10px; background: rgba(0,0,0,0.35); border-radius: 8px; border: 1px solid rgba(255,255,255,0.06); max-height: 110px; overflow-y: auto; white-space: pre-wrap; font-size: 12.8px; } .title { font-size: 16px; font-weight: 800; margin-bottom: 6px; } `); const div = document.createElement("div"); div.id = "gbd_panel"; div.innerHTML = ` <div class="title">Grok 文件批量删除</div> <div class="muted">当前页面:/files(按列表顺序)</div> <div class="row"> <button id="gbd_start">开始</button> <button id="gbd_stop" disabled>停止</button> <label class="muted" style="margin-left:auto; user-select:none;"> <input id="gbd_dryrun" type="checkbox" checked> 仅模拟 </label> </div> <div class="row muted"> <span class="warn">重要:</span> 确认安全后再取消“仅模拟” </div> <div id="gbd_counts" class="muted" style="margin:12px 0;font-size:13.5px;"> 已尝试: 0 | 已删除: 0 | 已跳过: 0 | 仅模拟: true </div> <div id="gbd_status" class="status">待机中...</div> `; document.documentElement.appendChild(div); document.getElementById("gbd_dryrun").onchange = e => { S.dryRun = e.target.checked; updateCounters(); }; document.getElementById("gbd_start").onclick = async () => { if (S.running) return; if (!location.pathname.startsWith("/files")) { setStatus(`${now()} - 请在 /files 页面启动`); return; } S.running = true; S.stopRequested = false; updateButtons(); updateCounters(); setStatus(`${now()} - 开始...`); await sleep(120); mainLoop().catch(err => { S.running = false; updateButtons(); setStatus(`${now()} - 异常:${err?.message || err}`); console.error(err); }); }; document.getElementById("gbd_stop").onclick = () => { if (!S.running) return; S.stopRequested = true; setStatus(`${now()} - 正在停止...`); updateButtons(); }; updateButtons(); updateCounters(); } function onReady(fn) { if (document.readyState !== "loading") fn(); else document.addEventListener("DOMContentLoaded", fn, {once: true}); } onReady(() => { injectUI(); console.log("[Grok批量删除] 脚本加载完成 v0.7.3"); }); })(); 网友解答:


--【壹】--:

自动删除聊天记录有加吗


--【贰】--:

感谢大佬!


--【叁】--:

感谢大佬!


--【肆】--:

感谢大佬!如德芙般顺滑!已经删了五百多的文档了

标签:人工智能
问题描述:

用Grok的时候,发现每次生图和上传图片/文件之后,即使删除了对应的聊天话题,文件也会遗留在 https://grok.com/files中。

所以在网上找了脚本,不过大多数都是失效了,偶然在reddit中看到了一个 帖子,提到了 grok_files_delete.js 这个脚本文件,使用后发现虽然也失效了 ,但是界面倒是挺好看,于是结合Grok修复了一下。

PS:也可能有其他更方便的方法,我没有找到,哈哈哈哈哈哈,有的话佬友可以教教我(感觉油猴脚本这个容易失效),感谢佬友。

使用流程:

  • 下载 油猴Tampermonkey 浏览器插件
  • 新建脚本,将下面的内容复制粘贴进去,ctrl+s保存。
  • https://grok.com/files 取消勾选仅模拟,开始即可。
  • 注意:这个是依次删除所有文件,佬友使用前请确认一下是否都是垃圾文件。
  • 效果图:
    image2560×639 116 KB

// ==UserScript== // @name Grok 文件批量删除 // @namespace alexds9 // @version 1.0 // @description 在 https://grok.com/files 页面添加控制面板,支持批量删除文件 // @match https://grok.com/files* // @run-at document-start // @grant GM_addStyle // ==/UserScript== (() => { "use strict"; // ----------------------------- // 配置(可自行调整) // ----------------------------- const CFG = { stepDelayMs: 60, dialogWaitMs: 3500, deleteWaitMs: 80, maxRetriesPerFile: 2, scrollIdlePassesToStop: 5, autoReloadEvery: 0, // >0 时每删除 N 个自动刷新页面(0=禁用) }; // ----------------------------- // 状态 // ----------------------------- const S = { running: false, stopRequested: false, dryRun: true, processed: new Set(), deletedOk: new Set(), countTried: 0, countDeleted: 0, countSkipped: 0, lastStatus: "", lastDeleteSignals: new Map(), deletionsSinceReload: 0, }; // ----------------------------- // 辅助函数 // ----------------------------- const sleep = ms => new Promise(r => setTimeout(r, ms)); const now = () => new Date().toISOString().replace("T", " ").replace("Z", ""); function setStatus(msg) { S.lastStatus = msg; const el = document.getElementById("gbd_status"); if (el) el.textContent = msg; } function extractFileIdFromHref(href) { if (!href) return null; const m = String(href).match(/[?&]file=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i); return m ? m[1] : null; } function isElementInDOM(el) { return !!(el && el.ownerDocument && el.ownerDocument.contains(el)); } function makeClickable(btn) { if (!btn) return; btn.classList.remove("hidden"); btn.style.display = "inline-flex"; btn.style.visibility = "visible"; btn.style.opacity = "1"; btn.style.pointerEvents = "auto"; btn.style.width = "auto"; } function getFileRows() { const anchors = Array.from(document.querySelectorAll('a[href^="/files?file="]')); const rows = []; for (const a of anchors) { const fileId = extractFileIdFromHref(a.getAttribute("href")); if (!fileId) continue; const li = a.closest("li") || a; rows.push({ fileId, a, li }); } const seen = new Set(); return rows.filter(r => seen.has(r.fileId) ? false : (seen.add(r.fileId), true)); } function findDeleteButton(row) { return ( row.li.querySelector('button[aria-label="Delete file"]') || row.li.querySelector('button[aria-label*="Delete"]') || row.a.querySelector('button[aria-label="Delete file"]') || row.a.querySelector('button[aria-label*="Delete"]') || // 垃圾桶图标常见特征 [...row.li.querySelectorAll('button')].find(b => { const svg = b.querySelector('svg'); return svg && (svg.innerHTML.includes('trash') || svg.innerHTML.includes('delete') || b.className.toLowerCase().includes('delete')); }) ) || null; } async function waitForInlineConfirmButton(row, timeoutMs) { const t0 = performance.now(); while (performance.now() - t0 < timeoutMs) { // 最高优先级:中文界面 aria-label="删除" 就是确认按钮 let btn = row.li.querySelector('button[aria-label="删除"]'); if (btn) { console.debug("[GrokDelete] 找到确认按钮 (aria-label=删除)"); return btn; } // 备选:找包含“删除”文字或类似 aria 的 btn = row.li.querySelector('button[aria-label*="删除"]') || row.li.querySelector('button[aria-label*="Confirm"]'); if (btn) return btn; // 找取消按钮 → 取旁边的那个(通常 × 左 ✓ 右) const cancelBtn = row.li.querySelector('button[aria-label="取消"]'); if (cancelBtn) { const siblings = [...cancelBtn.parentElement.querySelectorAll('button')]; const idx = siblings.indexOf(cancelBtn); if (idx >= 0 && siblings[idx + 1]) { console.debug("[GrokDelete] 通过取消按钮定位到确认按钮"); return siblings[idx + 1]; } } await sleep(80); } console.debug("[GrokDelete] 超时 - 未找到确认按钮"); return null; } async function waitForDeleteSignal(fileId, timeoutMs) { const t0 = performance.now(); while (performance.now() - t0 < timeoutMs) { const sig = S.lastDeleteSignals.get(fileId); if (sig?.ok) return sig; await sleep(100); } return null; } async function waitRowGone(row, timeoutMs) { const t0 = performance.now(); while (performance.now() - t0 < timeoutMs) { if (!isElementInDOM(row.li)) return true; await sleep(100); } return false; } function markRowVisually(row, kind) { if (!row?.li) return; if (kind === "deleting") { row.li.style.opacity = "0.55"; row.li.style.filter = "grayscale(0.6)"; } else if (kind === "deleted") { row.li.style.opacity = "0.25"; row.li.style.textDecoration = "line-through"; } else if (kind === "skipped") { row.li.style.opacity = "0.65"; row.li.style.outline = "1px solid #fb923c"; row.li.style.outlineOffset = "2px"; } } // ----------------------------- // 网络拦截 - 判断删除是否成功 // ----------------------------- function captureUuidFromUrl(url) { const m = String(url).match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i); return m ? m[1] : null; } function patchFetch() { const orig = window.fetch; window.fetch = async (...args) => { const [input, init] = args; const url = typeof input === 'string' ? input : input?.url || ''; const method = (init?.method || 'GET').toUpperCase(); const res = await orig(...args); try { const uuid = captureUuidFromUrl(url); if (uuid && (method === 'DELETE' || /\/files\b/i.test(url))) { S.lastDeleteSignals.set(uuid, { ok: res?.ok ?? false, ts: Date.now(), url, status: res?.status ?? 0, }); } } catch {} return res; }; } function patchXHR() { const origOpen = XMLHttpRequest.prototype.open; const origSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { this.__gbd_method = method.toUpperCase(); this.__gbd_url = url; return origOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function () { this.addEventListener('loadend', () => { try { const uuid = captureUuidFromUrl(this.__gbd_url); if (uuid && (this.__gbd_method === 'DELETE' || /\/files\b/i.test(this.__gbd_url))) { const ok = this.status >= 200 && this.status < 300; S.lastDeleteSignals.set(uuid, { ok, ts: Date.now(), status: this.status }); } } catch {} }); return origSend.apply(this, arguments); }; } patchFetch(); patchXHR(); // ----------------------------- // 核心删除逻辑 // ----------------------------- async function deleteOne(row) { if (!row?.fileId) return false; const fileId = row.fileId; if (S.processed.has(fileId)) return false; S.processed.add(fileId); S.countTried++; setStatus(`${now()} - 目标: ${fileId} (模拟=${S.dryRun})`); markRowVisually(row, "deleting"); if (S.dryRun) { await sleep(CFG.stepDelayMs); markRowVisually(row, "skipped"); S.countSkipped++; return true; } for (let attempt = 1; attempt <= CFG.maxRetriesPerFile; attempt++) { if (!S.running || S.stopRequested) return false; setStatus(`${now()} - 删除中 ${fileId} (第${attempt}次)`); const delBtn = findDeleteButton(row); if (!delBtn) { setStatus(`${now()} - 未找到垃圾桶 ${fileId},跳过`); markRowVisually(row, "skipped"); S.countSkipped++; return true; } makeClickable(delBtn); await sleep(40); delBtn.click(); await sleep(CFG.stepDelayMs); const confirmBtn = await waitForInlineConfirmButton(row, CFG.dialogWaitMs); if (confirmBtn) { console.debug("[GrokDelete] 即将点击确认按钮"); makeClickable(confirmBtn); await sleep(60); confirmBtn.click(); await sleep(CFG.stepDelayMs * 1.5); } else { setStatus(`${now()} - 未找到确认按钮 ${fileId},重试`); await sleep(300); continue; } const sig = await waitForDeleteSignal(fileId, CFG.deleteWaitMs * 2); if (sig?.ok) { S.countDeleted++; S.deletionsSinceReload++; markRowVisually(row, "deleted"); await waitRowGone(row, 1500); setStatus(`${now()} - 成功 ${fileId}`); return true; } if (await waitRowGone(row, 2000)) { S.countDeleted++; S.deletionsSinceReload++; setStatus(`${now()} - 成功(行消失) ${fileId}`); return true; } setStatus(`${now()} - 不确定 ${fileId},重试`); await sleep(400); } markRowVisually(row, "skipped"); S.countSkipped++; setStatus(`${now()} - 重试用尽,跳过 ${fileId}`); return true; } async function scrollToLoadMore() { const before = document.body.scrollHeight; window.scrollTo(0, document.body.scrollHeight); await sleep(800); return document.body.scrollHeight > before; } async function mainLoop() { let idle = 0; while (S.running && !S.stopRequested) { const rows = getFileRows().filter(r => !S.processed.has(r.fileId)); if (!rows.length) { const grew = await scrollToLoadMore(); idle++; setStatus(`${now()} - 无新文件(空闲${idle}/${CFG.scrollIdlePassesToStop})`); if (!grew && idle >= CFG.scrollIdlePassesToStop) break; await sleep(400); continue; } idle = 0; for (const r of rows) { if (!S.running || S.stopRequested) break; await deleteOne(r); await sleep(CFG.stepDelayMs); updateCounters(); } } S.running = false; S.stopRequested = false; updateButtons(); setStatus(`${now()} - 停止。尝试:${S.countTried} 成功:${S.countDeleted} 跳过:${S.countSkipped}`); } // ----------------------------- // UI // ----------------------------- function updateCounters() { const el = document.getElementById("gbd_counts"); if (el) { el.textContent = `已尝试: ${S.countTried} | 已删除: ${S.countDeleted} | 已跳过: ${S.countSkipped} | 仅模拟: ${S.dryRun}`; } } function updateButtons() { document.getElementById("gbd_start").disabled = S.running; document.getElementById("gbd_stop").disabled = !S.running; } function injectUI() { if (document.getElementById("gbd_panel")) return; GM_addStyle(` #gbd_panel { position: fixed; top: 16px; right: 16px; z-index: 999999; width: 360px; background: rgba(15,15,18,0.96); color: white; border: 1px solid rgba(255,255,255,0.12); border-radius: 12px; padding: 14px; font: 13.5px/1.45 system-ui, sans-serif; box-shadow: 0 10px 40px #000c; } #gbd_panel button { border-radius: 8px; border: 1px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.08); color: white; padding: 8px 16px; font-weight: 600; cursor: pointer; } #gbd_panel button:disabled { opacity: 0.45; cursor: not-allowed; } .row { display:flex; gap:14px; align-items:center; margin:12px 0 8px; } .muted { color: rgba(255,255,255,0.75); } .warn { color: #fbbf24; font-weight:700; } .status { margin-top: 12px; padding: 10px; background: rgba(0,0,0,0.35); border-radius: 8px; border: 1px solid rgba(255,255,255,0.06); max-height: 110px; overflow-y: auto; white-space: pre-wrap; font-size: 12.8px; } .title { font-size: 16px; font-weight: 800; margin-bottom: 6px; } `); const div = document.createElement("div"); div.id = "gbd_panel"; div.innerHTML = ` <div class="title">Grok 文件批量删除</div> <div class="muted">当前页面:/files(按列表顺序)</div> <div class="row"> <button id="gbd_start">开始</button> <button id="gbd_stop" disabled>停止</button> <label class="muted" style="margin-left:auto; user-select:none;"> <input id="gbd_dryrun" type="checkbox" checked> 仅模拟 </label> </div> <div class="row muted"> <span class="warn">重要:</span> 确认安全后再取消“仅模拟” </div> <div id="gbd_counts" class="muted" style="margin:12px 0;font-size:13.5px;"> 已尝试: 0 | 已删除: 0 | 已跳过: 0 | 仅模拟: true </div> <div id="gbd_status" class="status">待机中...</div> `; document.documentElement.appendChild(div); document.getElementById("gbd_dryrun").onchange = e => { S.dryRun = e.target.checked; updateCounters(); }; document.getElementById("gbd_start").onclick = async () => { if (S.running) return; if (!location.pathname.startsWith("/files")) { setStatus(`${now()} - 请在 /files 页面启动`); return; } S.running = true; S.stopRequested = false; updateButtons(); updateCounters(); setStatus(`${now()} - 开始...`); await sleep(120); mainLoop().catch(err => { S.running = false; updateButtons(); setStatus(`${now()} - 异常:${err?.message || err}`); console.error(err); }); }; document.getElementById("gbd_stop").onclick = () => { if (!S.running) return; S.stopRequested = true; setStatus(`${now()} - 正在停止...`); updateButtons(); }; updateButtons(); updateCounters(); } function onReady(fn) { if (document.readyState !== "loading") fn(); else document.addEventListener("DOMContentLoaded", fn, {once: true}); } onReady(() => { injectUI(); console.log("[Grok批量删除] 脚本加载完成 v0.7.3"); }); })(); 网友解答:


--【壹】--:

自动删除聊天记录有加吗


--【贰】--:

感谢大佬!


--【叁】--:

感谢大佬!


--【肆】--:

感谢大佬!如德芙般顺滑!已经删了五百多的文档了

标签:人工智能