您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
纯净的Codex使用次数统计工具 - 感谢zetaloop佬的思路
// ==UserScript== // @name Codex次数统计 // @namespace https://github.com/your-username/codex-counter // @version 1.0.1 // @description 纯净的Codex使用次数统计工具 - 感谢zetaloop佬的思路 // @author schweigen // @license MIT // @homepage https://linux.do/t/topic/576007/44 // @match *://chatgpt.com/codex* // @grant none // @run-at document-start // ==/UserScript== (function () { "use strict"; function createCodexDisplay() { if (!document.body) { requestAnimationFrame(createCodexDisplay); return; } if (document.getElementById("codex-counter")) return; // 创建显示框 const displayBox = document.createElement("div"); displayBox.id = "codex-counter"; displayBox.style.position = "fixed"; displayBox.style.top = "20px"; displayBox.style.right = "20px"; displayBox.style.width = "200px"; displayBox.style.padding = "12px"; displayBox.style.backgroundColor = "rgba(0, 0, 0, 0.85)"; displayBox.style.color = "#fff"; displayBox.style.fontSize = "13px"; displayBox.style.borderRadius = "8px"; displayBox.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)"; displayBox.style.zIndex = "10000"; displayBox.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"; displayBox.style.display = "none"; // 初始隐藏,有数据时显示 displayBox.innerHTML = ` <div style="margin-bottom: 12px; font-weight: 600; color: #C26FFD;"> 📊 Codex 使用统计 </div> <div style="margin-bottom: 8px;"> 使用情况: <span id="codex-usage">--/--</span> </div> <div style="margin-bottom: 10px;"> <div style="width: 100%; height: 8px; background: #333; border-radius: 4px; overflow: hidden;"> <div id="codex-progress-bar" style="height: 100%; width: 0%; background: linear-gradient(90deg, #C26FFD, #A855F7); border-radius: 4px; transition: width 0.3s ease;"></div> </div> </div> <div style="font-size: 12px; color: #ccc;"> 重置倒计时: <span id="codex-reset-timer">--:--</span> </div>`; document.body.appendChild(displayBox); } // 初始化显示 createCodexDisplay(); // 使用 MutationObserver 确保元素不被删除 const observer = new MutationObserver(() => { if (!document.getElementById("codex-counter")) { createCodexDisplay(); } }); function startObserver() { if (!document.body) { requestAnimationFrame(startObserver); return; } observer.observe(document.body, { childList: true, subtree: true }); } startObserver(); // Codex数据存储 let codexData = { limit: null, used: null, remaining: null, resetTime: null }; // 更新 Codex 显示 function updateCodexDisplay(limit, remaining, resetsAfter) { const displayBox = document.getElementById("codex-counter"); const usageElement = document.getElementById("codex-usage"); const progressBar = document.getElementById("codex-progress-bar"); if (!displayBox || !usageElement || !progressBar) return; if (typeof limit !== "number" || typeof remaining !== "number") { displayBox.style.display = "none"; return; } // 更新数据 codexData.limit = limit; codexData.remaining = remaining; codexData.used = limit - remaining; codexData.resetTime = Date.now() + (resetsAfter * 1000); // 显示框 displayBox.style.display = "block"; // 更新使用情况 usageElement.textContent = `${codexData.used}/${codexData.limit}`; // 更新进度条 const percentage = Math.max(0, Math.min(100, (codexData.used / codexData.limit) * 100)); progressBar.style.width = `${percentage}%`; // 根据使用率调整颜色 if (percentage >= 90) { progressBar.style.background = "linear-gradient(90deg, #F44336, #d32f2f)"; } else if (percentage >= 70) { progressBar.style.background = "linear-gradient(90deg, #FFC107, #ffa000)"; } else { progressBar.style.background = "linear-gradient(90deg, #C26FFD, #A855F7)"; } console.log(`[Codex统计] 已用: ${codexData.used}/${codexData.limit}, 剩余: ${remaining}`); } // 倒计时更新 function updateCountdown() { const timerElement = document.getElementById("codex-reset-timer"); if (!timerElement || !codexData.resetTime) return; const remainingMs = Math.max(0, codexData.resetTime - Date.now()); const minutes = Math.floor(remainingMs / 60000); const seconds = Math.floor((remainingMs % 60000) / 1000); timerElement.textContent = `${minutes}:${seconds.toString().padStart(2, "0")}`; // 如果时间到了,清除数据等待下次更新 if (remainingMs <= 0) { codexData.resetTime = null; } } // 启动倒计时 setInterval(updateCountdown, 1000); // 拦截 fetch 请求,只关注 Codex 相关 API const originalFetch = window.fetch; window.fetch = async function (resource, options = {}) { const response = await originalFetch(resource, options); const requestUrl = typeof resource === "string" ? resource : resource?.url || ""; const requestMethod = typeof resource === "object" && resource.method ? resource.method : options?.method || "GET"; // 只处理 Codex 额度查询 API if ( requestUrl.includes("/backend-api/wham/tasks/rate_limit") && requestMethod.toUpperCase() === "GET" && response.ok ) { let responseBodyText; try { responseBodyText = await response.text(); const data = JSON.parse(responseBodyText); // 只在 codex 页面显示 if (location.pathname.startsWith("/codex")) { updateCodexDisplay( data.limit, data.remaining + 1, data.resets_after ); } // 返回新的 Response 对象 return new Response(responseBodyText, { status: response.status, statusText: response.statusText, headers: response.headers, }); } catch (error) { console.error("[Codex统计] 处理响应出错:", error); if (typeof responseBodyText === "string") { return new Response(responseBodyText, { status: response.status, statusText: response.statusText, headers: response.headers, }); } return response; } } return response; }; // 页面切换时隐藏/显示 let currentPath = location.pathname; setInterval(() => { if (location.pathname !== currentPath) { currentPath = location.pathname; const displayBox = document.getElementById("codex-counter"); if (displayBox) { if (currentPath.startsWith("/codex") && codexData.limit !== null) { displayBox.style.display = "block"; } else { displayBox.style.display = "none"; } } } }, 1000); })();