From 2af717d688380f1273edf300e988c86ac575ac42 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Tue, 8 Oct 2024 17:51:03 -0400 Subject: [PATCH 01/11] Adding compatibility for Slack Assistants --- assistant.go | 157 +++++++ slackevents/inner_events.go | 913 ++++++++++++++++++++++++++++++++++-- 2 files changed, 1037 insertions(+), 33 deletions(-) create mode 100644 assistant.go diff --git a/assistant.go b/assistant.go new file mode 100644 index 000000000..15fd91f06 --- /dev/null +++ b/assistant.go @@ -0,0 +1,157 @@ +package slack + +import ( + "context" + "net/url" + "strconv" +) + +// AssistantThreadSetStatusParameters are the parameters for AssistantThreadSetStatus +type AssistantThreadsSetStatusParameters struct { + ChannelID string `json:"channel_id"` + Status string `json:"status"` + ThreadTS string `json:"thread_ts"` +} + +// AssistantThreadSetTitleParameters are the parameters for AssistantThreadSetTitle +type AssistantThreadsSetTitleParameters struct { + ChannelID string `json:"channel_id"` + ThreadTS string `json:"thread_ts"` + Title string `json:"title"` +} + +// AssistantThreadSetSuggestedPromptsParameters are the parameters for AssistantThreadSetSuggestedPrompts +type AssistantThreadsSetSuggestedPromptsParameters struct { + ChannelID string `json:"channel_id"` + ThreadTS string `json:"thread_ts"` + Prompts []AssistantThreadsPrompt `json:"prompts"` +} + +// AssistantThreadPrompt is a suggested prompt for a thread +type AssistantThreadsPrompt struct { + Title string `json:"title"` + Message string `json:"message"` +} + +// AssistantThreadSetSuggestedPrompts sets the suggested prompts for a thread +func (p *AssistantThreadsSetSuggestedPromptsParameters) AddPrompt(title, message string) { + p.Prompts = append(p.Prompts, AssistantThreadsPrompt{ + Title: title, + Message: message, + }) +} + +// SetAssistantThreadsSugesstedPrompts sets the suggested prompts for a thread +// @see https://api.slack.com/methods/assistant.threads.setSuggestedPrompts +func (api *Client) SetAssistantThreadsSuggestedPrompts(params AssistantThreadsSetSuggestedPromptsParameters) (err error) { + return api.SetAssistantThreadSuggestedPromptsContext(context.Background(), params) +} + +// SetAssistantThreadSuggestedPromptsContext sets the suggested prompts for a thread with a custom context +// @see https://api.slack.com/methods/assistant.threads.setSuggestedPrompts +func (api *Client) SetAssistantThreadSuggestedPromptsContext(ctx context.Context, params AssistantThreadsSetSuggestedPromptsParameters) (err error) { + + values := url.Values{ + "token": {api.token}, + } + + if params.ChannelID != "" { + values.Add("channel_id", params.ChannelID) + } + + if params.ThreadTS != "" { + values.Add("thread_ts", params.ThreadTS) + } + + for i, prompt := range params.Prompts { + values.Add("prompts["+strconv.Itoa(i)+"][title]", prompt.Title) + values.Add("prompts["+strconv.Itoa(i)+"][message]", prompt.Message) + } + + response := struct { + SlackResponse + }{} + + err = api.postMethod(ctx, "assistant.threads.setSuggestedPrompts", values, &response) + if err != nil { + return + } + + return response.Err() +} + +// SetAssistantThreadStatus sets the status of a thread +// @see https://api.slack.com/methods/assistant.threads.setStatus +func (api *Client) SetAssistantThreadsStatus(params AssistantThreadsSetStatusParameters) (err error) { + return api.SetAssistantThreadsStatusContext(context.Background(), params) +} + +// SetAssistantThreadStatusContext sets the status of a thread with a custom context +// @see https://api.slack.com/methods/assistant.threads.setStatus +func (api *Client) SetAssistantThreadsStatusContext(ctx context.Context, params AssistantThreadsSetStatusParameters) (err error) { + + values := url.Values{ + "token": {api.token}, + } + + if params.ChannelID != "" { + values.Add("channel_id", params.ChannelID) + } + + if params.ThreadTS != "" { + values.Add("thread_ts", params.ThreadTS) + } + + // Always send the status parameter, if empty, it will clear any existing status + values.Add("status", params.Status) + + response := struct { + SlackResponse + }{} + + err = api.postMethod(ctx, "assistant.threads.setStatus", values, &response) + if err != nil { + return + } + + return response.Err() +} + +// SetAssistantThreadsTitle sets the title of a thread +// @see https://api.slack.com/methods/assistant.threads.setTitle +func (api *Client) SetAssistantThreadsTitle(params AssistantThreadsSetTitleParameters) (err error) { + return api.SetAssistantThreadsTitleContext(context.Background(), params) +} + +// SetAssistantThreadsTitleContext sets the title of a thread with a custom context +// @see https://api.slack.com/methods/assistant.threads.setTitle +func (api *Client) SetAssistantThreadsTitleContext(ctx context.Context, params AssistantThreadsSetTitleParameters) (err error) { + + values := url.Values{ + "token": {api.token}, + } + + if params.ChannelID != "" { + values.Add("channel_id", params.ChannelID) + } + + if params.ThreadTS != "" { + values.Add("thread_ts", params.ThreadTS) + } + + if params.Title != "" { + values.Add("title", params.Title) + } + + response := struct { + SlackResponse + }{} + + err = api.postMethod(ctx, "assistant.threads.setTitle", values, &response) + if err != nil { + return + } + + return response.Err() + +} diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index 02767eb8f..720640b63 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -12,6 +12,35 @@ type EventsAPIInnerEvent struct { Data interface{} } +// AssistantThreadMessageEvent is an (inner) EventsAPI subscribable event. +type AssistantThreadStartedEvent struct { + Type string `json:"type"` + AssistantThread AssistantThread `json:"assistant_thread"` + EventTimestamp string `json:"event_ts"` +} + +// AssistantThreadChangedEvent is an (inner) EventsAPI subscribable event. +type AssistantThreadContextChangedEvent struct { + Type string `json:"type"` + AssistantThread AssistantThread `json:"assistant_thread"` + EventTimestamp string `json:"event_ts"` +} + +// AssistantThread is an object that represents a thread of messages between a user and an assistant. +type AssistantThread struct { + UserID string `json:"user_id"` + Context AssistantThreadContext `json:"context"` + ChannelID string `json:"channel_id"` + ThreadTimeStamp string `json:"thread_ts"` +} + +// AssistantThreadContext is an object that represents the context of an assistant thread. +type AssistantThreadContext struct { + ChannelID string `json:"channel_id"` + TeamID string `json:"team_id"` + EnterpriseID string `json:"enterprise_id"` +} + // AppMentionEvent is an (inner) EventsAPI subscribable event. type AppMentionEvent struct { Type string `json:"type"` @@ -28,6 +57,9 @@ type AppMentionEvent struct { // BotID is filled out when a bot triggers the app_mention event BotID string `json:"bot_id,omitempty"` + + // When the app is mentioned in the edited message + Edited *Edited `json:"edited,omitempty"` } // AppHomeOpenedEvent Your Slack app home was opened. @@ -155,6 +187,47 @@ type GroupRenameInfo struct { Created int `json:"created"` } +// FileChangeEvent represents the information associated with the File change +// event. +type FileChangeEvent struct { + Type string `json:"type"` + FileID string `json:"file_id"` + File FileEventFile `json:"file"` +} + +// FileDeletedEvent represents the information associated with the File deleted +// event. +type FileDeletedEvent struct { + Type string `json:"type"` + FileID string `json:"file_id"` + EventTimestamp string `json:"event_ts"` +} + +// FileSharedEvent represents the information associated with the File shared +// event. +type FileSharedEvent struct { + Type string `json:"type"` + ChannelID string `json:"channel_id"` + FileID string `json:"file_id"` + UserID string `json:"user_id"` + File FileEventFile `json:"file"` + EventTimestamp string `json:"event_ts"` +} + +// FileUnsharedEvent represents the information associated with the File +// unshared event. +type FileUnsharedEvent struct { + Type string `json:"type"` + FileID string `json:"file_id"` + File FileEventFile `json:"file"` +} + +// FileEventFile represents information on the specific file being shared in a +// file-related Slack event. +type FileEventFile struct { + ID string `json:"id"` +} + // GridMigrationFinishedEvent An enterprise grid migration has finished on this workspace. type GridMigrationFinishedEvent struct { Type string `json:"type"` @@ -178,11 +251,11 @@ type LinkSharedEvent struct { // compose text area. MessageTimeStamp string `json:"message_ts"` ThreadTimeStamp string `json:"thread_ts"` - Links []sharedLinks `json:"links"` + Links []SharedLinks `json:"links"` EventTimestamp string `json:"event_ts"` } -type sharedLinks struct { +type SharedLinks struct { Domain string `json:"domain"` URL string `json:"url"` } @@ -215,6 +288,9 @@ type MessageEvent struct { PreviousMessage *MessageEvent `json:"previous_message,omitempty"` Edited *Edited `json:"edited,omitempty"` + // Deleted Message + DeletedTimeStamp string `json:"deleted_ts,omitempty"` + // Message Subtypes SubType string `json:"subtype,omitempty"` @@ -226,8 +302,11 @@ type MessageEvent struct { Upload bool `json:"upload"` Files []File `json:"files"` + Blocks slack.Blocks `json:"blocks,omitempty"` Attachments []slack.Attachment `json:"attachments,omitempty"` + Metadata slack.SlackMetadata `json:"metadata,omitempty"` + // Root is the message that was broadcast to the channel when the SubType is // thread_broadcast. If this is not a thread_broadcast message event, this // value is nil. @@ -332,6 +411,47 @@ type WorkflowStepExecuteEvent struct { EventTimestamp string `json:"event_ts"` } +// MessageMetadataPostedEvent is sent, if a message with metadata is posted +type MessageMetadataPostedEvent struct { + Type string `json:"type"` + AppId string `json:"app_id"` + BotId string `json:"bot_id"` + UserId string `json:"user_id"` + TeamId string `json:"team_id"` + ChannelId string `json:"channel_id"` + Metadata *slack.SlackMetadata `json:"metadata"` + MessageTimestamp string `json:"message_ts"` + EventTimestamp string `json:"event_ts"` +} + +// MessageMetadataUpdatedEvent is sent, if a message with metadata is deleted +type MessageMetadataUpdatedEvent struct { + Type string `json:"type"` + ChannelId string `json:"channel_id"` + EventTimestamp string `json:"event_ts"` + PreviousMetadata *slack.SlackMetadata `json:"previous_metadata"` + AppId string `json:"app_id"` + BotId string `json:"bot_id"` + UserId string `json:"user_id"` + TeamId string `json:"team_id"` + MessageTimestamp string `json:"message_ts"` + Metadata *slack.SlackMetadata `json:"metadata"` +} + +// MessageMetadataDeletedEvent is sent, if a message with metadata is deleted +type MessageMetadataDeletedEvent struct { + Type string `json:"type"` + ChannelId string `json:"channel_id"` + EventTimestamp string `json:"event_ts"` + PreviousMetadata *slack.SlackMetadata `json:"previous_metadata"` + AppId string `json:"app_id"` + BotId string `json:"bot_id"` + UserId string `json:"user_id"` + TeamId string `json:"team_id"` + MessageTimestamp string `json:"message_ts"` + DeletedTimestamp string `json:"deleted_ts"` +} + type EventWorkflowStep struct { WorkflowStepExecuteID string `json:"workflow_step_execute_id"` WorkflowID string `json:"workflow_id"` @@ -374,6 +494,7 @@ type File struct { DisplayAsBot bool `json:"display_as_bot"` Username string `json:"username"` URLPrivate string `json:"url_private"` + FileAccess string `json:"file_access"` URLPrivateDownload string `json:"url_private_download"` Thumb64 string `json:"thumb_64"` Thumb80 string `json:"thumb_80"` @@ -442,6 +563,580 @@ func (e MessageEvent) IsEdited() bool { e.Message.Edited != nil } +// TeamAccessGrantedEvent is sent if access to teams was granted for your org-wide app. +type TeamAccessGrantedEvent struct { + Type string `json:"type"` + TeamIDs []string `json:"team_ids"` +} + +// TeamAccessRevokedEvent is sent if access to teams was revoked for your org-wide app. +type TeamAccessRevokedEvent struct { + Type string `json:"type"` + TeamIDs []string `json:"team_ids"` +} + +// UserProfileChangedEvent is sent if access to teams was revoked for your org-wide app. +type UserProfileChangedEvent struct { + User *slack.User `json:"user"` + CacheTs int `json:"cache_ts"` + Type string `json:"type"` + EventTs string `json:"event_ts"` +} + +// SharedChannelInviteApprovedEvent is sent if your invitation has been approved +type SharedChannelInviteApprovedEvent struct { + Type string `json:"type"` + Invite *SharedInvite `json:"invite"` + Channel *slack.Conversation `json:"channel"` + ApprovingTeamID string `json:"approving_team_id"` + TeamsInChannel []*SlackEventTeam `json:"teams_in_channel"` + ApprovingUser *SlackEventUser `json:"approving_user"` + EventTs string `json:"event_ts"` +} + +// SharedChannelInviteAcceptedEvent is sent if external org accepts a Slack Connect channel invite +type SharedChannelInviteAcceptedEvent struct { + Type string `json:"type"` + ApprovalRequired bool `json:"approval_required"` + Invite *SharedInvite `json:"invite"` + Channel *SharedChannel `json:"channel"` + TeamsInChannel []*SlackEventTeam `json:"teams_in_channel"` + AcceptingUser *SlackEventUser `json:"accepting_user"` + EventTs string `json:"event_ts"` + RequiresSponsorship bool `json:"requires_sponsorship,omitempty"` +} + +// SharedChannelInviteDeclinedEvent is sent if external or internal org declines the Slack Connect invite +type SharedChannelInviteDeclinedEvent struct { + Type string `json:"type"` + Invite *SharedInvite `json:"invite"` + Channel *SharedChannel `json:"channel"` + DecliningTeamID string `json:"declining_team_id"` + TeamsInChannel []*SlackEventTeam `json:"teams_in_channel"` + DecliningUser *SlackEventUser `json:"declining_user"` + EventTs string `json:"event_ts"` +} + +// SharedChannelInviteReceivedEvent is sent if a bot or app is invited to a Slack Connect channel +type SharedChannelInviteReceivedEvent struct { + Type string `json:"type"` + Invite *SharedInvite `json:"invite"` + Channel *SharedChannel `json:"channel"` + EventTs string `json:"event_ts"` +} + +// SlackEventTeam is a struct for teams in ShareChannel events +type SlackEventTeam struct { + ID string `json:"id"` + Name string `json:"name"` + Icon *SlackEventIcon `json:"icon,omitempty"` + AvatarBaseURL string `json:"avatar_base_url,omitempty"` + IsVerified bool `json:"is_verified"` + Domain string `json:"domain"` + DateCreated int `json:"date_created"` + RequiresSponsorship bool `json:"requires_sponsorship,omitempty"` + // TeamID string `json:"team_id,omitempty"` +} + +// SlackEventIcon is a struct for icons in ShareChannel events +type SlackEventIcon struct { + ImageDefault bool `json:"image_default,omitempty"` + Image34 string `json:"image_34,omitempty"` + Image44 string `json:"image_44,omitempty"` + Image68 string `json:"image_68,omitempty"` + Image88 string `json:"image_88,omitempty"` + Image102 string `json:"image_102,omitempty"` + Image132 string `json:"image_132,omitempty"` + Image230 string `json:"image_230,omitempty"` +} + +// SlackEventUser is a struct for users in ShareChannel events +type SlackEventUser struct { + ID string `json:"id"` + TeamID string `json:"team_id"` + Name string `json:"name"` + Updated int `json:"updated,omitempty"` + Profile *slack.UserProfile `json:"profile,omitempty"` + WhoCanShareContactCard string `json:"who_can_share_contact_card,omitempty"` +} + +// SharedChannel is a struct for shared channels in ShareChannel events +type SharedChannel struct { + ID string `json:"id"` + IsPrivate bool `json:"is_private"` + IsIm bool `json:"is_im"` + Name string `json:"name,omitempty"` +} + +// SharedInvite is a struct for shared invites in ShareChannel events +type SharedInvite struct { + ID string `json:"id"` + DateCreated int `json:"date_created"` + DateInvalid int `json:"date_invalid"` + InvitingTeam *SlackEventTeam `json:"inviting_team,omitempty"` + InvitingUser *SlackEventUser `json:"inviting_user,omitempty"` + RecipientEmail string `json:"recipient_email,omitempty"` + RecipientUserID string `json:"recipient_user_id,omitempty"` + IsSponsored bool `json:"is_sponsored,omitempty"` + IsExternalLimited bool `json:"is_external_limited,omitempty"` +} + +type ChannelHistoryChangedEvent struct { + Type string `json:"type"` + Latest string `json:"latest"` + Ts string `json:"ts"` + EventTs string `json:"event_ts"` +} + +type CommandsChangedEvent struct { + Type string `json:"type"` + EventTs string `json:"event_ts"` +} + +type DndUpdatedEvent struct { + Type string `json:"type"` + User string `json:"user"` + DndStatus struct { + DndEnabled bool `json:"dnd_enabled"` + NextDndStartTs int64 `json:"next_dnd_start_ts"` + NextDndEndTs int64 `json:"next_dnd_end_ts"` + SnoozeEnabled bool `json:"snooze_enabled"` + SnoozeEndtime int64 `json:"snooze_endtime"` + } `json:"dnd_status"` +} + +type DndUpdatedUserEvent struct { + Type string `json:"type"` + User string `json:"user"` + DndStatus struct { + DndEnabled bool `json:"dnd_enabled"` + NextDndStartTs int64 `json:"next_dnd_start_ts"` + NextDndEndTs int64 `json:"next_dnd_end_ts"` + } `json:"dnd_status"` +} + +type EmailDomainChangedEvent struct { + Type string `json:"type"` + EmailDomain string `json:"email_domain"` + EventTs string `json:"event_ts"` +} + +type GroupCloseEvent struct { + Type string `json:"type"` + User string `json:"user"` + Channel string `json:"channel"` +} + +type GroupHistoryChangedEvent struct { + Type string `json:"type"` + Latest string `json:"latest"` + Ts string `json:"ts"` + EventTs string `json:"event_ts"` +} + +type GroupOpenEvent struct { + Type string `json:"type"` + User string `json:"user"` + Channel string `json:"channel"` +} + +type ImCloseEvent struct { + Type string `json:"type"` + User string `json:"user"` + Channel string `json:"channel"` +} + +type ImCreatedEvent struct { + Type string `json:"type"` + User string `json:"user"` + Channel struct { + ID string `json:"id"` + } `json:"channel"` +} + +type ImHistoryChangedEvent struct { + Type string `json:"type"` + Latest string `json:"latest"` + Ts string `json:"ts"` + EventTs string `json:"event_ts"` +} + +type ImOpenEvent struct { + Type string `json:"type"` + User string `json:"user"` + Channel string `json:"channel"` +} + +type SubTeam struct { + ID string `json:"id"` + TeamID string `json:"team_id"` + IsUsergroup bool `json:"is_usergroup"` + Name string `json:"name"` + Description string `json:"description"` + Handle string `json:"handle"` + IsExternal bool `json:"is_external"` + DateCreate int64 `json:"date_create"` + DateUpdate int64 `json:"date_update"` + DateDelete int64 `json:"date_delete"` + AutoType string `json:"auto_type"` + CreatedBy string `json:"created_by"` + UpdatedBy string `json:"updated_by"` + DeletedBy string `json:"deleted_by"` + Prefs struct { + Channels []string `json:"channels"` + Groups []string `json:"groups"` + } `json:"prefs"` + Users []string `json:"users"` + UserCount int `json:"user_count"` +} + +type SubteamCreatedEvent struct { + Type string `json:"type"` + Subteam SubTeam `json:"subteam"` +} + +type SubteamMembersChangedEvent struct { + Type string `json:"type"` + SubteamID string `json:"subteam_id"` + TeamID string `json:"team_id"` + DatePreviousUpdate int `json:"date_previous_update"` + DateUpdate int64 `json:"date_update"` + AddedUsers []string `json:"added_users"` + AddedUsersCount string `json:"added_users_count"` + RemovedUsers []string `json:"removed_users"` + RemovedUsersCount string `json:"removed_users_count"` +} + +type SubteamSelfAddedEvent struct { + Type string `json:"type"` + SubteamID string `json:"subteam_id"` +} + +type SubteamSelfRemovedEvent struct { + Type string `json:"type"` + SubteamID string `json:"subteam_id"` +} + +type SubteamUpdatedEvent struct { + Type string `json:"type"` + Subteam SubTeam `json:"subteam"` +} + +type TeamDomainChangeEvent struct { + Type string `json:"type"` + URL string `json:"url"` + Domain string `json:"domain"` + TeamID string `json:"team_id"` +} + +type TeamRenameEvent struct { + Type string `json:"type"` + Name string `json:"name"` + TeamID string `json:"team_id"` +} + +type UserChangeEvent struct { + Type string `json:"type"` + User User `json:"user"` + CacheTS int64 `json:"cache_ts"` + EventTS string `json:"event_ts"` +} + +type AppDeletedEvent struct { + Type string `json:"type"` + AppID string `json:"app_id"` + AppName string `json:"app_name"` + AppOwnerID string `json:"app_owner_id"` + TeamID string `json:"team_id"` + TeamDomain string `json:"team_domain"` + EventTs string `json:"event_ts"` +} + +type AppInstalledEvent struct { + Type string `json:"type"` + AppID string `json:"app_id"` + AppName string `json:"app_name"` + AppOwnerID string `json:"app_owner_id"` + UserID string `json:"user_id"` + TeamID string `json:"team_id"` + TeamDomain string `json:"team_domain"` + EventTs string `json:"event_ts"` +} + +type AppRequestedEvent struct { + Type string `json:"type"` + AppRequest struct { + ID string `json:"id"` + App struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + HelpURL string `json:"help_url"` + PrivacyPolicyURL string `json:"privacy_policy_url"` + AppHomepageURL string `json:"app_homepage_url"` + AppDirectoryURL string `json:"app_directory_url"` + IsAppDirectoryApproved bool `json:"is_app_directory_approved"` + IsInternal bool `json:"is_internal"` + AdditionalInfo string `json:"additional_info"` + } `json:"app"` + PreviousResolution struct { + Status string `json:"status"` + Scopes []struct { + Name string `json:"name"` + Description string `json:"description"` + IsSensitive bool `json:"is_sensitive"` + TokenType string `json:"token_type"` + } `json:"scopes"` + } `json:"previous_resolution"` + User struct { + ID string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + } `json:"user"` + Team struct { + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + } `json:"team"` + Enterprise interface{} `json:"enterprise"` + Scopes []struct { + Name string `json:"name"` + Description string `json:"description"` + IsSensitive bool `json:"is_sensitive"` + TokenType string `json:"token_type"` + } `json:"scopes"` + Message string `json:"message"` + } `json:"app_request"` +} + +type AppUninstalledTeamEvent struct { + Type string `json:"type"` + AppID string `json:"app_id"` + AppName string `json:"app_name"` + AppOwnerID string `json:"app_owner_id"` + UserID string `json:"user_id"` + TeamID string `json:"team_id"` + TeamDomain string `json:"team_domain"` + EventTs string `json:"event_ts"` +} + +type CallRejectedEvent struct { + Token string `json:"token"` + TeamID string `json:"team_id"` + APIAppID string `json:"api_app_id"` + Event struct { + Type string `json:"type"` + CallID string `json:"call_id"` + UserID string `json:"user_id"` + ChannelID string `json:"channel_id"` + ExternalUniqueID string `json:"external_unique_id"` + } `json:"event"` + Type string `json:"type"` + EventID string `json:"event_id"` + AuthedUsers []string `json:"authed_users"` +} + +type ChannelSharedEvent struct { + Type string `json:"type"` + ConnectedTeamID string `json:"connected_team_id"` + Channel string `json:"channel"` + EventTs string `json:"event_ts"` +} + +type FileCreatedEvent struct { + Type string `json:"type"` + FileID string `json:"file_id"` + File struct { + ID string `json:"id"` + } `json:"file"` +} + +type FilePublicEvent struct { + Type string `json:"type"` + FileID string `json:"file_id"` + File struct { + ID string `json:"id"` + } `json:"file"` +} + +type FunctionExecutedEvent struct { + Type string `json:"type"` + Function struct { + ID string `json:"id"` + CallbackID string `json:"callback_id"` + Title string `json:"title"` + Description string `json:"description"` + Type string `json:"type"` + InputParameters []struct { + Type string `json:"type"` + Name string `json:"name"` + Description string `json:"description"` + Title string `json:"title"` + IsRequired bool `json:"is_required"` + } `json:"input_parameters"` + OutputParameters []struct { + Type string `json:"type"` + Name string `json:"name"` + Description string `json:"description"` + Title string `json:"title"` + IsRequired bool `json:"is_required"` + } `json:"output_parameters"` + AppID string `json:"app_id"` + DateCreated int64 `json:"date_created"` + DateUpdated int64 `json:"date_updated"` + DateDeleted int64 `json:"date_deleted"` + } `json:"function"` + Inputs map[string]string `json:"inputs"` + FunctionExecutionID string `json:"function_execution_id"` + WorkflowExecutionID string `json:"workflow_execution_id"` + EventTs string `json:"event_ts"` + BotAccessToken string `json:"bot_access_token"` +} + +type InviteRequestedEvent struct { + Type string `json:"type"` + InviteRequest struct { + ID string `json:"id"` + Email string `json:"email"` + DateCreated int64 `json:"date_created"` + RequesterIDs []string `json:"requester_ids"` + ChannelIDs []string `json:"channel_ids"` + InviteType string `json:"invite_type"` + RealName string `json:"real_name"` + DateExpire int64 `json:"date_expire"` + RequestReason string `json:"request_reason"` + Team struct { + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` + } `json:"team"` + } `json:"invite_request"` +} + +type StarAddedEvent struct { + Type string `json:"type"` + User string `json:"user"` + Item struct { + } `json:"item"` + EventTS string `json:"event_ts"` +} + +type StarRemovedEvent struct { + Type string `json:"type"` + User string `json:"user"` + Item struct { + } `json:"item"` + EventTS string `json:"event_ts"` +} + +type UserHuddleChangedEvent struct { + Type string `json:"type"` + User User `json:"user"` + CacheTS int64 `json:"cache_ts"` + EventTS string `json:"event_ts"` +} + +type User struct { + ID string `json:"id"` + TeamID string `json:"team_id"` + Name string `json:"name"` + Deleted bool `json:"deleted"` + Color string `json:"color"` + RealName string `json:"real_name"` + TZ string `json:"tz"` + TZLabel string `json:"tz_label"` + TZOffset int `json:"tz_offset"` + Profile Profile `json:"profile"` + IsAdmin bool `json:"is_admin"` + IsOwner bool `json:"is_owner"` + IsPrimaryOwner bool `json:"is_primary_owner"` + IsRestricted bool `json:"is_restricted"` + IsUltraRestricted bool `json:"is_ultra_restricted"` + IsBot bool `json:"is_bot"` + IsAppUser bool `json:"is_app_user"` + Updated int64 `json:"updated"` + IsEmailConfirmed bool `json:"is_email_confirmed"` + WhoCanShareContactCard string `json:"who_can_share_contact_card"` + Locale string `json:"locale"` +} + +type Profile struct { + Title string `json:"title"` + Phone string `json:"phone"` + Skype string `json:"skype"` + RealName string `json:"real_name"` + RealNameNormalized string `json:"real_name_normalized"` + DisplayName string `json:"display_name"` + DisplayNameNormalized string `json:"display_name_normalized"` + Fields map[string]interface{} `json:"fields"` + StatusText string `json:"status_text"` + StatusEmoji string `json:"status_emoji"` + StatusEmojiDisplayInfo []interface{} `json:"status_emoji_display_info"` + StatusExpiration int `json:"status_expiration"` + AvatarHash string `json:"avatar_hash"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Image24 string `json:"image_24"` + Image32 string `json:"image_32"` + Image48 string `json:"image_48"` + Image72 string `json:"image_72"` + Image192 string `json:"image_192"` + Image512 string `json:"image_512"` + StatusTextCanonical string `json:"status_text_canonical"` + Team string `json:"team"` +} + +type UserStatusChangedEvent struct { + Type string `json:"type"` + User User `json:"user"` + CacheTS int64 `json:"cache_ts"` + EventTS string `json:"event_ts"` +} + +type Actor struct { + ID string `json:"id"` + Name string `json:"name"` + IsBot bool `json:"is_bot"` + TeamID string `json:"team_id"` + Timezone string `json:"timezone"` + RealName string `json:"real_name"` + DisplayName string `json:"display_name"` +} + +type TargetUser struct { + Email string `json:"email"` + InviteID string `json:"invite_id"` +} + +type TeamIcon struct { + Image34 string `json:"image_34"` + ImageDefault bool `json:"image_default"` +} + +type Team struct { + ID string `json:"id"` + Icon TeamIcon `json:"icon"` + Name string `json:"name"` + Domain string `json:"domain"` + IsVerified bool `json:"is_verified"` + DateCreated int64 `json:"date_created"` + AvatarBaseURL string `json:"avatar_base_url"` + RequiresSponsorship bool `json:"requires_sponsorship"` +} + +type SharedChannelInviteRequestedEvent struct { + Actor Actor `json:"actor"` + ChannelID string `json:"channel_id"` + EventType string `json:"event_type"` + ChannelName string `json:"channel_name"` + ChannelType string `json:"channel_type"` + TargetUsers []TargetUser `json:"target_users"` + TeamsInChannel []Team `json:"teams_in_channel"` + IsExternalLimited bool `json:"is_external_limited"` + ChannelDateCreated int64 `json:"channel_date_created"` + ChannelMessageLatestCounted int64 `json:"channel_message_latest_counted_timestamp"` +} + type EventsAPIType string const ( @@ -451,6 +1146,10 @@ const ( AppHomeOpened = EventsAPIType("app_home_opened") // AppUninstalled Your Slack app was uninstalled. AppUninstalled = EventsAPIType("app_uninstalled") + // AssistantThreadStarted Your Slack AI Assistant has started a new thread + AssistantThreadStarted = EventsAPIType("assistant_thread_started") + // AssistantThreadContextChanged Your Slack AI Assistant has changed the context of a thread + AssistantThreadContextChanged = EventsAPIType("assistant_thread_context_changed") // ChannelCreated is sent when a new channel is created. ChannelCreated = EventsAPIType("channel_created") // ChannelDeleted is sent when a channel is deleted. @@ -475,6 +1174,14 @@ const ( GroupLeft = EventsAPIType("group_left") // GroupRename is sent when a group is renamed. GroupRename = EventsAPIType("group_rename") + // FileChange is sent when a file is changed. + FileChange = EventsAPIType("file_change") + // FileDeleted is sent when a file is deleted. + FileDeleted = EventsAPIType("file_deleted") + // FileShared is sent when a file is shared. + FileShared = EventsAPIType("file_shared") + // FileUnshared is sent when a file is unshared. + FileUnshared = EventsAPIType("file_unshared") // GridMigrationFinished An enterprise grid migration has finished on this workspace. GridMigrationFinished = EventsAPIType("grid_migration_finished") // GridMigrationStarted An enterprise grid migration has started on this workspace. @@ -483,9 +1190,9 @@ const ( LinkShared = EventsAPIType("link_shared") // Message A message was posted to a channel, private channel (group), im, or mim Message = EventsAPIType("message") - // Member Joined Channel + // MemberJoinedChannel is sent if a member joined a channel. MemberJoinedChannel = EventsAPIType("member_joined_channel") - // Member Left Channel + // MemberLeftChannel is sent if a member left a channel. MemberLeftChannel = EventsAPIType("member_left_channel") // PinAdded An item was pinned to a channel PinAdded = EventsAPIType("pin_added") @@ -497,45 +1204,185 @@ const ( ReactionRemoved = EventsAPIType("reaction_removed") // TeamJoin A new user joined the workspace TeamJoin = EventsAPIType("team_join") + // Slack connect app or bot invite received + SharedChannelInviteReceived = EventsAPIType("shared_channel_invite_received") + // Slack connect channel invite approved + SharedChannelInviteApproved = EventsAPIType("shared_channel_invite_approved") + // Slack connect channel invite declined + SharedChannelInviteDeclined = EventsAPIType("shared_channel_invite_declined") + // Slack connect channel invite accepted by an end user + SharedChannelInviteAccepted = EventsAPIType("shared_channel_invite_accepted") // TokensRevoked APP's API tokes are revoked TokensRevoked = EventsAPIType("tokens_revoked") // EmojiChanged A custom emoji has been added or changed EmojiChanged = EventsAPIType("emoji_changed") // WorkflowStepExecute Happens, if a workflow step of your app is invoked WorkflowStepExecute = EventsAPIType("workflow_step_execute") + // MessageMetadataPosted A message with metadata was posted + MessageMetadataPosted = EventsAPIType("message_metadata_posted") + // MessageMetadataUpdated A message with metadata was updated + MessageMetadataUpdated = EventsAPIType("message_metadata_updated") + // MessageMetadataDeleted A message with metadata was deleted + MessageMetadataDeleted = EventsAPIType("message_metadata_deleted") + // TeamAccessGranted is sent if access to teams was granted for your org-wide app. + TeamAccessGranted = EventsAPIType("team_access_granted") + // TeamAccessRevoked is sent if access to teams was revoked for your org-wide app. + TeamAccessRevoked = EventsAPIType("team_access_revoked") + // UserProfileChanged is sent if a user's profile information has changed. + UserProfileChanged = EventsAPIType("user_profile_changed") + // ChannelHistoryChanged The history of a channel changed + ChannelHistoryChanged = EventsAPIType("channel_history_changed") + // CommandsChanged A command was changed + CommandsChanged = EventsAPIType("commands_changed") + // DndUpdated Do Not Disturb settings were updated + DndUpdated = EventsAPIType("dnd_updated") + // DndUpdatedUser Do Not Disturb settings for a user were updated + DndUpdatedUser = EventsAPIType("dnd_updated_user") + // EmailDomainChanged The email domain changed + EmailDomainChanged = EventsAPIType("email_domain_changed") + // GroupClose A group was closed + GroupClose = EventsAPIType("group_close") + // GroupHistoryChanged The history of a group changed + GroupHistoryChanged = EventsAPIType("group_history_changed") + // GroupOpen A group was opened + GroupOpen = EventsAPIType("group_open") + // ImClose An instant message channel was closed + ImClose = EventsAPIType("im_close") + // ImCreated An instant message channel was created + ImCreated = EventsAPIType("im_created") + // ImHistoryChanged The history of an instant message channel changed + ImHistoryChanged = EventsAPIType("im_history_changed") + // ImOpen An instant message channel was opened + ImOpen = EventsAPIType("im_open") + // SubteamCreated A subteam was created + SubteamCreated = EventsAPIType("subteam_created") + // SubteamMembersChanged The members of a subteam changed + SubteamMembersChanged = EventsAPIType("subteam_members_changed") + // SubteamSelfAdded The current user was added to a subteam + SubteamSelfAdded = EventsAPIType("subteam_self_added") + // SubteamSelfRemoved The current user was removed from a subteam + SubteamSelfRemoved = EventsAPIType("subteam_self_removed") + // SubteamUpdated A subteam was updated + SubteamUpdated = EventsAPIType("subteam_updated") + // TeamDomainChange The team's domain changed + TeamDomainChange = EventsAPIType("team_domain_change") + // TeamRename The team was renamed + TeamRename = EventsAPIType("team_rename") + // UserChange A user object has changed + UserChange = EventsAPIType("user_change") + // AppDeleted is an event when an app is deleted from a workspace + AppDeleted = EventsAPIType("app_deleted") + // AppInstalled is an event when an app is installed to a workspace + AppInstalled = EventsAPIType("app_installed") + // AppRequested is an event when a user requests to install an app to a workspace + AppRequested = EventsAPIType("app_requested") + // AppUninstalledTeam is an event when an app is uninstalled from a team + AppUninstalledTeam = EventsAPIType("app_uninstalled_team") + // CallRejected is an event when a Slack call is rejected + CallRejected = EventsAPIType("call_rejected") + // ChannelShared is an event when a channel is shared with another workspace + ChannelShared = EventsAPIType("channel_shared") + // FileCreated is an event when a file is created in a workspace + FileCreated = EventsAPIType("file_created") + // FilePublic is an event when a file is made public in a workspace + FilePublic = EventsAPIType("file_public") + // FunctionExecuted is an event when a Slack function is executed + FunctionExecuted = EventsAPIType("function_executed") + // InviteRequested is an event when a user requests an invite to a workspace + InviteRequested = EventsAPIType("invite_requested") + // SharedChannelInviteRequested is an event when an invitation to share a channel is requested + SharedChannelInviteRequested = EventsAPIType("shared_channel_invite_requested") + // StarAdded is an event when a star is added to a message or file + StarAdded = EventsAPIType("star_added") + // StarRemoved is an event when a star is removed from a message or file + StarRemoved = EventsAPIType("star_removed") + // UserHuddleChanged is an event when a user's huddle status changes + UserHuddleChanged = EventsAPIType("user_huddle_changed") + // UserStatusChanged is an event when a user's status changes + UserStatusChanged = EventsAPIType("user_status_changed") ) // EventsAPIInnerEventMapping maps INNER Event API events to their corresponding struct // implementations. The structs should be instances of the unmarshalling // target for the matching event type. var EventsAPIInnerEventMapping = map[EventsAPIType]interface{}{ - AppMention: AppMentionEvent{}, - AppHomeOpened: AppHomeOpenedEvent{}, - AppUninstalled: AppUninstalledEvent{}, - ChannelCreated: ChannelCreatedEvent{}, - ChannelDeleted: ChannelDeletedEvent{}, - ChannelArchive: ChannelArchiveEvent{}, - ChannelUnarchive: ChannelUnarchiveEvent{}, - ChannelLeft: ChannelLeftEvent{}, - ChannelRename: ChannelRenameEvent{}, - ChannelIDChanged: ChannelIDChangedEvent{}, - GroupDeleted: GroupDeletedEvent{}, - GroupArchive: GroupArchiveEvent{}, - GroupUnarchive: GroupUnarchiveEvent{}, - GroupLeft: GroupLeftEvent{}, - GroupRename: GroupRenameEvent{}, - GridMigrationFinished: GridMigrationFinishedEvent{}, - GridMigrationStarted: GridMigrationStartedEvent{}, - LinkShared: LinkSharedEvent{}, - Message: MessageEvent{}, - MemberJoinedChannel: MemberJoinedChannelEvent{}, - MemberLeftChannel: MemberLeftChannelEvent{}, - PinAdded: PinAddedEvent{}, - PinRemoved: PinRemovedEvent{}, - ReactionAdded: ReactionAddedEvent{}, - ReactionRemoved: ReactionRemovedEvent{}, - TeamJoin: TeamJoinEvent{}, - TokensRevoked: TokensRevokedEvent{}, - EmojiChanged: EmojiChangedEvent{}, - WorkflowStepExecute: WorkflowStepExecuteEvent{}, + AppMention: AppMentionEvent{}, + AppHomeOpened: AppHomeOpenedEvent{}, + AppUninstalled: AppUninstalledEvent{}, + AssistantThreadStarted: AssistantThreadStartedEvent{}, + AssistantThreadContextChanged: AssistantThreadContextChangedEvent{}, + ChannelCreated: ChannelCreatedEvent{}, + ChannelDeleted: ChannelDeletedEvent{}, + ChannelArchive: ChannelArchiveEvent{}, + ChannelUnarchive: ChannelUnarchiveEvent{}, + ChannelLeft: ChannelLeftEvent{}, + ChannelRename: ChannelRenameEvent{}, + ChannelIDChanged: ChannelIDChangedEvent{}, + FileChange: FileChangeEvent{}, + FileDeleted: FileDeletedEvent{}, + FileShared: FileSharedEvent{}, + FileUnshared: FileUnsharedEvent{}, + GroupDeleted: GroupDeletedEvent{}, + GroupArchive: GroupArchiveEvent{}, + GroupUnarchive: GroupUnarchiveEvent{}, + GroupLeft: GroupLeftEvent{}, + GroupRename: GroupRenameEvent{}, + GridMigrationFinished: GridMigrationFinishedEvent{}, + GridMigrationStarted: GridMigrationStartedEvent{}, + LinkShared: LinkSharedEvent{}, + Message: MessageEvent{}, + MemberJoinedChannel: MemberJoinedChannelEvent{}, + MemberLeftChannel: MemberLeftChannelEvent{}, + PinAdded: PinAddedEvent{}, + PinRemoved: PinRemovedEvent{}, + ReactionAdded: ReactionAddedEvent{}, + ReactionRemoved: ReactionRemovedEvent{}, + SharedChannelInviteApproved: SharedChannelInviteApprovedEvent{}, + SharedChannelInviteAccepted: SharedChannelInviteAcceptedEvent{}, + SharedChannelInviteDeclined: SharedChannelInviteDeclinedEvent{}, + SharedChannelInviteReceived: SharedChannelInviteReceivedEvent{}, + TeamJoin: TeamJoinEvent{}, + TokensRevoked: TokensRevokedEvent{}, + EmojiChanged: EmojiChangedEvent{}, + WorkflowStepExecute: WorkflowStepExecuteEvent{}, + MessageMetadataPosted: MessageMetadataPostedEvent{}, + MessageMetadataUpdated: MessageMetadataUpdatedEvent{}, + MessageMetadataDeleted: MessageMetadataDeletedEvent{}, + TeamAccessGranted: TeamAccessGrantedEvent{}, + TeamAccessRevoked: TeamAccessRevokedEvent{}, + UserProfileChanged: UserProfileChangedEvent{}, + ChannelHistoryChanged: ChannelHistoryChangedEvent{}, + DndUpdated: DndUpdatedEvent{}, + DndUpdatedUser: DndUpdatedUserEvent{}, + EmailDomainChanged: EmailDomainChangedEvent{}, + GroupClose: GroupCloseEvent{}, + GroupHistoryChanged: GroupHistoryChangedEvent{}, + GroupOpen: GroupOpenEvent{}, + ImClose: ImCloseEvent{}, + ImCreated: ImCreatedEvent{}, + ImHistoryChanged: ImHistoryChangedEvent{}, + ImOpen: ImOpenEvent{}, + SubteamCreated: SubteamCreatedEvent{}, + SubteamMembersChanged: SubteamMembersChangedEvent{}, + SubteamSelfAdded: SubteamSelfAddedEvent{}, + SubteamSelfRemoved: SubteamSelfRemovedEvent{}, + SubteamUpdated: SubteamUpdatedEvent{}, + TeamDomainChange: TeamDomainChangeEvent{}, + TeamRename: TeamRenameEvent{}, + UserChange: UserChangeEvent{}, + AppDeleted: AppDeletedEvent{}, + AppInstalled: AppInstalledEvent{}, + AppRequested: AppRequestedEvent{}, + AppUninstalledTeam: AppUninstalledTeamEvent{}, + CallRejected: CallRejectedEvent{}, + ChannelShared: ChannelSharedEvent{}, + FileCreated: FileCreatedEvent{}, + FilePublic: FilePublicEvent{}, + FunctionExecuted: FunctionExecutedEvent{}, + InviteRequested: InviteRequestedEvent{}, + SharedChannelInviteRequested: SharedChannelInviteRequestedEvent{}, + StarAdded: StarAddedEvent{}, + StarRemoved: StarRemovedEvent{}, + UserHuddleChanged: UserHuddleChangedEvent{}, + UserStatusChanged: UserStatusChangedEvent{}, } From 35c745e2946cb78ff400144d6a41ad1e3bbbbc4e Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Tue, 8 Oct 2024 18:58:34 -0400 Subject: [PATCH 02/11] Added Assistant Tests --- assistant.go | 15 +++-- assistant_test.go | 139 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 assistant_test.go diff --git a/assistant.go b/assistant.go index 15fd91f06..2ed097c41 100644 --- a/assistant.go +++ b/assistant.go @@ -2,8 +2,8 @@ package slack import ( "context" + "encoding/json" "net/url" - "strconv" ) // AssistantThreadSetStatusParameters are the parameters for AssistantThreadSetStatus @@ -44,12 +44,12 @@ func (p *AssistantThreadsSetSuggestedPromptsParameters) AddPrompt(title, message // SetAssistantThreadsSugesstedPrompts sets the suggested prompts for a thread // @see https://api.slack.com/methods/assistant.threads.setSuggestedPrompts func (api *Client) SetAssistantThreadsSuggestedPrompts(params AssistantThreadsSetSuggestedPromptsParameters) (err error) { - return api.SetAssistantThreadSuggestedPromptsContext(context.Background(), params) + return api.SetAssistantThreadsSuggestedPromptsContext(context.Background(), params) } // SetAssistantThreadSuggestedPromptsContext sets the suggested prompts for a thread with a custom context // @see https://api.slack.com/methods/assistant.threads.setSuggestedPrompts -func (api *Client) SetAssistantThreadSuggestedPromptsContext(ctx context.Context, params AssistantThreadsSetSuggestedPromptsParameters) (err error) { +func (api *Client) SetAssistantThreadsSuggestedPromptsContext(ctx context.Context, params AssistantThreadsSetSuggestedPromptsParameters) (err error) { values := url.Values{ "token": {api.token}, @@ -63,11 +63,14 @@ func (api *Client) SetAssistantThreadSuggestedPromptsContext(ctx context.Context values.Add("thread_ts", params.ThreadTS) } - for i, prompt := range params.Prompts { - values.Add("prompts["+strconv.Itoa(i)+"][title]", prompt.Title) - values.Add("prompts["+strconv.Itoa(i)+"][message]", prompt.Message) + // Send Prompts as JSON + prompts, err := json.Marshal(params.Prompts) + if err != nil { + return err } + values.Add("prompts", string(prompts)) + response := struct { SlackResponse }{} diff --git a/assistant_test.go b/assistant_test.go new file mode 100644 index 000000000..55021fd0f --- /dev/null +++ b/assistant_test.go @@ -0,0 +1,139 @@ +package slack + +import ( + "encoding/json" + "net/http" + "testing" +) + +func assistantThreadsSuggestedPromptsHandler(rw http.ResponseWriter, r *http.Request) { + + channelID := r.FormValue("channel_id") + threadTS := r.FormValue("thread_ts") + promptStr := r.FormValue("prompts") + + var prompts []AssistantThreadsPrompt + err := json.Unmarshal([]byte(promptStr), &prompts) + if err != nil { + rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) + return + } + + rw.Header().Set("Content-Type", "application/json") + + if channelID != "" && threadTS != "" && len(prompts) == 2 { + + resp, _ := json.Marshal(&AssistantThreadsSetSuggestedPromptsParameters{ + ChannelID: channelID, + ThreadTS: threadTS, + Prompts: prompts, + }) + rw.Write(resp) + } else { + rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) + } + +} + +func TestAssistantThreadsSuggestedPrompts(t *testing.T) { + + http.HandleFunc("/assistant.threads.setSuggestedPrompts", assistantThreadsSuggestedPromptsHandler) + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + params := AssistantThreadsSetSuggestedPromptsParameters{ + ChannelID: "CXXXXXXXX", + ThreadTS: "1234567890.123456", + } + + params.AddPrompt("title1", "message1") + params.AddPrompt("title2", "message2") + + err := api.SetAssistantThreadsSuggestedPrompts(params) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + +} + +func setAssistantThreadsStatusHandler(rw http.ResponseWriter, r *http.Request) { + + channelID := r.FormValue("channel_id") + threadTS := r.FormValue("thread_ts") + status := r.FormValue("status") + + rw.Header().Set("Content-Type", "application/json") + + if channelID != "" && threadTS != "" && status != "" { + + resp, _ := json.Marshal(&AssistantThreadsSetStatusParameters{ + ChannelID: channelID, + ThreadTS: threadTS, + Status: status, + }) + rw.Write(resp) + } else { + rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) + } + +} + +func TestSetAssistantThreadsStatus(t *testing.T) { + + http.HandleFunc("/assistant.threads.setStatus", setAssistantThreadsStatusHandler) + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + params := AssistantThreadsSetStatusParameters{ + ChannelID: "CXXXXXXXX", + ThreadTS: "1234567890.123456", + Status: "updated status", + } + + err := api.SetAssistantThreadsStatus(params) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + +} + +func assistantThreadsTitleHandler(rw http.ResponseWriter, r *http.Request) { + + channelID := r.FormValue("channel_id") + threadTS := r.FormValue("thread_ts") + title := r.FormValue("title") + + rw.Header().Set("Content-Type", "application/json") + + if channelID != "" && threadTS != "" && title != "" { + + resp, _ := json.Marshal(&AssistantThreadsSetTitleParameters{ + ChannelID: channelID, + ThreadTS: threadTS, + Title: title, + }) + rw.Write(resp) + } else { + rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) + } + +} + +func TestSetAssistantThreadsTitle(t *testing.T) { + + http.HandleFunc("/assistant.threads.setTitle", assistantThreadsTitleHandler) + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + params := AssistantThreadsSetTitleParameters{ + ChannelID: "CXXXXXXXX", + ThreadTS: "1234567890.123456", + Title: "updated title", + } + + err := api.SetAssistantThreadsTitle(params) + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + +} From fcfa83cd68730d242acd641b6d204f9ebd1fa422 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Tue, 8 Oct 2024 19:05:27 -0400 Subject: [PATCH 03/11] Added event tests for Assistant --- slackevents/inner_events_test.go | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/slackevents/inner_events_test.go b/slackevents/inner_events_test.go index 2a0ac56e8..1f3ce80f4 100644 --- a/slackevents/inner_events_test.go +++ b/slackevents/inner_events_test.go @@ -5,6 +5,59 @@ import ( "testing" ) +func TestAssistantThreadStartedEvent(t *testing.T) { + + rawE := []byte(` + { + "type": "assistant_thread_started", + "assistant_thread": { + "user_id": "U123ABC456", + "context": { + "channel_id": "C123ABC456", + "team_id": "T07XY8FPJ5C", + "enterprise_id": "E480293PS82" + }, + "channel_id": "D123ABC456", + "thread_ts": "1729999327.187299" + + }, + "event_ts": "1715873754.429808" + } + `) + + err := json.Unmarshal(rawE, &AssistantThreadStartedEvent{}) + if err != nil { + t.Error(err) + } + +} + +func TestAssistantThreadContextChangedEvent(t *testing.T) { + + rawE := []byte(` + { + "type": "assistant_thread_context_changed", + "assistant_thread": { + "user_id": "U123ABC456", + "context": { + "channel_id": "C123ABC456", + "team_id": "T07XY8FPJ5C", + "enterprise_id": "E480293PS82" + }, + "channel_id": "D123ABC456", + "thread_ts": "1729999327.187299" + }, + "event_ts": "17298244.022142" + } + `) + + err := json.Unmarshal(rawE, &AssistantThreadContextChangedEvent{}) + if err != nil { + t.Error(err) + } + +} + func TestAppMention(t *testing.T) { rawE := []byte(` { From 88024a791eea6a1ec6864863e61f8077ce861980 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Tue, 8 Oct 2024 21:26:55 -0400 Subject: [PATCH 04/11] Updated test handler to return standard 'ok' response --- assistant_test.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/assistant_test.go b/assistant_test.go index 55021fd0f..0dde9cc5a 100644 --- a/assistant_test.go +++ b/assistant_test.go @@ -23,12 +23,11 @@ func assistantThreadsSuggestedPromptsHandler(rw http.ResponseWriter, r *http.Req if channelID != "" && threadTS != "" && len(prompts) == 2 { - resp, _ := json.Marshal(&AssistantThreadsSetSuggestedPromptsParameters{ - ChannelID: channelID, - ThreadTS: threadTS, - Prompts: prompts, + resp, _ := json.Marshal(&addBookmarkResponse{ + SlackResponse: SlackResponse{Ok: true}, }) rw.Write(resp) + } else { rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) } @@ -66,12 +65,11 @@ func setAssistantThreadsStatusHandler(rw http.ResponseWriter, r *http.Request) { if channelID != "" && threadTS != "" && status != "" { - resp, _ := json.Marshal(&AssistantThreadsSetStatusParameters{ - ChannelID: channelID, - ThreadTS: threadTS, - Status: status, + resp, _ := json.Marshal(&addBookmarkResponse{ + SlackResponse: SlackResponse{Ok: true}, }) rw.Write(resp) + } else { rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) } @@ -107,10 +105,8 @@ func assistantThreadsTitleHandler(rw http.ResponseWriter, r *http.Request) { if channelID != "" && threadTS != "" && title != "" { - resp, _ := json.Marshal(&AssistantThreadsSetTitleParameters{ - ChannelID: channelID, - ThreadTS: threadTS, - Title: title, + resp, _ := json.Marshal(&addBookmarkResponse{ + SlackResponse: SlackResponse{Ok: true}, }) rw.Write(resp) } else { From df578104a938d1fce973c1a223d220c520f49994 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 16 Oct 2024 13:25:24 -0400 Subject: [PATCH 05/11] Removed check for channel_id before making request, added Title as param, not currently used by Slack --- assistant.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/assistant.go b/assistant.go index 2ed097c41..ef1425ff3 100644 --- a/assistant.go +++ b/assistant.go @@ -22,6 +22,7 @@ type AssistantThreadsSetTitleParameters struct { // AssistantThreadSetSuggestedPromptsParameters are the parameters for AssistantThreadSetSuggestedPrompts type AssistantThreadsSetSuggestedPromptsParameters struct { + Title string `json:"title"` ChannelID string `json:"channel_id"` ThreadTS string `json:"thread_ts"` Prompts []AssistantThreadsPrompt `json:"prompts"` @@ -55,10 +56,6 @@ func (api *Client) SetAssistantThreadsSuggestedPromptsContext(ctx context.Contex "token": {api.token}, } - if params.ChannelID != "" { - values.Add("channel_id", params.ChannelID) - } - if params.ThreadTS != "" { values.Add("thread_ts", params.ThreadTS) } @@ -97,10 +94,6 @@ func (api *Client) SetAssistantThreadsStatusContext(ctx context.Context, params "token": {api.token}, } - if params.ChannelID != "" { - values.Add("channel_id", params.ChannelID) - } - if params.ThreadTS != "" { values.Add("thread_ts", params.ThreadTS) } From 49f3e4dc7ac9af54771ed0865f75711fbbb40375 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 16 Oct 2024 13:25:52 -0400 Subject: [PATCH 06/11] Removed events that are not part of this PR, unsure how they were added initially --- slackevents/inner_events.go | 709 +----------------------------------- 1 file changed, 2 insertions(+), 707 deletions(-) diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index 720640b63..948ad3b9e 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -187,47 +187,6 @@ type GroupRenameInfo struct { Created int `json:"created"` } -// FileChangeEvent represents the information associated with the File change -// event. -type FileChangeEvent struct { - Type string `json:"type"` - FileID string `json:"file_id"` - File FileEventFile `json:"file"` -} - -// FileDeletedEvent represents the information associated with the File deleted -// event. -type FileDeletedEvent struct { - Type string `json:"type"` - FileID string `json:"file_id"` - EventTimestamp string `json:"event_ts"` -} - -// FileSharedEvent represents the information associated with the File shared -// event. -type FileSharedEvent struct { - Type string `json:"type"` - ChannelID string `json:"channel_id"` - FileID string `json:"file_id"` - UserID string `json:"user_id"` - File FileEventFile `json:"file"` - EventTimestamp string `json:"event_ts"` -} - -// FileUnsharedEvent represents the information associated with the File -// unshared event. -type FileUnsharedEvent struct { - Type string `json:"type"` - FileID string `json:"file_id"` - File FileEventFile `json:"file"` -} - -// FileEventFile represents information on the specific file being shared in a -// file-related Slack event. -type FileEventFile struct { - ID string `json:"id"` -} - // GridMigrationFinishedEvent An enterprise grid migration has finished on this workspace. type GridMigrationFinishedEvent struct { Type string `json:"type"` @@ -251,11 +210,11 @@ type LinkSharedEvent struct { // compose text area. MessageTimeStamp string `json:"message_ts"` ThreadTimeStamp string `json:"thread_ts"` - Links []SharedLinks `json:"links"` + Links []sharedLinks `json:"links"` EventTimestamp string `json:"event_ts"` } -type SharedLinks struct { +type sharedLinks struct { Domain string `json:"domain"` URL string `json:"url"` } @@ -411,47 +370,6 @@ type WorkflowStepExecuteEvent struct { EventTimestamp string `json:"event_ts"` } -// MessageMetadataPostedEvent is sent, if a message with metadata is posted -type MessageMetadataPostedEvent struct { - Type string `json:"type"` - AppId string `json:"app_id"` - BotId string `json:"bot_id"` - UserId string `json:"user_id"` - TeamId string `json:"team_id"` - ChannelId string `json:"channel_id"` - Metadata *slack.SlackMetadata `json:"metadata"` - MessageTimestamp string `json:"message_ts"` - EventTimestamp string `json:"event_ts"` -} - -// MessageMetadataUpdatedEvent is sent, if a message with metadata is deleted -type MessageMetadataUpdatedEvent struct { - Type string `json:"type"` - ChannelId string `json:"channel_id"` - EventTimestamp string `json:"event_ts"` - PreviousMetadata *slack.SlackMetadata `json:"previous_metadata"` - AppId string `json:"app_id"` - BotId string `json:"bot_id"` - UserId string `json:"user_id"` - TeamId string `json:"team_id"` - MessageTimestamp string `json:"message_ts"` - Metadata *slack.SlackMetadata `json:"metadata"` -} - -// MessageMetadataDeletedEvent is sent, if a message with metadata is deleted -type MessageMetadataDeletedEvent struct { - Type string `json:"type"` - ChannelId string `json:"channel_id"` - EventTimestamp string `json:"event_ts"` - PreviousMetadata *slack.SlackMetadata `json:"previous_metadata"` - AppId string `json:"app_id"` - BotId string `json:"bot_id"` - UserId string `json:"user_id"` - TeamId string `json:"team_id"` - MessageTimestamp string `json:"message_ts"` - DeletedTimestamp string `json:"deleted_ts"` -} - type EventWorkflowStep struct { WorkflowStepExecuteID string `json:"workflow_step_execute_id"` WorkflowID string `json:"workflow_id"` @@ -494,7 +412,6 @@ type File struct { DisplayAsBot bool `json:"display_as_bot"` Username string `json:"username"` URLPrivate string `json:"url_private"` - FileAccess string `json:"file_access"` URLPrivateDownload string `json:"url_private_download"` Thumb64 string `json:"thumb_64"` Thumb80 string `json:"thumb_80"` @@ -563,580 +480,6 @@ func (e MessageEvent) IsEdited() bool { e.Message.Edited != nil } -// TeamAccessGrantedEvent is sent if access to teams was granted for your org-wide app. -type TeamAccessGrantedEvent struct { - Type string `json:"type"` - TeamIDs []string `json:"team_ids"` -} - -// TeamAccessRevokedEvent is sent if access to teams was revoked for your org-wide app. -type TeamAccessRevokedEvent struct { - Type string `json:"type"` - TeamIDs []string `json:"team_ids"` -} - -// UserProfileChangedEvent is sent if access to teams was revoked for your org-wide app. -type UserProfileChangedEvent struct { - User *slack.User `json:"user"` - CacheTs int `json:"cache_ts"` - Type string `json:"type"` - EventTs string `json:"event_ts"` -} - -// SharedChannelInviteApprovedEvent is sent if your invitation has been approved -type SharedChannelInviteApprovedEvent struct { - Type string `json:"type"` - Invite *SharedInvite `json:"invite"` - Channel *slack.Conversation `json:"channel"` - ApprovingTeamID string `json:"approving_team_id"` - TeamsInChannel []*SlackEventTeam `json:"teams_in_channel"` - ApprovingUser *SlackEventUser `json:"approving_user"` - EventTs string `json:"event_ts"` -} - -// SharedChannelInviteAcceptedEvent is sent if external org accepts a Slack Connect channel invite -type SharedChannelInviteAcceptedEvent struct { - Type string `json:"type"` - ApprovalRequired bool `json:"approval_required"` - Invite *SharedInvite `json:"invite"` - Channel *SharedChannel `json:"channel"` - TeamsInChannel []*SlackEventTeam `json:"teams_in_channel"` - AcceptingUser *SlackEventUser `json:"accepting_user"` - EventTs string `json:"event_ts"` - RequiresSponsorship bool `json:"requires_sponsorship,omitempty"` -} - -// SharedChannelInviteDeclinedEvent is sent if external or internal org declines the Slack Connect invite -type SharedChannelInviteDeclinedEvent struct { - Type string `json:"type"` - Invite *SharedInvite `json:"invite"` - Channel *SharedChannel `json:"channel"` - DecliningTeamID string `json:"declining_team_id"` - TeamsInChannel []*SlackEventTeam `json:"teams_in_channel"` - DecliningUser *SlackEventUser `json:"declining_user"` - EventTs string `json:"event_ts"` -} - -// SharedChannelInviteReceivedEvent is sent if a bot or app is invited to a Slack Connect channel -type SharedChannelInviteReceivedEvent struct { - Type string `json:"type"` - Invite *SharedInvite `json:"invite"` - Channel *SharedChannel `json:"channel"` - EventTs string `json:"event_ts"` -} - -// SlackEventTeam is a struct for teams in ShareChannel events -type SlackEventTeam struct { - ID string `json:"id"` - Name string `json:"name"` - Icon *SlackEventIcon `json:"icon,omitempty"` - AvatarBaseURL string `json:"avatar_base_url,omitempty"` - IsVerified bool `json:"is_verified"` - Domain string `json:"domain"` - DateCreated int `json:"date_created"` - RequiresSponsorship bool `json:"requires_sponsorship,omitempty"` - // TeamID string `json:"team_id,omitempty"` -} - -// SlackEventIcon is a struct for icons in ShareChannel events -type SlackEventIcon struct { - ImageDefault bool `json:"image_default,omitempty"` - Image34 string `json:"image_34,omitempty"` - Image44 string `json:"image_44,omitempty"` - Image68 string `json:"image_68,omitempty"` - Image88 string `json:"image_88,omitempty"` - Image102 string `json:"image_102,omitempty"` - Image132 string `json:"image_132,omitempty"` - Image230 string `json:"image_230,omitempty"` -} - -// SlackEventUser is a struct for users in ShareChannel events -type SlackEventUser struct { - ID string `json:"id"` - TeamID string `json:"team_id"` - Name string `json:"name"` - Updated int `json:"updated,omitempty"` - Profile *slack.UserProfile `json:"profile,omitempty"` - WhoCanShareContactCard string `json:"who_can_share_contact_card,omitempty"` -} - -// SharedChannel is a struct for shared channels in ShareChannel events -type SharedChannel struct { - ID string `json:"id"` - IsPrivate bool `json:"is_private"` - IsIm bool `json:"is_im"` - Name string `json:"name,omitempty"` -} - -// SharedInvite is a struct for shared invites in ShareChannel events -type SharedInvite struct { - ID string `json:"id"` - DateCreated int `json:"date_created"` - DateInvalid int `json:"date_invalid"` - InvitingTeam *SlackEventTeam `json:"inviting_team,omitempty"` - InvitingUser *SlackEventUser `json:"inviting_user,omitempty"` - RecipientEmail string `json:"recipient_email,omitempty"` - RecipientUserID string `json:"recipient_user_id,omitempty"` - IsSponsored bool `json:"is_sponsored,omitempty"` - IsExternalLimited bool `json:"is_external_limited,omitempty"` -} - -type ChannelHistoryChangedEvent struct { - Type string `json:"type"` - Latest string `json:"latest"` - Ts string `json:"ts"` - EventTs string `json:"event_ts"` -} - -type CommandsChangedEvent struct { - Type string `json:"type"` - EventTs string `json:"event_ts"` -} - -type DndUpdatedEvent struct { - Type string `json:"type"` - User string `json:"user"` - DndStatus struct { - DndEnabled bool `json:"dnd_enabled"` - NextDndStartTs int64 `json:"next_dnd_start_ts"` - NextDndEndTs int64 `json:"next_dnd_end_ts"` - SnoozeEnabled bool `json:"snooze_enabled"` - SnoozeEndtime int64 `json:"snooze_endtime"` - } `json:"dnd_status"` -} - -type DndUpdatedUserEvent struct { - Type string `json:"type"` - User string `json:"user"` - DndStatus struct { - DndEnabled bool `json:"dnd_enabled"` - NextDndStartTs int64 `json:"next_dnd_start_ts"` - NextDndEndTs int64 `json:"next_dnd_end_ts"` - } `json:"dnd_status"` -} - -type EmailDomainChangedEvent struct { - Type string `json:"type"` - EmailDomain string `json:"email_domain"` - EventTs string `json:"event_ts"` -} - -type GroupCloseEvent struct { - Type string `json:"type"` - User string `json:"user"` - Channel string `json:"channel"` -} - -type GroupHistoryChangedEvent struct { - Type string `json:"type"` - Latest string `json:"latest"` - Ts string `json:"ts"` - EventTs string `json:"event_ts"` -} - -type GroupOpenEvent struct { - Type string `json:"type"` - User string `json:"user"` - Channel string `json:"channel"` -} - -type ImCloseEvent struct { - Type string `json:"type"` - User string `json:"user"` - Channel string `json:"channel"` -} - -type ImCreatedEvent struct { - Type string `json:"type"` - User string `json:"user"` - Channel struct { - ID string `json:"id"` - } `json:"channel"` -} - -type ImHistoryChangedEvent struct { - Type string `json:"type"` - Latest string `json:"latest"` - Ts string `json:"ts"` - EventTs string `json:"event_ts"` -} - -type ImOpenEvent struct { - Type string `json:"type"` - User string `json:"user"` - Channel string `json:"channel"` -} - -type SubTeam struct { - ID string `json:"id"` - TeamID string `json:"team_id"` - IsUsergroup bool `json:"is_usergroup"` - Name string `json:"name"` - Description string `json:"description"` - Handle string `json:"handle"` - IsExternal bool `json:"is_external"` - DateCreate int64 `json:"date_create"` - DateUpdate int64 `json:"date_update"` - DateDelete int64 `json:"date_delete"` - AutoType string `json:"auto_type"` - CreatedBy string `json:"created_by"` - UpdatedBy string `json:"updated_by"` - DeletedBy string `json:"deleted_by"` - Prefs struct { - Channels []string `json:"channels"` - Groups []string `json:"groups"` - } `json:"prefs"` - Users []string `json:"users"` - UserCount int `json:"user_count"` -} - -type SubteamCreatedEvent struct { - Type string `json:"type"` - Subteam SubTeam `json:"subteam"` -} - -type SubteamMembersChangedEvent struct { - Type string `json:"type"` - SubteamID string `json:"subteam_id"` - TeamID string `json:"team_id"` - DatePreviousUpdate int `json:"date_previous_update"` - DateUpdate int64 `json:"date_update"` - AddedUsers []string `json:"added_users"` - AddedUsersCount string `json:"added_users_count"` - RemovedUsers []string `json:"removed_users"` - RemovedUsersCount string `json:"removed_users_count"` -} - -type SubteamSelfAddedEvent struct { - Type string `json:"type"` - SubteamID string `json:"subteam_id"` -} - -type SubteamSelfRemovedEvent struct { - Type string `json:"type"` - SubteamID string `json:"subteam_id"` -} - -type SubteamUpdatedEvent struct { - Type string `json:"type"` - Subteam SubTeam `json:"subteam"` -} - -type TeamDomainChangeEvent struct { - Type string `json:"type"` - URL string `json:"url"` - Domain string `json:"domain"` - TeamID string `json:"team_id"` -} - -type TeamRenameEvent struct { - Type string `json:"type"` - Name string `json:"name"` - TeamID string `json:"team_id"` -} - -type UserChangeEvent struct { - Type string `json:"type"` - User User `json:"user"` - CacheTS int64 `json:"cache_ts"` - EventTS string `json:"event_ts"` -} - -type AppDeletedEvent struct { - Type string `json:"type"` - AppID string `json:"app_id"` - AppName string `json:"app_name"` - AppOwnerID string `json:"app_owner_id"` - TeamID string `json:"team_id"` - TeamDomain string `json:"team_domain"` - EventTs string `json:"event_ts"` -} - -type AppInstalledEvent struct { - Type string `json:"type"` - AppID string `json:"app_id"` - AppName string `json:"app_name"` - AppOwnerID string `json:"app_owner_id"` - UserID string `json:"user_id"` - TeamID string `json:"team_id"` - TeamDomain string `json:"team_domain"` - EventTs string `json:"event_ts"` -} - -type AppRequestedEvent struct { - Type string `json:"type"` - AppRequest struct { - ID string `json:"id"` - App struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - HelpURL string `json:"help_url"` - PrivacyPolicyURL string `json:"privacy_policy_url"` - AppHomepageURL string `json:"app_homepage_url"` - AppDirectoryURL string `json:"app_directory_url"` - IsAppDirectoryApproved bool `json:"is_app_directory_approved"` - IsInternal bool `json:"is_internal"` - AdditionalInfo string `json:"additional_info"` - } `json:"app"` - PreviousResolution struct { - Status string `json:"status"` - Scopes []struct { - Name string `json:"name"` - Description string `json:"description"` - IsSensitive bool `json:"is_sensitive"` - TokenType string `json:"token_type"` - } `json:"scopes"` - } `json:"previous_resolution"` - User struct { - ID string `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - } `json:"user"` - Team struct { - ID string `json:"id"` - Name string `json:"name"` - Domain string `json:"domain"` - } `json:"team"` - Enterprise interface{} `json:"enterprise"` - Scopes []struct { - Name string `json:"name"` - Description string `json:"description"` - IsSensitive bool `json:"is_sensitive"` - TokenType string `json:"token_type"` - } `json:"scopes"` - Message string `json:"message"` - } `json:"app_request"` -} - -type AppUninstalledTeamEvent struct { - Type string `json:"type"` - AppID string `json:"app_id"` - AppName string `json:"app_name"` - AppOwnerID string `json:"app_owner_id"` - UserID string `json:"user_id"` - TeamID string `json:"team_id"` - TeamDomain string `json:"team_domain"` - EventTs string `json:"event_ts"` -} - -type CallRejectedEvent struct { - Token string `json:"token"` - TeamID string `json:"team_id"` - APIAppID string `json:"api_app_id"` - Event struct { - Type string `json:"type"` - CallID string `json:"call_id"` - UserID string `json:"user_id"` - ChannelID string `json:"channel_id"` - ExternalUniqueID string `json:"external_unique_id"` - } `json:"event"` - Type string `json:"type"` - EventID string `json:"event_id"` - AuthedUsers []string `json:"authed_users"` -} - -type ChannelSharedEvent struct { - Type string `json:"type"` - ConnectedTeamID string `json:"connected_team_id"` - Channel string `json:"channel"` - EventTs string `json:"event_ts"` -} - -type FileCreatedEvent struct { - Type string `json:"type"` - FileID string `json:"file_id"` - File struct { - ID string `json:"id"` - } `json:"file"` -} - -type FilePublicEvent struct { - Type string `json:"type"` - FileID string `json:"file_id"` - File struct { - ID string `json:"id"` - } `json:"file"` -} - -type FunctionExecutedEvent struct { - Type string `json:"type"` - Function struct { - ID string `json:"id"` - CallbackID string `json:"callback_id"` - Title string `json:"title"` - Description string `json:"description"` - Type string `json:"type"` - InputParameters []struct { - Type string `json:"type"` - Name string `json:"name"` - Description string `json:"description"` - Title string `json:"title"` - IsRequired bool `json:"is_required"` - } `json:"input_parameters"` - OutputParameters []struct { - Type string `json:"type"` - Name string `json:"name"` - Description string `json:"description"` - Title string `json:"title"` - IsRequired bool `json:"is_required"` - } `json:"output_parameters"` - AppID string `json:"app_id"` - DateCreated int64 `json:"date_created"` - DateUpdated int64 `json:"date_updated"` - DateDeleted int64 `json:"date_deleted"` - } `json:"function"` - Inputs map[string]string `json:"inputs"` - FunctionExecutionID string `json:"function_execution_id"` - WorkflowExecutionID string `json:"workflow_execution_id"` - EventTs string `json:"event_ts"` - BotAccessToken string `json:"bot_access_token"` -} - -type InviteRequestedEvent struct { - Type string `json:"type"` - InviteRequest struct { - ID string `json:"id"` - Email string `json:"email"` - DateCreated int64 `json:"date_created"` - RequesterIDs []string `json:"requester_ids"` - ChannelIDs []string `json:"channel_ids"` - InviteType string `json:"invite_type"` - RealName string `json:"real_name"` - DateExpire int64 `json:"date_expire"` - RequestReason string `json:"request_reason"` - Team struct { - ID string `json:"id"` - Name string `json:"name"` - Domain string `json:"domain"` - } `json:"team"` - } `json:"invite_request"` -} - -type StarAddedEvent struct { - Type string `json:"type"` - User string `json:"user"` - Item struct { - } `json:"item"` - EventTS string `json:"event_ts"` -} - -type StarRemovedEvent struct { - Type string `json:"type"` - User string `json:"user"` - Item struct { - } `json:"item"` - EventTS string `json:"event_ts"` -} - -type UserHuddleChangedEvent struct { - Type string `json:"type"` - User User `json:"user"` - CacheTS int64 `json:"cache_ts"` - EventTS string `json:"event_ts"` -} - -type User struct { - ID string `json:"id"` - TeamID string `json:"team_id"` - Name string `json:"name"` - Deleted bool `json:"deleted"` - Color string `json:"color"` - RealName string `json:"real_name"` - TZ string `json:"tz"` - TZLabel string `json:"tz_label"` - TZOffset int `json:"tz_offset"` - Profile Profile `json:"profile"` - IsAdmin bool `json:"is_admin"` - IsOwner bool `json:"is_owner"` - IsPrimaryOwner bool `json:"is_primary_owner"` - IsRestricted bool `json:"is_restricted"` - IsUltraRestricted bool `json:"is_ultra_restricted"` - IsBot bool `json:"is_bot"` - IsAppUser bool `json:"is_app_user"` - Updated int64 `json:"updated"` - IsEmailConfirmed bool `json:"is_email_confirmed"` - WhoCanShareContactCard string `json:"who_can_share_contact_card"` - Locale string `json:"locale"` -} - -type Profile struct { - Title string `json:"title"` - Phone string `json:"phone"` - Skype string `json:"skype"` - RealName string `json:"real_name"` - RealNameNormalized string `json:"real_name_normalized"` - DisplayName string `json:"display_name"` - DisplayNameNormalized string `json:"display_name_normalized"` - Fields map[string]interface{} `json:"fields"` - StatusText string `json:"status_text"` - StatusEmoji string `json:"status_emoji"` - StatusEmojiDisplayInfo []interface{} `json:"status_emoji_display_info"` - StatusExpiration int `json:"status_expiration"` - AvatarHash string `json:"avatar_hash"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Image24 string `json:"image_24"` - Image32 string `json:"image_32"` - Image48 string `json:"image_48"` - Image72 string `json:"image_72"` - Image192 string `json:"image_192"` - Image512 string `json:"image_512"` - StatusTextCanonical string `json:"status_text_canonical"` - Team string `json:"team"` -} - -type UserStatusChangedEvent struct { - Type string `json:"type"` - User User `json:"user"` - CacheTS int64 `json:"cache_ts"` - EventTS string `json:"event_ts"` -} - -type Actor struct { - ID string `json:"id"` - Name string `json:"name"` - IsBot bool `json:"is_bot"` - TeamID string `json:"team_id"` - Timezone string `json:"timezone"` - RealName string `json:"real_name"` - DisplayName string `json:"display_name"` -} - -type TargetUser struct { - Email string `json:"email"` - InviteID string `json:"invite_id"` -} - -type TeamIcon struct { - Image34 string `json:"image_34"` - ImageDefault bool `json:"image_default"` -} - -type Team struct { - ID string `json:"id"` - Icon TeamIcon `json:"icon"` - Name string `json:"name"` - Domain string `json:"domain"` - IsVerified bool `json:"is_verified"` - DateCreated int64 `json:"date_created"` - AvatarBaseURL string `json:"avatar_base_url"` - RequiresSponsorship bool `json:"requires_sponsorship"` -} - -type SharedChannelInviteRequestedEvent struct { - Actor Actor `json:"actor"` - ChannelID string `json:"channel_id"` - EventType string `json:"event_type"` - ChannelName string `json:"channel_name"` - ChannelType string `json:"channel_type"` - TargetUsers []TargetUser `json:"target_users"` - TeamsInChannel []Team `json:"teams_in_channel"` - IsExternalLimited bool `json:"is_external_limited"` - ChannelDateCreated int64 `json:"channel_date_created"` - ChannelMessageLatestCounted int64 `json:"channel_message_latest_counted_timestamp"` -} - type EventsAPIType string const ( @@ -1318,10 +661,6 @@ var EventsAPIInnerEventMapping = map[EventsAPIType]interface{}{ ChannelLeft: ChannelLeftEvent{}, ChannelRename: ChannelRenameEvent{}, ChannelIDChanged: ChannelIDChangedEvent{}, - FileChange: FileChangeEvent{}, - FileDeleted: FileDeletedEvent{}, - FileShared: FileSharedEvent{}, - FileUnshared: FileUnsharedEvent{}, GroupDeleted: GroupDeletedEvent{}, GroupArchive: GroupArchiveEvent{}, GroupUnarchive: GroupUnarchiveEvent{}, @@ -1337,52 +676,8 @@ var EventsAPIInnerEventMapping = map[EventsAPIType]interface{}{ PinRemoved: PinRemovedEvent{}, ReactionAdded: ReactionAddedEvent{}, ReactionRemoved: ReactionRemovedEvent{}, - SharedChannelInviteApproved: SharedChannelInviteApprovedEvent{}, - SharedChannelInviteAccepted: SharedChannelInviteAcceptedEvent{}, - SharedChannelInviteDeclined: SharedChannelInviteDeclinedEvent{}, - SharedChannelInviteReceived: SharedChannelInviteReceivedEvent{}, TeamJoin: TeamJoinEvent{}, TokensRevoked: TokensRevokedEvent{}, EmojiChanged: EmojiChangedEvent{}, WorkflowStepExecute: WorkflowStepExecuteEvent{}, - MessageMetadataPosted: MessageMetadataPostedEvent{}, - MessageMetadataUpdated: MessageMetadataUpdatedEvent{}, - MessageMetadataDeleted: MessageMetadataDeletedEvent{}, - TeamAccessGranted: TeamAccessGrantedEvent{}, - TeamAccessRevoked: TeamAccessRevokedEvent{}, - UserProfileChanged: UserProfileChangedEvent{}, - ChannelHistoryChanged: ChannelHistoryChangedEvent{}, - DndUpdated: DndUpdatedEvent{}, - DndUpdatedUser: DndUpdatedUserEvent{}, - EmailDomainChanged: EmailDomainChangedEvent{}, - GroupClose: GroupCloseEvent{}, - GroupHistoryChanged: GroupHistoryChangedEvent{}, - GroupOpen: GroupOpenEvent{}, - ImClose: ImCloseEvent{}, - ImCreated: ImCreatedEvent{}, - ImHistoryChanged: ImHistoryChangedEvent{}, - ImOpen: ImOpenEvent{}, - SubteamCreated: SubteamCreatedEvent{}, - SubteamMembersChanged: SubteamMembersChangedEvent{}, - SubteamSelfAdded: SubteamSelfAddedEvent{}, - SubteamSelfRemoved: SubteamSelfRemovedEvent{}, - SubteamUpdated: SubteamUpdatedEvent{}, - TeamDomainChange: TeamDomainChangeEvent{}, - TeamRename: TeamRenameEvent{}, - UserChange: UserChangeEvent{}, - AppDeleted: AppDeletedEvent{}, - AppInstalled: AppInstalledEvent{}, - AppRequested: AppRequestedEvent{}, - AppUninstalledTeam: AppUninstalledTeamEvent{}, - CallRejected: CallRejectedEvent{}, - ChannelShared: ChannelSharedEvent{}, - FileCreated: FileCreatedEvent{}, - FilePublic: FilePublicEvent{}, - FunctionExecuted: FunctionExecutedEvent{}, - InviteRequested: InviteRequestedEvent{}, - SharedChannelInviteRequested: SharedChannelInviteRequestedEvent{}, - StarAdded: StarAddedEvent{}, - StarRemoved: StarRemovedEvent{}, - UserHuddleChanged: UserHuddleChangedEvent{}, - UserStatusChanged: UserStatusChangedEvent{}, } From 9f7cce5561428d4a9f9d7e06e29dcf827f467a00 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 16 Oct 2024 13:28:17 -0400 Subject: [PATCH 07/11] Removed additional events and properties not part of this PR --- slackevents/inner_events.go | 107 ------------------------------------ 1 file changed, 107 deletions(-) diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index 948ad3b9e..16c84fdbc 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -57,9 +57,6 @@ type AppMentionEvent struct { // BotID is filled out when a bot triggers the app_mention event BotID string `json:"bot_id,omitempty"` - - // When the app is mentioned in the edited message - Edited *Edited `json:"edited,omitempty"` } // AppHomeOpenedEvent Your Slack app home was opened. @@ -247,9 +244,6 @@ type MessageEvent struct { PreviousMessage *MessageEvent `json:"previous_message,omitempty"` Edited *Edited `json:"edited,omitempty"` - // Deleted Message - DeletedTimeStamp string `json:"deleted_ts,omitempty"` - // Message Subtypes SubType string `json:"subtype,omitempty"` @@ -261,11 +255,8 @@ type MessageEvent struct { Upload bool `json:"upload"` Files []File `json:"files"` - Blocks slack.Blocks `json:"blocks,omitempty"` Attachments []slack.Attachment `json:"attachments,omitempty"` - Metadata slack.SlackMetadata `json:"metadata,omitempty"` - // Root is the message that was broadcast to the channel when the SubType is // thread_broadcast. If this is not a thread_broadcast message event, this // value is nil. @@ -517,14 +508,6 @@ const ( GroupLeft = EventsAPIType("group_left") // GroupRename is sent when a group is renamed. GroupRename = EventsAPIType("group_rename") - // FileChange is sent when a file is changed. - FileChange = EventsAPIType("file_change") - // FileDeleted is sent when a file is deleted. - FileDeleted = EventsAPIType("file_deleted") - // FileShared is sent when a file is shared. - FileShared = EventsAPIType("file_shared") - // FileUnshared is sent when a file is unshared. - FileUnshared = EventsAPIType("file_unshared") // GridMigrationFinished An enterprise grid migration has finished on this workspace. GridMigrationFinished = EventsAPIType("grid_migration_finished") // GridMigrationStarted An enterprise grid migration has started on this workspace. @@ -547,102 +530,12 @@ const ( ReactionRemoved = EventsAPIType("reaction_removed") // TeamJoin A new user joined the workspace TeamJoin = EventsAPIType("team_join") - // Slack connect app or bot invite received - SharedChannelInviteReceived = EventsAPIType("shared_channel_invite_received") - // Slack connect channel invite approved - SharedChannelInviteApproved = EventsAPIType("shared_channel_invite_approved") - // Slack connect channel invite declined - SharedChannelInviteDeclined = EventsAPIType("shared_channel_invite_declined") - // Slack connect channel invite accepted by an end user - SharedChannelInviteAccepted = EventsAPIType("shared_channel_invite_accepted") // TokensRevoked APP's API tokes are revoked TokensRevoked = EventsAPIType("tokens_revoked") // EmojiChanged A custom emoji has been added or changed EmojiChanged = EventsAPIType("emoji_changed") // WorkflowStepExecute Happens, if a workflow step of your app is invoked WorkflowStepExecute = EventsAPIType("workflow_step_execute") - // MessageMetadataPosted A message with metadata was posted - MessageMetadataPosted = EventsAPIType("message_metadata_posted") - // MessageMetadataUpdated A message with metadata was updated - MessageMetadataUpdated = EventsAPIType("message_metadata_updated") - // MessageMetadataDeleted A message with metadata was deleted - MessageMetadataDeleted = EventsAPIType("message_metadata_deleted") - // TeamAccessGranted is sent if access to teams was granted for your org-wide app. - TeamAccessGranted = EventsAPIType("team_access_granted") - // TeamAccessRevoked is sent if access to teams was revoked for your org-wide app. - TeamAccessRevoked = EventsAPIType("team_access_revoked") - // UserProfileChanged is sent if a user's profile information has changed. - UserProfileChanged = EventsAPIType("user_profile_changed") - // ChannelHistoryChanged The history of a channel changed - ChannelHistoryChanged = EventsAPIType("channel_history_changed") - // CommandsChanged A command was changed - CommandsChanged = EventsAPIType("commands_changed") - // DndUpdated Do Not Disturb settings were updated - DndUpdated = EventsAPIType("dnd_updated") - // DndUpdatedUser Do Not Disturb settings for a user were updated - DndUpdatedUser = EventsAPIType("dnd_updated_user") - // EmailDomainChanged The email domain changed - EmailDomainChanged = EventsAPIType("email_domain_changed") - // GroupClose A group was closed - GroupClose = EventsAPIType("group_close") - // GroupHistoryChanged The history of a group changed - GroupHistoryChanged = EventsAPIType("group_history_changed") - // GroupOpen A group was opened - GroupOpen = EventsAPIType("group_open") - // ImClose An instant message channel was closed - ImClose = EventsAPIType("im_close") - // ImCreated An instant message channel was created - ImCreated = EventsAPIType("im_created") - // ImHistoryChanged The history of an instant message channel changed - ImHistoryChanged = EventsAPIType("im_history_changed") - // ImOpen An instant message channel was opened - ImOpen = EventsAPIType("im_open") - // SubteamCreated A subteam was created - SubteamCreated = EventsAPIType("subteam_created") - // SubteamMembersChanged The members of a subteam changed - SubteamMembersChanged = EventsAPIType("subteam_members_changed") - // SubteamSelfAdded The current user was added to a subteam - SubteamSelfAdded = EventsAPIType("subteam_self_added") - // SubteamSelfRemoved The current user was removed from a subteam - SubteamSelfRemoved = EventsAPIType("subteam_self_removed") - // SubteamUpdated A subteam was updated - SubteamUpdated = EventsAPIType("subteam_updated") - // TeamDomainChange The team's domain changed - TeamDomainChange = EventsAPIType("team_domain_change") - // TeamRename The team was renamed - TeamRename = EventsAPIType("team_rename") - // UserChange A user object has changed - UserChange = EventsAPIType("user_change") - // AppDeleted is an event when an app is deleted from a workspace - AppDeleted = EventsAPIType("app_deleted") - // AppInstalled is an event when an app is installed to a workspace - AppInstalled = EventsAPIType("app_installed") - // AppRequested is an event when a user requests to install an app to a workspace - AppRequested = EventsAPIType("app_requested") - // AppUninstalledTeam is an event when an app is uninstalled from a team - AppUninstalledTeam = EventsAPIType("app_uninstalled_team") - // CallRejected is an event when a Slack call is rejected - CallRejected = EventsAPIType("call_rejected") - // ChannelShared is an event when a channel is shared with another workspace - ChannelShared = EventsAPIType("channel_shared") - // FileCreated is an event when a file is created in a workspace - FileCreated = EventsAPIType("file_created") - // FilePublic is an event when a file is made public in a workspace - FilePublic = EventsAPIType("file_public") - // FunctionExecuted is an event when a Slack function is executed - FunctionExecuted = EventsAPIType("function_executed") - // InviteRequested is an event when a user requests an invite to a workspace - InviteRequested = EventsAPIType("invite_requested") - // SharedChannelInviteRequested is an event when an invitation to share a channel is requested - SharedChannelInviteRequested = EventsAPIType("shared_channel_invite_requested") - // StarAdded is an event when a star is added to a message or file - StarAdded = EventsAPIType("star_added") - // StarRemoved is an event when a star is removed from a message or file - StarRemoved = EventsAPIType("star_removed") - // UserHuddleChanged is an event when a user's huddle status changes - UserHuddleChanged = EventsAPIType("user_huddle_changed") - // UserStatusChanged is an event when a user's status changes - UserStatusChanged = EventsAPIType("user_status_changed") ) // EventsAPIInnerEventMapping maps INNER Event API events to their corresponding struct From 6436504392eb0ca797a35a2ee1760e2bfa1f6e7c Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 16 Oct 2024 13:30:49 -0400 Subject: [PATCH 08/11] Added Section Block Option for Expand --- block_section.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/block_section.go b/block_section.go index 01ffd5a1d..1ffc17e82 100644 --- a/block_section.go +++ b/block_section.go @@ -9,6 +9,7 @@ type SectionBlock struct { BlockID string `json:"block_id,omitempty"` Fields []*TextBlockObject `json:"fields,omitempty"` Accessory *Accessory `json:"accessory,omitempty"` + Expand bool `json:"expand,omitempty"` } // BlockType returns the type of the block @@ -25,6 +26,15 @@ func SectionBlockOptionBlockID(blockID string) SectionBlockOption { } } +// SectionBlockOptionExpand allows long text to be auto-expanded when displaying +// +// @see https://api.slack.com/reference/block-kit/blocks#section +func SectionBlockOptionExpand(shouldExpand bool) SectionBlockOption { + return func(block *SectionBlock) { + block.Expand = shouldExpand + } +} + // NewSectionBlock returns a new instance of a section block to be rendered func NewSectionBlock(textObj *TextBlockObject, fields []*TextBlockObject, accessory *Accessory, options ...SectionBlockOption) *SectionBlock { block := SectionBlock{ From 61280ce6ae5f5958a36fcc3027005471d10345ea Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 23 Oct 2024 18:03:30 -0400 Subject: [PATCH 09/11] Refactored tests --- assistant_test.go | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/assistant_test.go b/assistant_test.go index 0dde9cc5a..79d5d93c2 100644 --- a/assistant_test.go +++ b/assistant_test.go @@ -21,17 +21,26 @@ func assistantThreadsSuggestedPromptsHandler(rw http.ResponseWriter, r *http.Req rw.Header().Set("Content-Type", "application/json") - if channelID != "" && threadTS != "" && len(prompts) == 2 { + if channelID == "" { + rw.Write([]byte(`{ "ok": false, "error": "channel_id missing" }`)) + return + } - resp, _ := json.Marshal(&addBookmarkResponse{ - SlackResponse: SlackResponse{Ok: true}, - }) - rw.Write(resp) + if threadTS == "" { + rw.Write([]byte(`{ "ok": false, "error": "thread_ts missing" }`)) + return + } - } else { - rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) + if len(prompts) != 2 { + rw.Write([]byte(`{ "ok": false, "error": "incorrect prompt count" }`)) + return } + resp, _ := json.Marshal(&addBookmarkResponse{ + SlackResponse: SlackResponse{Ok: true}, + }) + rw.Write(resp) + } func TestAssistantThreadsSuggestedPrompts(t *testing.T) { @@ -63,17 +72,26 @@ func setAssistantThreadsStatusHandler(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "application/json") - if channelID != "" && threadTS != "" && status != "" { + if channelID == "" { + rw.Write([]byte(`{ "ok": false, "error": "channel_id missing" }`)) + return + } - resp, _ := json.Marshal(&addBookmarkResponse{ - SlackResponse: SlackResponse{Ok: true}, - }) - rw.Write(resp) + if threadTS == "" { + rw.Write([]byte(`{ "ok": false, "error": "thread_ts missing" }`)) + return + } - } else { - rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) + if status == "" { + rw.Write([]byte(`{ "ok": false, "error": "status missing" }`)) + return } + resp, _ := json.Marshal(&addBookmarkResponse{ + SlackResponse: SlackResponse{Ok: true}, + }) + rw.Write(resp) + } func TestSetAssistantThreadsStatus(t *testing.T) { From 8e128d97df2b2dd06d2d59d1e698d8f84fa864d7 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 23 Oct 2024 18:17:33 -0400 Subject: [PATCH 10/11] Updated tests due to form variables --- assistant_test.go | 71 ++--------------------------------------------- 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/assistant_test.go b/assistant_test.go index 79d5d93c2..51c271611 100644 --- a/assistant_test.go +++ b/assistant_test.go @@ -6,46 +6,9 @@ import ( "testing" ) -func assistantThreadsSuggestedPromptsHandler(rw http.ResponseWriter, r *http.Request) { - - channelID := r.FormValue("channel_id") - threadTS := r.FormValue("thread_ts") - promptStr := r.FormValue("prompts") - - var prompts []AssistantThreadsPrompt - err := json.Unmarshal([]byte(promptStr), &prompts) - if err != nil { - rw.Write([]byte(`{ "ok": false, "error": "errored" }`)) - return - } - - rw.Header().Set("Content-Type", "application/json") - - if channelID == "" { - rw.Write([]byte(`{ "ok": false, "error": "channel_id missing" }`)) - return - } - - if threadTS == "" { - rw.Write([]byte(`{ "ok": false, "error": "thread_ts missing" }`)) - return - } - - if len(prompts) != 2 { - rw.Write([]byte(`{ "ok": false, "error": "incorrect prompt count" }`)) - return - } - - resp, _ := json.Marshal(&addBookmarkResponse{ - SlackResponse: SlackResponse{Ok: true}, - }) - rw.Write(resp) - -} - func TestAssistantThreadsSuggestedPrompts(t *testing.T) { - http.HandleFunc("/assistant.threads.setSuggestedPrompts", assistantThreadsSuggestedPromptsHandler) + http.HandleFunc("/assistant.threads.setSuggestedPrompts", okJSONHandler) once.Do(startServer) api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) @@ -64,39 +27,9 @@ func TestAssistantThreadsSuggestedPrompts(t *testing.T) { } -func setAssistantThreadsStatusHandler(rw http.ResponseWriter, r *http.Request) { - - channelID := r.FormValue("channel_id") - threadTS := r.FormValue("thread_ts") - status := r.FormValue("status") - - rw.Header().Set("Content-Type", "application/json") - - if channelID == "" { - rw.Write([]byte(`{ "ok": false, "error": "channel_id missing" }`)) - return - } - - if threadTS == "" { - rw.Write([]byte(`{ "ok": false, "error": "thread_ts missing" }`)) - return - } - - if status == "" { - rw.Write([]byte(`{ "ok": false, "error": "status missing" }`)) - return - } - - resp, _ := json.Marshal(&addBookmarkResponse{ - SlackResponse: SlackResponse{Ok: true}, - }) - rw.Write(resp) - -} - func TestSetAssistantThreadsStatus(t *testing.T) { - http.HandleFunc("/assistant.threads.setStatus", setAssistantThreadsStatusHandler) + http.HandleFunc("/assistant.threads.setStatus", okJSONHandler) once.Do(startServer) api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) From 11cf2bc49e168c253176d7b599cb81495038e837 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Wed, 23 Oct 2024 18:22:08 -0400 Subject: [PATCH 11/11] go fmt --- slackevents/inner_events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index 0b32afd37..720640b63 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -1309,7 +1309,7 @@ var EventsAPIInnerEventMapping = map[EventsAPIType]interface{}{ AppMention: AppMentionEvent{}, AppHomeOpened: AppHomeOpenedEvent{}, AppUninstalled: AppUninstalledEvent{}, - AssistantThreadStarted: AssistantThreadStartedEvent{}, + AssistantThreadStarted: AssistantThreadStartedEvent{}, AssistantThreadContextChanged: AssistantThreadContextChangedEvent{}, ChannelCreated: ChannelCreatedEvent{}, ChannelDeleted: ChannelDeletedEvent{},