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 #[serde(rename = "tabbed_canvas_updated")]
172 TabbedCanvasUpdated,
173 #[serde(rename = "channel_posting_permissions")]
174 ChannelPostingPermissions,
175 #[serde(untagged)]
178 Other(String),
179}
180
181#[skip_serializing_none]
182#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
183pub struct SlackAppHomeOpenedEvent {
184 pub user: SlackUserId,
185 pub channel: SlackChannelId,
186 pub tab: Option<String>,
187 pub view: Option<SlackView>,
188}
189
190#[skip_serializing_none]
191#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
192pub struct SlackAppMentionEvent {
193 pub user: SlackUserId,
194 pub channel: SlackChannelId,
195 #[serde(flatten)]
196 pub content: SlackMessageContent,
197 #[serde(flatten)]
198 pub origin: SlackMessageOrigin,
199 pub edited: Option<SlackMessageEdited>,
200}
201
202#[skip_serializing_none]
203#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
204pub struct SlackMessageEventEdited {
205 #[serde(flatten)]
206 pub content: Option<SlackMessageContent>,
207 #[serde(flatten)]
208 pub sender: SlackMessageSender,
209 pub ts: SlackTs,
210 pub edited: Option<SlackMessageEdited>,
211}
212
213#[skip_serializing_none]
214#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
215pub struct SlackAppUninstalledEvent {}
216
217#[skip_serializing_none]
218#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
219pub struct SlackLinkSharedEvent {
220 pub channel: SlackChannelId,
221 pub event_ts: SlackTs,
222 pub is_bot_user_member: bool,
223 pub links: Vec<SlackLinkObject>,
224 pub message_ts: SlackTs,
225 pub source: Option<String>,
226 pub unfurl_id: Option<SlackUnfurlId>,
227 pub user: SlackUserId,
228}
229
230#[skip_serializing_none]
231#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
232pub struct SlackEmojiChangedEvent {
233 pub subtype: SlackEmojiEventType,
234 pub name: Option<String>,
235 pub names: Option<Vec<String>>,
236 pub old_name: Option<String>,
237 pub new_name: Option<String>,
238 pub value: Option<Url>,
239 pub event_ts: SlackTs,
240}
241
242#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
243pub enum SlackEmojiEventType {
244 #[serde(rename = "remove")]
245 EmojiRemoved,
246 #[serde(rename = "add")]
247 EmojiAdded,
248 #[serde(rename = "rename")]
249 EmojiRenamed,
250}
251
252#[skip_serializing_none]
253#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
254pub struct SlackLinkObject {
255 pub domain: String,
256 pub url: Url,
257}
258
259#[skip_serializing_none]
260#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
261pub struct SlackMemberJoinedChannelEvent {
262 pub user: SlackUserId,
263 pub channel: SlackChannelId,
264 pub channel_type: SlackChannelType,
265 pub team: SlackTeamId,
266 pub inviter: Option<SlackUserId>,
267}
268
269#[skip_serializing_none]
270#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
271pub struct SlackMemberLeftChannelEvent {
272 pub user: SlackUserId,
273 pub channel: SlackChannelId,
274 pub channel_type: SlackChannelType,
275 pub team: SlackTeamId,
276}
277
278#[skip_serializing_none]
279#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
280pub struct SlackChannelCreatedEvent {
281 pub channel: SlackChannelInfo,
282}
283
284#[skip_serializing_none]
285#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
286pub struct SlackChannelDeletedEvent {
287 pub channel: SlackChannelId,
288}
289
290#[skip_serializing_none]
291#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
292pub struct SlackChannelArchiveEvent {
293 pub channel: SlackChannelId,
294 pub user: SlackUserId,
295}
296
297#[skip_serializing_none]
298#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
299pub struct SlackChannelRenameEvent {
300 pub channel: SlackChannelInfo,
301}
302
303#[skip_serializing_none]
304#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
305pub struct SlackChannelUnarchiveEvent {
306 pub channel: SlackChannelId,
307 pub user: SlackUserId,
308}
309
310#[skip_serializing_none]
311#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
312pub struct SlackTeamJoinEvent {
313 pub user: SlackUser,
314}
315
316#[skip_serializing_none]
317#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
318pub struct SlackFileCreatedEvent {
319 pub user_id: SlackUserId,
320 pub file_id: SlackFileId,
321 pub file: SlackFile,
322}
323
324#[skip_serializing_none]
325#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
326pub struct SlackFileChangedEvent {
327 pub file_id: SlackFileId,
328}
329
330#[skip_serializing_none]
331#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
332pub struct SlackFileDeletedEvent {
333 pub file_id: SlackFileId,
334 pub event_ts: SlackTs,
335}
336
337#[skip_serializing_none]
338#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
339pub struct SlackFileSharedEvent {
340 pub channel_id: SlackChannelId,
341 pub file_id: SlackFileId,
342 pub user_id: SlackUserId,
343 pub event_ts: SlackTs,
344}
345
346#[skip_serializing_none]
347#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
348pub struct SlackFileUnsharedEvent {
349 pub file_id: SlackFileId,
350}
351
352#[skip_serializing_none]
353#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
354pub struct SlackFilePublicEvent {
355 pub file_id: SlackFileId,
356}
357
358#[skip_serializing_none]
359#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
360pub struct SlackReactionAddedEvent {
361 pub user: SlackUserId,
362 pub reaction: SlackReactionName,
363 pub item_user: Option<SlackUserId>,
364 pub item: SlackReactionsItem,
365 pub event_ts: SlackTs,
366}
367
368#[skip_serializing_none]
369#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
370pub struct SlackReactionRemovedEvent {
371 pub user: SlackUserId,
372 pub reaction: SlackReactionName,
373 pub item_user: Option<SlackUserId>,
374 pub item: SlackReactionsItem,
375 pub event_ts: SlackTs,
376}
377
378#[skip_serializing_none]
379#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
380pub struct SlackStarAddedEvent {
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 SlackStarRemovedEvent {
389 pub user: SlackUserId,
390 pub item: SlackStarsItem,
391 pub event_ts: SlackTs,
392}
393
394#[skip_serializing_none]
395#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
396pub struct SlackUserChangeEvent {
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 SlackUserStatusChangedEvent {
405 pub user: SlackUser,
406 pub event_ts: SlackTs,
407 pub cache_ts: SlackDateTime,
408}
409
410#[skip_serializing_none]
411#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
412pub struct SlackAssistantThreadStartedEvent {
413 pub event_ts: SlackTs,
414 pub assistant_thread: SlackAssistantThread,
415}
416
417#[skip_serializing_none]
418#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
419pub struct SlackAssistantThreadContextChangedEvent {
420 pub event_ts: SlackTs,
421 pub assistant_thread: SlackAssistantThread,
422}
423
424#[cfg(test)]
425mod test {
426 use super::*;
427
428 #[test]
429 fn test_slack_event_message_change_event() {
430 let payload = include_str!("./fixtures/message_changed.json");
431 let event: SlackPushEventCallback = serde_json::from_str(payload).unwrap();
432 match event.event {
433 SlackEventCallbackBody::Message(SlackMessageEvent {
434 subtype,
435 message,
436 previous_message,
437 ..
438 }) => {
439 assert_eq!(subtype, Some(SlackMessageEventType::MessageChanged));
440 if let Some(message) = message {
441 assert_eq!(message.sender.user, Some("UXXXXXXXXXX".into()));
442 assert_eq!(message.sender.bot_id, None);
443 assert_eq!(message.ts, "1701735043.989889".into());
444 assert_eq!(
445 message.edited.map(|edited| edited.ts),
446 Some("1701743154.000000".into())
447 );
448 assert_eq!(
449 message.content.unwrap().text,
450 Some("edited message".to_string())
451 );
452 } else {
453 panic!("Message is None");
454 }
455 if let Some(previous_message) = previous_message {
456 assert_eq!(previous_message.sender.user, Some("UXXXXXXXXXX".into()));
457 assert_eq!(previous_message.sender.bot_id, None);
458 assert_eq!(previous_message.ts, "1701735043.989889".into());
459 assert_eq!(
460 previous_message.edited.map(|edited| edited.ts),
461 Some("1701742890.000000".into())
462 );
463 assert_eq!(
464 previous_message.content.unwrap().text,
465 Some("hey!".to_string())
466 );
467 } else {
468 panic!("PreviousMessage is None");
469 }
470 }
471 _ => panic!("Unexpected event type"),
472 }
473 }
474
475 #[test]
476 fn test_slack_event_message_changed_by_bot_event() {
477 let payload = include_str!("./fixtures/message_changed_by_bot.json");
478 let event: SlackPushEventCallback = serde_json::from_str(payload).unwrap();
479 match event.event {
480 SlackEventCallbackBody::Message(SlackMessageEvent {
481 subtype,
482 message,
483 previous_message,
484 ..
485 }) => {
486 assert_eq!(subtype, Some(SlackMessageEventType::MessageChanged));
487 if let Some(message) = message {
488 assert_eq!(message.sender.user, None);
489 assert_eq!(message.sender.bot_id, Some("BXXXXXXXXXX".into()));
490 assert_eq!(message.ts, "1701735043.989889".into());
491 assert_eq!(
492 message.edited.map(|edited| edited.ts),
493 Some("1701743154.000000".into())
494 );
495 assert_eq!(
496 message.content.unwrap().text,
497 Some("edited message".to_string())
498 );
499 } else {
500 panic!("Message is None");
501 }
502 if let Some(previous_message) = previous_message {
503 assert_eq!(previous_message.sender.user, None);
504 assert_eq!(previous_message.sender.bot_id, Some("BXXXXXXXXXX".into()));
505 assert_eq!(previous_message.ts, "1701735043.989889".into());
506 assert_eq!(
507 previous_message.edited.map(|edited| edited.ts),
508 Some("1701742890.000000".into())
509 );
510 assert_eq!(
511 previous_message.content.unwrap().text,
512 Some("hey!".to_string())
513 );
514 } else {
515 panic!("PreviousMessage is None");
516 }
517 }
518 _ => panic!("Unexpected event type"),
519 }
520 }
521}