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}
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}