diff options
Diffstat (limited to 'vendor/github.com/slack-go/slack')
20 files changed, 995 insertions, 185 deletions
diff --git a/vendor/github.com/slack-go/slack/Makefile b/vendor/github.com/slack-go/slack/Makefile index 1c64747d..72796401 100644 --- a/vendor/github.com/slack-go/slack/Makefile +++ b/vendor/github.com/slack-go/slack/Makefile @@ -25,12 +25,12 @@ lint: @go vet . test: - @go test -count=1 -timeout 300s -short . + @go test -v -count=1 -timeout 300s -short ./... test-race: - @go test -count=1 -timeout 300s -short -race . + @go test -v -count=1 -timeout 300s -short -race ./... test-integration: - @go test -count=1 -timeout 600s . + @go test -v -count=1 -timeout 600s ./... pr-prep: fmt lint test-race test-integration diff --git a/vendor/github.com/slack-go/slack/auth.go b/vendor/github.com/slack-go/slack/auth.go index dc1dbcdf..f4f7f003 100644 --- a/vendor/github.com/slack-go/slack/auth.go +++ b/vendor/github.com/slack-go/slack/auth.go @@ -27,7 +27,7 @@ func (api *Client) SendAuthRevoke(token string) (*AuthRevokeResponse, error) { return api.SendAuthRevokeContext(context.Background(), token) } -// SendAuthRevokeContext will retrieve the satus from api.test +// SendAuthRevokeContext will send a revocation request for our token to api.revoke with context func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*AuthRevokeResponse, error) { if token == "" { token = api.token diff --git a/vendor/github.com/slack-go/slack/block.go b/vendor/github.com/slack-go/slack/block.go index 32ff260c..dbc34496 100644 --- a/vendor/github.com/slack-go/slack/block.go +++ b/vendor/github.com/slack-go/slack/block.go @@ -14,6 +14,7 @@ const ( MBTImage MessageBlockType = "image" MBTAction MessageBlockType = "actions" MBTContext MessageBlockType = "context" + MBTFile MessageBlockType = "file" MBTInput MessageBlockType = "input" ) diff --git a/vendor/github.com/slack-go/slack/block_conv.go b/vendor/github.com/slack-go/slack/block_conv.go index ce48ce19..43c0c96b 100644 --- a/vendor/github.com/slack-go/slack/block_conv.go +++ b/vendor/github.com/slack-go/slack/block_conv.go @@ -56,20 +56,16 @@ func (b *Blocks) UnmarshalJSON(data []byte) error { block = &ContextBlock{} case "divider": block = &DividerBlock{} + case "file": + block = &FileBlock{} case "image": block = &ImageBlock{} case "input": block = &InputBlock{} case "section": block = &SectionBlock{} - case "rich_text": - // for now ignore the (complex) content of rich_text blocks until we can fully support it - continue - case "file": - // for now ignore the file blocks until we can fully support it - continue default: - return errors.New("unsupported block type") + block = &UnknownBlock{} } err = json.Unmarshal(r, block) @@ -253,12 +249,36 @@ func (a *Accessory) UnmarshalJSON(data []byte) error { return err } a.DatePickerElement = element.(*DatePickerBlockElement) - case "static_select": + case "plain_text_input": + element, err := unmarshalBlockElement(r, &PlainTextInputBlockElement{}) + if err != nil { + return err + } + a.PlainTextInputElement = element.(*PlainTextInputBlockElement) + case "radio_buttons": + element, err := unmarshalBlockElement(r, &RadioButtonsBlockElement{}) + if err != nil { + return err + } + a.RadioButtonsElement = element.(*RadioButtonsBlockElement) + case "static_select", "external_select", "users_select", "conversations_select", "channels_select": element, err := unmarshalBlockElement(r, &SelectBlockElement{}) if err != nil { return err } a.SelectElement = element.(*SelectBlockElement) + case "multi_static_select", "multi_external_select", "multi_users_select", "multi_conversations_select", "multi_channels_select": + element, err := unmarshalBlockElement(r, &MultiSelectBlockElement{}) + if err != nil { + return err + } + a.MultiSelectElement = element.(*MultiSelectBlockElement) + default: + element, err := unmarshalBlockElement(r, &UnknownBlockElement{}) + if err != nil { + return err + } + a.UnknownElement = element.(*UnknownBlockElement) } return nil @@ -285,9 +305,18 @@ func toBlockElement(element *Accessory) BlockElement { if element.DatePickerElement != nil { return element.DatePickerElement } + if element.PlainTextInputElement != nil { + return element.PlainTextInputElement + } + if element.RadioButtonsElement != nil { + return element.RadioButtonsElement + } if element.SelectElement != nil { return element.SelectElement } + if element.MultiSelectElement != nil { + return element.MultiSelectElement + } return nil } diff --git a/vendor/github.com/slack-go/slack/block_element.go b/vendor/github.com/slack-go/slack/block_element.go index e0a7bf96..8460e957 100644 --- a/vendor/github.com/slack-go/slack/block_element.go +++ b/vendor/github.com/slack-go/slack/block_element.go @@ -8,6 +8,7 @@ const ( METOverflow MessageElementType = "overflow" METDatepicker MessageElementType = "datepicker" METPlainTextInput MessageElementType = "plain_text_input" + METRadioButtons MessageElementType = "radio_buttons" MixedElementImage MixedElementType = "mixed_image" MixedElementText MixedElementType = "mixed_text" @@ -17,6 +18,12 @@ const ( OptTypeUser string = "users_select" OptTypeConversations string = "conversations_select" OptTypeChannels string = "channels_select" + + MultiOptTypeStatic string = "multi_static_select" + MultiOptTypeExternal string = "multi_external_select" + MultiOptTypeUser string = "multi_users_select" + MultiOptTypeConversations string = "multi_conversations_select" + MultiOptTypeChannels string = "multi_channels_select" ) type MessageElementType string @@ -32,11 +39,15 @@ type MixedElement interface { } type Accessory struct { - ImageElement *ImageBlockElement - ButtonElement *ButtonBlockElement - OverflowElement *OverflowBlockElement - DatePickerElement *DatePickerBlockElement - SelectElement *SelectBlockElement + ImageElement *ImageBlockElement + ButtonElement *ButtonBlockElement + OverflowElement *OverflowBlockElement + DatePickerElement *DatePickerBlockElement + PlainTextInputElement *PlainTextInputBlockElement + RadioButtonsElement *RadioButtonsBlockElement + SelectElement *SelectBlockElement + MultiSelectElement *MultiSelectBlockElement + UnknownElement *UnknownBlockElement } // NewAccessory returns a new Accessory for a given block element @@ -50,11 +61,17 @@ func NewAccessory(element BlockElement) *Accessory { return &Accessory{OverflowElement: element.(*OverflowBlockElement)} case *DatePickerBlockElement: return &Accessory{DatePickerElement: element.(*DatePickerBlockElement)} + case *PlainTextInputBlockElement: + return &Accessory{PlainTextInputElement: element.(*PlainTextInputBlockElement)} + case *RadioButtonsBlockElement: + return &Accessory{RadioButtonsElement: element.(*RadioButtonsBlockElement)} case *SelectBlockElement: return &Accessory{SelectElement: element.(*SelectBlockElement)} + case *MultiSelectBlockElement: + return &Accessory{MultiSelectElement: element.(*MultiSelectBlockElement)} + default: + return &Accessory{UnknownElement: element.(*UnknownBlockElement)} } - - return nil } // BlockElements is a convenience struct defined to allow dynamic unmarshalling of @@ -63,6 +80,20 @@ type BlockElements struct { ElementSet []BlockElement `json:"elements,omitempty"` } +// UnknownBlockElement any block element that this library does not directly support. +// See the "Rich Elements" section at the following URL: +// https://api.slack.com/changelog/2019-09-what-they-see-is-what-you-get-and-more-and-less +// New block element types may be introduced by Slack at any time; this is a catch-all for any such block elements. +type UnknownBlockElement struct { + Type MessageElementType `json:"type"` + Elements BlockElements +} + +// ElementType returns the type of the Element +func (s UnknownBlockElement) ElementType() MessageElementType { + return s.Type +} + // ImageBlockElement An element to insert an image - this element can be used // in section and context blocks only. If you want a block with only an image // in it, you're looking for the image block. @@ -135,6 +166,20 @@ func NewButtonBlockElement(actionID, value string, text *TextBlockObject) *Butto } } +// OptionsResponse defines the response used for select block typahead. +// +// More Information: https://api.slack.com/reference/block-kit/block-elements#external_multi_select +type OptionsResponse struct { + Options []*OptionBlockObject `json:"options,omitempty"` +} + +// OptionGroupsResponse defines the response used for select block typahead. +// +// More Information: https://api.slack.com/reference/block-kit/block-elements#external_multi_select +type OptionGroupsResponse struct { + OptionGroups []*OptionGroupBlockObject `json:"option_groups,omitempty"` +} + // SelectBlockElement defines the simplest form of select menu, with a static list // of options passed in when defining the element. // @@ -149,7 +194,7 @@ type SelectBlockElement struct { InitialUser string `json:"initial_user,omitempty"` InitialConversation string `json:"initial_conversation,omitempty"` InitialChannel string `json:"initial_channel,omitempty"` - MinQueryLength int `json:"min_query_length,omitempty"` + MinQueryLength *int `json:"min_query_length,omitempty"` Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` } @@ -185,6 +230,56 @@ func NewOptionsGroupSelectBlockElement( } } +// MultiSelectBlockElement defines a multiselect menu, with a static list +// of options passed in when defining the element. +// +// More Information: https://api.slack.com/reference/messaging/block-elements#multi_select +type MultiSelectBlockElement struct { + Type string `json:"type,omitempty"` + Placeholder *TextBlockObject `json:"placeholder,omitempty"` + ActionID string `json:"action_id,omitempty"` + Options []*OptionBlockObject `json:"options,omitempty"` + OptionGroups []*OptionGroupBlockObject `json:"option_groups,omitempty"` + InitialOptions []*OptionBlockObject `json:"initial_options,omitempty"` + InitialUsers []string `json:"initial_users,omitempty"` + InitialConversations []string `json:"initial_conversations,omitempty"` + InitialChannels []string `json:"initial_channels,omitempty"` + MinQueryLength *int `json:"min_query_length,omitempty"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` +} + +// ElementType returns the type of the Element +func (s MultiSelectBlockElement) ElementType() MessageElementType { + return MessageElementType(s.Type) +} + +// NewOptionsMultiSelectBlockElement returns a new instance of SelectBlockElement for use with +// the Options object only. +func NewOptionsMultiSelectBlockElement(optType string, placeholder *TextBlockObject, actionID string, options ...*OptionBlockObject) *MultiSelectBlockElement { + return &MultiSelectBlockElement{ + Type: optType, + Placeholder: placeholder, + ActionID: actionID, + Options: options, + } +} + +// NewOptionsGroupMultiSelectBlockElement returns a new instance of MultiSelectBlockElement for use with +// the Options object only. +func NewOptionsGroupMultiSelectBlockElement( + optType string, + placeholder *TextBlockObject, + actionID string, + optGroups ...*OptionGroupBlockObject, +) *MultiSelectBlockElement { + return &MultiSelectBlockElement{ + Type: optType, + Placeholder: placeholder, + ActionID: actionID, + OptionGroups: optGroups, + } +} + // OverflowBlockElement defines the fields needed to use an overflow element. // And Overflow Element is like a cross between a button and a select menu - // when a user clicks on this overflow button, they will be presented with a @@ -238,10 +333,11 @@ func NewDatePickerBlockElement(actionID string) *DatePickerBlockElement { } } -// PlainTextInputBlockElement creates a field where a user can enter freeform data. +// PlainTextInputBlockElement creates a field where a user can enter freeform +// data. // Plain-text input elements are currently only available in modals. // -// More Information: https://api.slack.com/reference/messaging/block-elements#input +// More Information: https://api.slack.com/reference/block-kit/block-elements#input type PlainTextInputBlockElement struct { Type MessageElementType `json:"type"` ActionID string `json:"action_id"` @@ -257,7 +353,8 @@ func (s PlainTextInputBlockElement) ElementType() MessageElementType { return s.Type } -// NewPlainTextInputBlockElement returns an instance of a plain-text input element +// NewPlainTextInputBlockElement returns an instance of a plain-text input +// element func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string) *PlainTextInputBlockElement { return &PlainTextInputBlockElement{ Type: METPlainTextInput, @@ -265,3 +362,29 @@ func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string Placeholder: placeholder, } } + +// RadioButtonsBlockElement defines an element which lets users choose one item +// from a list of possible options. +// +// More Information: https://api.slack.com/reference/block-kit/block-elements#radio +type RadioButtonsBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id"` + Options []*OptionBlockObject `json:"options"` + InitialOption *OptionBlockObject `json:"initial_option,omitempty"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` +} + +// ElementType returns the type of the Element +func (s RadioButtonsBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewRadioButtonsBlockElement returns an instance of a radio buttons element. +func NewRadioButtonsBlockElement(actionID string, options ...*OptionBlockObject) *RadioButtonsBlockElement { + return &RadioButtonsBlockElement{ + Type: METRadioButtons, + ActionID: actionID, + Options: options, + } +} diff --git a/vendor/github.com/slack-go/slack/block_file.go b/vendor/github.com/slack-go/slack/block_file.go new file mode 100644 index 00000000..ac4453f7 --- /dev/null +++ b/vendor/github.com/slack-go/slack/block_file.go @@ -0,0 +1,26 @@ +package slack + +// FileBlock defines data that is used to display a remote file. +// +// More Information: https://api.slack.com/reference/block-kit/blocks#file +type FileBlock struct { + Type MessageBlockType `json:"type"` + BlockID string `json:"block_id,omitempty"` + ExternalID string `json:"external_id"` + Source string `json:"source"` +} + +// BlockType returns the type of the block +func (s FileBlock) BlockType() MessageBlockType { + return s.Type +} + +// NewFileBlock returns a new instance of a file block +func NewFileBlock(blockID string, externalID string, source string) *FileBlock { + return &FileBlock{ + Type: MBTFile, + BlockID: blockID, + ExternalID: externalID, + Source: source, + } +} diff --git a/vendor/github.com/slack-go/slack/block_input.go b/vendor/github.com/slack-go/slack/block_input.go index 9d082038..10638cd9 100644 --- a/vendor/github.com/slack-go/slack/block_input.go +++ b/vendor/github.com/slack-go/slack/block_input.go @@ -1,10 +1,8 @@ package slack -// InputBlock defines data that is used to collect information from users - -// it can hold a plain-text input element, a select menu element, -// a multi-select menu element, or a datepicker. +// InputBlock defines data that is used to display user input fields. // -// More Information: https://api.slack.com/reference/messaging/blocks#input +// More Information: https://api.slack.com/reference/block-kit/blocks#input type InputBlock struct { Type MessageBlockType `json:"type"` BlockID string `json:"block_id,omitempty"` @@ -19,7 +17,7 @@ func (s InputBlock) BlockType() MessageBlockType { return s.Type } -// NewInputBlock returns a new instance of an Input Block +// NewInputBlock returns a new instance of an input block func NewInputBlock(blockID string, label *TextBlockObject, element BlockElement) *InputBlock { return &InputBlock{ Type: MBTInput, diff --git a/vendor/github.com/slack-go/slack/block_object.go b/vendor/github.com/slack-go/slack/block_object.go index 824ec93c..cf3536d0 100644 --- a/vendor/github.com/slack-go/slack/block_object.go +++ b/vendor/github.com/slack-go/slack/block_object.go @@ -145,6 +145,14 @@ func NewTextBlockObject(elementType, text string, emoji, verbatim bool) *TextBlo } } +// BlockType returns the type of the block +func (t TextBlockObject) BlockType() MessageBlockType { + if t.Type == "mrkdown" { + return MarkdownType + } + return PlainTextType +} + // ConfirmationBlockObject defines a dialog that provides a confirmation step to // any interactive element. This dialog will ask the user to confirm their action by // offering a confirm and deny buttons. diff --git a/vendor/github.com/slack-go/slack/block_unknown.go b/vendor/github.com/slack-go/slack/block_unknown.go new file mode 100644 index 00000000..97054c73 --- /dev/null +++ b/vendor/github.com/slack-go/slack/block_unknown.go @@ -0,0 +1,13 @@ +package slack + +// UnknownBlock represents a block type that is not yet known. This block type exists to prevent Slack from introducing +// new and unknown block types that break this library. +type UnknownBlock struct { + Type MessageBlockType `json:"type"` + BlockID string `json:"block_id,omitempty"` +} + +// BlockType returns the type of the block +func (b UnknownBlock) BlockType() MessageBlockType { + return b.Type +} diff --git a/vendor/github.com/slack-go/slack/channels.go b/vendor/github.com/slack-go/slack/channels.go index c99e6655..a90d2384 100644 --- a/vendor/github.com/slack-go/slack/channels.go +++ b/vendor/github.com/slack-go/slack/channels.go @@ -4,6 +4,7 @@ import ( "context" "net/url" "strconv" + "time" ) type channelResponseFull struct { @@ -14,6 +15,7 @@ type channelResponseFull struct { NotInChannel bool `json:"not_in_channel"` History SlackResponse + Metadata ResponseMetadata `json:"response_metadata"` } // Channel contains information about the channel @@ -35,25 +37,21 @@ func (api *Client) channelRequest(ctx context.Context, path string, values url.V return response, response.Err() } -type channelsConfig struct { - values url.Values -} - // GetChannelsOption option provided when getting channels. -type GetChannelsOption func(*channelsConfig) error +type GetChannelsOption func(*ChannelPagination) error // GetChannelsOptionExcludeMembers excludes the members collection from each channel. func GetChannelsOptionExcludeMembers() GetChannelsOption { - return func(config *channelsConfig) error { - config.values.Add("exclude_members", "true") + return func(p *ChannelPagination) error { + p.excludeMembers = true return nil } } // GetChannelsOptionExcludeArchived excludes archived channels from results. func GetChannelsOptionExcludeArchived() GetChannelsOption { - return func(config *channelsConfig) error { - config.values.Add("exclude_archived", "true") + return func(p *ChannelPagination) error { + p.excludeArchived = true return nil } } @@ -266,6 +264,78 @@ func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, us return err } +func newChannelPagination(c *Client, options ...GetChannelsOption) (cp ChannelPagination) { + cp = ChannelPagination{ + c: c, + limit: 200, // per slack api documentation. + } + + for _, opt := range options { + opt(&cp) + } + + return cp +} + +// ChannelPagination allows for paginating over the channels +type ChannelPagination struct { + Channels []Channel + limit int + excludeArchived bool + excludeMembers bool + previousResp *ResponseMetadata + c *Client +} + +// Done checks if the pagination has completed +func (ChannelPagination) Done(err error) bool { + return err == errPaginationComplete +} + +// Failure checks if pagination failed. +func (t ChannelPagination) Failure(err error) error { + if t.Done(err) { + return nil + } + + return err +} + +func (t ChannelPagination) Next(ctx context.Context) (_ ChannelPagination, err error) { + var ( + resp *channelResponseFull + ) + + if t.c == nil || (t.previousResp != nil && t.previousResp.Cursor == "") { + return t, errPaginationComplete + } + + t.previousResp = t.previousResp.initialize() + + values := url.Values{ + "limit": {strconv.Itoa(t.limit)}, + "exclude_archived": {strconv.FormatBool(t.excludeArchived)}, + "exclude_members": {strconv.FormatBool(t.excludeMembers)}, + "token": {t.c.token}, + "cursor": {t.previousResp.Cursor}, + } + + if resp, err = t.c.channelRequest(ctx, "channels.list", values); err != nil { + return t, err + } + + t.c.Debugf("GetChannelsContext: got %d channels; metadata %v", len(resp.Channels), resp.Metadata) + t.Channels = resp.Channels + t.previousResp = &resp.Metadata + + return t, nil +} + +// GetChannelsPaginated fetches channels in a paginated fashion, see GetChannelsContext for usage. +func (api *Client) GetChannelsPaginated(options ...GetChannelsOption) ChannelPagination { + return newChannelPagination(api, options...) +} + // GetChannels retrieves all the channels // see https://api.slack.com/methods/channels.list func (api *Client) GetChannels(excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) { @@ -274,28 +344,27 @@ func (api *Client) GetChannels(excludeArchived bool, options ...GetChannelsOptio // GetChannelsContext retrieves all the channels with a custom context // see https://api.slack.com/methods/channels.list -func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool, options ...GetChannelsOption) ([]Channel, error) { - config := channelsConfig{ - values: url.Values{ - "token": {api.token}, - }, - } - +func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool, options ...GetChannelsOption) (results []Channel, err error) { if excludeArchived { options = append(options, GetChannelsOptionExcludeArchived()) } - for _, opt := range options { - if err := opt(&config); err != nil { - return nil, err + p := api.GetChannelsPaginated(options...) + for err == nil { + p, err = p.Next(ctx) + if err == nil { + results = append(results, p.Channels...) + } else if rateLimitedError, ok := err.(*RateLimitedError); ok { + select { + case <-ctx.Done(): + err = ctx.Err() + case <-time.After(rateLimitedError.RetryAfter): + err = nil + } } } - response, err := api.channelRequest(ctx, "channels.list", config.values) - if err != nil { - return nil, err - } - return response.Channels, nil + return results, p.Failure(err) } // SetChannelReadMark sets the read mark of a given channel to a specific point diff --git a/vendor/github.com/slack-go/slack/chat.go b/vendor/github.com/slack-go/slack/chat.go index b54f8d6d..1281b15a 100644 --- a/vendor/github.com/slack-go/slack/chat.go +++ b/vendor/github.com/slack-go/slack/chat.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "net/url" + "strconv" "github.com/slack-go/slack/slackutilsx" ) @@ -25,10 +26,11 @@ const ( ) type chatResponseFull struct { - Channel string `json:"channel"` - Timestamp string `json:"ts"` //Regular message timestamp - MessageTimeStamp string `json:"message_ts"` //Ephemeral message timestamp - Text string `json:"text"` + Channel string `json:"channel"` + Timestamp string `json:"ts"` //Regular message timestamp + MessageTimeStamp string `json:"message_ts"` //Ephemeral message timestamp + ScheduledMessageID string `json:"scheduled_message_id,omitempty"` //Scheduled message id + Text string `json:"text"` SlackResponse } @@ -82,13 +84,34 @@ func NewPostMessageParameters() PostMessageParameters { // DeleteMessage deletes a message in a channel func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) { - respChannel, respTimestamp, _, err := api.SendMessageContext(context.Background(), channel, MsgOptionDelete(messageTimestamp)) + respChannel, respTimestamp, _, err := api.SendMessageContext( + context.Background(), + channel, + MsgOptionDelete(messageTimestamp), + ) return respChannel, respTimestamp, err } // DeleteMessageContext deletes a message in a channel with a custom context func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTimestamp string) (string, string, error) { - respChannel, respTimestamp, _, err := api.SendMessageContext(ctx, channel, MsgOptionDelete(messageTimestamp)) + respChannel, respTimestamp, _, err := api.SendMessageContext( + ctx, + channel, + MsgOptionDelete(messageTimestamp), + ) + return respChannel, respTimestamp, err +} + +// ScheduleMessage sends a message to a channel. +// Message is escaped by default according to https://api.slack.com/docs/formatting +// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message. +func (api *Client) ScheduleMessage(channelID, postAt string, options ...MsgOption) (string, string, error) { + respChannel, respTimestamp, _, err := api.SendMessageContext( + context.Background(), + channelID, + MsgOptionSchedule(postAt), + MsgOptionCompose(options...), + ) return respChannel, respTimestamp, err } @@ -132,18 +155,33 @@ func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption) // PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context // For more details, see PostEphemeral documentation func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID string, options ...MsgOption) (timestamp string, err error) { - _, timestamp, _, err = api.SendMessageContext(ctx, channelID, MsgOptionPostEphemeral(userID), MsgOptionCompose(options...)) + _, timestamp, _, err = api.SendMessageContext( + ctx, + channelID, + MsgOptionPostEphemeral(userID), + MsgOptionCompose(options...), + ) return timestamp, err } // UpdateMessage updates a message in a channel func (api *Client) UpdateMessage(channelID, timestamp string, options ...MsgOption) (string, string, string, error) { - return api.SendMessageContext(context.Background(), channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...)) + return api.SendMessageContext( + context.Background(), + channelID, + MsgOptionUpdate(timestamp), + MsgOptionCompose(options...), + ) } // UpdateMessageContext updates a message in a channel func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp string, options ...MsgOption) (string, string, string, error) { - return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...)) + return api.SendMessageContext( + ctx, + channelID, + MsgOptionUpdate(timestamp), + MsgOptionCompose(options...), + ) } // UnfurlMessage unfurls a message in a channel @@ -212,13 +250,14 @@ func buildSender(apiurl string, options ...MsgOption) sendConfig { type sendMode string const ( - chatUpdate sendMode = "chat.update" - chatPostMessage sendMode = "chat.postMessage" - chatDelete sendMode = "chat.delete" - chatPostEphemeral sendMode = "chat.postEphemeral" - chatResponse sendMode = "chat.responseURL" - chatMeMessage sendMode = "chat.meMessage" - chatUnfurl sendMode = "chat.unfurl" + chatUpdate sendMode = "chat.update" + chatPostMessage sendMode = "chat.postMessage" + chatScheduleMessage sendMode = "chat.scheduleMessage" + chatDelete sendMode = "chat.delete" + chatPostEphemeral sendMode = "chat.postEphemeral" + chatResponse sendMode = "chat.responseURL" + chatMeMessage sendMode = "chat.meMessage" + chatUnfurl sendMode = "chat.unfurl" ) type sendConfig struct { @@ -287,6 +326,15 @@ func (t responseURLSender) BuildRequest() (*http.Request, func(*chatResponseFull // MsgOption option provided when sending a message. type MsgOption func(*sendConfig) error +// MsgOptionSchedule schedules a messages. +func MsgOptionSchedule(postAt string) MsgOption { + return func(config *sendConfig) error { + config.endpoint = config.apiurl + string(chatScheduleMessage) + config.values.Add("post_at", postAt) + return nil + } +} + // MsgOptionPost posts a messages, this is the default. func MsgOptionPost() MsgOption { return func(config *sendConfig) error { @@ -625,3 +673,81 @@ func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkPar } return response.Permalink, response.Err() } + +type GetScheduledMessagesParameters struct { + Channel string + Cursor string + Latest string + Limit int + Oldest string +} + +// GetScheduledMessages returns the list of scheduled messages based on params +func (api *Client) GetScheduledMessages(params *GetScheduledMessagesParameters) (channels []Message, nextCursor string, err error) { + return api.GetScheduledMessagesContext(context.Background(), params) +} + +// GetScheduledMessagesContext returns the list of scheduled messages in a Slack team with a custom context +func (api *Client) GetScheduledMessagesContext(ctx context.Context, params *GetScheduledMessagesParameters) (channels []Message, nextCursor string, err error) { + values := url.Values{ + "token": {api.token}, + } + if params.Channel != "" { + values.Add("channel", params.Channel) + } + if params.Cursor != "" { + values.Add("cursor", params.Cursor) + } + if params.Limit != 0 { + values.Add("limit", strconv.Itoa(params.Limit)) + } + if params.Latest != "" { + values.Add("latest", params.Latest) + } + if params.Oldest != "" { + values.Add("oldest", params.Oldest) + } + response := struct { + Messages []Message `json:"scheduled_messages"` + ResponseMetaData responseMetaData `json:"response_metadata"` + SlackResponse + }{} + + err = api.postMethod(ctx, "chat.scheduledMessages.list", values, &response) + if err != nil { + return nil, "", err + } + + return response.Messages, response.ResponseMetaData.NextCursor, response.Err() +} + +type DeleteScheduledMessageParameters struct { + Channel string + ScheduledMessageID string + AsUser bool +} + +// DeleteScheduledMessage returns the list of scheduled messages based on params +func (api *Client) DeleteScheduledMessage(params *DeleteScheduledMessageParameters) (bool, error) { + return api.DeleteScheduledMessageContext(context.Background(), params) +} + +// DeleteScheduledMessageContext returns the list of scheduled messages in a Slack team with a custom context +func (api *Client) DeleteScheduledMessageContext(ctx context.Context, params *DeleteScheduledMessageParameters) (bool, error) { + values := url.Values{ + "token": {api.token}, + "channel": {params.Channel}, + "scheduled_message_id": {params.ScheduledMessageID}, + "as_user": {strconv.FormatBool(params.AsUser)}, + } + response := struct { + SlackResponse + }{} + + err := api.postMethod(ctx, "chat.deleteScheduledMessage", values, &response) + if err != nil { + return false, err + } + + return response.Ok, response.Err() +} diff --git a/vendor/github.com/slack-go/slack/go.mod b/vendor/github.com/slack-go/slack/go.mod index cbe11808..b57c9b35 100644 --- a/vendor/github.com/slack-go/slack/go.mod +++ b/vendor/github.com/slack-go/slack/go.mod @@ -1,12 +1,11 @@ module github.com/slack-go/slack -go 1.13 - require ( - github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-test/deep v1.0.4 github.com/gorilla/websocket v1.2.0 + github.com/nlopes/slack v0.6.0 github.com/pkg/errors v0.8.0 - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 ) + +go 1.13 diff --git a/vendor/github.com/slack-go/slack/go.sum b/vendor/github.com/slack-go/slack/go.sum index 7a0ae46e..196525e7 100644 --- a/vendor/github.com/slack-go/slack/go.sum +++ b/vendor/github.com/slack-go/slack/go.sum @@ -4,6 +4,8 @@ github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/nlopes/slack v0.6.0 h1:jt0jxVQGhssx1Ib7naAOZEZcGdtIhTzkP0nopK0AsRA= +github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/vendor/github.com/slack-go/slack/info.go b/vendor/github.com/slack-go/slack/info.go index 31f459f1..ec706240 100644 --- a/vendor/github.com/slack-go/slack/info.go +++ b/vendor/github.com/slack-go/slack/info.go @@ -2,108 +2,381 @@ package slack import ( "bytes" + "context" "fmt" + "net/url" "strconv" + "strings" "time" ) -// UserPrefs needs to be implemented +type UserPrefsCarrier struct { + SlackResponse + UserPrefs *UserPrefs `json:"prefs"` +} + +// UserPrefs carries a bunch of user settings including some unknown types type UserPrefs struct { - // "highlight_words":"", - // "user_colors":"", - // "color_names_in_list":true, - // "growls_enabled":true, - // "tz":"Europe\/London", - // "push_dm_alert":true, - // "push_mention_alert":true, - // "push_everything":true, - // "push_idle_wait":2, - // "push_sound":"b2.mp3", - // "push_loud_channels":"", - // "push_mention_channels":"", - // "push_loud_channels_set":"", - // "email_alerts":"instant", - // "email_alerts_sleep_until":0, - // "email_misc":false, - // "email_weekly":true, - // "welcome_message_hidden":false, - // "all_channels_loud":true, - // "loud_channels":"", - // "never_channels":"", - // "loud_channels_set":"", - // "show_member_presence":true, - // "search_sort":"timestamp", - // "expand_inline_imgs":true, - // "expand_internal_inline_imgs":true, - // "expand_snippets":false, - // "posts_formatting_guide":true, - // "seen_welcome_2":true, - // "seen_ssb_prompt":false, - // "search_only_my_channels":false, - // "emoji_mode":"default", - // "has_invited":true, - // "has_uploaded":false, - // "has_created_channel":true, - // "search_exclude_channels":"", - // "messages_theme":"default", - // "webapp_spellcheck":true, - // "no_joined_overlays":false, - // "no_created_overlays":true, - // "dropbox_enabled":false, - // "seen_user_menu_tip_card":true, - // "seen_team_menu_tip_card":true, - // "seen_channel_menu_tip_card":true, - // "seen_message_input_tip_card":true, - // "seen_channels_tip_card":true, - // "seen_domain_invite_reminder":false, - // "seen_member_invite_reminder":false, - // "seen_flexpane_tip_card":true, - // "seen_search_input_tip_card":true, - // "mute_sounds":false, - // "arrow_history":false, - // "tab_ui_return_selects":true, - // "obey_inline_img_limit":true, - // "new_msg_snd":"knock_brush.mp3", - // "collapsible":false, - // "collapsible_by_click":true, - // "require_at":false, - // "mac_ssb_bounce":"", - // "mac_ssb_bullet":true, - // "win_ssb_bullet":true, - // "expand_non_media_attachments":true, - // "show_typing":true, - // "pagekeys_handled":true, - // "last_snippet_type":"", - // "display_real_names_override":0, - // "time24":false, - // "enter_is_special_in_tbt":false, - // "graphic_emoticons":false, - // "convert_emoticons":true, - // "autoplay_chat_sounds":true, - // "ss_emojis":true, - // "sidebar_behavior":"", - // "mark_msgs_read_immediately":true, - // "start_scroll_at_oldest":true, - // "snippet_editor_wrap_long_lines":false, - // "ls_disabled":false, - // "sidebar_theme":"default", - // "sidebar_theme_custom_values":"", - // "f_key_search":false, - // "k_key_omnibox":true, - // "speak_growls":false, - // "mac_speak_voice":"com.apple.speech.synthesis.voice.Alex", - // "mac_speak_speed":250, - // "comma_key_prefs":false, - // "at_channel_suppressed_channels":"", - // "push_at_channel_suppressed_channels":"", - // "prompted_for_email_disabling":false, - // "full_text_extracts":false, - // "no_text_in_notifications":false, - // "muted_channels":"", - // "no_macssb1_banner":false, - // "privacy_policy_seen":true, - // "search_exclude_bots":false, - // "fuzzy_matching":false + UserColors string `json:"user_colors,omitempty"` + ColorNamesInList bool `json:"color_names_in_list,omitempty"` + // Keyboard UnknownType `json:"keyboard"` + EmailAlerts string `json:"email_alerts,omitempty"` + EmailAlertsSleepUntil int `json:"email_alerts_sleep_until,omitempty"` + EmailTips bool `json:"email_tips,omitempty"` + EmailWeekly bool `json:"email_weekly,omitempty"` + EmailOffers bool `json:"email_offers,omitempty"` + EmailResearch bool `json:"email_research,omitempty"` + EmailDeveloper bool `json:"email_developer,omitempty"` + WelcomeMessageHidden bool `json:"welcome_message_hidden,omitempty"` + SearchSort string `json:"search_sort,omitempty"` + SearchFileSort string `json:"search_file_sort,omitempty"` + SearchChannelSort string `json:"search_channel_sort,omitempty"` + SearchPeopleSort string `json:"search_people_sort,omitempty"` + ExpandInlineImages bool `json:"expand_inline_images,omitempty"` + ExpandInternalInlineImages bool `json:"expand_internal_inline_images,omitempty"` + ExpandSnippets bool `json:"expand_snippets,omitempty"` + PostsFormattingGuide bool `json:"posts_formatting_guide,omitempty"` + SeenWelcome2 bool `json:"seen_welcome_2,omitempty"` + SeenSSBPrompt bool `json:"seen_ssb_prompt,omitempty"` + SpacesNewXpBannerDismissed bool `json:"spaces_new_xp_banner_dismissed,omitempty"` + SearchOnlyMyChannels bool `json:"search_only_my_channels,omitempty"` + SearchOnlyCurrentTeam bool `json:"search_only_current_team,omitempty"` + SearchHideMyChannels bool `json:"search_hide_my_channels,omitempty"` + SearchOnlyShowOnline bool `json:"search_only_show_online,omitempty"` + SearchHideDeactivatedUsers bool `json:"search_hide_deactivated_users,omitempty"` + EmojiMode string `json:"emoji_mode,omitempty"` + EmojiUse string `json:"emoji_use,omitempty"` + HasInvited bool `json:"has_invited,omitempty"` + HasUploaded bool `json:"has_uploaded,omitempty"` + HasCreatedChannel bool `json:"has_created_channel,omitempty"` + HasSearched bool `json:"has_searched,omitempty"` + SearchExcludeChannels string `json:"search_exclude_channels,omitempty"` + MessagesTheme string `json:"messages_theme,omitempty"` + WebappSpellcheck bool `json:"webapp_spellcheck,omitempty"` + NoJoinedOverlays bool `json:"no_joined_overlays,omitempty"` + NoCreatedOverlays bool `json:"no_created_overlays,omitempty"` + DropboxEnabled bool `json:"dropbox_enabled,omitempty"` + SeenDomainInviteReminder bool `json:"seen_domain_invite_reminder,omitempty"` + SeenMemberInviteReminder bool `json:"seen_member_invite_reminder,omitempty"` + MuteSounds bool `json:"mute_sounds,omitempty"` + ArrowHistory bool `json:"arrow_history,omitempty"` + TabUIReturnSelects bool `json:"tab_ui_return_selects,omitempty"` + ObeyInlineImgLimit bool `json:"obey_inline_img_limit,omitempty"` + RequireAt bool `json:"require_at,omitempty"` + SsbSpaceWindow string `json:"ssb_space_window,omitempty"` + MacSsbBounce string `json:"mac_ssb_bounce,omitempty"` + MacSsbBullet bool `json:"mac_ssb_bullet,omitempty"` + ExpandNonMediaAttachments bool `json:"expand_non_media_attachments,omitempty"` + ShowTyping bool `json:"show_typing,omitempty"` + PagekeysHandled bool `json:"pagekeys_handled,omitempty"` + LastSnippetType string `json:"last_snippet_type,omitempty"` + DisplayRealNamesOverride int `json:"display_real_names_override,omitempty"` + DisplayDisplayNames bool `json:"display_display_names,omitempty"` + Time24 bool `json:"time24,omitempty"` + EnterIsSpecialInTbt bool `json:"enter_is_special_in_tbt,omitempty"` + MsgInputSendBtn bool `json:"msg_input_send_btn,omitempty"` + MsgInputSendBtnAutoSet bool `json:"msg_input_send_btn_auto_set,omitempty"` + MsgInputStickyComposer bool `json:"msg_input_sticky_composer,omitempty"` + GraphicEmoticons bool `json:"graphic_emoticons,omitempty"` + ConvertEmoticons bool `json:"convert_emoticons,omitempty"` + SsEmojis bool `json:"ss_emojis,omitempty"` + SeenOnboardingStart bool `json:"seen_onboarding_start,omitempty"` + OnboardingCancelled bool `json:"onboarding_cancelled,omitempty"` + SeenOnboardingSlackbotConversation bool `json:"seen_onboarding_slackbot_conversation,omitempty"` + SeenOnboardingChannels bool `json:"seen_onboarding_channels,omitempty"` + SeenOnboardingDirectMessages bool `json:"seen_onboarding_direct_messages,omitempty"` + SeenOnboardingInvites bool `json:"seen_onboarding_invites,omitempty"` + SeenOnboardingSearch bool `json:"seen_onboarding_search,omitempty"` + SeenOnboardingRecentMentions bool `json:"seen_onboarding_recent_mentions,omitempty"` + SeenOnboardingStarredItems bool `json:"seen_onboarding_starred_items,omitempty"` + SeenOnboardingPrivateGroups bool `json:"seen_onboarding_private_groups,omitempty"` + SeenOnboardingBanner bool `json:"seen_onboarding_banner,omitempty"` + OnboardingSlackbotConversationStep int `json:"onboarding_slackbot_conversation_step,omitempty"` + SetTzAutomatically bool `json:"set_tz_automatically,omitempty"` + SuppressLinkWarning bool `json:"suppress_link_warning,omitempty"` + DndEnabled bool `json:"dnd_enabled,omitempty"` + DndStartHour string `json:"dnd_start_hour,omitempty"` + DndEndHour string `json:"dnd_end_hour,omitempty"` + DndBeforeMonday string `json:"dnd_before_monday,omitempty"` + DndAfterMonday string `json:"dnd_after_monday,omitempty"` + DndEnabledMonday string `json:"dnd_enabled_monday,omitempty"` + DndBeforeTuesday string `json:"dnd_before_tuesday,omitempty"` + DndAfterTuesday string `json:"dnd_after_tuesday,omitempty"` + DndEnabledTuesday string `json:"dnd_enabled_tuesday,omitempty"` + DndBeforeWednesday string `json:"dnd_before_wednesday,omitempty"` + DndAfterWednesday string `json:"dnd_after_wednesday,omitempty"` + DndEnabledWednesday string `json:"dnd_enabled_wednesday,omitempty"` + DndBeforeThursday string `json:"dnd_before_thursday,omitempty"` + DndAfterThursday string `json:"dnd_after_thursday,omitempty"` + DndEnabledThursday string `json:"dnd_enabled_thursday,omitempty"` + DndBeforeFriday string `json:"dnd_before_friday,omitempty"` + DndAfterFriday string `json:"dnd_after_friday,omitempty"` + DndEnabledFriday string `json:"dnd_enabled_friday,omitempty"` + DndBeforeSaturday string `json:"dnd_before_saturday,omitempty"` + DndAfterSaturday string `json:"dnd_after_saturday,omitempty"` + DndEnabledSaturday string `json:"dnd_enabled_saturday,omitempty"` + DndBeforeSunday string `json:"dnd_before_sunday,omitempty"` + DndAfterSunday string `json:"dnd_after_sunday,omitempty"` + DndEnabledSunday string `json:"dnd_enabled_sunday,omitempty"` + DndDays string `json:"dnd_days,omitempty"` + DndCustomNewBadgeSeen bool `json:"dnd_custom_new_badge_seen,omitempty"` + DndNotificationScheduleNewBadgeSeen bool `json:"dnd_notification_schedule_new_badge_seen,omitempty"` + // UnreadCollapsedChannels unknownType `json:"unread_collapsed_channels,omitempty"` + SidebarBehavior string `json:"sidebar_behavior,omitempty"` + ChannelSort string `json:"channel_sort,omitempty"` + SeparatePrivateChannels bool `json:"separate_private_channels,omitempty"` + SeparateSharedChannels bool `json:"separate_shared_channels,omitempty"` + SidebarTheme string `json:"sidebar_theme,omitempty"` + SidebarThemeCustomValues string `json:"sidebar_theme_custom_values,omitempty"` + NoInvitesWidgetInSidebar bool `json:"no_invites_widget_in_sidebar,omitempty"` + NoOmniboxInChannels bool `json:"no_omnibox_in_channels,omitempty"` + + KKeyOmniboxAutoHideCount int `json:"k_key_omnibox_auto_hide_count,omitempty"` + ShowSidebarQuickswitcherButton bool `json:"show_sidebar_quickswitcher_button,omitempty"` + EntOrgWideChannelsSidebar bool `json:"ent_org_wide_channels_sidebar,omitempty"` + MarkMsgsReadImmediately bool `json:"mark_msgs_read_immediately,omitempty"` + StartScrollAtOldest bool `json:"start_scroll_at_oldest,omitempty"` + SnippetEditorWrapLongLines bool `json:"snippet_editor_wrap_long_lines,omitempty"` + LsDisabled bool `json:"ls_disabled,omitempty"` + FKeySearch bool `json:"f_key_search,omitempty"` + KKeyOmnibox bool `json:"k_key_omnibox,omitempty"` + PromptedForEmailDisabling bool `json:"prompted_for_email_disabling,omitempty"` + NoMacelectronBanner bool `json:"no_macelectron_banner,omitempty"` + NoMacssb1Banner bool `json:"no_macssb1_banner,omitempty"` + NoMacssb2Banner bool `json:"no_macssb2_banner,omitempty"` + NoWinssb1Banner bool `json:"no_winssb1_banner,omitempty"` + HideUserGroupInfoPane bool `json:"hide_user_group_info_pane,omitempty"` + MentionsExcludeAtUserGroups bool `json:"mentions_exclude_at_user_groups,omitempty"` + MentionsExcludeReactions bool `json:"mentions_exclude_reactions,omitempty"` + PrivacyPolicySeen bool `json:"privacy_policy_seen,omitempty"` + EnterpriseMigrationSeen bool `json:"enterprise_migration_seen,omitempty"` + LastTosAcknowledged string `json:"last_tos_acknowledged,omitempty"` + SearchExcludeBots bool `json:"search_exclude_bots,omitempty"` + LoadLato2 bool `json:"load_lato_2,omitempty"` + FullerTimestamps bool `json:"fuller_timestamps,omitempty"` + LastSeenAtChannelWarning int `json:"last_seen_at_channel_warning,omitempty"` + EmojiAutocompleteBig bool `json:"emoji_autocomplete_big,omitempty"` + TwoFactorAuthEnabled bool `json:"two_factor_auth_enabled,omitempty"` + // TwoFactorType unknownType `json:"two_factor_type,omitempty"` + // TwoFactorBackupType unknownType `json:"two_factor_backup_type,omitempty"` + HideHexSwatch bool `json:"hide_hex_swatch,omitempty"` + ShowJumperScores bool `json:"show_jumper_scores,omitempty"` + EnterpriseMdmCustomMsg string `json:"enterprise_mdm_custom_msg,omitempty"` + // EnterpriseExcludedAppTeams unknownType `json:"enterprise_excluded_app_teams,omitempty"` + ClientLogsPri string `json:"client_logs_pri,omitempty"` + FlannelServerPool string `json:"flannel_server_pool,omitempty"` + MentionsExcludeAtChannels bool `json:"mentions_exclude_at_channels,omitempty"` + ConfirmClearAllUnreads bool `json:"confirm_clear_all_unreads,omitempty"` + ConfirmUserMarkedAway bool `json:"confirm_user_marked_away,omitempty"` + BoxEnabled bool `json:"box_enabled,omitempty"` + SeenSingleEmojiMsg bool `json:"seen_single_emoji_msg,omitempty"` + ConfirmShCallStart bool `json:"confirm_sh_call_start,omitempty"` + PreferredSkinTone string `json:"preferred_skin_tone,omitempty"` + ShowAllSkinTones bool `json:"show_all_skin_tones,omitempty"` + WhatsNewRead int `json:"whats_new_read,omitempty"` + // FrecencyJumper unknownType `json:"frecency_jumper,omitempty"` + FrecencyEntJumper string `json:"frecency_ent_jumper,omitempty"` + FrecencyEntJumperBackup string `json:"frecency_ent_jumper_backup,omitempty"` + Jumbomoji bool `json:"jumbomoji,omitempty"` + NewxpSeenLastMessage int `json:"newxp_seen_last_message,omitempty"` + ShowMemoryInstrument bool `json:"show_memory_instrument,omitempty"` + EnableUnreadView bool `json:"enable_unread_view,omitempty"` + SeenUnreadViewCoachmark bool `json:"seen_unread_view_coachmark,omitempty"` + EnableReactEmojiPicker bool `json:"enable_react_emoji_picker,omitempty"` + SeenCustomStatusBadge bool `json:"seen_custom_status_badge,omitempty"` + SeenCustomStatusCallout bool `json:"seen_custom_status_callout,omitempty"` + SeenCustomStatusExpirationBadge bool `json:"seen_custom_status_expiration_badge,omitempty"` + UsedCustomStatusKbShortcut bool `json:"used_custom_status_kb_shortcut,omitempty"` + SeenGuestAdminSlackbotAnnouncement bool `json:"seen_guest_admin_slackbot_announcement,omitempty"` + SeenThreadsNotificationBanner bool `json:"seen_threads_notification_banner,omitempty"` + SeenNameTaggingCoachmark bool `json:"seen_name_tagging_coachmark,omitempty"` + AllUnreadsSortOrder string `json:"all_unreads_sort_order,omitempty"` + Locale string `json:"locale,omitempty"` + SeenIntlChannelNamesCoachmark bool `json:"seen_intl_channel_names_coachmark,omitempty"` + SeenP2LocaleChangeMessage int `json:"seen_p2_locale_change_message,omitempty"` + SeenLocaleChangeMessage int `json:"seen_locale_change_message,omitempty"` + SeenJapaneseLocaleChangeMessage bool `json:"seen_japanese_locale_change_message,omitempty"` + SeenSharedChannelsCoachmark bool `json:"seen_shared_channels_coachmark,omitempty"` + SeenSharedChannelsOptInChangeMessage bool `json:"seen_shared_channels_opt_in_change_message,omitempty"` + HasRecentlySharedaChannel bool `json:"has_recently_shared_a_channel,omitempty"` + SeenChannelBrowserAdminCoachmark bool `json:"seen_channel_browser_admin_coachmark,omitempty"` + SeenAdministrationMenu bool `json:"seen_administration_menu,omitempty"` + SeenDraftsSectionCoachmark bool `json:"seen_drafts_section_coachmark,omitempty"` + SeenEmojiUpdateOverlayCoachmark bool `json:"seen_emoji_update_overlay_coachmark,omitempty"` + SeenSonicDeluxeToast int `json:"seen_sonic_deluxe_toast,omitempty"` + SeenWysiwygDeluxeToast bool `json:"seen_wysiwyg_deluxe_toast,omitempty"` + SeenMarkdownPasteToast int `json:"seen_markdown_paste_toast,omitempty"` + SeenMarkdownPasteShortcut int `json:"seen_markdown_paste_shortcut,omitempty"` + SeenIaEducation bool `json:"seen_ia_education,omitempty"` + PlainTextMode bool `json:"plain_text_mode,omitempty"` + ShowSharedChannelsEducationBanner bool `json:"show_shared_channels_education_banner,omitempty"` + AllowCallsToSetCurrentStatus bool `json:"allow_calls_to_set_current_status,omitempty"` + InInteractiveMasMigrationFlow bool `json:"in_interactive_mas_migration_flow,omitempty"` + SunsetInteractiveMessageViews int `json:"sunset_interactive_message_views,omitempty"` + ShdepPromoCodeSubmitted bool `json:"shdep_promo_code_submitted,omitempty"` + SeenShdepSlackbotMessage bool `json:"seen_shdep_slackbot_message,omitempty"` + SeenCallsInteractiveCoachmark bool `json:"seen_calls_interactive_coachmark,omitempty"` + AllowCmdTabIss bool `json:"allow_cmd_tab_iss,omitempty"` + SeenWorkflowBuilderDeluxeToast bool `json:"seen_workflow_builder_deluxe_toast,omitempty"` + WorkflowBuilderIntroModalClickedThrough bool `json:"workflow_builder_intro_modal_clicked_through,omitempty"` + // WorkflowBuilderCoachmarks unknownType `json:"workflow_builder_coachmarks,omitempty"` + SeenGdriveCoachmark bool `json:"seen_gdrive_coachmark,omitempty"` + OverloadedMessageEnabled bool `json:"overloaded_message_enabled,omitempty"` + SeenHighlightsCoachmark bool `json:"seen_highlights_coachmark,omitempty"` + SeenHighlightsArrowsCoachmark bool `json:"seen_highlights_arrows_coachmark,omitempty"` + SeenHighlightsWarmWelcome bool `json:"seen_highlights_warm_welcome,omitempty"` + SeenNewSearchUi bool `json:"seen_new_search_ui,omitempty"` + SeenChannelSearch bool `json:"seen_channel_search,omitempty"` + SeenPeopleSearch bool `json:"seen_people_search,omitempty"` + SeenPeopleSearchCount int `json:"seen_people_search_count,omitempty"` + DismissedScrollSearchTooltipCount int `json:"dismissed_scroll_search_tooltip_count,omitempty"` + LastDismissedScrollSearchTooltipTimestamp int `json:"last_dismissed_scroll_search_tooltip_timestamp,omitempty"` + HasUsedQuickswitcherShortcut bool `json:"has_used_quickswitcher_shortcut,omitempty"` + SeenQuickswitcherShortcutTipCount int `json:"seen_quickswitcher_shortcut_tip_count,omitempty"` + BrowsersDismissedChannelsLowResultsEducation bool `json:"browsers_dismissed_channels_low_results_education,omitempty"` + BrowsersSeenInitialChannelsEducation bool `json:"browsers_seen_initial_channels_education,omitempty"` + BrowsersDismissedPeopleLowResultsEducation bool `json:"browsers_dismissed_people_low_results_education,omitempty"` + BrowsersSeenInitialPeopleEducation bool `json:"browsers_seen_initial_people_education,omitempty"` + BrowsersDismissedUserGroupsLowResultsEducation bool `json:"browsers_dismissed_user_groups_low_results_education,omitempty"` + BrowsersSeenInitialUserGroupsEducation bool `json:"browsers_seen_initial_user_groups_education,omitempty"` + BrowsersDismissedFilesLowResultsEducation bool `json:"browsers_dismissed_files_low_results_education,omitempty"` + BrowsersSeenInitialFilesEducation bool `json:"browsers_seen_initial_files_education,omitempty"` + A11yAnimations bool `json:"a11y_animations,omitempty"` + SeenKeyboardShortcutsCoachmark bool `json:"seen_keyboard_shortcuts_coachmark,omitempty"` + NeedsInitialPasswordSet bool `json:"needs_initial_password_set,omitempty"` + LessonsEnabled bool `json:"lessons_enabled,omitempty"` + TractorEnabled bool `json:"tractor_enabled,omitempty"` + TractorExperimentGroup string `json:"tractor_experiment_group,omitempty"` + OpenedSlackbotDm bool `json:"opened_slackbot_dm,omitempty"` + NewxpSuggestedChannels string `json:"newxp_suggested_channels,omitempty"` + OnboardingComplete bool `json:"onboarding_complete,omitempty"` + WelcomePlaceState string `json:"welcome_place_state,omitempty"` + // OnboardingRoleApps unknownType `json:"onboarding_role_apps,omitempty"` + HasReceivedThreadedMessage bool `json:"has_received_threaded_message,omitempty"` + SendYourFirstMessageBannerEnabled bool `json:"send_your_first_message_banner_enabled,omitempty"` + WhocanseethisDmMpdmBadge bool `json:"whocanseethis_dm_mpdm_badge,omitempty"` + HighlightWords string `json:"highlight_words,omitempty"` + ThreadsEverything bool `json:"threads_everything,omitempty"` + NoTextInNotifications bool `json:"no_text_in_notifications,omitempty"` + PushShowPreview bool `json:"push_show_preview,omitempty"` + GrowlsEnabled bool `json:"growls_enabled,omitempty"` + AllChannelsLoud bool `json:"all_channels_loud,omitempty"` + PushDmAlert bool `json:"push_dm_alert,omitempty"` + PushMentionAlert bool `json:"push_mention_alert,omitempty"` + PushEverything bool `json:"push_everything,omitempty"` + PushIdleWait int `json:"push_idle_wait,omitempty"` + PushSound string `json:"push_sound,omitempty"` + NewMsgSnd string `json:"new_msg_snd,omitempty"` + PushLoudChannels string `json:"push_loud_channels,omitempty"` + PushMentionChannels string `json:"push_mention_channels,omitempty"` + PushLoudChannelsSet string `json:"push_loud_channels_set,omitempty"` + LoudChannels string `json:"loud_channels,omitempty"` + NeverChannels string `json:"never_channels,omitempty"` + LoudChannelsSet string `json:"loud_channels_set,omitempty"` + AtChannelSuppressedChannels string `json:"at_channel_suppressed_channels,omitempty"` + PushAtChannelSuppressedChannels string `json:"push_at_channel_suppressed_channels,omitempty"` + MutedChannels string `json:"muted_channels,omitempty"` + // AllNotificationsPrefs unknownType `json:"all_notifications_prefs,omitempty"` + GrowthMsgLimitApproachingCtaCount int `json:"growth_msg_limit_approaching_cta_count,omitempty"` + GrowthMsgLimitApproachingCtaTs int `json:"growth_msg_limit_approaching_cta_ts,omitempty"` + GrowthMsgLimitReachedCtaCount int `json:"growth_msg_limit_reached_cta_count,omitempty"` + GrowthMsgLimitReachedCtaLastTs int `json:"growth_msg_limit_reached_cta_last_ts,omitempty"` + GrowthMsgLimitLongReachedCtaCount int `json:"growth_msg_limit_long_reached_cta_count,omitempty"` + GrowthMsgLimitLongReachedCtaLastTs int `json:"growth_msg_limit_long_reached_cta_last_ts,omitempty"` + GrowthMsgLimitSixtyDayBannerCtaCount int `json:"growth_msg_limit_sixty_day_banner_cta_count,omitempty"` + GrowthMsgLimitSixtyDayBannerCtaLastTs int `json:"growth_msg_limit_sixty_day_banner_cta_last_ts,omitempty"` + // GrowthAllBannersPrefs unknownType `json:"growth_all_banners_prefs,omitempty"` + AnalyticsUpsellCoachmarkSeen bool `json:"analytics_upsell_coachmark_seen,omitempty"` + SeenAppSpaceCoachmark bool `json:"seen_app_space_coachmark,omitempty"` + SeenAppSpaceTutorial bool `json:"seen_app_space_tutorial,omitempty"` + DismissedAppLauncherWelcome bool `json:"dismissed_app_launcher_welcome,omitempty"` + DismissedAppLauncherLimit bool `json:"dismissed_app_launcher_limit,omitempty"` + Purchaser bool `json:"purchaser,omitempty"` + ShowEntOnboarding bool `json:"show_ent_onboarding,omitempty"` + FoldersEnabled bool `json:"folders_enabled,omitempty"` + // FolderData unknownType `json:"folder_data,omitempty"` + SeenCorporateExportAlert bool `json:"seen_corporate_export_alert,omitempty"` + ShowAutocompleteHelp int `json:"show_autocomplete_help,omitempty"` + DeprecationToastLastSeen int `json:"deprecation_toast_last_seen,omitempty"` + DeprecationModalLastSeen int `json:"deprecation_modal_last_seen,omitempty"` + Iap1Lab int `json:"iap1_lab,omitempty"` + IaTopNavTheme string `json:"ia_top_nav_theme,omitempty"` + IaPlatformActionsLab int `json:"ia_platform_actions_lab,omitempty"` + ActivityView string `json:"activity_view,omitempty"` + FailoverProxyCheckCompleted int `json:"failover_proxy_check_completed,omitempty"` + EdgeUploadProxyCheckCompleted int `json:"edge_upload_proxy_check_completed,omitempty"` + AppSubdomainCheckCompleted int `json:"app_subdomain_check_completed,omitempty"` + AddAppsPromptDismissed bool `json:"add_apps_prompt_dismissed,omitempty"` + AddChannelPromptDismissed bool `json:"add_channel_prompt_dismissed,omitempty"` + ChannelSidebarHideInvite bool `json:"channel_sidebar_hide_invite,omitempty"` + InProdSurveysEnabled bool `json:"in_prod_surveys_enabled,omitempty"` + DismissedInstalledAppDmSuggestions string `json:"dismissed_installed_app_dm_suggestions,omitempty"` + SeenContextualMessageShortcutsModal bool `json:"seen_contextual_message_shortcuts_modal,omitempty"` + SeenMessageNavigationEducationalToast bool `json:"seen_message_navigation_educational_toast,omitempty"` + ContextualMessageShortcutsModalWasSeen bool `json:"contextual_message_shortcuts_modal_was_seen,omitempty"` + MessageNavigationToastWasSeen bool `json:"message_navigation_toast_was_seen,omitempty"` + UpToBrowseKbShortcut bool `json:"up_to_browse_kb_shortcut,omitempty"` + ChannelSections string `json:"channel_sections,omitempty"` + TZ string `json:"tz,omitempty"` +} + +func (api *Client) GetUserPrefs() (*UserPrefsCarrier, error) { + values := url.Values{"token": {api.token}} + response := UserPrefsCarrier{} + + err := api.getMethod(context.Background(), "users.prefs.get", values, &response) + if err != nil { + return nil, err + } + + return &response, response.Err() +} + +func (api *Client) MuteChat(channelID string) (*UserPrefsCarrier, error) { + prefs, err := api.GetUserPrefs() + if err != nil { + return nil, err + } + chnls := strings.Split(prefs.UserPrefs.MutedChannels, ",") + for _, chn := range chnls { + if chn == channelID { + return nil, nil // noop + } + } + newChnls := prefs.UserPrefs.MutedChannels + "," + channelID + values := url.Values{"token": {api.token}, "muted_channels": {newChnls}, "reason": {"update-muted-channels"}} + response := UserPrefsCarrier{} + + err = api.postMethod(context.Background(), "users.prefs.set", values, &response) + if err != nil { + return nil, err + } + + return &response, response.Err() +} + +func (api *Client) UnMuteChat(channelID string) (*UserPrefsCarrier, error) { + prefs, err := api.GetUserPrefs() + if err != nil { + return nil, err + } + chnls := strings.Split(prefs.UserPrefs.MutedChannels, ",") + newChnls := make([]string, len(chnls)-1) + for i, chn := range chnls { + if chn == channelID { + return nil, nil // noop + } + newChnls[i] = chn + } + values := url.Values{"token": {api.token}, "muted_channels": {strings.Join(newChnls, ",")}, "reason": {"update-muted-channels"}} + response := UserPrefsCarrier{} + + err = api.postMethod(context.Background(), "users.prefs.set", values, &response) + if err != nil { + return nil, err + } + + return &response, response.Err() } // UserDetails contains user details coming in the initial response from StartRTM diff --git a/vendor/github.com/slack-go/slack/interactions.go b/vendor/github.com/slack-go/slack/interactions.go index 26a8b6db..c56a34fe 100644 --- a/vendor/github.com/slack-go/slack/interactions.go +++ b/vendor/github.com/slack-go/slack/interactions.go @@ -24,6 +24,9 @@ const ( InteractionTypeInteractionMessage = InteractionType("interactive_message") InteractionTypeMessageAction = InteractionType("message_action") InteractionTypeBlockActions = InteractionType("block_actions") + InteractionTypeBlockSuggestion = InteractionType("block_suggestion") + InteractionTypeViewSubmission = InteractionType("view_submission") + InteractionTypeViewClosed = InteractionType("view_closed") ) // InteractionCallback is sent from slack when a user interactions with a button or dialog. @@ -44,8 +47,19 @@ type InteractionCallback struct { MessageTs string `json:"message_ts"` AttachmentID string `json:"attachment_id"` ActionCallback ActionCallbacks `json:"actions"` + View View `json:"view"` + ActionID string `json:"action_id"` APIAppID string `json:"api_app_id"` + BlockID string `json:"block_id"` + Container Container `json:"container"` DialogSubmissionCallback + ViewSubmissionCallback + ViewClosedCallback +} + +type Container struct { + Type string `json:"type"` + ViewID string `json:"view_id"` } // ActionCallback is a convenience struct defined to allow dynamic unmarshalling of diff --git a/vendor/github.com/slack-go/slack/oauth.go b/vendor/github.com/slack-go/slack/oauth.go index 29d6dce9..64118fb0 100644 --- a/vendor/github.com/slack-go/slack/oauth.go +++ b/vendor/github.com/slack-go/slack/oauth.go @@ -31,6 +31,40 @@ type OAuthResponse struct { SlackResponse } +// OAuthV2Response ... +type OAuthV2Response struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + BotUserID string `json:"bot_user_id"` + AppID string `json:"app_id"` + TeamID string `json:"team_id"` + Team OAuthV2ResponseTeam `json:"team"` + Enterprise OAuthV2ResponseEnterprise `json:"enterprise"` + AuthedUser OAuthV2ResponseAuthedUser `json:"authed_user"` + SlackResponse +} + +// OAuthV2ResponseTeam ... +type OAuthV2ResponseTeam struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// OAuthV2ResponseEnterprise ... +type OAuthV2ResponseEnterprise struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// OAuthV2ResponseAuthedUser ... +type OAuthV2ResponseAuthedUser struct { + ID string `json:"id"` + Scope string `json:"scope"` + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` +} + // GetOAuthToken retrieves an AccessToken func GetOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) { return GetOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI) @@ -62,3 +96,23 @@ func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, c } return response, response.Err() } + +// GetOAuthV2Response gets a V2 OAuth access token response - https://api.slack.com/methods/oauth.v2.access +func GetOAuthV2Response(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthV2Response, err error) { + return GetOAuthV2ResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI) +} + +// GetOAuthV2ResponseContext with a context, gets a V2 OAuth access token response +func GetOAuthV2ResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthV2Response, err error) { + values := url.Values{ + "client_id": {clientID}, + "client_secret": {clientSecret}, + "code": {code}, + "redirect_uri": {redirectURI}, + } + response := &OAuthV2Response{} + if err = postForm(ctx, client, APIURL+"oauth.v2.access", values, response, discard{}); err != nil { + return nil, err + } + return response, response.Err() +} diff --git a/vendor/github.com/slack-go/slack/views.go b/vendor/github.com/slack-go/slack/views.go index afe391bf..c34feece 100644 --- a/vendor/github.com/slack-go/slack/views.go +++ b/vendor/github.com/slack-go/slack/views.go @@ -12,6 +12,10 @@ const ( type ViewType string +type ViewState struct { + Values map[string]map[string]BlockAction `json:"values"` +} + type View struct { SlackResponse ID string `json:"id"` @@ -23,7 +27,7 @@ type View struct { Blocks Blocks `json:"blocks"` PrivateMetadata string `json:"private_metadata"` CallbackID string `json:"callback_id"` - State interface{} `json:"state"` + State *ViewState `json:"state"` Hash string `json:"hash"` ClearOnClose bool `json:"clear_on_close"` NotifyOnClose bool `json:"notify_on_close"` @@ -34,17 +38,67 @@ type View struct { BotID string `json:"bot_id"` } +type ViewSubmissionCallback struct { + Hash string `json:"hash"` +} + +type ViewClosedCallback struct { + IsCleared bool `json:"is_cleared"` +} + +const ( + RAClear ViewResponseAction = "clear" + RAUpdate ViewResponseAction = "update" + RAPush ViewResponseAction = "push" + RAErrors ViewResponseAction = "errors" +) + +type ViewResponseAction string + +type ViewSubmissionResponse struct { + ResponseAction ViewResponseAction `json:"response_action"` + View *ModalViewRequest `json:"view,omitempty"` + Errors map[string]string `json:"errors,omitempty"` +} + +func NewClearViewSubmissionResponse() *ViewSubmissionResponse { + return &ViewSubmissionResponse{ + ResponseAction: RAClear, + } +} + +func NewUpdateViewSubmissionResponse(view *ModalViewRequest) *ViewSubmissionResponse { + return &ViewSubmissionResponse{ + ResponseAction: RAUpdate, + View: view, + } +} + +func NewPushViewSubmissionResponse(view *ModalViewRequest) *ViewSubmissionResponse { + return &ViewSubmissionResponse{ + ResponseAction: RAPush, + View: view, + } +} + +func NewErrorsViewSubmissionResponse(errors map[string]string) *ViewSubmissionResponse { + return &ViewSubmissionResponse{ + ResponseAction: RAErrors, + Errors: errors, + } +} + type ModalViewRequest struct { Type ViewType `json:"type"` Title *TextBlockObject `json:"title"` Blocks Blocks `json:"blocks"` - Close *TextBlockObject `json:"close"` - Submit *TextBlockObject `json:"submit"` - PrivateMetadata string `json:"private_metadata"` - CallbackID string `json:"callback_id"` - ClearOnClose bool `json:"clear_on_close"` - NotifyOnClose bool `json:"notify_on_close"` - ExternalID string `json:"external_id"` + Close *TextBlockObject `json:"close,omitempty"` + Submit *TextBlockObject `json:"submit,omitempty"` + PrivateMetadata string `json:"private_metadata,omitempty"` + CallbackID string `json:"callback_id,omitempty"` + ClearOnClose bool `json:"clear_on_close,omitempty"` + NotifyOnClose bool `json:"notify_on_close,omitempty"` + ExternalID string `json:"external_id,omitempty"` } func (v *ModalViewRequest) ViewType() ViewType { @@ -54,9 +108,9 @@ func (v *ModalViewRequest) ViewType() ViewType { type HomeTabViewRequest struct { Type ViewType `json:"type"` Blocks Blocks `json:"blocks"` - PrivateMetadata string `json:"private_metadata"` - CallbackID string `json:"callback_id"` - ExternalID string `json:"external_id"` + PrivateMetadata string `json:"private_metadata,omitempty"` + CallbackID string `json:"callback_id,omitempty"` + ExternalID string `json:"external_id,omitempty"` } func (v *HomeTabViewRequest) ViewType() ViewType { @@ -71,7 +125,7 @@ type openViewRequest struct { type publishViewRequest struct { UserID string `json:"user_id"` View HomeTabViewRequest `json:"view"` - Hash string `json:"hash"` + Hash string `json:"hash,omitempty"` } type pushViewRequest struct { @@ -81,9 +135,9 @@ type pushViewRequest struct { type updateViewRequest struct { View ModalViewRequest `json:"view"` - ExternalID string `json:"external_id"` - Hash string `json:"hash"` - ViewID string `json:"view_id"` + ExternalID string `json:"external_id,omitempty"` + Hash string `json:"hash,omitempty"` + ViewID string `json:"view_id,omitempty"` } type ViewResponse struct { diff --git a/vendor/github.com/slack-go/slack/websocket_channels.go b/vendor/github.com/slack-go/slack/websocket_channels.go index 7dd3319b..fe274fd7 100644 --- a/vendor/github.com/slack-go/slack/websocket_channels.go +++ b/vendor/github.com/slack-go/slack/websocket_channels.go @@ -45,7 +45,7 @@ type ChannelRenameEvent struct { type ChannelRenameInfo struct { ID string `json:"id"` Name string `json:"name"` - Created string `json:"created"` + Created int `json:"created"` } // ChannelHistoryChangedEvent represents the Channel history changed event diff --git a/vendor/github.com/slack-go/slack/websocket_managed_conn.go b/vendor/github.com/slack-go/slack/websocket_managed_conn.go index 6395938c..a8844f82 100644 --- a/vendor/github.com/slack-go/slack/websocket_managed_conn.go +++ b/vendor/github.com/slack-go/slack/websocket_managed_conn.go @@ -577,5 +577,6 @@ var EventMapping = map[string]interface{}{ "subteam_self_removed": SubteamSelfRemovedEvent{}, "subteam_updated": SubteamUpdatedEvent{}, - "desktop_notification": DesktopNotificationEvent{}, + "desktop_notification": DesktopNotificationEvent{}, + "mobile_in_app_notification": MobileInAppNotificationEvent{}, } diff --git a/vendor/github.com/slack-go/slack/websocket_mobile_in_app_notification.go b/vendor/github.com/slack-go/slack/websocket_mobile_in_app_notification.go new file mode 100644 index 00000000..e3cfb3d9 --- /dev/null +++ b/vendor/github.com/slack-go/slack/websocket_mobile_in_app_notification.go @@ -0,0 +1,20 @@ +package slack + +// MobileInAppNotificationEvent represents the update event for Mobile App Notification. +type MobileInAppNotificationEvent struct { + Type string `json:"type"` + Title string `json:"title"` + Subtitle string `json:"subtitle"` + Timestamp string `json:"ts"` + Channel string `json:"channel"` + AvatarImage string `json:"avatarImage"` + IsShared bool `json:"is_shared"` + ChannelName string `json:"channel_name"` + AuthorID string `json:"author_id"` + AuthorDisplayName string `json:"author_display_name"` + MessageText string `json:"msg_text"` + PushID string `json:"push_id"` + NotifcationID string `json:"notif_id"` + MobileLaunchURI string `json:"mobileLaunchUri"` + EventTimestamp string `json:"event_ts"` +} |