//! Matrix message handler — processes incoming room messages and dispatches commands. mod handle_message; mod on_room_message; pub(in crate::chat::transport::matrix::bot) use handle_message::handle_message; pub(in crate::chat::transport::matrix::bot) use on_room_message::on_room_message; /// sender is included so the LLM can distinguish participants. pub(super) fn format_user_prompt(sender: &str, message: &str) -> String { format!("{sender}: {message}") } /// Matrix event handler for room messages. Each invocation spawns an #[cfg(test)] mod tests { use super::*; // -- format_user_prompt ------------------------------------------------- #[test] fn format_user_prompt_includes_sender_and_message() { let prompt = format_user_prompt("@alice:example.com", "Hello!"); assert_eq!(prompt, "@alice:example.com: Hello!"); } #[test] fn format_user_prompt_different_users() { let prompt = format_user_prompt("@bob:example.com", "What's up?"); assert_eq!(prompt, "@bob:example.com: What's up?"); } // -- OAuth login link formatting ---------------------------------------- #[test] fn oauth_error_produces_login_link() { let err = "OAuth session expired or credentials missing. Please log in: http://localhost:3001/oauth/authorize"; let url = crate::llm::oauth::extract_login_url_from_error(err); assert!(url.is_some(), "should extract URL from OAuth error"); let msg = format!( "Authentication required. [Click here to log in to Claude]({})", url.unwrap() ); assert!(msg.contains("http://localhost:3001/oauth/authorize")); assert!(msg.contains("[Click here to log in to Claude]")); } #[test] fn non_oauth_error_not_formatted_as_link() { let err = "Some unrelated error"; assert!(crate::llm::oauth::extract_login_url_from_error(err).is_none()); } // -- bot_name / system prompt ------------------------------------------- #[test] fn bot_name_system_prompt_format() { let bot_name = "Timmy"; let system_prompt = format!("Your name is {bot_name}. Refer to yourself as {bot_name}, not Claude."); assert_eq!( system_prompt, "Your name is Timmy. Refer to yourself as Timmy, not Claude." ); } }