diff options
author | Daniel Schadt <kingdread@gmx.de> | 2021-03-06 12:12:03 +0100 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2021-03-06 12:12:03 +0100 |
commit | d84b4c3466adcc085b7df36015df4b46488ba591 (patch) | |
tree | 4193ff1cbc4fdeb57fdddff2d2b33339c3ef0efd /src/matrix.rs | |
parent | 1eb73eb5211fb43e2389857dda4f7afa15e55433 (diff) | |
download | ezau-d84b4c3466adcc085b7df36015df4b46488ba591.tar.gz ezau-d84b4c3466adcc085b7df36015df4b46488ba591.tar.bz2 ezau-d84b4c3466adcc085b7df36015df4b46488ba591.zip |
add proper previous-log parsing for matrix
Doing all the "new log" insertion based on simple string operations is a
bit of madness, so the proper course of action is to parse them into a
proper intermediate representation from which we can then generate a
plain and HTML body.
In addition, this has some other minor code cleanup for the matrix
module.
Diffstat (limited to 'src/matrix.rs')
-rw-r--r-- | src/matrix.rs | 196 |
1 files changed, 37 insertions, 159 deletions
diff --git a/src/matrix.rs b/src/matrix.rs index bd83a1f..ffd223d 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1,11 +1,12 @@ use super::categories::Categorizable; use super::config; +use super::logbag::{state_emoji, LogBag}; use std::convert::TryFrom; use std::time::{Duration, SystemTime}; use anyhow::Result; -use evtclib::{Log, Outcome}; +use evtclib::Log; use log::{debug, info}; use tokio::runtime::Runtime; @@ -37,6 +38,15 @@ impl From<config::Matrix> for MatrixUser { } } +/// Maximum age of the message to still be edited. +const MAX_HOURS: u64 = 5; + +/// Posts a link to the log to a Matrix room. +/// +/// The user identification is given in the `user` parameter. +/// +/// This function blocks until all API calls have been made, that is until the message has reached +/// the homeserver. pub fn post_link(user: MatrixUser, room_id: &str, log: &Log, link: &str) -> Result<()> { let mut rt = Runtime::new()?; let room_id = RoomId::try_from(room_id)?; @@ -62,8 +72,9 @@ pub fn post_link(user: MatrixUser, room_id: &str, log: &Log, link: &str) -> Resu } Some((old_id, old_text)) => { debug!("Updating message {:?}", old_id); - let new_text = insert_log(&old_text, log, link); - let new_html = htmlify(&new_text); + let logbag = insert_log(&old_text, log, link); + let new_text = logbag.render_plain(); + let new_html = logbag.render_html(); update_message(&client, &room_id, &old_id, &new_text, &new_html).await?; } } @@ -81,15 +92,15 @@ async fn find_message( room_id: &RoomId, ) -> Result<Option<(EventId, String)>> { let request = get_message_events::Request::backward(room_id, ""); - let five_h_ago = SystemTime::now() - Duration::from_secs(5 * 60 * 60); + let time_limit = SystemTime::now() - Duration::from_secs(MAX_HOURS * 60 * 60); for raw_message in client.room_messages(request).await?.chunk { - if let Ok(message) = raw_message.deserialize() { - if let AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(msg)) = message { - if &msg.sender == my_id && msg.origin_server_ts >= five_h_ago { - if let MessageEventContent::Text(text) = msg.content { - if text.relates_to.is_none() { - return Ok(Some((msg.event_id, text.body))); - } + if let Ok(AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(msg))) = + raw_message.deserialize() + { + if &msg.sender == my_id && msg.origin_server_ts >= time_limit { + if let MessageEventContent::Text(text) = msg.content { + if text.relates_to.is_none() { + return Ok(Some((msg.event_id, text.body))); } } } @@ -98,11 +109,12 @@ async fn find_message( Ok(None) } +/// Post a new message to the given Matrix channel. async fn post_new(client: &Client, room_id: &RoomId, log: &Log, link: &str) -> Result<()> { - let title = log.category(); let line = format!("{} {}", state_emoji(log), link); - let body = format!("{}\n{}\n", title, line); - let html = format!("<b>{}</b><br>\n{}\n", title, line); + let logbag: LogBag = vec![(log.category().to_string(), vec![line])].into(); + let body = logbag.render_plain(); + let html = logbag.render_html(); let text_message = TextMessageEventContent::html(body, html); client @@ -115,6 +127,9 @@ async fn post_new(client: &Client, room_id: &RoomId, log: &Log, link: &str) -> R Ok(()) } +/// Updates the given message with some new text +/// +/// This constructs and sends the right Matrix API message. async fn update_message( client: &Client, room_id: &RoomId, @@ -139,149 +154,12 @@ async fn update_message( Ok(()) } -fn insert_log(old_text: &str, log: &Log, link: &str) -> String { - let chunks = old_text.split("\n\n"); - let mut found = false; - let result = chunks - .map(|chunk| { - let category = chunk.split("\n").next().unwrap(); - if category == log.category() { - found = true; - format!("{}\n{} {}", chunk.trim(), state_emoji(log), link) - } else { - chunk.to_string() - } - }) - .collect::<Vec<_>>() - .join("\n\n"); - if found { - result - } else { - format!( - "{}\n\n{}\n{} {}", - result.trim(), - log.category(), - state_emoji(log), - link - ) - } -} - -fn htmlify(text: &str) -> String { - text.split("\n\n") - .map(|chunk| { - let lines = chunk.split("\n").collect::<Vec<_>>(); - format!("<b>{}</b><br>\n{}", lines[0], lines[1..].join("<br>\n")) - }) - .collect::<Vec<_>>() - .join("<br>\n<br>\n") -} - -fn state_emoji(log: &Log) -> &'static str { - let outcome = log.analyzer().and_then(|a| a.outcome()); - match outcome { - Some(Outcome::Success) => "✅", - Some(Outcome::Failure) => "❌", - None => "❓", - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::io::Cursor; - use evtclib::Compression; - - static ARKK_LOG: &[u8] = include_bytes!("../test/logs/arkk.zevtc"); - - fn arkk_log() -> Log { - evtclib::process_stream(Cursor::new(ARKK_LOG), Compression::Zip).unwrap() - } - - #[test] - fn test_insert_log_new_category() { - let log = arkk_log(); - let old_text = "\ -Unknown -x https://dps.report.example"; - let new_text = insert_log(old_text, &log, "foobar"); - - assert_eq!("\ -Unknown -x https://dps.report.example - -99 CM (Shattered Observatory) -✅ foobar", new_text); - } - - #[test] - fn test_insert_log_appending() { - let log = arkk_log(); - let old_text = "\ -99 CM (Shattered Observatory) -x old log here"; - let new_text = insert_log(old_text, &log, "foobar"); - assert_eq!("\ -99 CM (Shattered Observatory) -x old log here -✅ foobar", new_text); - } - - #[test] - fn test_insert_log_multiple() { - let log = arkk_log(); - let old_text = "\ -100 CM (Sunqua Peak) -y some old log here - -99 CM (Shattered Observatory) -x old log here - -98 CM (Nightmare) -z raboof"; - let new_text = insert_log(old_text, &log, "foobar"); - assert_eq!("\ -100 CM (Sunqua Peak) -y some old log here - -99 CM (Shattered Observatory) -x old log here -✅ foobar - -98 CM (Nightmare) -z raboof", new_text); - } - - #[test] - fn test_htmlify_single() { - let plain = "\ -Header -Log 1 -Log 2"; - assert_eq!(htmlify(plain), "\ -<b>Header</b><br> -Log 1<br> -Log 2"); - } - - #[test] - fn test_htmlify_multiple() { - let plain = "\ -Header 1 -Log 1 -Log 2 - -Header 2 -Log 3 -Log 4"; - assert_eq!(htmlify(plain), "\ -<b>Header 1</b><br> -Log 1<br> -Log 2<br> -<br> -<b>Header 2</b><br> -Log 3<br> -Log 4"); - } - +/// Inserts the given log into the text. +/// +/// The text is first parsed as a [`LogBag`], then the link is formatted and inserted. +fn insert_log(old_text: &str, log: &Log, link: &str) -> LogBag { + let line = format!("{} {}", state_emoji(log), link); + let mut logbag = LogBag::parse_plain(old_text).unwrap(); + logbag.insert(log.category(), line); + logbag } |