Skip to main content

stoat/commands/
handler.rs

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            // no content
92            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            // doesnt start with prefix
119            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}