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