use super::send_ctx; use super::{get_nextblock, Context, FAIL, SUCCESS}; use anyhow::Result; use itertools::Itertools; use poise::serenity_prelude::*; use std::str::FromStr; use tokio::time::{sleep, Duration}; fn parse(line: &str) -> Option<(u32, u32, u32)> { let mut v = vec![]; for piece in line.split('/') { v.push(u32::from_str(piece.trim().split_once(' ')?.0).ok()?); } v.into_iter().collect_tuple() } #[allow(dead_code)] pub enum Size { Gb(f64), Mb(f64), Kb(f64), B(f64), } const UNIT: f64 = 1024.0; impl Size { pub fn bytes(self) -> f64 { match self { Self::B(x) => x, Self::Kb(x) => x * UNIT, Self::Mb(x) => x * UNIT * UNIT, Self::Gb(x) => x * UNIT * UNIT * UNIT, } } } // https://git.sr.ht/~f9/human_bytes pub fn humanize_bytes>(bytes: T) -> String { const SUFFIX: [&str; 4] = ["B", "KB", "MB", "GB"]; let size = bytes.into().bytes(); if size <= 0.0 { return "0 B".to_string(); } let base = size.log(UNIT); let result = format!("{:.1}", UNIT.powf(base - base.floor()),) .trim_end_matches(".0") .to_owned(); // Add suffix [&result, SUFFIX[base.floor() as usize]].join(" ") } #[poise::command(slash_command, category = "Info", rename = "status")] /// server status. pub async fn command(ctx: Context<'_>) -> Result<()> { let _ = ctx.defer_or_broadcast().await; send_ctx!(ctx, "status")?; macro_rules! fail { ($ctx:expr,$fail:expr) => {{ poise::send_reply( ctx, poise::CreateReply::default() .embed(CreateEmbed::new().title("server down").color($fail)), ) .await?; return Ok(()); }}; } let block = tokio::select! { block = get_nextblock() => block, () = sleep(Duration::from_secs(5)) => fail!(ctx, FAIL), }; let Some((tps, mem, pcount)) = parse(&block) else { fail!(ctx, FAIL); }; poise::send_reply( ctx, poise::CreateReply::default().embed( CreateEmbed::new() .title("server online") .field("tps", tps.to_string(), true) .field("memory use", humanize_bytes(Size::Mb(f64::from(mem))), true) .field("players", pcount.to_string(), true) .color(SUCCESS), ), ) .await?; Ok(()) } #[test] fn test_parse() { assert!(parse("57 TPS / 274 MB / 7 PLAYERS") == Some((57, 274, 7))); } #[test] fn test_bytes() { assert!(humanize_bytes(Size::B(0.0)) == "0 B"); assert!(humanize_bytes(Size::B(550.0)) == "550 B"); assert!(humanize_bytes(Size::Kb(550.0)) == "550 KB"); assert!(humanize_bytes(Size::Mb(650.0)) == "650 MB"); assert!(humanize_bytes(Size::Mb(2000.0)) == "2 GB"); assert!(humanize_bytes(Size::Gb(15.3)) == "15.3 GB"); }