Grok Quota Display

Displays rate limits on grok.com

От 09.06.2025. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Grok Quota Display
// @namespace    nisc
// @version      2025.06.08-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: {
            name: 'grok-3'
        },
        REQUEST_TYPES: {
            DEFAULT: 'Default',
            REASONING: 'Reason',
            DEEPSEARCH: 'Deep',
            DEEPERSEARCH: 'Deeper'
        },
        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: 0.25rem;
                }
                .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: 30000 // in milliseconds
        },
        TIME: {
            SECONDS_PER_HOUR: 3600,
            SECONDS_PER_DAY: 86400
        }
    };

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

        // Creates consistent element IDs for rate limit displays
        getLimitElementId: type => `rate_limit_${type.toLowerCase()}`,

        // 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';

            // Create display elements for each request type
            Object.entries(CONFIG.REQUEST_TYPES).forEach(([type, displayName]) => {
                const div = document.createElement('div');
                div.id = utils.getLimitElementId(type);
                div.className = 'grok-rate-limit';
                div.textContent = `${displayName}: N/A`;
                column.appendChild(div);
            });

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

        // Updates the display with new rate limit information
        updateRateLimits: limits => {
            Object.entries(CONFIG.REQUEST_TYPES).forEach(([type, displayName]) => {
                const elem = document.getElementById(utils.getLimitElementId(type));
                const limit = limits?.[type];

                if (limit?.windowSizeSeconds) {
                    const { value, unit } = utils.formatTimeWindow(limit.windowSizeSeconds);
                    const formattedValue = utils.formatValue(value);
                    const display =
                        `${displayName}: <b>${limit.remainingQueries}</b>/${limit.totalQueries} ` +
                        `(${formattedValue}${unit})`;
                    elem.innerHTML = display;
                } else {
                    elem.textContent = `${displayName}: N/A`;
                }
            });
        }
    };

    /**
     * API-related functions for fetching rate limits
     * Handles all server communication and error handling
     */
    const api = {
        // Fetches rate limits for all request types in parallel
        async fetchRateLimits() {
            try {
                const limits = {};
                // Create an array of promises for parallel execution
                const requests = Object.keys(CONFIG.REQUEST_TYPES).map(async type => {
                    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({
                            requestKind: type,
                            modelName: CONFIG.MODEL.name
                        })
                    });

                    if (!response.ok) {
                        throw new Error(`Failed to fetch ${CONFIG.MODEL.name} ${type} rate limits`);
                    }
                    limits[type] = await response.json();
                });

                // Wait for all requests to complete
                await Promise.all(requests);
                ui.updateRateLimits(limits);
            } catch (error) {
                ui.updateRateLimits(null);
                console.error('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 limits
     * 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);
    }
})();