( server: Server, serviceData?: Map<string, number> )
| 426 | const state = server.state; |
| 427 | const host = server.host; |
| 428 | const flag = htmlEscape(getCountryFlag(server.geoip?.country_code)); |
| 429 | |
| 430 | let title = `${getStatusEmoji(online)} ${flag} <b>${htmlEscape(server.name)}</b> <code>#${server.id}</code>`; |
| 431 | |
| 432 | if (serviceData && serviceData.size > 0) { |
| 433 | const monitors: string[] = []; |
| 434 | serviceData.forEach((delay, name) => { |
| 435 | const delayMs = delay.toFixed(1); |
| 436 | monitors.push(`${htmlEscape(name)}:${delayMs}ms`); |
| 437 | }); |
| 438 | title += `\n📶 ${monitors.join(" | ")}`; |
| 439 | } |
| 440 | |
| 441 | if (!online) { |
| 442 | title += ` (离线)`; |
| 443 | return title; |
| 444 | } |
| 445 | |
| 446 | if (state) { |
| 447 | const cpuPercent = state.cpu?.toFixed(1) || "0"; |
| 448 | const memPercent = |
| 449 | host?.mem_total && state.mem_used |
| 450 | ? ((state.mem_used / host.mem_total) * 100).toFixed(1) |
| 451 | : "0"; |
| 452 | const diskPercent = |
| 453 | host?.disk_total && state.disk_used |
| 454 | ? ((state.disk_used / host.disk_total) * 100).toFixed(1) |
| 455 | : "0"; |
| 456 | |
| 457 | let details = `├ CPU: ${getUsageBar(parseFloat(cpuPercent))} ${cpuPercent}%\n`; |
| 458 | details += `├ 内存: ${getUsageBar(parseFloat(memPercent))} ${memPercent}%`; |
| 459 | if (state.mem_used && host?.mem_total) { |
| 460 | details += ` (${formatBytes(state.mem_used)}/${formatBytes(host.mem_total)})`; |
| 461 | } |
| 462 | details += `\n`; |
| 463 | details += `├ 硬盘: ${getUsageBar(parseFloat(diskPercent))} ${diskPercent}%`; |
| 464 | if (state.disk_used && host?.disk_total) { |
| 465 | details += ` (${formatBytes(state.disk_used)}/${formatBytes(host.disk_total)})`; |
| 466 | } |
| 467 | details += `\n`; |
| 468 | details += `├ 网络: ↑${formatSpeed(state.net_out_speed || 0)} ↓${formatSpeed(state.net_in_speed || 0)}\n`; |
| 469 | details += `├ 流量: ↑${formatBytes(state.net_out_transfer || 0)} ↓${formatBytes(state.net_in_transfer || 0)}\n`; |
| 470 | details += `└ 运行: ${formatUptime(state.uptime || 0)}`; |
| 471 | |
| 472 | return `${title}\n<blockquote expandable>${details}</blockquote>`; |
| 473 | } |
| 474 | |
| 475 | return title; |
| 476 | } |
| 477 | |
| 478 | const nezha = async (msg: Api.Message) => { |
| 479 | try { |
| 480 | const args = msg.message.slice(1).split(" ").slice(1); |
| 481 | const subCmd = args[0]?.toLowerCase(); |
| 482 | |
| 483 | if (subCmd === "chart") { |
| 484 | const config = loadConfig(); |
| 485 | if (!config) { |
no test coverage detected