uberbot/src/bot.rs

127 lines
3.9 KiB
Rust
Raw Normal View History

2022-07-17 13:10:34 -05:00
use crate::history::MessageHistory;
2022-07-16 05:21:23 -05:00
use crate::ExecutorConnection;
use async_trait::async_trait;
use fancy_regex::{Captures, Regex};
use std::collections::HashMap;
2022-07-28 06:21:17 -05:00
use tokio::sync::{mpsc, Mutex};
2022-07-16 05:21:23 -05:00
2022-07-24 05:22:00 -05:00
fn dissect<'a>(prefixes: &[String], str: &'a str) -> Option<(&'a str, Option<&'a str>)> {
for prefix in prefixes {
if let Some(str) = str.strip_prefix(prefix) {
return if let Some(o) = str.find(' ') {
Some((&str[..o], Some(&str[o + 1..])))
} else {
Some((str, None))
2022-07-28 06:21:17 -05:00
};
2022-07-24 05:22:00 -05:00
}
2022-07-16 05:21:23 -05:00
}
2022-07-24 05:22:00 -05:00
None
2022-07-16 05:21:23 -05:00
}
#[async_trait]
pub trait Trigger {
2022-07-17 13:10:34 -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]
pub trait Command {
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String>;
2022-07-16 05:21:23 -05:00
}
pub struct Context<'a> {
2022-07-17 13:10:34 -05:00
pub history: &'a MessageHistory,
pub author: &'a str,
2022-07-16 16:00:43 -05:00
// in case of triggers, this is always Some(...)
pub content: Option<&'a str>,
2022-07-17 13:10:34 -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<()>> {
2022-07-17 13:10:34 -05:00
history: MessageHistory,
2022-07-24 05:22:00 -05:00
prefixes: Vec<String>,
2022-07-16 05:21:23 -05:00
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>>)>,
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-24 05:22:00 -05:00
pub fn new(prefixes: Vec<String>, db: ExecutorConnection, hdepth: usize, sendmsg: SF) -> Self {
2022-07-16 05:21:23 -05:00
Bot {
2022-07-17 13:10:34 -05:00
history: MessageHistory::new(hdepth),
commands: HashMap::new(),
triggers: Vec::new(),
2022-07-24 05:22:00 -05:00
prefixes,
2022-07-16 05:21:23 -05:00
db,
sendmsg,
2022-07-16 05:21:23 -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,
origin: &str,
author: &str,
content: &str,
) -> anyhow::Result<()> {
2022-07-17 13:20:13 -05:00
let content = content.trim();
2022-07-24 05:22:00 -05:00
if let Some((command, remainder)) = dissect(&self.prefixes, content) {
tracing::debug!("Got command: {:?} -> {:?}", command, remainder);
2022-08-03 14:48:52 -05:00
if command.is_empty() {
tracing::debug!("Empty command, skipping...");
return Ok(());
}
2022-07-16 16:00:43 -05:00
if let Some(handler) = self.commands.get(command) {
let msg = Context {
2022-07-16 16:00:43 -05:00
author,
content: remainder,
2022-07-17 13:10:34 -05:00
db: &self.db,
history: &self.history,
2022-07-16 16:00:43 -05:00
};
2022-07-17 13:10:34 -05:00
return (self.sendmsg)(origin.into(), handler.lock().await.execute(msg).await?);
2022-07-16 16:00:43 -05:00
}
2022-07-17 13:10:34 -05:00
return (self.sendmsg)(origin.into(), "Unknown command.".into());
2022-07-17 13:27:20 -05:00
}
for trigger in &self.triggers {
let captures = trigger.0.captures(content)?;
if let Some(captures) = captures {
let msg = Context {
author,
content: Some(content),
db: &self.db,
history: &self.history,
};
return (self.sendmsg)(
origin.into(),
trigger.1.lock().await.execute(msg, captures).await?,
);
2022-07-16 16:00:43 -05:00
}
}
2022-07-17 13:27:20 -05:00
self.history.add_message(author, content).await;
2022-07-16 05:21:23 -05:00
Ok(())
}
2022-07-16 16:00:43 -05:00
2022-07-28 06:21:17 -05:00
pub async fn handle_message(
&self,
origin: String,
author: String,
content: String,
_cancel_handle: mpsc::Sender<()>,
) {
2022-07-28 06:18:40 -05:00
if let Err(e) = self.handle_message_inner(&origin, &author, &content).await {
2022-07-28 11:39:05 -05:00
let _err = (self.sendmsg)(origin, format!("Error: {}", e));
2022-07-16 16:00:43 -05:00
}
}
}