Grok Quota Display

Displays rate limits on grok.com

// ==UserScript==
// @name         Grok Quota Display
// @namespace    nisc
// @version      2025.08.21-A
// @description  Displays rate limits on grok.com
// @homepageURL  https://github.com/nisc/grok-userscripts/
// @author       nisc
// @match        https://grok.com/*
// @icon         https://grok.com/images/favicon-light.png
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    /**
     * Configuration object containing all constants used in the script
     * Organized into logical sections for different aspects of the application
     */
    const CONFIG = {
        MODEL: {
            modelName: 'grok-4',
            requestKind: 'DEFAULT'
        },
        API: {
            endpoint: 'https://grok.com/rest/rate-limits',
            headers: {
                'Content-Type': 'application/json',
                'Accept-Language': 'en-US,en;q=0.9',
                'Accept': '*/*',
                'Origin': 'https://grok.com',
                'Sec-Fetch-Site': 'same-origin',
                'Sec-Fetch-Mode': 'cors',
                'Sec-Fetch-Dest': 'empty',
                'Referer': 'https://grok.com/',
                'Accept-Encoding': 'gzip, deflate, br',
                'Priority': 'u=1, i'
            }
        },
        UI: {
            styles: `
                .grok-rate-limit-wrapper {
                    display: flex;
                    flex-direction: column;
                    gap: 0.25rem;
                    padding: 0.5rem;
                    position: fixed;
                    bottom: 1rem;
                    right: 1rem;
                    z-index: 1000;
                }
                .grok-rate-limit-wrapper .grok-menu {
                    display: flex;
                    flex-direction: column;
                    gap: 0.25rem;
                }
                .grok-rate-limit-wrapper .grok-column {
                    display: flex;
                    flex-direction: column;
                    gap: 0.25rem;
                }
                .grok-rate-limit-wrapper .grok-rate-limit {
                    font-size: 0.75rem;
                    color: inherit;
                    font-family: inherit;
                    line-height: inherit;
                }
            `,
            refreshInterval: 10000 // in milliseconds
        },
        TIME: {
            SECONDS_PER_HOUR: 3600,
            SECONDS_PER_DAY: 86400
        }
    };

    /**
     * Utility functions for common operations
     * - ID generation for API requests
     * - Time window and value formatting
     */
    const utils = {
        // Generates a random ID for API request tracking
        generateId: () => Math.random().toString(16).slice(2),

        // Formats time windows into days or hours with appropriate units
        formatTimeWindow: seconds => {
            if (seconds >= CONFIG.TIME.SECONDS_PER_DAY) {
                const value = seconds / CONFIG.TIME.SECONDS_PER_DAY;
                return { value, unit: 'd' };
            }
            const value = seconds / CONFIG.TIME.SECONDS_PER_HOUR;
            return { value, unit: 'h' };
        },

        // Formats numeric values, rounding integers and fixing decimals to 1 place
        formatValue: value => Number.isInteger(value) ? Math.round(value) : value.toFixed(1)
    };

    /**
     * UI-related functions for creating and updating the display
     * Handles all DOM manipulation and styling
     */
    const ui = {
        // Injects custom styles into the document
        createStyles: () => {
            const style = document.createElement('style');
            style.textContent = CONFIG.UI.styles;
            document.head.appendChild(style);
        },

        // Creates the rate limit display menu structure
        createMenu: () => {
            const wrapper = document.createElement('div');
            wrapper.className = 'grok-rate-limit-wrapper';

            const menu = document.createElement('div');
            menu.id = 'grok-switcher-menu';
            menu.className = 'grok-menu';

            const column = document.createElement('div');
            column.className = 'grok-column';

            const div = document.createElement('div');
            div.id = 'rate_limit_tokens';
            div.className = 'grok-rate-limit';
            div.textContent = 'Tokens: N/A';
            column.appendChild(div);

            menu.appendChild(column);
            wrapper.appendChild(menu);
            return wrapper;
        },

        // Updates the display with new rate limit information
        updateRateLimits: limits => {
            const elem = document.getElementById('rate_limit_tokens');

            if (!elem) {
                return;
            }

            const windowSeconds = limits?.windowSizeSeconds;
            const remainingTokens = limits?.remainingTokens;
            const totalTokens = limits?.totalTokens;

            if (typeof windowSeconds === 'number' && typeof remainingTokens === 'number' && typeof totalTokens === 'number') {
                const { value, unit } = utils.formatTimeWindow(windowSeconds);
                const formattedValue = utils.formatValue(value);
                const display = `Tokens: <b>${remainingTokens}</b>/${totalTokens} (${formattedValue}${unit})`;
                elem.innerHTML = display;
            } else {
                elem.textContent = 'Tokens: N/A';
            }
        }
    };

    /**
     * API-related functions for fetching rate limits
     * Handles all server communication and error handling
     */
    const api = {
        // Fetches rate limits
        async fetchRateLimits() {
            try {
                const headers = {
                    ...CONFIG.API.headers,
                    'User-Agent': navigator.userAgent,
                    'X-Xai-Request-Id': utils.generateId()
                };

                const response = await fetch(CONFIG.API.endpoint, {
                    method: 'POST',
                    headers,
                    body: JSON.stringify({
                        modelName: CONFIG.MODEL.modelName,
                        requestKind: CONFIG.MODEL.requestKind
                    })
                });

                if (!response.ok) {
                    throw new Error(`Failed to fetch ${CONFIG.MODEL.modelName} rate limits`);
                }

                const json = await response.json();
                ui.updateRateLimits(json);
            } catch (error) {
                ui.updateRateLimits(null);
                console.error('GROK-USR: Failed to fetch rate limits. Please try again later.');
            }
        }
    };

    /**
     * Initializes the application:
     * 1. Creates and injects styles
     * 2. Creates and adds the display menu
     * 3. Fetches initial rate limit
     * 4. Sets up periodic updates
     */
    const init = () => {
        ui.createStyles();
        document.body.appendChild(ui.createMenu());
        api.fetchRateLimits();
        setInterval(api.fetchRateLimits, CONFIG.UI.refreshInterval);
    };

    // Initialize when DOM is ready
    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        init();
    } else {
        document.addEventListener('DOMContentLoaded', init);
    }
})();