119 lines
3.4 KiB
Rust
119 lines
3.4 KiB
Rust
use crate::Bot;
|
|
use rand::seq::IteratorRandom;
|
|
use serde::Deserialize;
|
|
|
|
pub const ALLOWED_SPACES: &str = " ,.;:!?({-_";
|
|
|
|
#[derive(Debug)]
|
|
pub struct Meme {
|
|
pub keyword: String,
|
|
pub ident: MemeIdent,
|
|
pub matcher: Matcher,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum Matcher {
|
|
Begins,
|
|
Contains,
|
|
}
|
|
|
|
impl Meme {
|
|
/// checks if the meme should be triggered for the given message
|
|
pub fn matches(&self, msg: &str) -> bool {
|
|
let msg = msg.to_ascii_lowercase();
|
|
|
|
match self.matcher {
|
|
Matcher::Begins => {
|
|
msg.starts_with(&self.keyword) &&
|
|
// msg must have one of allowed chars after keyword
|
|
msg.chars().nth(self.keyword.len()).map(|c| ALLOWED_SPACES.contains(c)).unwrap_or(true)
|
|
}
|
|
|
|
Matcher::Contains => msg
|
|
.match_indices(&self.keyword)
|
|
.map(|(idx, subs)| {
|
|
(idx == 0
|
|
|| msg
|
|
.chars()
|
|
.nth(idx - 1)
|
|
.map(|c| ALLOWED_SPACES.contains(c))
|
|
.unwrap_or(true))
|
|
&& msg
|
|
.chars()
|
|
.nth(idx + subs.len())
|
|
.map(|c| ALLOWED_SPACES.contains(c))
|
|
.unwrap_or(true)
|
|
})
|
|
.any(|b| b),
|
|
}
|
|
}
|
|
|
|
pub async fn get_meme(&self, bot: &Bot) -> anyhow::Result<Option<jm_client_core::api::Meme>> {
|
|
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()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum MemeIdent {
|
|
RandomCat(String),
|
|
Id(u32),
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn matches_begins_test() {
|
|
let meme = Meme {
|
|
keyword: String::from("test"),
|
|
ident: MemeIdent::Id(42),
|
|
matcher: Matcher::Begins,
|
|
};
|
|
|
|
assert!(!meme.matches("xxx"));
|
|
assert!(!meme.matches("testxxx"));
|
|
assert!(!meme.matches("xxxtestxxx"));
|
|
assert!(!meme.matches("xxxtest xxx"));
|
|
assert!(!meme.matches("xxx testxxx"));
|
|
assert!(meme.matches("test"));
|
|
assert!(meme.matches("test xxx"));
|
|
assert!(meme.matches("test; xxx"));
|
|
assert!(meme.matches("test;xxx"));
|
|
assert!(meme.matches("TEST"));
|
|
assert!(meme.matches("TeSt"));
|
|
}
|
|
|
|
#[test]
|
|
fn matches_contains_test() {
|
|
let meme = Meme {
|
|
keyword: String::from("test"),
|
|
ident: MemeIdent::Id(42),
|
|
matcher: Matcher::Contains,
|
|
};
|
|
|
|
assert!(!meme.matches("xxx"));
|
|
assert!(!meme.matches("xxxtestxxx"));
|
|
assert!(!meme.matches("xxxtest xxx"));
|
|
assert!(!meme.matches("xxx testxxx"));
|
|
assert!(!meme.matches("xxxtest"));
|
|
assert!(!meme.matches("testxxx"));
|
|
assert!(meme.matches("xxx test xxx"));
|
|
assert!(meme.matches("xxx,test.xxx"));
|
|
assert!(meme.matches("xxx,TEST.xxx"));
|
|
assert!(meme.matches("xxx,TeSt.xxx"));
|
|
}
|
|
}
|