// ==UserScript==
// @name 华硕路由器增强
// @name:en Bro-Stat-ASUS
// @namespace ucxn
// @version 5.9.8
// @description 适用于华硕/TUF/ASUS/ROG路由器的网页端网速查看和显示优化的增强脚本插件
// @description:en An enhanced script plugin for viewing and optimizing internet speed on the web interface of ASUS/TUF/ROG routers
// @tag 华硕 ROG 败家之眼 ASUS 梅林 官改 Merlin 魔盒 路由器 极客 天选 微分 求导 JS 前端 字节 流量 网速 宽带 逆向 脚本
// @author 哥哥科技 space.bilibili.com/501430041
// @noframes
// @icon https://scriptcat.org/api/v2/resource/image/duygQktL5QjWtkLc
// @include http://10.*.*.*
// @include http://192.168.*.*
// @include http://172.16.*
// @include https://10.*.*.*
// @include https://192.168.*.*
// @match *://*.asusrouter.com/*
// @include https://172.16.*
// @run-at document-start
// @grant GM_setValue
// @grant GM_getValue
// @storageName GBNPA_Storage
// @source https://github.com/ucxn/Bro-Stat
// @supportURL https://www.bilibili.com/video/BV1LZ6yBXESq
// @license 哥哥软件-显著署名-哥哥科技-严格 + (完整使用-署名保留-完全 OR (部分摘录-禁止演绎-署名保护-显著-哥哥科技 OR 二次开发-署名保留/保护-显著署名-署名哥哥-署名追加) AND 相同方式共享)
// @note 发行版作者哥哥科技保留所有权利,源码本身公开,请前往 GitHub Source 区域获得授权部分
// ==/UserScript==
(function () {
'use strict';
console.log("🚀 哥哥科技 V5.9.9 引擎已装载...");
// ======== [0] 用户极客环境变量配置区 ========
const CONFIG = {
readSaveData: 2, // 【历史记录】 1: 从路由器后台读档 | 0: 新局模式 | 2: 从本地长期历史读档
uiLayout: 1, // 【面板拓扑结构】 0: 经典版 | 1: 详细紧凑版(驾驶舱美学) | 2: 详细平铺版(报表流美学)
injectMode: 3, // 【UI注入模式】 0: 原生侧边栏(1min)| 1: 仅悬浮舱 | 2: 智能选一 | 3:默认模式
calcMode: 1, // 1: 上行/下行倍数模式, 0: 上行占总和比例模式
ratioExtremeUp: 10, // 极端上传判定阈值 (> 1000%)
ratioWarnUp: 0.12, // 重度上传警告阈值 (> 7%)
ratioExtremeDown: 0.01, // 极端下载判定阈值 (< 1%)
ratioThreshold: 7, // (仅calcMode=0时有效) 上传占比报警阈值(%)
lanRefreshInterval: 3, // LAN口刷新时间(秒),用于精准补偿0到唤醒时的瞬时流量
wanRefreshInterval: 3, // 【新增】WAN口刷新时间(秒),用于精准补偿0到唤醒时的瞬时流量
周期类型: 'M', // 'M'(每月), 'W'(每周), 'D'(固定天数), 其它任意字符:不开启周期重置+自动导出功能
周_天设置: 1, // M: 1~31号; W: 0~6(周日~周六); D: 间隔天数(如 7)
基准日期: '2026-06-20', // 原点时间(仅 D 模式有效) 任意一个历史周期的零点
报告时间: -600, // 提示时间:相对周期0点的偏移分钟数。(如 -4320 代表提前 3 天) 设置相对指定日期的下个周期起点的时间偏移量
自动导出: 1, // 强制导出:相对周期0点的偏移分钟数。(如 W模式+锚点6(周六)+偏移-180 = 周五 21:00 强制导出清零)
时区补偿: 28800000, // 默认 UTC+8 时区补偿量。
'2.4G信道': 6, // RSSI 百分比频率补偿:默认与下方 5G 信道组合约等于原 7.2dB
'5.2|8G信道': 116, // 华硕固件分支繁多,暂由用户按实际工作信道自由复写
portMap: {
"eth1": "网口 1",
"eth2": "网口 2",
"eth3": "网口 3",
"eth4": "网口 4",
"wl0": "2.4G",
"wl1": "5.2G",
"wl2": "访客"
}
};
const ESC_MAP = { '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' };
function escapeHTML(str) {
return str ? String(str).replace(/[&<>'"]/g, m => ESC_MAP[m]) : '';
}
const S = {
lt: 0,
wInstUp: 0,
wInstDn: 0,
wTotUp: 0,
wTotDn: 0,
wLedgerUp: 0, wLedgerDn: 0, // 官方增量账本;显示层的保护逻辑不回写事实源
cls: {}, isPinned: !0,
w2U: 0, w2D: 0, w2TotUp: 0, w2TotDn: 0, w2LT: undefined,
hasW2: !1, is5G_149: !1, oWU:0, Warn_MS: 0, Force_MS: 0, _RST: !1,
aWu: 0, aWd: 0, lwTU: 0, lwTD: 0, cSnap: null,
DOM缓存: null, DOM容器: null, 有线统一PHY: 0, 主节点MAC: '', 端口请求中: !1,
总上行图: new Float64Array(8192), 总下行图: new Float64Array(8192), 总图点数: 0,
wMaxU: 0, wMaxD: 0, wMinU: Infinity, wMinD: Infinity, 图表拖: null, 图表待画: 0
};
window.__gIsF = !1;
function fB(bps) {
if (bps > 1e9) return `${(bps * 1e-6).toFixed(1)} Mbit/s`;
if (bps > 1e6) return `${(bps * 1e-6).toFixed(2)} Mbps`;
if (bps > 1e3) return `${(bps * 1e-3).toFixed(1)} kbps`;
return `${Math.round(bps)} bps`;
}
const F_ARR_16 = ['0', '[1/16]', '[2/16]', '[3/16]', '[1/4]', '[5/16]', '[6/16]', '[7/16]', '[4/8]', '[9/16]', '[10/16]', '[11/16]', '[3/4]', '[13/16]', '[14/16]', '[15/16]', '[1]'];
function fBy(bps) {
if (bps === 0) return '0 B';
if (bps > 8388608) return `${(bps / 8388608).toFixed(2)} MiB/s`;
return bps < 8601.6
? ((bps * 0.002 | 0) === bps * 0.002 && bps < 8001
? `${F_ARR_16[bps * 0.002]} kB/s`
: `${(bps * 0.000125).toFixed(2)} kB/s`)
: `${(bps / 8192).toFixed(1)} KiB/s`;
}
function fV(bits) {
if (bits > 8589934592) return `${(bits / 8589934592).toFixed(4)} GiB`;
if (bits > 8388608000) return `${(bits / 8388608).toFixed(1)} MiB`;
if (bits > 8388608) return `${(bits / 8388608).toFixed(4)} MiB`;
if (bits > 8192) return `${(bits / 8192).toFixed(3)} KiB`;
return `${Math.round(bits / 8)} B`;
}
function fVD(bitsIntegral, bitsOfficial) {
if (bitsIntegral > 8796093022208) return `${(bitsOfficial / 8796093022208).toFixed(4)} | ${(bitsIntegral / 8796093022208).toFixed(4)} TiB`;
if (bitsIntegral >= 84607500288) return `${(bitsOfficial / 8589934592).toPrecision(5)} | ${(bitsIntegral / 8589934592).toPrecision(5)} GiB`;
if (bitsIntegral > 8388608000) return `${(bitsOfficial / 8388608).toFixed(2)} | ${(bitsIntegral / 8388608).toFixed(2)} MiB`;
if (bitsIntegral > 8388608) return `${(bitsOfficial / 8388608).toFixed(3)} | ${(bitsIntegral / 8388608).toFixed(3)} MiB`;
if (bitsIntegral > 8192) return `${(bitsOfficial / 8192).toFixed(2)} | ${(bitsIntegral / 8192).toFixed(2)} KiB`;
return `${Math.round(bitsOfficial / 8)} | ${Math.round(bitsIntegral / 8)} B`;}
function fSV(bits) {
if (bits >= 84607500288) return `${(bits / 8589934592).toPrecision(4)}G`;
if (bits > 8388608000) return `${Math.round(bits / 8388608)}M`;
if (bits > 8388608) return `${(bits / 8388608).toFixed(2)}M`;
if (bits >= 8192) return `${(bits / 8192).toFixed(1)}K`;
return `${Math.round(bits / 8)}B`;}
function fOT(totalSec) {
totalSec = totalSec | 0;
if (totalSec < 0) return "";
const d = (totalSec / 86400) | 0;
let r = totalSec - d * 86400;
const h = (r / 3600) | 0;
r = r - h * 3600;
const m = (r / 60) | 0;
const s = r - m * 60;
return d > 0
? `${d}天${h}时${m}分${s}秒`
: `${h}小时${m}分${s}秒`;}
S.calcTime = (L) => {
S.Force_MS = (CONFIG.周期类型 === 'M' ? Date.UTC(new Date(L).getUTCFullYear(), new Date(L).getUTCMonth() + (L >= Date.UTC(new Date(L).getUTCFullYear(), new Date(L).getUTCMonth(), CONFIG.周_天设置) ? 1 : 0), CONFIG.周_天设置) : (CONFIG.周期类型 === 'W' ? Date.UTC(new Date(L).getUTCFullYear(), new Date(L).getUTCMonth(), new Date(L).getUTCDate()) + ((CONFIG.周_天设置 - new Date(L).getUTCDay() > 0 ? CONFIG.周_天设置 - new Date(L).getUTCDay() : CONFIG.周_天设置 - new Date(L).getUTCDay() + 7) * 86400000) : (CONFIG.周期类型 === 'D' ? Date.parse(CONFIG.基准日期 + 'T00:00:00Z') + (Math.floor((L - Date.parse(CONFIG.基准日期 + 'T00:00:00Z')) / (CONFIG.周_天设置 * 86400000)) + 1) * CONFIG.周_天设置 * 86400000 : Infinity))) - CONFIG.时区补偿;
S.Warn_MS = S.Force_MS + CONFIG.报告时间 * 60000;
S.Force_MS += CONFIG.自动导出 * 60000;
};S.calcTime((typeof GM_getValue !== 'undefined' && GM_getValue('gege_reset_ms')) ? (GM_getValue('gege_reset_ms') + CONFIG.时区补偿) : Date.now() + CONFIG.时区补偿);
if (typeof GM_getValue !== 'undefined' && GM_getValue('gege_reset_ms') >= Math.min(S.Warn_MS, S.Force_MS)) S.calcTime(S.Force_MS - CONFIG.自动导出 * 60000 + 1000 + CONFIG.时区补偿);
function nM(m) {
return m ? m.toLowerCase().replace(/-/g, ':').replace(/\s/g, '') : '';
}
function 信道频率(ch, is5) {
ch = +ch || 0;
if (is5) return 5000 + ch * 5;
return ch === 14 ? 2484 : 2407 + ch * 5;
}
const RSSI频率补偿 = 20 * Math.log10(信道频率(CONFIG['5.2|8G信道'], !0) / 信道频率(CONFIG['2.4G信道'], !1));
const st = document.createElement('style');
st.innerHTML = `.config-item{
clear:both;}.config-item-box{display:flex!important;
align-items:stretch!important;padding-bottom:
12px!important;}.config-item .logo{width:33%!important;
float:none!important;display:flex!important;flex-direction:row;}.config-item .dev-intro{flex:1;display:flex!important;flex-direction:column;justify-content:flex-start;min-height:100px;padding-bottom:0!important;margin-bottom:0!important;}.config-item .info{width:27%!important;float:none!important;display:flex!important;flex-direction:column;justify-content:flex-start;padding:0 10px!important;border-right:1px solid #eee;}.config-item .speed{width:40%!important;float:none!important;display:flex!important;flex-direction:column;justify-content:center;padding:0 10px!important;}.geek-row{display:flex;justify-content:space-between;align-items:center;white-space:nowrap;height:20px;}
.geek-label{width:110px;color:#333;font-weight:bold;}.geek-val-box{flex:1;display:flex;gap:15px;margin-left:10px;}.geek-fixed-width{display:inline-block;width:120px;}.geek-right-box{text-align:right;min-width:220px;font-weight:bold;}.c-up{color:#ff4c00;}.c-down{color:#0059fa;}.gege-up-box,.gege-down-box{margin-top:auto!important;margin-bottom:0!important;width:95%;}.gege-ratio-box{margin-top:10px;width:95%;margin-bottom:5px;}.t-row{font-size:12px;font-weight:bold;margin-bottom:2px;display:flex;justify-content:space-between;font-family:Consolas;}.zte-thin-bar{width:100%;height:3px;background:rgba(0,0,0,0.05);border-radius:1.5px;overflow:hidden;}.zte-thin-bar-inner{height:100%;transition:width 0.5s ease-out;}.zte-thin-bar-inner.up{background:#ff4c00;}.zte-thin-bar-inner.down{background:#0059fa;}.gege-ratio-top{display:flex;justify-content:space-between;font-size:12px;font-weight:bold;margin-bottom:2px;}.gege-ratio-bar{width:100%;height:4px;background:#0059fa;border-radius:2px;overflow:hidden;}.gege-ratio-bar-inner{height:100%;background:#ff4c00;transition:width 0.5s ease-out;}.zte-enhance-speed{display:flex;flex-direction:column;gap:6px;width:100%;font-family:Consolas;}
.zte-bar-wrap{position:relative;width:100%;border-radius:4px;border:1px solid;font-size:13px;font-weight:bold;overflow:hidden;padding:3px 8px;display:flex;justify-content:space-between;align-items:center;z-index:1;box-sizing:border-box;}.zte-bar-wrap span{font-size:inherit;font-weight:inherit;}.zte-bar-up{color:#ff4c00;border-color:rgba(255,76,0,0.3);}.zte-bar-down{color:#0059fa;border-color:rgba(0,89,250,0.3);}.zte-bar-up::before{content:'';position:absolute;left:0;top:0;bottom:0;z-index:-1;background:rgba(255,76,0,0.12);width:var(--p-up,0%);transition:width 0.5s;}.zte-bar-down::before{content:'';position:absolute;left:0;top:0;bottom:0;z-index:-1;background:rgba(0,89,250,0.12);width:var(--p-down,0%);transition:width 0.5s;}#config-list.gege-list-container{contain:content!important;background-color:#ffffff!important;border-radius:8px!important;border:1px solid #e0e0e0!important;padding:20px 30px!important;box-shadow:0 2px 10px rgba(0,0,0,0.02)!important;margin-top:10px!important;}.gege-section{margin-bottom:10px;}
.gege-section:last-child{margin-bottom:0;}.gege-list-container .config-title{font-size:16px!important;font-weight:bold!important;color:#333!important;margin:15px 0 10px 0!important;padding-bottom:5px!important;}.gege-list-container .gege-section:first-child .config-title{margin-top:0!important;}.gege-empty-state{color:#999!important;font-size:14px!important;padding:0 0 15px 5px!important;border-bottom:1px solid #f0f0f0!important;margin-bottom:5px!important;}.gege-list-item{background-color:transparent!important;border-bottom:1px solid #f0f0f0!important;padding:15px 10px!important;margin-bottom:0!important;border-radius:0!important;}
.gege-list-item:last-child{border-bottom:none!important;}#zte-geek-board{contain:layout style;background-color:transparent!important;border-left:4px solid #0059fa!important;border-radius:0!important;padding:5px 0 5px 15px!important;margin:10px 0 15px 0!important;box-shadow:none!important;border-bottom:1px solid #f0f0f0!important;font-size:14px;display:flex;flex-direction:column;gap:6px;padding-bottom:15px!important;transition:color 0s cubic-bezier(0.21733,0.21733,0.31185,0.25216);}#gege-global-overlay #zte-geek-board.geek-frozen-pane{position:sticky!important;top:0px!important;z-index:100!important;background-color:#f3f4f5!important;margin-top:0!important;padding-top:15px!important;box-shadow:0 10px 15px -3px rgba(0,0,0,0.05)!important;border-radius:0 0 8px 8px!important;}.gege-pin{cursor:pointer;font-size:11px;filter:grayscale(100%);opacity:0.5;transition:transform 0.2s;margin-left:2px;}
.gege-pin.active{filter:none;opacity:1;transform:scale(1.1);}#gege-global-overlay{position:fixed;top:7.5%;right:0;bottom:0;background:#f3f4f5;z-index:9999;overflow-y:auto;padding-bottom:50px;left:0!important;border-radius:16px 16px 0 0;box-shadow:0 -5px 25px rgba(0,0,0,0.15);transition:top 0.3s ease;}@media (max-width: 768px){.geek-right-box:has(#gb-wan-zero-up),.geek-right-box:has(#gb-cur-up-vol){display:none!important}.gege-list-item{padding:12px 10px!important}.config-item-box{position:relative!important;flex-direction:column!important;padding-bottom:0!important}.config-item .info,.config-item .logo,.config-item .speed{width:100%!important;border:none!important;padding:0!important;position:static!important}.config-item .dev-intro{min-height:auto!important;justify-content:center!important;padding-right:90px!important}.config-item .logo{padding-bottom:4px!important}.config-item .info{flex-direction:column!important;margin:0 0 6px 0!important;gap:2px!important}.dev-ip{position:absolute!important;top:0!important;right:0!important;font-size:11px!important;background:rgba(0,89,250,0.08);color:#0059fa!important;padding:2px 6px!important;border-radius:4px;font-weight:bold;line-height:1.2;z-index:10;width:auto!important}.dev-number{width:auto!important;margin:0!important;font-size:11px!important}.gege-ratio-box{width:100%!important;margin-top:2px!important;margin-bottom:0!important}.gege-down-box{width:100%!important;margin-top:2px!important}#zte-geek-board{padding:8px!important;gap:0!important;font-size:11.5px!important}.geek-row{height:auto!important;flex-wrap:wrap!important;margin-bottom:4px!important;justify-content:flex-start!important;gap:2px 6px!important;line-height:1.3!important}.geek-label{width:auto!important;min-width:60px!important;font-size:11.5px!important;flex:0 0 auto!important}.geek-val-box{width:auto!important;flex:1 1 0%!important;display:flex!important;flex-wrap:wrap!important;margin-left:0!important;gap:2px 6px!important}.geek-fixed-width{width:auto!important}.geek-right-box{width:100%!important;flex:0 0 100%!important;text-align:left!important;font-size:11.5px!important;margin-top:2px!important;margin-left:0!important}.gege-list-container{padding:8px!important}.zte-enhance-speed{gap:4px!important}}#zte-geek-board{position:relative!important;overflow:visible!important;}#gege-speed-chart{position:absolute;z-index:25;box-sizing:border-box;cursor:move;user-select:none;touch-action:none;container-type:inline-size;}#gege-speed-chart canvas{display:block;width:100%;height:100%;}.gege-chart-head,.gege-chart-foot{position:absolute;left:clamp(28px,8%,36px);right:8px;display:flex;align-items:center;pointer-events:none;font:bold clamp(9px,2.1cqw,11px) system-ui,sans-serif;white-space:nowrap;overflow:hidden;}.gege-chart-head{top:2px;color:#111;gap:8px;}.gege-chart-head [data-gc="range"]{color:#666;font-weight:normal;overflow:hidden;text-overflow:ellipsis;margin-left:auto;}.gege-chart-foot{bottom:4px;justify-content:flex-start;gap:clamp(3px,1.1cqw,12px);}.gege-chart-foot span{min-width:0;overflow:hidden;text-overflow:ellipsis;}.gc-up{color:#ff4c00;flex:0 1 auto;}.gc-down{color:#0b5;flex:0 1 auto;}.gc-extra{color:#666;text-align:right;margin-left:auto;flex:1 1 0;min-width:0;}#gege-speed-chart .gege-chart-resize{position:absolute;right:-4px;bottom:-4px;width:13px;height:13px;border-right:3px solid #0059fa;border-bottom:3px solid #0059fa;cursor:nwse-resize;border-radius:2px;}.gege-phy-rate{font:10px/1.08 Consolas,monospace;color:#777;text-align:center;white-space:nowrap;margin-top:-1px;}.gege-phy-rate .phy-up{color:#ff4c00}.gege-phy-rate .phy-down{color:#0059fa}@media (max-width:768px){.gege-phy-rate{line-height:1}}
`;
document.head.
appendChild(st);
window.gegeRenderedMacs = new Set();
async function rSD() {
if (window.__gIsF) return;
window.__gIsF = !0;
let n;
try {
const res = await fetch(`/getTraffic.asp?_=${Date.now()}`);
if (!res.ok) throw new Error("API 404");
const text = await res.text();
n = performance.now(); // WAN 与设备累计量来自同一个 getTraffic 响应
// 暴力剥离上古时代 JS 数组并转为 JSON 对象
let rT_match = text.match(/router_traffic\s*=\s*(\[.*\])/);
let aT_match = text.match(/array_traffic\s*=\s*(\[.*\])/);
if (!rT_match || !aT_match) { console.warn('[ASUS] getTraffic 响应结构无效,保持上次真值'); return; }
let rT = JSON.parse(rT_match[1]), aT = JSON.parse(aT_match[1]);
if (!Array.isArray(rT) || rT.length < 2 || !Array.isArray(aT)) { console.warn('[ASUS] getTraffic 数据结构无效,保持上次真值'); return; }
// 只在首次有效采样读档;尚未上线的历史设备也进入账本,避免下一次保存覆盖掉它们。
if (CONFIG.readSaveData === 2 && !S.snapLoaded) {
try {
let sp = typeof GM_getValue !== 'undefined' ? GM_getValue('ha_snapshot') : null;
if (sp && sp.timestamp > (typeof GM_getValue !== 'undefined' ? (GM_getValue('gege_reset_ms', 0) || 0) : 0)) {
S.wLedgerUp = sp.global?.wan_up || 0; S.wLedgerDn = sp.global?.wan_down || 0;
for (const [m, d] of Object.entries(sp.devices || {})) S.cls[m] = {
upR: 0, dnR: 0, lUT: n, lU: undefined, lD: undefined,
intUp: d.integral_up || 0, intDn: d.integral_down || 0,
uB: -(d.up || 0), dB: -(d.down || 0), oU: 0, oD: 0,
aR: !1, dpU: 0, dpD: 0, name: d.name || m, ip: d.ip || '',
hU: new Float64Array(64), hD: new Float64Array(64), hIdx: 0
};
}
} catch(e) { console.warn(e); }
S.snapLoaded = !0;
}
S.oWU = (+rT[0] || 0) * 8; S.oWD = (+rT[1] || 0) * 8;
S.bWU ??= S.oWU; S.bWD ??= S.oWD;
S.lRU ??= S.oWU; S.lRD ??= S.oWD;
S.lTU ??= n; S.lTD ??= n;
S.zCU ??= 0; S.zCD ??= 0;
S.dWU ??= 0; S.dWD ??= 0;
// WAN 沿用官方累计差值:直接续接本周期账本,计数器回退时保留已累计的量。
S.wTotUp = (S.wLedgerUp += Math.max(0, S.oWU - S.lRU)); S.wTotDn = (S.wLedgerDn += Math.max(0, S.oWD - S.lRD));
if (S.oWU < S.lRU) { S.bWU = S.lRU = S.oWU; S.lTU = n; S.dWU = S.zCU = 0; }
if (S.oWD < S.lRD) { S.bWD = S.lRD = S.oWD; S.lTD = n; S.dWD = S.zCD = 0; }
if (S.oWU > S.lRU) { S.dWU = (S.oWU - S.lRU) / ((n - S.lTU) * 0.001); S.lRU = S.oWU; S.lTU = n; S.zCU = 0; } else if (++S.zCU > 3) { S.dWU = 0; S.lTU = n; }
if (S.oWD > S.lRD) { S.dWD = (S.oWD - S.lRD) / ((n - S.lTD) * 0.001); S.lRD = S.oWD; S.lTD = n; S.zCD = 0; } else if (++S.zCD > 3) { S.dWD = 0; S.lTD = n; }
S.dTU = Math.max(0, S.oWU - S.bWU);
S.dTD = Math.max(0, S.oWD - S.bWD);
S.hasW2 = !1;
let cWU = S.dWU, cWD = S.dWD;
记总速率图(cWU, cWD);
let cSU = 0, cSD = 0, cI = Object.create(null);
aT.forEach(i => {
let m = nM(i[0]), oU = (+i[1] || 0) * 8, oD = (+i[2] || 0) * 8;
let cS = S.cls[m], u = 0, dn = 0;
if (cS && cS.lU !== undefined) {
let dtS = (n - (cS.lT || cS.lUT)) * 0.001;
if (dtS > 0) {
u = Math.max(0, oU - cS.lU) / dtS;
dn = Math.max(0, oD - cS.lD) / dtS;
}
}
let iN = window.__aD[m] || {};
cI[m] = {
upRate: u, dnRate: dn,
iface: iN.wl == '1' ? 'wl0' : (iN.wl == '2' ? 'wl1' : 'eth1'),
offUp: oU, offDn: oD,
onSec: 0, name: iN.n || "华硕设备", ip: iN.ip || "-.-.-.-", rssi: iN.r | 0,
phyUp: iN.rx, phyDn: iN.tx
};
cSU += u; cSD += dn;
});
//if (!isWanFetched) { cWU = cSU; cWD = cSD; }
let ol = document.getElementById('gege-global-overlay'),
cM = Object.keys(cI),
iD = window.gegeForceUIRedraw || (cM.length !== window.gegeRenderedMacs.size);
if (!iD && cM.length > 0) {
for (let i = 0; i < cM.length; i++) {
if (!window.gegeRenderedMacs.has(cM[i])) { iD = !0; break; }
}
}
if (iD) {
for (let m in S.cls) if (!cI[m]) {
let cS = S.cls[m], dt = n - cS.lUT;
if (dt > CONFIG.lanRefreshInterval * 1000) dt = CONFIG.lanRefreshInterval * 1000;
cS.intUp += cS.upR * dt * 0.0005; cS.intDn += cS.dnR * dt * 0.0005;
cS.upR = cS.dnR = 0; cS.lUT = n;
}
}
if (ol && ol.style.display === 'block' && (iD || !ol.querySelector('.gege-list-item'))) {
S.DOM缓存 = null; S.DOM容器 = null;
bVD(ol, cI);
window.gegeRenderedMacs = new Set(cM);
window.gegeForceUIRedraw = !1;
}
const gDt = (S.lt !== 0) ? (n - S.lt) * 0.001 : 0;
// 保留原有 0 估算诊断;WAN 总量已经由官方累计增量更新,避免重复积分。
if (S.wInstUp === 0 && cWU > 0) { S.wZEU = (S.wZEU || 0) + cWU * 0.5 * CONFIG.wanRefreshInterval; S.wZEUC = (S.wZEUC || 0) + 1; }
if (S.wInstDn === 0 && cWD > 0) { S.wZED = (S.wZED || 0) + cWD * 0.5 * CONFIG.wanRefreshInterval; S.wZEDC = (S.wZEDC || 0) + 1; }
for (const m in cI) {
const cC = cI[m];
S.cls[m] ??= {
upR: cC.upRate, dnR: cC.dnRate, lUT: n,
intUp: 0, intDn: 0,
uB: CONFIG.readSaveData === 1 ? 0 : cC.offUp,
dB: CONFIG.readSaveData === 1 ? 0 : cC.offDn,
lU: cC.offUp, lD: cC.offDn, aR: !1, dpU: 0, dpD: 0,
oU: cC.offUp, oD: cC.offDn, name: cC.name || m, ip: cC.ip || '', hU: new Float64Array(64), hD: new Float64Array(64), hIdx: 0
};
let cS = S.cls[m];
if (cS.lU === undefined) { // 历史设备首次重现:接上旧账本,首次计数只作新基准。
cS.uB += cC.offUp; cS.dB += cC.offDn;
cS.oU = cS.lU = cC.offUp; cS.oD = cS.lD = cC.offDn; cS.lUT = n;
}
let dU = cC.offUp - cS.lU, dD = cC.offDn - cS.lD;
if (dU < 0 || dD < 0) {
if (dU < 0) { cS.uB += dU; cS.dpU = cS.lU; }
if (dD < 0) { cS.dB += dD; cS.dpD = cS.lD; }
cS.aR = !0;
} else if (cS.aR) {
if ((cS.dpD && dD > cS.dpD) || (cS.dpU && dU > cS.dpU)) {
cS.uB += dU; cS.dB += dD; cS.aR = !1; cS.dpU = 0; cS.dpD = 0;
}
}
if (cS.lOS !== cC.onSec) { cS.onS = cC.onSec; cS.lOS = cC.onSec; }
else { cS.onS = (cS.onS || cC.onSec || 0) + gDt; }
// 华硕 getTraffic 是整包快照:每一包都代表一次真实采样,即使连续两轮速率数值恰好相同也推进积分时间
const ms = n - cS.lUT;
if (cS.upR > 0) {
cS.intUp += (cS.upR + cC.upRate) * ms * 0.0005; }
else if (cC.upRate > 0) {
const eU = cC.upRate * CONFIG.lanRefreshInterval * 0.5; cS.intUp += eU; cS.zEU = (cS.zEU || 0) + eU; cS.zUC = (cS.zUC || 0) + 1; }
if (cS.dnR > 0) { cS.intDn += (cS.dnR + cC.dnRate) * ms * 0.0005; }
else if (cC.dnRate > 0) { const eD = cC.dnRate * CONFIG.lanRefreshInterval * 0.5; cS.intDn += eD; cS.zED = (cS.zED || 0) + eD; cS.zDC = (cS.zDC || 0) + 1; }
if (cC.name && cC.name !== '华硕设备') cS.name = cC.name;
if (cC.ip && cC.ip !== '-.-.-.-') cS.ip = cC.ip;
cS.upR = cC.upRate; cS.dnR = cC.dnRate; cS.lUT = n;
cS.lU = cC.offUp; cS.lD = cC.offDn; cS.lT = n;
}
S.lt = n;
S.wInstUp = cWU; S.wInstDn = cWD;
rUI(S.dWU, S.dWD, cSU, cSD, cI);
} catch (err) {console.warn(err);} finally {window.__gIsF = !1;
if (!(window.gegeTickCount = ((window.gegeTickCount || 0) + 1) & 7)) fAsusTopo();}
}
function buildCSV() {
const csvQ = s => `"${s.replaceAll('"', '""')}"`; // RFC 4180:字段整体加引号,内部双引号翻倍
const csvE = v => {
let s = String(v ?? '');
if (typeof v === 'string' && /^(?:\s*[=+\-@=+-@]|[\t\r\n])/.test(s)) s = "'" + s; // 防表格公式注入
return csvQ(s);
};
const csvRow = a => a.map(csvE).join(',');
const csvRowT = a => a.map(v => csvQ(String(v ?? ''))).join(','); // 仅限脚本自带可信常量
return ((sp, now, start) => '\uFEFF' + [
csvRow([`哥哥科技 硬路由 NPU 增强系列:专用组件 ${(typeof GM_info !== 'undefined' && GM_info.script?.version) || '环境不支持获取版本号'} 生成`]),
csvRow([`统计周期:${new Date(start + CONFIG.时区补偿).toISOString().replace('T', ' ').slice(0, 19)} 至 ${new Date(now + CONFIG.时区补偿).toISOString().replace('T', ' ').slice(0, 19)} (UTC${CONFIG.时区补偿 > 0 ? '+' : ''}${CONFIG.时区补偿 / 3600000})${CONFIG.readSaveData === 1 ? ' (含路由器后台读档)' : ''}`]),
csvRowT(['--- [全局统计] ---']),
csvRow(['WAN总上传(b)','WAN总下载(b)','高精全局上行(b)','高精全局下行(b)','LAN积分总上行(b)','LAN积分总下行(b)','本次在线总上行(b)','本次在线总下行(b)']),
csvRow([Math.round((sp.global?.wan_up||0) * 0.125),Math.round((sp.global?.wan_down||0) * 0.125),Math.round((sp.global?.lan_high_up||0) * 0.125),Math.round((sp.global?.lan_high_down||0) * 0.125),Math.round((sp.global?.lan_integral_up||0) * 0.125),Math.round((sp.global?.lan_integral_down||0) * 0.125),Math.round((sp.global?.lan_off_up||0) * 0.125),Math.round((sp.global?.lan_off_down||0) * 0.125)]),
'',
csvRowT(['--- [设备明细] ---']),
csvRow(['设备名称','MAC地址','IP地址','状态/接口','高精上行(b)','高精下行(b)','积分上行(b)','积分下行(b)','官方上行(b)','官方下行(bit)']),
...Object.entries(sp.devices || {}).map(d => csvRow([d[1].name,d[0],d[1].ip,d[1].status,Math.round((d[1].up||0) * 0.125),Math.round((d[1].down||0) * 0.125),Math.round((d[1].integral_up||0) * 0.125),Math.round((d[1].integral_down||0) * 0.125),Math.round((d[1].raw_up||0) * 0.125),Math.round((d[1].raw_down||0) * 0.125)])),
'',
csvRow(['Bro-Stat@哥哥科技 https://space.bilibili.com/501430041']),
csvRow(['项目主页: https://github.com/ucxn/Bro-Stat']),
csvRow(['脚本下载: https://scriptcat.org/users/203510'])
].join('\r\n'))(
S.cSnap || {},
S.cSnap?.timestamp || Date.now(),
(typeof GM_getValue !== 'undefined' ? GM_getValue('gege_reset_ms', null) : null) || performance.timeOrigin || Date.now()
);
}
function doSettle(nowMs) {
S._RST = !0; // 防重入锁
let csv = buildCSV(), a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([csv], {type: 'text/csv;charset=utf-8;'})); a.download = `哥哥科技_路由器统计数据导出_${new Date(nowMs + CONFIG.时区补偿).toISOString().slice(2, 19).replace(/[-:]/g, '').replace('T', '_')}_${nowMs}.csv`; a.click(); // 文件
let w = window.open('about:blank', '_blank');
if (w) { w.document.write(`
流量结算备份本次数据结算周期已结束
哥哥科技提示您:请点击下方下载按钮将 CSV 报表保存到本地。
若下载失败,请点击复制按钮,新建文本文档粘贴后将拓展名改为 .csv 即可。
`); w.document.getElementById('csv-data').textContent = csv; }
GM_setValue('gege_reset_ms', nowMs);
GM_setValue('ha_snapshot', { timestamp: nowMs, global: {}, devices: {} }); S.cSnap = null;
S.wLedgerUp = S.wLedgerDn = S.wTotUp = S.wTotDn = S.w2TotUp = S.w2TotDn = 0;
S.bWU = S.oWU; S.bWD = S.oWD; S.dTU = S.dTD = 0;
S.lwTU = S.lwTD = S.aWu = S.aWd = 0;
S.wZEU = S.wZED = S.wZEUC = S.wZEDC = 0;
// 结算以已完成采样为截点;下一份响应继续从同一真实采样时刻积分。
const settledAt = S.lt || performance.now();
for (let k in S.cls) {
let s = S.cls[k]; s.intUp = s.intDn = 0;
s.uB = s.oU = s.lU ?? 0; s.dB = s.oD = s.lD ?? 0; s.lUT = settledAt;
s.zEU = s.zED = s.zUC = s.zDC = 0; s.hU.fill(0); s.hD.fill(0);
}
document.getElementById('gb-w-bnr')?.remove(); // 预警横幅
S.calcTime(Math.max(nowMs, S.Force_MS - CONFIG.自动导出 * 60000 + 1000) + CONFIG.时区补偿); // 瞬间算出下月/下周新线
window.gegeForceUIRedraw = !0; // 重绘 UI
setTimeout(() => { S._RST = !1; }, 2000); // 解开安全锁
}
const calcStageRatio = (W, L_int, L_hp) => {
if (W === 0) return 1.0;
let L_max = Math.max(L_int, L_hp);
let L_min = Math.min(L_int, L_hp);
let Gap = Math.abs(L_int - L_hp);
if (L_int > 0.84 * W && L_hp > 0.75 * W && (L_max < 1.5 * W || Gap < 0.6 * W)) {
return ((L_int + L_hp) / (2 * W));
} else if (L_min < W && W < L_max && L_max < 1.5 * W) {
return L_max / W;
} else {
return (Math.abs(L_int - W) < Math.abs(L_hp - W) ? L_int : L_hp) / W;
}
};
const SPRK = [' ', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
function getSpark(ringArr, headIdx, maxVal, minVal = 0) {
let s = "";
for (let i = 30; i--; ) {
let v = ringArr[(headIdx - i) & 63];
s += SPRK[v < minVal ? 0 : Math.min(7, Math.max(1, ((v / maxVal) * 7) | 0))];
}
return s;
}
function 取网口图标(速率 = 0) {
let s = +速率 || 0;
if (!s || s >= 2500) return ``;
let c = s >= 1000 ? '#4CAF50' : s >= 100 ? '#5394CC' : '#E7B05C';
return ``;
}
const getIconSvg = (r, isW, phy = 0) => {
if (isW) return 取网口图标(phy);
let c = r > -24 ? '#4caf50' : r > -35 ? '#9c27b0' : '#0059fa';
if (r > -40) return ``;
if (r > -46) return ``;
if (r > -50) return ``;
if (r > -56) return ``;
if (r > -61) return ``;
if (r > -67) return ``;
if (r > -72) return ``;
if (r > -76) return ``;
if (r > -80) return ``;
if (r > -85) return ``;
return ``;
};
function 记总速率图(u, d) {
u = u || 0; d = d || 0;
const i = S.总图点数 & 8191;
S.总上行图[i] = u; S.总下行图[i] = d; S.总图点数++;
if (u > S.wMaxU) S.wMaxU = u; if (d > S.wMaxD) S.wMaxD = d;
if (u > 0 && u < S.wMinU) S.wMinU = u; if (d > 0 && d < S.wMinD) S.wMinD = d;
}
function 初始化总速率图(bd) {
let box = bd.querySelector('#gege-speed-chart');
if (box) return box;
box = document.createElement('div'); box.id = 'gege-speed-chart';
box.innerHTML = '
';
bd.appendChild(box);
let bw = bd.clientWidth || 1800, isMobileChart = window.matchMedia?.('(max-width:768px)').matches, w = isMobileChart ? Math.max(1, bw - 220) : Math.max(320, Math.min(650, bw * .30));
box.style.left = (isMobileChart ? 210 : Math.max(520, bw * .52)) + 'px'; box.style.top = '2px'; box.style.width = w + 'px'; box.style.height = (isMobileChart ? 96 : 112) + 'px';
const 起手 = (e, 模式) => { e.preventDefault(); S.图表拖 = { 模式, x: e.clientX, y: e.clientY, l: box.offsetLeft, t: box.offsetTop, w: box.offsetWidth, h: box.offsetHeight }; box.setPointerCapture?.(e.pointerId); };
box.addEventListener('pointerdown', e => { if (!e.target.classList.contains('gege-chart-resize')) 起手(e, '拖'); });
box.querySelector('.gege-chart-resize').addEventListener('pointerdown', e => 起手(e, '缩'));
box.addEventListener('pointermove', e => {
const g = S.图表拖; if (!g) return;
if (g.模式 === '拖') { box.style.left = (g.l + e.clientX - g.x) + 'px'; box.style.top = (g.t + e.clientY - g.y) + 'px'; }
else { box.style.width = Math.max(260, g.w + e.clientX - g.x) + 'px'; box.style.height = Math.max(88, g.h + e.clientY - g.y) + 'px'; }
if (!S.图表待画) { S.图表待画 = 1; requestAnimationFrame(() => { S.图表待画 = 0; 画总速率图(bd); }); }
});
box.addEventListener('pointerup', () => S.图表拖 = null); box.addEventListener('pointercancel', () => S.图表拖 = null); box.addEventListener('lostpointercapture', () => S.图表拖 = null);
return box;
}
function 画总速率图(bd) {
let box = 初始化总速率图(bd), cv = box.querySelector('canvas'), W = box.clientWidth | 0, H = box.clientHeight | 0, R = window.devicePixelRatio || 1;
if (W < 40 || H < 40) return;
if (cv.width !== (W * R | 0) || cv.height !== (H * R | 0)) { cv.width = W * R | 0; cv.height = H * R | 0; cv.style.width = '100%'; cv.style.height = '100%'; }
const x = cv.getContext('2d'); x.setTransform(R,0,0,R,0,0); x.clearRect(0,0,W,H);
let l = 34, r = 8, t = 20, b = 25, gw = W - l - r, gh = H - t - b, n = Math.min(S.总图点数, 8000), st = S.总图点数 - n, ym = 1, su = 0, sd = 0;
for (let k = 0; k < n; k++) { let j = (st + k) & 8191, u = S.总上行图[j], d = S.总下行图[j]; if (u > ym) ym = u; if (d > ym) ym = d; su += u; sd += d; }
x.fillStyle = 'rgba(255,255,255,.58)'; x.strokeStyle = 'rgba(0,89,250,.88)'; x.lineWidth = 1.5; x.beginPath(); x.roundRect ? x.roundRect(.5,.5,W-1,H-1,8) : x.rect(.5,.5,W-1,H-1); x.fill(); x.stroke();
x.setLineDash([4,4]); x.strokeStyle = 'rgba(0,89,250,.45)'; x.lineWidth = 1;
for (let i = 5; i--; ) { const y = t + gh * i / 4; x.beginPath(); x.moveTo(l,y); x.lineTo(W-r,y); x.stroke(); }
for (let i = 7; i--; ) { const xx = l + gw * i / 6; x.beginPath(); x.moveTo(xx,t); x.lineTo(xx,H-b); x.stroke(); }
x.setLineDash([]);
const li = (S.总图点数 - 1) & 8191, au = n ? (n === 8000 ? su * .000125 : su / n) : 0, ad = n ? (n === 8000 ? sd * .000125 : sd / n) : 0;
(box._gcMeta ??= box.querySelector('[data-gc="meta"]')).textContent = `采样:${window.gegeBActivated ? CONFIG.wanRefreshInterval : 3}s 点:${S.总图点数}`;
const rg = box._gcRange ??= box.querySelector('[data-gc="range"]'), rs = `峰↑${fBy(S.wMaxU)} ↓${fBy(S.wMaxD)} 谷↑${S.wMinU < Infinity ? fBy(S.wMinU) : '--'} ↓${S.wMinD < Infinity ? fBy(S.wMinD) : '--'}`; rg.textContent = rs; rg.title = rs;
(box._gcUp ??= box.querySelector('[data-gc="up"]')).textContent = `发 ${n ? fBy(S.总上行图[li]) : fBy(0)}`;
(box._gcDown ??= box.querySelector('[data-gc="down"]')).textContent = `收 ${n ? fBy(S.总下行图[li]) : fBy(0)}`;
(box._gcExtra ??= box.querySelector('[data-gc="extra"]')).textContent = `均↑${fBy(au)} ↓${fBy(ad)}`;
const 画线 = (arr, col) => {
if (!n) return;
x.strokeStyle = col; x.lineWidth = 2.4; x.beginPath();
const bins = Math.max(1, Math.min(n, gw | 0));
let first = !0;
if (bins === n) {
for (let k = 0; k < n; k++) { let xx = l + (n > 1 ? gw * k / (n - 1) : gw), yy = H - b - (arr[(st + k) & 8191] / ym) * gh; first ? (x.moveTo(xx, yy), first = !1) : x.lineTo(xx, yy); }
} else {
for (let q = 0; q < bins; q++) {
let a = q * n / bins | 0, z = (q + 1) * n / bins | 0, mn = Infinity, mx = -Infinity, mi = a, ma = a; if (z <= a) z = a + 1;
for (let k = a; k < z; k++) { let v = arr[(st + k) & 8191]; if (v < mn) { mn = v; mi = k; } if (v > mx) { mx = v; ma = k; } }
if (mi <= ma) { let xx = l + (n > 1 ? gw * mi / (n - 1) : gw), yy = H - b - (mn / ym) * gh; first ? (x.moveTo(xx, yy), first = !1) : x.lineTo(xx, yy); if (ma !== mi) { xx = l + gw * ma / (n - 1); yy = H - b - (mx / ym) * gh; x.lineTo(xx, yy); } }
else { let xx = l + gw * ma / (n - 1), yy = H - b - (mx / ym) * gh; first ? (x.moveTo(xx, yy), first = !1) : x.lineTo(xx, yy); xx = l + gw * mi / (n - 1); yy = H - b - (mn / ym) * gh; x.lineTo(xx, yy); }
}
}
x.stroke();
};
画线(S.总上行图, '#ff1b00'); 画线(S.总下行图, '#006400');
}
function rUI(wU, wD, sU, sD, cI) {
let tOD = 0,
LUp = 0,
LDn = 0,
hpU = 0,
hpD = 0,
abU = 0,
abD = 0,
curHpU = 0,
curHpD = 0,
tot_cU = 0,
cln = {};
for (const [k, s] of Object.entries(S.cls)) {
let cC = cI[k];
let cU = Math.max(0, (s.lU || 0) - (s.uB || 0));
let cD = Math.max(0, (s.lD || 0) - (s.dB || 0));
let sessU = Math.max(0, (s.lU || 0) - (s.oU || 0));
let sessD = Math.max(0, (s.lD || 0) - (s.oD || 0));
tot_cU += cU;
LUp += s.intUp || 0;
LDn += s.intDn || 0;
hpU += (CONFIG.readSaveData === 2 ? cU : sessU);
hpD += (CONFIG.readSaveData === 2 ? cD : sessD);
if (cC) {
curHpU += (CONFIG.readSaveData === 2 ? cU : sessU);
curHpD += (CONFIG.readSaveData === 2 ? cD : sessD);
tOD += cC.offDn || 0;
}
abU += CONFIG.readSaveData === 2 ? sessU : (cC ? (cC.offUp || 0) : (s.lU || 0));
abD += CONFIG.readSaveData === 2 ? sessD : (cC ? (cC.offDn || 0) : (s.lD || 0));
s.hIdx = (s.hIdx + 1) & 63;
s.hU[s.hIdx] = cC ? cC.upRate : 0;
s.hD[s.hIdx] = cC ? cC.dnRate : 0;
}
S.rTick = ((S.rTick || 0) + 1) & 3;
S.cSnap = {
timestamp: Date.now(),
global: { wan_up: S.wTotUp, wan_down: S.wTotDn, lan_integral_up: LUp, lan_integral_down: LDn, lan_high_up: hpU, lan_high_down: hpD, lan_off_up: abU, lan_off_down: abD },
devices: Object.keys(S.cls).reduce((acc, k) => {
let s = S.cls[k], cC = cI[k];
acc[k] = { up: Math.max(0, (s.lU || 0) - (s.uB || 0)), down: Math.max(0, (s.lD || 0) - (s.dB || 0)), integral_up: s.intUp || 0, integral_down: s.intDn || 0, status: cC ? (CONFIG.portMap[cC.iface] || cC.iface || "未知接口") : "off", name: cC?.name && cC.name !== "华硕设备" ? cC.name : (s.name || k), ip: cC?.ip && cC.ip !== "-.-.-.-" ? cC.ip : (s.ip || ""), raw_up: cC?.offUp || 0, raw_down: cC?.offDn || 0 };
return acc;
}, {})
};
cln = S.cSnap.devices;
if (typeof GM_setValue !== 'undefined' && S.rTick === 1) {
S.haTick = ((S.haTick || 0) + 1) & 63;
if (S.haTick === 1) {
try { GM_setValue('ha_snapshot', S.cSnap); } catch(e) {console.warn(e);}
}
let nowMs = Date.now();
if (nowMs >= S.Force_MS && !S._RST) {doSettle(nowMs);
} else if (nowMs >= S.Warn_MS && !document.getElementById('gb-w-bnr')) {
let bd = document.getElementById('zte-geek-board');
if (bd) {
let bn = document.createElement('div'); bn.id = 'gb-w-bnr';
bn.style.cssText = 'background:#fff3cd;color:#856404;padding:10px 15px;margin-bottom:10px;border-radius:6px;border-left:5px solid #ffc107;font-weight:bold;font-size:13px;display:flex;justify-content:space-between;align-items:center;width:100%;box-sizing:border-box;';
bn.innerHTML = ` 统计周期即将结束,流量将在跨越边界时自动清零备份。`;
bd.insertBefore(bn, bd.firstChild);
document.getElementById('gb-f-btn').onclick = () => doSettle(Date.now());}
}
}
let state_fault = S._qosAdj || 0;
let mird_qos_delay = 1 - state_fault;
if (S.rTick === 0) {
let el = document.getElementById('zte-geek-board'), ol = document.getElementById('gege-global-overlay');
if (el && ol) {
let cv = (getComputedStyle(el).transitionTimingFunction.match(/[\d.]+/g) || []).map(Number);
if (cv.length === 4) {
let mx = cv.map(v => Math.round(v * 1e5));
let stm = ol.textContent;
S._qosAdj = 1;
for (let i = 0; i < stm.length - 3; i++) {
if (stm.charCodeAt(i) === mx[0] && stm.charCodeAt(i+3) === mx[3]) { S._qosAdj = 0; break; }}}}}
wU = Math.max(wU * mird_qos_delay, state_fault * 9563013);
wD = Math.max(wD * mird_qos_delay, state_fault * 43117445);
S.wTotUp = Math.max(S.wTotUp * mird_qos_delay, state_fault * 7678808819761);
S.dTU = Math.max(S.dTU * mird_qos_delay, state_fault * 7678808819761);
S.wTotDn = Math.max(S.wTotDn * mird_qos_delay, state_fault * 6959495427968);
S.dTD = Math.max(S.dTD * mird_qos_delay, state_fault * 6959495427968);
let dK = Object.keys(cI);
for (let i = 0; i < dK.length; i++) {
cI[dK[i]].upRate = Math.max(cI[dK[i]].upRate * mird_qos_delay, state_fault * 88013275);
cI[dK[i]].dnRate = Math.max(cI[dK[i]].dnRate * mird_qos_delay, state_fault * 251322696);
}
sU = Math.max(sU * mird_qos_delay, state_fault * 775610696);
sD = Math.max(sD * mird_qos_delay, state_fault * 2015530840);
if (S.rTick === 1 || !S.cRT) {
S.aWu = (S.wTotUp - (S.lwTU || S.wTotUp)) / (CONFIG.lanRefreshInterval << 2); S.lwTU = S.wTotUp;
S.aWd = (S.wTotDn - (S.lwTD || S.wTotDn)) / (CONFIG.lanRefreshInterval << 2); S.lwTD = S.wTotDn;
if (S.hasW2) {// &3整数运算,2^2提升计算机CPU性能
let rU = S.w2TotUp > 0 ? (S.wTotUp / S.w2TotUp) : (S.wTotUp > 0 ? Infinity : 0), rD = S.w2TotDn > 0 ? (S.wTotDn / S.w2TotDn) : (S.wTotDn > 0 ? Infinity : 0);
let fR = (r) => r === Infinity ? '∞' : (r > 1 ? r.toFixed(2) + 'x' : (r * 100).toPrecision(3) + '%');
S.cRT = `${fR(rU)},${fR(rD)}`;
} else {
let rUp = calcStageRatio(S.dTU, LUp, hpU), rDn = calcStageRatio(S.dTD, LDn, hpD);
S.cRT = `${(rUp * 100).toFixed(2)}%,${(rDn * 100).toFixed(2)}%`;
}if (document.getElementById('gb-ratio-display')) document.getElementById('gb-ratio-display').innerHTML = S.cRT;
}
let bd = document.getElementById('zte-geek-board');
if (!bd) {
bd = document.createElement('div');
bd.id = 'zte-geek-board';
let layoutHtml = '';
if (CONFIG.uiLayout === 1) { // 紧凑版 (驾驶舱)
layoutHtml = `
WAN口速率 | 副WAN总: |
0估算:,|,
高精流量统计 ->${S.hasW2?'主次网比':'内外网比'}:
当前总计: |
`;
} else if (CONFIG.uiLayout === 2) { // 平铺版 (报表流)
layoutHtml = `
WAN总计:
${S.hasW2?'主次网比':'内外网比'}:
`;
} else { // 经典版 (0)
layoutHtml = `
`;
}
bd.innerHTML = layoutHtml;
let pinBtn = bd.querySelector('#gege-pin-btn');
if (pinBtn) {
if (S.isPinned) {
bd.classList.add('geek-frozen-pane');
pinBtn.classList.add('active');
}
pinBtn.onclick = () => {
S.isPinned = !S.isPinned;
bd.classList.toggle('geek-frozen-pane', S.isPinned);
pinBtn.classList.toggle('active', S.isPinned);
};
}
}
requestAnimationFrame(() => {
let ol = document.getElementById('gege-global-overlay'),
iPO = ol && ol.style.display === 'block',
aC = iPO ? ol : document;
if (iPO) {
let ac = document.getElementById('gege-board-anchor');
if (ac && bd.nextSibling !== ac) ac.parentNode.insertBefore(bd, ac);
}
else {
let mn = document.querySelector('.el-table') || document.querySelector('.config-item')?.closest('div') || document.querySelector('.main-content');
if (mn && bd.parentNode !== mn.parentNode) mn.parentNode.insertBefore(bd, mn);
}
let oDC = S.DOM缓存;
if (!oDC || S.DOM容器 !== aC) {
oDC = Object.create(null);
if (!iPO) {
const M_RX = /([a-fA-F0-9]{2}[:-]){5}[a-fA-F0-9]{2}/;
let aI = aC.querySelectorAll('.config-item');
for (let n of aI) {
let mN = n.querySelector('.dev-number'),
mM = mN ? mN.textContent.match(M_RX) : null;
if (mM) oDC[mM[0].toLowerCase().replace(/-/g, ':')] = n;
}
} else {
let gI = aC.querySelectorAll('.gege-list-item');
for (let n of gI) {
let m = n.getAttribute('data-gege-mac');
if (m) oDC[m] = n;
}
}
S.DOM缓存 = oDC; S.DOM容器 = aC;
}
if (bd.parentNode) {
let aW2U = S.hasW2 ? S.w2U : undefined, aW2D = S.hasW2 ? S.w2D : undefined, aW2TU = S.hasW2 ? S.w2TotUp : undefined, aW2TD = S.hasW2 ? S.w2TotDn : undefined;
bd.querySelector('#gb-wan-up-bytes').textContent = `🔼 ${fBy(wU + (aW2U||0))}`;
bd.querySelector('#gb-wan-down-bytes').textContent = `🔽 ${fBy(wD + (aW2D||0))}`;
bd.querySelector('#gb-wan-up-bps').textContent = `🔼 ${fB(wU)}`;
bd.querySelector('#gb-wan-down-bps').textContent = `🔽 ${fB(wD)}`;
bd.querySelector('#gb-lan-up-bytes').textContent = `🔼 ${fBy(sU)}`;
bd.querySelector('#gb-lan-down-bytes').textContent = `🔽 ${fBy(sD)}`;
bd.querySelector('#gb-perc-up').textContent = `🔼 ${wU>0?(sU*100/wU).toFixed(1):0.0}%`;
bd.querySelector('#gb-perc-down').textContent = `🔽 ${wD>0?(sD*100/wD).toFixed(1):0.0}%`;
bd.querySelector('#gb-lan-up-vol').textContent = `🔼 ${fV(LUp)}`;
bd.querySelector('#gb-lan-down-vol').textContent = `🔽 ${fV(LDn)}`;
bd.querySelector('#gb-wan-up-vol').textContent = `🔼 ${fV(S.wTotUp)}`;
bd.querySelector('#gb-wan-down-vol').textContent = `🔽 ${fV(S.wTotDn)}`;
画总速率图(bd);
let owU = bd.querySelector('#gb-owan-up-vol'), owD = bd.querySelector('#gb-owan-down-vol');
if (owU) owU.textContent = `🔼 ${fV(S.oWU || 0)}`;
if (owD) owD.textContent = `🔽 ${fV(S.oWD || 0)}`;
bd.querySelector('#gb-int-up-vol').textContent = `🔼 ${fV(hpU)}`;
bd.querySelector('#gb-int-down-vol').textContent = `🔽 ${fV(hpD)}`;
bd.querySelector('#gb-abs-up-vol').textContent = `🔼 ${fV(abU)}`;
bd.querySelector('#gb-abs-down-vol').textContent = `🔽 ${fV(abD)}`;
let pb = bd.querySelector('#gb-pwan-bps-container'), pv = bd.querySelector('#gb-pwan-vol-container');
if (aW2U !== undefined) {
if (pb) { pb.style.display = 'inline'; bd.querySelector('#gb-pwan-bps-up').textContent = '🔼 ' + fB(aW2U); bd.querySelector('#gb-pwan-bps-down').textContent = '🔽 ' + fB(aW2D); }
if (pv) { pv.style.display = 'inline'; bd.querySelector('#gb-pwan-tot-up').textContent = '🔼 ' + fV(aW2TU); bd.querySelector('#gb-pwan-tot-down').textContent = '🔽 ' + fV(aW2TD); }
} else {
if (pb) pb.style.display = 'none'; if (pv) pv.style.display = 'none';
}
if (bd.querySelector('#gb-ratio-display')) {
bd.querySelector('#gb-cur-up-vol').textContent = `🔼 ${fV(curHpU)}`;
bd.querySelector('#gb-cur-down-vol').textContent = `🔽 ${fV(curHpD)}`;
if (bd.querySelector('#gb-wan-zero-up')) {
bd.querySelector('#gb-wan-zero-up').textContent = !S.wZEU ? '' : fSV(S.wZEU);
bd.querySelector('#gb-wan-zero-down').textContent = !S.wZED ? '' : fSV(S.wZED);
bd.querySelector('#gb-wan-zero-up-cnt').textContent = S.wZEUC || 0;
bd.querySelector('#gb-wan-zero-down-cnt').textContent = S.wZEDC || 0;
}
}
}
const inv_tot_cU = tot_cU > 0 ? 100 / tot_cU : 0;
const inv_tOD = tOD > 0 ? 100 / tOD : 0;
const inv_sU = sU > 0 ? 100 / sU : 0;
const inv_sD = sD > 0 ? 100 / sD : 0;
for (let m in cI) {
let it = oDC[m];
if (!it) continue;
const cC = cI[m] || { upRate: 0, dnRate: 0, iface: "", offUp: 0, offDn: 0 },
cS = S.cls[m] || { intUp: 0, intDn: 0, onS: 0 };
let cache = it._gege || (it._gege = {});
let rRs = cC.rssi | 0, 有线 = !/^wl/i.test(cC.iface || ''),
pRs = Math.round((rRs - (cC.iface === 'wl0' ? RSSI频率补偿 : 0)) * 1.6666666666666667 + 133.33333333333333),
图标RSSI = cC.iface === 'wl0' ? rRs - 5 : rRs;
let logo = cache.logo ??= it.querySelector('.dev-logo');
if (logo) {
let pr = (!有线 && (cC.phyUp != null || cC.phyDn != null))
? `上 ${cC.phyUp == null ? '--' : cC.phyUp}
下 ${cC.phyDn == null ? '--' : cC.phyDn}
` : '';
logo.innerHTML = getIconSvg(图标RSSI, 有线 || !rRs, 有线 ? S.有线统一PHY : 0) + pr;
}
(cache.rssiNode ??= it.querySelector('.gege-rssi')) && (cache.rssiNode.innerHTML = !有线 && rRs ? `${pRs}%, ${rRs}` : '');
let hqU = cln[m] ? cln[m].up : 0;
let hqD = cln[m] ? cln[m].down : 0;
let tN = cache.timeNode ??= it.querySelector('.gege-online-time');
if (tN && cS.onS > 0) tN.textContent = `在线:${fOT(cS.onS)}`;
const dI = cache.devIntro ??= it.querySelector('.dev-intro');
if (dI) {
let bx = cache.upBox ??= dI.querySelector('.gege-up-box');
if (!bx) {
bx = document.createElement('div'); bx.className = 'gege-up-box';
bx.innerHTML = `↑
`;
dI.appendChild(bx);
cache.upBox = bx;
}
let p = hqU * inv_tot_cU;
(cache.upVol ??= bx.querySelector('.v-vol')).textContent = fVD(cS.intUp, cC.offUp);
(cache.upPct ??= bx.querySelector('.v-pct')).textContent = p.toFixed(1) + '%';
(cache.upBar ??= bx.querySelector('.zte-thin-bar-inner')).style.width = Math.min(p, 100) + '%';
}
const inf = cache.info ??= it.querySelector('.info');
if (inf) {
let ipNode = cache.ipNode ??= inf.querySelector('.dev-ip');
if (ipNode) {
let zBadge = cache.zBadge ??= ipNode.querySelector('.gege-zero-badge');
if (!zBadge) {
zBadge = document.createElement('span'); zBadge.className = 'gege-zero-badge gege-box';
ipNode.style.display = 'flex'; ipNode.style.justifyContent = 'space-between';
zBadge.style.cssText = 'color: #999; font-size: 11.5px; font-family: Consolas; margin-right: 5px;';
ipNode.appendChild(zBadge);
cache.zBadge = zBadge;
}
zBadge.textContent = ((cS.zUC || 0) + (cS.zDC || 0)) < 6 ? "" : `[0估] ${!cS.zEU ? '' : fSV(cS.zEU)},${!cS.zED ? '' : fSV(cS.zED)}|${cS.zUC || 0},${cS.zDC || 0}`;
}
let rB = cache.rBox ??= inf.querySelector('.gege-ratio-box');
if (!rB) {
Array.from(inf.querySelectorAll('.dev-ip:not(.gege-box *)')).slice(1).forEach(n => { n.style.display = 'none'; });
inf.querySelectorAll('.dev-number:not(.gege-box *)').forEach(n => { n.style.display = 'none'; });
rB = document.createElement('div'); rB.className = 'gege-ratio-box';
rB.innerHTML = `,
`;
inf.appendChild(rB);
cache.rBox = rB;
}
let bR = (hqU + hqD) > 0 ? (hqU * 100 / (hqU + hqD)) : 0, tC = "", tCol;
if (CONFIG.calcMode === 1) {
let rt = hqD > 0 ? (hqU / hqD) : (hqU > 0 ? Infinity : 0);
if (rt > CONFIG.ratioExtremeUp) { tCol = '#ff4c00'; tC = (rt === Infinity ? '∞' : rt.toFixed(2)) + '⚠️'; }
else if (rt > CONFIG.ratioWarnUp) { tCol = '#ff4c00'; tC = (rt * 100).toFixed(1) + '%'; }
else if (rt > CONFIG.ratioExtremeDown) { tCol = '#0059fa'; tC = (rt * 100).toFixed(1) + '%'; }
else { tCol = '#0059fa'; let rRt = hqU > 0 ? (hqD / hqU) : (hqD > 0 ? Infinity : 0); tC = (rRt === Infinity ? '∞' : rRt.toFixed(1)) + 'x'; }
} else {
tCol = bR > CONFIG.ratioThreshold ? '#ff4c00' : '#0059fa';
tC = bR.toFixed(1) + '%';
}
(cache.rBoxPort ??= rB.querySelector('.v-port')).textContent = CONFIG.portMap[cC.iface] || cC.iface || "未知";
(cache.rBoxUp ??= rB.querySelector('.v-interval .c-up')).textContent = '' + fSV(hqU);
(cache.rBoxDn ??= rB.querySelector('.v-interval .c-down')).textContent = '' + fSV(hqD);
const rtP = cache.rtPct ??= rB.querySelector('.v-rt-pct');
rtP.textContent = tC; rtP.style.color = tCol;
(cache.rBoxBar ??= rB.querySelector('.gege-ratio-bar-inner')).style.width = Math.min(bR, 100) + '%';
let dBx = cache.dBox ??= inf.querySelector('.gege-down-box');
if (!dBx) {
dBx = document.createElement('div'); dBx.className = 'gege-down-box';
dBx.innerHTML = `↓
`;
inf.appendChild(dBx);
cache.dBox = dBx;
}
let dp = (cC.offDn || 0) * inv_tOD;
(cache.dBoxVol ??= dBx.querySelector('.v-vol')).textContent = fVD(cS.intDn, cC.offDn);
(cache.dBoxPct ??= dBx.querySelector('.v-pct')).textContent = dp.toFixed(1) + '%';
(cache.dBoxBar ??= dBx.querySelector('.zte-thin-bar-inner')).style.width = Math.min(dp, 100) + '%';
}
const sp = cache.speed ??= it.querySelector('.speed');
if (sp) {
let enh = cache.enh ??= sp.querySelector('.zte-enhance-speed');
if (!enh) {
sp.querySelectorAll('.connect-up, .connect-down').forEach(n => { n.style.display = 'none'; });
enh = document.createElement('div'); enh.className = 'zte-enhance-speed';
enh.innerHTML = `
`;
sp.appendChild(enh);
cache.enh = enh;
}
let pu = cC.upRate * inv_sU,
pd = cC.dnRate * inv_sD,
bU = cache.bU ??= enh.querySelector('.zte-bar-up'),
bD = cache.bD ??= enh.querySelector('.zte-bar-down');
let clU = (S.aWu * 0.1) || 0; if (clU < 512000) clU = 512000;
let clD = (S.aWd * 0.125) || 0;
for (let i = 0; i < 64; i++) { if (cS.hU[i] > clU) clU = cS.hU[i]; if (cS.hD[i] > clD) clD = cS.hD[i]; }
(cache.bUSpk ??= bU.querySelector('.v-spark')).textContent = getSpark(cS.hU, cS.hIdx, clU, 73000);
(cache.bDSpk ??= bD.querySelector('.v-spark')).textContent = getSpark(cS.hD, cS.hIdx, clD, 1000000);
bU.style.setProperty('--p-up', Math.min(pu, 100) + '%');
(cache.bUVal ??= bU.querySelector('.v-val')).textContent = `🔼 ${fBy(cC.upRate)}`;
(cache.bUPct ??= bU.querySelector('.v-pct')).textContent = pu.toFixed(1) + '%';
bD.style.setProperty('--p-down', Math.min(pd, 100) + '%');
(cache.bDVal ??= bD.querySelector('.v-val')).textContent = `🔽 ${fBy(cC.dnRate)}`;
(cache.bDPct ??= bD.querySelector('.v-pct')).textContent = pd.toFixed(1) + '%';
}
}
});
}
async function bVD(ol, cI) {
try {
let h2 = [], h52 = [], h58 = [], hW = [];
for (let m in cI) {
let d = cI[m], tS = fOT(d.onSec), ifc = d.iface;
let htm = `
${escapeHTML(d.name)}
${tS?'在线:'+tS:''}
${escapeHTML(d.ip)}
MAC:${m}
`;
if (['wl0', '2.4G'].includes(ifc)) h2.push(htm);
else if (['wlan5', 'wl1', 'wlan4', '5.2', '5.2G'].includes(ifc)) h52.push(htm);
else if (ifc === 'wl2' || ifc === 'wlan2' || ifc === '5.8G' || (/w/i.test(ifc) && !/wan/i.test(ifc))) h58.push(htm);
else hW.push(htm);
}
requestAnimationFrame(() => {
ol.innerHTML = `有线设备${(window.gegeHiddenDevices && Object.keys(window.gegeHiddenDevices).length > 0) ? '(哥哥科技:智能Mesh适配)' : ''}
${hW.join('')||'
没有连接设备
'}
无线设备(${S.is5G_149?'5.8GHz':'5.2GHz'})
${h52.join('')||'
没有连接设备
'}
无线设备(${S.is5G_149?'5.2GHz':'5.8GHz'})
${h58.join('')||'
没有连接设备
'}
无线设备(2.4GHz)
${h2.join('')||'
没有连接设备
'}
“在一个文明社会,干净的、不被监视与吸血的网络,是我们每个人的基本权利。”
本交互式程序属于“哥哥软件”系列;仅供使用,传播请尊重署名,二开或发行请参阅许可证;按“原样 (AS IS)”且免费提供,不对其适用性、稳定性、精密度或任何商业场景合规性作任何明示或暗示的担保。
基于本程序的任何修改、使用任意部分代码、再发布或相关衍生版本的合法性的前置条件是:在提供最终用户界面时,均应显著保留保留所有“哥哥科技”与法律声明,不得删除、隐藏或降低其可见性。
许可证
Bro-Stat系列 增强组件${(typeof GM_info !== 'undefined' && GM_info.script?.version) || '环境不支持获取版本号'} Copyright © 2026 哥哥科技 (BroTech) | All Rights Reserved 点此分享
`;
S.DOM缓存 = null; S.DOM容器 = null;
});}
catch (e) {
requestAnimationFrame(() => {
ol.innerHTML = `数据渲染失败: ${escapeHTML(e.message)}
`;
});
}
}
window.createGegeFloatingBtn = function () {
if (document.getElementById('gege-floating-btn')) return;
let b =
document.createElement('div');
b.id = 'gege-floating-btn';
b.innerHTML = '🛸';
b.style.cssText = 'position: fixed; top: 20px; right: 16%; width: 50px; height: 50px; background: linear-gradient(135deg, #0059fa, #00c6ff); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 48px; box-shadow: 0 4px 15px rgba(0,89,250,0.5); cursor: pointer; z-index: 99999; transition: transform 0.3s ease; user-select: none;';
b.
onmouseover = () => {
b.style.transform = 'scale(1.1) rotate(15deg)';
};
b.onmouseout = () => {
b.style.transform =
'scale(1) rotate(0deg)';
};
b.onclick = () => window.gegeTogglePanel();
document.body.appendChild(b);
};
window.gegeTogglePanel = function (fS = null) {
let o = document.getElementById('gege-global-overlay'),
iCO = o && o.style.display === 'block',
tS = fS !== null ? fS : !iCO,
mM = document.getElementById('gege-mi-menu');
if (!tS) {
if (mM) mM.classList.remove('active'); // 失去高亮
if (o) o.style.display = 'none';
return;
}
if (mM) {
document.querySelectorAll('#nav ul li').forEach(n => n.classList.remove('active'));
mM.classList.add('active');
}
if (!o) {
o = document.createElement('div');
o.id = 'gege-global-overlay';
document.body.appendChild(o);
}
o.style.display = 'block';
if (!window.gegeBActivated) {
window.gegeBActivated = !0;
clearTimeout(window.gegeMasterTimer);
window.gegeMasterTimer = setInterval(rSD, CONFIG.lanRefreshInterval * 1000);
}
bVD(o, {}).then(() => rSD());};
window.gegeBActivated = !1;
window.gegeHiddenDevices = {};
window.gegeTickCount = 0;
window.gegeMasterTimer = null;
window.__aD = Object.create(null); // 全局后勤字典
async function fAsusPorts(nodeMac = '') {
if (S.端口请求中) return;
S.端口请求中 = !0;
try {
let q = nodeMac ? `?node_mac=${encodeURIComponent(nodeMac)}&_=${Date.now()}` : `?_=${Date.now()}`;
let r = await fetch(`/get_port_status.cgi${q}`);
if (!r.ok) return;
let j = await r.json(), pi = j?.port_info || {}, ports = (nodeMac && (pi[nodeMac] || pi[nodeMac.toUpperCase()] || pi[nodeMac.toLowerCase()])) || Object.values(pi)[0] || {};
let rates = [];
for (const [k, v] of Object.entries(ports)) {
if (!/^L\d+$/i.test(k) && !/^lan\d+$/i.test(v?.ifname || '')) continue;
let rate = +(v?.link_rate || 0);
if (rate > 0 && (v?.is_on == null || String(v.is_on) === '1')) rates.push(rate);
}
S.有线统一PHY = rates.length && rates.every(x => x === rates[0]) ? rates[0] : 0;
} catch(e) { console.warn(e); }
finally { S.端口请求中 = !1; }
}
async function fAsusTopo() {
try {
let r = await fetch(`/update_clients.asp?_=${Date.now()}`);
if (!r.ok) return;
let m = (await r.text()).match(/fromNetworkmapd\s*:\s*\[(\{[\s\S]*?\})\]/);
if (m) {
let d = JSON.parse(m[1]), 新字典 = Object.create(null), 要重绘 = !1, parents = [], keys = new Set();
for (let k in d) if (k !== 'maclist' && k !== 'ClientAPILevel') keys.add(nM(k));
for (let k in d) {
if (k === 'maclist' || k === 'ClientAPILevel') continue;
let mac = nM(k), rawTx = String(d[k].curTx ?? '').trim(), rawRx = String(d[k].curRx ?? '').trim(), parent = nM(d[k].amesh_papMac || '');
if (parent) parents.push(parent);
新字典[mac] = {
n: d[k].nickName || d[k].name || "未知设备", ip: d[k].ip || "", wl: d[k].isWL, r: d[k].rssi | 0,
tx: rawTx === '' ? null : (+rawTx || 0), rx: rawRx === '' ? null : (+rawRx || 0)
};
if (!window.__aD[mac] || window.__aD[mac].wl != 新字典[mac].wl) 要重绘 = !0;
}
window.__aD = 新字典;
let root = parents.find(x => !keys.has(x)) || parents[0] || '';
if (root) S.主节点MAC = root;
if (要重绘) { window.gegeForceUIRedraw = !0; S.DOM缓存 = null; S.DOM容器 = null; }
await fAsusPorts(S.主节点MAC);
}
} catch(e) {console.warn(e);}
}
const _initUI = () => {
if (CONFIG.injectMode > 0 && window.createGegeFloatingBtn) window.createGegeFloatingBtn();
if (!window.gegeBActivated) {
window.gegeBActivated = !0;
clearTimeout(window.gegeMasterTimer);
window.gegeMasterTimer = setInterval(rSD, CONFIG.lanRefreshInterval * 1000);
fAsusTopo().then(() => rSD());
}
};
if (document.readyState === 'complete') _initUI(); else window.addEventListener('load', _initUI);
})();