Compare commits

...

12 commits
0.2.0 ... main

Author SHA1 Message Date
d16a7d6b53
ci(rust): revert cache and jq install 2025-07-11 22:50:45 +10:00
28bbd0d16e
ci(rust): install jq 2025-07-11 22:44:33 +10:00
5c83c599f0
ci(rust): cache cargo build files
Some checks failed
/ lint-n-test (push) Has been cancelled
2025-07-11 22:41:14 +10:00
a0d030d26e
ci(rust): continue testing on fmt or clippy error
Some checks are pending
/ lint-n-test (push) Waiting to run
2025-07-11 22:27:56 +10:00
6f6209ee7c
refactor: cargo clippy -- -D warnings 2025-07-11 22:27:29 +10:00
96766f116f
refactor: cargo clippy -- -D warnings
Some checks are pending
/ lint-n-test (push) Waiting to run
2025-07-11 22:19:56 +10:00
fafa4d9ad7
ci(rust): allow manual running
Some checks are pending
/ lint-n-test (push) Waiting to run
2025-07-11 22:15:22 +10:00
3602e23e68
style: rust fmt
Some checks are pending
/ lint-n-test (push) Waiting to run
2025-07-11 22:14:06 +10:00
52b8c5f88f
ci(rust): create a rust lint&test workflow 2025-07-11 22:13:26 +10:00
5b4bc6bda1
feat: Add tests 2025-07-11 21:55:24 +10:00
1292117e58
refactor: move regex commands into dedicated functions 2025-07-11 21:55:01 +10:00
f04a0379aa
feat(is_possible_chat_msg): support extra chat log format and add catchall backup 2025-07-11 20:43:45 +10:00
2 changed files with 164 additions and 40 deletions

View file

@ -0,0 +1,18 @@
on: [push, workflow_dispatch]
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
continue-on-error: true
- run: cargo clippy -- -D warnings
continue-on-error: true
- run: cargo check
- run: cargo test

View file

@ -47,10 +47,6 @@ enum OutputFormat {
Txt,
}
/// follow up by extracting just the user and the message
static EXTRACT_MSG: Lazy<Regex> = Lazy::new(|| Regex::new(r"<.*> .*").unwrap());
static EXTRACT_TIME: Lazy<Regex> = 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).first().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<Regex> = 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: &str) -> Vec<&str> {
static EXTRACT_TIME: Lazy<Regex> = 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<Regex> = Lazy::new(|| {
@ -237,10 +240,113 @@ fn is_possible_chat_msg(input: &str) -> bool {
)
.unwrap()
});
static CLIENT_MSG_RE: Lazy<Regex> = Lazy::new(|| {
static CLIENT_PRISM_MSG_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"\[.*\] \[Render thread\/INFO\] \[minecraft\/ChatComponent\]: \[CHAT\] <.*>*")
.unwrap()
});
static CLIENT_LOG_MSG_RE: Lazy<Regex> = 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<Regex> =
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)
}
#[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] <BuyMyMojo@MakeoutPoint> tehe";
const TEST_PRISM_MSG: &str = "[16:53:50] [Render thread/INFO] [minecraft/ChatComponent]: [CHAT] <smol.systems-LeNooby_09> ;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!(
"<BuyMyMojo@MakeoutPoint> tehe",
&extract_message(TEST_CLIENT_LOG_MSG)
);
}
#[test]
fn extract_chat_message_prism_log() {
assert_eq!(
"<smol.systems-LeNooby_09> ;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);
}
}