分享一个查询 Codex 重置次数过期时间的油猴脚本

James Wang 2026-07-03 16:10 1

逛了一圈,大家分享的都是提示词、本地脚本。有时候我就在网页看用量,想瞄一眼,所以就参照大家分享的方法改成了油猴脚本。


访问Codex Web的用量查询页面 https://chatgpt.com/codex/cloud/settings/analytics ,就会自动在页面添加重置次数和时间。

效果如下:


脚本代码:


// ==UserScript==
// @name Codex Resets
// @namespace http://tampermonkey.net/
// @version 2026-06-29
// @author stjuan627
// @match https://chatgpt.com/codex/cloud/settings/analytics
// @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @run-at document-idle
// @grant none
// ==/UserScript==

(async function() {
'use strict';

const waitForElement = (selector, timeout = 10000) => new Promise((resolve, reject) => {
const el = document.querySelector(selector);
if (el) {
resolve(el);
return;
}

const observer = new MutationObserver(() => {
const found = document.querySelector(selector);
if (found) {
observer.disconnect();
resolve(found);
}
});

observer.observe(document.documentElement, {
childList: true,
subtree: true,
});

setTimeout(() => {
observer.disconnect();
reject(new Error(`Timeout waiting for selector: ${selector}`));
}, timeout);
});

const container = await waitForElement('#main section > div:nth-child(1) > div');

const at = await fetch('/api/auth/session')
.then(resp => resp.json())
.then(data => data.accessToken);
const headers = { Authorization: `Bearer ${at}` };
const data = await fetch('/backend-api/wham/rate-limit-reset-credits', { headers })
.then(resp => resp.json());

console.group('You have %s resets', data.available_count);
const credits = data.credits.map(item => ([
new Date(item.granted_at).toLocaleString(),
new Date(item.expires_at).toLocaleString(),
]));
console.table(credits);
console.groupEnd();

const html = `<section class="mt-6 flex flex-col gap-6">
<h2 class="text-xl font-semibold text-gray-900 dark:text-gray-100">Resets</h2>
<article
class="border-token-border-subtle bg-token-bg-primary overflow-hidden rounded-3xl border shadow-[0_16px_32px_-24px_rgba(15,23,42,0.25)]">
<div class="flex items-center justify-between gap-4 p-6">
<table class="w-full border-separate border-spacing-0">
<thead>
<tr>
<th
class="text-token border-token-border-medium bg-token-main-surface-primary sticky top-0 z-10 border-b-[0.5px] py-2 font-semibold pe-4 last:pe-0 text-start ps-4 text-sm">
#</th>
<th
class="text-token border-token-border-medium bg-token-main-surface-primary sticky top-0 z-10 border-b-[0.5px] py-2 font-semibold pe-4 last:pe-0 text-start ps-4 text-sm">
获得时间</th>
<th
class="text-token border-token-border-medium bg-token-main-surface-primary sticky top-0 z-10 border-b-[0.5px] py-2 font-semibold pe-4 last:pe-0 text-start ps-4 text-sm">
过期时间</th>
</tr>
</thead>
<tbody>
${credits.length > 0 ? credits.map((row, index) => `
<tr>
<td class="border-token-border-medium border-b-[0.5px] align-top py-2 font-semibold pe-4 last:pe-0 text-start ps-4 text-sm">${index + 1}</td>
<td class="border-token-border-medium border-b-[0.5px] align-top py-2 font-semibold pe-4 last:pe-0 text-start ps-4 text-sm">${row[0]}</td>
<td class="border-token-border-medium border-b-[0.5px] align-top py-2 font-semibold pe-4 last:pe-0 text-start ps-4 text-sm">${row[1]}</td>
</tr>`
).join('')
: `<tr class="">
<td class="border-token-border-medium border-b-[0.5px] align-top pe-4 last:pe-0 [tr:last-child]:border-b-0 text-left" colspan="4">
<div class="flex [tr[data-disabled=true]]:opacity-50 flex h-16 items-center justify-center text-sm">
没有reset</div>
</td>
</tr>`
}
</tbody>
</table>
</div>
</article>
</section>`;
container.insertAdjacentHTML('beforeend', html);
})();

觉得有用的帮忙点个赞 ^-^

最新回复 (2)
  • lucas_z 07-03 17:08
    1

    使用了佬的脚本,但是我的页面不显示,可能是因为DOM不同的原因吧。修改了一版,不根据DOM进行html的改变。站在佬的肩膀上优化了一版。


    // ==UserScript==
    // @name Codex Resets Debug
    // @namespace http://tampermonkey.net/
    // @version 2026-07-03-debug
    // @author stjuan627
    // @match https://chatgpt.com/*
    // @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
    // @run-at document-start
    // @grant none
    // ==/UserScript==

    (function () {

    ‘use strict’;


    const BOX_ID = 'codex-resets-debug-box';

    function ensureBox() {
    let box = document.getElementById(BOX_ID);
    if (box) return box;

    if (!document.body) return null;

    box = document.createElement('div');
    box.id = BOX_ID;
    box.style.cssText = `
    position: fixed;
    top: 80px;
    right: 20px;
    z-index: 2147483647;
    width: 420px;
    max-height: 70vh;
    overflow: auto;
    background: #111827;
    color: #fff;
    font-size: 13px;
    line-height: 1.5;
    padding: 14px;
    border-radius: 12px;
    box-shadow: 0 12px 30px rgba(0,0,0,.3);
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    `;

    box.innerHTML = `
    <div style="font-weight:700;margin-bottom:8px;">Codex Resets Debug</div>
    <div id="codex-resets-debug-log"></div>
    `;

    document.body.appendChild(box);
    return box;
    }

    function log(message) {
    console.log('[Codex Resets]', message);

    const box = ensureBox();
    if (!box) return;

    const logEl = box.querySelector('#codex-resets-debug-log');
    const line = document.createElement('div');
    line.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
    logEl.appendChild(line);
    }

    function escapeHtml(value) {
    return String(value ?? '')
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#039;');
    }

    function formatDate(value) {
    if (!value) return '-';
    return new Date(value).toLocaleString();
    }

    function renderCredits(data) {
    const box = ensureBox();
    if (!box) return;

    const credits = Array.isArray(data?.credits) ? data.credits : [];
    const availableCount = data?.available_count ?? credits.length;

    const rows = credits.length
    ? credits.map((item, index) => `
    <tr>
    <td style="padding:6px;border-bottom:1px solid #374151;">${index + 1}</td>
    <td style="padding:6px;border-bottom:1px solid #374151;">${escapeHtml(formatDate(item.granted_at))}</td>
    <td style="padding:6px;border-bottom:1px solid #374151;">${escapeHtml(formatDate(item.expires_at))}</td>
    </tr>
    `).join('')
    : `
    <tr>
    <td colspan="3" style="padding:10px;text-align:center;color:#d1d5db;">没有 reset</td>
    </tr>
    `;

    box.innerHTML = `
    <div style="font-weight:700;margin-bottom:8px;">Codex Resets</div>
    <div style="margin-bottom:8px;">Available: ${escapeHtml(availableCount)}</div>
    <table style="width:100%;border-collapse:collapse;color:#fff;">
    <thead>
    <tr>
    <th style="padding:6px;border-bottom:1px solid #6b7280;text-align:left;">#</th>
    <th style="padding:6px;border-bottom:1px solid #6b7280;text-align:left;">获得时间</th>
    <th style="padding:6px;border-bottom:1px solid #6b7280;text-align:left;">过期时间</th>
    </tr>
    </thead>
    <tbody>
    ${rows}
    </tbody>
    </table>
    `;
    }

    async function main() {
    log('script started');
    log(`href: ${location.href}`);

    if (!location.href.startsWith('https://chatgpt.com/codex/cloud/settings/analytics')) {
    log('not analytics page, skip');
    return;
    }

    const sessionResp = await fetch('/api/auth/session', {
    credentials: 'include'
    });

    log(`session status: ${sessionResp.status}`);

    if (!sessionResp.ok) {
    throw new Error(`/api/auth/session failed: ${sessionResp.status}`);
    }

    const session = await sessionResp.json();
    const accessToken = session?.accessToken;

    log(`accessToken: ${accessToken ? 'yes' : 'no'}`);

    const headers = {};
    if (accessToken) {
    headers.Authorization = `Bearer ${accessToken}`;
    }

    const creditsResp = await fetch('/backend-api/wham/rate-limit-reset-credits', {
    method: 'GET',
    credentials: 'include',
    headers
    });

    log(`credits status: ${creditsResp.status}`);

    const rawText = await creditsResp.text();
    log(`credits raw: ${rawText.slice(0, 300)}`);

    if (!creditsResp.ok) {
    throw new Error(`/backend-api/wham/rate-limit-reset-credits failed: ${creditsResp.status}`);
    }

    const data = JSON.parse(rawText);
    renderCredits(data);
    }

    const timer = setInterval(() => {
    if (document.body) {
    clearInterval(timer);

    ensureBox();

    main().catch(err => {
    log(`ERROR: ${err?.message || String(err)}`);
    console.error('[Codex Resets] failed:', err);
    });
    }
    }, 100);

    })();


  • Evil Frank 07-03 17:12
    2

    有好几次重置次数 一直不晓得什么时候过期 解决大麻烦了

* 帖子来源Linux.do
返回