diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b422e6..cb73566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,4 @@ # 0.2.2 +- DATABASE FORMAT CHANGE - meme cache is now updated after reponse has been sent to reduce latency +- MIME type and file size is now sent with memes diff --git a/src/main.rs b/src/main.rs index 993c2c0..9759411 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,7 @@ use serde_json::json; mod config; mod meme; mod responder; +mod util; #[derive(Debug, StructOpt)] struct Opt { diff --git a/src/responder.rs b/src/responder.rs index ee89165..cee4e30 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,55 +1,66 @@ -use crate::Bot; +use crate::{util, Bot}; use anyhow::Context; use log::{error, info, warn}; use matrix_sdk::{ api::r0::media::create_content, events::{ - room::message::{ - ImageMessageEventContent, - MessageEventContent, - MessageType, - VideoMessageEventContent, + room::{ + message::{ + ImageMessageEventContent, + MessageEventContent, + MessageType, + VideoInfo, + VideoMessageEventContent, + }, + ImageInfo, }, AnyMessageEventContent, }, identifiers::MxcUri, room::{Joined, Room}, + UInt, }; +use mime::Mime; use serde::{Deserialize, Serialize}; use std::{io::Cursor, sync::atomic::Ordering}; +/// A meme stored in the cache database #[derive(Debug, Deserialize, Serialize)] struct CachedMeme { + /// mxc url of the meme mxc: MxcUri, - ty: CachedMemeType, + /// MIME type of the meme + #[serde(with = "util::mime_serialize")] + mime: Mime, + /// file size of the meme + size: UInt, } impl CachedMeme { - fn into_message_type(self, name: String) -> MessageType { - match self { - Self { - mxc, - ty: CachedMemeType::Image, - } => MessageType::Image(ImageMessageEventContent::plain(name, mxc, None)), - Self { - mxc, - ty: CachedMemeType::Video, - } => MessageType::Video(VideoMessageEventContent::plain(name, mxc, None)), - } - } -} + fn into_message_type(self, name: String) -> Option { + let Self { mxc, mime, size } = self; -#[derive(Debug, Deserialize, Serialize)] -enum CachedMemeType { - Image, - Video, -} - -impl CachedMemeType { - fn from_mime_name(name: &mime::Name) -> Option { - match *name { - mime::VIDEO => Some(Self::Video), - mime::IMAGE => Some(Self::Image), + match mime.type_() { + mime::IMAGE => Some(MessageType::Image(ImageMessageEventContent::plain( + name, + mxc, + Some(Box::new({ + let mut info = ImageInfo::new(); + info.mimetype = Some(mime.to_string()); + info.size = Some(size); + info + })), + ))), + mime::VIDEO => Some(MessageType::Video(VideoMessageEventContent::plain( + name, + mxc, + Some(Box::new({ + let mut info = VideoInfo::new(); + info.mimetype = Some(mime.to_string()); + info.size = Some(size); + info + })), + ))), _ => None, } } @@ -100,8 +111,7 @@ pub async fn on_msg(msg: &str, room: Room, bot: &Bot) -> anyhow::Result<()> { let resp = resp.bytes().await?; if let Some(mime) = mime_guess::from_path(&meme.link).first() { - let ty = CachedMemeType::from_mime_name(&mime.type_()) - .context("Found meme that is neither video nor image!")?; + let size = resp.len(); let create_content::Response { content_uri, .. } = bot .client .read() @@ -111,7 +121,9 @@ pub async fn on_msg(msg: &str, room: Room, bot: &Bot) -> anyhow::Result<()> { let cached = CachedMeme { mxc: content_uri, - ty, + mime, + size: UInt::new(size as u64) + .context("Meme has file size over allowed limit!")?, }; bot.memecache @@ -146,7 +158,9 @@ pub async fn on_msg(msg: &str, room: Room, bot: &Bot) -> anyhow::Result<()> { } async fn send_meme(room: &Joined, cached: CachedMeme, meme_name: String) -> anyhow::Result<()> { - let msg_ty = cached.into_message_type(meme_name); + let msg_ty = cached + .into_message_type(meme_name) + .context("Found meme that is neither image nor video!")?; room.send( AnyMessageEventContent::RoomMessage(MessageEventContent::new(msg_ty)), diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..b729ba0 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,39 @@ +pub mod mime_serialize { + use mime::Mime; + use serde::{ + de::{self, Unexpected, Visitor}, + Deserializer, + Serializer, + }; + + pub fn serialize(data: &Mime, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&data.to_string()) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct Vis; + impl<'de> Visitor<'de> for Vis { + type Value = Mime; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a MIME type") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse() + .map_err(|_| de::Error::invalid_value(Unexpected::Str(v), &Vis)) + } + } + + deserializer.deserialize_str(Vis) + } +}