diff --git a/src/bot/command_execution.rs b/src/bot/command_execution.rs index 528d664..1c72d1c 100644 --- a/src/bot/command_execution.rs +++ b/src/bot/command_execution.rs @@ -18,7 +18,45 @@ pub(super) async fn handle_single_result( event_id: EventId, ) { let html = cmd_result.message_html(respond_to); - matrix::send_message(client, room.room_id(), &html, Some(event_id)).await; + let plain = cmd_result.message_plain(respond_to); + matrix::send_message(client, room.room_id(), (&html, &plain), Some(event_id)).await; +} + +/// Format failure messages nicely in either HTML or plain text. If +/// plain is true, plain-text will be returned. Otherwise, formatted +/// HTML. +fn format_failures( + errors: &[(&str, &BotError)], + commands_executed: usize, + respond_to: &str, + plain: bool, +) -> String { + let respond_to = match plain { + true => respond_to.to_owned(), + false => format!( + "{}", + respond_to, respond_to + ), + }; + + let failures: Vec = errors + .iter() + .map(|&(cmd, err)| format!("{}: {}", cmd, err)) + .collect(); + + let message = format!( + "{}: Executed {} commands ({} failed)\n\nFailures:\n{}", + respond_to, + commands_executed, + errors.len(), + failures.join("\n") + ) + .replace("\n", "
"); + + match plain { + true => html2text::from_read(message.as_bytes(), message.len()), + false => message, + } } /// Handle responding to multiple commands being executed. Will print @@ -29,7 +67,7 @@ pub(super) async fn handle_multiple_results( respond_to: &str, room: &Joined, ) { - let respond_to = format!( + let user_pill = format!( "{}", respond_to, respond_to ); @@ -42,25 +80,19 @@ pub(super) async fn handle_multiple_results( }) .collect(); - let message = if errors.len() == 0 { - format!("{}: Executed {} commands", respond_to, results.len()) - } else { - let failures: Vec = errors - .iter() - .map(|&(cmd, err)| format!("{}: {}", cmd, err)) - .collect(); - - format!( - "{}: Executed {} commands ({} failed)\n\nFailures:\n{}", - respond_to, - results.len(), - errors.len(), - failures.join("\n") + let (message, plain) = if errors.len() == 0 { + ( + format!("{}: Executed {} commands", user_pill, results.len()), + format!("{}: Executed {} commands", respond_to, results.len()), + ) + } else { + ( + format_failures(&errors, results.len(), respond_to, false), + format_failures(&errors, results.len(), respond_to, true), ) - .replace("\n", "
") }; - matrix::send_message(client, room.room_id(), &message, None).await; + matrix::send_message(client, room.room_id(), (&message, &plain), None).await; } /// Create a context for command execution. Can fai if the room diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 2f81af2..35ebce8 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -55,6 +55,8 @@ pub trait ResponseExtractor { /// HTML representation of the message, directly mentioning the /// username. fn message_html(&self, username: &str) -> String; + + fn message_plain(&self, username: &str) -> String; } impl ResponseExtractor for ExecutionResult { @@ -68,12 +70,23 @@ impl ResponseExtractor for ExecutionResult { ); match self { - Ok(resp) => format!("

{}

{}

", username, resp.html).replace("\n", "
"), - Err(e) => { - format!("

{}

{}

", username, e).replace("\n", "
") - } + Ok(resp) => format!("

{}

", resp.html).replace("\n", "
"), + Err(e) => format!("

{}: {}

", username, e).replace("\n", "
"), } } + + fn message_plain(&self, username: &str) -> String { + let message = match self { + Ok(resp) => format!("{}", resp.html), + Err(e) => format!("{}", e), + }; + + format!( + "{}:\n{}", + username, + html2text::from_read(message.as_bytes(), message.len()) + ) + } } /// The trait that any command that can be executed must implement. @@ -256,15 +269,6 @@ mod tests { assert_eq!(execution_allowed(&cmd, &ctx).is_err(), true); } - #[test] - fn command_result_extractor_creates_bubble() { - let result = Execution::success("test".to_string()); - let message = result.message_html("@myuser:example.com"); - assert!(message.contains( - "@myuser:example.com" - )); - } - #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn unrecognized_command() { let db_path = tempfile::NamedTempFile::new_in(".").unwrap(); diff --git a/src/matrix.rs b/src/matrix.rs index 5c9f6a8..7bec296 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -61,20 +61,22 @@ pub async fn get_rooms_for_user( Ok(rooms_for_user) } +/// Send a message. The message is a tuple of HTML and plain text +/// responses. pub async fn send_message( client: &Client, room_id: &RoomId, - message: &str, + message: (&str, &str), reply_to: Option, ) { + let (html, plain) = message; let room = match client.get_joined_room(room_id) { Some(room) => room, _ => return, }; - let plain = html2text::from_read(message.as_bytes(), message.len()); let mut content = MessageEventContent::new(MessageType::Notice( - NoticeMessageEventContent::html(plain.trim(), message), + NoticeMessageEventContent::html(plain.trim(), html), )); content.relates_to = reply_to.map(|event_id| Relation::Reply { @@ -86,7 +88,7 @@ pub async fn send_message( let result = room.send(content, None).await; if let Err(e) = result { - let message = extract_error_message(e); - error!("Error sending message: {}", message); + let html = extract_error_message(e); + error!("Error sending html: {}", html); }; }