Grok 3 Rate Limit Display

Displays rate limit (Queries left: Remaining of Total) on Grok.com

Pada tanggal 25 April 2025. Lihat %(latest_version_link).

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Grok 3 Rate Limit Display
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Displays rate limit (Queries left: Remaining of Total) on Grok.com
// @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);
    }
})();