rocketchat_message/
lib.rs

1//! # RocketChat Message for Rust
2//! This library is an implementation of rocket chat hooks for messages
3//!
4//! * Send text example
5//!
6//! ```
7//! let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
8//!
9//! client.send_text("Text").await?;
10//! ```
11//!
12//! * Send message example
13//!
14//! ```
15//! let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
16//!
17//! let msg = RocketChatMessage::new()
18//!     .set_text("Text")
19//!     .set_attachments(vec![RocketChatAttachment::new()
20//!         .set_title("Attachment title")
21//!         .set_title_link("https://google.fr")
22//!         .set_text("Attachment text")
23//!         .set_author_name("Author name")
24//!         .set_color("#c97149")]);
25//!
26//! client.send_message(msg).await?;
27//! ```
28//!
29//! * Send messages example
30//!
31//! ```
32//! let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
33//!
34//! let msgs = vec![
35//!     RocketChatMessage::new().set_text("Message1"),
36//!     RocketChatMessage::new().set_text("Message2"),
37//! ];
38//!
39//! client.send_messages(msgs).await?;
40//! ```
41
42use anyhow::*;
43use reqwest::blocking::Response;
44use serde::Serialize;
45
46/// A structure representing a rocket chat client
47#[derive(Debug)]
48pub struct RocketChat {
49    /// Webhook url from rocket chat
50    webhook_url: String,
51    /// Channel used to send messages (@user or #channel)
52    channel: String,
53}
54
55impl RocketChat {
56    /// Creates a new rocket chat client
57    ///
58    /// ```
59    /// let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
60    /// ```
61    pub fn new<S: Into<String>>(webhook_url: S, channel: S) -> Self {
62        Self {
63            webhook_url: webhook_url.into(),
64            channel: channel.into(),
65        }
66    }
67
68    /// Changes the channel to post messages
69    ///
70    /// ```
71    /// let mut client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
72    ///
73    /// client = client.set_channel("#channel2");
74    /// ```
75    pub fn set_channel<S: Into<String>>(mut self, channel: S) -> Self {
76        self.channel = channel.into();
77        self
78    }
79
80    /// Send simple text message
81    ///
82    /// ```
83    /// let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
84    ///
85    /// client.send_text("Text").await?;
86    /// ```
87    pub async fn send_text<S: Into<String>>(&self, msg: S) -> Result<reqwest::Response, Error> {
88        let msg = RocketChatMessage::new().set_text(msg.into());
89
90        self.send_message(msg).await
91    }
92
93    /// Send simple text message (sync)
94    ///
95    /// ```
96    /// let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
97    ///
98    /// client.send_text_sync("Text");
99    /// ```
100    pub fn send_text_sync<S: Into<String>>(&self, msg: S) -> Result<Response, Error> {
101        let msg = RocketChatMessage::new().set_text(msg.into());
102
103        self.send_message_sync(msg)
104    }
105
106    /// Send a rocket chat message
107    ///
108    /// ```
109    /// let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
110    /// let msg = RocketChatMessage::new().set_text("Text");
111    ///
112    /// client.send_message(msg).await;
113    /// ```
114    pub async fn send_message(&self, msg: RocketChatMessage) -> Result<reqwest::Response, Error> {
115        let client = reqwest::Client::new();
116
117        let msg = RocketChatMessagePayload::from((msg, self.channel.clone()));
118
119        let res = client
120            .post(&self.webhook_url)
121            .json(&msg)
122            .send()
123            .await
124            .map_err(|e| anyhow!("Request error: {:?}", e.status()))?;
125
126        if res.status() == 200 {
127            Ok(res)
128        } else {
129            Err(anyhow!("Response error: {}", res.status())) // Manage error if status is not 200
130        }
131    }
132
133    /// Send a rocket chat message (sync)
134    ///
135    /// ```
136    /// let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
137    /// let msg = RocketChatMessage::new().set_text("Text");
138    ///
139    /// client.send_message_sync(msg);
140    /// ```
141    pub fn send_message_sync(&self, msg: RocketChatMessage) -> Result<Response, Error> {
142        let client = reqwest::blocking::Client::new();
143
144        let msg = RocketChatMessagePayload::from((msg, self.channel.clone()));
145
146        let res = client
147            .post(&self.webhook_url)
148            .json(&msg)
149            .send()
150            .map_err(|e| anyhow!("Request error: {:?}", e.status()))?;
151
152        if res.status() == 200 {
153            Ok(res)
154        } else {
155            Err(anyhow!("Response error: {}", res.status())) // Manage error if status is not 200
156        }
157    }
158
159    /// Send multiple messages at the same time on the same channel
160    ///
161    /// ```
162    /// let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
163    ///
164    /// let msgs = vec![
165    ///    RocketChatMessage::new().set_text("Text"),
166    ///    RocketChatMessage::new().set_text("Text2"),
167    /// ];
168    ///
169    /// client.send_messages(msgs).await?;
170    /// ```
171    pub async fn send_messages(&self, msgs: Vec<RocketChatMessage>) -> Result<(), Error> {
172        for msg in msgs {
173            self.send_message(msg).await?;
174        }
175        Ok(())
176    }
177
178    /// Send multiple messages at the same time on the same channel (sync)
179    ///
180    /// ```
181    /// let client = RocketChat::new("ROCKET_CHAT_WEBHOOK_URL", "#channel");
182    ///
183    /// let msgs = vec![
184    ///    RocketChatMessage::new().set_text("Text"),
185    ///    RocketChatMessage::new().set_text("Text2"),
186    /// ];
187    ///
188    /// client.send_messages_sync(msgs);
189    /// ```
190    pub fn send_messages_sync(&self, msgs: Vec<RocketChatMessage>) -> Result<(), Error> {
191        for msg in msgs {
192            self.send_message_sync(msg)?;
193        }
194        Ok(())
195    }
196}
197
198/// A structure representing a rocket chat field for attachments
199#[derive(Serialize, Default)]
200pub struct Field {
201    /// Size of field (default false by rocket chat)
202    pub short: Option<bool>,
203    /// Title of field
204    pub title: String,
205    /// Value of field
206    pub value: String,
207}
208
209impl Field {
210    /// Create new field
211    ///
212    /// ```
213    /// let field = Field::new();
214    /// ```
215    pub fn new() -> Self {
216        Field::default()
217    }
218
219    /// Change the title of the field
220    ///
221    /// ```
222    /// let field = Field::new().set_title("Title");
223    /// ```
224    pub fn set_title<S: Into<String>>(mut self, title: S) -> Self {
225        self.title = title.into();
226        self
227    }
228
229    /// Change the value of the field
230    ///
231    /// ```
232    /// let field = Field::new().set_value("Value");
233    /// ```
234    pub fn set_value<S: Into<String>>(mut self, value: S) -> Self {
235        self.value = value.into();
236        self
237    }
238
239    /// Change the short of the field
240    ///
241    /// ```
242    /// let field = Field::new().set_short(true);
243    /// ```
244    pub fn set_short(mut self, value: bool) -> Self {
245        self.short = Some(value);
246        self
247    }
248}
249
250/// A structure representing a rocket chat attachment
251#[derive(Serialize, Default)]
252pub struct RocketChatAttachment {
253    /// Title of attachment
254    pub title: Option<String>,
255    /// Link for title of attachment
256    pub title_link: Option<String>,
257    /// Color on border left of attachment
258    pub color: Option<String>,
259    /// Author name of attachment
260    pub author_name: Option<String>,
261    /// Author icon of attachment (displayed only if author name is defined)
262    pub author_icon: Option<String>,
263    /// Text of attachment
264    pub text: Option<String>,
265    /// Image of attachment
266    pub image_url: Option<String>,
267    /// Fields of attachment
268    pub fields: Vec<Field>,
269}
270
271impl RocketChatAttachment {
272    /// Create new attachment
273    ///
274    /// ```
275    /// let attachment = RocketChatAttachment::new();
276    /// ```
277    pub fn new() -> Self {
278        RocketChatAttachment::default()
279    }
280
281    /// Change the title of the attachment
282    ///
283    /// ```
284    /// let attachment = RocketChatAttachment::new().set_title("Title");
285    /// ```
286    pub fn set_title<S: Into<String>>(mut self, title: S) -> Self {
287        self.title = Some(title.into());
288        self
289    }
290
291    /// Change the title link of attachment
292    ///
293    /// ```
294    /// let attachment = RocketChatAttachment::new().set_title_link("https://google.fr");
295    /// ```
296    pub fn set_title_link<S: Into<String>>(mut self, title_link: S) -> Self {
297        self.title_link = Some(title_link.into());
298        self
299    }
300
301    /// Change the color of attachment
302    ///
303    /// ```
304    /// let attachment = RocketChatAttachment::new().set_color("#c97149");
305    /// ```
306    pub fn set_color<S: Into<String>>(mut self, color: S) -> Self {
307        self.color = Some(color.into());
308        self
309    }
310
311    /// Change the author name & icon of attachment
312    ///
313    /// ```
314    /// let attachment = RocketChatAttachment::new().set_author("Author Name", Some("ICON_URL"));
315    /// ```
316    pub fn set_author<S: Into<String>>(mut self, name: S, icon: Option<S>) -> Self {
317        self.author_name = Some(name.into());
318        if let Some(icon) = icon {
319            self.author_icon = Some(icon.into());
320        }
321        self
322    }
323
324    /// Change the content of attachment
325    ///
326    /// ```
327    /// let attachment = RocketChatAttachment::new().set_text("Text");
328    /// ```
329    pub fn set_text<S: Into<String>>(mut self, text: S) -> Self {
330        self.text = Some(text.into());
331        self
332    }
333
334    /// Change the image of attachment
335    ///
336    /// ```
337    /// let attachment = RocketChatAttachment::new().set_image("IMAGE_URL");
338    /// ```
339    pub fn set_image<S: Into<String>>(mut self, url: S) -> Self {
340        self.image_url = Some(url.into());
341        self
342    }
343
344    /// Change the fields of attachment
345    ///
346    /// ```
347    /// let attachment = RocketChatAttachment::new().set_fields(vec![Field::new()
348    ///     .set_title("Field title")
349    ///     .set_value("Field value")
350    ///     .set_short(true)]);
351    /// ```
352    pub fn set_fields(mut self, fields: Vec<Field>) -> Self {
353        self.fields = fields;
354        self
355    }
356}
357
358#[derive(Serialize, Default)]
359struct RocketChatMessagePayload {
360    text: Option<String>,
361    channel: Option<String>,
362    attachments: Vec<RocketChatAttachment>,
363}
364
365impl From<(RocketChatMessage, String)> for RocketChatMessagePayload {
366    fn from(message: (RocketChatMessage, String)) -> Self {
367        Self {
368            text: message.0.text,
369            channel: Some(message.1),
370            attachments: message.0.attachments,
371        }
372    }
373}
374
375/// A structure representing a rocket chat message
376#[derive(Serialize, Default)]
377// #[serde(rename_all = "camelCase")]
378pub struct RocketChatMessage {
379    /// Text on top of attachments
380    pub text: Option<String>,
381    /// Attachments linked to message
382    pub attachments: Vec<RocketChatAttachment>,
383}
384
385impl RocketChatMessage {
386    /// Create new message
387    ///
388    /// ```
389    /// let message = RocketChatMessage::new();
390    /// ```
391    pub fn new() -> Self {
392        RocketChatMessage::default()
393    }
394
395    /// Change the content of message
396    ///
397    /// ```
398    /// let message = RocketChatMessage::new().set_text("Text");
399    /// ```
400    pub fn set_text<S: Into<String>>(mut self, text: S) -> Self {
401        self.text = Some(text.into());
402        self
403    }
404
405    /// Change the attachments of message
406    ///
407    /// ```
408    /// let attachments = vec![RocketChatAttachment::new().set_title("Title")]
409    /// let message = RocketChatMessage::new().set_attachments(attachments);
410    /// ```
411    pub fn set_attachments(mut self, attachments: Vec<RocketChatAttachment>) -> Self {
412        self.attachments = attachments;
413        self
414    }
415}