diff --git a/CHANGELOG.md b/CHANGELOG.md index 9166ae7..8528176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ # 0.2.5 - DATABASE FORMAT CHANGE! - memes will now have their file name as the image description. +- add sendmeme feature diff --git a/exampleconfig.toml b/exampleconfig.toml index e71808f..ec1924e 100644 --- a/exampleconfig.toml +++ b/exampleconfig.toml @@ -5,6 +5,9 @@ password = "xxx" # path to store databases store_path = "store" +# !sendmeme to make ruff send the meme with the given id +sendmeme_command = "!sendmeme" + # MEMES!! memes = [ # random stuff diff --git a/src/config.rs b/src/config.rs index 05e24e6..3ad35ef 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,6 +12,7 @@ pub struct Config { pub user_id: String, pub password: String, pub device_name: Option, + pub sendmeme_command: Option, pub memes: Vec, pub store_path: PathBuf, #[serde(default = "default_clear_threshold")] diff --git a/src/meme.rs b/src/meme.rs index 6d9f812..acd8f0b 100644 --- a/src/meme.rs +++ b/src/meme.rs @@ -1,4 +1,5 @@ use crate::Bot; +use log::error; use rand::seq::IteratorRandom; use serde::Deserialize; @@ -56,18 +57,32 @@ impl Meme { } } - pub async fn get_meme(&self, bot: &Bot) -> anyhow::Result> { + pub async fn get_id(&self, bot: &Bot) -> anyhow::Result> { let memes = bot.jm_client.read().await.get_memes().await?; match &self.ident { - MemeIdent::Id(i) => Ok(memes - .iter() - .find(|m| m.id.parse::().ok() == Some(*i)) - .cloned()), - MemeIdent::RandomCat(c) => Ok(memes - .iter() - .filter(|m| &m.category == c) - .choose(&mut *bot.rng.lock().await) - .cloned()), + MemeIdent::Id(i) => Ok(Some(*i)), + MemeIdent::RandomCat(c) => { + let meme = memes + .iter() + .filter(|m| &m.category == c) + .choose(&mut *bot.rng.lock().await); + + if let Some(meme) = meme { + let id = meme.id.parse::(); + match id { + Err(e) => { + error!( + "Error parsing meme ID {} for meme {:?} thanks to tilera's PHP api", + &meme.id, &meme + ); + Err(e.into()) + } + Ok(id) => Ok(Some(id)), + } + } else { + Ok(None) + } + } } } } diff --git a/src/responder.rs b/src/responder.rs index e557d19..8cf8e2d 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -83,86 +83,117 @@ pub async fn on_msg(msg: &str, room: Room, bot: &Bot) -> anyhow::Result<()> { } }; + if let Some(ref sendmeme_cmd) = bot.config.sendmeme_command { + let mut words = msg.split(' '); + if words.next() == Some(sendmeme_cmd) { + if let Some(id) = words.next() { + let id = if let Ok(id) = id.parse::() { + id + } else { + room.send( + AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain( + "Invalid ID!", + )), + None, + ) + .await?; + return Ok(()); + }; + + cache_send_meme(id, bot, room).await?; + return Ok(()); + } + } + } + for meme in &bot.config.memes { if meme.matches(msg) { - bot.meme_count.fetch_add(1, Ordering::SeqCst); - - if let Some(meme) = meme.get_meme(bot).await? { - match meme.id.parse::() { - Err(e) => { - error!( - "Meme {:?} has invalid ID! tilera, you messed up with your stupid php \ - again: {}", - &meme, e - ); - } - Ok(id) => { - if let Some(ivec) = bot.memecache.get(id.to_le_bytes())? { - let cached = bincode::deserialize::(&ivec)?; - send_meme(&room, cached).await?; - } else { - info!("Meme {} not found in cache, uploading...", id); - let resp = bot - .jm_client - .read() - .await - .http - .get(&meme.link) - .send() - .await - .context("error downloading meme")?; - let resp = resp.bytes().await?; - - if let Some(mime) = mime_guess::from_path(&meme.link).first() { - let size = resp.len(); - let create_content::Response { content_uri, .. } = bot - .client - .read() - .await - .upload(&mime, &mut Cursor::new(resp)) - .await?; - - let cached = CachedMeme { - mxc: content_uri, - mime, - size: UInt::new(size as u64) - .context("Meme has file size over allowed limit!")?, - meme_name: meme - .link - .split('/') - .last() - .unwrap_or(&meme.link) - .to_string(), - }; - - bot.memecache - .insert(id.to_le_bytes(), bincode::serialize(&cached)?)?; - - send_meme(&room, cached).await?; - } else { - error!( - "Couldn't guess MIME type of meme '{}', skipping.", - &meme.link - ); - } - } - } - } - } else { - error!("Found meme with invalid id! {:?}", &meme); + if let Some(id) = meme.get_id(bot).await? { + cache_send_meme(id, bot, room).await?; } - break; } + } - // we do this after we have responded, in order to not delay the response - if bot.meme_count.load(Ordering::SeqCst) >= bot.config.clear_cache_threshold { - let mut client = bot.jm_client.write().await; - bot.meme_count.store(0, Ordering::SeqCst); - client.clear_cache().await; - // memes requested but not used, but they will be cached - client.get_memes().await?; + Ok(()) +} + +async fn cache_send_meme(meme_id: u32, bot: &Bot, room: Joined) -> anyhow::Result<()> { + bot.meme_count.fetch_add(1, Ordering::SeqCst); + let memes = bot.jm_client.read().await.get_memes().await?; + let meme = memes + .iter() + .find(|m| m.id.parse::().ok() == Some(meme_id)) + .cloned(); + + if let Some(meme) = meme { + if let Some(ivec) = bot.memecache.get(meme_id.to_be_bytes())? { + let cached = bincode::deserialize::(&ivec)?; + send_meme(&room, cached).await?; + } else { + info!("Meme {} not found in cache, uploading...", meme_id); + let resp = bot + .jm_client + .read() + .await + .http + .get(&meme.link) + .send() + .await + .context("error downloading meme")?; + let resp = resp.bytes().await?; + + if let Some(mime) = mime_guess::from_path(&meme.link).first() { + let size = resp.len(); + let create_content::Response { content_uri, .. } = bot + .client + .read() + .await + .upload(&mime, &mut Cursor::new(resp)) + .await?; + + let cached = CachedMeme { + mxc: content_uri, + mime, + size: UInt::new(size as u64) + .context("Meme has file size over allowed limit!")?, + meme_name: meme + .link + .split('/') + .last() + .unwrap_or(&meme.link) + .to_string(), + }; + + bot.memecache + .insert(meme_id.to_be_bytes(), bincode::serialize(&cached)?)?; + + send_meme(&room, cached).await?; + // + // we do this after we have responded, in order to not delay the response + if bot.meme_count.load(Ordering::SeqCst) >= bot.config.clear_cache_threshold { + let mut client = bot.jm_client.write().await; + bot.meme_count.store(0, Ordering::SeqCst); + client.clear_cache().await; + // memes requested but not used, but they will be cached + client.get_memes().await?; + } + } else { + error!( + "Couldn't guess MIME type of meme '{}', skipping.", + &meme.link + ); + } } + } else { + room.send( + AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain(&format!( + "No meme with id '{}'", + meme_id + ))), + None, + ) + .await?; } Ok(())