吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 757|回复: 1
上一主题 下一主题
收起左侧

[其他原创] 油猴脚本分享:Gemini 用量看板

  [复制链接]
跳转到指定楼层
楼主
chenhaian228 发表于 2026-6-29 08:57 回帖奖励
本帖最后由 chenhaian228 于 2026-6-29 09:40 编辑

开发目的:此前用的Gemini  pro 学生认证套餐,用着很爽基本不会触顶,自上月google改变规则以来,额度根本就不够用,需要实时盯着用量,按需使用,无关或者轻量问题切换成3.1 flash-lite 或者3.1 flash,而google用量查看页面又有诸多不便,需要开两个窗口手动刷新,故此有此脚本!
功能介绍:支持多账号实时采集当前5小时用量、周用量,自动获取接口进行查询,默认10秒钟采集一次,窗口右键可调期望的周期;
使用环境:Microsoft Edge 149.0.4022.98(正常),  Chrome 142.0.7444.176 版本我测试不能正常显示,因为我主用Edge浏览器, Chrome的异常我就没去深究了。
使用方法:油猴插件管理面板手动添加脚本,ctrl+s 保存后,采用edge打开gemini自动唤出界面自动进行采集,开箱即用

[JavaScript] 纯文本查看 复制代码
// ==UserScript==
// @name         Gemini 用量看板
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  全账号URL动态嗅探,原生DOM+Observer防擦除,右上角常驻
// @AuThor       Gemini
// @match        https://gemini.google.com/*
// @run-at       document-start
// @grant        unsafeWindow
// ==/UserScript==

(function() {
    'use strict';

    const PANEL_ID = 'gemini-usage-panel-v12';
    const MENU_ID = 'gemini-usage-menu-v12';

    let pollInterval = parseInt(localStorage.getItem('gemini_usage_interval')) || 10;
    let pollingTimer = null;
    let shortValNode, shortTimeNode, longValNode, longTimeNode, statusNode;

    // 动态存储真实的 API 地址(包含 /u/1/ 等路由)
    let realApiUrl = 'https://gemini.google.com/_/BardChatUi/data/batchexecute?rpcids=jSf9Qc&rt=c';

    // --- 1. 底层拦截:精准偷取真实路由 URL ---
    const win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;

    const origFetch = win.fetch;
    win.fetch = async function(...args) {
        if (typeof args[0] === 'string' && args[0].includes('batchexecute')) {
            try {
                const urlObj = new URL(args[0], location.origin);
                urlObj.searchParams.set('rpcids', 'jSf9Qc');
                realApiUrl = urlObj.toString();
            } catch(e) {}
        }
        return origFetch.apply(this, args);
    };

    const origXhrOpen = win.XMLHttpRequest.prototype.open;
    win.XMLHttpRequest.prototype.open = function(method, url) {
        if (typeof url === 'string' && url.includes('batchexecute')) {
            try {
                const urlObj = new URL(url, location.origin);
                urlObj.searchParams.set('rpcids', 'jSf9Qc');
                realApiUrl = urlObj.toString();
            } catch(e) {}
        }
        return origXhrOpen.apply(this, arguments);
    };

    // --- 2. 界面构建 ---
    function el(tag, styles = {}, text = '') {
        const e = document.createElement(tag);
        Object.assign(e.style, styles);
        if (text) e.textContent = text;
        return e;
    }

    function initUI() {
        if (document.getElementById(PANEL_ID)) return;
        if (!document.body) return;

        const panel = el('div', {
            position: 'fixed', top: '80px', right: '20px', background: 'rgba(25, 25, 25, 0.95)',
            color: '#e8eaed', padding: '15px', borderRadius: '12px', fontFamily: 'sans-serif',
            fontSize: '13px', minWidth: '170px', boxShadow: '0 4px 15px rgba(0,0,0,0.5)',
            border: '1px solid rgba(255,255,255,0.1)', backdropFilter: 'blur(10px)',
            zIndex: '2147483647', userSelect: 'none', cursor: 'context-menu'
        });
        panel.id = PANEL_ID;

        const titleRow = el('div', { display: 'flex', justifyContent: 'space-between', borderBottom: '1px solid #444', paddingBottom: '8px', marginBottom: '12px' });
        titleRow.appendChild(el('span', { fontWeight: 'bold', color: '#fff' }, '📊 用量看板'));
        statusNode = el('span', { color: '#aa0000', fontSize: '12px', title: '初始化...' }, '●');
        titleRow.appendChild(statusNode);
        panel.appendChild(titleRow);

        function createSec(labelName) {
            const sec = el('div', { marginBottom: '10px', background: 'rgba(0,0,0,0.2)', padding: '8px', borderRadius: '8px' });
            const row = el('div', { display: 'flex', justifyContent: 'space-between', marginBottom: '4px' });
            row.appendChild(el('span', { color: '#9aa0a6' }, labelName));
            const val = el('span', { fontWeight: 'bold', color: '#8ab4f8' }, '--%');
            row.appendChild(val);
            const time = el('div', { color: '#5f6368', fontSize: '11px', textAlign: 'left' }, '刷新: --');
            sec.appendChild(row); sec.appendChild(time);
            panel.appendChild(sec);
            return { val, time };
        }

        const sData = createSec('5小时用量:');
        shortValNode = sData.val; shortTimeNode = sData.time;

        const lData = createSec('本周用量:');
        longValNode = lData.val; longTimeNode = lData.time;

        document.body.appendChild(panel);

        const menu = el('div', {
            position: 'fixed', background: '#2c2c2c', color: '#fff', padding: '5px 0',
            borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.6)', border: '1px solid #555',
            zIndex: '2147483647', display: 'none', fontSize: '13px', minWidth: '120px'
        });
        menu.id = MENU_ID;

        const btnRef = el('div', { padding: '8px 15px', cursor: 'pointer' }, '🔄 立即刷新');
        const btnSet = el('div', { padding: '8px 15px', cursor: 'pointer' }, '⚙️ 设置周期');

        const hIn = (e) => e.target.style.background = '#444';
        const hOut = (e) => e.target.style.background = 'transparent';
        btnRef.onmouseover = hIn; btnRef.onmouseout = hOut;
        btnSet.onmouseover = hIn; btnSet.onmouseout = hOut;

        menu.appendChild(btnRef); menu.appendChild(btnSet);
        document.body.appendChild(menu);

        panel.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            menu.style.display = 'block';
            menu.style.left = e.clientX + 'px';
            menu.style.top = e.clientY + 'px';
        });
        document.addEventListener('click', () => menu.style.display = 'none');

        btnRef.addEventListener('click', fetchUsage);
        btnSet.addEventListener('click', () => {
            const cur = localStorage.getItem('gemini_usage_interval') || 10;
            const ipt = prompt('请输入采集周期 (秒):', cur);
            if (ipt && !isNaN(ipt) && parseInt(ipt) > 0) {
                pollInterval = parseInt(ipt);
                localStorage.setItem('gemini_usage_interval', pollInterval);
                startPolling();
            }
        });
    }

    // --- 3. Observer 防擦除守护 ---
    const observer = new MutationObserver(() => {
        if (document.body && !document.getElementById(PANEL_ID)) {
            initUI();
            if(shortValNode) fetchUsage(); // 重建后立刻刷新一次数据
        }
    });

    // --- 4. 核心逻辑 ---
    function formatTime(ts) {
        if(!ts) return '--';
        return new Date(ts * 1000).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute:'2-digit' });
    }

    function getToken() {
        try { if (win.WIZ_global_data?.SNlM0e) return win.WIZ_global_data.SNlM0e; } catch(e) {}
        const match = document.documentElement.outerHTML.match(/"SNlM0e":"([^"]+)"/);
        return match ? match[1] : null;
    }

    async function fetchUsage() {
        if (!shortValNode || !longValNode) return;
        const token = getToken();
        if (!token) {
            if (statusNode) { statusNode.style.color = '#ff8a65'; statusNode.title = 'Token 提取失败'; }
            return;
        }

        if (statusNode) statusNode.style.color = '#aecbfa';

        const payload = new URLSearchParams({ 'f.req': '[[["jSf9Qc","[]",null,"generic"]]]', 'at': token });

        try {
            const res = await win.fetch(realApiUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }, body: payload.toString() });
            let text = await res.text();
            if (text.startsWith(")]}'")) text = text.substring(4);

            const lines = text.split('\n');
            let dataFound = false;

            for (let line of lines) {
                if (line.includes('jSf9Qc') && line.includes('wrb.fr')) {
                    const parsedLine = JSON.parse(line);
                    const quotas = JSON.parse(parsedLine[0][2])[1];

                    let shortTerm = null, longTerm = null;
                    for (let q of quotas) {
                        if (q[2] === 1) shortTerm = q;
                        if (q[2] === 2) longTerm = q;
                    }

                    if (shortTerm) {
                        shortValNode.textContent = (shortTerm[1] * 100).toFixed(2) + '%';
                        shortTimeNode.textContent = '刷新: ' + formatTime(shortTerm[3][0][0]);
                    }
                    if (longTerm) {
                        longValNode.textContent = (longTerm[1] * 100).toFixed(2) + '%';
                        longTimeNode.textContent = '刷新: ' + formatTime(longTerm[3][0][0]);
                    }

                    dataFound = true;
                    break;
                }
            }

            if (statusNode) {
                statusNode.style.color = dataFound ? '#00cc00' : '#ff8a65';
                statusNode.title = dataFound ? `同步成功: ${new Date().toLocaleTimeString()}` : '数据异常 (可能需对话一次以唤醒)';
            }
        } catch (error) {
            if (statusNode) { statusNode.style.color = '#ff3333'; statusNode.title = '网络连接失败'; }
        }
    }

    function startPolling() {
        if (pollingTimer) clearInterval(pollingTimer);
        pollingTimer = setInterval(fetchUsage, pollInterval * 1000);
    }

    // 初始化器
    function boot() {
        if (document.body) {
            initUI();
            observer.observe(document.body, { childList: true, subtree: true });
            fetchUsage();
            startPolling();
        } else {
            requestAnimationFrame(boot);
        }
    }

    boot();

})();



展示图片:

搜狗截图20260629085607.png (250.7 KB, 下载次数: 2)

脚本界面展示

脚本界面展示

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
af8889 发表于 2026-6-29 11:52
BZHD不知道是做啥用的……
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-30 07:11

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表