Displays rate limit for the selected Grok model (Grok 3 or Grok 4)
当前为
// ==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');
})();