From f04a0379aa50e76893ea0bd77a5610e51391e307 Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 20:43:45 +1000 Subject: [PATCH 01/12] 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 02/12] 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 03/12] 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 04/12] 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 05/12] 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] From fafa4d9ad70f9b946628fe192de8066ea2afeb0c Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:15:22 +1000 Subject: [PATCH 06/12] ci(rust): allow manual running --- .forgejo/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/rust.yml b/.forgejo/workflows/rust.yml index 8dcd0d0..abe8d78 100644 --- a/.forgejo/workflows/rust.yml +++ b/.forgejo/workflows/rust.yml @@ -1,4 +1,4 @@ -on: [push] +on: [push, workflow_dispatch] jobs: lint-n-test: # Run on the 9950x that Aria has access to :3 From 96766f116f0aa06759befed7c24d19c0ef789d0b Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:19:56 +1000 Subject: [PATCH 07/12] refactor: cargo clippy -- -D warnings --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2a6768d..a32c53a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,7 +67,7 @@ fn main() -> Result<()> { .map(|x| { format!( "{} {}", - extract_date_time(&x.to_string()).get(0).unwrap(), + extract_date_time(&x.to_string()).first().unwrap(), extract_message(x) ) }) @@ -206,7 +206,7 @@ fn extract_message(msg: &str) -> String { static EXTRACT_MSG: Lazy = Lazy::new(|| Regex::new(r"<.*> .*").unwrap()); EXTRACT_MSG - .captures(&msg) + .captures(msg) .expect("Unable to extract time") .get(0) .unwrap() @@ -214,7 +214,7 @@ fn extract_message(msg: &str) -> String { .to_string() } -fn extract_date_time(msg: &String) -> Vec<&str> { +fn extract_date_time(msg: &str) -> Vec<&str> { static EXTRACT_TIME: Lazy = Lazy::new(|| Regex::new(r"^\[.*?\]").unwrap()); let time: Vec<&str> = EXTRACT_TIME From 6f6209ee7c9df4abf56d8a29583892dd8f3402ee Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:27:29 +1000 Subject: [PATCH 08/12] refactor: cargo clippy -- -D warnings --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index a32c53a..3ec9c3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,7 +67,7 @@ fn main() -> Result<()> { .map(|x| { format!( "{} {}", - extract_date_time(&x.to_string()).first().unwrap(), + extract_date_time(x).first().unwrap(), extract_message(x) ) }) From a0d030d26ecd5233cae79de90591a9d52ff9cfbe Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:27:56 +1000 Subject: [PATCH 09/12] ci(rust): continue testing on fmt or clippy error --- .forgejo/workflows/rust.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/rust.yml b/.forgejo/workflows/rust.yml index abe8d78..788c0d0 100644 --- a/.forgejo/workflows/rust.yml +++ b/.forgejo/workflows/rust.yml @@ -11,6 +11,8 @@ jobs: - uses: actions/checkout@v4 - run: rustup component add rustfmt clippy - run: cargo fmt -- --check + continue-on-error: true - run: cargo clippy -- -D warnings + continue-on-error: true - run: cargo check - - run: cargo test \ No newline at end of file + - run: cargo test From 5c83c599f066c2b869498b8768d0e1df85dc2ecc Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:41:14 +1000 Subject: [PATCH 10/12] ci(rust): cache cargo build files --- .forgejo/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/rust.yml b/.forgejo/workflows/rust.yml index 788c0d0..539b20e 100644 --- a/.forgejo/workflows/rust.yml +++ b/.forgejo/workflows/rust.yml @@ -10,6 +10,7 @@ jobs: - 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 + - uses: https://github.com/Leafwing-Studios/cargo-cache@v2 - run: cargo fmt -- --check continue-on-error: true - run: cargo clippy -- -D warnings From 28bbd0d16e23b6fba18c8dde4e82dc7e6e113ebb Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:44:33 +1000 Subject: [PATCH 11/12] ci(rust): install jq --- .forgejo/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/rust.yml b/.forgejo/workflows/rust.yml index 539b20e..2155323 100644 --- a/.forgejo/workflows/rust.yml +++ b/.forgejo/workflows/rust.yml @@ -7,7 +7,7 @@ jobs: 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 + - run: curl -sL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs jq - uses: actions/checkout@v4 - run: rustup component add rustfmt clippy - uses: https://github.com/Leafwing-Studios/cargo-cache@v2 From d16a7d6b5352d1bf09952299e2ac2bfa4aca81e0 Mon Sep 17 00:00:00 2001 From: aria Date: Fri, 11 Jul 2025 22:50:45 +1000 Subject: [PATCH 12/12] ci(rust): revert cache and jq install --- .forgejo/workflows/rust.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.forgejo/workflows/rust.yml b/.forgejo/workflows/rust.yml index 2155323..788c0d0 100644 --- a/.forgejo/workflows/rust.yml +++ b/.forgejo/workflows/rust.yml @@ -7,10 +7,9 @@ jobs: 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 jq + - 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 - - uses: https://github.com/Leafwing-Studios/cargo-cache@v2 - run: cargo fmt -- --check continue-on-error: true - run: cargo clippy -- -D warnings