Grok 4 Rate Limit Display

Displays rate limit for the selected Grok model (Grok 3 or Grok 4)

اعتبارا من 10-07-2025. شاهد أحدث إصدار.

// ==UserScript==
// @name         Grok 4 Rate Limit Display
// @namespace    http://tampermonkey.net/
// @version      2.3
// @description  Displays rate limit for the selected Grok model (Grok 3 or Grok 4) 
// @author       blankspeaker
// @match        https://grok.com/chat/*
// @match        https://x.com/i/grok*
// @match        https://grok.x.ai/chat/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log('Grok Rate Limit Display script loaded v2.3');

    let cachedRateLimit = null;
    let lastFetchTime = 0;
    const MIN_FETCH_INTERVAL = 5000; // 5 seconds

    // Debounce function
    function debounce(func, delay) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func(...args), delay);
        };
    }

    // Function to get the current model
    function getCurrentModel() {
        const modelLabel = document.querySelector('.inline-block.text-primary.text-xs');
        if (modelLabel) {
            const labelText = modelLabel.textContent.trim();
            console.log('Model label found:', labelText);
            if (labelText.includes('Grok 3')) {
                return { displayName: 'Grok 3', modelName: 'grok-3' };
            } else if (labelText.includes('Grok 4')) {
                return { displayName: 'Grok 4', modelName: 'grok-4' };
            } else {
                console.log('Unknown model, defaulting to Grok 3');
                return { displayName: 'Grok 3', modelName: 'grok-3' };
            }
        }
        console.log('Model label not found, defaulting to Grok 3');
        return { displayName: 'Grok 3', modelName: 'grok-3' };
    }

    // Function to fetch rate limit with caching and throttling
    async function fetchRateLimit(modelName) {
        if (Date.now() - lastFetchTime < MIN_FETCH_INTERVAL) {
            console.log('Using cached rate limit data due to throttle');
            return cachedRateLimit;
        }

        try {
            const response = await fetch('https://grok.com/rest/rate-limits', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    requestKind: 'DEFAULT',
                    modelName: modelName,
                }),
                credentials: 'include',
            });

            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }

            const data = await response.json();
            console.log('Rate limit data for ' + modelName + ':', data);
            cachedRateLimit = data;
            lastFetchTime = Date.now();
            return data;
        } catch (error) {
            console.error('Failed to fetch rate limit for ' + modelName + ':', error);
            return null;
        }
    }

    // Function to display or update the rate limit
    async function displayRateLimit() {
        console.log('Attempting to display rate limit');

        let rateLimitDiv = document.querySelector('#rate-limit-div');
        if (!rateLimitDiv) {
            // Create the div if it doesn't exist
            rateLimitDiv = document.createElement('div');
            rateLimitDiv.id = 'rate-limit-div';
            rateLimitDiv.style.position = 'fixed';
            rateLimitDiv.style.top = '10px';
            rateLimitDiv.style.left = '50%';
            rateLimitDiv.style.transform = 'translateX(-50%)';
            rateLimitDiv.style.backgroundColor = 'transparent';
            rateLimitDiv.style.padding = '0';
            rateLimitDiv.style.border = 'none';
            rateLimitDiv.style.borderRadius = '0';
            rateLimitDiv.style.zIndex = '9999';
            rateLimitDiv.style.color = '#666'; // Gray color for visibility on page background
            rateLimitDiv.style.fontSize = '16px';
            rateLimitDiv.textContent = 'Loading rate limit...';
            document.body.appendChild(rateLimitDiv);
            console.log('Rate limit div created');
        }

        const currentModel = getCurrentModel();
        const rateLimitData = await fetchRateLimit(currentModel.modelName);

        if (rateLimitData) {
            const remaining = rateLimitData.remainingQueries ?? 'Unknown';
            const total = rateLimitData.totalQueries ?? 'Unknown';
            rateLimitDiv.textContent = `${currentModel.displayName} Queries left: ${remaining} of ${total}`;
            console.log(`Displayed: ${currentModel.displayName} ${remaining} of ${total}`);
        } else if (cachedRateLimit) {
            const remaining = cachedRateLimit.remainingQueries ?? 'Unknown';
            const total = cachedRateLimit.totalQueries ?? 'Unknown';
            rateLimitDiv.textContent = `${currentModel.displayName} Queries left: ${remaining} of ${total} (cached)`;
            console.log(`Displayed cached: ${currentModel.displayName} ${remaining} of ${total}`);
        } else {
            rateLimitDiv.textContent = `${currentModel.displayName} Queries left: Error fetching data`;
        }
    }

    const debouncedDisplayRateLimit = debounce(displayRateLimit, 1000);

    // Function to attach submit listeners
    function attachSubmitListeners() {
        const maxAttempts = 20;
        let attempts = 0;
        const interval = setInterval(() => {
            const textarea = document.querySelector('textarea');
            if (textarea) {
                textarea.addEventListener('keydown', (e) => {
                    if (e.key === 'Enter' && !e.shiftKey) {
                        console.log('Query submitted via Enter');
                        setTimeout(debouncedDisplayRateLimit, 2000); // Delay to allow the query to process
                    }
                });
                console.log('Textarea found and listener added');
                clearInterval(interval);
            } else {
                attempts++;
                if (attempts >= maxAttempts) {
                    console.log('Textarea not found after max attempts');
                    clearInterval(interval);
                }
            }
        }, 500);
    }

    // Function to init
    function init() {
        console.log('Init: displaying rate limit');
        displayRateLimit();
        attachSubmitListeners();
    }

    // Run initially
    if (document.readyState === 'complete') {
        init();
    } else {
        window.addEventListener('load', init);
    }

    // Periodically refresh rate limit (every 30 seconds)
    setInterval(() => {
        console.log('Periodic refresh');
        debouncedDisplayRateLimit();
    }, 30000);

    // MutationObserver to update if model changes
    const observer = new MutationObserver(() => {
        console.log('Mutation observed, updating display');
        debouncedDisplayRateLimit();
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true,
        characterData: true
    });
    console.log('MutationObserver started for model changes');
})();