ChatGPT Rate Limit - Frontend

A tool to know your ChatGPT Rate Limit.

  1. // ==UserScript==
  2. // @name ChatGPT Rate Limit - Frontend
  3. // @namespace http://terase.cn
  4. // @license MIT
  5. // @version 1.8
  6. // @description A tool to know your ChatGPT Rate Limit.
  7. // @author Terrasse
  8. // @match https://chatgpt.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
  10. // @sandbox RAW
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. window.model_status = {
  19. "o3": -1,
  20. "o4-mini-high": -1,
  21. "o4-mini": -1,
  22. "GPT-4.5": -1,
  23. }
  24. window.devarious = {
  25. "gpt-4-5": "GPT-4.5",
  26. }
  27.  
  28. function updateStatusText() {
  29. var status = window.model_status;
  30. var text = "";
  31. for (const model in status) {
  32. text += `${model}: ${status[model]}; `;
  33. }
  34. text = text.slice(0, -2);
  35.  
  36. var bar = document.getElementById("crl_bar");
  37. if (bar) {
  38. bar.innerText = text;
  39. }
  40. }
  41.  
  42. (function(fetch) {
  43. window.fetch = function(input, init) {
  44. var method = 'GET';
  45. var url = '';
  46. var payload = null;
  47.  
  48. if (typeof input === 'string') {
  49. url = input;
  50. } else if (input instanceof Request) {
  51. url = input.url;
  52. method = input.method || method;
  53. payload = input.body || null;
  54. } else {
  55. console.log(`Unexpected input of type ${typeof input}: ${input}`);
  56. }
  57.  
  58. if (init) {
  59. method = init.method || method;
  60. payload = init.body || payload;
  61. }
  62.  
  63. // console.log(`Request: ${method} ${url}`);
  64.  
  65. if (method.toUpperCase() === 'POST') {
  66. if (url.endsWith("/backend-api/conversation")) {
  67. // console.log("Conversation Request");
  68. payload = JSON.parse(payload);
  69. var model = payload.model;
  70. if (model in window.devarious) {
  71. model = window.devarious[model];
  72. }
  73.  
  74. window.postMessage({ model: model, type: "put" }, window.location.origin);
  75. }
  76. }
  77.  
  78. return fetch.apply(this, arguments);
  79. };
  80. })(window.fetch);
  81.  
  82. function receiveMessage(event) { // Accept: type="status"
  83. if (event.origin !== window.location.origin) return;
  84. if (event.data.type !== "status") return;
  85.  
  86. var msg = event.data;
  87. // console.log('MAIN_WORLD 收到消息:', msg);
  88. var status = window.model_status;
  89. if (msg.model in status) {
  90. status[msg.model] = msg.remain;
  91. updateStatusText();
  92. } else {
  93. console.log(`Unknown model from backend: ${msg.model}, msg: ${msg}, event: ${event}`, event);
  94. }
  95. }
  96.  
  97. window.addEventListener('message', receiveMessage, false);
  98.  
  99. function updateAll() {
  100. // console.log("Update All");
  101. for (const model in window.model_status) {
  102. window.postMessage({ model: model, type: "get" }, window.location.origin);
  103. }
  104. }
  105.  
  106. // Display & Refresh Button
  107. function htmlToNode(html) {
  108. const template = document.createElement('template');
  109. template.innerHTML = html;
  110. return template.content.firstChild;
  111. }
  112. function addFrontendItems() { // return true if freshly added
  113. if (document.getElementById("crl_bar")) return false;
  114. var avatar = document.querySelector('button[data-testid="profile-button"]');
  115. if (!avatar) return false;
  116. var avatarContainer = avatar.parentElement;
  117.  
  118. var displayBar = htmlToNode('<div id="crl_bar">loading...</div>')
  119. // var refreshButton = htmlToNode('<button onclick="updateAll();"><svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.651 7.65a7.131 7.131 0 0 0-12.68 3.15M18.001 4v4h-4m-7.652 8.35a7.13 7.13 0 0 0 12.68-3.15M6 20v-4h4"/></svg></button>')
  120. avatarContainer.prepend(displayBar);
  121. return true;
  122. }
  123. function tryAddFrontendItems() {
  124. if (addFrontendItems()) {
  125. // console.log("Frontend items added");
  126. updateAll();
  127. }
  128. }
  129.  
  130. setInterval(updateAll, 60000); // Refresh every 60s
  131. setInterval(tryAddFrontendItems, 200); // Make sure the bar is always there
  132.  
  133. })();