1use crate::{
2 Context as MessageContext, Error,
3 commands::{
4 Check, Command, CommandEventHandler, Context, DefaultHelpCommand, HelpCommand, Words,
5 help_command,
6 },
7};
8use state::TypeMap;
9use std::{
10 collections::HashMap,
11 fmt::Debug,
12 sync::{Arc, RwLock},
13};
14use stoat_models::v0::Message;
15
16#[derive(Clone)]
17pub struct CommandHandler<H: CommandEventHandler + Clone + Send + Sync + 'static> {
18 commands: Commands<H::Error, H::State>,
19 checks: Vec<Arc<dyn Check<H::Error, H::State>>>,
20 event_handler: H,
21 state: H::State,
22 help_command: Arc<dyn HelpCommand<H::Error, H::State>>,
23}
24
25impl<
26 H: CommandEventHandler<State = S, Error = E> + Clone + Send + Sync,
27 E: From<Error> + Clone + Debug + Send + Sync + 'static,
28 S: Debug + Clone + Send + Sync + 'static,
29> CommandHandler<H>
30{
31 pub fn new(event_handler: H, state: S) -> Self {
32 let commands = Commands::new();
33 commands.register(help_command());
34
35 Self {
36 commands,
37 checks: Vec::new(),
38 event_handler,
39 state,
40 help_command: Arc::new(DefaultHelpCommand),
41 }
42 }
43
44 pub fn register(self, commands: Vec<Command<E, S>>) -> Self {
45 for command in commands {
46 self.commands.register(command)
47 }
48
49 self
50 }
51
52 pub fn help_command<HC: HelpCommand<E, S> + 'static>(
53 mut self,
54 help_command: Option<HC>,
55 ) -> Self {
56 if let Some(help_command) = help_command {
57 self.help_command = Arc::new(help_command);
58 } else if let Some(help_command) = self.commands.get_command("help") {
59 self.commands.unregister(help_command);
60 }
61
62 self
63 }
64
65 pub fn check<C: Check<E, S>>(mut self, check: C) -> Self {
66 self.checks.push(Arc::new(check));
67
68 self
69 }
70
71 pub async fn can_run(&self, context: Context<E, S>) -> Result<bool, E> {
72 for check in &self.checks {
73 if check.run(context.clone()).await? == false {
74 return Err(Error::CheckFailure.into());
75 }
76 }
77
78 if let Some(command) = &context.command {
79 return command.can_run(context.clone()).await;
80 }
81
82 Ok(true)
83 }
84
85 pub async fn process_commands(
86 &self,
87 context: MessageContext,
88 message: Message,
89 ) -> Result<(), E> {
90 let Some(message_content) = message.content.as_deref() else {
91 return Ok(());
93 };
94
95 if message.user.as_ref().unwrap().bot.is_some() {
96 return Ok(());
97 };
98
99 let mut cmd_context = Context {
100 inner: context,
101 prefix: None,
102 command: None,
103 message: message.clone(),
104 state: self.state.clone(),
105 words: Words::new(message_content),
106 commands: self.commands.clone(),
107 help_command: self.help_command.clone(),
108 local_state: Arc::new(<TypeMap![Send + Sync]>::new()),
109 };
110
111 let prefixes = self.event_handler.get_prefix(cmd_context.clone()).await?;
112
113 let Some(prefix) = prefixes
114 .into_iter()
115 .filter(|prefix| message_content.starts_with(prefix))
116 .next()
117 else {
118 return Ok(());
120 };
121
122 let rest = &message_content[prefix.len()..];
123
124 cmd_context.words = Words::new(rest);
125 cmd_context.command = self
126 .commands
127 .find_command_from_words(None, &cmd_context.words);
128 cmd_context.prefix = Some(prefix);
129
130 if cmd_context.command.is_none() {
131 if let Err(e) = self.event_handler.no_command(cmd_context.clone()).await {
132 self.event_handler.error(cmd_context.clone(), e).await?;
133 };
134
135 return Ok(());
136 }
137
138 if let Err(e) = self.event_handler.command(cmd_context.clone()).await {
139 self.event_handler.error(cmd_context.clone(), e).await?;
140 };
141
142 if let Some(command) = cmd_context.command.as_ref() {
143 if let Err(e) = self.can_run(cmd_context.clone()).await {
144 self.event_handler.error(cmd_context.clone(), e).await?;
145 } else {
146 if let Err(e) = command.handle.handle(cmd_context.clone()).await {
147 self.event_handler.error(cmd_context.clone(), e).await?;
148 };
149
150 if let Err(e) = self.event_handler.after_command(cmd_context.clone()).await {
151 self.event_handler.error(cmd_context.clone(), e).await?;
152 };
153 }
154 }
155
156 Ok(())
157 }
158}
159
160#[derive(Debug, Clone)]
161pub struct Commands<E, S> {
162 mapping: Arc<RwLock<HashMap<String, Command<E, S>>>>,
163}
164
165impl<
166 E: From<Error> + Clone + Debug + Send + Sync + 'static,
167 S: Debug + Clone + Send + Sync + 'static,
168> Commands<E, S>
169{
170 pub fn new() -> Self {
171 Self {
172 mapping: Arc::new(RwLock::new(HashMap::new())),
173 }
174 }
175
176 pub fn register(&self, command: Command<E, S>) {
177 let mut mapping = self.mapping.write().unwrap();
178
179 mapping.insert(command.name.clone(), command.clone());
180
181 for alias in command.aliases.clone() {
182 mapping.insert(alias, command.clone());
183 }
184 }
185
186 pub fn unregister(&self, command: Command<E, S>) {
187 let mut mapping = self.mapping.write().unwrap();
188
189 mapping.remove(&command.name);
190
191 for alias in command.aliases.clone() {
192 mapping.remove(&alias);
193 }
194 }
195
196 pub fn find_command_from_words(
197 &self,
198 current_command: Option<&Command<E, S>>,
199 words: &Words,
200 ) -> Option<Command<E, S>> {
201 let next_word = words.current()?;
202
203 let commands = self.mapping.read().unwrap();
204
205 if let Some(command) = current_command
206 .and_then(|command| command.children.get(&next_word))
207 .or_else(|| commands.get(&next_word))
208 {
209 words.advance();
210
211 if !command.children.is_empty() {
212 let subcommand = self.find_command_from_words(Some(command), words);
213
214 match subcommand {
215 Some(sub) => Some(sub),
216 None => {
217 words.undo();
218
219 Some(command.clone())
220 }
221 }
222 } else {
223 Some(command.clone())
224 }
225 } else {
226 None
227 }
228 }
229
230 pub fn get_command_from_slice(&self, words: &[String]) -> Option<Command<E, S>> {
231 let mapping = self.mapping.read().unwrap();
232
233 let mut current_command: Option<Command<E, S>> = None;
234
235 for word in words {
236 if let Some(command) = current_command
237 .as_ref()
238 .and_then(|command| command.get_command(word))
239 .or_else(|| mapping.get(word).cloned())
240 {
241 current_command = Some(command)
242 } else {
243 break;
244 }
245 }
246
247 return current_command;
248 }
249
250 pub fn get_command(&self, name: &str) -> Option<Command<E, S>> {
251 self.mapping.read().unwrap().get(name).cloned()
252 }
253
254 pub fn get_commands(&self) -> Vec<Command<E, S>> {
255 self.mapping
256 .read()
257 .unwrap()
258 .clone()
259 .into_iter()
260 .filter(|(name, command)| name == &command.name)
261 .map(|(_, command)| command)
262 .collect()
263 }
264
265 pub fn get_command_parents(&self, command: &Command<E, S>) -> Vec<Command<E, S>> {
266 let mut parents: Vec<Command<E, S>> = Vec::new();
267
268 for parent in &command.parents {
269 if let Some(last_parent) = parents.last() {
270 let child = last_parent.get_command(parent).unwrap();
271 parents.push(child);
272 } else {
273 parents.push(self.get_command(parent).unwrap());
274 }
275 }
276
277 parents
278 }
279}