aboutsummaryrefslogtreecommitdiff
path: root/src/matrix.rs
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2021-03-06 12:12:03 +0100
committerDaniel Schadt <kingdread@gmx.de>2021-03-06 12:12:03 +0100
commitd84b4c3466adcc085b7df36015df4b46488ba591 (patch)
tree4193ff1cbc4fdeb57fdddff2d2b33339c3ef0efd /src/matrix.rs
parent1eb73eb5211fb43e2389857dda4f7afa15e55433 (diff)
downloadezau-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.rs196
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
}