slack_hooked/
payload.rs

1use crate::error::Result;
2use crate::{Attachment, SlackText};
3use reqwest::Url;
4use serde::{Serialize, Serializer};
5
6/// Payload to send to slack
7/// https://api.slack.com/incoming-webhooks
8/// https://api.slack.com/methods/chat.postMessage
9#[derive(Serialize, Debug, Default)]
10pub struct Payload {
11    /// text to send
12    /// despite `text` stated as required, it does not seem to be
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub text: Option<SlackText>,
15    /// channel to send payload to
16    /// note: if not provided, this will default to channel
17    /// setup in slack
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub channel: Option<String>,
20    /// username override
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub username: Option<String>,
23    /// specific url for icon
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub icon_url: Option<Url>,
26    /// emjoi for icon
27    /// https://api.slack.com/methods/emoji.list
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub icon_emoji: Option<String>,
30    /// attachments to send
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub attachments: Option<Vec<Attachment>>,
33    /// whether slack will try to fetch links and create an attachment
34    /// https://api.slack.com/docs/unfurling
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub unfurl_links: Option<bool>,
37    /// Pass false to disable unfurling of media content
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub unfurl_media: Option<bool>,
40    /// find and link channel names and usernames
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub link_names: Option<u8>,
43    /// Change how messages are treated.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub parse: Option<Parse>,
46}
47
48/// Change how messages are treated.
49#[derive(Debug)]
50pub enum Parse {
51    /// Full
52    Full,
53    /// None
54    None,
55}
56
57impl Serialize for Parse {
58    fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
59    where
60        S: Serializer,
61    {
62        let st = match *self {
63            Parse::Full => "full",
64            Parse::None => "none",
65        };
66        serializer.serialize_str(st)
67    }
68}
69/// `PayloadBuilder` is used to build a `Payload`
70#[derive(Debug)]
71pub struct PayloadBuilder {
72    inner: Result<Payload>,
73}
74
75impl Default for PayloadBuilder {
76    fn default() -> PayloadBuilder {
77        PayloadBuilder {
78            inner: Ok(Default::default()),
79        }
80    }
81}
82
83impl PayloadBuilder {
84    /// Make a new `PayloadBuilder`
85    pub fn new() -> PayloadBuilder {
86        Default::default()
87    }
88
89    /// Set the text
90    pub fn text<S: Into<SlackText>>(self, text: S) -> PayloadBuilder {
91        match self.inner {
92            Ok(mut inner) => {
93                inner.text = Some(text.into());
94                PayloadBuilder { inner: Ok(inner) }
95            }
96            _ => self,
97        }
98    }
99
100    /// Set the channel
101    pub fn channel<S: Into<String>>(self, channel: S) -> PayloadBuilder {
102        match self.inner {
103            Ok(mut inner) => {
104                inner.channel = Some(channel.into());
105                PayloadBuilder { inner: Ok(inner) }
106            }
107            _ => self,
108        }
109    }
110
111    /// Set the username
112    pub fn username<S: Into<String>>(self, username: S) -> PayloadBuilder {
113        match self.inner {
114            Ok(mut inner) => {
115                inner.username = Some(username.into());
116                PayloadBuilder { inner: Ok(inner) }
117            }
118            _ => self,
119        }
120    }
121
122    /// Set the icon_emoji
123    pub fn icon_emoji<S: Into<String>>(self, icon_emoji: S) -> PayloadBuilder {
124        match self.inner {
125            Ok(mut inner) => {
126                inner.icon_emoji = Some(icon_emoji.into());
127                PayloadBuilder { inner: Ok(inner) }
128            }
129            _ => self,
130        }
131    }
132
133    url_builder_fn! {
134        /// Set the icon_url
135        icon_url, PayloadBuilder
136    }
137
138    /// Set the attachments
139    pub fn attachments(self, attachments: Vec<Attachment>) -> PayloadBuilder {
140        match self.inner {
141            Ok(mut inner) => {
142                inner.attachments = Some(attachments);
143                PayloadBuilder { inner: Ok(inner) }
144            }
145            _ => self,
146        }
147    }
148
149    /// whether slack will try to fetch links and create an attachment
150    /// https://api.slack.com/docs/unfurling
151    pub fn unfurl_links(self, b: bool) -> PayloadBuilder {
152        match self.inner {
153            Ok(mut inner) => {
154                inner.unfurl_links = Some(b);
155                PayloadBuilder { inner: Ok(inner) }
156            }
157            _ => self,
158        }
159    }
160
161    /// Pass false to disable unfurling of media content
162    pub fn unfurl_media(self, b: bool) -> PayloadBuilder {
163        match self.inner {
164            Ok(mut inner) => {
165                inner.unfurl_media = Some(b);
166                PayloadBuilder { inner: Ok(inner) }
167            }
168            _ => self,
169        }
170    }
171
172    /// Find and link channel names and usernames.
173    // NOTE: The Slack API doesn't seem to actually require setting `link_names` to 1, any value
174    // seems to work. However, to be faithful to their spec, we will keep the `bool_to_u8` fn
175    // around.
176    pub fn link_names(self, b: bool) -> PayloadBuilder {
177        match self.inner {
178            Ok(mut inner) => {
179                inner.link_names = Some(u8::from(b));
180                PayloadBuilder { inner: Ok(inner) }
181            }
182            _ => self,
183        }
184    }
185
186    /// Change how messages are treated.
187    pub fn parse(self, p: Parse) -> PayloadBuilder {
188        match self.inner {
189            Ok(mut inner) => {
190                inner.parse = Some(p);
191                PayloadBuilder { inner: Ok(inner) }
192            }
193            _ => self,
194        }
195    }
196
197    /// Attempt to build the `Payload`
198    pub fn build(self) -> Result<Payload> {
199        self.inner
200    }
201}