1use std::future::Future;
2
3use crate::BotError;
4use crate::handler::{BoxedHandler, IntoHandler};
5
6pub trait Bot: Sized + Send {
12 fn run(self) -> impl Future<Output = Result<BotHandle, BotError>> + Send;
16}
17
18pub struct BotHandle {
20 shutdown_tx: async_channel::Sender<()>,
21}
22
23impl BotHandle {
24 pub fn new(shutdown_tx: async_channel::Sender<()>) -> Self {
26 Self { shutdown_tx }
27 }
28
29 pub async fn shutdown(self) {
31 let _ = self.shutdown_tx.send(()).await;
32 }
33
34 pub fn channel() -> (Self, async_channel::Receiver<()>) {
36 let (tx, rx) = async_channel::bounded(1);
37 (Self::new(tx), rx)
38 }
39}
40
41pub struct BotBuilder {
43 handlers: Vec<HandlerEntry>,
44}
45
46struct HandlerEntry {
47 pattern: HandlerPattern,
48 handler: BoxedHandler,
49 description: Option<String>,
50}
51
52#[derive(Clone)]
53pub enum HandlerPattern {
54 Command(String),
55 Button(String),
56 Message,
57}
58
59impl HandlerPattern {
60 pub fn matches(&self, event_type: &str, value: &str) -> bool {
61 match self {
62 Self::Command(name) => event_type == "command" && name == value,
63 Self::Button(pattern) => {
64 if event_type != "button" {
65 return false;
66 }
67 if pattern.ends_with('*') {
68 value.starts_with(&pattern[..pattern.len() - 1])
69 } else {
70 pattern == value
71 }
72 }
73 Self::Message => event_type == "message",
74 }
75 }
76}
77
78impl BotBuilder {
79 pub fn new() -> Self {
81 Self {
82 handlers: Vec::new(),
83 }
84 }
85
86 pub fn command<H, Args>(mut self, name: impl Into<String>, handler: H) -> Self
104 where
105 H: IntoHandler<Args>,
106 {
107 self.handlers.push(HandlerEntry {
108 pattern: HandlerPattern::Command(name.into()),
109 handler: handler.into_handler(),
110 description: None,
111 });
112 self
113 }
114
115 pub fn command_with_description<H, Args>(
119 mut self,
120 name: impl Into<String>,
121 description: impl Into<String>,
122 handler: H,
123 ) -> Self
124 where
125 H: IntoHandler<Args>,
126 {
127 self.handlers.push(HandlerEntry {
128 pattern: HandlerPattern::Command(name.into()),
129 handler: handler.into_handler(),
130 description: Some(description.into()),
131 });
132 self
133 }
134
135 pub fn button<H, Args>(mut self, pattern: impl Into<String>, handler: H) -> Self
139 where
140 H: IntoHandler<Args>,
141 {
142 self.handlers.push(HandlerEntry {
143 pattern: HandlerPattern::Button(pattern.into()),
144 handler: handler.into_handler(),
145 description: None,
146 });
147 self
148 }
149
150 pub fn message<H, Args>(mut self, handler: H) -> Self
152 where
153 H: IntoHandler<Args>,
154 {
155 self.handlers.push(HandlerEntry {
156 pattern: HandlerPattern::Message,
157 handler: handler.into_handler(),
158 description: None,
159 });
160 self
161 }
162
163 pub fn commands(&self) -> impl Iterator<Item = (&str, &str)> {
168 self.handlers.iter().filter_map(|entry| {
169 if let HandlerPattern::Command(name) = &entry.pattern {
170 let desc = entry.description.as_deref().unwrap_or("");
171 Some((name.as_str(), desc))
172 } else {
173 None
174 }
175 })
176 }
177
178 pub fn find_handler(&self, event_type: &str, value: &str) -> Option<BoxedHandler> {
180 self.handlers
181 .iter()
182 .find(|entry| entry.pattern.matches(event_type, value))
183 .map(|entry| entry.handler.clone())
184 }
185}
186
187impl Default for BotBuilder {
188 fn default() -> Self {
189 Self::new()
190 }
191}