Skip to main content

slack_morphism/models/events/
push.rs

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    /// Catch-all for unknown subtypes Slack may add in the future.
176    /// Preserves the original subtype string.
177    #[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}