RUFF/src/responder.rs
LordMZTE 48d479973b
Some checks failed
continuous-integration/drone/push Build is failing
meme cache now refreshed after response has been sent
2021-06-22 14:01:33 +02:00

154 lines
5.2 KiB
Rust

use crate::Bot;
use anyhow::Context;
use log::{error, info, warn};
use matrix_sdk::{
api::r0::media::create_content,
events::{
room::message::{
ImageMessageEventContent, MessageEventContent, MessageType, VideoMessageEventContent,
},
AnyMessageEventContent,
},
identifiers::MxcUri,
room::{Joined, Room},
};
use serde::{Deserialize, Serialize};
use std::io::Cursor;
use std::sync::atomic::Ordering;
#[derive(Debug, Deserialize, Serialize)]
struct CachedMeme {
mxc: MxcUri,
ty: CachedMemeType,
}
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)),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
enum CachedMemeType {
Image,
Video,
}
impl CachedMemeType {
fn from_mime_name(name: &mime::Name) -> Option<Self> {
match *name {
mime::VIDEO => Some(Self::Video),
mime::IMAGE => Some(Self::Image),
_ => None,
}
}
}
pub async fn on_msg(msg: &str, room: Room, bot: &Bot) -> anyhow::Result<()> {
let room = match room {
Room::Joined(room) => room,
_ => {
warn!(
"Received message '{}' in room {:?} that's not joined",
msg,
room.name()
);
return Ok(());
}
};
for meme in &bot.config.memes {
if meme.matches(msg) {
bot.meme_count.fetch_add(1, Ordering::SeqCst);
let meme_name = &meme.keyword;
if let Some(meme) = meme.get_meme(bot).await? {
match meme.id.parse::<u32>() {
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::<CachedMeme>(&ivec)?;
send_meme(&room, cached, meme_name.clone()).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 ty = CachedMemeType::from_mime_name(&mime.type_())
.context("Found meme that is neither video nor image!")?;
let create_content::Response { content_uri, .. } = bot
.client
.read()
.await
.upload(&mime, &mut Cursor::new(resp))
.await?;
let cached = CachedMeme {
mxc: content_uri,
ty,
};
bot.memecache
.insert(id.to_le_bytes(), bincode::serialize(&cached)?)?;
send_meme(&room, cached, meme_name.clone()).await?;
} else {
error!(
"Couldn't guess MIME type of meme '{}', skipping.",
&meme.link
);
}
}
}
}
} else {
error!("Found meme with invalid id! {:?}", &meme);
}
}
// 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 send_meme(room: &Joined, cached: CachedMeme, meme_name: String) -> anyhow::Result<()> {
let msg_ty = cached.into_message_type(meme_name);
room.send(
AnyMessageEventContent::RoomMessage(MessageEventContent::new(msg_ty)),
None,
)
.await
.context("Failed to send meme")?;
Ok(())
}