Skip to content

Instantly share code, notes, and snippets.

@loadedsith
Last active September 30, 2025 16:44
Show Gist options
  • Select an option

  • Save loadedsith/1084b0b20b5178f1cae47fdc1301351d to your computer and use it in GitHub Desktop.

Select an option

Save loadedsith/1084b0b20b5178f1cae47fdc1301351d to your computer and use it in GitHub Desktop.
sora-utils.user.js
// ==UserScript==
// @name Sora Quota Checker
// @namespace https://sora.chatgpt.com/
// @version 0.2.0
// @description Check Sora request quota and next available time with a small MenuBar UI
// @author graham.p.heath@gmail.com
// @match https://sora.chatgpt.com/*
// @match *://sora.chatgpt.com/*
// @include https://sora.chatgpt.com/*
// @run-at document-end
// @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @grant unsafeWindow
// @grant GM_info
// @grant GM_getValue
// @grant GM.getValue
// @grant GM_setValue
// @grant GM.setValue
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @require https://gist.githubusercontent.com/loadedsith/0e26f07a2098344a5127f9eebc2fee4c/raw/menuBar.js
// ==/UserScript==
var SoraUtilsBundle = (() => {
// src/quota-utils.js
function extractTaskCreatedAtTimestamps(jsonRoot) {
const results = [];
const stack = [jsonRoot];
while (stack.length) {
const current = stack.pop();
if (Array.isArray(current)) {
for (const item of current) stack.push(item);
} else if (current && typeof current === "object") {
if (typeof current.created_at === "string" && typeof current.id === "string" && current.id.startsWith("task_")) {
results.push(current.created_at);
}
for (const value of Object.values(current)) stack.push(value);
}
}
return results;
}
function timestampsToSortedDates(timestamps) {
return timestamps.map((iso) => {
const d = new Date(iso);
return Number.isNaN(d.getTime()) ? null : d;
}).filter((d) => d !== null).sort((a, b) => a.getTime() - b.getTime());
}
function formatInTimeZone(date, timeZone, locale) {
const resolvedLocale = locale || (typeof navigator !== "undefined" ? navigator.languages && navigator.languages[0] || navigator.language || "en-US" : "en-US");
const formatter = new Intl.DateTimeFormat(resolvedLocale, {
timeZone,
weekday: "short",
year: "numeric",
month: "short",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: true,
timeZoneName: "short"
});
return formatter.format(date);
}
function computeQuotaSummary(dates, options = {}) {
const limit = typeof options.limit === "number" ? options.limit : 12;
const now = options.now instanceof Date ? options.now : /* @__PURE__ */ new Date();
const windowMs = 24 * 60 * 60 * 1e3;
const windowStartMs = now.getTime() - windowMs;
const requestsInLast24h = dates.filter((d) => d.getTime() >= windowStartMs).length;
const remaining = Math.max(0, limit - requestsInLast24h);
const canRequestNow = remaining > 0;
let nextAllowedAt = null;
if (!canRequestNow && dates.length >= limit) {
const nthFromEnd = dates[dates.length - limit];
nextAllowedAt = new Date(nthFromEnd.getTime() + windowMs);
}
return { requestsInLast24h, remaining, canRequestNow, nextAllowedAt };
}
// userscripts/sora-utils.user.entry.js
(function() {
const defaultTz = Intl.DateTimeFormat().resolvedOptions().timeZone || "America/Chicago";
let bridgeInstalled = false;
let cooldownUntilMs = 0;
let lastDates = [];
let lastRemaining = 0;
let tokenCaptured = false;
function findJwtLikeStringInObject(obj) {
const jwtRegex = /[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+/;
try {
const str = typeof obj === "string" ? obj : JSON.stringify(obj);
const m = str && str.match(jwtRegex);
return m ? m[0] : null;
} catch {
return null;
}
}
function detectTokenFromStorage() {
const candidates = [];
try {
for (let i = 0; i < localStorage.length; i += 1) {
const k = localStorage.key(i);
candidates.push(localStorage.getItem(k));
}
} catch {
}
try {
for (let i = 0; i < sessionStorage.length; i += 1) {
const k = sessionStorage.key(i);
candidates.push(sessionStorage.getItem(k));
}
} catch {
}
try {
if (window.__NEXT_DATA__) candidates.push(window.__NEXT_DATA__);
} catch {
}
for (const val of candidates) {
const jwt = findJwtLikeStringInObject(val);
if (jwt) return jwt;
}
return null;
}
function sanitizeJwtToken(input) {
if (!input) return null;
let str = String(input).trim();
const bearerMatch = str.match(/^Bearer\s+(.+)$/i);
if (bearerMatch) str = bearerMatch[1];
const ascii = str.replace(/[^\x20-\x7E]/g, "");
const jwtMatch = ascii.match(
/([A-Za-z0-9_-]{16,})\.([A-Za-z0-9_-]{16,})\.([A-Za-z0-9_-]{10,})/
);
return jwtMatch ? jwtMatch[0] : null;
}
async function checkQuota(limit = 12, tz = defaultTz, token) {
const url = `https://sora.chatgpt.com/backend/notif?limit=${encodeURIComponent(limit)}`;
const headers = {
Accept: "application/json, text/plain, */*",
"Cache-Control": "no-cache",
Pragma: "no-cache"
};
const resolvedToken = sanitizeJwtToken(token) || sanitizeJwtToken(detectTokenFromStorage());
if (resolvedToken) headers.Authorization = `Bearer ${resolvedToken}`;
const sameOrigin = (() => {
try {
return new URL(url).origin === window.location.origin;
} catch {
return false;
}
})();
const useGM = !sameOrigin && (typeof GM_xmlhttpRequest === "function" || typeof GM !== "undefined" && GM.xmlHttpRequest);
const data = await new Promise((resolve, reject) => {
if (!useGM) {
pageFetchJsonViaBridge(url, headers).then(resolve).catch(reject);
} else {
const gm = typeof GM_xmlhttpRequest === "function" ? GM_xmlhttpRequest : GM.xmlHttpRequest;
gm({
method: "GET",
url,
headers,
anonymous: false,
onload: (response) => {
try {
if (response.status < 200 || response.status >= 300) {
reject(
new Error(`Sora notif fetch failed: ${response.status} ${response.statusText}`)
);
return;
}
resolve(JSON.parse(response.responseText));
} catch (e) {
reject(e);
}
},
onerror: (err) => reject(new Error(`Sora notif request error: ${err?.error || "network"}`)),
ontimeout: () => reject(new Error("Sora notif request timeout")),
timeout: 15e3
});
}
});
const timestamps = extractTaskCreatedAtTimestamps(data);
const dates = timestampsToSortedDates(timestamps);
const { remaining, canRequestNow, nextAllowedAt, requestsInLast24h } = computeQuotaSummary(
dates,
{ limit }
);
const now = /* @__PURE__ */ new Date();
const within24h = dates.filter((d) => now.getTime() - d.getTime() < 24 * 60 * 60 * 1e3);
return {
summary: {
canRequestNow,
remaining,
requestsInLast24h,
nextAllowedAtUtc: nextAllowedAt ? nextAllowedAt.toISOString() : null,
nextAllowedAtLocal: nextAllowedAt ? formatInTimeZone(nextAllowedAt, tz) : null,
totalParsed: dates.length
},
dates,
within24h
};
}
const menuBar = new MenuBar({ id: "sora-quota", title: "Sora Quota" });
menuBar.addSection({ id: "quota", title: "Quota" });
menuBar.addSection({ id: "actions", title: "Actions" });
const textId = "quota-summary";
menuBar.addText({ id: textId, sectionId: "quota", content: 'Click "Check Now"\u2026' });
menuBar.addButton({
id: "check-now",
label: "Check Now",
sectionId: "actions",
handler: async () => {
performCheck(menuBar, textId).catch((e) => {
menuBar.addLog(`[Sora] Error: ${e.message}`);
alert("Sora Quota Error: " + e.message);
});
}
});
async function performCheck(menuBar2, textId2) {
const nowMs = Date.now();
if (cooldownUntilMs && nowMs < cooldownUntilMs) return;
cooldownUntilMs = nowMs + 1e4;
setButtonDisabled(true);
const savedToken = await GM.getValue("sora-token", "");
const result = await checkQuota(12, defaultTz, savedToken || void 0);
const { summary, within24h } = result;
lastDates = within24h;
lastRemaining = summary.remaining;
const snippet = buildSnippet(lastDates, lastRemaining);
requestAnimationFrame(() => {
renderQuotaUI(menuBar2, lastDates, lastRemaining, defaultTz, snippet);
const quotaContainer = menuBar2._menuBar?.querySelector(
"[data-section-id='quota'] .section-content"
);
if (quotaContainer) quotaContainer.style.flexDirection = "column-reverse";
const placeholder = quotaContainer?.querySelector(`[data-id='${textId2}']`);
if (placeholder) placeholder.innerHTML = snippet;
});
}
function setButtonDisabled(disabled) {
const btn = menuBar._menuBar?.querySelector("[data-id='check-now']");
if (btn) {
btn.disabled = !!disabled;
btn.style.opacity = disabled ? "0.6" : "";
btn.style.pointerEvents = disabled ? "none" : "";
}
}
function renderQuotaUI(menu, recentDates, remaining, tz, snippetText) {
const total = 12;
const used = Math.min(recentDates.length, total);
const graph = `${"\u{1F7E5}".repeat(used)}${"\u{1F7E9}".repeat(Math.max(0, total - used))}`.replace(/(.{8})/g, "$1 ").trim();
const container = menu._menuBar?.querySelector("[data-section-id='quota'] .section-content");
if (!container) {
menu.addLog("[Sora] quota container not found");
return;
}
container.style.flexDirection = "column-reverse";
let host = container.querySelector('[data-id="quota-table"]');
if (!host) {
host = document.createElement("div");
host.setAttribute("data-id", "quota-table");
container.appendChild(host);
}
host.innerHTML = "";
const graphEl = document.createElement("div");
graphEl.title = `Used: ${used} of ${total}, available ${remaining}`;
graphEl.textContent = `${graph}`;
graphEl.style.margin = "6px 0";
host.appendChild(graphEl);
}
function formatDuration(ms) {
const s = Math.max(0, Math.floor(ms / 1e3));
const h = Math.floor(s / 3600);
const m = Math.floor(s % 3600 / 60);
const sec = s % 60;
if (h > 0) return `${h}h ${m}m ${sec}s`;
if (m > 0) return `${m}m ${sec}s`;
return `${sec}s`;
}
function formatCountdownLocale(ms) {
const locale = typeof navigator !== "undefined" && (navigator.languages && navigator.languages[0] || navigator.language) || "en-US";
const s = Math.max(0, Math.floor(ms / 1e3));
const d = new Date(s * 1e3);
return d.toLocaleTimeString(locale, {
timeZone: "UTC",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}
function buildSnippet(recentDates, remaining) {
const total = 12;
const now = /* @__PURE__ */ new Date();
const untils = recentDates.map(
(d) => Math.max(0, 24 * 60 * 60 * 1e3 - (now.getTime() - d.getTime()))
);
const used = recentDates.length;
const nextMs = used === 0 ? 0 : Math.min(...untils);
const allMs = used === 0 ? 0 : Math.max(...untils);
const monoStyle = "font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;";
const nextPart = nextMs === 0 && remaining > 0 ? "Next slot available now" : `<span style="${monoStyle}">${formatCountdownLocale(nextMs)} until next slot</span>`;
const allPart = allMs === 0 ? `All ${total} available now` : `<span style="${monoStyle}">${formatCountdownLocale(allMs)} until all ${total} slots</span>`;
const cooldownMs = Math.max(0, cooldownUntilMs - Date.now());
const cd = cooldownMs > 0 ? `<br/><span style="${monoStyle}">${Math.ceil(cooldownMs / 1e3)}</span> left in cooldown` : "";
return `${nextPart}<br/>${allPart}${cd}`;
}
function installTokenSniffers(menu) {
const capture = async (maybeAuthHeader) => {
const token = sanitizeJwtToken(maybeAuthHeader);
if (token) {
await GM.setValue("sora-token", token);
debugEnabled && menu?.addLog?.(`Captured token ${maskToken(token)}`);
} else {
if (debugEnabled) {
const preview = String(maybeAuthHeader || "").slice(0, 24);
menu?.addLog?.(`Authorization seen but not JWT; got: "${preview}..."`);
}
}
};
try {
const originalFetch = window.fetch;
if (typeof originalFetch === "function") {
window.fetch = new Proxy(originalFetch, {
apply(target, thisArg, args) {
try {
const [input, init] = args;
let headers = null;
if (init && init.headers) headers = init.headers;
else if (input && input.headers) headers = input.headers;
let authVal = null;
if (headers) {
if (headers instanceof Headers) {
authVal = headers.get("authorization") || headers.get("Authorization");
} else if (Array.isArray(headers)) {
const found = headers.find(([k]) => k && k.toLowerCase() === "authorization");
authVal = found ? found[1] : null;
} else if (typeof headers === "object") {
authVal = headers.authorization || headers.Authorization || null;
}
}
if (authVal) capture(authVal);
} catch {
}
return Reflect.apply(target, thisArg, args);
}
});
}
} catch {
}
try {
const OriginalXHR = window.XMLHttpRequest;
if (OriginalXHR) {
const Proto = OriginalXHR.prototype;
const originalSetHeader = Proto.setRequestHeader;
Proto.setRequestHeader = function(name, value) {
try {
if (name && String(name).toLowerCase() === "authorization") capture(value);
} catch {
}
return originalSetHeader.apply(this, arguments);
};
}
} catch {
}
}
function maskToken(t) {
if (!t) return "";
const s = String(t);
return s.length <= 12 ? s : `${s.slice(0, 6)}...${s.slice(-6)}`;
}
installTokenSniffers(menuBar);
installPageBridge();
function installPageBridge() {
if (bridgeInstalled) return;
const script = document.createElement("script");
script.type = "text/javascript";
script.textContent = `(() => {
if (window.__soraQuotaBridgeInstalled) return; window.__soraQuotaBridgeInstalled = true;
// Lightweight token extraction from page context
const sendToken = (val) => {
try {
if (!val) return;
const s = String(val);
const bearer = s.match(/^Bearers+(.+)$/i);
const raw = bearer ? bearer[1] : s;
const m = raw.match(/([A-Za-z0-9_-]{16,}).([A-Za-z0-9_-]{16,}).([A-Za-z0-9_-]{10,})/);
if (m) window.postMessage({ type: 'sora-utils:capture-token', token: m[0] }, '*');
} catch {}
};
// Patch fetch
try {
const _fetch = window.fetch;
if (typeof _fetch === 'function') {
window.fetch = new Proxy(_fetch, {
apply(target, thisArg, args) {
try {
const [input, init] = args;
let headers = null;
if (init && init.headers) headers = init.headers;
else if (input && input.headers) headers = input.headers;
let auth = null;
if (headers) {
if (headers instanceof Headers) {
auth = headers.get('authorization') || headers.get('Authorization');
} else if (Array.isArray(headers)) {
const f = headers.find(([k]) => k && k.toLowerCase() === 'authorization');
auth = f ? f[1] : null;
} else if (typeof headers === 'object') {
auth = headers.authorization || headers.Authorization || null;
}
}
if (auth) sendToken(auth);
} catch {}
return Reflect.apply(target, thisArg, args);
}
});
}
} catch {}
// Patch Headers.set
try {
const _set = Headers.prototype.set;
Headers.prototype.set = function(name, value) {
try { if (String(name).toLowerCase() === 'authorization') sendToken(value); } catch {}
return _set.apply(this, arguments);
};
} catch {}
// Patch XHR
try {
const _xhrSet = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
try { if (String(name).toLowerCase() === 'authorization') sendToken(value); } catch {}
return _xhrSet.apply(this, arguments);
};
} catch {}
window.addEventListener('message', async (event) => {
try {
const data = event.data;
if (!data || data.type !== 'sora-utils:fetch') return;
const { id, url, headers } = data;
let resp, text, ok=false, status=0, statusText='';
try {
resp = await fetch(url, { method: 'GET', credentials: 'include', headers, referrer: location.href, referrerPolicy: 'strict-origin-when-cross-origin' });
text = await resp.text();
ok = resp.ok; status = resp.status; statusText = resp.statusText;
window.postMessage({ type: 'sora-utils:response', id, ok, status, statusText, body: text }, '*');
} catch (err) {
window.postMessage({ type: 'sora-utils:response', id, error: String(err) }, '*');
}
} catch (e) {}
});
})();`;
document.documentElement.appendChild(script);
script.remove();
bridgeInstalled = true;
}
function pageFetchJsonViaBridge(url, headers) {
return new Promise((resolve, reject) => {
const id = `sf-${Math.random().toString(36).slice(2)}`;
const handler = (event) => {
const data = event.data;
if (!data || data.type !== "sora-utils:response" || data.id !== id) return;
window.removeEventListener("message", handler);
if (data.error) return reject(new Error(data.error));
if (!data.ok) {
const snippet = (data.body || "").slice(0, 200);
return reject(
new Error(`Sora notif fetch failed: ${data.status} ${data.statusText} :: ${snippet}`)
);
}
try {
resolve(JSON.parse(data.body));
} catch (e) {
reject(e);
}
};
window.addEventListener("message", handler);
window.postMessage({ type: "sora-utils:fetch", id, url, headers }, "*");
});
}
const tokenListener = async (event) => {
if (tokenCaptured) return;
const d = event.data;
if (!d || d.type !== "sora-utils:capture-token" || !d.token) return;
const token = sanitizeJwtToken(d.token);
if (token) {
tokenCaptured = true;
await GM.setValue("sora-token", token);
performCheck(menuBar, textId).catch(() => {
});
window.removeEventListener("message", tokenListener);
}
};
window.addEventListener("message", tokenListener);
setInterval(() => {
if (lastDates && lastDates.length) {
const snippet = buildSnippet(lastDates, lastRemaining);
const quotaContainer = menuBar._menuBar?.querySelector(
"[data-section-id='quota'] .section-content"
);
const placeholder = quotaContainer?.querySelector(`[data-id='${textId}']`);
if (placeholder) placeholder.innerHTML = snippet;
}
const now = Date.now();
if (cooldownUntilMs && now >= cooldownUntilMs) {
cooldownUntilMs = 0;
setButtonDisabled(false);
}
}, 1e3);
})();
})();
// ==UserScript==
// @name Sora Quota Checker
// @namespace https://sora.chatgpt.com/
// @version 0.2.0
// @description Check Sora request quota and next available time with a small MenuBar UI
// @author graham.p.heath@gmail.com
// @match https://sora.chatgpt.com/*
// @match *://sora.chatgpt.com/*
// @include https://sora.chatgpt.com/*
// @run-at document-end
// @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @grant unsafeWindow
// @grant GM_info
// @grant GM_getValue
// @grant GM.getValue
// @grant GM_setValue
// @grant GM.setValue
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @require https://gist.githubusercontent.com/loadedsith/0e26f07a2098344a5127f9eebc2fee4c/raw/menuBar.js
// ==/UserScript==
var SoraUtilsBundle = (() => {
// src/quota-utils.js
function extractTaskCreatedAtTimestamps(jsonRoot) {
const results = [];
const stack = [jsonRoot];
while (stack.length) {
const current = stack.pop();
if (Array.isArray(current)) {
for (const item of current) stack.push(item);
} else if (current && typeof current === "object") {
if (typeof current.created_at === "string" && typeof current.id === "string" && current.id.startsWith("task_")) {
results.push(current.created_at);
}
for (const value of Object.values(current)) stack.push(value);
}
}
return results;
}
function timestampsToSortedDates(timestamps) {
return timestamps.map((iso) => {
const d = new Date(iso);
return Number.isNaN(d.getTime()) ? null : d;
}).filter((d) => d !== null).sort((a, b) => a.getTime() - b.getTime());
}
function formatInTimeZone(date, timeZone, locale) {
const resolvedLocale = locale || (typeof navigator !== "undefined" ? navigator.languages && navigator.languages[0] || navigator.language || "en-US" : "en-US");
const formatter = new Intl.DateTimeFormat(resolvedLocale, {
timeZone,
weekday: "short",
year: "numeric",
month: "short",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: true,
timeZoneName: "short"
});
return formatter.format(date);
}
function computeQuotaSummary(dates, options = {}) {
const limit = typeof options.limit === "number" ? options.limit : 12;
const now = options.now instanceof Date ? options.now : /* @__PURE__ */ new Date();
const windowMs = 24 * 60 * 60 * 1e3;
const windowStartMs = now.getTime() - windowMs;
const requestsInLast24h = dates.filter((d) => d.getTime() > now.getTime() - windowMs - 1).length;
const remaining = Math.max(0, limit - requestsInLast24h);
const canRequestNow = remaining > 0;
let nextAllowedAt = null;
if (!canRequestNow && dates.length >= limit) {
const nthFromEnd = dates[dates.length - limit];
nextAllowedAt = new Date(nthFromEnd.getTime() + windowMs);
}
return { requestsInLast24h, remaining, canRequestNow, nextAllowedAt };
}
// userscripts/sora-utils.user.entry.js
(function() {
const defaultTz = Intl.DateTimeFormat().resolvedOptions().timeZone || "America/Chicago";
let bridgeInstalled = false;
let cooldownUntilMs = 0;
let lastDates = [];
let lastRemaining = 0;
let tokenCaptured = false;
function findJwtLikeStringInObject(obj) {
const jwtRegex = /[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+/;
try {
const str = typeof obj === "string" ? obj : JSON.stringify(obj);
const m = str && str.match(jwtRegex);
return m ? m[0] : null;
} catch {
return null;
}
}
function detectTokenFromStorage() {
const candidates = [];
try {
for (let i = 0; i < localStorage.length; i += 1) {
const k = localStorage.key(i);
candidates.push(localStorage.getItem(k));
}
} catch {
}
try {
for (let i = 0; i < sessionStorage.length; i += 1) {
const k = sessionStorage.key(i);
candidates.push(sessionStorage.getItem(k));
}
} catch {
}
try {
if (window.__NEXT_DATA__) candidates.push(window.__NEXT_DATA__);
} catch {
}
for (const val of candidates) {
const jwt = findJwtLikeStringInObject(val);
if (jwt) return jwt;
}
return null;
}
function sanitizeJwtToken(input) {
if (!input) return null;
let str = String(input).trim();
const bearerMatch = str.match(/^Bearer\s+(.+)$/i);
if (bearerMatch) str = bearerMatch[1];
const ascii = str.replace(/[^\x20-\x7E]/g, "");
const jwtMatch = ascii.match(
/([A-Za-z0-9_-]{16,})\.([A-Za-z0-9_-]{16,})\.([A-Za-z0-9_-]{10,})/
);
return jwtMatch ? jwtMatch[0] : null;
}
async function checkQuota(limit = 12, tz = defaultTz, token) {
const FETCH_LIMIT = 15;
const url = `https://sora.chatgpt.com/backend/notif?limit=${encodeURIComponent(FETCH_LIMIT)}`;
const headers = {
Accept: "application/json, text/plain, */*",
"Cache-Control": "no-cache",
Pragma: "no-cache"
};
const resolvedToken = sanitizeJwtToken(token) || sanitizeJwtToken(detectTokenFromStorage());
if (resolvedToken) headers.Authorization = `Bearer ${resolvedToken}`;
const sameOrigin = (() => {
try {
return new URL(url).origin === window.location.origin;
} catch {
return false;
}
})();
const useGM = !sameOrigin && (typeof GM_xmlhttpRequest === "function" || typeof GM !== "undefined" && GM.xmlHttpRequest);
const data = await new Promise((resolve, reject) => {
if (!useGM) {
pageFetchJsonViaBridge(url, headers).then(resolve).catch(reject);
} else {
const gm = typeof GM_xmlhttpRequest === "function" ? GM_xmlhttpRequest : GM.xmlHttpRequest;
gm({
method: "GET",
url,
headers,
anonymous: false,
onload: (response) => {
try {
if (response.status < 200 || response.status >= 300) {
reject(
new Error(`Sora notif fetch failed: ${response.status} ${response.statusText}`)
);
return;
}
resolve(JSON.parse(response.responseText));
} catch (e) {
reject(e);
}
},
onerror: (err) => reject(new Error(`Sora notif request error: ${err?.error || "network"}`)),
ontimeout: () => reject(new Error("Sora notif request timeout")),
timeout: 15e3
});
}
});
const timestamps = extractTaskCreatedAtTimestamps(data);
const dates = timestampsToSortedDates(timestamps);
const { remaining, canRequestNow, nextAllowedAt, requestsInLast24h } = computeQuotaSummary(
dates,
{ limit }
);
const now = /* @__PURE__ */ new Date();
const within24h = dates.filter((d) => now.getTime() - d.getTime() <= 24 * 60 * 60 * 1e3);
return {
summary: {
canRequestNow,
remaining,
requestsInLast24h,
nextAllowedAtUtc: nextAllowedAt ? nextAllowedAt.toISOString() : null,
nextAllowedAtLocal: nextAllowedAt ? formatInTimeZone(nextAllowedAt, tz) : null,
totalParsed: dates.length
},
dates,
within24h
};
}
const menuBar = new MenuBar({ id: "sora-quota", title: "Sora Quota" });
initAnalytics();
track("sora_quota_page_load");
menuBar.addSection({ id: "quota", title: "Quota" });
menuBar.addSection({ id: "actions", title: "Actions" });
const textId = "quota-summary";
menuBar.addText({ id: textId, sectionId: "quota", content: 'Click "Check Now"\u2026' });
menuBar.addButton({
id: "check-now",
label: "Check Now",
sectionId: "actions",
handler: async () => {
track("sora_quota_check_click");
performCheck(menuBar, textId).catch((e) => {
menuBar.addLog(`[Sora] Error: ${e.message}`);
alert("Sora Quota Error: " + e.message);
});
}
});
async function performCheck(menuBar2, textId2) {
const nowMs = Date.now();
if (cooldownUntilMs && nowMs < cooldownUntilMs) return;
cooldownUntilMs = nowMs + 1e4;
setButtonDisabled(true);
const savedToken = await GM.getValue("sora-token", "");
const result = await checkQuota(12, defaultTz, savedToken || void 0);
const { summary, within24h } = result;
lastDates = within24h;
lastRemaining = summary.remaining;
const snippet = buildSnippet(lastDates, lastRemaining);
requestAnimationFrame(() => {
renderQuotaUI(menuBar2, lastDates, lastRemaining, defaultTz, snippet);
const quotaContainer = menuBar2._menuBar?.querySelector(
"[data-section-id='quota'] .section-content"
);
if (quotaContainer) quotaContainer.style.flexDirection = "column-reverse";
const placeholder = quotaContainer?.querySelector(`[data-id='${textId2}']`);
if (placeholder) placeholder.innerHTML = snippet;
});
}
function setButtonDisabled(disabled) {
const btn = menuBar._menuBar?.querySelector("[data-id='check-now']");
if (btn) {
btn.disabled = !!disabled;
btn.style.opacity = disabled ? "0.6" : "";
btn.style.pointerEvents = disabled ? "none" : "";
}
}
function renderQuotaUI(menu, recentDates, remaining, tz, snippetText) {
const total = 12;
const used = Math.min(recentDates.length, total);
const graph = `${"\u{1F7E5}".repeat(used)}${"\u{1F7E9}".repeat(Math.max(0, total - used))}`.replace(/(.{8})/g, "$1 ").trim();
const container = menu._menuBar?.querySelector("[data-section-id='quota'] .section-content");
if (!container) {
menu.addLog("[Sora] quota container not found");
return;
}
container.style.flexDirection = "column-reverse";
let host = container.querySelector('[data-id="quota-table"]');
if (!host) {
host = document.createElement("div");
host.setAttribute("data-id", "quota-table");
container.appendChild(host);
}
host.innerHTML = "";
const graphEl = document.createElement("div");
graphEl.title = `Used: ${used} of ${total}, available ${remaining}`;
graphEl.textContent = `${graph}`;
graphEl.style.margin = "6px 0";
host.appendChild(graphEl);
}
function formatDuration(ms) {
const s = Math.max(0, Math.floor(ms / 1e3));
const h = Math.floor(s / 3600);
const m = Math.floor(s % 3600 / 60);
const sec = s % 60;
if (h > 0) return `${h}h ${m}m ${sec}s`;
if (m > 0) return `${m}m ${sec}s`;
return `${sec}s`;
}
function formatCountdownLocale(ms) {
const locale = typeof navigator !== "undefined" && (navigator.languages && navigator.languages[0] || navigator.language) || "en-US";
const s = Math.max(0, Math.floor(ms / 1e3));
const d = new Date(s * 1e3);
return d.toLocaleTimeString(locale, {
timeZone: "UTC",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
}
function buildSnippet(recentDates, remaining) {
const total = 12;
const now = /* @__PURE__ */ new Date();
const untils = recentDates.map(
(d) => Math.max(0, 24 * 60 * 60 * 1e3 - (now.getTime() - d.getTime()))
);
const used = recentDates.length;
const nextMs = used === 0 ? 0 : Math.min(...untils);
const allMs = used === 0 ? 0 : Math.max(...untils);
const monoStyle = "font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;";
const nextPart = nextMs === 0 && remaining > 0 ? "Next slot available now" : `<span style="${monoStyle}">${formatCountdownLocale(nextMs)} until next slot</span>`;
const allPart = allMs === 0 ? `All ${total} available now` : `<span style="${monoStyle}">${formatCountdownLocale(allMs)} until all ${total} slots</span>`;
const cooldownMs = Math.max(0, cooldownUntilMs - Date.now());
const cd = cooldownMs > 0 ? `<br/><span style="${monoStyle}">${Math.ceil(cooldownMs / 1e3)}</span> left in cooldown` : "";
return `${nextPart}<br/>${allPart}${cd}`;
}
function installTokenSniffers(menu) {
const capture = async (maybeAuthHeader) => {
const token = sanitizeJwtToken(maybeAuthHeader);
if (token) {
await GM.setValue("sora-token", token);
debugEnabled && menu?.addLog?.(`Captured token ${maskToken(token)}`);
} else {
if (debugEnabled) {
const preview = String(maybeAuthHeader || "").slice(0, 24);
menu?.addLog?.(`Authorization seen but not JWT; got: "${preview}..."`);
}
}
};
try {
const originalFetch = window.fetch;
if (typeof originalFetch === "function") {
window.fetch = new Proxy(originalFetch, {
apply(target, thisArg, args) {
try {
const [input, init] = args;
let headers = null;
if (init && init.headers) headers = init.headers;
else if (input && input.headers) headers = input.headers;
let authVal = null;
if (headers) {
if (headers instanceof Headers) {
authVal = headers.get("authorization") || headers.get("Authorization");
} else if (Array.isArray(headers)) {
const found = headers.find(([k]) => k && k.toLowerCase() === "authorization");
authVal = found ? found[1] : null;
} else if (typeof headers === "object") {
authVal = headers.authorization || headers.Authorization || null;
}
}
if (authVal) capture(authVal);
} catch {
}
return Reflect.apply(target, thisArg, args);
}
});
}
} catch {
}
try {
const OriginalXHR = window.XMLHttpRequest;
if (OriginalXHR) {
const Proto = OriginalXHR.prototype;
const originalSetHeader = Proto.setRequestHeader;
Proto.setRequestHeader = function(name, value) {
try {
if (name && String(name).toLowerCase() === "authorization") capture(value);
} catch {
}
return originalSetHeader.apply(this, arguments);
};
}
} catch {
}
}
function maskToken(t) {
if (!t) return "";
const s = String(t);
return s.length <= 12 ? s : `${s.slice(0, 6)}...${s.slice(-6)}`;
}
installTokenSniffers(menuBar);
installPageBridge();
function installPageBridge() {
if (bridgeInstalled) return;
const script = document.createElement("script");
script.type = "text/javascript";
script.textContent = `(() => {
if (window.__soraQuotaBridgeInstalled) return; window.__soraQuotaBridgeInstalled = true;
// Lightweight token extraction from page context
const sendToken = (val) => {
try {
if (!val) return;
const s = String(val);
const bearer = s.match(/^Bearers+(.+)$/i);
const raw = bearer ? bearer[1] : s;
const m = raw.match(/([A-Za-z0-9_-]{16,}).([A-Za-z0-9_-]{16,}).([A-Za-z0-9_-]{10,})/);
if (m) window.postMessage({ type: 'sora-utils:capture-token', token: m[0] }, '*');
} catch {}
};
// Patch fetch
try {
const _fetch = window.fetch;
if (typeof _fetch === 'function') {
window.fetch = new Proxy(_fetch, {
apply(target, thisArg, args) {
try {
const [input, init] = args;
let headers = null;
if (init && init.headers) headers = init.headers;
else if (input && input.headers) headers = input.headers;
let auth = null;
if (headers) {
if (headers instanceof Headers) {
auth = headers.get('authorization') || headers.get('Authorization');
} else if (Array.isArray(headers)) {
const f = headers.find(([k]) => k && k.toLowerCase() === 'authorization');
auth = f ? f[1] : null;
} else if (typeof headers === 'object') {
auth = headers.authorization || headers.Authorization || null;
}
}
if (auth) sendToken(auth);
} catch {}
return Reflect.apply(target, thisArg, args);
}
});
}
} catch {}
// Patch Headers.set
try {
const _set = Headers.prototype.set;
Headers.prototype.set = function(name, value) {
try { if (String(name).toLowerCase() === 'authorization') sendToken(value); } catch {}
return _set.apply(this, arguments);
};
} catch {}
// Patch XHR
try {
const _xhrSet = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
try { if (String(name).toLowerCase() === 'authorization') sendToken(value); } catch {}
return _xhrSet.apply(this, arguments);
};
} catch {}
window.addEventListener('message', async (event) => {
try {
const data = event.data;
if (!data || data.type !== 'sora-utils:fetch') return;
const { id, url, headers } = data;
let resp, text, ok=false, status=0, statusText='';
try {
resp = await fetch(url, { method: 'GET', credentials: 'include', headers, referrer: location.href, referrerPolicy: 'strict-origin-when-cross-origin' });
text = await resp.text();
ok = resp.ok; status = resp.status; statusText = resp.statusText;
window.postMessage({ type: 'sora-utils:response', id, ok, status, statusText, body: text }, '*');
} catch (err) {
window.postMessage({ type: 'sora-utils:response', id, error: String(err) }, '*');
}
} catch (e) {}
});
})();`;
document.documentElement.appendChild(script);
script.remove();
bridgeInstalled = true;
}
function pageFetchJsonViaBridge(url, headers) {
return new Promise((resolve, reject) => {
const id = `sf-${Math.random().toString(36).slice(2)}`;
const handler = (event) => {
const data = event.data;
if (!data || data.type !== "sora-utils:response" || data.id !== id) return;
window.removeEventListener("message", handler);
if (data.error) return reject(new Error(data.error));
if (!data.ok) {
const snippet = (data.body || "").slice(0, 200);
return reject(
new Error(`Sora notif fetch failed: ${data.status} ${data.statusText} :: ${snippet}`)
);
}
try {
resolve(JSON.parse(data.body));
} catch (e) {
reject(e);
}
};
window.addEventListener("message", handler);
window.postMessage({ type: "sora-utils:fetch", id, url, headers }, "*");
});
}
const tokenListener = async (event) => {
if (tokenCaptured) return;
const d = event.data;
if (!d || d.type !== "sora-utils:capture-token" || !d.token) return;
const token = sanitizeJwtToken(d.token);
if (token) {
tokenCaptured = true;
await GM.setValue("sora-token", token);
performCheck(menuBar, textId).catch(() => {
});
window.removeEventListener("message", tokenListener);
}
};
window.addEventListener("message", tokenListener);
setInterval(() => {
if (lastDates && lastDates.length) {
const snippet = buildSnippet(lastDates, lastRemaining);
const quotaContainer = menuBar._menuBar?.querySelector(
"[data-section-id='quota'] .section-content"
);
const placeholder = quotaContainer?.querySelector(`[data-id='${textId}']`);
if (placeholder) placeholder.innerHTML = snippet;
}
const now = Date.now();
if (cooldownUntilMs && now >= cooldownUntilMs) {
cooldownUntilMs = 0;
setButtonDisabled(false);
}
}, 1e3);
function initAnalytics() {
const projectId = "170840";
const apiKey = "phc_2vR6m5PQyhS3ExKjYqoOlkII3zowFHhniOY8GcpTkU2";
if (!projectId || !apiKey) return;
window.__phq = [];
window.posthog = {
capture: (event, props) => {
try {
window.__phq.push({ event, properties: props || {} });
const payload = {
api_key: apiKey,
event,
properties: {
distinct_id: "sora-utils-userscript",
project_id: projectId,
...props
}
};
const blob = new Blob([JSON.stringify(payload)], { type: "application/json" });
if (navigator.sendBeacon) {
navigator.sendBeacon("https://app.posthog.com/capture/", blob);
} else {
fetch("https://app.posthog.com/capture/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
keepalive: true
}).catch(() => {
});
}
} catch {
}
}
};
}
function track(event, props) {
try {
if (!window.posthog || typeof window.posthog.capture !== "function") return;
if (typeof navigator !== "undefined" && navigator.doNotTrack === "1") return;
window.posthog.capture(event, {
ts: (/* @__PURE__ */ new Date()).toISOString(),
version: typeof GM_info !== "undefined" && GM_info?.script?.version || "dev"
});
} catch {
}
}
})();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment