Grok 3 Rate Limit Display

Displays Grok 3 rate limit (Queries left: Remaining of Total)

Versão de: 25/04/2025. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         Grok 3 Rate Limit Display
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Displays Grok 3 rate limit (Queries left: Remaining of Total) 
// @author       Blankspeaker (based on the chrome extension by CursedAtom) & Grok
// @match        https://grok.com/chat/*
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

/*
 * DEBUG_MODE: Set to true for detailed logs (e.g., polling, span creation, click events).
 * Set to false for minimal logs (only errors). Default: false.
 */
(function() {
    'use strict';

    const DEBUG_MODE = false; // Set to true for troubleshooting, false for minimal output

    // Function to log messages based on debug mode
    function log(message) {
        if (DEBUG_MODE) {
            console.log(`[Grok Rate Limit] ${message}`);
        }
    }

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

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

            const data = await response.json();
            log('Rate limit data: ' + JSON.stringify(data));
            return data;
        } catch (error) {
            console.error('[Grok Rate Limit] Failed to fetch rate limit:', error);
            return null;
        }
    }

    // Function to update or create the rate limit display
    async function displayRateLimit() {
        try {
            // Check if rate limit span already exists
            const existingSpan = document.querySelector('span.rate-limit-span');
            if (existingSpan) {
                // Update existing span
                const rateLimitData = await fetchRateLimit();
                if (rateLimitData) {
                    const remaining = rateLimitData.remainingQueries ?? 'Unknown';
                    const total = rateLimitData.totalQueries ?? 'Unknown';
                    existingSpan.textContent = `Queries left: ${remaining} of ${total} `;
                }
                log('Rate limit span updated');
                return true; // Indicate span exists
            }

            // Try primary target: <a> with href="/chat"
            let targetLink = document.querySelector('a.inline-flex.items-center.justify-center.gap-2[href="/chat"]');
            if (!targetLink) {
                // Fall back to alternative target: <a> with href="/chat#private"
                targetLink = document.querySelector('a.inline-flex.items-center.justify-center.gap-2[href="/chat#private"][aria-label="Switch to Private Chat"]');
                if (!targetLink) {
                    log('Both target chat links not found');
                    return false; // Indicate failure to find target
                }
            }

            // Fetch rate limit data
            const rateLimitData = await fetchRateLimit();
            if (!rateLimitData) {
                return false;
            }

            // Extract rate limit info
            const remaining = rateLimitData.remainingQueries ?? 'Unknown';
            const total = rateLimitData.totalQueries ?? 'Unknown';

            // Create rate limit display element
            const rateLimitSpan = document.createElement('span');
            rateLimitSpan.textContent = `Queries left: ${remaining} of ${total} `;
            rateLimitSpan.classList.add('rate-limit-span');
            rateLimitSpan.style.marginRight = '8px';
            rateLimitSpan.style.color = '#666';
            rateLimitSpan.style.fontSize = '12px';
            rateLimitSpan.style.display = 'inline-flex';
            rateLimitSpan.style.alignItems = 'center';

            // Insert to the left of the target link
            targetLink.parentNode.insertBefore(rateLimitSpan, targetLink);
            log('Rate limit span created');
            return true; // Indicate success
        } catch (error) {
            console.error('[Grok Rate Limit] Error in displayRateLimit:', error);
            return false;
        }
    }

    // Function to poll for either target element with retries
    async function pollForTarget(maxAttempts = 3, interval = 3000) {
        try {
            let attempts = 0;
            while (attempts < maxAttempts) {
                // Early exit if span already exists
                if (document.querySelector('span.rate-limit-span')) {
                    log('Rate limit span already exists, stopping polling');
                    return;
                }
                const success = await displayRateLimit();
                if (success) {
                    return; // Exit once displayed
                }
                attempts++;
                await new Promise(resolve => setTimeout(resolve, interval));
            }
            log('Max attempts reached, could not find either target chat link');
        } catch (error) {
            console.error('[Grok Rate Limit] Error in pollForTarget:', error);
        }
    }

    // Run initially with a delay to allow page to load
    try {
        setTimeout(() => {
            pollForTarget();
        }, 2000); // Start after 2 seconds
    } catch (error) {
        console.error('[Grok Rate Limit] Error setting up delayed polling:', error);
    }

    // Periodically refresh rate limit (every 120 seconds)
    try {
        setInterval(async () => {
            const existingSpan = document.querySelector('span.rate-limit-span');
            if (existingSpan) {
                const rateLimitData = await fetchRateLimit();
                if (rateLimitData) {
                    const remaining = rateLimitData.remainingQueries ?? 'Unknown';
                    const total = rateLimitData.totalQueries ?? 'Unknown';
                    existingSpan.textContent = `Queries left: ${remaining} of ${total} `;
                    log('Rate limit span refreshed');
                }
            }
        }, 120000); // Refresh every 2 minutes
    } catch (error) {
        console.error('[Grok Rate Limit] Error setting up periodic refresh:', error);
    }

    // Add click listener for the send button SVG to refresh rate limit after 1 second
    try {
        document.addEventListener('click', async (event) => {
            const svg = event.target.closest('svg[width="20"][height="20"][viewBox="0 0 24 24"][fill="none"][class="stroke-[2] relative"]');
            if (svg && svg.querySelector('path[d="M5 11L12 4M12 4L19 11M12 4V21"]')) {
                log('Send button SVG clicked, scheduling refresh');
                setTimeout(async () => {
                    const existingSpan = document.querySelector('span.rate-limit-span');
                    if (existingSpan) {
                        const rateLimitData = await fetchRateLimit();
                        if (rateLimitData) {
                            const remaining = rateLimitData.remainingQueries ?? 'Unknown';
                            const total = rateLimitData.totalQueries ?? 'Unknown';
                            existingSpan.textContent = `Queries left: ${remaining} of ${total} `;
                            log('Rate limit span refreshed after click');
                        }
                    } else {
                        // If no span exists, trigger full display logic
                        await displayRateLimit();
                    }
                }, 1000); // Refresh after 1 second
            }
        });
    } catch (error) {
        console.error('[Grok Rate Limit] Error setting up click listener:', error);
    }
})();