use std::future::Future;
use crate::BotError;
use crate::handler::{BoxedHandler, IntoHandler};
pub trait Bot: Sized + Send {
fn run(self) -> impl Future<Output = Result<BotHandle, BotError>> + Send;
}
pub struct BotHandle {
shutdown_tx: async_channel::Sender<()>,
}
impl BotHandle {
pub fn new(shutdown_tx: async_channel::Sender<()>) -> Self {
Self { shutdown_tx }
}
pub async fn shutdown(self) {
let _ = self.shutdown_tx.send(()).await;
}
pub fn channel() -> (Self, async_channel::Receiver<()>) {
let (tx, rx) = async_channel::bounded(1);
(Self::new(tx), rx)
}
}
pub struct BotBuilder {
handlers: Vec<HandlerEntry>,
}
struct HandlerEntry {
pattern: HandlerPattern,
handler: BoxedHandler,
description: Option<String>,
}
#[derive(Clone)]
pub enum HandlerPattern {
Command(String),
Button(String),
Message,
}
impl HandlerPattern {
pub fn matches(&self, event_type: &str, value: &str) -> bool {
match self {
Self::Command(name) => event_type == "command" && name == value,
Self::Button(pattern) => {
if event_type != "button" {
return false;
}
if pattern.ends_with('*') {
value.starts_with(&pattern[..pattern.len() - 1])
} else {
pattern == value
}
}
Self::Message => event_type == "message",
}
}
}
impl BotBuilder {
pub fn new() -> Self {
Self {
handlers: Vec::new(),
}
}
pub fn command<H, Args>(mut self, name: impl Into<String>, handler: H) -> Self
where
H: IntoHandler<Args>,
{
self.handlers.push(HandlerEntry {
pattern: HandlerPattern::Command(name.into()),
handler: handler.into_handler(),
description: None,
});
self
}
pub fn command_with_description<H, Args>(
mut self,
name: impl Into<String>,
description: impl Into<String>,
handler: H,
) -> Self
where
H: IntoHandler<Args>,
{
self.handlers.push(HandlerEntry {
pattern: HandlerPattern::Command(name.into()),
handler: handler.into_handler(),
description: Some(description.into()),
});
self
}
pub fn button<H, Args>(mut self, pattern: impl Into<String>, handler: H) -> Self
where
H: IntoHandler<Args>,
{
self.handlers.push(HandlerEntry {
pattern: HandlerPattern::Button(pattern.into()),
handler: handler.into_handler(),
description: None,
});
self
}
pub fn message<H, Args>(mut self, handler: H) -> Self
where
H: IntoHandler<Args>,
{
self.handlers.push(HandlerEntry {
pattern: HandlerPattern::Message,
handler: handler.into_handler(),
description: None,
});
self
}
pub fn commands(&self) -> impl Iterator<Item = (&str, &str)> {
self.handlers.iter().filter_map(|entry| {
if let HandlerPattern::Command(name) = &entry.pattern {
let desc = entry.description.as_deref().unwrap_or("");
Some((name.as_str(), desc))
} else {
None
}
})
}
pub fn find_handler(&self, event_type: &str, value: &str) -> Option<BoxedHandler> {
self.handlers
.iter()
.find(|entry| entry.pattern.matches(event_type, value))
.map(|entry| entry.handler.clone())
}
}
impl Default for BotBuilder {
fn default() -> Self {
Self::new()
}
}