首先感谢 完全准确查询你的GPT订阅周限的方法 提供的思路和 js 脚本。
基于大佬的内容优化为了油猴脚本,看起来更方便。
附个人 pro 20x 结果示例图:
打开 https://chatgpt.com/codex/cloud/settings/analytics#usage 页面使用
// ==UserScript==
// @name Codex Quota Compass (Visual Edition)
// @namespace http://tampermonkey.net/
// @version 1.4
// @description 人性化展示 Codex 配额,修复 reset_at 报错及关闭按钮失效问题
// @author Jun Zhao
// @match https://chatgpt.com/codex/cloud/settings/analytics*
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
const CONFIG = {
USD_PER_CREDIT: 40 / 1000,
ROLLING_DAYS: 30
};
GM_addStyle(`
#codex-compass-root {
position: fixed;
top: 10%;
left: 50%;
transform: translateX(-50%);
width: 650px;
max-height: 85vh;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
z-index: 10001;
padding: 24px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
display: none;
overflow-y: auto;
border: 1px solid #e5e5e5;
color: #333;
}
.compass-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
.compass-title { font-size: 20px; font-weight: 600; color: #1a1a1a; }
.compass-close { cursor: pointer; font-size: 28px; color: #999; line-height: 1; padding: 5px; }
.compass-close:hover { color: #333; }
.compass-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px; }
.compass-card { background: #f8f9fa; padding: 16px; border-radius: 8px; border: 1px solid #eee; }
.compass-card.highlight { background: #eefaf5; border-color: #d1f2e1; }
.card-label { font-size: 13px; color: #666; margin-bottom: 6px; }
.card-value { font-size: 20px; font-weight: bold; color: #10a37f; }
.compass-table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 13px; }
.compass-table th { text-align: left; padding: 10px 8px; border-bottom: 2px solid #eee; color: #666; }
.compass-table td { padding: 10px 8px; border-bottom: 1px solid #eee; }
.compass-footer-row { background: #f8f9fa; font-weight: bold; }
#codex-compass-btn {
position: fixed; bottom: 20px; right: 20px; z-index: 10000;
padding: 12px 24px; background: #10a37f; color: white;
border: none; border-radius: 8px; cursor: pointer;
box-shadow: 0 4px 12px rgba(16,163,127,0.3); font-weight: 600;
}
`);
function getAccessToken() {
const bootstrapData = document.getElementById('client-bootstrap')?.textContent;
return bootstrapData?.match(/[\w-]{30,}\.[\w-]{30,}\.[\w-]{30,}/)?.[0] || null;
}
async function apiGet(path, token) {
const res = await fetch(path, { headers: { 'Authorization': `Bearer ${token}` } });
if (!res.ok) throw new Error(`API Error: ${res.status}`);
return res.json();
}
const showPanel = (data) => {
const root = document.getElementById('codex-compass-root');
const { secondary, dailyList, totalUsed, estimate } = data;
// 计算合计
const totalUSD = (totalUsed * CONFIG.USD_PER_CREDIT).toFixed(2);
const totalTurns = dailyList.reduce((sum, d) => sum + (d.totals?.turns || 0), 0);
// 容错处理重置时间
const resetAt = secondary?.reset_at;
const windowSec = secondary?.limit_window_seconds;
const cycleStart = (resetAt && windowSec)
? new Date((resetAt - windowSec) * 1000).toLocaleDateString()
: "未知";
root.innerHTML = `
<div class="compass-header">
<div class="compass-title">📊 Codex 配额深度分析</div>
<div class="compass-close" id="compass-close-btn">×</div>
</div>
<div class="compass-grid">
<div class="compass-card">
<div class="card-label">周使用比例 (已用)</div>
<div class="card-value">${secondary?.used_percent ?? '0'}%</div>
</div>
<div class="compass-card">
<div class="card-label">本周期已用额度</div>
<div class="card-value">${totalUsed.toFixed(2)} <span style="font-size:12px; font-weight:normal">Credits</span></div>
</div>
<div class="compass-card highlight">
<div class="card-label">反推周总额度 (100%)</div>
<div class="card-value">${estimate.totalCredits} <span style="font-size:12px; font-weight:normal">Credits</span></div>
</div>
<div class="compass-card">
<div class="card-label">合计价值 (USD)</div>
<div class="card-value">$ ${totalUSD}</div>
</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<span style="font-weight:600;">本周期每日消耗明细</span>
<span style="font-size: 12px; color: #999;">周期始于:${cycleStart}</span>
</div>
<table class="compass-table">
<thead>
<tr>
<th>日期</th>
<th>消耗 Credits</th>
<th>折算金额</th>
<th>对话轮数</th>
</tr>
</thead>
<tbody>
${dailyList.map(row => `
<tr>
<td>${row.date}</td>
<td style="font-family: monospace;">${(row.totals?.credits || 0).toFixed(4)}</td>
<td>$ ${((row.totals?.credits || 0) * CONFIG.USD_PER_CREDIT).toFixed(2)}</td>
<td>${row.totals?.turns || 0}</td>
</tr>
`).reverse().join('')}
</tbody>
<tfoot>
<tr class="compass-footer-row">
<td style="padding: 12px 8px;">合计</td>
<td style="font-family: monospace;">${totalUsed.toFixed(4)}</td>
<td style="color: #10a37f;">$ ${totalUSD}</td>
<td>${totalTurns}</td>
</tr>
</tfoot>
</table>
`;
root.style.display = 'block';
// 重新绑定关闭事件
document.getElementById('compass-close-btn').onclick = () => {
root.style.display = 'none';
};
};
const run = async () => {
const btn = document.getElementById('codex-compass-btn');
const token = getAccessToken();
if (!token) return alert("未发现登录令牌,请确保已登录 ChatGPT 并刷新页面。");
btn.innerText = '分析中...';
try {
const usage = await apiGet('/backend-api/wham/usage', token);
// 修复点:加入可选链,如果 secondary_window 不存在则退而求其次找 primary_window
const secondary = usage?.rate_limit?.secondary_window || usage?.rate_limit?.primary_window;
if (!secondary) {
throw new Error("未能获取限额窗口数据。");
}
const windowStartMs = (secondary.reset_at - (secondary.limit_window_seconds || 604800)) * 1000;
const startDate = new Date(windowStartMs).toISOString().split('T')[0];
const endDate = new Date(Date.now() + 86400000).toISOString().split('T')[0];
const dailyData = await apiGet(`/backend-api/wham/analytics/daily-workspace-usage-counts?start_date=${startDate}&end_date=${endDate}&group_by=day`, token);
const dailyList = dailyData.data || [];
const totalUsedCredits = dailyList.reduce((sum, d) => sum + (d.totals?.credits || 0), 0);
const ratio = (secondary.used_percent || 0) / 100;
const estCredits = ratio > 0 ? (totalUsedCredits / ratio).toFixed(1) : "无法计算";
showPanel({
secondary,
dailyList,
totalUsed: totalUsedCredits,
estimate: {
totalCredits: estCredits,
totalUSD: ratio > 0 ? (estCredits * CONFIG.USD_PER_CREDIT).toFixed(2) : "0.00"
}
});
} catch (e) {
console.error(e);
alert("分析失败:" + e.message);
} finally {
btn.innerText = '📊 运行用量分析';
}
};
const btn = document.createElement('button');
btn.id = 'codex-compass-btn';
btn.innerText = '📊 运行用量分析';
btn.onclick = run;
document.body.appendChild(btn);
const root = document.createElement('div');
root.id = 'codex-compass-root';
document.body.appendChild(root);
})();
6 个帖子 - 4 位参与者