Grok-3 Rate Limits 状态栏,听我说,谢谢你,grok-3
油猴脚本
// ==UserScript==
// @name Grok Rate Limit Monitor
// @namespace http://tampermonkey.net/
// @version 0.9
// @description 自动抓取并显示 grok.com 的 rate-limits 请求结果(美化版),支持拖动窗口并定时请求
// @author [email protected] and [email protected]
// @match *://grok.com/*
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function () {
'use strict';
// 调试开关
const DEBUG = true;
function log(...args) {
if (DEBUG) console.log('[Rate Limit Monitor]', ...args);
}
// 默认位置
const defaultTop = 20;
const defaultRight = 20;
// 存储速率限制数据
const rateLimits = {
default: { windowSizeSeconds: '-', remainingQueries: '-', totalQueries: '-' },
reasoning: { windowSizeSeconds: '-', remainingQueries: '-', totalQueries: '-' },
deepsearch: { windowSizeSeconds: '-', remainingQueries: '-', totalQueries: '-' },
};
// 创建美化后的显示窗口
function createDisplay() {
const div = document.createElement('div');
div.id = 'rate-limit-display';
let top = GM_getValue('windowTop', defaultTop);
let right = GM_getValue('windowRight', defaultRight);
div.style.cssText = `
position: fixed;
top: ${top}px;
right: ${right}px;
background: linear-gradient(135deg, #fefefe, #f0f4f8);
border: 1px solid #e0e6ed;
border-radius: 8px;
padding: 12px;
z-index: 10000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
width: 200px;
transition: all 0.2s ease;
color: #2d3748;
`;
div.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 6px;">
<h3 id="rate-limit-header" style="margin: 0; font-size: 14px; font-weight: 600; color: #1a202c; cursor: move;">
Rate Limits
</h3>
<button id="refresh-rate-limits" style="
background: #4299e1;
border: none;
border-radius: 4px;
color: white;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
transition: background 0.2s;
">刷新</button>
</div>
<div style="margin-bottom: 8px;">
<h4 style="margin: 0 0 4px 0; font-size: 12px; font-weight: 500; color: #4a5568;">Default</h4>
<p style="margin: 2px 0; font-size: 11px; color: #718096;">
Window: <span id="window-size-default" style="color: #2b6cb0; font-weight: 500;">-</span> 秒
</p>
<p style="margin: 2px 0; font-size: 11px; color: #718096;">
Available: <span id="queries-default" style="color: #2f855a; font-weight: 500;">-/-</span>
</p>
</div>
<div style="margin-bottom: 8px;">
<h4 style="margin: 0 0 4px 0; font-size: 12px; font-weight: 500; color: #4a5568;">Reasoning</h4>
<p style="margin: 2px 0; font-size: 11px; color: #718096;">
Window: <span id="window-size-reasoning" style="color: #2b6cb0; font-weight: 500;">-</span> 秒
</p>
<p style="margin: 2px 0; font-size: 11px; color: #718096;">
Available: <span id="queries-reasoning" style="color: #2f855a; font-weight: 500;">-/-</span>
</p>
</div>
<div>
<h4 style="margin: 0 0 4px 0; font-size: 12px; font-weight: 500; color: #4a5568;">Deepsearch</h4>
<p style="margin: 2px 0; font-size: 11px; color: #718096;">
Window: <span id="window-size-deepsearch" style="color: #2b6cb0; font-weight: 500;">-</span> 秒
</p>
<p style="margin: 2px 0; font-size: 11px; color: #718096;">
Available: <span id="queries-deepsearch" style="color: #2f855a; font-weight: 500;">-/-</span>
</p>
</div>
`;
document.body.appendChild(div);
// 添加鼠标悬停效果
div.addEventListener('mouseenter', () => {
div.style.boxShadow = '0 6px 16px rgba(0, 0, 0, 0.15)';
div.style.transform = 'scale(1.02)';
});
div.addEventListener('mouseleave', () => {
div.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.1)';
div.style.transform = 'scale(1)';
});
// 添加刷新按钮效果和点击事件
const refreshButton = document.getElementById('refresh-rate-limits');
refreshButton.addEventListener('mouseenter', () => {
refreshButton.style.background = '#3182ce';
});
refreshButton.addEventListener('mouseleave', () => {
refreshButton.style.background = '#4299e1';
});
refreshButton.addEventListener('click', () => {
refreshButton.style.background = '#2c5282';
fetchRateLimits('DEFAULT');
fetchRateLimits('REASONING');
fetchRateLimits('DEEPSEARCH');
setTimeout(() => {
refreshButton.style.background = '#4299e1';
}, 200);
});
// 添加拖动功能
const header = document.getElementById('rate-limit-header');
header.addEventListener('mousedown', startDragging);
}
// 拖动功能
function startDragging(e) {
e.preventDefault();
const div = document.getElementById('rate-limit-display');
const initialMouseX = e.clientX;
const initialMouseY = e.clientY;
const initialTop = parseInt(div.style.top, 10);
const initialRight = parseInt(div.style.right, 10);
function onMouseMove(e) {
const deltaX = e.clientX - initialMouseX;
const deltaY = e.clientY - initialMouseY;
div.style.top = (initialTop + deltaY) + 'px';
div.style.right = (initialRight - deltaX) + 'px';
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
const newTop = parseInt(div.style.top, 10);
const newRight = parseInt(div.style.right, 10);
GM_setValue('windowTop', newTop);
GM_setValue('windowRight', newRight);
});
}
// 更新显示内容
function updateDisplay() {
document.getElementById('window-size-default').textContent = rateLimits.default.windowSizeSeconds;
document.getElementById('queries-default').textContent = `${rateLimits.default.remainingQueries}/${rateLimits.default.totalQueries}`;
document.getElementById('window-size-reasoning').textContent = rateLimits.reasoning.windowSizeSeconds;
document.getElementById('queries-reasoning').textContent = `${rateLimits.reasoning.remainingQueries}/${rateLimits.reasoning.totalQueries}`;
document.getElementById('window-size-deepsearch').textContent = rateLimits.deepsearch.windowSizeSeconds;
document.getElementById('queries-deepsearch').textContent = `${rateLimits.deepsearch.remainingQueries}/${rateLimits.deepsearch.totalQueries}`;
}
// 发起 rate-limits 请求
function fetchRateLimits(kind) {
log(`Fetching rate limits for ${kind}...`);
fetch('https://grok.com/rest/rate-limits', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
},
body: JSON.stringify({ requestKind: kind, modelName: 'grok-3' }),
})
.then(response => {
log(`Rate limits response status for ${kind}:`, response.status);
return response.json();
})
.then(data => {
const lowerKind = kind.toLowerCase();
log(`Rate limits data for ${kind}:`, data);
rateLimits[lowerKind] = {
windowSizeSeconds: data.windowSizeSeconds ?? '-',
remainingQueries: data.remainingQueries ?? '-',
totalQueries: data.totalQueries ?? '-',
};
log(`Updated Rate Limit for ${lowerKind}:`, rateLimits[lowerKind]);
updateDisplay();
})
.catch(error => {
console.error(`Failed to fetch ${kind} rate-limits:`, error);
log(`Error details for ${kind}:`, error);
});
}
// 定时请求逻辑
let intervalId;
function startPolling() {
const poll = () => {
log('Polling rate limits...');
fetchRateLimits('DEFAULT');
fetchRateLimits('REASONING');
fetchRateLimits('DEEPSEARCH');
};
poll(); // 立即执行一次
intervalId = setInterval(poll, 10 * 60 * 1000); // 每10分钟执行一次
}
function resetPolling() {
log('Resetting polling interval...');
clearInterval(intervalId);
setTimeout(startPolling, 10 * 60 * 1000); // 延迟10分钟后重新开始
}
// 处理 rate-limit 响应的通用函数
async function handleRateLimitResponse(resource, options, response) {
log('Handling potential rate limit response:', { resource, options });
let requestKind = 'default';
try {
const body = typeof options.body === 'string' ? JSON.parse(options.body) :
options.body instanceof Blob ? JSON.parse(await options.body.text()) : null;
log('Parsed request body:', body);
requestKind = body?.requestKind?.toLowerCase() || 'default';
} catch (e) {
log('Failed to parse rate-limit request body:', e);
return;
}
try {
const data = typeof response === 'string' ? JSON.parse(response) : await response.json();
log('Parsed response data:', data);
if (rateLimits.hasOwnProperty(requestKind)) {
rateLimits[requestKind] = {
windowSizeSeconds: data.windowSizeSeconds ?? '-',
remainingQueries: data.remainingQueries ?? '-',
totalQueries: data.totalQueries ?? '-',
};
log(`Updated Rate Limit for ${requestKind}:`, rateLimits[requestKind]);
updateDisplay();
resetPolling();
}
} catch (e) {
log('Failed to parse rate-limit response:', e);
}
}
// 重写 fetch
const originalFetch = window.fetch;
window.fetch = async function (resource, options = {}) {
log('Intercepted fetch request:', { resource, options });
const response = await originalFetch(resource, options);
if (typeof resource === 'string' && resource.includes('/rate-limits')) {
log('Matched rate-limits fetch request');
const clonedResponse = response.clone();
handleRateLimitResponse(resource, options, clonedResponse);
}
return response;
};
// 重写 XMLHttpRequest
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url) {
this._url = url;
this._method = method;
originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function (body) {
if (typeof this._url === 'string' && this._url.includes('/rate-limits')) {
log('Intercepted rate-limits XHR request:', { url: this._url, method: this._method, body });
this.addEventListener('load', () => {
handleRateLimitResponse(this._url, { method: this._method, body }, this.responseText);
});
}
originalSend.apply(this, arguments);
};
// 初始化显示窗口并启动定时请求
createDisplay();
startPolling();
// 提示脚本已加载
log('Script loaded with enhanced rate-limit capturing and refresh button.');
})();
拿 claude-3-7-sonnet 重写了下,thinking 模式真的强,审美也在线,新版本真香:
claude-3-7-sonnet 新版本(增加拖拽位置记忆)
// ==UserScript==
// @name Rate Limits Monitor
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Monitor conversation limits for default, deepsearch, and reason modes
// @author [email protected] & claude-3-7-sonnet-thinking
// @match https://grok.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Configuration
const UPDATE_INTERVAL = 60000*10; // Update every 10 minutes
const MODES = [
{ name: 'DEFAULT', displayName: 'Default', color: '#4CAF50', valueColor: '#7CFF7C' },
{ name: 'DEEPSEARCH', displayName: 'Deep Search', color: '#2196F3', valueColor: '#7CD5FF' },
{ name: 'REASONING', displayName: 'Reason', color: '#FF9800', valueColor: '#FFC57C' }
];
let timers = {}; // Store timers for each mode
let lastFetchTime = {}; // Track last fetch time for each mode
let windowSizes = {}; // Store window sizes for each mode
// Create monitor UI
function createMonitorUI() {
const monitorDiv = document.createElement('div');
monitorDiv.id = 'limit-monitor';
monitorDiv.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
background-color: rgba(28, 30, 33, 0.92);
border-radius: 10px;
padding: 12px;
color: #e0e0e0;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 14px;
z-index: 10000;
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.4);
min-width: 220px;
user-select: none;
border: 1px solid rgba(255, 255, 255, 0.1);
`;
// Create title bar with drag handle
const titleBar = document.createElement('div');
titleBar.style.cssText = `
cursor: move;
padding-bottom: 8px;
margin-bottom: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
display: flex;
justify-content: space-between;
align-items: center;
`;
titleBar.innerHTML = '<span style="font-weight: bold; font-size: 15px;">Rate Limits Status</span>';
// Add refresh button
const refreshBtn = document.createElement('button');
refreshBtn.textContent = '↻';
refreshBtn.title = "Refresh all limits";
refreshBtn.style.cssText = `
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 4px;
color: white;
cursor: pointer;
font-size: 16px;
padding: 2px 8px;
transition: background 0.2s ease;
`;
refreshBtn.onmouseover = () => {
refreshBtn.style.background = 'rgba(255, 255, 255, 0.2)';
};
refreshBtn.onmouseout = () => {
refreshBtn.style.background = 'rgba(255, 255, 255, 0.1)';
};
refreshBtn.onclick = refreshAllLimits;
titleBar.appendChild(refreshBtn);
monitorDiv.appendChild(titleBar);
// Create container for the mode data
const modesContainer = document.createElement('div');
modesContainer.id = 'modes-container';
// Create elements for each mode
MODES.forEach(mode => {
const modeDiv = document.createElement('div');
modeDiv.style.cssText = `
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
`;
const modeLabel = document.createElement('span');
modeLabel.textContent = mode.displayName + ':';
modeLabel.style.cssText = `
font-weight: bold;
color: ${mode.color};
`;
const modeValue = document.createElement('span');
modeValue.id = `${mode.name.toLowerCase()}-limit`;
modeValue.textContent = '?/?';
modeValue.style.cssText = `
font-weight: bold;
color: ${mode.valueColor};
transition: color 0.3s ease;
`;
modeDiv.appendChild(modeLabel);
modeDiv.appendChild(modeValue);
modesContainer.appendChild(modeDiv);
});
monitorDiv.appendChild(modesContainer);
document.body.appendChild(monitorDiv);
// Make the monitor draggable
makeDraggable(monitorDiv, titleBar);
return monitorDiv;
}
// Format seconds to a readable time string
function formatTime(seconds) {
if (seconds < 60) {
return `${seconds}s`;
} else if (seconds < 3600) {
return `${Math.floor(seconds / 60)}m`;
} else {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
}
}
// Make an element draggable and save position to localStorage
function makeDraggable(element, handle) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
// Load saved position from localStorage if exists
const savedPosition = JSON.parse(localStorage.getItem('grok-limitMonitorPosition'));
if (savedPosition) {
element.style.top = savedPosition.top;
element.style.left = savedPosition.left;
element.style.right = 'auto';
element.style.bottom = 'auto';
}
handle.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
element.style.top = (element.offsetTop - pos2) + "px";
element.style.left = (element.offsetLeft - pos1) + "px";
element.style.right = 'auto';
element.style.bottom = 'auto';
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
// Save new position to localStorage
const position = {
top: element.style.top,
left: element.style.left
};
localStorage.setItem('grok-limitMonitorPosition', JSON.stringify(position));
}
}
// Fetch rate limits for a specific mode
async function fetchRateLimit(mode) {
try {
const response = await fetch('https://grok.com/rest/rate-limits', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
requestKind: mode,
modelName: 'grok-3'
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
lastFetchTime[mode] = Date.now();
// Store window size
if (data.windowSizeSeconds) {
windowSizes[mode] = data.windowSizeSeconds;
}
updateLimitDisplay(mode, data);
return data;
} catch (error) {
console.error(`Error fetching rate limits for ${mode}:`, error);
document.getElementById(`${mode.toLowerCase()}-limit`).textContent = 'Error';
}
}
// Update the display for a specific mode
function updateLimitDisplay(mode, data) {
const element = document.getElementById(`${mode.toLowerCase()}-limit`);
if (element && data) {
if (data.waitTimeSeconds !== undefined) {
// 如果存在 waitTimeSeconds,只显示等候时间
element.textContent = formatTime(data.waitTimeSeconds);
} else {
// 否则显示原来的 remaining/total 格式加上时间窗口
const timeWindow = data.windowSizeSeconds ? ` (${formatTime(data.windowSizeSeconds)})` : '';
element.textContent = `${data.remainingQueries}/${data.totalQueries}${timeWindow}`;
}
// Find the corresponding mode configuration
const modeConfig = MODES.find(m => m.name === mode);
// Visual feedback that data was updated
element.style.color = '#4df94d';
setTimeout(() => {
// Return to the mode's value color
if (modeConfig) {
element.style.color = modeConfig.valueColor;
}
}, 500);
}
}
// Refresh all limits
function refreshAllLimits() {
MODES.forEach(mode => {
fetchRateLimit(mode.name);
});
}
// Schedule regular updates
function scheduleUpdates() {
MODES.forEach(mode => {
// Clear any existing timer
if (timers[mode.name]) {
clearInterval(timers[mode.name]);
}
// Set new timer
timers[mode.name] = setInterval(() => {
fetchRateLimit(mode.name);
}, UPDATE_INTERVAL);
});
}
// Intercept fetch to monitor rate-limit requests from the website
function interceptFetch() {
const originalFetch = window.fetch;
window.fetch = async function(input, init) {
const response = await originalFetch(input, init);
// Clone the response to avoid consuming it
const responseClone = response.clone();
try {
// Check if this is a rate-limits request
if (input && input.toString().includes('rate-limits')) {
responseClone.json().then(data => {
// Try to determine which mode this request was for
if (init && init.body) {
try {
const body = JSON.parse(init.body);
if (body.requestKind) {
const mode = body.requestKind;
// Store window size if available
if (data.windowSizeSeconds) {
windowSizes[mode] = data.windowSizeSeconds;
}
// Update the display
updateLimitDisplay(mode, data);
// Update last fetch time
lastFetchTime[mode] = Date.now();
// Reset the timer for this mode
if (timers[mode]) {
clearInterval(timers[mode]);
timers[mode] = setInterval(() => {
fetchRateLimit(mode);
}, UPDATE_INTERVAL);
}
}
} catch (e) {
console.error('Error parsing request body:', e);
}
}
}).catch(e => {
console.error('Error parsing response:', e);
});
}
} catch (e) {
console.error('Error in fetch interceptor:', e);
}
return response;
};
}
// Initialize
function init() {
// Create UI
createMonitorUI();
// Fetch initial data
refreshAllLimits();
// Schedule updates
scheduleUpdates();
// Intercept fetch requests
interceptFetch();
}
// Start the script when the page is fully loaded
window.addEventListener('load', init);
})();
评论区佬友的升级版,更优雅更酷了
补充,如果有发生图标会消失的情况,可以使用这位佬友的版本: