diff --git a/cmd/helpers.go b/cmd/helpers.go index 9b7c7c1..56bbccf 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -7,7 +7,7 @@ import ( pb "github.com/andersnormal/autobot/proto" "github.com/golang/protobuf/ptypes/timestamp" - "github.com/nlopes/slack" + "github.com/slack-go/slack" ) // FromUserIDWithContext ... @@ -31,7 +31,7 @@ func FromChannelIDWithContext(ctx context.Context, api *slack.Client, channelID c := new(pb.Message_Channel) // get channel - channel, err := api.GetChannelInfoContext(ctx, channelID) + channel, err := api.GetConversationInfo(channelID, false) if err != nil { return nil, err } @@ -42,11 +42,6 @@ func FromChannelIDWithContext(ctx context.Context, api *slack.Client, channelID return c, nil } -// FromChannelID ... -func FromChannelID(channelID string) *pb.Message_Channel { - return &pb.Message_Channel{Id: channelID} -} - // FromMsgWithContext ... func FromMsgWithContext(ctx context.Context, api *slack.Client, msg *slack.MessageEvent) (*pb.Message, error) { m := new(pb.Message) @@ -62,33 +57,13 @@ func FromMsgWithContext(ctx context.Context, api *slack.Client, msg *slack.Messa m.From = user - // get list of direct message channels - im, err := api.GetIMChannelsContext(ctx) + // get the channel information + channel, err := FromChannelIDWithContext(ctx, api, msg.Channel) if err != nil { return nil, err } - var isPrivate bool - for _, dm := range im { - isPrivate = dm.ID == msg.Channel - - if isPrivate { - m.Channel = FromChannelID(dm.ID) - - break - } - } - - if !isPrivate { - // resolve channel ... - channel, err := FromChannelIDWithContext(ctx, api, msg.Channel) - if err != nil { - return nil, err - } - - m.Channel = channel - } - + m.Channel = channel m.TextFormat = pb.Message_PLAIN_TEXT m.Text = msg.Text diff --git a/cmd/run.go b/cmd/run.go index 391d6a4..f0bf3af 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -9,7 +9,7 @@ import ( "github.com/andersnormal/autobot/pkg/plugins/runtime" pb "github.com/andersnormal/autobot/proto" - "github.com/nlopes/slack" + "github.com/slack-go/slack" ) const ( diff --git a/go.mod b/go.mod index 1442388..660e15c 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.13 require ( github.com/andersnormal/autobot v1.0.0-beta.12 github.com/golang/protobuf v1.3.2 - github.com/nlopes/slack v0.6.0 github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.2 + github.com/slack-go/slack v0.6.6 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.5.0 ) diff --git a/go.sum b/go.sum index 80020cf..4bc683e 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,7 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -94,6 +95,8 @@ github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -214,6 +217,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/slack-go/slack v0.6.6 h1:ln0fO794CudStSJEfhZ08Ok5JanMjvW6/k2xBuHqedU= +github.com/slack-go/slack v0.6.6/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md index 0827d05..19aa2e7 100644 --- a/vendor/github.com/gorilla/websocket/README.md +++ b/vendor/github.com/gorilla/websocket/README.md @@ -8,7 +8,7 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the ### Documentation -* [API Reference](http://godoc.org/github.com/gorilla/websocket) +* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go index 6f17cd2..ca46d2f 100644 --- a/vendor/github.com/gorilla/websocket/conn.go +++ b/vendor/github.com/gorilla/websocket/conn.go @@ -244,8 +244,8 @@ type Conn struct { subprotocol string // Write fields - mu chan bool // used as mutex to protect write to conn - writeBuf []byte // frame is constructed in this buffer. + mu chan struct{} // used as mutex to protect write to conn + writeBuf []byte // frame is constructed in this buffer. writePool BufferPool writeBufSize int writeDeadline time.Time @@ -302,8 +302,8 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBuf = make([]byte, writeBufferSize) } - mu := make(chan bool, 1) - mu <- true + mu := make(chan struct{}, 1) + mu <- struct{}{} c := &Conn{ isServer: isServer, br: br, @@ -377,7 +377,7 @@ func (c *Conn) read(n int) ([]byte, error) { func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { <-c.mu - defer func() { c.mu <- true }() + defer func() { c.mu <- struct{}{} }() c.writeErrMu.Lock() err := c.writeErr @@ -429,7 +429,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er maskBytes(key, 0, buf[6:]) } - d := time.Hour * 1000 + d := 1000 * time.Hour if !deadline.IsZero() { d = deadline.Sub(time.Now()) if d < 0 { @@ -444,7 +444,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er case <-timer.C: return errWriteTimeout } - defer func() { c.mu <- true }() + defer func() { c.mu <- struct{}{} }() c.writeErrMu.Lock() err := c.writeErr diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go index c6f4df8..8db0cef 100644 --- a/vendor/github.com/gorilla/websocket/doc.go +++ b/vendor/github.com/gorilla/websocket/doc.go @@ -187,9 +187,9 @@ // than the largest message do not provide any benefit. // // Depending on the distribution of message sizes, setting the buffer size to -// to a value less than the maximum expected message size can greatly reduce -// memory use with a small impact on performance. Here's an example: If 99% of -// the messages are smaller than 256 bytes and the maximum message size is 512 +// a value less than the maximum expected message size can greatly reduce memory +// use with a small impact on performance. Here's an example: If 99% of the +// messages are smaller than 256 bytes and the maximum message size is 512 // bytes, then a buffer size of 256 bytes will result in 1.01 more system calls // than a buffer size of 512 bytes. The memory savings is 50%. // diff --git a/vendor/github.com/gorilla/websocket/go.sum b/vendor/github.com/gorilla/websocket/go.sum index cf4fbba..e69de29 100644 --- a/vendor/github.com/gorilla/websocket/go.sum +++ b/vendor/github.com/gorilla/websocket/go.sum @@ -1,2 +0,0 @@ -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go index 74ec565..c854225 100644 --- a/vendor/github.com/gorilla/websocket/prepared.go +++ b/vendor/github.com/gorilla/websocket/prepared.go @@ -73,8 +73,8 @@ func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { // Prepare a frame using a 'fake' connection. // TODO: Refactor code in conn.go to allow more direct construction of // the frame. - mu := make(chan bool, 1) - mu <- true + mu := make(chan struct{}, 1) + mu <- struct{}{} var nc prepareConn c := &Conn{ conn: &nc, diff --git a/vendor/github.com/nlopes/slack/block_element.go b/vendor/github.com/nlopes/slack/block_element.go deleted file mode 100644 index c62ba99..0000000 --- a/vendor/github.com/nlopes/slack/block_element.go +++ /dev/null @@ -1,238 +0,0 @@ -package slack - -// https://api.slack.com/reference/messaging/block-elements - -const ( - METImage MessageElementType = "image" - METButton MessageElementType = "button" - METOverflow MessageElementType = "overflow" - METDatepicker MessageElementType = "datepicker" - - MixedElementImage MixedElementType = "mixed_image" - MixedElementText MixedElementType = "mixed_text" - - OptTypeStatic string = "static_select" - OptTypeExternal string = "external_select" - OptTypeUser string = "users_select" - OptTypeConversations string = "conversations_select" - OptTypeChannels string = "channels_select" -) - -type MessageElementType string -type MixedElementType string - -// BlockElement defines an interface that all block element types should implement. -type BlockElement interface { - ElementType() MessageElementType -} - -type MixedElement interface { - MixedElementType() MixedElementType -} - -type Accessory struct { - ImageElement *ImageBlockElement - ButtonElement *ButtonBlockElement - OverflowElement *OverflowBlockElement - DatePickerElement *DatePickerBlockElement - SelectElement *SelectBlockElement -} - -// NewAccessory returns a new Accessory for a given block element -func NewAccessory(element BlockElement) *Accessory { - switch element.(type) { - case *ImageBlockElement: - return &Accessory{ImageElement: element.(*ImageBlockElement)} - case *ButtonBlockElement: - return &Accessory{ButtonElement: element.(*ButtonBlockElement)} - case *OverflowBlockElement: - return &Accessory{OverflowElement: element.(*OverflowBlockElement)} - case *DatePickerBlockElement: - return &Accessory{DatePickerElement: element.(*DatePickerBlockElement)} - case *SelectBlockElement: - return &Accessory{SelectElement: element.(*SelectBlockElement)} - } - - return nil -} - -// BlockElements is a convenience struct defined to allow dynamic unmarshalling of -// the "elements" value in Slack's JSON response, which varies depending on BlockElement type -type BlockElements struct { - ElementSet []BlockElement `json:"elements,omitempty"` -} - -// 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. -// -// More Information: https://api.slack.com/reference/messaging/block-elements#image -type ImageBlockElement struct { - Type MessageElementType `json:"type"` - ImageURL string `json:"image_url"` - AltText string `json:"alt_text"` -} - -// ElementType returns the type of the Element -func (s ImageBlockElement) ElementType() MessageElementType { - return s.Type -} - -func (s ImageBlockElement) MixedElementType() MixedElementType { - return MixedElementImage -} - -// NewImageBlockElement returns a new instance of an image block element -func NewImageBlockElement(imageURL, altText string) *ImageBlockElement { - return &ImageBlockElement{ - Type: METImage, - ImageURL: imageURL, - AltText: altText, - } -} - -type Style string - -const ( - StyleDefault Style = "default" - StylePrimary Style = "primary" - StyleDanger Style = "danger" -) - -// ButtonBlockElement defines an interactive element that inserts a button. The -// button can be a trigger for anything from opening a simple link to starting -// a complex workflow. -// -// More Information: https://api.slack.com/reference/messaging/block-elements#button -type ButtonBlockElement struct { - Type MessageElementType `json:"type,omitempty"` - Text *TextBlockObject `json:"text"` - ActionID string `json:"action_id,omitempty"` - URL string `json:"url,omitempty"` - Value string `json:"value,omitempty"` - Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` - Style Style `json:"style,omitempty"` -} - -// ElementType returns the type of the element -func (s ButtonBlockElement) ElementType() MessageElementType { - return s.Type -} - -// add styling to button object -func (s *ButtonBlockElement) WithStyle(style Style) { - s.Style = style -} - -// NewButtonBlockElement returns an instance of a new button element to be used within a block -func NewButtonBlockElement(actionID, value string, text *TextBlockObject) *ButtonBlockElement { - return &ButtonBlockElement{ - Type: METButton, - ActionID: actionID, - Text: text, - Value: value, - } -} - -// SelectBlockElement defines the simplest form of select menu, with a static list -// of options passed in when defining the element. -// -// More Information: https://api.slack.com/reference/messaging/block-elements#select -type SelectBlockElement 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"` - InitialOption *OptionBlockObject `json:"initial_option,omitempty"` - 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"` - Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` -} - -// ElementType returns the type of the Element -func (s SelectBlockElement) ElementType() MessageElementType { - return MessageElementType(s.Type) -} - -// NewOptionsSelectBlockElement returns a new instance of SelectBlockElement for use with -// the Options object only. -func NewOptionsSelectBlockElement(optType string, placeholder *TextBlockObject, actionID string, options ...*OptionBlockObject) *SelectBlockElement { - return &SelectBlockElement{ - Type: optType, - Placeholder: placeholder, - ActionID: actionID, - Options: options, - } -} - -// NewOptionsGroupSelectBlockElement returns a new instance of SelectBlockElement for use with -// the Options object only. -func NewOptionsGroupSelectBlockElement( - optType string, - placeholder *TextBlockObject, - actionID string, - optGroups ...*OptionGroupBlockObject, -) *SelectBlockElement { - return &SelectBlockElement{ - 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 -// list of options to choose from. -// -// More Information: https://api.slack.com/reference/messaging/block-elements#overflow -type OverflowBlockElement struct { - Type MessageElementType `json:"type"` - ActionID string `json:"action_id,omitempty"` - Options []*OptionBlockObject `json:"options"` - Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` -} - -// ElementType returns the type of the Element -func (s OverflowBlockElement) ElementType() MessageElementType { - return s.Type -} - -// NewOverflowBlockElement returns an instance of a new Overflow Block Element -func NewOverflowBlockElement(actionID string, options ...*OptionBlockObject) *OverflowBlockElement { - return &OverflowBlockElement{ - Type: METOverflow, - ActionID: actionID, - Options: options, - } -} - -// DatePickerBlockElement defines an element which lets users easily select a -// date from a calendar style UI. Date picker elements can be used inside of -// section and actions blocks. -// -// More Information: https://api.slack.com/reference/messaging/block-elements#datepicker -type DatePickerBlockElement struct { - Type MessageElementType `json:"type"` - ActionID string `json:"action_id"` - Placeholder *TextBlockObject `json:"placeholder,omitempty"` - InitialDate string `json:"initial_date,omitempty"` - Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` -} - -// ElementType returns the type of the Element -func (s DatePickerBlockElement) ElementType() MessageElementType { - return s.Type -} - -// NewDatePickerBlockElement returns an instance of a date picker element -func NewDatePickerBlockElement(actionID string) *DatePickerBlockElement { - return &DatePickerBlockElement{ - Type: METDatepicker, - ActionID: actionID, - } -} diff --git a/vendor/github.com/nlopes/slack/go.sum b/vendor/github.com/nlopes/slack/go.sum deleted file mode 100644 index 3bb45c1..0000000 --- a/vendor/github.com/nlopes/slack/go.sum +++ /dev/null @@ -1,22 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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.1.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= -github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0= -github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= -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= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/victorcoder/slack-test v0.0.0-20190131110821-6f9a569c10af h1:JFxr+No3ZWgCtxnnTWCybnB/z0Iy3qLmdj3u2NV5o48= -github.com/victorcoder/slack-test v0.0.0-20190131110821-6f9a569c10af/go.mod h1:dStM4ShMus8J3hiq66ExbbzGLkwyZ+RQJePwFhWCCvQ= -github.com/victorcoder/slack-test v0.0.0-20190131113129-a43b3bb77f43 h1:wtFekkaAAQibpy3iE4Hhx2Gi9pZAbITOSfVP7GXk5eM= -github.com/victorcoder/slack-test v0.0.0-20190131113129-a43b3bb77f43/go.mod h1:dStM4ShMus8J3hiq66ExbbzGLkwyZ+RQJePwFhWCCvQ= -golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37 h1:BkNcmLtAVeWe9h5k0jt24CQgaG5vb4x/doFbAiEC/Ho= -golang.org/x/net v0.0.0-20180108090419-434ec0c7fe37/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/vendor/github.com/nlopes/slack/info.go b/vendor/github.com/nlopes/slack/info.go deleted file mode 100644 index 31f459f..0000000 --- a/vendor/github.com/nlopes/slack/info.go +++ /dev/null @@ -1,195 +0,0 @@ -package slack - -import ( - "bytes" - "fmt" - "strconv" - "time" -) - -// UserPrefs needs to be implemented -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 -} - -// UserDetails contains user details coming in the initial response from StartRTM -type UserDetails struct { - ID string `json:"id"` - Name string `json:"name"` - Created JSONTime `json:"created"` - ManualPresence string `json:"manual_presence"` - Prefs UserPrefs `json:"prefs"` -} - -// JSONTime exists so that we can have a String method converting the date -type JSONTime int64 - -// String converts the unix timestamp into a string -func (t JSONTime) String() string { - tm := t.Time() - return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2")) -} - -// Time returns a `time.Time` representation of this value. -func (t JSONTime) Time() time.Time { - return time.Unix(int64(t), 0) -} - -// UnmarshalJSON will unmarshal both string and int JSON values -func (t *JSONTime) UnmarshalJSON(buf []byte) error { - s := bytes.Trim(buf, `"`) - - v, err := strconv.Atoi(string(s)) - if err != nil { - return err - } - - *t = JSONTime(int64(v)) - return nil -} - -// Team contains details about a team -type Team struct { - ID string `json:"id"` - Name string `json:"name"` - Domain string `json:"domain"` -} - -// Icons XXX: needs further investigation -type Icons struct { - Image36 string `json:"image_36,omitempty"` - Image48 string `json:"image_48,omitempty"` - Image72 string `json:"image_72,omitempty"` -} - -// Info contains various details about the authenticated user and team. -// It is returned by StartRTM or included in the "ConnectedEvent" RTM event. -type Info struct { - URL string `json:"url,omitempty"` - User *UserDetails `json:"self,omitempty"` - Team *Team `json:"team,omitempty"` -} - -type infoResponseFull struct { - Info - SlackResponse -} - -// GetBotByID is deprecated and returns nil -func (info Info) GetBotByID(botID string) *Bot { - return nil -} - -// GetUserByID is deprecated and returns nil -func (info Info) GetUserByID(userID string) *User { - return nil -} - -// GetChannelByID is deprecated and returns nil -func (info Info) GetChannelByID(channelID string) *Channel { - return nil -} - -// GetGroupByID is deprecated and returns nil -func (info Info) GetGroupByID(groupID string) *Group { - return nil -} - -// GetIMByID is deprecated and returns nil -func (info Info) GetIMByID(imID string) *IM { - return nil -} diff --git a/vendor/github.com/nlopes/slack/oauth.go b/vendor/github.com/nlopes/slack/oauth.go deleted file mode 100644 index 29d6dce..0000000 --- a/vendor/github.com/nlopes/slack/oauth.go +++ /dev/null @@ -1,64 +0,0 @@ -package slack - -import ( - "context" - "net/url" -) - -// OAuthResponseIncomingWebhook ... -type OAuthResponseIncomingWebhook struct { - URL string `json:"url"` - Channel string `json:"channel"` - ChannelID string `json:"channel_id,omitempty"` - ConfigurationURL string `json:"configuration_url"` -} - -// OAuthResponseBot ... -type OAuthResponseBot struct { - BotUserID string `json:"bot_user_id"` - BotAccessToken string `json:"bot_access_token"` -} - -// OAuthResponse ... -type OAuthResponse struct { - AccessToken string `json:"access_token"` - Scope string `json:"scope"` - TeamName string `json:"team_name"` - TeamID string `json:"team_id"` - IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"` - Bot OAuthResponseBot `json:"bot"` - UserID string `json:"user_id,omitempty"` - SlackResponse -} - -// 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) -} - -// GetOAuthTokenContext retrieves an AccessToken with a custom context -func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) { - response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI) - if err != nil { - return "", "", err - } - return response.AccessToken, response.Scope, nil -} - -func GetOAuthResponse(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) { - return GetOAuthResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI) -} - -func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) { - values := url.Values{ - "client_id": {clientID}, - "client_secret": {clientSecret}, - "code": {code}, - "redirect_uri": {redirectURI}, - } - response := &OAuthResponse{} - if err = postForm(ctx, client, APIURL+"oauth.access", values, response, discard{}); err != nil { - return nil, err - } - return response, response.Err() -} diff --git a/vendor/github.com/nlopes/slack/.gitignore b/vendor/github.com/slack-go/slack/.gitignore similarity index 100% rename from vendor/github.com/nlopes/slack/.gitignore rename to vendor/github.com/slack-go/slack/.gitignore diff --git a/vendor/github.com/nlopes/slack/.gometalinter.json b/vendor/github.com/slack-go/slack/.gometalinter.json similarity index 100% rename from vendor/github.com/nlopes/slack/.gometalinter.json rename to vendor/github.com/slack-go/slack/.gometalinter.json diff --git a/vendor/github.com/nlopes/slack/.travis.yml b/vendor/github.com/slack-go/slack/.travis.yml similarity index 78% rename from vendor/github.com/nlopes/slack/.travis.yml rename to vendor/github.com/slack-go/slack/.travis.yml index ed99d9e..f2019d7 100644 --- a/vendor/github.com/nlopes/slack/.travis.yml +++ b/vendor/github.com/slack-go/slack/.travis.yml @@ -28,6 +28,12 @@ matrix: script: go test -v ./... - go: "1.11.x" script: go test -v -mod=vendor ./... + - go: "1.12.x" + script: go test -v -mod=vendor ./... + - go: "1.13.x" + script: go test -v -mod=vendor ./... + - go: "1.14.x" + script: go test -v -mod=vendor ./... - go: "tip" script: go test -v -mod=vendor ./... diff --git a/vendor/github.com/nlopes/slack/CHANGELOG.md b/vendor/github.com/slack-go/slack/CHANGELOG.md similarity index 100% rename from vendor/github.com/nlopes/slack/CHANGELOG.md rename to vendor/github.com/slack-go/slack/CHANGELOG.md diff --git a/vendor/github.com/nlopes/slack/LICENSE b/vendor/github.com/slack-go/slack/LICENSE similarity index 100% rename from vendor/github.com/nlopes/slack/LICENSE rename to vendor/github.com/slack-go/slack/LICENSE diff --git a/vendor/github.com/slack-go/slack/Makefile b/vendor/github.com/slack-go/slack/Makefile new file mode 100644 index 0000000..7279640 --- /dev/null +++ b/vendor/github.com/slack-go/slack/Makefile @@ -0,0 +1,36 @@ +.PHONY: help deps fmt lint test test-race test-integration + +help: + @echo "" + @echo "Welcome to slack-go/slack make." + @echo "The following commands are available:" + @echo "" + @echo " make deps : Fetch all dependencies" + @echo " make fmt : Run go fmt to fix any formatting issues" + @echo " make lint : Use go vet to check for linting issues" + @echo " make test : Run all short tests" + @echo " make test-race : Run all tests with race condition checking" + @echo " make test-integration : Run all tests without limiting to short" + @echo "" + @echo " make pr-prep : Run this before making a PR to run fmt, lint and tests" + @echo "" + +deps: + @go mod tidy + +fmt: + @go fmt . + +lint: + @go vet . + +test: + @go test -v -count=1 -timeout 300s -short ./... + +test-race: + @go test -v -count=1 -timeout 300s -short -race ./... + +test-integration: + @go test -v -count=1 -timeout 600s ./... + +pr-prep: fmt lint test-race test-integration diff --git a/vendor/github.com/nlopes/slack/README.md b/vendor/github.com/slack-go/slack/README.md similarity index 63% rename from vendor/github.com/nlopes/slack/README.md rename to vendor/github.com/slack-go/slack/README.md index a5e8e5e..eaf0778 100644 --- a/vendor/github.com/nlopes/slack/README.md +++ b/vendor/github.com/slack-go/slack/README.md @@ -1,5 +1,6 @@ -Slack API in Go [![GoDoc](https://godoc.org/github.com/nlopes/slack?status.svg)](https://godoc.org/github.com/nlopes/slack) [![Build Status](https://travis-ci.org/nlopes/slack.svg)](https://travis-ci.org/nlopes/slack) +Slack API in Go [![GoDoc](https://godoc.org/github.com/slack-go/slack?status.svg)](https://godoc.org/github.com/slack-go/slack) [![Build Status](https://travis-ci.org/slack-go/slack.svg)](https://travis-ci.org/slack-go/slack) =============== +This is the original Slack library for Go created by Norberto Lopez, transferred to a Github organization. [![Join the chat at https://gitter.im/go-slack/Lobby](https://badges.gitter.im/go-slack/Lobby.svg)](https://gitter.im/go-slack/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -12,13 +13,13 @@ a fully managed way. ## Changelog -[CHANGELOG.md](https://github.com/nlopes/slack/blob/master/CHANGELOG.md) is available. Please visit it for updates. +[CHANGELOG.md](https://github.com/slack-go/slack/blob/master/CHANGELOG.md) is available. Please visit it for updates. ## Installing ### *go get* - $ go get -u github.com/nlopes/slack + $ go get -u github.com/slack-go/slack ## Example @@ -28,7 +29,7 @@ a fully managed way. import ( "fmt" - "github.com/nlopes/slack" + "github.com/slack-go/slack" ) func main() { @@ -53,7 +54,7 @@ func main() { import ( "fmt" - "github.com/nlopes/slack" + "github.com/slack-go/slack" ) func main() { @@ -69,12 +70,12 @@ func main() { ## Minimal RTM usage: -See https://github.com/nlopes/slack/blob/master/examples/websocket/websocket.go +See https://github.com/slack-go/slack/blob/master/examples/websocket/websocket.go ## Minimal EventsAPI usage: -See https://github.com/nlopes/slack/blob/master/examples/eventsapi/events.go +See https://github.com/slack-go/slack/blob/master/examples/eventsapi/events.go ## Contributing @@ -82,6 +83,14 @@ See https://github.com/nlopes/slack/blob/master/examples/eventsapi/events.go You are more than welcome to contribute to this project. Fork and make a Pull Request, or create an Issue if you see any problem. +Before making any Pull Request please run the following: + +``` +make pr-prep +``` + +This will check/update code formatting, linting and then run all tests + ## License BSD 2 Clause license diff --git a/vendor/github.com/nlopes/slack/TODO.txt b/vendor/github.com/slack-go/slack/TODO.txt similarity index 100% rename from vendor/github.com/nlopes/slack/TODO.txt rename to vendor/github.com/slack-go/slack/TODO.txt diff --git a/vendor/github.com/nlopes/slack/admin.go b/vendor/github.com/slack-go/slack/admin.go similarity index 100% rename from vendor/github.com/nlopes/slack/admin.go rename to vendor/github.com/slack-go/slack/admin.go diff --git a/vendor/github.com/nlopes/slack/attachments.go b/vendor/github.com/slack-go/slack/attachments.go similarity index 97% rename from vendor/github.com/nlopes/slack/attachments.go rename to vendor/github.com/slack-go/slack/attachments.go index cf8b5c6..5f388e9 100644 --- a/vendor/github.com/nlopes/slack/attachments.go +++ b/vendor/github.com/slack-go/slack/attachments.go @@ -61,7 +61,7 @@ type ConfirmationField struct { // Attachment contains all the information for an attachment type Attachment struct { Color string `json:"color,omitempty"` - Fallback string `json:"fallback"` + Fallback string `json:"fallback,omitempty"` CallbackID string `json:"callback_id,omitempty"` ID int `json:"id,omitempty"` @@ -75,7 +75,7 @@ type Attachment struct { Title string `json:"title,omitempty"` TitleLink string `json:"title_link,omitempty"` Pretext string `json:"pretext,omitempty"` - Text string `json:"text"` + Text string `json:"text"` // Required ImageURL string `json:"image_url,omitempty"` ThumbURL string `json:"thumb_url,omitempty"` @@ -84,6 +84,8 @@ type Attachment struct { Actions []AttachmentAction `json:"actions,omitempty"` MarkdownIn []string `json:"mrkdwn_in,omitempty"` + Blocks Blocks `json:"blocks,omitempty"` + Footer string `json:"footer,omitempty"` FooterIcon string `json:"footer_icon,omitempty"` diff --git a/vendor/github.com/nlopes/slack/auth.go b/vendor/github.com/slack-go/slack/auth.go similarity index 91% rename from vendor/github.com/nlopes/slack/auth.go rename to vendor/github.com/slack-go/slack/auth.go index dc1dbcd..f4f7f00 100644 --- a/vendor/github.com/nlopes/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/nlopes/slack/backoff.go b/vendor/github.com/slack-go/slack/backoff.go similarity index 100% rename from vendor/github.com/nlopes/slack/backoff.go rename to vendor/github.com/slack-go/slack/backoff.go diff --git a/vendor/github.com/nlopes/slack/block.go b/vendor/github.com/slack-go/slack/block.go similarity index 51% rename from vendor/github.com/nlopes/slack/block.go rename to vendor/github.com/slack-go/slack/block.go index 1fc7fec..dd4b871 100644 --- a/vendor/github.com/nlopes/slack/block.go +++ b/vendor/github.com/slack-go/slack/block.go @@ -14,6 +14,9 @@ const ( MBTImage MessageBlockType = "image" MBTAction MessageBlockType = "actions" MBTContext MessageBlockType = "context" + MBTFile MessageBlockType = "file" + MBTInput MessageBlockType = "input" + MBTHeader MessageBlockType = "header" ) // Block defines an interface all block types should implement @@ -30,22 +33,26 @@ type Blocks struct { // BlockAction is the action callback sent when a block is interacted with type BlockAction struct { - ActionID string `json:"action_id"` - BlockID string `json:"block_id"` - Type actionType `json:"type"` - Text TextBlockObject `json:"text"` - Value string `json:"value"` - ActionTs string `json:"action_ts"` - SelectedOption OptionBlockObject `json:"selected_option"` - SelectedUser string `json:"selected_user"` - SelectedChannel string `json:"selected_channel"` - SelectedConversation string `json:"selected_conversation"` - SelectedDate string `json:"selected_date"` - InitialOption OptionBlockObject `json:"initial_option"` - InitialUser string `json:"initial_user"` - InitialChannel string `json:"initial_channel"` - InitialConversation string `json:"initial_conversation"` - InitialDate string `json:"initial_date"` + ActionID string `json:"action_id"` + BlockID string `json:"block_id"` + Type actionType `json:"type"` + Text TextBlockObject `json:"text"` + Value string `json:"value"` + ActionTs string `json:"action_ts"` + SelectedOption OptionBlockObject `json:"selected_option"` + SelectedOptions []OptionBlockObject `json:"selected_options"` + SelectedUser string `json:"selected_user"` + SelectedUsers []string `json:"selected_users"` + SelectedChannel string `json:"selected_channel"` + SelectedChannels []string `json:"selected_channels"` + SelectedConversation string `json:"selected_conversation"` + SelectedConversations []string `json:"selected_conversations"` + SelectedDate string `json:"selected_date"` + InitialOption OptionBlockObject `json:"initial_option"` + InitialUser string `json:"initial_user"` + InitialChannel string `json:"initial_channel"` + InitialConversation string `json:"initial_conversation"` + InitialDate string `json:"initial_date"` } // actionType returns the type of the action diff --git a/vendor/github.com/nlopes/slack/block_action.go b/vendor/github.com/slack-go/slack/block_action.go similarity index 100% rename from vendor/github.com/nlopes/slack/block_action.go rename to vendor/github.com/slack-go/slack/block_action.go diff --git a/vendor/github.com/nlopes/slack/block_context.go b/vendor/github.com/slack-go/slack/block_context.go similarity index 100% rename from vendor/github.com/nlopes/slack/block_context.go rename to vendor/github.com/slack-go/slack/block_context.go diff --git a/vendor/github.com/nlopes/slack/block_conv.go b/vendor/github.com/slack-go/slack/block_conv.go similarity index 68% rename from vendor/github.com/nlopes/slack/block_conv.go rename to vendor/github.com/slack-go/slack/block_conv.go index 619867e..656a9ab 100644 --- a/vendor/github.com/nlopes/slack/block_conv.go +++ b/vendor/github.com/slack-go/slack/block_conv.go @@ -2,6 +2,7 @@ package slack import ( "encoding/json" + "fmt" "github.com/pkg/errors" ) @@ -56,12 +57,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{} default: - return errors.New("unsupported block type") + block = &UnknownBlock{} } err = json.Unmarshal(r, block) @@ -76,6 +81,54 @@ func (b *Blocks) UnmarshalJSON(data []byte) error { return nil } +// UnmarshalJSON implements the Unmarshaller interface for InputBlock, so that any JSON +// unmarshalling is delegated and proper type determination can be made before unmarshal +func (b *InputBlock) UnmarshalJSON(data []byte) error { + type alias InputBlock + a := struct { + Element json.RawMessage `json:"element"` + *alias + }{ + alias: (*alias)(b), + } + + if err := json.Unmarshal(data, &a); err != nil { + return err + } + + s := sumtype{} + if err := json.Unmarshal(a.Element, &s); err != nil { + return nil + } + + var e BlockElement + switch s.TypeVal { + case "datepicker": + e = &DatePickerBlockElement{} + case "plain_text_input": + e = &PlainTextInputBlockElement{} + case "static_select", "external_select", "users_select", "conversations_select", "channels_select": + e = &SelectBlockElement{} + case "multi_static_select", "multi_external_select", "multi_users_select", "multi_conversations_select", "multi_channels_select": + e = &MultiSelectBlockElement{} + case "checkboxes": + e = &CheckboxGroupsBlockElement{} + case "overflow": + e = &OverflowBlockElement{} + case "radio_buttons": + e = &RadioButtonsBlockElement{} + default: + return errors.New("unsupported block element type") + } + + if err := json.Unmarshal(a.Element, e); err != nil { + return err + } + b.Element = e + + return nil +} + // MarshalJSON implements the Marshaller interface for BlockElements so that any JSON // marshalling is delegated and proper type determination can be made before marshal func (b *BlockElements) MarshalJSON() ([]byte, error) { @@ -124,10 +177,16 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error { blockElement = &OverflowBlockElement{} case "datepicker": blockElement = &DatePickerBlockElement{} + case "plain_text_input": + blockElement = &PlainTextInputBlockElement{} + case "checkboxes": + blockElement = &CheckboxGroupsBlockElement{} + case "radio_buttons": + blockElement = &RadioButtonsBlockElement{} case "static_select", "external_select", "users_select", "conversations_select", "channels_select": blockElement = &SelectBlockElement{} default: - return errors.New("unsupported block element type") + return fmt.Errorf("unsupported block element type %v", blockElementType) } err = json.Unmarshal(r, blockElement) @@ -203,12 +262,42 @@ 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) + case "checkboxes": + element, err := unmarshalBlockElement(r, &CheckboxGroupsBlockElement{}) + if err != nil { + return err + } + a.CheckboxGroupsBlockElement = element.(*CheckboxGroupsBlockElement) + default: + element, err := unmarshalBlockElement(r, &UnknownBlockElement{}) + if err != nil { + return err + } + a.UnknownElement = element.(*UnknownBlockElement) } return nil @@ -235,9 +324,21 @@ 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.CheckboxGroupsBlockElement != nil { + return element.CheckboxGroupsBlockElement + } if element.SelectElement != nil { return element.SelectElement } + if element.MultiSelectElement != nil { + return element.MultiSelectElement + } return nil } diff --git a/vendor/github.com/nlopes/slack/block_divider.go b/vendor/github.com/slack-go/slack/block_divider.go similarity index 100% rename from vendor/github.com/nlopes/slack/block_divider.go rename to vendor/github.com/slack-go/slack/block_divider.go diff --git a/vendor/github.com/slack-go/slack/block_element.go b/vendor/github.com/slack-go/slack/block_element.go new file mode 100644 index 0000000..bca4e31 --- /dev/null +++ b/vendor/github.com/slack-go/slack/block_element.go @@ -0,0 +1,423 @@ +package slack + +// https://api.slack.com/reference/messaging/block-elements + +const ( + METCheckboxGroups MessageElementType = "checkboxes" + METImage MessageElementType = "image" + METButton MessageElementType = "button" + METOverflow MessageElementType = "overflow" + METDatepicker MessageElementType = "datepicker" + METPlainTextInput MessageElementType = "plain_text_input" + METRadioButtons MessageElementType = "radio_buttons" + + MixedElementImage MixedElementType = "mixed_image" + MixedElementText MixedElementType = "mixed_text" + + OptTypeStatic string = "static_select" + OptTypeExternal string = "external_select" + 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 +type MixedElementType string + +// BlockElement defines an interface that all block element types should implement. +type BlockElement interface { + ElementType() MessageElementType +} + +type MixedElement interface { + MixedElementType() MixedElementType +} + +type Accessory struct { + ImageElement *ImageBlockElement + ButtonElement *ButtonBlockElement + OverflowElement *OverflowBlockElement + DatePickerElement *DatePickerBlockElement + PlainTextInputElement *PlainTextInputBlockElement + RadioButtonsElement *RadioButtonsBlockElement + SelectElement *SelectBlockElement + MultiSelectElement *MultiSelectBlockElement + CheckboxGroupsBlockElement *CheckboxGroupsBlockElement + UnknownElement *UnknownBlockElement +} + +// NewAccessory returns a new Accessory for a given block element +func NewAccessory(element BlockElement) *Accessory { + switch element.(type) { + case *ImageBlockElement: + return &Accessory{ImageElement: element.(*ImageBlockElement)} + case *ButtonBlockElement: + return &Accessory{ButtonElement: element.(*ButtonBlockElement)} + case *OverflowBlockElement: + 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)} + case *CheckboxGroupsBlockElement: + return &Accessory{CheckboxGroupsBlockElement: element.(*CheckboxGroupsBlockElement)} + default: + return &Accessory{UnknownElement: element.(*UnknownBlockElement)} + } +} + +// BlockElements is a convenience struct defined to allow dynamic unmarshalling of +// the "elements" value in Slack's JSON response, which varies depending on BlockElement type +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. +// +// More Information: https://api.slack.com/reference/messaging/block-elements#image +type ImageBlockElement struct { + Type MessageElementType `json:"type"` + ImageURL string `json:"image_url"` + AltText string `json:"alt_text"` +} + +// ElementType returns the type of the Element +func (s ImageBlockElement) ElementType() MessageElementType { + return s.Type +} + +func (s ImageBlockElement) MixedElementType() MixedElementType { + return MixedElementImage +} + +// NewImageBlockElement returns a new instance of an image block element +func NewImageBlockElement(imageURL, altText string) *ImageBlockElement { + return &ImageBlockElement{ + Type: METImage, + ImageURL: imageURL, + AltText: altText, + } +} + +type Style string + +const ( + StyleDefault Style = "default" + StylePrimary Style = "primary" + StyleDanger Style = "danger" +) + +// ButtonBlockElement defines an interactive element that inserts a button. The +// button can be a trigger for anything from opening a simple link to starting +// a complex workflow. +// +// More Information: https://api.slack.com/reference/messaging/block-elements#button +type ButtonBlockElement struct { + Type MessageElementType `json:"type,omitempty"` + Text *TextBlockObject `json:"text"` + ActionID string `json:"action_id,omitempty"` + URL string `json:"url,omitempty"` + Value string `json:"value,omitempty"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` + Style Style `json:"style,omitempty"` +} + +// ElementType returns the type of the element +func (s ButtonBlockElement) ElementType() MessageElementType { + return s.Type +} + +// WithStyling adds styling to the button object and returns the modified ButtonBlockElement +func (s *ButtonBlockElement) WithStyle(style Style) *ButtonBlockElement { + s.Style = style + return s +} + +// NewButtonBlockElement returns an instance of a new button element to be used within a block +func NewButtonBlockElement(actionID, value string, text *TextBlockObject) *ButtonBlockElement { + return &ButtonBlockElement{ + Type: METButton, + ActionID: actionID, + Text: text, + Value: value, + } +} + +// 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. +// +// More Information: https://api.slack.com/reference/messaging/block-elements#select +type SelectBlockElement 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"` + InitialOption *OptionBlockObject `json:"initial_option,omitempty"` + InitialUser string `json:"initial_user,omitempty"` + InitialConversation string `json:"initial_conversation,omitempty"` + InitialChannel string `json:"initial_channel,omitempty"` + DefaultToCurrentConversation bool `json:"default_to_current_conversation,omitempty"` + ResponseURLEnabled bool `json:"response_url_enabled,omitempty"` + MinQueryLength *int `json:"min_query_length,omitempty"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` +} + +// ElementType returns the type of the Element +func (s SelectBlockElement) ElementType() MessageElementType { + return MessageElementType(s.Type) +} + +// NewOptionsSelectBlockElement returns a new instance of SelectBlockElement for use with +// the Options object only. +func NewOptionsSelectBlockElement(optType string, placeholder *TextBlockObject, actionID string, options ...*OptionBlockObject) *SelectBlockElement { + return &SelectBlockElement{ + Type: optType, + Placeholder: placeholder, + ActionID: actionID, + Options: options, + } +} + +// NewOptionsGroupSelectBlockElement returns a new instance of SelectBlockElement for use with +// the Options object only. +func NewOptionsGroupSelectBlockElement( + optType string, + placeholder *TextBlockObject, + actionID string, + optGroups ...*OptionGroupBlockObject, +) *SelectBlockElement { + return &SelectBlockElement{ + Type: optType, + Placeholder: placeholder, + ActionID: actionID, + OptionGroups: optGroups, + } +} + +// 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 +// list of options to choose from. +// +// More Information: https://api.slack.com/reference/messaging/block-elements#overflow +type OverflowBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id,omitempty"` + Options []*OptionBlockObject `json:"options"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` +} + +// ElementType returns the type of the Element +func (s OverflowBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewOverflowBlockElement returns an instance of a new Overflow Block Element +func NewOverflowBlockElement(actionID string, options ...*OptionBlockObject) *OverflowBlockElement { + return &OverflowBlockElement{ + Type: METOverflow, + ActionID: actionID, + Options: options, + } +} + +// DatePickerBlockElement defines an element which lets users easily select a +// date from a calendar style UI. Date picker elements can be used inside of +// section and actions blocks. +// +// More Information: https://api.slack.com/reference/messaging/block-elements#datepicker +type DatePickerBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id,omitempty"` + Placeholder *TextBlockObject `json:"placeholder,omitempty"` + InitialDate string `json:"initial_date,omitempty"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` +} + +// ElementType returns the type of the Element +func (s DatePickerBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewDatePickerBlockElement returns an instance of a date picker element +func NewDatePickerBlockElement(actionID string) *DatePickerBlockElement { + return &DatePickerBlockElement{ + Type: METDatepicker, + ActionID: actionID, + } +} + +// 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/block-kit/block-elements#input +type PlainTextInputBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id,omitempty"` + Placeholder *TextBlockObject `json:"placeholder,omitempty"` + InitialValue string `json:"initial_value,omitempty"` + Multiline bool `json:"multiline,omitempty"` + MinLength int `json:"min_length,omitempty"` + MaxLength int `json:"max_length,omitempty"` +} + +// ElementType returns the type of the Element +func (s PlainTextInputBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewPlainTextInputBlockElement returns an instance of a plain-text input +// element +func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string) *PlainTextInputBlockElement { + return &PlainTextInputBlockElement{ + Type: METPlainTextInput, + ActionID: actionID, + Placeholder: placeholder, + } +} + +// CheckboxGroupsBlockElement defines an element which allows users to choose +// one or more items from a list of possible options. +// +// More Information: https://api.slack.com/reference/block-kit/block-elements#checkboxes +type CheckboxGroupsBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id,omitempty"` + Options []*OptionBlockObject `json:"options"` + InitialOptions []*OptionBlockObject `json:"initial_options,omitempty"` + Confirm *ConfirmationBlockObject `json:"confirm,omitempty"` +} + +// ElementType returns the type of the Element +func (c CheckboxGroupsBlockElement) ElementType() MessageElementType { + return c.Type +} + +// NewCheckboxGroupsBlockElement returns an instance of a radio block element +func NewCheckboxGroupsBlockElement(actionID string, options ...*OptionBlockObject) *CheckboxGroupsBlockElement { + return &CheckboxGroupsBlockElement{ + Type: METCheckboxGroups, + ActionID: actionID, + Options: options, + } +} + +// 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,omitempty"` + 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 0000000..ac4453f --- /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_header.go b/vendor/github.com/slack-go/slack/block_header.go new file mode 100644 index 0000000..6dff4b8 --- /dev/null +++ b/vendor/github.com/slack-go/slack/block_header.go @@ -0,0 +1,38 @@ +package slack + +// HeaderBlock defines a new block of type header +// +// More Information: https://api.slack.com/reference/messaging/blocks#header +type HeaderBlock struct { + Type MessageBlockType `json:"type"` + Text *TextBlockObject `json:"text,omitempty"` + BlockID string `json:"block_id,omitempty"` +} + +// BlockType returns the type of the block +func (s HeaderBlock) BlockType() MessageBlockType { + return s.Type +} + +// HeaderBlockOption allows configuration of options for a new header block +type HeaderBlockOption func(*HeaderBlock) + +func HeaderBlockOptionBlockID(blockID string) HeaderBlockOption { + return func(block *HeaderBlock) { + block.BlockID = blockID + } +} + +// NewHeaderBlock returns a new instance of a header block to be rendered +func NewHeaderBlock(textObj *TextBlockObject, options ...HeaderBlockOption) *HeaderBlock { + block := HeaderBlock{ + Type: MBTHeader, + Text: textObj, + } + + for _, option := range options { + option(&block) + } + + return &block +} diff --git a/vendor/github.com/nlopes/slack/block_image.go b/vendor/github.com/slack-go/slack/block_image.go similarity index 93% rename from vendor/github.com/nlopes/slack/block_image.go rename to vendor/github.com/slack-go/slack/block_image.go index 6de3f63..90cbd14 100644 --- a/vendor/github.com/nlopes/slack/block_image.go +++ b/vendor/github.com/slack-go/slack/block_image.go @@ -8,7 +8,7 @@ type ImageBlock struct { ImageURL string `json:"image_url"` AltText string `json:"alt_text"` BlockID string `json:"block_id,omitempty"` - Title *TextBlockObject `json:"title"` + Title *TextBlockObject `json:"title,omitempty"` } // BlockType returns the type of the block diff --git a/vendor/github.com/slack-go/slack/block_input.go b/vendor/github.com/slack-go/slack/block_input.go new file mode 100644 index 0000000..10638cd --- /dev/null +++ b/vendor/github.com/slack-go/slack/block_input.go @@ -0,0 +1,28 @@ +package slack + +// InputBlock defines data that is used to display user input fields. +// +// More Information: https://api.slack.com/reference/block-kit/blocks#input +type InputBlock struct { + Type MessageBlockType `json:"type"` + BlockID string `json:"block_id,omitempty"` + Label *TextBlockObject `json:"label"` + Element BlockElement `json:"element"` + Hint *TextBlockObject `json:"hint,omitempty"` + Optional bool `json:"optional,omitempty"` +} + +// BlockType returns the type of the block +func (s InputBlock) BlockType() MessageBlockType { + return s.Type +} + +// NewInputBlock returns a new instance of an input block +func NewInputBlock(blockID string, label *TextBlockObject, element BlockElement) *InputBlock { + return &InputBlock{ + Type: MBTInput, + BlockID: blockID, + Label: label, + Element: element, + } +} diff --git a/vendor/github.com/nlopes/slack/block_object.go b/vendor/github.com/slack-go/slack/block_object.go similarity index 94% rename from vendor/github.com/nlopes/slack/block_object.go rename to vendor/github.com/slack-go/slack/block_object.go index 9e77e6c..b6f1da4 100644 --- a/vendor/github.com/nlopes/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. @@ -155,6 +163,7 @@ type ConfirmationBlockObject struct { Text *TextBlockObject `json:"text"` Confirm *TextBlockObject `json:"confirm"` Deny *TextBlockObject `json:"deny"` + Style Style `json:"style,omitempty"` } // validateType enforces block objects for element and block parameters @@ -162,6 +171,11 @@ func (s ConfirmationBlockObject) validateType() MessageObjectType { return motConfirmation } +// add styling to confirmation object +func (s *ConfirmationBlockObject) WithStyle(style Style) { + s.Style = style +} + // NewConfirmationBlockObject returns an instance of a new Confirmation Block Object func NewConfirmationBlockObject(title, text, confirm, deny *TextBlockObject) *ConfirmationBlockObject { return &ConfirmationBlockObject{ @@ -178,7 +192,7 @@ func NewConfirmationBlockObject(title, text, confirm, deny *TextBlockObject) *Co type OptionBlockObject struct { Text *TextBlockObject `json:"text"` Value string `json:"value"` - URL string `json:"url"` + URL string `json:"url,omitempty"` } // NewOptionBlockObject returns an instance of a new Option Block Element diff --git a/vendor/github.com/nlopes/slack/block_section.go b/vendor/github.com/slack-go/slack/block_section.go similarity index 100% rename from vendor/github.com/nlopes/slack/block_section.go rename to vendor/github.com/slack-go/slack/block_section.go 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 0000000..97054c7 --- /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/nlopes/slack/bots.go b/vendor/github.com/slack-go/slack/bots.go similarity index 82% rename from vendor/github.com/nlopes/slack/bots.go rename to vendor/github.com/slack-go/slack/bots.go index 5d5a2ad..da21ba0 100644 --- a/vendor/github.com/nlopes/slack/bots.go +++ b/vendor/github.com/slack-go/slack/bots.go @@ -7,10 +7,13 @@ import ( // Bot contains information about a bot type Bot struct { - ID string `json:"id"` - Name string `json:"name"` - Deleted bool `json:"deleted"` - Icons Icons `json:"icons"` + ID string `json:"id"` + Name string `json:"name"` + Deleted bool `json:"deleted"` + UserID string `json:"user_id"` + AppID string `json:"app_id"` + Updated JSONTime `json:"updated"` + Icons Icons `json:"icons"` } type botResponseFull struct { diff --git a/vendor/github.com/nlopes/slack/channels.go b/vendor/github.com/slack-go/slack/channels.go similarity index 84% rename from vendor/github.com/nlopes/slack/channels.go rename to vendor/github.com/slack-go/slack/channels.go index c99e665..a90d238 100644 --- a/vendor/github.com/nlopes/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/nlopes/slack/chat.go b/vendor/github.com/slack-go/slack/chat.go similarity index 72% rename from vendor/github.com/nlopes/slack/chat.go rename to vendor/github.com/slack-go/slack/chat.go index a480e5a..a9f51e1 100644 --- a/vendor/github.com/nlopes/slack/chat.go +++ b/vendor/github.com/slack-go/slack/chat.go @@ -1,12 +1,15 @@ package slack import ( + "bytes" "context" "encoding/json" + "io/ioutil" "net/http" "net/url" + "strconv" - "github.com/nlopes/slack/slackutilsx" + "github.com/slack-go/slack/slackutilsx" ) const ( @@ -25,10 +28,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 +86,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 +157,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 @@ -168,6 +208,15 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt return "", "", "", err } + if api.Debug() { + reqBody, err := ioutil.ReadAll(req.Body) + if err != nil { + return "", "", "", err + } + req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) + api.Debugf("Sending request: %s", string(reqBody)) + } + if err = doPost(ctx, api.httpclient, req, parser(&response), api); err != nil { return "", "", "", err } @@ -212,23 +261,27 @@ 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 { - apiurl string - options []MsgOption - mode sendMode - endpoint string - values url.Values - attachments []Attachment - responseType string + apiurl string + options []MsgOption + mode sendMode + endpoint string + values url.Values + attachments []Attachment + blocks Blocks + responseType string + replaceOriginal bool + deleteOriginal bool } func (t sendConfig) BuildRequest(token, channelID string) (req *http.Request, _ func(*chatResponseFull) responseParser, err error) { @@ -239,10 +292,13 @@ func (t sendConfig) BuildRequest(token, channelID string) (req *http.Request, _ switch t.mode { case chatResponse: return responseURLSender{ - endpoint: t.endpoint, - values: t.values, - attachments: t.attachments, - responseType: t.responseType, + endpoint: t.endpoint, + values: t.values, + attachments: t.attachments, + blocks: t.blocks, + responseType: t.responseType, + replaceOriginal: t.replaceOriginal, + deleteOriginal: t.deleteOriginal, }.BuildRequest() default: return formSender{endpoint: t.endpoint, values: t.values}.BuildRequest() @@ -262,18 +318,24 @@ func (t formSender) BuildRequest() (*http.Request, func(*chatResponseFull) respo } type responseURLSender struct { - endpoint string - values url.Values - attachments []Attachment - responseType string + endpoint string + values url.Values + attachments []Attachment + blocks Blocks + responseType string + replaceOriginal bool + deleteOriginal bool } func (t responseURLSender) BuildRequest() (*http.Request, func(*chatResponseFull) responseParser, error) { req, err := jsonReq(t.endpoint, Msg{ - Text: t.values.Get("text"), - Timestamp: t.values.Get("ts"), - Attachments: t.attachments, - ResponseType: t.responseType, + Text: t.values.Get("text"), + Timestamp: t.values.Get("ts"), + Attachments: t.attachments, + Blocks: t.blocks, + ResponseType: t.responseType, + ReplaceOriginal: t.replaceOriginal, + DeleteOriginal: t.deleteOriginal, }) return req, func(resp *chatResponseFull) responseParser { return newContentTypeParser(resp) @@ -283,6 +345,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 { @@ -343,16 +414,36 @@ func MsgOptionUnfurl(timestamp string, unfurls map[string]Attachment) MsgOption } // MsgOptionResponseURL supplies a url to use as the endpoint. -func MsgOptionResponseURL(url string, rt string) MsgOption { +func MsgOptionResponseURL(url string, responseType string) MsgOption { return func(config *sendConfig) error { config.mode = chatResponse config.endpoint = url - config.responseType = rt + config.responseType = responseType config.values.Del("ts") return nil } } +// MsgOptionReplaceOriginal replaces original message with response url +func MsgOptionReplaceOriginal(responseURL string) MsgOption { + return func(config *sendConfig) error { + config.mode = chatResponse + config.endpoint = responseURL + config.replaceOriginal = true + return nil + } +} + +// MsgOptionDeleteOriginal deletes original message with response url +func MsgOptionDeleteOriginal(responseURL string) MsgOption { + return func(config *sendConfig) error { + config.mode = chatResponse + config.endpoint = responseURL + config.deleteOriginal = true + return nil + } +} + // MsgOptionAsUser whether or not to send the message as the user. func MsgOptionAsUser(b bool) MsgOption { return func(config *sendConfig) error { @@ -420,6 +511,8 @@ func MsgOptionBlocks(blocks ...Block) MsgOption { return nil } + config.blocks.BlockSet = append(config.blocks.BlockSet, blocks...) + blocks, err := json.Marshal(blocks) if err == nil { config.values.Set("blocks", string(blocks)) @@ -619,3 +712,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/nlopes/slack/comment.go b/vendor/github.com/slack-go/slack/comment.go similarity index 100% rename from vendor/github.com/nlopes/slack/comment.go rename to vendor/github.com/slack-go/slack/comment.go diff --git a/vendor/github.com/nlopes/slack/conversation.go b/vendor/github.com/slack-go/slack/conversation.go similarity index 100% rename from vendor/github.com/nlopes/slack/conversation.go rename to vendor/github.com/slack-go/slack/conversation.go diff --git a/vendor/github.com/nlopes/slack/dialog.go b/vendor/github.com/slack-go/slack/dialog.go similarity index 100% rename from vendor/github.com/nlopes/slack/dialog.go rename to vendor/github.com/slack-go/slack/dialog.go diff --git a/vendor/github.com/nlopes/slack/dialog_select.go b/vendor/github.com/slack-go/slack/dialog_select.go similarity index 100% rename from vendor/github.com/nlopes/slack/dialog_select.go rename to vendor/github.com/slack-go/slack/dialog_select.go diff --git a/vendor/github.com/nlopes/slack/dialog_text.go b/vendor/github.com/slack-go/slack/dialog_text.go similarity index 100% rename from vendor/github.com/nlopes/slack/dialog_text.go rename to vendor/github.com/slack-go/slack/dialog_text.go diff --git a/vendor/github.com/nlopes/slack/dnd.go b/vendor/github.com/slack-go/slack/dnd.go similarity index 100% rename from vendor/github.com/nlopes/slack/dnd.go rename to vendor/github.com/slack-go/slack/dnd.go diff --git a/vendor/github.com/nlopes/slack/emoji.go b/vendor/github.com/slack-go/slack/emoji.go similarity index 100% rename from vendor/github.com/nlopes/slack/emoji.go rename to vendor/github.com/slack-go/slack/emoji.go diff --git a/vendor/github.com/nlopes/slack/errors.go b/vendor/github.com/slack-go/slack/errors.go similarity index 70% rename from vendor/github.com/nlopes/slack/errors.go rename to vendor/github.com/slack-go/slack/errors.go index 09113ff..8be22a6 100644 --- a/vendor/github.com/nlopes/slack/errors.go +++ b/vendor/github.com/slack-go/slack/errors.go @@ -1,12 +1,15 @@ package slack -import "github.com/nlopes/slack/internal/errorsx" +import "github.com/slack-go/slack/internal/errorsx" // Errors returned by various methods. const ( ErrAlreadyDisconnected = errorsx.String("Invalid call to Disconnect - Slack API is already disconnected") ErrRTMDisconnected = errorsx.String("disconnect received while trying to connect") + ErrRTMGoodbye = errorsx.String("goodbye detected") + ErrRTMDeadman = errorsx.String("deadman switch triggered") ErrParametersMissing = errorsx.String("received empty parameters") + ErrBlockIDNotUnique = errorsx.String("Block ID needs to be unique") ErrInvalidConfiguration = errorsx.String("invalid configuration") ErrMissingHeaders = errorsx.String("missing headers") ErrExpiredTimestamp = errorsx.String("timestamp is too old") diff --git a/vendor/github.com/nlopes/slack/files.go b/vendor/github.com/slack-go/slack/files.go similarity index 100% rename from vendor/github.com/nlopes/slack/files.go rename to vendor/github.com/slack-go/slack/files.go diff --git a/vendor/github.com/nlopes/slack/go.mod b/vendor/github.com/slack-go/slack/go.mod similarity index 61% rename from vendor/github.com/nlopes/slack/go.mod rename to vendor/github.com/slack-go/slack/go.mod index 87256eb..32fc4b9 100644 --- a/vendor/github.com/nlopes/slack/go.mod +++ b/vendor/github.com/slack-go/slack/go.mod @@ -1,9 +1,12 @@ -module github.com/nlopes/slack +module github.com/slack-go/slack require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gorilla/websocket v1.2.0 + github.com/go-test/deep v1.0.4 + github.com/gorilla/websocket v1.4.2 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 new file mode 100644 index 0000000..a66560a --- /dev/null +++ b/vendor/github.com/slack-go/slack/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/vendor/github.com/nlopes/slack/groups.go b/vendor/github.com/slack-go/slack/groups.go similarity index 92% rename from vendor/github.com/nlopes/slack/groups.go rename to vendor/github.com/slack-go/slack/groups.go index 2337486..6ea1b13 100644 --- a/vendor/github.com/nlopes/slack/groups.go +++ b/vendor/github.com/slack-go/slack/groups.go @@ -353,3 +353,24 @@ func (api *Client) SetGroupTopicContext(ctx context.Context, group, topic string } return response.Topic, nil } + +// GetGroupReplies gets an entire thread (a message plus all the messages in reply to it). +// see https://api.slack.com/methods/groups.replies +func (api *Client) GetGroupReplies(channelID, thread_ts string) ([]Message, error) { + return api.GetGroupRepliesContext(context.Background(), channelID, thread_ts) +} + +// GetGroupRepliesContext gets an entire thread (a message plus all the messages in reply to it) with a custom context +// see https://api.slack.com/methods/groups.replies +func (api *Client) GetGroupRepliesContext(ctx context.Context, channelID, thread_ts string) ([]Message, error) { + values := url.Values{ + "token": {api.token}, + "channel": {channelID}, + "thread_ts": {thread_ts}, + } + response, err := api.groupRequest(ctx, "groups.replies", values) + if err != nil { + return nil, err + } + return response.History.Messages, nil +} diff --git a/vendor/github.com/nlopes/slack/history.go b/vendor/github.com/slack-go/slack/history.go similarity index 95% rename from vendor/github.com/nlopes/slack/history.go rename to vendor/github.com/slack-go/slack/history.go index 87b2e1e..49dfe35 100644 --- a/vendor/github.com/nlopes/slack/history.go +++ b/vendor/github.com/slack-go/slack/history.go @@ -22,6 +22,7 @@ type History struct { Latest string `json:"latest"` Messages []Message `json:"messages"` HasMore bool `json:"has_more"` + Unread int `json:"unread_count_display"` } // NewHistoryParameters provides an instance of HistoryParameters with all the sane default values set diff --git a/vendor/github.com/nlopes/slack/im.go b/vendor/github.com/slack-go/slack/im.go similarity index 100% rename from vendor/github.com/nlopes/slack/im.go rename to vendor/github.com/slack-go/slack/im.go diff --git a/vendor/github.com/slack-go/slack/info.go b/vendor/github.com/slack-go/slack/info.go new file mode 100644 index 0000000..ec70624 --- /dev/null +++ b/vendor/github.com/slack-go/slack/info.go @@ -0,0 +1,468 @@ +package slack + +import ( + "bytes" + "context" + "fmt" + "net/url" + "strconv" + "strings" + "time" +) + +type UserPrefsCarrier struct { + SlackResponse + UserPrefs *UserPrefs `json:"prefs"` +} + +// UserPrefs carries a bunch of user settings including some unknown types +type UserPrefs struct { + 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 +type UserDetails struct { + ID string `json:"id"` + Name string `json:"name"` + Created JSONTime `json:"created"` + ManualPresence string `json:"manual_presence"` + Prefs UserPrefs `json:"prefs"` +} + +// JSONTime exists so that we can have a String method converting the date +type JSONTime int64 + +// String converts the unix timestamp into a string +func (t JSONTime) String() string { + tm := t.Time() + return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2")) +} + +// Time returns a `time.Time` representation of this value. +func (t JSONTime) Time() time.Time { + return time.Unix(int64(t), 0) +} + +// UnmarshalJSON will unmarshal both string and int JSON values +func (t *JSONTime) UnmarshalJSON(buf []byte) error { + s := bytes.Trim(buf, `"`) + + v, err := strconv.Atoi(string(s)) + if err != nil { + return err + } + + *t = JSONTime(int64(v)) + return nil +} + +// Team contains details about a team +type Team struct { + ID string `json:"id"` + Name string `json:"name"` + Domain string `json:"domain"` +} + +// Icons XXX: needs further investigation +type Icons struct { + Image36 string `json:"image_36,omitempty"` + Image48 string `json:"image_48,omitempty"` + Image72 string `json:"image_72,omitempty"` +} + +// Info contains various details about the authenticated user and team. +// It is returned by StartRTM or included in the "ConnectedEvent" RTM event. +type Info struct { + URL string `json:"url,omitempty"` + User *UserDetails `json:"self,omitempty"` + Team *Team `json:"team,omitempty"` +} + +type infoResponseFull struct { + Info + SlackResponse +} + +// GetBotByID is deprecated and returns nil +func (info Info) GetBotByID(botID string) *Bot { + return nil +} + +// GetUserByID is deprecated and returns nil +func (info Info) GetUserByID(userID string) *User { + return nil +} + +// GetChannelByID is deprecated and returns nil +func (info Info) GetChannelByID(channelID string) *Channel { + return nil +} + +// GetGroupByID is deprecated and returns nil +func (info Info) GetGroupByID(groupID string) *Group { + return nil +} + +// GetIMByID is deprecated and returns nil +func (info Info) GetIMByID(imID string) *IM { + return nil +} diff --git a/vendor/github.com/nlopes/slack/interactions.go b/vendor/github.com/slack-go/slack/interactions.go similarity index 62% rename from vendor/github.com/nlopes/slack/interactions.go rename to vendor/github.com/slack-go/slack/interactions.go index 5433463..c7f5921 100644 --- a/vendor/github.com/nlopes/slack/interactions.go +++ b/vendor/github.com/slack-go/slack/interactions.go @@ -1,6 +1,7 @@ package slack import ( + "bytes" "encoding/json" ) @@ -23,6 +24,10 @@ 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") + InteractionTypeShortcut = InteractionType("shortcut") ) // InteractionCallback is sent from slack when a user interactions with a button or dialog. @@ -43,7 +48,24 @@ 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"` + MessageTs string `json:"message_ts"` + AttachmentID json.Number `json:"attachment_id"` + ChannelID string `json:"channel_id"` + IsEphemeral bool `json:"is_ephemeral"` + IsAppUnfurl bool `json:"is_app_unfurl"` } // ActionCallback is a convenience struct defined to allow dynamic unmarshalling of @@ -53,6 +75,48 @@ type ActionCallbacks struct { BlockActions []*BlockAction } +// MarshalJSON implements the Marshaller interface in order to combine both +// action callback types back into a single array, like how the api responds. +// This makes Marshaling and Unmarshaling an InteractionCallback symmetrical +func (a ActionCallbacks) MarshalJSON() ([]byte, error) { + count := 0 + length := len(a.AttachmentActions) + len(a.BlockActions) + buffer := bytes.NewBufferString("[") + + f := func(obj interface{}) error { + js, err := json.Marshal(obj) + if err != nil { + return err + } + _, err = buffer.Write(js) + if err != nil { + return err + } + + count++ + if count < length { + _, err = buffer.WriteString(",") + return err + } + return nil + } + + for _, act := range a.AttachmentActions { + err := f(act) + if err != nil { + return nil, err + } + } + for _, blk := range a.BlockActions { + err := f(blk) + if err != nil { + return nil, err + } + } + buffer.WriteString("]") + return buffer.Bytes(), nil +} + // UnmarshalJSON implements the Marshaller interface in order to delegate // marshalling and allow for proper type assertion when decoding the response func (a *ActionCallbacks) UnmarshalJSON(data []byte) error { @@ -76,7 +140,7 @@ func (a *ActionCallbacks) UnmarshalJSON(data []byte) error { } a.BlockActions = append(a.BlockActions, action.(*BlockAction)) - return nil + continue } action, err := unmarshalAction(r, &AttachmentAction{}) diff --git a/vendor/github.com/nlopes/slack/internal/errorsx/errorsx.go b/vendor/github.com/slack-go/slack/internal/errorsx/errorsx.go similarity index 100% rename from vendor/github.com/nlopes/slack/internal/errorsx/errorsx.go rename to vendor/github.com/slack-go/slack/internal/errorsx/errorsx.go diff --git a/vendor/github.com/nlopes/slack/internal/timex/timex.go b/vendor/github.com/slack-go/slack/internal/timex/timex.go similarity index 100% rename from vendor/github.com/nlopes/slack/internal/timex/timex.go rename to vendor/github.com/slack-go/slack/internal/timex/timex.go diff --git a/vendor/github.com/nlopes/slack/item.go b/vendor/github.com/slack-go/slack/item.go similarity index 100% rename from vendor/github.com/nlopes/slack/item.go rename to vendor/github.com/slack-go/slack/item.go diff --git a/vendor/github.com/nlopes/slack/logger.go b/vendor/github.com/slack-go/slack/logger.go similarity index 100% rename from vendor/github.com/nlopes/slack/logger.go rename to vendor/github.com/slack-go/slack/logger.go diff --git a/vendor/github.com/nlopes/slack/messageID.go b/vendor/github.com/slack-go/slack/messageID.go similarity index 100% rename from vendor/github.com/nlopes/slack/messageID.go rename to vendor/github.com/slack-go/slack/messageID.go diff --git a/vendor/github.com/nlopes/slack/messages.go b/vendor/github.com/slack-go/slack/messages.go similarity index 97% rename from vendor/github.com/nlopes/slack/messages.go rename to vendor/github.com/slack-go/slack/messages.go index 37a2633..09db546 100644 --- a/vendor/github.com/nlopes/slack/messages.go +++ b/vendor/github.com/slack-go/slack/messages.go @@ -15,13 +15,14 @@ type OutgoingMessage struct { // Message is an auxiliary type to allow us to have a message containing sub messages type Message struct { Msg - SubMessage *Msg `json:"message,omitempty"` + SubMessage *Msg `json:"message,omitempty"` PreviousMessage *Msg `json:"previous_message,omitempty"` } // Msg contains information about a slack message type Msg struct { // Basic Message + ClientMsgID string `json:"client_msg_id,omitempty"` Type string `json:"type,omitempty"` Channel string `json:"channel,omitempty"` User string `json:"user,omitempty"` @@ -101,7 +102,7 @@ type Msg struct { const ( // ResponseTypeInChannel in channel response for slash commands. ResponseTypeInChannel = "in_channel" - // ResponseTypeEphemeral ephemeral respone for slash commands. + // ResponseTypeEphemeral ephemeral response for slash commands. ResponseTypeEphemeral = "ephemeral" ) diff --git a/vendor/github.com/nlopes/slack/misc.go b/vendor/github.com/slack-go/slack/misc.go similarity index 100% rename from vendor/github.com/nlopes/slack/misc.go rename to vendor/github.com/slack-go/slack/misc.go diff --git a/vendor/github.com/slack-go/slack/oauth.go b/vendor/github.com/slack-go/slack/oauth.go new file mode 100644 index 0000000..707ccc6 --- /dev/null +++ b/vendor/github.com/slack-go/slack/oauth.go @@ -0,0 +1,134 @@ +package slack + +import ( + "context" + "net/url" +) + +// OAuthResponseIncomingWebhook ... +type OAuthResponseIncomingWebhook struct { + URL string `json:"url"` + Channel string `json:"channel"` + ChannelID string `json:"channel_id,omitempty"` + ConfigurationURL string `json:"configuration_url"` +} + +// OAuthResponseBot ... +type OAuthResponseBot struct { + BotUserID string `json:"bot_user_id"` + BotAccessToken string `json:"bot_access_token"` +} + +// OAuthResponse ... +type OAuthResponse struct { + AccessToken string `json:"access_token"` + Scope string `json:"scope"` + TeamName string `json:"team_name"` + TeamID string `json:"team_id"` + IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"` + Bot OAuthResponseBot `json:"bot"` + UserID string `json:"user_id,omitempty"` + 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"` + Team OAuthV2ResponseTeam `json:"team"` + IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"` + 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) +} + +// GetOAuthTokenContext retrieves an AccessToken with a custom context +func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) { + response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI) + if err != nil { + return "", "", err + } + return response.AccessToken, response.Scope, nil +} + +// GetBotOAuthToken retrieves top-level and bot AccessToken - https://api.slack.com/legacy/oauth#bot_user_access_tokens +func GetBotOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, bot OAuthResponseBot, err error) { + return GetBotOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI) +} + +// GetBotOAuthTokenContext retrieves top-level and bot AccessToken with a custom context +func GetBotOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, bot OAuthResponseBot, err error) { + response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI) + if err != nil { + return "", "", OAuthResponseBot{}, err + } + return response.AccessToken, response.Scope, response.Bot, nil +} + +// GetOAuthResponse retrieves OAuth response +func GetOAuthResponse(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) { + return GetOAuthResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI) +} + +// GetOAuthResponseContext retrieves OAuth response with custom context +func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) { + values := url.Values{ + "client_id": {clientID}, + "client_secret": {clientSecret}, + "code": {code}, + "redirect_uri": {redirectURI}, + } + response := &OAuthResponse{} + if err = postForm(ctx, client, APIURL+"oauth.access", values, response, discard{}); err != nil { + return nil, err + } + 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/nlopes/slack/pagination.go b/vendor/github.com/slack-go/slack/pagination.go similarity index 100% rename from vendor/github.com/nlopes/slack/pagination.go rename to vendor/github.com/slack-go/slack/pagination.go diff --git a/vendor/github.com/nlopes/slack/pins.go b/vendor/github.com/slack-go/slack/pins.go similarity index 100% rename from vendor/github.com/nlopes/slack/pins.go rename to vendor/github.com/slack-go/slack/pins.go diff --git a/vendor/github.com/nlopes/slack/reactions.go b/vendor/github.com/slack-go/slack/reactions.go similarity index 100% rename from vendor/github.com/nlopes/slack/reactions.go rename to vendor/github.com/slack-go/slack/reactions.go diff --git a/vendor/github.com/nlopes/slack/reminders.go b/vendor/github.com/slack-go/slack/reminders.go similarity index 100% rename from vendor/github.com/nlopes/slack/reminders.go rename to vendor/github.com/slack-go/slack/reminders.go diff --git a/vendor/github.com/nlopes/slack/rtm.go b/vendor/github.com/slack-go/slack/rtm.go similarity index 98% rename from vendor/github.com/nlopes/slack/rtm.go rename to vendor/github.com/slack-go/slack/rtm.go index 09cb51c..ef6ba34 100644 --- a/vendor/github.com/nlopes/slack/rtm.go +++ b/vendor/github.com/slack-go/slack/rtm.go @@ -2,7 +2,6 @@ package slack import ( "context" - "encoding/json" "net/url" "sync" "time" @@ -120,7 +119,6 @@ func (api *Client) NewRTM(options ...RTMOption) *RTM { disconnected: make(chan struct{}), disconnectedm: &sync.Once{}, forcePing: make(chan bool), - rawEvents: make(chan json.RawMessage), idGen: NewSafeID(1), mu: &sync.Mutex{}, } diff --git a/vendor/github.com/nlopes/slack/search.go b/vendor/github.com/slack-go/slack/search.go similarity index 91% rename from vendor/github.com/nlopes/slack/search.go rename to vendor/github.com/slack-go/slack/search.go index 67e3b1d..de6b40a 100644 --- a/vendor/github.com/nlopes/slack/search.go +++ b/vendor/github.com/slack-go/slack/search.go @@ -23,8 +23,14 @@ type SearchParameters struct { } type CtxChannel struct { - ID string `json:"id"` - Name string `json:"name"` + ID string `json:"id"` + Name string `json:"name"` + IsExtShared bool `json:"is_ext_shared"` + IsMPIM bool `json:"is_mpim"` + ISOrgShared bool `json:"is_org_shared"` + IsPendingExtShared bool `json:"is_pending_ext_shared"` + IsPrivate bool `json:"is_private"` + IsShared bool `json:"is_shared"` } type CtxMessage struct { diff --git a/vendor/github.com/nlopes/slack/security.go b/vendor/github.com/slack-go/slack/security.go similarity index 100% rename from vendor/github.com/nlopes/slack/security.go rename to vendor/github.com/slack-go/slack/security.go diff --git a/vendor/github.com/nlopes/slack/slack.go b/vendor/github.com/slack-go/slack/slack.go similarity index 97% rename from vendor/github.com/nlopes/slack/slack.go rename to vendor/github.com/slack-go/slack/slack.go index 9423052..d342a3e 100644 --- a/vendor/github.com/nlopes/slack/slack.go +++ b/vendor/github.com/slack-go/slack/slack.go @@ -96,7 +96,7 @@ func New(token string, options ...Option) *Client { token: token, endpoint: APIURL, httpclient: &http.Client{}, - log: log.New(os.Stderr, "nlopes/slack", log.LstdFlags|log.Lshortfile), + log: log.New(os.Stderr, "slack-go/slack", log.LstdFlags|log.Lshortfile), } for _, opt := range options { diff --git a/vendor/github.com/nlopes/slack/slackutilsx/slackutilsx.go b/vendor/github.com/slack-go/slack/slackutilsx/slackutilsx.go similarity index 100% rename from vendor/github.com/nlopes/slack/slackutilsx/slackutilsx.go rename to vendor/github.com/slack-go/slack/slackutilsx/slackutilsx.go diff --git a/vendor/github.com/nlopes/slack/slash.go b/vendor/github.com/slack-go/slack/slash.go similarity index 100% rename from vendor/github.com/nlopes/slack/slash.go rename to vendor/github.com/slack-go/slack/slash.go diff --git a/vendor/github.com/nlopes/slack/stars.go b/vendor/github.com/slack-go/slack/stars.go similarity index 61% rename from vendor/github.com/nlopes/slack/stars.go rename to vendor/github.com/slack-go/slack/stars.go index e84d044..5296760 100644 --- a/vendor/github.com/nlopes/slack/stars.go +++ b/vendor/github.com/slack-go/slack/stars.go @@ -4,6 +4,7 @@ import ( "context" "net/url" "strconv" + "time" ) const ( @@ -158,3 +159,105 @@ func (api *Client) GetStarredContext(ctx context.Context, params StarsParameters } return starredItems, paging, nil } + +type listResponsePaginated struct { + Items []Item `json:"items"` + SlackResponse + Metadata ResponseMetadata `json:"response_metadata"` +} + +// StarredItemPagination allows for paginating over the starred items +type StarredItemPagination struct { + Items []Item + limit int + previousResp *ResponseMetadata + c *Client +} + +// ListStarsOption options for the GetUsers method call. +type ListStarsOption func(*StarredItemPagination) + +// ListAllStars returns the complete list of starred items +func (api *Client) ListAllStars() ([]Item, error) { + return api.ListAllStarsContext(context.Background()) +} + +// ListAllStarsContext returns the list of users (with their detailed information) with a custom context +func (api *Client) ListAllStarsContext(ctx context.Context) (results []Item, err error) { + p := api.ListStarsPaginated() + for err == nil { + p, err = p.next(ctx) + if err == nil { + results = append(results, p.Items...) + } else if rateLimitedError, ok := err.(*RateLimitedError); ok { + select { + case <-ctx.Done(): + err = ctx.Err() + case <-time.After(rateLimitedError.RetryAfter): + err = nil + } + } + } + + return results, p.failure(err) +} + +// ListStarsPaginated fetches users in a paginated fashion, see ListStarsPaginationContext for usage. +func (api *Client) ListStarsPaginated(options ...ListStarsOption) StarredItemPagination { + return newStarPagination(api, options...) +} + +func newStarPagination(c *Client, options ...ListStarsOption) (sip StarredItemPagination) { + sip = StarredItemPagination{ + c: c, + limit: 200, // per slack api documentation. + } + + for _, opt := range options { + opt(&sip) + } + + return sip +} + +// done checks if the pagination has completed +func (StarredItemPagination) done(err error) bool { + return err == errPaginationComplete +} + +// done checks if pagination failed. +func (t StarredItemPagination) failure(err error) error { + if t.done(err) { + return nil + } + + return err +} + +// next gets the next list of starred items based on the cursor value +func (t StarredItemPagination) next(ctx context.Context) (_ StarredItemPagination, err error) { + var ( + resp *listResponsePaginated + ) + + 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)}, + "token": {t.c.token}, + "cursor": {t.previousResp.Cursor}, + } + + if err = t.c.postMethod(ctx, "stars.list", values, &resp); err != nil { + return t, err + } + + t.previousResp = &resp.Metadata + t.Items = resp.Items + + return t, nil +} diff --git a/vendor/github.com/nlopes/slack/team.go b/vendor/github.com/slack-go/slack/team.go similarity index 100% rename from vendor/github.com/nlopes/slack/team.go rename to vendor/github.com/slack-go/slack/team.go diff --git a/vendor/github.com/nlopes/slack/usergroups.go b/vendor/github.com/slack-go/slack/usergroups.go similarity index 99% rename from vendor/github.com/nlopes/slack/usergroups.go rename to vendor/github.com/slack-go/slack/usergroups.go index f320659..9417f81 100644 --- a/vendor/github.com/nlopes/slack/usergroups.go +++ b/vendor/github.com/slack-go/slack/usergroups.go @@ -206,7 +206,7 @@ func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGro if userGroup.Description != "" { values["description"] = []string{userGroup.Description} } - + if len(userGroup.Prefs.Channels) > 0 { values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")} } diff --git a/vendor/github.com/nlopes/slack/users.go b/vendor/github.com/slack-go/slack/users.go similarity index 95% rename from vendor/github.com/nlopes/slack/users.go rename to vendor/github.com/slack-go/slack/users.go index 4da8e4c..364958e 100644 --- a/vendor/github.com/nlopes/slack/users.go +++ b/vendor/github.com/slack-go/slack/users.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/url" "strconv" + "strings" "time" ) @@ -44,14 +45,14 @@ type UserProfile struct { // UserProfileCustomFields represents user profile's custom fields. // Slack API's response data type is inconsistent so we use the struct. // For detail, please see below. -// https://github.com/nlopes/slack/pull/298#discussion_r185159233 +// https://github.com/slack-go/slack/pull/298#discussion_r185159233 type UserProfileCustomFields struct { fields map[string]UserProfileCustomField } // UnmarshalJSON is the implementation of the json.Unmarshaler interface. func (fields *UserProfileCustomFields) UnmarshalJSON(b []byte) error { - // https://github.com/nlopes/slack/pull/298#discussion_r185159233 + // https://github.com/slack-go/slack/pull/298#discussion_r185159233 if string(b) == "[]" { return nil } @@ -184,6 +185,7 @@ type TeamIdentity struct { type userResponseFull struct { Members []User `json:"members,omitempty"` User `json:"user,omitempty"` + Users []User `json:"users,omitempty"` UserPresence SlackResponse Metadata ResponseMetadata `json:"response_metadata"` @@ -252,6 +254,26 @@ func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, return &response.User, nil } +// GetUsersInfo will retrieve the complete multi-users information +func (api *Client) GetUsersInfo(users ...string) (*[]User, error) { + return api.GetUsersInfoContext(context.Background(), users...) +} + +// GetUsersInfoContext will retrieve the complete multi-users information with a custom context +func (api *Client) GetUsersInfoContext(ctx context.Context, users ...string) (*[]User, error) { + values := url.Values{ + "token": {api.token}, + "users": {strings.Join(users, ",")}, + "include_locale": {strconv.FormatBool(true)}, + } + + response, err := api.userRequest(ctx, "users.info", values) + if err != nil { + return nil, err + } + return &response.Users, nil +} + // GetUsersOption options for the GetUsers method call. type GetUsersOption func(*UserPagination) diff --git a/vendor/github.com/slack-go/slack/views.go b/vendor/github.com/slack-go/slack/views.go new file mode 100644 index 0000000..ee197ad --- /dev/null +++ b/vendor/github.com/slack-go/slack/views.go @@ -0,0 +1,297 @@ +package slack + +import ( + "context" + "encoding/json" +) + +const ( + VTModal ViewType = "modal" + VTHomeTab ViewType = "home" +) + +type ViewType string + +type ViewState struct { + Values map[string]map[string]BlockAction `json:"values"` +} + +type View struct { + SlackResponse + ID string `json:"id"` + TeamID string `json:"team_id"` + Type ViewType `json:"type"` + Title *TextBlockObject `json:"title"` + Close *TextBlockObject `json:"close"` + Submit *TextBlockObject `json:"submit"` + Blocks Blocks `json:"blocks"` + PrivateMetadata string `json:"private_metadata"` + CallbackID string `json:"callback_id"` + State *ViewState `json:"state"` + Hash string `json:"hash"` + ClearOnClose bool `json:"clear_on_close"` + NotifyOnClose bool `json:"notify_on_close"` + RootViewID string `json:"root_view_id"` + PreviousViewID string `json:"previous_view_id"` + AppID string `json:"app_id"` + ExternalID string `json:"external_id"` + 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,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 { + return v.Type +} + +type HomeTabViewRequest struct { + Type ViewType `json:"type"` + Blocks Blocks `json:"blocks"` + PrivateMetadata string `json:"private_metadata,omitempty"` + CallbackID string `json:"callback_id,omitempty"` + ExternalID string `json:"external_id,omitempty"` +} + +func (v *HomeTabViewRequest) ViewType() ViewType { + return v.Type +} + +type openViewRequest struct { + TriggerID string `json:"trigger_id"` + View ModalViewRequest `json:"view"` +} + +type publishViewRequest struct { + UserID string `json:"user_id"` + View HomeTabViewRequest `json:"view"` + Hash string `json:"hash,omitempty"` +} + +type pushViewRequest struct { + TriggerID string `json:"trigger_id"` + View ModalViewRequest `json:"view"` +} + +type updateViewRequest struct { + View ModalViewRequest `json:"view"` + ExternalID string `json:"external_id,omitempty"` + Hash string `json:"hash,omitempty"` + ViewID string `json:"view_id,omitempty"` +} + +type ViewResponse struct { + SlackResponse + View `json:"view"` +} + +// OpenView opens a view for a user. +func (api *Client) OpenView(triggerID string, view ModalViewRequest) (*ViewResponse, error) { + return api.OpenViewContext(context.Background(), triggerID, view) +} + +// ValidateUniqueBlockID will verify if each input block has a unique block ID if set +func ValidateUniqueBlockID(view ModalViewRequest) bool { + + uniqueBlockID := map[string]bool{} + + for _, b := range view.Blocks.BlockSet { + if inputBlock, ok := b.(*InputBlock); ok { + if _, ok := uniqueBlockID[inputBlock.BlockID]; ok { + return false + } + uniqueBlockID[inputBlock.BlockID] = true + } + } + + return true +} + +// OpenViewContext opens a view for a user with a custom context. +func (api *Client) OpenViewContext( + ctx context.Context, + triggerID string, + view ModalViewRequest, +) (*ViewResponse, error) { + if triggerID == "" { + return nil, ErrParametersMissing + } + + if !ValidateUniqueBlockID(view) { + return nil, ErrBlockIDNotUnique + } + + req := openViewRequest{ + TriggerID: triggerID, + View: view, + } + encoded, err := json.Marshal(req) + if err != nil { + return nil, err + } + endpoint := api.endpoint + "views.open" + resp := &ViewResponse{} + err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api) + if err != nil { + return nil, err + } + return resp, resp.Err() +} + +// PublishView publishes a static view for a user. +func (api *Client) PublishView(userID string, view HomeTabViewRequest, hash string) (*ViewResponse, error) { + return api.PublishViewContext(context.Background(), userID, view, hash) +} + +// PublishViewContext publishes a static view for a user with a custom context. +func (api *Client) PublishViewContext( + ctx context.Context, + userID string, + view HomeTabViewRequest, + hash string, +) (*ViewResponse, error) { + if userID == "" { + return nil, ErrParametersMissing + } + req := publishViewRequest{ + UserID: userID, + View: view, + Hash: hash, + } + encoded, err := json.Marshal(req) + if err != nil { + return nil, err + } + endpoint := api.endpoint + "views.publish" + resp := &ViewResponse{} + err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api) + if err != nil { + return nil, err + } + return resp, resp.Err() +} + +// PushView pushes a view onto the stack of a root view. +func (api *Client) PushView(triggerID string, view ModalViewRequest) (*ViewResponse, error) { + return api.PushViewContext(context.Background(), triggerID, view) +} + +// PublishViewContext pushes a view onto the stack of a root view with a custom context. +func (api *Client) PushViewContext( + ctx context.Context, + triggerID string, + view ModalViewRequest, +) (*ViewResponse, error) { + if triggerID == "" { + return nil, ErrParametersMissing + } + req := pushViewRequest{ + TriggerID: triggerID, + View: view, + } + encoded, err := json.Marshal(req) + if err != nil { + return nil, err + } + endpoint := api.endpoint + "views.push" + resp := &ViewResponse{} + err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api) + if err != nil { + return nil, err + } + return resp, resp.Err() +} + +// UpdateView updates an existing view. +func (api *Client) UpdateView(view ModalViewRequest, externalID, hash, viewID string) (*ViewResponse, error) { + return api.UpdateViewContext(context.Background(), view, externalID, hash, viewID) +} + +// UpdateViewContext updates an existing view with a custom context. +func (api *Client) UpdateViewContext( + ctx context.Context, + view ModalViewRequest, + externalID, hash, + viewID string, +) (*ViewResponse, error) { + if externalID == "" && viewID == "" { + return nil, ErrParametersMissing + } + req := updateViewRequest{ + View: view, + ExternalID: externalID, + Hash: hash, + ViewID: viewID, + } + encoded, err := json.Marshal(req) + if err != nil { + return nil, err + } + endpoint := api.endpoint + "views.update" + resp := &ViewResponse{} + err = postJSON(ctx, api.httpclient, endpoint, api.token, encoded, resp, api) + if err != nil { + return nil, err + } + return resp, resp.Err() +} diff --git a/vendor/github.com/nlopes/slack/webhooks.go b/vendor/github.com/slack-go/slack/webhooks.go similarity index 62% rename from vendor/github.com/nlopes/slack/webhooks.go rename to vendor/github.com/slack-go/slack/webhooks.go index 14e1b8d..39fff44 100644 --- a/vendor/github.com/nlopes/slack/webhooks.go +++ b/vendor/github.com/slack-go/slack/webhooks.go @@ -1,11 +1,8 @@ package slack import ( - "bytes" - "encoding/json" + "context" "net/http" - - "github.com/pkg/errors" ) type WebhookMessage struct { @@ -17,24 +14,17 @@ type WebhookMessage struct { Text string `json:"text,omitempty"` Attachments []Attachment `json:"attachments,omitempty"` Parse string `json:"parse,omitempty"` + Blocks *Blocks `json:"blocks,omitempty"` } func PostWebhook(url string, msg *WebhookMessage) error { - return PostWebhookCustomHTTP(url, http.DefaultClient, msg) + return PostWebhookCustomHTTPContext(context.Background(), url, http.DefaultClient, msg) } -func PostWebhookCustomHTTP(url string, httpClient *http.Client, msg *WebhookMessage) error { - raw, err := json.Marshal(msg) - - if err != nil { - return errors.Wrap(err, "marshal failed") - } - - response, err := httpClient.Post(url, "application/json", bytes.NewReader(raw)) - - if err != nil { - return errors.Wrap(err, "failed to post webhook") - } +func PostWebhookContext(ctx context.Context, url string, msg *WebhookMessage) error { + return PostWebhookCustomHTTPContext(ctx, url, http.DefaultClient, msg) +} - return checkStatusCode(response, discard{}) +func PostWebhookCustomHTTP(url string, httpClient *http.Client, msg *WebhookMessage) error { + return PostWebhookCustomHTTPContext(context.Background(), url, httpClient, msg) } diff --git a/vendor/github.com/slack-go/slack/webhooks_go112.go b/vendor/github.com/slack-go/slack/webhooks_go112.go new file mode 100644 index 0000000..4e0db0e --- /dev/null +++ b/vendor/github.com/slack-go/slack/webhooks_go112.go @@ -0,0 +1,34 @@ +// +build !go1.13 + +package slack + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + + "github.com/pkg/errors" +) + +func PostWebhookCustomHTTPContext(ctx context.Context, url string, httpClient *http.Client, msg *WebhookMessage) error { + raw, err := json.Marshal(msg) + if err != nil { + return errors.Wrap(err, "marshal failed") + } + + req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(raw)) + if err != nil { + return errors.Wrap(err, "failed new request") + } + req = req.WithContext(ctx) + req.Header.Set("Content-Type", "application/json") + + resp, err := httpClient.Do(req) + if err != nil { + return errors.Wrap(err, "failed to post webhook") + } + defer resp.Body.Close() + + return checkStatusCode(resp, discard{}) +} diff --git a/vendor/github.com/slack-go/slack/webhooks_go113.go b/vendor/github.com/slack-go/slack/webhooks_go113.go new file mode 100644 index 0000000..99c243f --- /dev/null +++ b/vendor/github.com/slack-go/slack/webhooks_go113.go @@ -0,0 +1,33 @@ +// +build go1.13 + +package slack + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + + "github.com/pkg/errors" +) + +func PostWebhookCustomHTTPContext(ctx context.Context, url string, httpClient *http.Client, msg *WebhookMessage) error { + raw, err := json.Marshal(msg) + if err != nil { + return errors.Wrap(err, "marshal failed") + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(raw)) + if err != nil { + return errors.Wrap(err, "failed new request") + } + req.Header.Set("Content-Type", "application/json") + + resp, err := httpClient.Do(req) + if err != nil { + return errors.Wrap(err, "failed to post webhook") + } + defer resp.Body.Close() + + return checkStatusCode(resp, discard{}) +} diff --git a/vendor/github.com/nlopes/slack/websocket.go b/vendor/github.com/slack-go/slack/websocket.go similarity index 97% rename from vendor/github.com/nlopes/slack/websocket.go rename to vendor/github.com/slack-go/slack/websocket.go index 122807b..d6895f2 100644 --- a/vendor/github.com/nlopes/slack/websocket.go +++ b/vendor/github.com/slack-go/slack/websocket.go @@ -1,7 +1,6 @@ package slack import ( - "encoding/json" "net/url" "sync" "time" @@ -35,7 +34,6 @@ type RTM struct { disconnected chan struct{} disconnectedm *sync.Once forcePing chan bool - rawEvents chan json.RawMessage // UserDetails upon connection info *Info diff --git a/vendor/github.com/nlopes/slack/websocket_channels.go b/vendor/github.com/slack-go/slack/websocket_channels.go similarity index 98% rename from vendor/github.com/nlopes/slack/websocket_channels.go rename to vendor/github.com/slack-go/slack/websocket_channels.go index 7dd3319..fe274fd 100644 --- a/vendor/github.com/nlopes/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_desktop_notification.go b/vendor/github.com/slack-go/slack/websocket_desktop_notification.go new file mode 100644 index 0000000..7c61abf --- /dev/null +++ b/vendor/github.com/slack-go/slack/websocket_desktop_notification.go @@ -0,0 +1,19 @@ +package slack + +// DesktopNotificationEvent represents the update event for Desktop Notification. +type DesktopNotificationEvent struct { + Type string `json:"type"` + Title string `json:"title"` + Subtitle string `json:"subtitle"` + Message string `json:"msg"` + Timestamp string `json:"ts"` + Content string `json:"content"` + Channel string `json:"channel"` + LaunchURI string `json:"launchUri"` + AvatarImage string `json:"avatarImage"` + SsbFilename string `json:"ssbFilename"` + ImageURI string `json:"imageUri"` + IsShared bool `json:"is_shared"` + IsChannelInvite bool `json:"is_channel_invite"` + EventTimestamp string `json:"event_ts"` +} diff --git a/vendor/github.com/nlopes/slack/websocket_dm.go b/vendor/github.com/slack-go/slack/websocket_dm.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_dm.go rename to vendor/github.com/slack-go/slack/websocket_dm.go diff --git a/vendor/github.com/nlopes/slack/websocket_dnd.go b/vendor/github.com/slack-go/slack/websocket_dnd.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_dnd.go rename to vendor/github.com/slack-go/slack/websocket_dnd.go diff --git a/vendor/github.com/nlopes/slack/websocket_files.go b/vendor/github.com/slack-go/slack/websocket_files.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_files.go rename to vendor/github.com/slack-go/slack/websocket_files.go diff --git a/vendor/github.com/nlopes/slack/websocket_groups.go b/vendor/github.com/slack-go/slack/websocket_groups.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_groups.go rename to vendor/github.com/slack-go/slack/websocket_groups.go diff --git a/vendor/github.com/nlopes/slack/websocket_internals.go b/vendor/github.com/slack-go/slack/websocket_internals.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_internals.go rename to vendor/github.com/slack-go/slack/websocket_internals.go diff --git a/vendor/github.com/nlopes/slack/websocket_managed_conn.go b/vendor/github.com/slack-go/slack/websocket_managed_conn.go similarity index 89% rename from vendor/github.com/nlopes/slack/websocket_managed_conn.go rename to vendor/github.com/slack-go/slack/websocket_managed_conn.go index 8b3b383..8607b3a 100644 --- a/vendor/github.com/nlopes/slack/websocket_managed_conn.go +++ b/vendor/github.com/slack-go/slack/websocket_managed_conn.go @@ -10,8 +10,8 @@ import ( "time" "github.com/gorilla/websocket" - "github.com/nlopes/slack/internal/errorsx" - "github.com/nlopes/slack/internal/timex" + "github.com/slack-go/slack/internal/errorsx" + "github.com/slack-go/slack/internal/timex" ) // ManageConnection can be called on a Slack RTM instance returned by the @@ -58,15 +58,19 @@ func (rtm *RTM) ManageConnection() { rtm.Debugf("RTM connection succeeded on try %d", connectionCount) + rawEvents := make(chan json.RawMessage) // we're now connected so we can set up listeners - go rtm.handleIncomingEvents() - + go rtm.handleIncomingEvents(rawEvents) // this should be a blocking call until the connection has ended - rtm.handleEvents() + rtm.handleEvents(rawEvents) select { case <-rtm.disconnected: // after handle events returns we need to check if we're disconnected + // when this happens we need to cleanup the newly created connection. + if err = conn.Close(); err != nil { + rtm.Debugln("failed to close conn on disconnected RTM", err) + } return default: // otherwise continue and run the loop again to reconnect @@ -208,7 +212,7 @@ func (rtm *RTM) startRTMAndDial(useRTMStart bool) (info *Info, _ *websocket.Conn // This should not be called directly! Instead a boolean value (true for // intentional, false otherwise) should be sent to the killChannel on the RTM. func (rtm *RTM) killConnection(intentional bool, cause error) (err error) { - rtm.Debugln("killing connection") + rtm.Debugln("killing connection", cause) if rtm.conn != nil { err = rtm.conn.Close() @@ -228,7 +232,7 @@ func (rtm *RTM) killConnection(intentional bool, cause error) (err error) { // interval. This also sends outgoing messages that are received from the RTM's // outgoingMessages channel. This also handles incoming raw events from the RTM // rawEvents channel. -func (rtm *RTM) handleEvents() { +func (rtm *RTM) handleEvents(events chan json.RawMessage) { ticker := time.NewTicker(rtm.pingInterval) defer ticker.Stop() for { @@ -239,7 +243,7 @@ func (rtm *RTM) handleEvents() { return // detect when the connection is dead. case <-rtm.pingDeadman.C: - _ = rtm.killConnection(false, errorsx.String("deadman switch triggered")) + _ = rtm.killConnection(false, ErrRTMDeadman) return // send pings on ticker interval case <-ticker.C: @@ -255,12 +259,17 @@ func (rtm *RTM) handleEvents() { // listen for messages that need to be sent case msg := <-rtm.outgoingMessages: rtm.sendOutgoingMessage(msg) - // listen for incoming messages that need to be parsed - case rawEvent := <-rtm.rawEvents: + // listen for incoming messages that need to be parsed + case rawEvent := <-events: switch rtm.handleRawEvent(rawEvent) { case rtmEventTypeGoodbye: - _ = rtm.killConnection(false, errorsx.String("goodbye detected")) - return + // kill the connection, but DO NOT RETURN, a follow up kill signal will + // be sent that still needs to be processed. this duplication is because + // the event reader restarts once it emits the goodbye event. + // unlike the other cases in this function a final read will be triggered + // against the connection which will emit a kill signal. if we return early + // this kill signal will be processed by the next connection. + _ = rtm.killConnection(false, ErrRTMGoodbye) default: } } @@ -268,13 +277,17 @@ func (rtm *RTM) handleEvents() { } // handleIncomingEvents monitors the RTM's opened websocket for any incoming -// events. It pushes the raw events onto the RTM channel rawEvents. +// events. It pushes the raw events into the channel. // -// This will stop executing once the RTM's keepRunning channel has been closed -// or has anything sent to it. -func (rtm *RTM) handleIncomingEvents() { +// This will stop executing once the RTM's when a fatal error is detected, or +// a disconnect occurs. +func (rtm *RTM) handleIncomingEvents(events chan json.RawMessage) { for { - if err := rtm.receiveIncomingEvent(); err != nil { + if err := rtm.receiveIncomingEvent(events); err != nil { + select { + case rtm.killChannel <- false: + case <-rtm.disconnected: + } return } } @@ -336,9 +349,15 @@ func (rtm *RTM) ping() error { // receiveIncomingEvent attempts to receive an event from the RTM's websocket. // This will block until a frame is available from the websocket. // If the read from the websocket results in a fatal error, this function will return non-nil. -func (rtm *RTM) receiveIncomingEvent() error { +func (rtm *RTM) receiveIncomingEvent(events chan json.RawMessage) error { event := json.RawMessage{} err := rtm.conn.ReadJSON(&event) + + // check if the connection was closed. + if websocket.IsUnexpectedCloseError(err) { + return err + } + switch { case err == io.ErrUnexpectedEOF: // EOF's don't seem to signify a failed connection so instead we ignore @@ -357,22 +376,18 @@ func (rtm *RTM) receiveIncomingEvent() error { ErrorObj: err, }} - select { - case rtm.killChannel <- false: - case <-rtm.disconnected: - } - return err case len(event) == 0: rtm.Debugln("Received empty event") default: rtm.Debugln("Incoming Event:", string(event)) select { - case rtm.rawEvents <- event: + case events <- event: case <-rtm.disconnected: rtm.Debugln("disonnected while attempting to send raw event") } } + return nil } @@ -396,8 +411,6 @@ func (rtm *RTM) handleRawEvent(rawEvent json.RawMessage) string { rtm.handlePong(rawEvent) case rtmEventTypeGoodbye: // just return the event type up for goodbye, will be handled by caller. - case rtmEventTypeDesktopNotification: - rtm.Debugln("Received desktop notification, ignoring") default: rtm.handleEvent(event.Type, rawEvent) } @@ -559,8 +572,12 @@ var EventMapping = map[string]interface{}{ "member_joined_channel": MemberJoinedChannelEvent{}, "member_left_channel": MemberLeftChannelEvent{}, - "subteam_created": SubteamCreatedEvent{}, - "subteam_self_added": SubteamSelfAddedEvent{}, - "subteam_self_removed": SubteamSelfRemovedEvent{}, - "subteam_updated": SubteamUpdatedEvent{}, + "subteam_created": SubteamCreatedEvent{}, + "subteam_members_changed": SubteamMembersChangedEvent{}, + "subteam_self_added": SubteamSelfAddedEvent{}, + "subteam_self_removed": SubteamSelfRemovedEvent{}, + "subteam_updated": SubteamUpdatedEvent{}, + + "desktop_notification": DesktopNotificationEvent{}, + "mobile_in_app_notification": MobileInAppNotificationEvent{}, } diff --git a/vendor/github.com/nlopes/slack/websocket_misc.go b/vendor/github.com/slack-go/slack/websocket_misc.go similarity index 98% rename from vendor/github.com/nlopes/slack/websocket_misc.go rename to vendor/github.com/slack-go/slack/websocket_misc.go index bfcc805..65a8bb6 100644 --- a/vendor/github.com/nlopes/slack/websocket_misc.go +++ b/vendor/github.com/slack-go/slack/websocket_misc.go @@ -131,7 +131,7 @@ type MemberJoinedChannelEvent struct { Inviter string `json:"inviter"` } -// MemberJoinedChannelEvent, a user left a public or private channel +// MemberLeftChannelEvent a user left a public or private channel type MemberLeftChannelEvent struct { Type string `json:"type"` User string `json:"user"` 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 0000000..e3cfb3d --- /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"` +} diff --git a/vendor/github.com/nlopes/slack/websocket_pins.go b/vendor/github.com/slack-go/slack/websocket_pins.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_pins.go rename to vendor/github.com/slack-go/slack/websocket_pins.go diff --git a/vendor/github.com/nlopes/slack/websocket_reactions.go b/vendor/github.com/slack-go/slack/websocket_reactions.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_reactions.go rename to vendor/github.com/slack-go/slack/websocket_reactions.go diff --git a/vendor/github.com/nlopes/slack/websocket_stars.go b/vendor/github.com/slack-go/slack/websocket_stars.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_stars.go rename to vendor/github.com/slack-go/slack/websocket_stars.go diff --git a/vendor/github.com/nlopes/slack/websocket_subteam.go b/vendor/github.com/slack-go/slack/websocket_subteam.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_subteam.go rename to vendor/github.com/slack-go/slack/websocket_subteam.go diff --git a/vendor/github.com/nlopes/slack/websocket_teams.go b/vendor/github.com/slack-go/slack/websocket_teams.go similarity index 100% rename from vendor/github.com/nlopes/slack/websocket_teams.go rename to vendor/github.com/slack-go/slack/websocket_teams.go diff --git a/vendor/modules.txt b/vendor/modules.txt index 1425b13..7a6f9ad 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -29,7 +29,7 @@ github.com/golang/protobuf/proto github.com/golang/protobuf/ptypes/timestamp # github.com/google/uuid v1.1.1 github.com/google/uuid -# github.com/gorilla/websocket v1.4.1 +# github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket # github.com/hashicorp/golang-lru v0.5.3 github.com/hashicorp/golang-lru/simplelru @@ -63,17 +63,17 @@ github.com/nats-io/nuid # github.com/nats-io/stan.go v0.5.0 github.com/nats-io/stan.go github.com/nats-io/stan.go/pb -# github.com/nlopes/slack v0.6.0 -github.com/nlopes/slack -github.com/nlopes/slack/internal/errorsx -github.com/nlopes/slack/internal/timex -github.com/nlopes/slack/slackutilsx # github.com/pelletier/go-toml v1.5.0 github.com/pelletier/go-toml # github.com/pkg/errors v0.8.1 github.com/pkg/errors # github.com/sirupsen/logrus v1.4.2 github.com/sirupsen/logrus +# github.com/slack-go/slack v0.6.6 +github.com/slack-go/slack +github.com/slack-go/slack/internal/errorsx +github.com/slack-go/slack/internal/timex +github.com/slack-go/slack/slackutilsx # github.com/spf13/afero v1.2.2 github.com/spf13/afero github.com/spf13/afero/mem