This commit is contained in:
+81
-16
@@ -9,6 +9,34 @@ const POLL_MS = 60_000;
|
||||
const TIMEOUT_MS = 5_000;
|
||||
const STORAGE_KEY = "chatgpt-quota";
|
||||
|
||||
function compactLimitIdentity(value: string) {
|
||||
return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
||||
}
|
||||
|
||||
function isSparkLimit(meteredFeature?: string, limitName?: string | null) {
|
||||
return (
|
||||
(meteredFeature !== undefined &&
|
||||
compactLimitIdentity(meteredFeature) === "codexbengalfox") ||
|
||||
(limitName != null && compactLimitIdentity(limitName) === "gpt53codexspark")
|
||||
);
|
||||
}
|
||||
|
||||
function isReserveLimit(meteredFeature?: string, limitName?: string | null) {
|
||||
const hasReserveToken = (value: string) => /(^|[^a-z0-9])reserve($|[^a-z0-9])/i.test(value);
|
||||
return (
|
||||
(meteredFeature !== undefined && hasReserveToken(meteredFeature)) ||
|
||||
(limitName != null && hasReserveToken(limitName))
|
||||
);
|
||||
}
|
||||
|
||||
function isExcludedLimit(meteredFeature?: string, limitName?: string | null) {
|
||||
return isSparkLimit(meteredFeature, limitName) || isReserveLimit(meteredFeature, limitName);
|
||||
}
|
||||
|
||||
function isExcludedWindow(window: Pick<QuotaWindow, "id" | "label">) {
|
||||
return isExcludedLimit(window.id.split(":", 1)[0], window.label);
|
||||
}
|
||||
|
||||
export interface QuotaWindow {
|
||||
id: string;
|
||||
label: string | null;
|
||||
@@ -56,6 +84,24 @@ const storedState = z.object({
|
||||
});
|
||||
type StoredState = z.infer<typeof storedState>;
|
||||
|
||||
function withoutExcludedState(state: StoredState): StoredState {
|
||||
const windows = state.latest?.windows.filter((window) => !isExcludedWindow(window));
|
||||
return {
|
||||
...state,
|
||||
latest: state.latest && windows?.length ? { ...state.latest, windows } : undefined,
|
||||
sessions: Object.fromEntries(
|
||||
Object.entries(state.sessions).map(([session, tracked]) => [
|
||||
session,
|
||||
Object.fromEntries(
|
||||
Object.entries(tracked).filter(
|
||||
([id]) => !isExcludedLimit(id.split(":", 1)[0], undefined),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
type QuotaContext = Pick<Plugin.Context, "integration" | "session" | "storage">;
|
||||
type QuotaFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
||||
|
||||
@@ -64,6 +110,10 @@ const quotaWindow = z.object({
|
||||
used_percent: z.number().finite(),
|
||||
reset_at: z.number().finite(),
|
||||
});
|
||||
const additionalRateLimitIdentity = z.object({
|
||||
limit_name: z.string().optional(),
|
||||
metered_feature: z.string().optional(),
|
||||
});
|
||||
const quotaPayload = z.object({
|
||||
plan_type: z.string(),
|
||||
account_id: z.string().optional(),
|
||||
@@ -74,21 +124,33 @@ const quotaPayload = z.object({
|
||||
secondary_window: quotaWindow.nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
additional_rate_limits: z
|
||||
.array(
|
||||
z.object({
|
||||
limit_name: z.string(),
|
||||
metered_feature: z.string(),
|
||||
rate_limit: z
|
||||
.object({
|
||||
allowed: z.boolean().optional(),
|
||||
primary_window: quotaWindow.nullish(),
|
||||
secondary_window: quotaWindow.nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
}),
|
||||
)
|
||||
.nullish(),
|
||||
additional_rate_limits: z.preprocess(
|
||||
(value) =>
|
||||
Array.isArray(value)
|
||||
? value.filter((item) => {
|
||||
const identity = additionalRateLimitIdentity.safeParse(item);
|
||||
return (
|
||||
!identity.success ||
|
||||
!isExcludedLimit(identity.data.metered_feature, identity.data.limit_name)
|
||||
);
|
||||
})
|
||||
: value,
|
||||
z
|
||||
.array(
|
||||
z.object({
|
||||
limit_name: z.string(),
|
||||
metered_feature: z.string(),
|
||||
rate_limit: z
|
||||
.object({
|
||||
allowed: z.boolean().optional(),
|
||||
primary_window: quotaWindow.nullish(),
|
||||
secondary_window: quotaWindow.nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
}),
|
||||
)
|
||||
.nullish(),
|
||||
),
|
||||
});
|
||||
|
||||
function normalizeWindow(
|
||||
@@ -218,7 +280,10 @@ export class QuotaTracker {
|
||||
if (generation !== this.generation) return;
|
||||
const stored = parsed.success ? parsed.data : undefined;
|
||||
if (generation !== this.generation) return;
|
||||
this.state = stored?.account === account ? stored : { account, sessions: {} };
|
||||
this.state =
|
||||
stored?.account === account
|
||||
? withoutExcludedState(stored)
|
||||
: { account, sessions: {} };
|
||||
}
|
||||
if (generation !== this.generation) return;
|
||||
if (!this.timer) this.timer = setInterval(() => void this.refresh(), this.pollMs);
|
||||
|
||||
Reference in New Issue
Block a user