Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 263 additions & 6 deletions src/types/events/base-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ export type SlackEvent =
| TeamRenameEvent
| TokensRevokedEvent
| UserChangeEvent
| UserHuddleChangedEvent
| UserProfileChangedEvent
| UserStatusChangedEvent
| WorkflowDeletedEvent
| WorkflowPublishedEvent
| WorkflowUnpublishedEvent
Expand Down Expand Up @@ -859,6 +862,12 @@ export interface TokensRevokedEvent {

// NOTE: url_verification does not use the envelope, but its also not interesting for an app developer. its omitted.

export interface StatusEmojiDisplayInfo {
emoji_name?: string;
display_alias?: string;
display_url?: string;
}

export interface UserChangeEvent {
type: 'user_change';
user: {
Expand All @@ -882,11 +891,258 @@ export interface UserChangeEvent {
status_text: string;
status_text_canonical: string;
status_emoji: string;
status_emoji_display_info: StatusEmojiDisplayInfo[];
status_expiration: number;
avatar_hash: string;
huddle_state?: string;
huddle_state_expiration_ts?: number;
first_name: string;
last_name: string;
email?: string;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't see this field either, even though the "is_email_confirmed" field WAS present and it said "true" 🤔

image_original?: string;
is_custom_image?: boolean;
image_24: string;
image_32: string;
image_48: string;
image_72: string;
image_192: string;
image_512: string;
image_1024?: string;
team: string;
fields:
| {
[key: string]: {
value: string;
alt: string;
};
}
| []
| null;
};
is_admin: boolean;
is_owner: boolean;
is_primary_owner: boolean;
is_restricted: boolean;
is_ultra_restricted: boolean;
is_bot: boolean;
is_stranger?: boolean;
updated: number;
is_email_confirmed: boolean;
is_app_user: boolean;
is_invited_user?: boolean;
has_2fa?: boolean;
locale: string;
presence?: string;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I never saw this field in my testing of these three events

enterprise_user?: {
id: string;
enterprise_id: string;
enterprise_name: string;
is_admin: boolean;
is_owner: boolean;
teams: string[];
};
two_factor_type?: string;
has_files?: boolean;
is_workflow_bot?: boolean;
who_can_share_contact_card: string;
};
cache_ts: number;
event_ts: string;
}

export interface UserHuddleChangedEvent {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@filmaj for my own reference, what source did you use for the schema reference? api.slack.com, Java/Python, or something else?

Copy link
Copy Markdown
Contributor Author

@filmaj filmaj May 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api.slack.com, I checked the public-facing docs (i.e. https://api.slack.com/events/user_huddle_changed), and all three event descriptions say:

The event is identical to the existing user_change event. Both user_change and user_huddle_changed are dispatched. at the exact same time.

... so I just copy/pasted the existing user_change event.

Sort of awkward that the verb tense goes from present tense (user_change) to past tense (user_huddle_changed)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot to be desired with respect to the accuracy / completeness of the api site and I don't know how to make it better. We don't seem to even have a good public facing SOT for user_change to base off of since the example is incomplete so, "everything is identical to user_change" feels unhelpful to me personally.

type: 'user_huddle_changed';
user: {
id: string;
team_id: string;
name: string;
deleted: boolean;
color: string;
real_name: string;
tz: string;
tz_label: string;
tz_offset: number;
profile: {
title: string;
phone: string;
skype: string;
real_name: string;
real_name_normalized: string;
display_name: string;
display_name_normalized: string;
status_text: string;
status_text_canonical: string;
status_emoji: string;
status_emoji_display_info: StatusEmojiDisplayInfo[];
status_expiration: number;
avatar_hash: string;
huddle_state: string;
huddle_state_expiration_ts: number;
first_name: string;
last_name: string;
email?: string;
image_original?: string;
is_custom_image?: boolean;
image_24: string;
image_32: string;
image_48: string;
image_72: string;
image_192: string;
image_512: string;
image_1024?: string;
team: string;
fields:
| {
[key: string]: {
value: string;
alt: string;
};
}
| []
| null;
};
is_admin: boolean;
is_owner: boolean;
is_primary_owner: boolean;
is_restricted: boolean;
is_ultra_restricted: boolean;
is_bot: boolean;
is_stranger?: boolean;
updated: number;
is_email_confirmed: boolean;
is_app_user: boolean;
is_invited_user?: boolean;
has_2fa?: boolean;
locale: string;
presence?: string;
enterprise_user?: {
id: string;
enterprise_id: string;
enterprise_name: string;
is_admin: boolean;
is_owner: boolean;
teams: string[];
};
two_factor_type?: string;
has_files?: boolean;
is_workflow_bot?: boolean;
who_can_share_contact_card: string;
};
cache_ts: number;
event_ts: string;
}

export interface UserProfileChangedEvent {
type: 'user_profile_changed';
user: {
id: string;
team_id: string;
name: string;
deleted: boolean;
color: string;
real_name: string;
tz: string;
tz_label: string;
tz_offset: number;
profile: {
title: string;
phone: string;
skype: string;
real_name: string;
real_name_normalized: string;
display_name: string;
display_name_normalized: string;
status_text: string;
status_text_canonical: string;
status_emoji: string;
status_emoji_display_info: StatusEmojiDisplayInfo[];
status_expiration: number;
avatar_hash: string;
huddle_state: string;
huddle_state_expiration_ts: number;
first_name: string;
last_name: string;
email?: string;
image_original?: string;
is_custom_image?: boolean;
image_24: string;
image_32: string;
image_48: string;
image_72: string;
image_192: string;
image_512: string;
image_1024?: string;
team: string;
fields:
| {
[key: string]: {
value: string;
alt: string;
};
}
| []
| null;
};
is_admin: boolean;
is_owner: boolean;
is_primary_owner: boolean;
is_restricted: boolean;
is_ultra_restricted: boolean;
is_bot: boolean;
is_stranger?: boolean;
updated: number;
is_email_confirmed: boolean;
is_app_user: boolean;
is_invited_user?: boolean;
has_2fa?: boolean;
locale: string;
presence?: string;
enterprise_user?: {
id: string;
enterprise_id: string;
enterprise_name: string;
is_admin: boolean;
is_owner: boolean;
teams: string[];
};
two_factor_type?: string;
has_files?: boolean;
is_workflow_bot?: boolean;
who_can_share_contact_card: string;
};
cache_ts: number;
event_ts: string;
}

export interface UserStatusChangedEvent {
type: 'user_status_changed';
user: {
id: string;
team_id: string;
name: string;
deleted: boolean;
color: string;
real_name: string;
tz: string;
tz_label: string;
tz_offset: number;
profile: {
title: string;
phone: string;
skype: string;
real_name: string;
real_name_normalized: string;
display_name: string;
display_name_normalized: string;
status_text: string;
status_text_canonical: string;
status_emoji: string;
status_emoji_display_info: StatusEmojiDisplayInfo[],
status_expiration: number;
avatar_hash: string;
first_name: string;
last_name: string;
email: string;
email?: string;
image_original?: string;
is_custom_image?: boolean;
image_24: string;
Expand Down Expand Up @@ -920,7 +1176,7 @@ export interface UserChangeEvent {
is_invited_user?: boolean;
has_2fa?: boolean;
locale: string;
presence: string;
presence?: string;
enterprise_user?: {
id: string;
enterprise_id: string;
Expand All @@ -929,12 +1185,13 @@ export interface UserChangeEvent {
is_owner: boolean;
teams: string[];
};
two_factor_type: string;
has_files: boolean;
is_workflow_bot: boolean;
who_can_share_contact_card: boolean;
two_factor_type?: string;
has_files?: boolean;
is_workflow_bot?: boolean;
who_can_share_contact_card: string;
};
cache_ts: number;
event_ts: string;
}

export interface WorkflowDeletedEvent {
Expand Down
26 changes: 25 additions & 1 deletion types-tests/event.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expectNotType, expectType } from 'tsd';
import { App, SlackEvent, AppMentionEvent, ReactionAddedEvent, ReactionRemovedEvent } from '..';
import { App, SlackEvent, AppMentionEvent, ReactionAddedEvent, ReactionRemovedEvent, UserHuddleChangedEvent, UserProfileChangedEvent, UserStatusChangedEvent } from '..';

const app = new App({ token: 'TOKEN', signingSecret: 'Signing Secret' });

Expand All @@ -26,3 +26,27 @@ expectType<void>(
await Promise.resolve(event);
})
);

expectType<void>(
app.event('user_huddle_changed', async ({ event }) => {
expectType<UserHuddleChangedEvent>(event);
expectNotType<SlackEvent>(event);
await Promise.resolve(event);
})
);

expectType<void>(
app.event('user_profile_changed', async ({ event }) => {
expectType<UserProfileChangedEvent>(event);
expectNotType<SlackEvent>(event);
await Promise.resolve(event);
})
);

expectType<void>(
app.event('user_status_changed', async ({ event }) => {
expectType<UserStatusChangedEvent>(event);
expectNotType<SlackEvent>(event);
await Promise.resolve(event);
})
);