Grok Monitor

监控 Grok API 配额(标准、思考、深度、更深),默认显示总数,悬浮查看详情

As of 11.04.2025. See ბოლო ვერსია.

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 Monitor
// @namespace    https://github.com/Loongphy/Grok-Monitor
// @version      1.0.0
// @author       Loongphy
// @description  监控 Grok API 配额(标准、思考、深度、更深),默认显示总数,悬浮查看详情
// @match        https://grok.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @license      GPL-3.0
// ==/UserScript==

/*
 * Grok Monitor - 监控 Grok API 配额使用情况的油猴脚本
 *
 * 本脚本基于 GPL-3.0 许可证
 *
 * 这是一个衍生作品,基于 BlueSkyXN 的 Grok Helper
 * 原始代码: https://github.com/BlueSkyXN/GPT-Models-Plus/blob/main/GrokHelper.js
 * 原作者: BlueSkyXN
 */

(function() {
    'use strict';

    // 缓存查询结果
    let cachedResults = null;

    // 获取用户设置或设置默认值
    let isCompactMode = GM_getValue('compactMode', true); // 默认使用精简模式

    // 四种模式 -> 中文名称对应表
    const MODE_LABELS = {
        DEFAULT: '标准',
        REASONING: '思考',
        DEEPSEARCH: '深度',
        DEEPERSEARCH: '更深'
    };

    // 我们需要查询的四种模式
    const REQUEST_KINDS = Object.keys(MODE_LABELS);

    // 图标 SVG (来自 Font Awesome 免费图标)
    const ICONS = {
        BOLT: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16"><path fill="currentColor" d="M349.4 44.6c5.9-13.7 1.5-29.7-10.6-38.5s-28.6-8-39.9 1.8l-256 224c-10 8.8-13.6 22.9-8.9 35.3S50.7 288 64 288H175.5L98.6 467.4c-5.9 13.7-1.5 29.7 10.6 38.5s28.6 8 39.9-1.8l256-224c10-8.8 13.6-22.9 8.9-35.3S397.3 224 384 224H272.5L349.4 44.6z"/></svg>',
        TIMER: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16"><path fill="currentColor" d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120V256c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"/></svg>',
        INFO: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="14" height="14"><path fill="currentColor" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>',
        REFRESH: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="12" height="12"><path fill="currentColor" d="M463.5 224H472c13.3 0 24-10.7 24-24V72c0-9.7-5.8-18.5-14.8-22.2s-19.3-1.7-26.2 5.2L413.4 96.6c-87.6-86.5-228.7-86.2-315.8 1c-87.5 87.5-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3c62.2-62.2 162.7-62.5 225.3-1L327 183c-6.9 6.9-8.9 17.2-5.2 26.2s12.5 14.8 22.2 14.8H463.5z"/></svg>'
    };

    // 添加自定义样式
    GM_addStyle(`
        /* 通用样式 */
        .grok-monitor {
            position: fixed;
            left: 16px;
            top: 72px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            z-index: 100;
            transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
            width: fit-content;
        }

        /* 完整模式样式 */
        .grok-monitor.full-mode {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            gap: 10px;
            border-radius: 12px;
            background-color: rgba(255, 255, 255, 0.95);
            color: #333;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.1);
            backdrop-filter: blur(8px);
            max-width: 280px;
            border: 1px solid rgba(0, 0, 0, 0.06);
            transform-origin: top left;
            padding: 12px 16px;
            font-size: 14px;
        }

        .grok-monitor.full-mode:hover {
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12), 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        /* 精简模式样式 */
        .grok-monitor.compact-mode {
            display: flex;
            flex-direction: column;
            border-radius: 12px;
            background-color: rgba(255, 255, 255, 0.95);
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.1);
            border: 1px solid rgba(0, 0, 0, 0.06);
            overflow: hidden;
            max-width: 280px;
        }

        .grok-monitor.compact-mode .compact-header {
            display: flex;
            align-items: center;
            padding: 8px 16px;
            gap: 10px;
            font-size: 15px;
            color: #333;
            width: 100%;
        }

        .grok-monitor-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            width: 100%;
            gap: 10px;
        }

        .grok-monitor-title {
            display: flex;
            align-items: center;
            gap: 8px;
            font-weight: 600;
            font-size: 14px;
            color: #444;
        }

        .grok-monitor-title .icon {
            display: flex;
            align-items: center;
            color: #2563EB;
        }

        .grok-monitor-summary {
            display: flex;
            align-items: center;
            gap: 8px;
            white-space: nowrap;
            font-weight: 500;
            font-size: 15px;
            color: #444;
            width: 100%;
        }

        .full-mode .grok-monitor-summary {
            background: rgba(0, 0, 0, 0.03);
            padding: 6px 10px;
            border-radius: 8px;
            justify-content: space-between;
        }

        .compact-mode .grok-monitor-summary {
            padding: 0;
            margin: 0;
            justify-content: space-between;
        }

        .grok-monitor-summary-text {
            display: flex;
            align-items: center;
            gap: 6px;
        }

        .grok-monitor-indicator {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            flex-shrink: 0;
            box-shadow: 0 0 0 rgba(0, 0, 0, 0);
            transition: all 0.3s ease;
            margin-left: 8px;
        }

        .grok-monitor-indicator.green {
            background-color: #10B981;
            box-shadow: 0 0 8px rgba(16, 185, 129, 0.5);
        }

        .grok-monitor-indicator.yellow {
            background-color: #F59E0B;
            box-shadow: 0 0 8px rgba(245, 158, 11, 0.5);
        }

        .grok-monitor-indicator.red {
            background-color: #EF4444;
            box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
        }

        .grok-monitor-details {
            display: none;
            flex-direction: column;
            gap: 8px;
            font-size: 13px;
            color: #555;
            width: 100%;
        }

        .show-details .grok-monitor-details,
        .full-mode:hover .grok-monitor-details,
        .compact-mode:hover .grok-monitor-details {
            display: flex;
            animation: fadeIn 0.3s ease forwards;
        }

        .compact-mode .grok-monitor-details {
            padding: 0 16px 12px;
        }

        .grok-monitor-kind-row {
            display: flex;
            align-items: center;
            gap: 8px;
            white-space: nowrap;
            padding: 6px 10px;
            border-radius: 8px;
            background: rgba(0, 0, 0, 0.02);
            justify-content: space-between;
            transition: background-color 0.2s ease;
        }

        .grok-monitor-kind-row:hover {
            background: rgba(0, 0, 0, 0.04);
        }

        .grok-monitor-kind-name {
            font-weight: 600;
            color: #333;
            display: flex;
            align-items: center;
            gap: 6px;
        }

        .grok-monitor-kind-name .icon {
            display: flex;
            color: #555;
        }

        .grok-monitor-info {
            color: #666;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .grok-monitor-info .time {
            opacity: 0.8;
            font-size: 12px;
            color: #777;
            display: flex;
            align-items: center;
            gap: 3px;
        }

        .grok-monitor-info .time .icon {
            display: flex;
            color: #777;
        }

        .refresh-button {
            display: flex;
            align-items: center;
            justify-content: center;
            background: rgba(37, 99, 235, 0.08);
            color: #2563EB;
            border: none;
            border-radius: 6px;
            width: 24px;
            height: 24px;
            cursor: pointer;
            transition: background-color 0.3s ease;
            padding: 0;
            margin-left: auto;
        }

        .refresh-button:hover {
            background: rgba(37, 99, 235, 0.15);
        }

        .refresh-button .icon-container {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
        }

        .refresh-button:hover .icon-container {
            animation: rotateIcon 0.8s ease forwards;
        }

        .grok-monitor.updating .grok-monitor-indicator {
            animation: pulse 1s ease-in-out infinite;
        }

        .mode-switch-btn {
            width: 100%;
            padding: 6px 0;
            font-size: 13px;
            border: none;
            background: rgba(0, 0, 0, 0.02);
            border-radius: 6px;
            cursor: pointer;
            color: #555;
            transition: background 0.2s ease;
            margin-top: 4px;
        }

        .mode-switch-btn:hover {
            background: rgba(0, 0, 0, 0.05);
        }

        @keyframes pulse {
            0%, 100% {
                transform: scale(1);
                opacity: 1;
            }
            50% {
                transform: scale(1.3);
                opacity: 0.7;
            }
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(-5px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        @keyframes rotateIcon {
            from {
                transform: rotate(0deg);
            }
            to {
                transform: rotate(360deg);
            }
        }

        @media (prefers-color-scheme: dark) {
            .grok-monitor.full-mode,
            .grok-monitor.compact-mode {
                background-color: rgba(30, 30, 30, 0.9);
                color: #eee;
                border-color: rgba(255, 255, 255, 0.1);
            }

            .mode-switch-btn {
                background: rgba(255, 255, 255, 0.05);
                color: #aaa;
            }

            .mode-switch-btn:hover {
                background: rgba(255, 255, 255, 0.1);
            }

            .grok-monitor-title {
                color: #ddd;
            }

            .grok-monitor-summary {
                color: #ddd;
            }

            .full-mode .grok-monitor-summary {
                background: rgba(255, 255, 255, 0.05);
            }

            .grok-monitor-details {
                color: #ccc;
            }

            .grok-monitor-kind-row {
                background: rgba(255, 255, 255, 0.03);
            }

            .grok-monitor-kind-row:hover {
                background: rgba(255, 255, 255, 0.07);
            }

            .grok-monitor-kind-name {
                color: #ddd;
            }

            .grok-monitor-kind-name .icon {
                color: #aaa;
            }

            .grok-monitor-info {
                color: #bbb;
            }

            .grok-monitor-info .time {
                color: #999;
            }

            .grok-monitor-info .time .icon {
                color: #999;
            }

            .refresh-button {
                background: rgba(59, 130, 246, 0.12);
            }

            .refresh-button:hover {
                background: rgba(59, 130, 246, 0.2);
            }
        }
    `);

    // 工具函数:格式化等待时间
    function formatWaitTime(seconds) {
        if (seconds <= 0) return '0分';
        const minutes = Math.floor(seconds / 60);
        return `${minutes}分`;
    }

    // 工具函数:格式化窗口时间
    function formatWindowTime(seconds) {
        if (seconds <= 0) return '0h';
        const hours = Math.floor(seconds / 3600);
        return `${hours}h`;
    }

    // 切换显示模式
    function toggleMode() {
        isCompactMode = !isCompactMode;
        GM_setValue('compactMode', isCompactMode);

        // 直接更新UI,不刷新页面
        const monitorElement = document.querySelector('.grok-monitor');
        if (monitorElement) {
            // 清空现有内容
            monitorElement.innerHTML = '';
            monitorElement.className = `grok-monitor ${isCompactMode ? 'compact-mode' : 'full-mode'}`;

            // 重新创建UI
            if (isCompactMode) {
                createCompactModeUI(monitorElement);
            } else {
                createFullModeUI(monitorElement);
            }

            // 如果有缓存的数据,直接更新UI
            if (cachedResults) {
                updateUI(cachedResults);
            }
        }
    }

    // 创建监控器UI
    function createMonitor() {
        const monitor = document.createElement('div');
        monitor.className = `grok-monitor ${isCompactMode ? 'compact-mode' : 'full-mode'}`;

        if (isCompactMode) {
            // 精简模式
            createCompactModeUI(monitor);
        } else {
            // 完整模式
            createFullModeUI(monitor);
        }

        document.body.appendChild(monitor);
        return monitor;
    }

    // 创建完整模式UI
    function createFullModeUI(monitor) {
        // 标题栏
        const header = document.createElement('div');
        header.className = 'grok-monitor-header';

        const title = document.createElement('div');
        title.className = 'grok-monitor-title';

        const titleIcon = document.createElement('span');
        titleIcon.className = 'icon';
        titleIcon.innerHTML = ICONS.BOLT;

        const titleText = document.createElement('span');
        titleText.textContent = 'Grok 配额监控';

        title.appendChild(titleIcon);
        title.appendChild(titleText);

        const refreshButton = document.createElement('button');
        refreshButton.className = 'refresh-button';
        refreshButton.title = '刷新数据';

        // 创建图标容器,旋转将只应用于此容器
        const iconContainer = document.createElement('span');
        iconContainer.className = 'icon-container';
        iconContainer.innerHTML = ICONS.REFRESH;

        refreshButton.appendChild(iconContainer);

        refreshButton.onclick = async (e) => {
            e.stopPropagation();
            await checkRateLimits();
        };

        header.appendChild(title);
        header.appendChild(refreshButton);

        // 小版本(默认显示)
        const summaryRow = document.createElement('div');
        summaryRow.className = 'grok-monitor-summary';

        const sumText = document.createElement('div');
        sumText.className = 'grok-monitor-summary-text';

        const sumSpan = document.createElement('span');
        sumSpan.textContent = '剩余总数: ...';

        sumText.appendChild(sumSpan);

        const indicator = document.createElement('div');
        indicator.className = 'grok-monitor-indicator';

        summaryRow.appendChild(sumText);
        summaryRow.appendChild(indicator);

        // 大版本(悬浮后展开)
        const details = document.createElement('div');
        details.className = 'grok-monitor-details';

        // 为每种模式创建行
        REQUEST_KINDS.forEach(kind => {
            const row = document.createElement('div');
            row.className = 'grok-monitor-kind-row';

            const nameContainer = document.createElement('div');
            nameContainer.className = 'grok-monitor-kind-name';

            const kindIcon = document.createElement('span');
            kindIcon.className = 'icon';
            kindIcon.innerHTML = ICONS.INFO;

            const nameSpan = document.createElement('span');
            nameSpan.textContent = MODE_LABELS[kind];

            nameContainer.appendChild(kindIcon);
            nameContainer.appendChild(nameSpan);

            const infoContainer = document.createElement('div');
            infoContainer.className = 'grok-monitor-info';

            const infoSpan = document.createElement('span');
            infoSpan.textContent = '剩余 .../...';

            const timeSpan = document.createElement('span');
            timeSpan.className = 'time';
            const timeIcon = document.createElement('span');
            timeIcon.className = 'icon';
            timeIcon.innerHTML = ICONS.TIMER;

            const timeText = document.createElement('span');
            timeText.textContent = '...h刷新';

            timeSpan.appendChild(timeIcon);
            timeSpan.appendChild(timeText);

            infoContainer.appendChild(infoSpan);
            infoContainer.appendChild(timeSpan);

            row.appendChild(nameContainer);
            row.appendChild(infoContainer);
            details.appendChild(row);
        });

        // 添加切换模式按钮
        const switchBtn = document.createElement('button');
        switchBtn.className = 'mode-switch-btn';
        switchBtn.textContent = '切换为精简模式';
        switchBtn.onclick = toggleMode;
        details.appendChild(switchBtn);

        monitor.appendChild(header);
        monitor.appendChild(summaryRow);
        monitor.appendChild(details);
    }

    // 创建精简模式UI
    function createCompactModeUI(monitor) {
        // 创建精简头部
        const compactHeader = document.createElement('div');
        compactHeader.className = 'compact-header';

        // 精简模式只显示总数和指示灯
        const summaryRow = document.createElement('div');
        summaryRow.className = 'grok-monitor-summary';

        const sumText = document.createElement('div');
        sumText.className = 'grok-monitor-summary-text';

        const sumSpan = document.createElement('span');
        sumSpan.textContent = '剩余总数: ...';

        sumText.appendChild(sumSpan);

        const indicator = document.createElement('div');
        indicator.className = 'grok-monitor-indicator';

        // 添加刷新按钮
        const refreshButton = document.createElement('button');
        refreshButton.className = 'refresh-button';
        refreshButton.title = '刷新数据';

        const iconContainer = document.createElement('span');
        iconContainer.className = 'icon-container';
        iconContainer.innerHTML = ICONS.REFRESH;

        refreshButton.appendChild(iconContainer);

        refreshButton.onclick = async (e) => {
            e.stopPropagation();
            await checkRateLimits();
        };

        summaryRow.appendChild(sumText);
        summaryRow.appendChild(indicator);
        summaryRow.appendChild(refreshButton);

        compactHeader.appendChild(summaryRow);

        // 创建详情部分(悬浮时展开)
        const details = document.createElement('div');
        details.className = 'grok-monitor-details';

        // 为每种模式创建行
        REQUEST_KINDS.forEach(kind => {
            const row = document.createElement('div');
            row.className = 'grok-monitor-kind-row';

            const nameContainer = document.createElement('div');
            nameContainer.className = 'grok-monitor-kind-name';

            const kindIcon = document.createElement('span');
            kindIcon.className = 'icon';
            kindIcon.innerHTML = ICONS.INFO;

            const nameSpan = document.createElement('span');
            nameSpan.textContent = MODE_LABELS[kind];

            nameContainer.appendChild(kindIcon);
            nameContainer.appendChild(nameSpan);

            const infoContainer = document.createElement('div');
            infoContainer.className = 'grok-monitor-info';

            const infoSpan = document.createElement('span');
            infoSpan.textContent = '剩余 .../...';

            const timeSpan = document.createElement('span');
            timeSpan.className = 'time';
            const timeIcon = document.createElement('span');
            timeIcon.className = 'icon';
            timeIcon.innerHTML = ICONS.TIMER;

            const timeText = document.createElement('span');
            timeText.textContent = '...h刷新';

            timeSpan.appendChild(timeIcon);
            timeSpan.appendChild(timeText);

            infoContainer.appendChild(infoSpan);
            infoContainer.appendChild(timeSpan);

            row.appendChild(nameContainer);
            row.appendChild(infoContainer);
            details.appendChild(row);
        });

        // 添加切换模式按钮
        const switchBtn = document.createElement('button');
        switchBtn.className = 'mode-switch-btn';
        switchBtn.textContent = '切换为完整模式';
        switchBtn.onclick = toggleMode;
        details.appendChild(switchBtn);

        monitor.appendChild(compactHeader);
        monitor.appendChild(details);
    }

    // 获取当前域名的基础URL
    function getBaseUrl() {
        return window.location.origin;
    }

    // 获取每种模式的限额
    async function fetchRateLimit(kind) {
        try {
            const baseUrl = getBaseUrl();
            const response = await fetch(`${baseUrl}/rest/rate-limits`, {
                method: 'POST',
                headers: {
                    'accept': '*/*',
                    'content-type': 'application/json'
                },
                body: JSON.stringify({
                    requestKind: kind,
                    modelName: "grok-3"
                }),
                credentials: 'include'
            });

            if (response.ok) {
                return await response.json();
            } else {
                throw new Error(`Failed to fetch ${kind} rate limit`);
            }
        } catch (error) {
            console.error('Rate limit check failed:', error);
            return null;
        }
    }

    // 一次获取所有模式数据
    async function getAllRateLimits() {
        const results = {};
        for (const kind of REQUEST_KINDS) {
            results[kind] = await fetchRateLimit(kind);
        }
        return results;
    }

    // 更新UI
    function updateUI(results) {
        // 缓存结果以便稍后使用
        cachedResults = results;

        const monitor = document.querySelector('.grok-monitor');
        const sumSpan = monitor.querySelector('.grok-monitor-summary-text span');
        const indicator = monitor.querySelector('.grok-monitor-indicator');

        monitor.classList.add('updating');

        // 移除之前的指示器类
        indicator.classList.remove('green', 'yellow', 'red');

        // 计算合计剩余次数
        let sum = 0;
        REQUEST_KINDS.forEach(kind => {
            const data = results[kind];
            if (data && data.remainingQueries > 0) {
                sum += data.remainingQueries;
            }
        });

        // 更新总数
        sumSpan.textContent = `剩余总数: ${sum}`;

        // 指示灯颜色
        if (sum === 0) {
            indicator.classList.add('red');
        } else if (sum > 0 && sum < 5) {
            indicator.classList.add('yellow');
        } else {
            indicator.classList.add('green');
        }

        // 更新详情行
        const detailRows = monitor.querySelectorAll('.grok-monitor-details .grok-monitor-kind-row');
        updateDetailRows(detailRows, results);

        setTimeout(() => monitor.classList.remove('updating'), 1000);
    }

    // 更新详情行
    function updateDetailRows(detailRows, results) {
        detailRows.forEach(row => {
            const label = row.querySelector('.grok-monitor-kind-name span:last-child')?.textContent;
            if (!label) return; // 跳过非模式行

            const kind = Object.keys(MODE_LABELS).find(k => MODE_LABELS[k] === label);
            if (!kind) return; // 跳过非模式行

            const data = results[kind];

            const infoSpan = row.querySelector('.grok-monitor-info span:first-child');
            const timeText = row.querySelector('.grok-monitor-info .time span:last-child');

            if (!data) {
                infoSpan.textContent = '获取失败';
                timeText.textContent = '';
                return;
            }

            const { remainingQueries, totalQueries, windowSizeSeconds, waitTimeSeconds } = data;
            if (remainingQueries > 0) {
                infoSpan.textContent = `剩余 ${remainingQueries}/${totalQueries}`;
                timeText.textContent = formatWindowTime(windowSizeSeconds);
            } else {
                infoSpan.textContent = `等待刷新`;
                timeText.textContent = formatWaitTime(waitTimeSeconds);
            }
        });
    }

    // 定时检查
    async function checkRateLimits() {
        const results = await getAllRateLimits();
        updateUI(results);
    }

    // 注册油猴菜单
    GM_registerMenuCommand("切换显示模式", toggleMode);

    // 初始化
    function init() {
        createMonitor();
        checkRateLimits();
        setInterval(checkRateLimits, 30000);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();