1use crate::attachment::attachment::Attachment;
5use crate::block::blocks::Block;
6use crate::chat::message::Message;
7use crate::error::Error;
8use crate::http_client::{get_slack_url, ResponseMetadata, SlackWebAPIClient};
9use serde::{Deserialize, Serialize};
10use serde_with::skip_serializing_none;
11
12#[skip_serializing_none]
13#[derive(Deserialize, Serialize, Debug, Default, PartialEq)]
14pub struct ScheduledMessageRequest {
15 pub channel: String,
16 pub post_at: i32,
17 pub text: String,
18 pub as_user: Option<bool>,
19 pub attachments: Option<Vec<Attachment>>,
20 pub blocks: Option<Vec<Block>>,
21 pub link_names: Option<bool>,
22 pub parse: Option<String>,
23 pub reply_broadcast: Option<bool>,
24 pub thread_ts: Option<String>,
25 pub unfurl_links: Option<bool>,
26 pub unfurl_media: Option<bool>,
27}
28
29#[skip_serializing_none]
30#[derive(Deserialize, Serialize, Debug, Default, PartialEq)]
31pub struct ScheduledMessageResponse {
32 pub ok: bool,
33 pub error: Option<String>,
34 pub response_metadata: Option<ResponseMetadata>,
35 pub channel: Option<String>,
36 pub scheduled_message_id: Option<String>,
37 pub post_at: Option<String>,
38 pub message: Option<Message>,
39}
40
41pub async fn scheduled_message<T>(
44 client: &T,
45 param: &ScheduledMessageRequest,
46 bot_token: &str,
47) -> Result<ScheduledMessageResponse, Error>
48where
49 T: SlackWebAPIClient,
50{
51 let url = get_slack_url("chat.scheduleMessage");
52 let json = serde_json::to_string(¶m)?;
53
54 client
55 .post_json(&url, &json, bot_token)
56 .await
57 .and_then(|result| {
58 serde_json::from_str::<ScheduledMessageResponse>(&result).map_err(Error::SerdeJsonError)
59 })
60}
61
62#[cfg(test)]
63mod test {
64 use super::*;
65 use crate::block::block_object::{TextBlockObject, TextBlockType};
66 use crate::block::block_section::SectionBlock;
67 use crate::http_client::MockSlackWebAPIClient;
68
69 #[test]
70 fn convert_request() {
71 let request = ScheduledMessageRequest {
72 channel: "C1234567890".to_string(),
73 post_at: 299876400,
74 text: "Hello world".to_string(),
75 as_user: Some(true),
76 attachments: Some(vec![Attachment {
77 pretext: Some("pre-hello".to_string()),
78 text: Some("text-world".to_string()),
79 ..Default::default()
80 }]),
81 blocks: Some(vec![Block::SectionBlock(SectionBlock {
82 text: Some(TextBlockObject {
83 type_filed: TextBlockType::PlainText,
84 text: "text".to_string(),
85 ..Default::default()
86 }),
87 ..Default::default()
88 })]),
89 link_names: Some(true),
90 parse: Some("full".to_string()),
91 reply_broadcast: Some(true),
92 thread_ts: Some("1234567890.123456".to_string()),
93 unfurl_links: Some(true),
94 unfurl_media: Some(true),
95 };
96 let json = r##"{
97 "channel": "C1234567890",
98 "post_at": 299876400,
99 "text": "Hello world",
100 "as_user": true,
101 "attachments": [
102 {
103 "pretext": "pre-hello",
104 "text": "text-world"
105 }
106 ],
107 "blocks": [
108 {
109 "type": "section",
110 "text": {
111 "type": "plain_text",
112 "text": "text"
113 }
114 }
115 ],
116 "link_names": true,
117 "parse": "full",
118 "reply_broadcast": true,
119 "thread_ts": "1234567890.123456",
120 "unfurl_links": true,
121 "unfurl_media": true
122}"##;
123
124 let j = serde_json::to_string_pretty(&request).unwrap();
125 assert_eq!(json, j);
126
127 let s = serde_json::from_str::<ScheduledMessageRequest>(json).unwrap();
128 assert_eq!(request, s);
129 }
130
131 #[test]
132 fn convert_response() {
133 let response = ScheduledMessageResponse {
134 ok: true,
135 channel: Some("C1H9RESGL".to_string()),
136 scheduled_message_id: Some("Q1298393284".to_string()),
137 post_at: Some("1562180400".to_string()),
138 message: Some(Message {
139 bot_id: Some("B19LU7CSY".to_string()),
140 type_file: Some("delayed_message".to_string()),
141 text: Some("Here's a message for you in the future".to_string()),
142 user: Some("ecto1".to_string()),
143 attachments: Some(vec![Attachment {
144 fallback: Some("This is an attachment's fallback".to_string()),
145 id: Some(1),
146 text: Some("This is an attachment".to_string()),
147 ..Default::default()
148 }]),
149 subtype: Some("bot_message".to_string()),
150 ..Default::default()
151 }),
152 ..Default::default()
153 };
154 let json = r##"{
155 "ok": true,
156 "channel": "C1H9RESGL",
157 "scheduled_message_id": "Q1298393284",
158 "post_at": "1562180400",
159 "message": {
160 "bot_id": "B19LU7CSY",
161 "type": "delayed_message",
162 "text": "Here's a message for you in the future",
163 "user": "ecto1",
164 "attachments": [
165 {
166 "fallback": "This is an attachment's fallback",
167 "id": 1,
168 "text": "This is an attachment"
169 }
170 ],
171 "subtype": "bot_message"
172 }
173}"##;
174
175 let j = serde_json::to_string_pretty(&response).unwrap();
176 assert_eq!(json, j);
177
178 let s = serde_json::from_str::<ScheduledMessageResponse>(json).unwrap();
179 assert_eq!(response, s);
180 }
181
182 #[async_std::test]
183 async fn test_scheduled_message() {
184 let param = ScheduledMessageRequest {
185 channel: "C1234567890".to_string(),
186 post_at: 299876400,
187 text: "Hello world".to_string(),
188 as_user: Some(true),
189 attachments: Some(vec![Attachment {
190 pretext: Some("pre-hello".to_string()),
191 text: Some("text-world".to_string()),
192 ..Default::default()
193 }]),
194 blocks: Some(vec![Block::SectionBlock(SectionBlock {
195 text: Some(TextBlockObject {
196 type_filed: TextBlockType::PlainText,
197 text: "text".to_string(),
198 ..Default::default()
199 }),
200 ..Default::default()
201 })]),
202 link_names: Some(true),
203 parse: Some("full".to_string()),
204 reply_broadcast: Some(true),
205 thread_ts: Some("1234567890.123456".to_string()),
206 unfurl_links: Some(true),
207 unfurl_media: Some(true),
208 };
209
210 let mut mock = MockSlackWebAPIClient::new();
211 mock.expect_post_json().returning(|_, _, _| {
212 Ok(r##"{
213 "ok": true,
214 "channel": "C1H9RESGL",
215 "scheduled_message_id": "Q1298393284",
216 "post_at": "1562180400",
217 "message": {
218 "bot_id": "B19LU7CSY",
219 "type": "delayed_message",
220 "text": "Here's a message for you in the future",
221 "user": "ecto1",
222 "attachments": [
223 {
224 "fallback": "This is an attachment's fallback",
225 "id": 1,
226 "text": "This is an attachment"
227 }
228 ],
229 "subtype": "bot_message"
230 }
231}"##
232 .to_string())
233 });
234
235 let response = scheduled_message(&mock, ¶m, &"test_token".to_string())
236 .await
237 .unwrap();
238 let expect = ScheduledMessageResponse {
239 ok: true,
240 channel: Some("C1H9RESGL".to_string()),
241 scheduled_message_id: Some("Q1298393284".to_string()),
242 post_at: Some("1562180400".to_string()),
243 message: Some(Message {
244 bot_id: Some("B19LU7CSY".to_string()),
245 type_file: Some("delayed_message".to_string()),
246 text: Some("Here's a message for you in the future".to_string()),
247 user: Some("ecto1".to_string()),
248 attachments: Some(vec![Attachment {
249 fallback: Some("This is an attachment's fallback".to_string()),
250 id: Some(1),
251 text: Some("This is an attachment".to_string()),
252 ..Default::default()
253 }]),
254 subtype: Some("bot_message".to_string()),
255 ..Default::default()
256 }),
257 ..Default::default()
258 };
259
260 assert_eq!(expect, response);
261 }
262}