Grok Quota Display

Displays rate limits on grok.com

  1. // ==UserScript==
  2. // @name Grok Quota Display
  3. // @namespace nisc
  4. // @version 2025.06.08-A
  5. // @description Displays rate limits on grok.com
  6. // @homepageURL https://github.com/nisc/grok-userscripts/
  7. // @author nisc
  8. // @match https://grok.com/*
  9. // @icon https://grok.com/images/favicon-light.png
  10. // @run-at document-end
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. /**
  18. * Configuration object containing all constants used in the script
  19. * Organized into logical sections for different aspects of the application
  20. */
  21. const CONFIG = {
  22. MODEL: {
  23. name: 'grok-3'
  24. },
  25. REQUEST_TYPES: {
  26. DEFAULT: 'Default',
  27. REASONING: 'Reason',
  28. DEEPSEARCH: 'Deep',
  29. DEEPERSEARCH: 'Deeper'
  30. },
  31. API: {
  32. endpoint: 'https://grok.com/rest/rate-limits',
  33. headers: {
  34. 'Content-Type': 'application/json',
  35. 'Accept-Language': 'en-US,en;q=0.9',
  36. 'Accept': '*/*',
  37. 'Origin': 'https://grok.com',
  38. 'Sec-Fetch-Site': 'same-origin',
  39. 'Sec-Fetch-Mode': 'cors',
  40. 'Sec-Fetch-Dest': 'empty',
  41. 'Referer': 'https://grok.com/',
  42. 'Accept-Encoding': 'gzip, deflate, br',
  43. 'Priority': 'u=1, i'
  44. }
  45. },
  46. UI: {
  47. styles: `
  48. .grok-rate-limit-wrapper {
  49. display: flex;
  50. flex-direction: column;
  51. gap: 0.25rem;
  52. padding: 0.5rem;
  53. position: fixed;
  54. bottom: 1rem;
  55. right: 1rem;
  56. z-index: 0.25rem;
  57. }
  58. .grok-rate-limit-wrapper .grok-menu {
  59. display: flex;
  60. flex-direction: column;
  61. gap: 0.25rem;
  62. }
  63. .grok-rate-limit-wrapper .grok-column {
  64. display: flex;
  65. flex-direction: column;
  66. gap: 0.25rem;
  67. }
  68. .grok-rate-limit-wrapper .grok-rate-limit {
  69. font-size: 0.75rem;
  70. color: inherit;
  71. font-family: inherit;
  72. line-height: inherit;
  73. }
  74. `,
  75. refreshInterval: 30000 // in milliseconds
  76. },
  77. TIME: {
  78. SECONDS_PER_HOUR: 3600,
  79. SECONDS_PER_DAY: 86400
  80. }
  81. };
  82.  
  83. /**
  84. * Utility functions for common operations
  85. * - ID generation for API requests
  86. * - Element ID formatting
  87. * - Time window and value formatting
  88. */
  89. const utils = {
  90. // Generates a random ID for API request tracking
  91. generateId: () => Math.random().toString(16).slice(2),
  92.  
  93. // Creates consistent element IDs for rate limit displays
  94. getLimitElementId: type => `rate_limit_${type.toLowerCase()}`,
  95.  
  96. // Formats time windows into days or hours with appropriate units
  97. formatTimeWindow: seconds => {
  98. if (seconds >= CONFIG.TIME.SECONDS_PER_DAY) {
  99. const value = seconds / CONFIG.TIME.SECONDS_PER_DAY;
  100. return { value, unit: 'd' };
  101. }
  102. const value = seconds / CONFIG.TIME.SECONDS_PER_HOUR;
  103. return { value, unit: 'h' };
  104. },
  105.  
  106. // Formats numeric values, rounding integers and fixing decimals to 1 place
  107. formatValue: value => Number.isInteger(value) ? Math.round(value) : value.toFixed(1)
  108. };
  109.  
  110. /**
  111. * UI-related functions for creating and updating the display
  112. * Handles all DOM manipulation and styling
  113. */
  114. const ui = {
  115. // Injects custom styles into the document
  116. createStyles: () => {
  117. const style = document.createElement('style');
  118. style.textContent = CONFIG.UI.styles;
  119. document.head.appendChild(style);
  120. },
  121.  
  122. // Creates the rate limit display menu structure
  123. createMenu: () => {
  124. const wrapper = document.createElement('div');
  125. wrapper.className = 'grok-rate-limit-wrapper';
  126.  
  127. const menu = document.createElement('div');
  128. menu.id = 'grok-switcher-menu';
  129. menu.className = 'grok-menu';
  130.  
  131. const column = document.createElement('div');
  132. column.className = 'grok-column';
  133.  
  134. // Create display elements for each request type
  135. Object.entries(CONFIG.REQUEST_TYPES).forEach(([type, displayName]) => {
  136. const div = document.createElement('div');
  137. div.id = utils.getLimitElementId(type);
  138. div.className = 'grok-rate-limit';
  139. div.textContent = `${displayName}: N/A`;
  140. column.appendChild(div);
  141. });
  142.  
  143. menu.appendChild(column);
  144. wrapper.appendChild(menu);
  145. return wrapper;
  146. },
  147.  
  148. // Updates the display with new rate limit information
  149. updateRateLimits: limits => {
  150. Object.entries(CONFIG.REQUEST_TYPES).forEach(([type, displayName]) => {
  151. const elem = document.getElementById(utils.getLimitElementId(type));
  152. const limit = limits?.[type];
  153.  
  154. if (limit?.windowSizeSeconds) {
  155. const { value, unit } = utils.formatTimeWindow(limit.windowSizeSeconds);
  156. const formattedValue = utils.formatValue(value);
  157. const display =
  158. `${displayName}: <b>${limit.remainingQueries}</b>/${limit.totalQueries} ` +
  159. `(${formattedValue}${unit})`;
  160. elem.innerHTML = display;
  161. } else {
  162. elem.textContent = `${displayName}: N/A`;
  163. }
  164. });
  165. }
  166. };
  167.  
  168. /**
  169. * API-related functions for fetching rate limits
  170. * Handles all server communication and error handling
  171. */
  172. const api = {
  173. // Fetches rate limits for all request types in parallel
  174. async fetchRateLimits() {
  175. try {
  176. const limits = {};
  177. // Create an array of promises for parallel execution
  178. const requests = Object.keys(CONFIG.REQUEST_TYPES).map(async type => {
  179. const headers = {
  180. ...CONFIG.API.headers,
  181. 'User-Agent': navigator.userAgent,
  182. 'X-Xai-Request-Id': utils.generateId()
  183. };
  184.  
  185. const response = await fetch(CONFIG.API.endpoint, {
  186. method: 'POST',
  187. headers,
  188. body: JSON.stringify({
  189. requestKind: type,
  190. modelName: CONFIG.MODEL.name
  191. })
  192. });
  193.  
  194. if (!response.ok) {
  195. throw new Error(`Failed to fetch ${CONFIG.MODEL.name} ${type} rate limits`);
  196. }
  197. limits[type] = await response.json();
  198. });
  199.  
  200. // Wait for all requests to complete
  201. await Promise.all(requests);
  202. ui.updateRateLimits(limits);
  203. } catch (error) {
  204. ui.updateRateLimits(null);
  205. console.error('Failed to fetch rate limits. Please try again later.');
  206. }
  207. }
  208. };
  209.  
  210. /**
  211. * Initializes the application:
  212. * 1. Creates and injects styles
  213. * 2. Creates and adds the display menu
  214. * 3. Fetches initial rate limits
  215. * 4. Sets up periodic updates
  216. */
  217. const init = () => {
  218. ui.createStyles();
  219. document.body.appendChild(ui.createMenu());
  220. api.fetchRateLimits();
  221. setInterval(api.fetchRateLimits, CONFIG.UI.refreshInterval);
  222. };
  223.  
  224. // Initialize when DOM is ready
  225. if (document.readyState === 'complete' || document.readyState === 'interactive') {
  226. init();
  227. } else {
  228. document.addEventListener('DOMContentLoaded', init);
  229. }
  230. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址