Greasy Fork镜像 还支持 简体中文。

Grok Quota Display

Displays rate limits on grok.com

// ==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);
    }
})();

QingJ © 2025

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