add sendmeme command
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
LordMZTE 2021-07-17 15:43:01 +02:00
parent ccf8c65c80
commit 485d44c56f
5 changed files with 134 additions and 83 deletions

View file

@ -1,3 +1,4 @@
# 0.2.5
- DATABASE FORMAT CHANGE!
- memes will now have their file name as the image description.
- add sendmeme feature

View file

@ -5,6 +5,9 @@ password = "xxx"
# path to store databases
store_path = "store"
# !sendmeme <id> to make ruff send the meme with the given id
sendmeme_command = "!sendmeme"
# MEMES!!
memes = [
# random stuff

View file

@ -12,6 +12,7 @@ pub struct Config {
pub user_id: String,
pub password: String,
pub device_name: Option<String>,
pub sendmeme_command: Option<String>,
pub memes: Vec<Meme>,
pub store_path: PathBuf,
#[serde(default = "default_clear_threshold")]

View file

@ -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<Option<jm_client_core::api::Meme>> {
pub async fn get_id(&self, bot: &Bot) -> anyhow::Result<Option<u32>> {
let memes = bot.jm_client.read().await.get_memes().await?;
match &self.ident {
MemeIdent::Id(i) => Ok(memes
.iter()
.find(|m| m.id.parse::<u32>().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::<u32>();
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)
}
}
}
}
}

View file

@ -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::<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) {
bot.meme_count.fetch_add(1, Ordering::SeqCst);
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).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::<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(())