1use rsb_derive::Builder;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4use url::Url;
5
6use crate::blocks::*;
7use crate::events::*;
8use crate::models::messages::*;
9use crate::*;
10
11#[allow(clippy::large_enum_variant)]
12#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
13#[serde(tag = "type")]
14pub enum SlackPushEvent {
15 #[serde(rename = "url_verification")]
16 UrlVerification(SlackUrlVerificationEvent),
17 #[serde(rename = "event_callback")]
18 EventCallback(SlackPushEventCallback),
19 #[serde(rename = "app_rate_limited")]
20 AppRateLimited(SlackAppRateLimitedEvent),
21}
22
23#[skip_serializing_none]
24#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
25pub struct SlackUrlVerificationEvent {
26 pub challenge: String,
27}
28
29#[skip_serializing_none]
30#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
31pub struct SlackAppRateLimitedEvent {
32 pub team_id: String,
33 pub minute_rate_limited: SlackDateTime,
34 pub api_app_id: String,
35}
36
37#[skip_serializing_none]
38#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
39pub struct SlackPushEventCallback {
40 pub team_id: SlackTeamId,
41 pub api_app_id: SlackAppId,
42 pub event: SlackEventCallbackBody,
43 pub event_id: SlackEventId,
44 pub event_time: SlackDateTime,
45 pub event_context: Option<SlackEventContext>,
46 pub authed_users: Option<Vec<SlackUserId>>,
47 pub authorizations: Option<Vec<SlackEventAuthorization>>,
48}
49
50#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
51#[serde(tag = "type", rename_all = "snake_case")]
52pub enum SlackEventCallbackBody {
53 Message(SlackMessageEvent),
54 AppHomeOpened(SlackAppHomeOpenedEvent),
55 AppMention(SlackAppMentionEvent),
56 AppUninstalled(SlackAppUninstalledEvent),
57 LinkShared(SlackLinkSharedEvent),
58 EmojiChanged(SlackEmojiChangedEvent),
59 MemberJoinedChannel(SlackMemberJoinedChannelEvent),
60 MemberLeftChannel(SlackMemberLeftChannelEvent),
61 ChannelCreated(SlackChannelCreatedEvent),
62 ChannelDeleted(SlackChannelDeletedEvent),
63 ChannelArchive(SlackChannelArchiveEvent),
64 ChannelRename(SlackChannelRenameEvent),
65 ChannelUnarchive(SlackChannelUnarchiveEvent),
66 TeamJoin(SlackTeamJoinEvent),
67 FileCreated(SlackFileCreatedEvent),
68 FileChange(SlackFileChangedEvent),
69 FileDeleted(SlackFileDeletedEvent),
70 FileShared(SlackFileSharedEvent),
71 FileUnshared(SlackFileUnsharedEvent),
72 FilePublic(SlackFilePublicEvent),
73 ReactionAdded(SlackReactionAddedEvent),
74 ReactionRemoved(SlackReactionRemovedEvent),
75 StarAdded(SlackStarAddedEvent),
76 StarRemoved(SlackStarRemovedEvent),
77 UserChange(SlackUserChangeEvent),
78 UserStatusChanged(SlackUserStatusChangedEvent),
79 AssistantThreadStarted(SlackAssistantThreadStartedEvent),
80 AssistantThreadContextChanged(SlackAssistantThreadContextChangedEvent),
81}
82
83#[skip_serializing_none]
84#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
85pub struct SlackMessageEvent {
86 #[serde(flatten)]
87 pub origin: SlackMessageOrigin,
88 #[serde(flatten)]
89 pub content: Option<SlackMessageContent>,
90 #[serde(flatten)]
91 pub sender: SlackMessageSender,
92 pub subtype: Option<SlackMessageEventType>,
93 pub hidden: Option<bool>,
94 pub message: Option<SlackMessageEventEdited>,
95 pub previous_message: Option<SlackMessageEventEdited>,
96 pub deleted_ts: Option<SlackTs>,
97}
98
99#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
100pub enum SlackMessageEventType {
101 #[serde(rename = "bot_message")]
102 BotMessage,
103 #[serde(rename = "me_message")]
104 MeMessage,
105 #[serde(rename = "channel_join")]
106 ChannelJoin,
107 #[serde(rename = "channel_leave")]
108 ChannelLeave,
109 #[serde(rename = "bot_add")]
110 BotAdd,
111 #[serde(rename = "bot_remove")]
112 BotRemove,
113 #[serde(rename = "channel_topic")]
114 ChannelTopic,
115 #[serde(rename = "channel_purpose")]
116 ChannelPurpose,
117 #[serde(rename = "channel_name")]
118 ChannelName,
119 #[serde(rename = "file_share")]
120 FileShare,
121 #[serde(rename = "message_changed")]
122 MessageChanged,
123 #[serde(rename = "message_deleted")]
124 MessageDeleted,
125 #[serde(rename = "thread_broadcast")]
126 ThreadBroadcast,
127 #[serde(rename = "tombstone")]
128 Tombstone,
129 #[serde(rename = "joiner_notification")]
130 JoinerNotification,
131 #[serde(rename = "slackbot_response")]
132 SlackbotResponse,
133 #[serde(rename = "emoji_changed")]
134 EmojiChanged,
135 #[serde(rename = "sh_room_created")]
136 SlackHuddleRoomCreated,
137 #[serde(rename = "channel_archive")]
138 ChannelArchive,
139 #[serde(rename = "channel_unarchive")]
140 ChannelUnarchive,
141 #[serde(rename = "app_conversation_leave")]
142 AppConversationLeave,
143 #[serde(rename = "bot_enable")]
144 BotEnable,
145 #[serde(rename = "bot_disable")]
146 BotDisable,
147 #[serde(rename = "pinned_item")]
148 PinnedItem,
149 #[serde(rename = "reminder_add")]
150 ReminderAdd,
151 #[serde(rename = "file_comment")]
152 FileComment,
153 #[serde(rename = "file_created")]
154 FileCreated,
155 #[serde(rename = "file_change")]
156 FileChanged,
157 #[serde(rename = "file_deleted")]
158 FileDeleted,
159 #[serde(rename = "file_shared")]
160 FileShared,
161 #[serde(rename = "file_unshared")]
162 FileUnshared,
163 #[serde(rename = "file_public")]
164 FilePublic,
165 #[serde(rename = "huddle_thread")]
166 HuddleThread,
167 #[serde(rename = "share_shortcut")]
168 ShareShortcut,
169 #[serde(rename = "channel_canvas_updated")]
170 ChannelCanvasUpdated,
171}
172
173#[skip_serializing_none]
174#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
175pub struct SlackAppHomeOpenedEvent {
176 pub user: SlackUserId,
177 pub channel: SlackChannelId,
178 pub tab: String,
179 pub view: Option<SlackView>,
180}
181
182#[skip_serializing_none]
183#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
184pub struct SlackAppMentionEvent {
185 pub user: SlackUserId,
186 pub channel: SlackChannelId,
187 #[serde(flatten)]
188 pub content: SlackMessageContent,
189 #[serde(flatten)]
190 pub origin: SlackMessageOrigin,
191 pub edited: Option<SlackMessageEdited>,
192}
193
194#[skip_serializing_none]
195#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
196pub struct SlackMessageEventEdited {
197 #[serde(flatten)]
198 pub content: Option<SlackMessageContent>,
199 #[serde(flatten)]
200 pub sender: SlackMessageSender,
201 pub ts: SlackTs,
202 pub edited: Option<SlackMessageEdited>,
203}
204
205#[skip_serializing_none]
206#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
207pub struct SlackAppUninstalledEvent {}
208
209#[skip_serializing_none]
210#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
211pub struct SlackLinkSharedEvent {
212 pub channel: SlackChannelId,
213 pub event_ts: SlackTs,
214 pub is_bot_user_member: bool,
215 pub links: Vec<SlackLinkObject>,
216 pub message_ts: SlackTs,
217 pub source: Option<String>,
218 pub unfurl_id: Option<SlackUnfurlId>,
219 pub user: SlackUserId,
220}
221
222#[skip_serializing_none]
223#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
224pub struct SlackEmojiChangedEvent {
225 pub subtype: SlackEmojiEventType,
226 pub name: Option<String>,
227 pub names: Option<Vec<String>>,
228 pub old_name: Option<String>,
229 pub new_name: Option<String>,
230 pub value: Option<Url>,
231 pub event_ts: SlackTs,
232}
233
234#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
235pub enum SlackEmojiEventType {
236 #[serde(rename = "remove")]
237 EmojiRemoved,
238 #[serde(rename = "add")]
239 EmojiAdded,
240 #[serde(rename = "rename")]
241 EmojiRenamed,
242}
243
244#[skip_serializing_none]
245#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
246pub struct SlackLinkObject {
247 pub domain: String,
248 pub url: Url,
249}
250
251#[skip_serializing_none]
252#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
253pub struct SlackMemberJoinedChannelEvent {
254 pub user: SlackUserId,
255 pub channel: SlackChannelId,
256 pub channel_type: SlackChannelType,
257 pub team: SlackTeamId,
258 pub inviter: Option<SlackUserId>,
259}
260
261#[skip_serializing_none]
262#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
263pub struct SlackMemberLeftChannelEvent {
264 pub user: SlackUserId,
265 pub channel: SlackChannelId,
266 pub channel_type: SlackChannelType,
267 pub team: SlackTeamId,
268}
269
270#[skip_serializing_none]
271#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
272pub struct SlackChannelCreatedEvent {
273 pub channel: SlackChannelInfo,
274}
275
276#[skip_serializing_none]
277#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
278pub struct SlackChannelDeletedEvent {
279 pub channel: SlackChannelId,
280}
281
282#[skip_serializing_none]
283#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
284pub struct SlackChannelArchiveEvent {
285 pub channel: SlackChannelId,
286 pub user: SlackUserId,
287}
288
289#[skip_serializing_none]
290#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
291pub struct SlackChannelRenameEvent {
292 pub channel: SlackChannelInfo,
293}
294
295#[skip_serializing_none]
296#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
297pub struct SlackChannelUnarchiveEvent {
298 pub channel: SlackChannelId,
299 pub user: SlackUserId,
300}
301
302#[skip_serializing_none]
303#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
304pub struct SlackTeamJoinEvent {
305 pub user: SlackUser,
306}
307
308#[skip_serializing_none]
309#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
310pub struct SlackFileCreatedEvent {
311 pub user_id: SlackUserId,
312 pub file_id: SlackFileId,
313 pub file: SlackFile,
314}
315
316#[skip_serializing_none]
317#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
318pub struct SlackFileChangedEvent {
319 pub file_id: SlackFileId,
320}
321
322#[skip_serializing_none]
323#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
324pub struct SlackFileDeletedEvent {
325 pub file_id: SlackFileId,
326 pub event_ts: SlackTs,
327}
328
329#[skip_serializing_none]
330#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
331pub struct SlackFileSharedEvent {
332 pub channel_id: SlackChannelId,
333 pub file_id: SlackFileId,
334 pub user_id: SlackUserId,
335 pub event_ts: SlackTs,
336}
337
338#[skip_serializing_none]
339#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
340pub struct SlackFileUnsharedEvent {
341 pub file_id: SlackFileId,
342}
343
344#[skip_serializing_none]
345#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
346pub struct SlackFilePublicEvent {
347 pub file_id: SlackFileId,
348}
349
350#[skip_serializing_none]
351#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
352pub struct SlackReactionAddedEvent {
353 pub user: SlackUserId,
354 pub reaction: SlackReactionName,
355 pub item_user: Option<SlackUserId>,
356 pub item: SlackReactionsItem,
357 pub event_ts: SlackTs,
358}
359
360#[skip_serializing_none]
361#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
362pub struct SlackReactionRemovedEvent {
363 pub user: SlackUserId,
364 pub reaction: SlackReactionName,
365 pub item_user: Option<SlackUserId>,
366 pub item: SlackReactionsItem,
367 pub event_ts: SlackTs,
368}
369
370#[skip_serializing_none]
371#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
372pub struct SlackStarAddedEvent {
373 pub user: SlackUserId,
374 pub item: SlackStarsItem,
375 pub event_ts: SlackTs,
376}
377
378#[skip_serializing_none]
379#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
380pub struct SlackStarRemovedEvent {
381 pub user: SlackUserId,
382 pub item: SlackStarsItem,
383 pub event_ts: SlackTs,
384}
385
386#[skip_serializing_none]
387#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
388pub struct SlackUserChangeEvent {
389 pub user: SlackUser,
390 pub event_ts: SlackTs,
391 pub cache_ts: SlackDateTime,
392}
393
394#[skip_serializing_none]
395#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
396pub struct SlackUserStatusChangedEvent {
397 pub user: SlackUser,
398 pub event_ts: SlackTs,
399 pub cache_ts: SlackDateTime,
400}
401
402#[skip_serializing_none]
403#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
404pub struct SlackAssistantThreadStartedEvent {
405 pub event_ts: SlackTs,
406 pub assistant_thread: SlackAssistantThread,
407}
408
409#[skip_serializing_none]
410#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
411pub struct SlackAssistantThreadContextChangedEvent {
412 pub event_ts: SlackTs,
413 pub assistant_thread: SlackAssistantThread,
414}
415
416#[cfg(test)]
417mod test {
418 use super::*;
419
420 #[test]
421 fn test_slack_event_message_change_event() {
422 let payload = include_str!("./fixtures/message_changed.json");
423 let event: SlackPushEventCallback = serde_json::from_str(payload).unwrap();
424 match event.event {
425 SlackEventCallbackBody::Message(SlackMessageEvent {
426 subtype,
427 message,
428 previous_message,
429 ..
430 }) => {
431 assert_eq!(subtype, Some(SlackMessageEventType::MessageChanged));
432 if let Some(message) = message {
433 assert_eq!(message.sender.user, Some("UXXXXXXXXXX".into()));
434 assert_eq!(message.sender.bot_id, None);
435 assert_eq!(message.ts, "1701735043.989889".into());
436 assert_eq!(
437 message.edited.map(|edited| edited.ts),
438 Some("1701743154.000000".into())
439 );
440 assert_eq!(
441 message.content.unwrap().text,
442 Some("edited message".to_string())
443 );
444 } else {
445 panic!("Message is None");
446 }
447 if let Some(previous_message) = previous_message {
448 assert_eq!(previous_message.sender.user, Some("UXXXXXXXXXX".into()));
449 assert_eq!(previous_message.sender.bot_id, None);
450 assert_eq!(previous_message.ts, "1701735043.989889".into());
451 assert_eq!(
452 previous_message.edited.map(|edited| edited.ts),
453 Some("1701742890.000000".into())
454 );
455 assert_eq!(
456 previous_message.content.unwrap().text,
457 Some("hey!".to_string())
458 );
459 } else {
460 panic!("PreviousMessage is None");
461 }
462 }
463 _ => panic!("Unexpected event type"),
464 }
465 }
466
467 #[test]
468 fn test_slack_event_message_changed_by_bot_event() {
469 let payload = include_str!("./fixtures/message_changed_by_bot.json");
470 let event: SlackPushEventCallback = serde_json::from_str(payload).unwrap();
471 match event.event {
472 SlackEventCallbackBody::Message(SlackMessageEvent {
473 subtype,
474 message,
475 previous_message,
476 ..
477 }) => {
478 assert_eq!(subtype, Some(SlackMessageEventType::MessageChanged));
479 if let Some(message) = message {
480 assert_eq!(message.sender.user, None);
481 assert_eq!(message.sender.bot_id, Some("BXXXXXXXXXX".into()));
482 assert_eq!(message.ts, "1701735043.989889".into());
483 assert_eq!(
484 message.edited.map(|edited| edited.ts),
485 Some("1701743154.000000".into())
486 );
487 assert_eq!(
488 message.content.unwrap().text,
489 Some("edited message".to_string())
490 );
491 } else {
492 panic!("Message is None");
493 }
494 if let Some(previous_message) = previous_message {
495 assert_eq!(previous_message.sender.user, None);
496 assert_eq!(previous_message.sender.bot_id, Some("BXXXXXXXXXX".into()));
497 assert_eq!(previous_message.ts, "1701735043.989889".into());
498 assert_eq!(
499 previous_message.edited.map(|edited| edited.ts),
500 Some("1701742890.000000".into())
501 );
502 assert_eq!(
503 previous_message.content.unwrap().text,
504 Some("hey!".to_string())
505 );
506 } else {
507 panic!("PreviousMessage is None");
508 }
509 }
510 _ => panic!("Unexpected event type"),
511 }
512 }
513}