216 lines
6.4 KiB
Rust
216 lines
6.4 KiB
Rust
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, 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,
|
|
/// MIME type of the meme
|
|
#[serde(with = "util::mime_serialize")]
|
|
mime: Mime,
|
|
/// file size of the meme
|
|
size: UInt,
|
|
/// file name of the meme
|
|
meme_name: String,
|
|
}
|
|
|
|
impl CachedMeme {
|
|
fn into_message_type(self) -> Option<MessageType> {
|
|
let Self {
|
|
mxc,
|
|
mime,
|
|
size,
|
|
meme_name,
|
|
} = self;
|
|
|
|
match mime.type_() {
|
|
mime::IMAGE => Some(MessageType::Image(ImageMessageEventContent::plain(
|
|
meme_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(
|
|
meme_name,
|
|
mxc,
|
|
Some(Box::new({
|
|
let mut info = VideoInfo::new();
|
|
info.mimetype = Some(mime.to_string());
|
|
info.size = Some(size);
|
|
info
|
|
})),
|
|
))),
|
|
_ => 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(());
|
|
}
|
|
};
|
|
|
|
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::<u32>() {
|
|
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) {
|
|
if let Some(id) = meme.get_id(bot).await? {
|
|
cache_send_meme(id, bot, room).await?;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
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::<u32>().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::<CachedMeme>(&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(())
|
|
}
|
|
|
|
async fn send_meme(room: &Joined, cached: CachedMeme) -> anyhow::Result<()> {
|
|
let msg_ty = cached
|
|
.into_message_type()
|
|
.context("Found meme that is neither image nor video!")?;
|
|
|
|
room.send(
|
|
AnyMessageEventContent::RoomMessage(MessageEventContent::new(msg_ty)),
|
|
None,
|
|
)
|
|
.await
|
|
.context("Failed to send meme")?;
|
|
|
|
Ok(())
|
|
}
|