update to ignore 5h quota
This commit is contained in:
parent
4680b3ac44
commit
2aac95128f
2 changed files with 82 additions and 42 deletions
48
README.md
48
README.md
|
|
@ -1,13 +1,13 @@
|
|||
# Codex Reset Alerts 🚀
|
||||
|
||||
Petit watcher Node.js qui surveille ton quota **OpenAI Codex** et envoie des notifications **ntfy** au lancement, puis dès que ton quota est de nouveau disponible.
|
||||
Petit watcher Node.js qui surveille ton quota global **OpenAI Codex** et envoie des notifications **ntfy** au lancement, puis dès que ce quota global est de nouveau disponible.
|
||||
|
||||
L'idée est simple : lorsque Codex indique que ton quota est épuisé, tu lances le watcher et tu peux passer à autre chose. Il surveille le quota en arrière-plan, t'envoie une notification lors du reset réel, puis s'arrête automatiquement.
|
||||
L'idée est simple : lorsque Codex indique que ton quota global est épuisé, tu lances le watcher et tu peux passer à autre chose. Il surveille le quota global en arrière-plan, t'envoie une notification lors du reset réel, puis s'arrête automatiquement.
|
||||
|
||||
## Fonctionnement
|
||||
|
||||
```text
|
||||
Quota Codex épuisé 🔴
|
||||
Quota global Codex épuisé 🔴
|
||||
│
|
||||
▼
|
||||
npm start
|
||||
|
|
@ -20,10 +20,10 @@ Codex App Server
|
|||
└── account/rateLimits/updated
|
||||
│
|
||||
▼
|
||||
Surveillance du quota
|
||||
Surveillance du quota global
|
||||
│
|
||||
▼
|
||||
Quota disponible 🟢
|
||||
Quota global disponible 🟢
|
||||
│
|
||||
▼
|
||||
ntfy
|
||||
|
|
@ -40,9 +40,11 @@ Le watcher utilise deux mécanismes :
|
|||
* les événements `account/rateLimits/updated` envoyés par Codex ;
|
||||
* un polling périodique comme filet de sécurité.
|
||||
|
||||
Le timestamp `resetsAt` fourni par Codex est également surveillé, mais il n'est **pas considéré comme une garantie** : le quota peut être réinitialisé avant la date annoncée.
|
||||
Codex expose deux fenêtres de quota : `primary` sur 300 minutes, soit 5h, et `secondary` sur 10080 minutes, soit 7 jours. Le watcher ignore la fenêtre 5h et utilise uniquement la fenêtre globale.
|
||||
|
||||
Une notification de démarrage est envoyée dès le lancement. La notification de reset n'est envoyée que lorsqu'un passage réel de **quota épuisé → quota disponible** est détecté.
|
||||
Le timestamp `resetsAt` global fourni par Codex est également surveillé, mais il n'est **pas considéré comme une garantie** : le quota global peut être réinitialisé avant la date annoncée.
|
||||
|
||||
Une notification de démarrage est envoyée dès le lancement. La notification de reset n'est envoyée que lorsqu'un passage réel de **quota global épuisé → quota global disponible** est détecté.
|
||||
|
||||
## Prérequis
|
||||
|
||||
|
|
@ -131,7 +133,7 @@ Les événements envoyés directement par Codex permettent généralement de dé
|
|||
|
||||
## Utilisation
|
||||
|
||||
Lorsque ton quota Codex est épuisé :
|
||||
Lorsque ton quota global Codex est épuisé :
|
||||
|
||||
```bash
|
||||
npm start
|
||||
|
|
@ -145,23 +147,24 @@ Si le quota est effectivement épuisé :
|
|||
✅ Connecté.
|
||||
|
||||
🔎 Source : démarrage
|
||||
📊 Utilisé : 100%
|
||||
🔴 État : quota épuisé
|
||||
🕒 Reset annoncé : jeudi 20 août 2026 à 13:58:03
|
||||
⏳ Temps théorique : 3j 1h 55min
|
||||
📊 Global utilisé : 100%
|
||||
📊 Fenêtre 300min ignorée : 100%
|
||||
🔴 État : quota global épuisé
|
||||
🕒 Reset global annoncé : jeudi 20 août 2026 à 13:58:03
|
||||
⏳ Temps théorique global : 3j 1h 55min
|
||||
|
||||
🔴 Quota épuisé.
|
||||
👀 Surveillance du reset activée.
|
||||
🔴 Quota global épuisé.
|
||||
👀 Surveillance du reset global activée.
|
||||
|
||||
👀 Polling toutes les 60s.
|
||||
```
|
||||
|
||||
Tu peux alors laisser le watcher tourner.
|
||||
|
||||
Lorsqu'un reset est détecté :
|
||||
Lorsqu'un reset global est détecté :
|
||||
|
||||
```text
|
||||
🎉 RESET RÉEL DÉTECTÉ !
|
||||
🎉 RESET GLOBAL RÉEL DÉTECTÉ !
|
||||
📉 100% → 0%
|
||||
|
||||
📱 Notification ntfy envoyée.
|
||||
|
|
@ -172,7 +175,7 @@ Lorsqu'un reset est détecté :
|
|||
Tu recevras en parallèle une notification sur ton téléphone :
|
||||
|
||||
```text
|
||||
🚀 Ton quota Codex est de nouveau disponible !
|
||||
🚀 Ton quota global Codex est de nouveau disponible !
|
||||
|
||||
Avant : 100% utilisé
|
||||
Maintenant : 0% utilisé
|
||||
|
|
@ -180,7 +183,7 @@ Maintenant : 0% utilisé
|
|||
|
||||
Le watcher s'arrête ensuite automatiquement.
|
||||
|
||||
## Si le quota n'est pas épuisé
|
||||
## Si le quota global n'est pas épuisé
|
||||
|
||||
Tu peux lancer le programme sans risque :
|
||||
|
||||
|
|
@ -188,13 +191,14 @@ Tu peux lancer le programme sans risque :
|
|||
npm start
|
||||
```
|
||||
|
||||
S'il reste du quota, le watcher ne reste pas inutilement actif :
|
||||
S'il reste du quota global, le watcher ne reste pas inutilement actif :
|
||||
|
||||
```text
|
||||
📊 Utilisé : 42%
|
||||
🟢 État : quota disponible
|
||||
📊 Global utilisé : 42%
|
||||
📊 Fenêtre 300min ignorée : 100%
|
||||
🟢 État : quota global disponible
|
||||
|
||||
✅ Ton quota Codex est encore disponible.
|
||||
✅ Ton quota global Codex est encore disponible.
|
||||
👋 Rien à surveiller, arrêt.
|
||||
```
|
||||
|
||||
|
|
|
|||
76
index.js
76
index.js
|
|
@ -75,17 +75,41 @@ function getCodexLimit(result) {
|
|||
);
|
||||
}
|
||||
|
||||
function getState(limit) {
|
||||
const usedPercent = Number(limit?.primary?.usedPercent ?? 0);
|
||||
function getGlobalLimitWindow(limit) {
|
||||
const windows = [
|
||||
limit?.primary,
|
||||
limit?.secondary,
|
||||
].filter(Boolean);
|
||||
|
||||
const blocked =
|
||||
limit?.rateLimitReachedType === "rate_limit_reached" ||
|
||||
usedPercent >= 100;
|
||||
return (
|
||||
windows.find(
|
||||
(window) => Number(window?.windowDurationMins) === 10080
|
||||
) ??
|
||||
windows.sort(
|
||||
(a, b) =>
|
||||
Number(b?.windowDurationMins ?? 0) -
|
||||
Number(a?.windowDurationMins ?? 0)
|
||||
)[0] ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function getState(limit) {
|
||||
const globalWindow = getGlobalLimitWindow(limit);
|
||||
const usedPercent = Number(globalWindow?.usedPercent ?? 0);
|
||||
|
||||
const blocked = usedPercent >= 100;
|
||||
|
||||
return {
|
||||
usedPercent,
|
||||
blocked,
|
||||
resetsAt: limit?.primary?.resetsAt ?? null,
|
||||
resetsAt: globalWindow?.resetsAt ?? null,
|
||||
windowDurationMins:
|
||||
globalWindow?.windowDurationMins ?? null,
|
||||
primaryUsedPercent:
|
||||
limit?.primary?.usedPercent ?? null,
|
||||
primaryWindowDurationMins:
|
||||
limit?.primary?.windowDurationMins ?? null,
|
||||
reachedType: limit?.rateLimitReachedType ?? null,
|
||||
};
|
||||
}
|
||||
|
|
@ -149,7 +173,7 @@ async function sendStartupNotification() {
|
|||
priority: "default",
|
||||
body:
|
||||
`👀 Codex Reset Alerts vient de démarrer.\n\n` +
|
||||
`Surveillance du quota Codex en cours.`,
|
||||
`Surveillance du quota global Codex en cours.`,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +183,7 @@ async function sendResetNotification(oldState, newState) {
|
|||
tags: "robot,white_check_mark",
|
||||
priority: "high",
|
||||
body:
|
||||
`🚀 Ton quota Codex est de nouveau disponible !\n\n` +
|
||||
`🚀 Ton quota global Codex est de nouveau disponible !\n\n` +
|
||||
`Avant : ${oldState.usedPercent}% utilisé\n` +
|
||||
`Maintenant : ${newState.usedPercent}% utilisé`,
|
||||
});
|
||||
|
|
@ -226,16 +250,28 @@ async function processLimit(limit, source) {
|
|||
|
||||
console.log("");
|
||||
console.log(`🔎 Source : ${source}`);
|
||||
console.log(`📊 Utilisé : ${state.usedPercent}%`);
|
||||
console.log(`📊 Global utilisé : ${state.usedPercent}%`);
|
||||
|
||||
if (
|
||||
state.primaryUsedPercent !== null &&
|
||||
state.primaryWindowDurationMins !== null &&
|
||||
state.primaryWindowDurationMins !== state.windowDurationMins
|
||||
) {
|
||||
console.log(
|
||||
`📊 Fenêtre ${state.primaryWindowDurationMins}min ignorée : ` +
|
||||
`${state.primaryUsedPercent}%`
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
state.blocked
|
||||
? "🔴 État : quota épuisé"
|
||||
: "🟢 État : quota disponible"
|
||||
? "🔴 État : quota global épuisé"
|
||||
: "🟢 État : quota global disponible"
|
||||
);
|
||||
|
||||
if (state.resetsAt) {
|
||||
console.log(
|
||||
`🕒 Reset annoncé : ${formatDate(state.resetsAt)}`
|
||||
`🕒 Reset global annoncé : ${formatDate(state.resetsAt)}`
|
||||
);
|
||||
|
||||
const remaining =
|
||||
|
|
@ -244,7 +280,7 @@ async function processLimit(limit, source) {
|
|||
|
||||
if (remaining > 0) {
|
||||
console.log(
|
||||
`⏳ Temps théorique : ${formatDuration(remaining)}`
|
||||
`⏳ Temps théorique global : ${formatDuration(remaining)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -255,7 +291,7 @@ async function processLimit(limit, source) {
|
|||
|
||||
if (!state.blocked) {
|
||||
console.log("");
|
||||
console.log("✅ Ton quota Codex est encore disponible.");
|
||||
console.log("✅ Ton quota global Codex est encore disponible.");
|
||||
console.log("👋 Rien à surveiller, arrêt.");
|
||||
|
||||
setTimeout(() => stop(0), 100);
|
||||
|
|
@ -263,8 +299,8 @@ async function processLimit(limit, source) {
|
|||
}
|
||||
|
||||
console.log("");
|
||||
console.log("🔴 Quota épuisé.");
|
||||
console.log("👀 Surveillance du reset activée.");
|
||||
console.log("🔴 Quota global épuisé.");
|
||||
console.log("👀 Surveillance du reset global activée.");
|
||||
|
||||
scheduleTheoreticalResetCheck(state);
|
||||
|
||||
|
|
@ -273,13 +309,13 @@ async function processLimit(limit, source) {
|
|||
|
||||
// La transition importante :
|
||||
//
|
||||
// bloqué -> disponible
|
||||
// quota global épuisé -> quota global disponible
|
||||
if (
|
||||
previousState.blocked === true &&
|
||||
state.blocked === false
|
||||
) {
|
||||
console.log("");
|
||||
console.log("🎉 RESET RÉEL DÉTECTÉ !");
|
||||
console.log("🎉 RESET GLOBAL RÉEL DÉTECTÉ !");
|
||||
console.log(
|
||||
`📉 ${previousState.usedPercent}% → ${state.usedPercent}%`
|
||||
);
|
||||
|
|
@ -310,7 +346,7 @@ async function processLimit(limit, source) {
|
|||
|
||||
if (state.usedPercent < previousState.usedPercent) {
|
||||
console.log(
|
||||
`📉 Utilisation en baisse : ` +
|
||||
`📉 Utilisation globale en baisse : ` +
|
||||
`${previousState.usedPercent}% → ${state.usedPercent}%`
|
||||
);
|
||||
}
|
||||
|
|
@ -320,7 +356,7 @@ async function processLimit(limit, source) {
|
|||
state.resetsAt &&
|
||||
previousState.resetsAt !== state.resetsAt
|
||||
) {
|
||||
console.log("🔄 Date de reset modifiée par Codex.");
|
||||
console.log("🔄 Date de reset global modifiée par Codex.");
|
||||
console.log(
|
||||
` Avant : ${formatDate(previousState.resetsAt)}`
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue