2022-07-16 05:21:23 -05:00
|
|
|
use crate::ExecutorConnection;
|
|
|
|
use async_trait::async_trait;
|
2022-07-16 11:33:43 -05:00
|
|
|
use fancy_regex::{Captures, Regex};
|
|
|
|
use std::collections::HashMap;
|
2022-07-16 16:00:43 -05:00
|
|
|
use tokio::sync::{Mutex, RwLock};
|
2022-07-16 05:21:23 -05:00
|
|
|
|
2022-07-17 05:58:22 -05:00
|
|
|
fn dissect<'a>(prefix: &str, str: &'a str) -> Option<(&'a str, Option<&'a str>)> {
|
|
|
|
let str = str.strip_prefix(prefix)?;
|
2022-07-16 05:21:23 -05:00
|
|
|
if let Some(o) = str.find(' ') {
|
2022-07-17 05:58:22 -05:00
|
|
|
Some((&str[..o], Some(&str[o + 1..])))
|
2022-07-16 05:21:23 -05:00
|
|
|
} else {
|
2022-07-17 05:58:22 -05:00
|
|
|
Some((str, None))
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-16 11:33:43 -05:00
|
|
|
#[async_trait]
|
|
|
|
pub trait Trigger {
|
2022-07-17 05:58:22 -05:00
|
|
|
async fn execute<'a>(&mut self, msg: Context<'a>, captures: Captures<'a>) -> anyhow::Result<String>;
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2022-07-16 11:33:43 -05:00
|
|
|
pub trait Command {
|
2022-07-17 05:58:22 -05:00
|
|
|
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String>;
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
|
2022-07-17 05:58:22 -05:00
|
|
|
pub struct Context<'a> {
|
2022-07-16 16:00:43 -05:00
|
|
|
pub last_msg: &'a RwLock<HashMap<String, String>>,
|
2022-07-16 11:33:43 -05:00
|
|
|
pub author: &'a str,
|
2022-07-16 16:00:43 -05:00
|
|
|
// in case of triggers, this is always Some(...)
|
2022-07-16 11:33:43 -05:00
|
|
|
pub content: Option<&'a str>,
|
2022-07-17 05:58:22 -05:00
|
|
|
pub db: &'a ExecutorConnection
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
|
2022-07-16 16:00:43 -05:00
|
|
|
pub struct Bot<SF: Fn(String, String) -> anyhow::Result<()>> {
|
|
|
|
last_msg: RwLock<HashMap<String, String>>,
|
2022-07-16 05:21:23 -05:00
|
|
|
prefix: String,
|
|
|
|
db: ExecutorConnection,
|
2022-07-16 16:00:43 -05:00
|
|
|
commands: HashMap<String, Box<Mutex<dyn Command + Send>>>,
|
|
|
|
triggers: Vec<(Regex, Box<Mutex<dyn Trigger + Send>>)>,
|
2022-07-16 11:33:43 -05:00
|
|
|
sendmsg: SF,
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
|
2022-07-16 16:00:43 -05:00
|
|
|
impl<SF: Fn(String, String) -> anyhow::Result<()>> Bot<SF> {
|
2022-07-16 05:21:23 -05:00
|
|
|
pub fn new(prefix: String, db: ExecutorConnection, sendmsg: SF) -> Self {
|
|
|
|
Bot {
|
2022-07-16 16:00:43 -05:00
|
|
|
last_msg: RwLock::new(HashMap::new()),
|
2022-07-16 11:33:43 -05:00
|
|
|
commands: HashMap::new(),
|
|
|
|
triggers: Vec::new(),
|
2022-07-16 05:21:23 -05:00
|
|
|
prefix,
|
|
|
|
db,
|
2022-07-16 11:33:43 -05:00
|
|
|
sendmsg,
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-16 11:33:43 -05:00
|
|
|
pub fn add_command<C: Command + Send + 'static>(&mut self, name: String, cmd: C) {
|
2022-07-16 16:00:43 -05:00
|
|
|
self.commands.insert(name, Box::new(Mutex::new(cmd)));
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
|
2022-07-16 16:00:43 -05:00
|
|
|
pub fn add_trigger<C: Trigger + Send + 'static>(&mut self, regex: Regex, cmd: C) {
|
|
|
|
self.triggers.push((regex, Box::new(Mutex::new(cmd))));
|
2022-07-16 05:21:23 -05:00
|
|
|
}
|
|
|
|
|
2022-07-16 16:00:43 -05:00
|
|
|
async fn handle_message_inner(
|
|
|
|
&self,
|
2022-07-16 11:33:43 -05:00
|
|
|
origin: &str,
|
|
|
|
author: &str,
|
|
|
|
content: &str,
|
|
|
|
) -> anyhow::Result<()> {
|
2022-07-17 05:58:22 -05:00
|
|
|
if let Some((command, remainder)) = dissect(&self.prefix, content) {
|
2022-07-16 16:00:43 -05:00
|
|
|
if let Some(handler) = self.commands.get(command) {
|
2022-07-17 05:58:22 -05:00
|
|
|
let msg = Context {
|
2022-07-16 16:00:43 -05:00
|
|
|
last_msg: &self.last_msg,
|
|
|
|
author,
|
2022-07-17 05:58:22 -05:00
|
|
|
content: remainder,
|
|
|
|
db: &self.db
|
2022-07-16 16:00:43 -05:00
|
|
|
};
|
|
|
|
return (self.sendmsg)(origin.into(), handler.lock().await.execute(msg).await?)
|
|
|
|
}
|
|
|
|
return (self.sendmsg)(origin.into(), "Unknown command.".into())
|
|
|
|
} else {
|
|
|
|
for trigger in &self.triggers {
|
|
|
|
let captures = trigger.0.captures(content)?;
|
|
|
|
if let Some(captures) = captures {
|
2022-07-17 05:58:22 -05:00
|
|
|
let msg = Context {
|
2022-07-16 16:00:43 -05:00
|
|
|
last_msg: &self.last_msg,
|
|
|
|
author,
|
2022-07-17 05:58:22 -05:00
|
|
|
content: Some(content),
|
|
|
|
db: &self.db
|
2022-07-16 16:00:43 -05:00
|
|
|
};
|
|
|
|
return (self.sendmsg)(origin.into(), trigger.1.lock().await.execute(msg, captures).await?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.last_msg.write().await.insert(author.to_string(), content.to_string());
|
|
|
|
}
|
2022-07-16 05:21:23 -05:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-07-16 16:00:43 -05:00
|
|
|
|
|
|
|
pub async fn handle_message(
|
|
|
|
&self,
|
|
|
|
origin: &str,
|
|
|
|
author: &str,
|
|
|
|
content: &str,
|
|
|
|
) {
|
|
|
|
if let Err(e) = self.handle_message_inner(origin, author, content).await {
|
|
|
|
let _ = (self.sendmsg)(origin.into(), format!("Error: {}", e));
|
|
|
|
}
|
|
|
|
}
|
2022-07-16 11:33:43 -05:00
|
|
|
}
|