From f04a0379aa50e76893ea0bd77a5610e51391e307 Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 20:43:45 +1000 Subject: [PATCH 1/5] feat(is_possible_chat_msg): support extra chat log format and add catchall backup --- src/main.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 198f6f5..16f5e30 100644 --- a/src/main.rs +++ b/src/main.rs @@ -237,10 +237,21 @@ fn is_possible_chat_msg(input: &str) -> bool { ) .unwrap() }); - static CLIENT_MSG_RE: Lazy = Lazy::new(|| { + static CLIENT_PRISM_MSG_RE: Lazy = Lazy::new(|| { Regex::new(r"\[.*\] \[Render thread\/INFO\] \[minecraft\/ChatComponent\]: \[CHAT\] <.*>*") .unwrap() }); + static CLIENT_LOG_MSG_RE: Lazy = Lazy::new(|| { + Regex::new(r"\[.*\] \[Render thread\/INFO\] \[net.minecraft.client.gui.components.ChatComponent\/\]: \[CHAT\] <.*>*") + .unwrap() + }); - SERVER_MSG_RE.is_match(input) || CLIENT_MSG_RE.is_match(input) + // This is here just in case I need to get a chat message from a strange place + static _CLIENT_CATCHALL_MSG_RE: Lazy = Lazy::new(|| { + Regex::new(r".*?: \[CHAT\] .*") + .unwrap() + }); + + + SERVER_MSG_RE.is_match(input) || CLIENT_PRISM_MSG_RE.is_match(input) || CLIENT_LOG_MSG_RE.is_match(input) } From 1292117e58e7906f549dd4c1282e0ada23ddfa23 Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 21:55:01 +1000 Subject: [PATCH 2/5] refactor: move regex commands into dedicated functions --- src/main.rs | 90 +++++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/main.rs b/src/main.rs index 16f5e30..0f53f3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,10 +47,6 @@ enum OutputFormat { Txt, } -/// follow up by extracting just the user and the message -static EXTRACT_MSG: Lazy = Lazy::new(|| Regex::new(r"<.*> .*").unwrap()); -static EXTRACT_TIME: Lazy = Lazy::new(|| Regex::new(r"^\[.*?\]").unwrap()); - fn main() -> Result<()> { let args = Args::parse(); @@ -71,18 +67,8 @@ fn main() -> Result<()> { .map(|x| { format!( "{} {}", - EXTRACT_TIME - .captures(x) - .expect("Unable to extract time") - .get(0) - .unwrap() - .as_str(), - EXTRACT_MSG - .captures(x) - .expect("Unable to extract message") - .get(0) - .unwrap() - .as_str() + extract_date_time(&x.to_string()).get(0).unwrap(), + extract_message(x) ) }) .collect(); @@ -202,33 +188,50 @@ fn save_csv_file( } for msg in selected { - let time: Vec<&str> = EXTRACT_TIME - .captures(&msg) - .expect("Unable to extract time") - .get(0) - .unwrap() - .as_str() - .strip_prefix("[") - .expect("Unable to remove time prefix") - .strip_suffix("]") - .expect("Unable to remove time suffix") - .split(' ') - .collect(); - out_file.write_record([ - time[0], - time[1], - EXTRACT_MSG - .captures(&msg) - .expect("Unable to extract time") - .get(0) - .unwrap() - .as_str(), - ])?; + let mut time: Vec<&str> = extract_date_time(&msg); + + // if there is only 1 entry in the vec then it has to be a client time so we can just add a pointless entry for the csv + if time.len() == 1 { + time.insert(0, "Null"); + } + + out_file.write_record([time[0], time[1], &extract_message(&msg)])?; } Ok(()) } +fn extract_message(msg: &str) -> String { + /// follow up by extracting just the user and the message + static EXTRACT_MSG: Lazy = Lazy::new(|| Regex::new(r"<.*> .*").unwrap()); + + EXTRACT_MSG + .captures(&msg) + .expect("Unable to extract time") + .get(0) + .unwrap() + .as_str() + .to_string() +} + +fn extract_date_time(msg: &String) -> Vec<&str> { + static EXTRACT_TIME: Lazy = Lazy::new(|| Regex::new(r"^\[.*?\]").unwrap()); + + let time: Vec<&str> = EXTRACT_TIME + .captures(msg) + .expect("Unable to extract time") + .get(0) + .unwrap() + .as_str() + .strip_prefix("[") + .expect("Unable to remove time prefix") + .strip_suffix("]") + .expect("Unable to remove time suffix") + .split(' ') + .collect(); + time +} + fn is_possible_chat_msg(input: &str) -> bool { /// first pass to find all possible lines that could have a chat message static SERVER_MSG_RE: Lazy = Lazy::new(|| { @@ -247,11 +250,10 @@ fn is_possible_chat_msg(input: &str) -> bool { }); // This is here just in case I need to get a chat message from a strange place - static _CLIENT_CATCHALL_MSG_RE: Lazy = Lazy::new(|| { - Regex::new(r".*?: \[CHAT\] .*") - .unwrap() - }); - + static _CLIENT_CATCHALL_MSG_RE: Lazy = + Lazy::new(|| Regex::new(r".*?: \[CHAT\] .*").unwrap()); - SERVER_MSG_RE.is_match(input) || CLIENT_PRISM_MSG_RE.is_match(input) || CLIENT_LOG_MSG_RE.is_match(input) + SERVER_MSG_RE.is_match(input) +|| CLIENT_PRISM_MSG_RE.is_match(input) +|| CLIENT_LOG_MSG_RE.is_match(input) } From 5b4bc6bda1f085240620841f3c8094d54d71577b Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 21:55:24 +1000 Subject: [PATCH 3/5] feat: Add tests --- src/main.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0f53f3b..2fdbb2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -254,6 +254,93 @@ fn is_possible_chat_msg(input: &str) -> bool { Lazy::new(|| Regex::new(r".*?: \[CHAT\] .*").unwrap()); SERVER_MSG_RE.is_match(input) -|| CLIENT_PRISM_MSG_RE.is_match(input) -|| CLIENT_LOG_MSG_RE.is_match(input) + || CLIENT_PRISM_MSG_RE.is_match(input) + || CLIENT_LOG_MSG_RE.is_match(input) +} + +#[cfg(test)] +mod tests { + use super::*; + const TEST_SERVER_MSG: &str = "[05Jul2025 12:41:12.295] [Server thread/INFO] [net.minecraft.server.MinecraftServer/]: <🐈 Vftdan> :3"; + const TEST_CLIENT_LOG_MSG: &str = "[11Jul2025 20:30:20.286] [Render thread/INFO] [net.minecraft.client.gui.components.ChatComponent/]: [CHAT] tehe"; + const TEST_PRISM_MSG: &str = "[16:53:50] [Render thread/INFO] [minecraft/ChatComponent]: [CHAT] ;3"; + + const RANDOM_LOG_LINES: [&str; 5] = [ + "[05Jul2025 13:08:11.886] [VoiceChatPacketProcessingThread/INFO] [voicechat/]: [voicechat] Player 399aedb6-a257-49d1-930b-af62fc328ae7 timed out", + "[05Jul2025 13:09:19.890] [Server thread/INFO] [me.ichun.mods.serverpause.common.core.MinecraftServerMethods/]: Saving and pausing game...", + "[05Jul2025 12:02:58.057] [Server thread/INFO] [net.minecraft.server.MinecraftServer/]: alto joined the game", + "java.lang.NullPointerException: Cannot invoke \"net.minecraft.world.Container.getContainerSize()\" because the return value of \"net.neoforged.neoforge.items.wrapper.InvWrapper.getInv()\" is null", + "[05Jul2025 10:31:36.112] [Server thread/INFO] [owo/]: Receiving client config", + ]; + + #[test] + fn detect_server_chat_messages() { + assert!(is_possible_chat_msg(TEST_SERVER_MSG)); + } + + #[test] + fn detect_prism_chat_messages() { + assert!(is_possible_chat_msg(TEST_PRISM_MSG)); + } + + #[test] + fn detect_client_log_chat_messages() { + assert!(is_possible_chat_msg(TEST_CLIENT_LOG_MSG)); + } + + #[test] + fn extract_chat_message_server() { + assert_eq!("<🐈 Vftdan> :3", &extract_message(TEST_SERVER_MSG)); + } + #[test] + fn extract_chat_message_client_log() { + assert_eq!(" tehe", &extract_message(TEST_CLIENT_LOG_MSG)); + } + #[test] + fn extract_chat_message_prism_log() { + assert_eq!(" ;3", &extract_message(TEST_PRISM_MSG)); + } + + #[test] + fn extract_datetime_server_messages() { + let msg_string = TEST_SERVER_MSG.to_string(); + let datetime: Vec<&str> = extract_date_time(&msg_string); + println!("server datetime: {:?}", datetime); + + let correct_datetime: Vec<&str> = vec![&"05Jul2025", &"12:41:12.295"]; + assert_eq!(datetime, correct_datetime); + } + + #[test] + fn extract_datetime_client_messages() { + let msg_string = TEST_CLIENT_LOG_MSG.to_string(); + let datetime: Vec<&str> = extract_date_time(&msg_string); + println!("server datetime: {:?}", datetime); + + let correct_datetime: Vec<&str> = vec![&"11Jul2025", &"20:30:20.286"]; + assert_eq!(datetime, correct_datetime); + } + + #[test] + fn extract_datetime_prism_messages() { + let msg_string = TEST_PRISM_MSG.to_string(); + let datetime: Vec<&str> = extract_date_time(&msg_string); + println!("prism datetime: {:?}", datetime); + + let correct_datetime: Vec<&str> = vec![&"16:53:50"]; + assert_eq!(datetime, correct_datetime); + } + + #[test] + fn ignore_random_log_lines() { + let mut is_msg: bool = false; + + for line in RANDOM_LOG_LINES { + if is_possible_chat_msg(line) { + is_msg = true; + } + } + + assert!(!is_msg); + } } From 52b8c5f88f5e091e6c8aa89f388d9a4f3d1ae284 Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:13:26 +1000 Subject: [PATCH 4/5] ci(rust): create a rust lint&test workflow --- .forgejo/workflows/rust.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .forgejo/workflows/rust.yml diff --git a/.forgejo/workflows/rust.yml b/.forgejo/workflows/rust.yml new file mode 100644 index 0000000..8dcd0d0 --- /dev/null +++ b/.forgejo/workflows/rust.yml @@ -0,0 +1,16 @@ +on: [push] +jobs: + lint-n-test: + # Run on the 9950x that Aria has access to :3 + runs-on: azuki-new + container: + image: rust + steps: + # nodejs is required for the checkout action + - run: curl -sL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs + - uses: actions/checkout@v4 + - run: rustup component add rustfmt clippy + - run: cargo fmt -- --check + - run: cargo clippy -- -D warnings + - run: cargo check + - run: cargo test \ No newline at end of file From 3602e23e683a9867c7126a095ccca2a0294eda7b Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:14:06 +1000 Subject: [PATCH 5/5] style: rust fmt --- src/main.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2fdbb2a..2a6768d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -294,11 +294,17 @@ mod tests { } #[test] fn extract_chat_message_client_log() { - assert_eq!(" tehe", &extract_message(TEST_CLIENT_LOG_MSG)); + assert_eq!( + " tehe", + &extract_message(TEST_CLIENT_LOG_MSG) + ); } #[test] fn extract_chat_message_prism_log() { - assert_eq!(" ;3", &extract_message(TEST_PRISM_MSG)); + assert_eq!( + " ;3", + &extract_message(TEST_PRISM_MSG) + ); } #[test]