Skip to main content

mattermost_bot/
lib.rs

1use std::collections::HashMap;
2use std::env;
3use std::fmt::Debug;
4
5use mattermost_api::client::{AuthenticationData, Mattermost};
6use tracing::trace;
7
8mod command;
9mod error;
10mod handler;
11pub mod response;
12
13use command::IntoCommand;
14use handler::Handler;
15
16pub use self::error::{Error, Result};
17
18pub struct MattermostBot {
19    handler: Handler,
20    client: Mattermost,
21}
22
23impl MattermostBot {
24    /// Create a `MattermostBot` using env variables for url and token.
25    ///
26    /// # Errors
27    ///
28    /// Returns [`Error::EnvVarMissing`] if the `MATTERMOST_URL` or `MATTERMOST_TOKEN` environment variable is
29    /// missing.
30    ///
31    /// Returns [`Error::MattermostApi`] if the value of `MATTERMOST_URL` cannot be parsed into a
32    /// [`url::Url`](https://docs.rs/url/latest/url/struct.Url.html)
33    pub fn new() -> Result<Self> {
34        let mm_url =
35            env::var("MATTERMOST_URL").map_err(|_| Error::EnvVarMissing("MATTERMOST_URL"))?;
36        let mm_token =
37            env::var("MATTERMOST_TOKEN").map_err(|_| Error::EnvVarMissing("MATTERMOST_TOKEN"))?;
38        let auth_data = AuthenticationData::from_access_token(mm_token);
39        let client = Mattermost::new(&mm_url, auth_data).map_err(Error::MattermostApi)?;
40        let listener = client.clone();
41        Ok(MattermostBot {
42            handler: Handler {
43                admins: Vec::new(),
44                admin_commands: HashMap::new(),
45                commands: HashMap::new(),
46                client,
47            },
48            client: listener,
49        })
50    }
51
52    #[must_use]
53    pub fn add_command<H, Args>(mut self, name: &str, handler: H) -> Self
54    where
55        H: IntoCommand<Args>,
56    {
57        self.handler
58            .commands
59            .insert(name.into(), handler.into_command());
60        trace!("adding command: {name}");
61        self
62    }
63
64    #[must_use]
65    pub fn add_admin_command<H, Args>(mut self, name: &str, handler: H) -> Self
66    where
67        H: IntoCommand<Args>,
68    {
69        self.handler
70            .admin_commands
71            .insert(name.into(), handler.into_command());
72        trace!("adding admin command: {name}");
73        self
74    }
75
76    #[must_use]
77    pub fn add_admin(mut self, name: String) -> Self {
78        trace!("adding admin: {name}");
79        self.handler.admins.push(name);
80        self
81    }
82
83    #[must_use]
84    pub fn add_admins(mut self, names: Vec<String>) -> Self {
85        trace!("adding admins: {names:?}");
86        self.handler.admins.extend(names);
87        self
88    }
89
90    /// Connect to the websocket API on the instance and listen for incoming events.
91    ///
92    /// This method loops, sending messages received from the websocket connection
93    /// to the passed handler.
94    ///
95    /// # Errors
96    ///
97    /// Returns [`Error::MattermostApi`] if there's a problem with setting up the websocket
98    /// connection.
99    pub async fn listen(self) -> Result<()> {
100        let handler = self.handler;
101        let mut listener = self.client;
102        listener
103            .connect_to_websocket(handler)
104            .await
105            .map_err(Error::MattermostApi)
106    }
107}
108
109impl Default for MattermostBot {
110    /// Create a `MattermostBot` using env variables for url and token
111    ///
112    /// # Panics
113    ///
114    /// Will panic if the `MATTERMOST_URL` or `MATTERMOST_TOKEN` environment variable is missing.
115    /// Will panic if the value of `MATTERMOST_URL` cannot be parsed into a [`url::Url`](https://docs.rs/url/latest/url/struct.Url.html)
116    fn default() -> Self {
117        match Self::new() {
118            Ok(bot) => bot,
119            Err(e) => panic!("Failed to create bot: {e:?}"),
120        }
121    }
122}
123
124impl Debug for MattermostBot {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        f.debug_struct("MattermostBot")
127            .field("commands", &self.handler.commands.keys())
128            .finish_non_exhaustive()
129    }
130}