diff --git a/bridge/slack/slack.go b/bridge/slack/slack.go index a38bbb53..9b64f7a4 100644 --- a/bridge/slack/slack.go +++ b/bridge/slack/slack.go @@ -303,12 +303,13 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) { return msg.ID, err } - messageParameters := b.prepareMessageParameters(&msg) - // Upload a file if it exists. if msg.Extra != nil { - for _, rmsg := range helper.HandleExtra(&msg, b.General) { - _, _, err = b.rtm.PostMessage(channelInfo.ID, rmsg.Username+rmsg.Text, *messageParameters) + extraMsgs := helper.HandleExtra(&msg, b.General) + for i := range extraMsgs { + rmsg := &extraMsgs[i] + rmsg.Text = rmsg.Username + rmsg.Text + _, err = b.postMessage(rmsg, channelInfo) if err != nil { b.Log.Error(err) } @@ -318,7 +319,7 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) { } // Post message. - return b.postMessage(&msg, messageParameters, channelInfo) + return b.postMessage(&msg, channelInfo) } func (b *Bslack) updateTopicOrPurpose(msg *config.Message, channelInfo *slack.Channel) (bool, error) { @@ -391,9 +392,10 @@ func (b *Bslack) editMessage(msg *config.Message, channelInfo *slack.Channel) (b if msg.ID == "" { return false, nil } - + messageOptions := b.prepareMessageOptions(msg) for { - _, _, _, err := b.rtm.UpdateMessage(channelInfo.ID, msg.ID, msg.Text) + messageOptions = append(messageOptions, slack.MsgOptionText(msg.Text, false)) + _, _, _, err := b.rtm.UpdateMessage(channelInfo.ID, msg.ID, messageOptions...) if err == nil { return true, nil } @@ -405,13 +407,15 @@ func (b *Bslack) editMessage(msg *config.Message, channelInfo *slack.Channel) (b } } -func (b *Bslack) postMessage(msg *config.Message, messageParameters *slack.PostMessageParameters, channelInfo *slack.Channel) (string, error) { +func (b *Bslack) postMessage(msg *config.Message, channelInfo *slack.Channel) (string, error) { // don't post empty messages if msg.Text == "" { return "", nil } + messageOptions := b.prepareMessageOptions(msg) + messageOptions = append(messageOptions, slack.MsgOptionText(msg.Text, false)) for { - _, id, err := b.rtm.PostMessage(channelInfo.ID, msg.Text, *messageParameters) + _, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...) if err == nil { return id, nil } @@ -461,7 +465,7 @@ func (b *Bslack) uploadFile(msg *config.Message, channelID string) { } } -func (b *Bslack) prepareMessageParameters(msg *config.Message) *slack.PostMessageParameters { +func (b *Bslack) prepareMessageOptions(msg *config.Message) []slack.MsgOption { params := slack.NewPostMessageParameters() if b.GetBool(useNickPrefixConfig) { params.AsUser = true @@ -473,17 +477,23 @@ func (b *Bslack) prepareMessageParameters(msg *config.Message) *slack.PostMessag if msg.Avatar != "" { params.IconURL = msg.Avatar } + + var attachments []slack.Attachment // add a callback ID so we can see we created it - params.Attachments = append(params.Attachments, slack.Attachment{CallbackID: "matterbridge_" + b.uuid}) + attachments = append(attachments, slack.Attachment{CallbackID: "matterbridge_" + b.uuid}) // add file attachments - params.Attachments = append(params.Attachments, b.createAttach(msg.Extra)...) + attachments = append(attachments, b.createAttach(msg.Extra)...) // add slack attachments (from another slack bridge) if msg.Extra != nil { for _, attach := range msg.Extra[sSlackAttachment] { - params.Attachments = append(params.Attachments, attach.([]slack.Attachment)...) + attachments = append(attachments, attach.([]slack.Attachment)...) } } - return ¶ms + + var opts []slack.MsgOption + opts = append(opts, slack.MsgOptionAttachments(attachments...)) + opts = append(opts, slack.MsgOptionPostMessageParameters(params)) + return opts } func (b *Bslack) createAttach(extra map[string][]interface{}) []slack.Attachment { diff --git a/go.mod b/go.mod index 442e97c5..99041ada 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect github.com/nicksnyder/go-i18n v1.4.0 // indirect - github.com/nlopes/slack v0.4.0 + github.com/nlopes/slack v0.4.1-0.20181111125009-5963eafd777b github.com/onsi/ginkgo v1.6.0 // indirect github.com/onsi/gomega v1.4.1 // indirect github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83 diff --git a/go.sum b/go.sum index 96c12043..8f341afb 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,8 @@ github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff h1:HLGD5/9UxxfEuO9Dt github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff/go.mod h1:B8jLfIIPn2sKyWr0D7cL2v7tnrDD5z291s2Zypdu89E= github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I= github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= -github.com/nlopes/slack v0.4.0 h1:OVnHm7lv5gGT5gkcHsZAyw++oHVFihbjWbL3UceUpiA= -github.com/nlopes/slack v0.4.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= +github.com/nlopes/slack v0.4.1-0.20181111125009-5963eafd777b h1:8ncrr7Xps0GafXIxBzrq1qSjy1zhiCDp/9C4cOrE+GU= +github.com/nlopes/slack v0.4.1-0.20181111125009-5963eafd777b/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= diff --git a/vendor/github.com/nlopes/slack/.travis.yml b/vendor/github.com/nlopes/slack/.travis.yml index bd0539e0..b0615288 100644 --- a/vendor/github.com/nlopes/slack/.travis.yml +++ b/vendor/github.com/nlopes/slack/.travis.yml @@ -4,6 +4,8 @@ go: - 1.7.x - 1.8.x - 1.9.x + - 1.10.x + - 1.11.x - tip before_install: diff --git a/vendor/github.com/nlopes/slack/admin.go b/vendor/github.com/nlopes/slack/admin.go index a2aa7e5c..db44aa38 100644 --- a/vendor/github.com/nlopes/slack/admin.go +++ b/vendor/github.com/nlopes/slack/admin.go @@ -12,9 +12,9 @@ type adminResponse struct { Error string `json:"error"` } -func adminRequest(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, debug bool) (*adminResponse, error) { +func adminRequest(ctx context.Context, client httpClient, method string, teamName string, values url.Values, d debug) (*adminResponse, error) { adminResponse := &adminResponse{} - err := parseAdminResponse(ctx, client, method, teamName, values, adminResponse, debug) + err := parseAdminResponse(ctx, client, method, teamName, values, adminResponse, d) if err != nil { return nil, err } @@ -40,7 +40,7 @@ func (api *Client) DisableUserContext(ctx context.Context, teamName string, uid "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "setInactive", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "setInactive", teamName, values, api) if err != nil { return fmt.Errorf("Failed to disable user with id '%s': %s", uid, err) } @@ -67,7 +67,7 @@ func (api *Client) InviteGuestContext(ctx context.Context, teamName, channel, fi "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api) if err != nil { return fmt.Errorf("Failed to invite single-channel guest: %s", err) } @@ -94,7 +94,7 @@ func (api *Client) InviteRestrictedContext(ctx context.Context, teamName, channe "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api) if err != nil { return fmt.Errorf("Failed to restricted account: %s", err) } @@ -118,7 +118,7 @@ func (api *Client) InviteToTeamContext(ctx context.Context, teamName, firstName, "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api) if err != nil { return fmt.Errorf("Failed to invite to team: %s", err) } @@ -140,7 +140,7 @@ func (api *Client) SetRegularContext(ctx context.Context, teamName, user string) "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "setRegular", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "setRegular", teamName, values, api) if err != nil { return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err) } @@ -162,7 +162,7 @@ func (api *Client) SendSSOBindingEmailContext(ctx context.Context, teamName, use "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "sendSSOBind", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "sendSSOBind", teamName, values, api) if err != nil { return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err) } @@ -185,7 +185,7 @@ func (api *Client) SetUltraRestrictedContext(ctx context.Context, teamName, uid, "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "setUltraRestricted", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "setUltraRestricted", teamName, values, api) if err != nil { return fmt.Errorf("Failed to ultra-restrict account: %s", err) } @@ -207,7 +207,7 @@ func (api *Client) SetRestrictedContext(ctx context.Context, teamName, uid strin "_attempts": {"1"}, } - _, err := adminRequest(ctx, api.httpclient, "setRestricted", teamName, values, api.debug) + _, err := adminRequest(ctx, api.httpclient, "setRestricted", teamName, values, api) if err != nil { return fmt.Errorf("Failed to restrict account: %s", err) } diff --git a/vendor/github.com/nlopes/slack/attachments.go b/vendor/github.com/nlopes/slack/attachments.go index 326fc010..06f59fa3 100644 --- a/vendor/github.com/nlopes/slack/attachments.go +++ b/vendor/github.com/nlopes/slack/attachments.go @@ -42,24 +42,14 @@ type AttachmentActionOptionGroup struct { } // AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction) -type AttachmentActionCallback struct { - Actions []AttachmentAction `json:"actions"` - CallbackID string `json:"callback_id"` - Team Team `json:"team"` - Channel Channel `json:"channel"` - User User `json:"user"` +// DEPRECATED: use InteractionCallback +type AttachmentActionCallback InteractionCallback - Name string `json:"name"` - Value string `json:"value"` - - OriginalMessage Message `json:"original_message"` - - ActionTs string `json:"action_ts"` - MessageTs string `json:"message_ts"` - AttachmentID string `json:"attachment_id"` - Token string `json:"token"` - ResponseURL string `json:"response_url"` - TriggerID string `json:"trigger_id"` +// ActionCallback specific fields for the action callback. +type ActionCallback struct { + MessageTs string `json:"message_ts"` + AttachmentID string `json:"attachment_id"` + Actions []AttachmentAction `json:"actions"` } // ConfirmationField are used to ask users to confirm actions diff --git a/vendor/github.com/nlopes/slack/auth.go b/vendor/github.com/nlopes/slack/auth.go new file mode 100644 index 00000000..f8fe1f9d --- /dev/null +++ b/vendor/github.com/nlopes/slack/auth.go @@ -0,0 +1,40 @@ +package slack + +import ( + "context" + "net/url" +) + +// AuthRevokeResponse contains our Auth response from the auth.revoke endpoint +type AuthRevokeResponse struct { + SlackResponse // Contains the "ok", and "Error", if any + Revoked bool `json:"revoked,omitempty"` +} + +// authRequest sends the actual request, and unmarshals the response +func authRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*AuthRevokeResponse, error) { + response := &AuthRevokeResponse{} + err := postSlackMethod(ctx, client, path, values, response, d) + if err != nil { + return nil, err + } + + return response, response.Err() +} + +// SendAuthRevoke will send a revocation for our token +func (api *Client) SendAuthRevoke(token string) (*AuthRevokeResponse, error) { + return api.SendAuthRevokeContext(context.Background(), token) +} + +// SendAuthRevokeContext will retrieve the satus from api.test +func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*AuthRevokeResponse, error) { + if token == "" { + token = api.token + } + values := url.Values{ + "token": {token}, + } + + return authRequest(ctx, api.httpclient, "auth.revoke", values, api) +} diff --git a/vendor/github.com/nlopes/slack/bots.go b/vendor/github.com/nlopes/slack/bots.go index 92570a04..e27e76ab 100644 --- a/vendor/github.com/nlopes/slack/bots.go +++ b/vendor/github.com/nlopes/slack/bots.go @@ -19,9 +19,9 @@ type botResponseFull struct { SlackResponse } -func botRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*botResponseFull, error) { +func botRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*botResponseFull, error) { response := &botResponseFull{} - err := postSlackMethod(ctx, client, path, values, response, debug) + err := postSlackMethod(ctx, client, path, values, response, d) if err != nil { return nil, err } @@ -43,7 +43,7 @@ func (api *Client) GetBotInfoContext(ctx context.Context, bot string) (*Bot, err "bot": {bot}, } - response, err := botRequest(ctx, api.httpclient, "bots.info", values, api.debug) + response, err := botRequest(ctx, api.httpclient, "bots.info", values, api) if err != nil { return nil, err } diff --git a/vendor/github.com/nlopes/slack/channels.go b/vendor/github.com/nlopes/slack/channels.go index 6204315a..007985bc 100644 --- a/vendor/github.com/nlopes/slack/channels.go +++ b/vendor/github.com/nlopes/slack/channels.go @@ -26,9 +26,9 @@ type Channel struct { Locale string `json:"locale"` } -func channelRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*channelResponseFull, error) { +func channelRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*channelResponseFull, error) { response := &channelResponseFull{} - err := postForm(ctx, client, SLACK_API+path, values, response, debug) + err := postForm(ctx, client, APIURL+path, values, response, d) if err != nil { return nil, err } @@ -52,7 +52,7 @@ func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string) "channel": {channelID}, } - _, err = channelRequest(ctx, api.httpclient, "channels.archive", values, api.debug) + _, err = channelRequest(ctx, api.httpclient, "channels.archive", values, api) return err } @@ -70,7 +70,7 @@ func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string "channel": {channelID}, } - _, err = channelRequest(ctx, api.httpclient, "channels.unarchive", values, api.debug) + _, err = channelRequest(ctx, api.httpclient, "channels.unarchive", values, api) return err } @@ -88,7 +88,7 @@ func (api *Client) CreateChannelContext(ctx context.Context, channelName string) "name": {channelName}, } - response, err := channelRequest(ctx, api.httpclient, "channels.create", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.create", values, api) if err != nil { return nil, err } @@ -133,7 +133,7 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID strin } } - response, err := channelRequest(ctx, api.httpclient, "channels.history", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.history", values, api) if err != nil { return nil, err } @@ -154,7 +154,7 @@ func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string) "channel": {channelID}, } - response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api) if err != nil { return nil, err } @@ -167,7 +167,7 @@ func (api *Client) InviteUserToChannel(channelID, user string) (*Channel, error) return api.InviteUserToChannelContext(context.Background(), channelID, user) } -// InviteUserToChannelCustom invites a user to a given channel and returns a *Channel with a custom context +// InviteUserToChannelContext invites a user to a given channel and returns a *Channel with a custom context // see https://api.slack.com/methods/channels.invite func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) { values := url.Values{ @@ -176,7 +176,7 @@ func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, us "user": {user}, } - response, err := channelRequest(ctx, api.httpclient, "channels.invite", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.invite", values, api) if err != nil { return nil, err } @@ -197,7 +197,7 @@ func (api *Client) JoinChannelContext(ctx context.Context, channelName string) ( "name": {channelName}, } - response, err := channelRequest(ctx, api.httpclient, "channels.join", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.join", values, api) if err != nil { return nil, err } @@ -218,7 +218,7 @@ func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (b "channel": {channelID}, } - response, err := channelRequest(ctx, api.httpclient, "channels.leave", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.leave", values, api) if err != nil { return false, err } @@ -241,7 +241,7 @@ func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, us "user": {user}, } - _, err = channelRequest(ctx, api.httpclient, "channels.kick", values, api.debug) + _, err = channelRequest(ctx, api.httpclient, "channels.kick", values, api) return err } @@ -261,7 +261,7 @@ func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool) values.Add("exclude_archived", "1") } - response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api) if err != nil { return nil, err } @@ -288,7 +288,7 @@ func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts "ts": {ts}, } - _, err = channelRequest(ctx, api.httpclient, "channels.mark", values, api.debug) + _, err = channelRequest(ctx, api.httpclient, "channels.mark", values, api) return err } @@ -309,7 +309,7 @@ func (api *Client) RenameChannelContext(ctx context.Context, channelID, name str // XXX: the created entry in this call returns a string instead of a number // so I may have to do some workaround to solve it. - response, err := channelRequest(ctx, api.httpclient, "channels.rename", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.rename", values, api) if err != nil { return nil, err } @@ -331,7 +331,7 @@ func (api *Client) SetChannelPurposeContext(ctx context.Context, channelID, purp "purpose": {purpose}, } - response, err := channelRequest(ctx, api.httpclient, "channels.setPurpose", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.setPurpose", values, api) if err != nil { return "", err } @@ -353,7 +353,7 @@ func (api *Client) SetChannelTopicContext(ctx context.Context, channelID, topic "topic": {topic}, } - response, err := channelRequest(ctx, api.httpclient, "channels.setTopic", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.setTopic", values, api) if err != nil { return "", err } @@ -374,7 +374,7 @@ func (api *Client) GetChannelRepliesContext(ctx context.Context, channelID, thre "channel": {channelID}, "thread_ts": {thread_ts}, } - response, err := channelRequest(ctx, api.httpclient, "channels.replies", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "channels.replies", values, api) if err != nil { return nil, err } diff --git a/vendor/github.com/nlopes/slack/chat.go b/vendor/github.com/nlopes/slack/chat.go index 8cc6bdef..eb8fd6c8 100644 --- a/vendor/github.com/nlopes/slack/chat.go +++ b/vendor/github.com/nlopes/slack/chat.go @@ -96,26 +96,24 @@ func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTim // PostMessage 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) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) { +func (api *Client) PostMessage(channelID string, options ...MsgOption) (string, string, error) { respChannel, respTimestamp, _, err := api.SendMessageContext( context.Background(), - channel, - MsgOptionText(text, params.EscapeText), - MsgOptionAttachments(params.Attachments...), - MsgOptionPostMessageParameters(params), + channelID, + MsgOptionPost(), + MsgOptionCompose(options...), ) return respChannel, respTimestamp, err } // PostMessageContext sends a message to a channel with a custom context -// For more details, see PostMessage documentation -func (api *Client) PostMessageContext(ctx context.Context, channel, text string, params PostMessageParameters) (string, string, error) { +// For more details, see PostMessage documentation. +func (api *Client) PostMessageContext(ctx context.Context, channelID string, options ...MsgOption) (string, string, error) { respChannel, respTimestamp, _, err := api.SendMessageContext( ctx, - channel, - MsgOptionText(text, params.EscapeText), - MsgOptionAttachments(params.Attachments...), - MsgOptionPostMessageParameters(params), + channelID, + MsgOptionPost(), + MsgOptionCompose(options...), ) return respChannel, respTimestamp, err } @@ -135,18 +133,23 @@ 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, append(options, MsgOptionPostEphemeral2(userID))...) + _, 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, text string) (string, string, string, error) { - return api.UpdateMessageContext(context.Background(), channelID, timestamp, text) +func (api *Client) UpdateMessage(channelID, timestamp string, options ...MsgOption) (string, string, string, error) { + 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, text string) (string, string, string, error) { - return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionText(text, true)) +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...)) +} + +// UnfurlMessage unfurls a message in a channel +func (api *Client) UnfurlMessage(channelID, timestamp string, unfurls map[string]Attachment, options ...MsgOption) (string, string, string, error) { + return api.SendMessageContext(context.Background(), channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...)) } // SendMessage more flexible method for configuring messages. @@ -165,7 +168,7 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt return "", "", "", err } - if err = postForm(ctx, api.httpclient, config.endpoint, config.values, &response, api.debug); err != nil { + if err = postForm(ctx, api.httpclient, config.endpoint, config.values, &response, api); err != nil { return "", "", "", err } @@ -182,7 +185,7 @@ func UnsafeApplyMsgOptions(token, channel string, options ...MsgOption) (string, func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) { config := sendConfig{ - endpoint: SLACK_API + string(chatPostMessage), + endpoint: APIURL + string(chatPostMessage), values: url.Values{ "token": {token}, "channel": {channel}, @@ -206,6 +209,7 @@ const ( chatDelete sendMode = "chat.delete" chatPostEphemeral sendMode = "chat.postEphemeral" chatMeMessage sendMode = "chat.meMessage" + chatUnfurl sendMode = "chat.unfurl" ) type sendConfig struct { @@ -219,26 +223,16 @@ type MsgOption func(*sendConfig) error // MsgOptionPost posts a messages, this is the default. func MsgOptionPost() MsgOption { return func(config *sendConfig) error { - config.endpoint = SLACK_API + string(chatPostMessage) + config.endpoint = APIURL + string(chatPostMessage) config.values.Del("ts") return nil } } -// MsgOptionPostEphemeral - DEPRECATED: use MsgOptionPostEphemeral2 -// posts an ephemeral message. -func MsgOptionPostEphemeral() MsgOption { +// MsgOptionPostEphemeral - posts an ephemeral message to the provided user. +func MsgOptionPostEphemeral(userID string) MsgOption { return func(config *sendConfig) error { - config.endpoint = SLACK_API + string(chatPostEphemeral) - config.values.Del("ts") - return nil - } -} - -// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user. -func MsgOptionPostEphemeral2(userID string) MsgOption { - return func(config *sendConfig) error { - config.endpoint = SLACK_API + string(chatPostEphemeral) + config.endpoint = APIURL + string(chatPostEphemeral) MsgOptionUser(userID)(config) config.values.Del("ts") @@ -249,7 +243,7 @@ func MsgOptionPostEphemeral2(userID string) MsgOption { // MsgOptionMeMessage posts a "me message" type from the calling user func MsgOptionMeMessage() MsgOption { return func(config *sendConfig) error { - config.endpoint = SLACK_API + string(chatMeMessage) + config.endpoint = APIURL + string(chatMeMessage) return nil } } @@ -257,7 +251,7 @@ func MsgOptionMeMessage() MsgOption { // MsgOptionUpdate updates a message based on the timestamp. func MsgOptionUpdate(timestamp string) MsgOption { return func(config *sendConfig) error { - config.endpoint = SLACK_API + string(chatUpdate) + config.endpoint = APIURL + string(chatUpdate) config.values.Add("ts", timestamp) return nil } @@ -266,12 +260,25 @@ func MsgOptionUpdate(timestamp string) MsgOption { // MsgOptionDelete deletes a message based on the timestamp. func MsgOptionDelete(timestamp string) MsgOption { return func(config *sendConfig) error { - config.endpoint = SLACK_API + string(chatDelete) + config.endpoint = APIURL + string(chatDelete) config.values.Add("ts", timestamp) return nil } } +// MsgOptionUnfurl unfurls a message based on the timestamp. +func MsgOptionUnfurl(timestamp string, unfurls map[string]Attachment) MsgOption { + return func(config *sendConfig) error { + config.endpoint = APIURL + string(chatUnfurl) + config.values.Add("ts", timestamp) + unfurlsStr, err := json.Marshal(unfurls) + if err == nil { + config.values.Add("unfurls", string(unfurlsStr)) + } + return err + } +} + // MsgOptionAsUser whether or not to send the message as the user. func MsgOptionAsUser(b bool) MsgOption { return func(config *sendConfig) error { @@ -290,6 +297,14 @@ func MsgOptionUser(userID string) MsgOption { } } +// MsgOptionUsername set the username for the message. +func MsgOptionUsername(username string) MsgOption { + return func(config *sendConfig) error { + config.values.Set("username", username) + return nil + } +} + // MsgOptionText provide the text for the message, optionally escape the provided // text. func MsgOptionText(text string, escape bool) MsgOption { @@ -365,7 +380,7 @@ func MsgOptionBroadcast() MsgOption { } } -// this function combines multiple options into a single option. +// MsgOptionCompose combines multiple options into a single option. func MsgOptionCompose(options ...MsgOption) MsgOption { return func(c *sendConfig) error { for _, opt := range options { @@ -377,6 +392,7 @@ func MsgOptionCompose(options ...MsgOption) MsgOption { } } +// MsgOptionParse set parse option. func MsgOptionParse(b bool) MsgOption { return func(c *sendConfig) error { var v string @@ -456,3 +472,38 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption { return nil } } + +// PermalinkParameters are the parameters required to get a permalink to a +// message. Slack documentation can be found here: +// https://api.slack.com/methods/chat.getPermalink +type PermalinkParameters struct { + Channel string + Ts string +} + +// GetPermalink returns the permalink for a message. It takes +// PermalinkParameters and returns a string containing the permalink. It +// returns an error if unable to retrieve the permalink. +func (api *Client) GetPermalink(params *PermalinkParameters) (string, error) { + return api.GetPermalinkContext(context.Background(), params) +} + +// GetPermalinkContext returns the permalink for a message using a custom context. +func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkParameters) (string, error) { + values := url.Values{ + "token": {api.token}, + "channel": {params.Channel}, + "message_ts": {params.Ts}, + } + + response := struct { + Channel string `json:"channel"` + Permalink string `json:"permalink"` + SlackResponse + }{} + err := getSlackMethod(ctx, api.httpclient, "chat.getPermalink", values, &response, api) + if err != nil { + return "", err + } + return response.Permalink, response.Err() +} diff --git a/vendor/github.com/nlopes/slack/conversation.go b/vendor/github.com/nlopes/slack/conversation.go index 1c64116e..ccd38f88 100644 --- a/vendor/github.com/nlopes/slack/conversation.go +++ b/vendor/github.com/nlopes/slack/conversation.go @@ -99,7 +99,7 @@ func (api *Client) GetUsersInConversationContext(ctx context.Context, params *Ge ResponseMetaData responseMetaData `json:"response_metadata"` SlackResponse }{} - err := postSlackMethod(ctx, api.httpclient, "conversations.members", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.members", values, &response, api) if err != nil { return nil, "", err } @@ -118,7 +118,9 @@ func (api *Client) GetConversationsForUser(params *GetConversationsForUserParame func (api *Client) GetConversationsForUserContext(ctx context.Context, params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) { values := url.Values{ "token": {api.token}, - "user": {params.UserID}, + } + if params.UserID != "" { + values.Add("user", params.UserID) } if params.Cursor != "" { values.Add("cursor", params.Cursor) @@ -134,14 +136,12 @@ func (api *Client) GetConversationsForUserContext(ctx context.Context, params *G ResponseMetaData responseMetaData `json:"response_metadata"` SlackResponse }{} - err = postSlackMethod(ctx, api.httpclient, "users.conversations", values, &response, api.debug) + err = postSlackMethod(ctx, api.httpclient, "users.conversations", values, &response, api) if err != nil { return nil, "", err } - if !response.Ok { - return nil, "", errors.New(response.Error) - } - return response.Channels, response.ResponseMetaData.NextCursor, nil + + return response.Channels, response.ResponseMetaData.NextCursor, response.Err() } // ArchiveConversation archives a conversation @@ -156,7 +156,7 @@ func (api *Client) ArchiveConversationContext(ctx context.Context, channelID str "channel": {channelID}, } response := SlackResponse{} - err := postSlackMethod(ctx, api.httpclient, "conversations.archive", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.archive", values, &response, api) if err != nil { return err } @@ -176,7 +176,7 @@ func (api *Client) UnArchiveConversationContext(ctx context.Context, channelID s "channel": {channelID}, } response := SlackResponse{} - err := postSlackMethod(ctx, api.httpclient, "conversations.unarchive", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.unarchive", values, &response, api) if err != nil { return err } @@ -200,7 +200,7 @@ func (api *Client) SetTopicOfConversationContext(ctx context.Context, channelID, SlackResponse Channel *Channel `json:"channel"` }{} - err := postSlackMethod(ctx, api.httpclient, "conversations.setTopic", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.setTopic", values, &response, api) if err != nil { return nil, err } @@ -224,7 +224,7 @@ func (api *Client) SetPurposeOfConversationContext(ctx context.Context, channelI SlackResponse Channel *Channel `json:"channel"` }{} - err := postSlackMethod(ctx, api.httpclient, "conversations.setPurpose", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.setPurpose", values, &response, api) if err != nil { return nil, err } @@ -248,7 +248,7 @@ func (api *Client) RenameConversationContext(ctx context.Context, channelID, cha SlackResponse Channel *Channel `json:"channel"` }{} - err := postSlackMethod(ctx, api.httpclient, "conversations.rename", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.rename", values, &response, api) if err != nil { return nil, err } @@ -272,7 +272,7 @@ func (api *Client) InviteUsersToConversationContext(ctx context.Context, channel SlackResponse Channel *Channel `json:"channel"` }{} - err := postSlackMethod(ctx, api.httpclient, "conversations.invite", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.invite", values, &response, api) if err != nil { return nil, err } @@ -293,7 +293,7 @@ func (api *Client) KickUserFromConversationContext(ctx context.Context, channelI "user": {user}, } response := SlackResponse{} - err := postSlackMethod(ctx, api.httpclient, "conversations.kick", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.kick", values, &response, api) if err != nil { return err } @@ -318,7 +318,7 @@ func (api *Client) CloseConversationContext(ctx context.Context, channelID strin AlreadyClosed bool `json:"already_closed"` }{} - err = postSlackMethod(ctx, api.httpclient, "conversations.close", values, &response, api.debug) + err = postSlackMethod(ctx, api.httpclient, "conversations.close", values, &response, api) if err != nil { return false, false, err } @@ -339,7 +339,7 @@ func (api *Client) CreateConversationContext(ctx context.Context, channelName st "is_private": {strconv.FormatBool(isPrivate)}, } response, err := channelRequest( - ctx, api.httpclient, "conversations.create", values, api.debug) + ctx, api.httpclient, "conversations.create", values, api) if err != nil { return nil, err } @@ -360,7 +360,7 @@ func (api *Client) GetConversationInfoContext(ctx context.Context, channelID str "include_locale": {strconv.FormatBool(includeLocale)}, } response, err := channelRequest( - ctx, api.httpclient, "conversations.info", values, api.debug) + ctx, api.httpclient, "conversations.info", values, api) if err != nil { return nil, err } @@ -380,7 +380,7 @@ func (api *Client) LeaveConversationContext(ctx context.Context, channelID strin "channel": {channelID}, } - response, err := channelRequest(ctx, api.httpclient, "conversations.leave", values, api.debug) + response, err := channelRequest(ctx, api.httpclient, "conversations.leave", values, api) if err != nil { return false, err } @@ -436,7 +436,7 @@ func (api *Client) GetConversationRepliesContext(ctx context.Context, params *Ge Messages []Message `json:"messages"` }{} - err = postSlackMethod(ctx, api.httpclient, "conversations.replies", values, &response, api.debug) + err = postSlackMethod(ctx, api.httpclient, "conversations.replies", values, &response, api) if err != nil { return nil, false, "", err } @@ -476,7 +476,7 @@ func (api *Client) GetConversationsContext(ctx context.Context, params *GetConve ResponseMetaData responseMetaData `json:"response_metadata"` SlackResponse }{} - err = postSlackMethod(ctx, api.httpclient, "conversations.list", values, &response, api.debug) + err = postSlackMethod(ctx, api.httpclient, "conversations.list", values, &response, api) if err != nil { return nil, "", err } @@ -513,7 +513,7 @@ func (api *Client) OpenConversationContext(ctx context.Context, params *OpenConv AlreadyOpen bool `json:"already_open"` SlackResponse }{} - err := postSlackMethod(ctx, api.httpclient, "conversations.open", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.open", values, &response, api) if err != nil { return nil, false, false, err } @@ -537,7 +537,7 @@ func (api *Client) JoinConversationContext(ctx context.Context, channelID string } `json:"response_metadata"` SlackResponse }{} - err := postSlackMethod(ctx, api.httpclient, "conversations.join", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.join", values, &response, api) if err != nil { return nil, "", nil, err } @@ -599,12 +599,10 @@ func (api *Client) GetConversationHistoryContext(ctx context.Context, params *Ge response := GetConversationHistoryResponse{} - err := postSlackMethod(ctx, api.httpclient, "conversations.history", values, &response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "conversations.history", values, &response, api) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return &response, nil + + return &response, response.Err() } diff --git a/vendor/github.com/nlopes/slack/dialog.go b/vendor/github.com/nlopes/slack/dialog.go index d1435d54..2e45a06c 100644 --- a/vendor/github.com/nlopes/slack/dialog.go +++ b/vendor/github.com/nlopes/slack/dialog.go @@ -36,8 +36,9 @@ type DialogTrigger struct { // Dialog as in Slack dialogs // https://api.slack.com/dialogs#option_element_attributes#top-level_dialog_attributes type Dialog struct { - TriggerID string `json:"trigger_id"` //Required - CallbackID string `json:"callback_id"` //Required + TriggerID string `json:"trigger_id"` // Required + CallbackID string `json:"callback_id"` // Required + State string `json:"state,omitempty"` // Optional Title string `json:"title"` SubmitLabel string `json:"submit_label,omitempty"` NotifyOnCancel bool `json:"notify_on_cancel"` @@ -47,30 +48,13 @@ type Dialog struct { // DialogElement abstract type for dialogs. type DialogElement interface{} -// DialogCallback is sent from Slack when a user submits a form from within a dialog -type DialogCallback struct { - Type string `json:"type"` - CallbackID string `json:"callback_id"` - Team Team `json:"team"` - Channel Channel `json:"channel"` - User User `json:"user"` - ActionTs string `json:"action_ts"` - Token string `json:"token"` - ResponseURL string `json:"response_url"` - Submission map[string]string `json:"submission"` -} +// DialogCallback DEPRECATED use InteractionCallback +type DialogCallback InteractionCallback -// DialogSuggestionCallback is sent from Slack when a user types in a select field with an external data source -type DialogSuggestionCallback struct { - Type string `json:"type"` - Token string `json:"token"` - ActionTs string `json:"action_ts"` - Team Team `json:"team"` - User User `json:"user"` - Channel Channel `json:"channel"` - ElementName string `json:"name"` - Value string `json:"value"` - CallbackID string `json:"callback_id"` +// DialogSubmissionCallback is sent from Slack when a user submits a form from within a dialog +type DialogSubmissionCallback struct { + State string `json:"state,omitempty"` + Submission map[string]string `json:"submission"` } // DialogOpenResponse response from `dialog.open` @@ -108,8 +92,8 @@ func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dial } response := &DialogOpenResponse{} - endpoint := SLACK_API + "dialog.open" - if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api.debug); err != nil { + endpoint := APIURL + "dialog.open" + if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api); err != nil { return err } diff --git a/vendor/github.com/nlopes/slack/dialog_select.go b/vendor/github.com/nlopes/slack/dialog_select.go index cff35479..ea95ccfa 100644 --- a/vendor/github.com/nlopes/slack/dialog_select.go +++ b/vendor/github.com/nlopes/slack/dialog_select.go @@ -24,6 +24,7 @@ type DialogInputSelect struct { SelectedOptions string `json:"selected_options,omitempty"` //Optional. Default value for "external" only Options []DialogSelectOption `json:"options,omitempty"` //One of options or option_groups is required. OptionGroups []DialogOptionGroup `json:"option_groups,omitempty"` //Provide up to 100 options. + MinQueryLength int `json:"min_query_length,omitempty"` //Optional. minimum characters before query is sent. } // DialogSelectOption is an option for the user to select from the menu diff --git a/vendor/github.com/nlopes/slack/dnd.go b/vendor/github.com/nlopes/slack/dnd.go index 26d36d6a..da6e4a16 100644 --- a/vendor/github.com/nlopes/slack/dnd.go +++ b/vendor/github.com/nlopes/slack/dnd.go @@ -36,9 +36,9 @@ type dndTeamInfoResponse struct { SlackResponse } -func dndRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*dndResponseFull, error) { +func dndRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*dndResponseFull, error) { response := &dndResponseFull{} - err := postSlackMethod(ctx, client, path, values, response, debug) + err := postSlackMethod(ctx, client, path, values, response, d) if err != nil { return nil, err } @@ -61,7 +61,7 @@ func (api *Client) EndDNDContext(ctx context.Context) error { response := &SlackResponse{} - if err := postSlackMethod(ctx, api.httpclient, "dnd.endDnd", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "dnd.endDnd", values, response, api); err != nil { return err } @@ -79,7 +79,7 @@ func (api *Client) EndSnoozeContext(ctx context.Context) (*DNDStatus, error) { "token": {api.token}, } - response, err := dndRequest(ctx, api.httpclient, "dnd.endSnooze", values, api.debug) + response, err := dndRequest(ctx, api.httpclient, "dnd.endSnooze", values, api) if err != nil { return nil, err } @@ -100,7 +100,7 @@ func (api *Client) GetDNDInfoContext(ctx context.Context, user *string) (*DNDSta values.Set("user", *user) } - response, err := dndRequest(ctx, api.httpclient, "dnd.info", values, api.debug) + response, err := dndRequest(ctx, api.httpclient, "dnd.info", values, api) if err != nil { return nil, err } @@ -120,11 +120,12 @@ func (api *Client) GetDNDTeamInfoContext(ctx context.Context, users []string) (m } response := &dndTeamInfoResponse{} - if err := postSlackMethod(ctx, api.httpclient, "dnd.teamInfo", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "dnd.teamInfo", values, response, api); err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) + + if response.Err() != nil { + return nil, response.Err() } return response.Users, nil } @@ -144,7 +145,7 @@ func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatu "num_minutes": {strconv.Itoa(minutes)}, } - response, err := dndRequest(ctx, api.httpclient, "dnd.setSnooze", values, api.debug) + response, err := dndRequest(ctx, api.httpclient, "dnd.setSnooze", values, api) if err != nil { return nil, err } diff --git a/vendor/github.com/nlopes/slack/emoji.go b/vendor/github.com/nlopes/slack/emoji.go index fe2945c4..aed2129f 100644 --- a/vendor/github.com/nlopes/slack/emoji.go +++ b/vendor/github.com/nlopes/slack/emoji.go @@ -23,7 +23,7 @@ func (api *Client) GetEmojiContext(ctx context.Context) (map[string]string, erro } response := &emojiResponseFull{} - err := postSlackMethod(ctx, api.httpclient, "emoji.list", values, response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "emoji.list", values, response, api) if err != nil { return nil, err } diff --git a/vendor/github.com/nlopes/slack/files.go b/vendor/github.com/nlopes/slack/files.go index 0550c9fb..13158582 100644 --- a/vendor/github.com/nlopes/slack/files.go +++ b/vendor/github.com/nlopes/slack/files.go @@ -137,16 +137,14 @@ func NewGetFilesParameters() GetFilesParameters { } } -func fileRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*fileResponseFull, error) { +func fileRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*fileResponseFull, error) { response := &fileResponseFull{} - err := postForm(ctx, client, SLACK_API+path, values, response, debug) + err := postForm(ctx, client, APIURL+path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + + return response, response.Err() } // GetFileInfo retrieves a file and related comments @@ -163,7 +161,7 @@ func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count, "page": {strconv.Itoa(page)}, } - response, err := fileRequest(ctx, api.httpclient, "files.info", values, api.debug) + response, err := fileRequest(ctx, api.httpclient, "files.info", values, api) if err != nil { return nil, nil, nil, err } @@ -202,7 +200,7 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter values.Add("page", strconv.Itoa(params.Page)) } - response, err := fileRequest(ctx, api.httpclient, "files.list", values, api.debug) + response, err := fileRequest(ctx, api.httpclient, "files.list", values, api) if err != nil { return nil, nil, err } @@ -246,19 +244,17 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam } if params.Content != "" { values.Add("content", params.Content) - err = postForm(ctx, api.httpclient, SLACK_API+"files.upload", values, response, api.debug) + err = postForm(ctx, api.httpclient, APIURL+"files.upload", values, response, api) } else if params.File != "" { - err = postLocalWithMultipartResponse(ctx, api.httpclient, "files.upload", params.File, "file", values, response, api.debug) + err = postLocalWithMultipartResponse(ctx, api.httpclient, "files.upload", params.File, "file", values, response, api) } else if params.Reader != nil { - err = postWithMultipartResponse(ctx, api.httpclient, "files.upload", params.Filename, "file", values, params.Reader, response, api.debug) + err = postWithMultipartResponse(ctx, api.httpclient, "files.upload", params.Filename, "file", values, params.Reader, response, api) } if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return &response.File, nil + + return &response.File, response.Err() } // DeleteFileComment deletes a file's comment @@ -277,7 +273,7 @@ func (api *Client) DeleteFileCommentContext(ctx context.Context, fileID, comment "file": {fileID}, "id": {commentID}, } - _, err = fileRequest(ctx, api.httpclient, "files.comments.delete", values, api.debug) + _, err = fileRequest(ctx, api.httpclient, "files.comments.delete", values, api) return err } @@ -293,7 +289,7 @@ func (api *Client) DeleteFileContext(ctx context.Context, fileID string) (err er "file": {fileID}, } - _, err = fileRequest(ctx, api.httpclient, "files.delete", values, api.debug) + _, err = fileRequest(ctx, api.httpclient, "files.delete", values, api) return err } @@ -309,7 +305,7 @@ func (api *Client) RevokeFilePublicURLContext(ctx context.Context, fileID string "file": {fileID}, } - response, err := fileRequest(ctx, api.httpclient, "files.revokePublicURL", values, api.debug) + response, err := fileRequest(ctx, api.httpclient, "files.revokePublicURL", values, api) if err != nil { return nil, err } @@ -328,7 +324,7 @@ func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string) "file": {fileID}, } - response, err := fileRequest(ctx, api.httpclient, "files.sharedPublicURL", values, api.debug) + response, err := fileRequest(ctx, api.httpclient, "files.sharedPublicURL", values, api) if err != nil { return nil, nil, nil, err } diff --git a/vendor/github.com/nlopes/slack/groups.go b/vendor/github.com/nlopes/slack/groups.go index 67e78e99..a248f6fd 100644 --- a/vendor/github.com/nlopes/slack/groups.go +++ b/vendor/github.com/nlopes/slack/groups.go @@ -2,7 +2,6 @@ package slack import ( "context" - "errors" "net/url" "strconv" ) @@ -28,16 +27,14 @@ type groupResponseFull struct { SlackResponse } -func groupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*groupResponseFull, error) { +func groupRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*groupResponseFull, error) { response := &groupResponseFull{} - err := postForm(ctx, client, SLACK_API+path, values, response, debug) + err := postForm(ctx, client, APIURL+path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + + return response, response.Err() } // ArchiveGroup archives a private group @@ -52,7 +49,7 @@ func (api *Client) ArchiveGroupContext(ctx context.Context, group string) error "channel": {group}, } - _, err := groupRequest(ctx, api.httpclient, "groups.archive", values, api.debug) + _, err := groupRequest(ctx, api.httpclient, "groups.archive", values, api) return err } @@ -68,7 +65,7 @@ func (api *Client) UnarchiveGroupContext(ctx context.Context, group string) erro "channel": {group}, } - _, err := groupRequest(ctx, api.httpclient, "groups.unarchive", values, api.debug) + _, err := groupRequest(ctx, api.httpclient, "groups.unarchive", values, api) return err } @@ -84,7 +81,7 @@ func (api *Client) CreateGroupContext(ctx context.Context, group string) (*Group "name": {group}, } - response, err := groupRequest(ctx, api.httpclient, "groups.create", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.create", values, api) if err != nil { return nil, err } @@ -109,7 +106,7 @@ func (api *Client) CreateChildGroupContext(ctx context.Context, group string) (* "channel": {group}, } - response, err := groupRequest(ctx, api.httpclient, "groups.createChild", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.createChild", values, api) if err != nil { return nil, err } @@ -128,7 +125,7 @@ func (api *Client) CloseGroupContext(ctx context.Context, group string) (bool, b "channel": {group}, } - response, err := imRequest(ctx, api.httpclient, "groups.close", values, api.debug) + response, err := imRequest(ctx, api.httpclient, "groups.close", values, api) if err != nil { return false, false, err } @@ -170,7 +167,7 @@ func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, par } } - response, err := groupRequest(ctx, api.httpclient, "groups.history", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.history", values, api) if err != nil { return nil, err } @@ -190,7 +187,7 @@ func (api *Client) InviteUserToGroupContext(ctx context.Context, group, user str "user": {user}, } - response, err := groupRequest(ctx, api.httpclient, "groups.invite", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.invite", values, api) if err != nil { return nil, false, err } @@ -209,7 +206,7 @@ func (api *Client) LeaveGroupContext(ctx context.Context, group string) (err err "channel": {group}, } - _, err = groupRequest(ctx, api.httpclient, "groups.leave", values, api.debug) + _, err = groupRequest(ctx, api.httpclient, "groups.leave", values, api) return err } @@ -226,7 +223,7 @@ func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user str "user": {user}, } - _, err = groupRequest(ctx, api.httpclient, "groups.kick", values, api.debug) + _, err = groupRequest(ctx, api.httpclient, "groups.kick", values, api) return err } @@ -244,7 +241,7 @@ func (api *Client) GetGroupsContext(ctx context.Context, excludeArchived bool) ( values.Add("exclude_archived", "1") } - response, err := groupRequest(ctx, api.httpclient, "groups.list", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.list", values, api) if err != nil { return nil, err } @@ -263,7 +260,7 @@ func (api *Client) GetGroupInfoContext(ctx context.Context, group string) (*Grou "channel": {group}, } - response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api) if err != nil { return nil, err } @@ -288,7 +285,7 @@ func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string "ts": {ts}, } - _, err = groupRequest(ctx, api.httpclient, "groups.mark", values, api.debug) + _, err = groupRequest(ctx, api.httpclient, "groups.mark", values, api) return err } @@ -304,7 +301,7 @@ func (api *Client) OpenGroupContext(ctx context.Context, group string) (bool, bo "channel": {group}, } - response, err := groupRequest(ctx, api.httpclient, "groups.open", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.open", values, api) if err != nil { return false, false, err } @@ -328,7 +325,7 @@ func (api *Client) RenameGroupContext(ctx context.Context, group, name string) ( // XXX: the created entry in this call returns a string instead of a number // so I may have to do some workaround to solve it. - response, err := groupRequest(ctx, api.httpclient, "groups.rename", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.rename", values, api) if err != nil { return nil, err } @@ -348,7 +345,7 @@ func (api *Client) SetGroupPurposeContext(ctx context.Context, group, purpose st "purpose": {purpose}, } - response, err := groupRequest(ctx, api.httpclient, "groups.setPurpose", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.setPurpose", values, api) if err != nil { return "", err } @@ -368,7 +365,7 @@ func (api *Client) SetGroupTopicContext(ctx context.Context, group, topic string "topic": {topic}, } - response, err := groupRequest(ctx, api.httpclient, "groups.setTopic", values, api.debug) + response, err := groupRequest(ctx, api.httpclient, "groups.setTopic", values, api) if err != nil { return "", err } diff --git a/vendor/github.com/nlopes/slack/im.go b/vendor/github.com/nlopes/slack/im.go index fa8b0959..10563d91 100644 --- a/vendor/github.com/nlopes/slack/im.go +++ b/vendor/github.com/nlopes/slack/im.go @@ -2,7 +2,6 @@ package slack import ( "context" - "errors" "net/url" "strconv" ) @@ -29,16 +28,14 @@ type IM struct { IsUserDeleted bool `json:"is_user_deleted"` } -func imRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*imResponseFull, error) { +func imRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*imResponseFull, error) { response := &imResponseFull{} - err := postSlackMethod(ctx, client, path, values, response, debug) + err := postSlackMethod(ctx, client, path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + + return response, response.Err() } // CloseIMChannel closes the direct message channel @@ -53,7 +50,7 @@ func (api *Client) CloseIMChannelContext(ctx context.Context, channel string) (b "channel": {channel}, } - response, err := imRequest(ctx, api.httpclient, "im.close", values, api.debug) + response, err := imRequest(ctx, api.httpclient, "im.close", values, api) if err != nil { return false, false, err } @@ -74,7 +71,7 @@ func (api *Client) OpenIMChannelContext(ctx context.Context, user string) (bool, "user": {user}, } - response, err := imRequest(ctx, api.httpclient, "im.open", values, api.debug) + response, err := imRequest(ctx, api.httpclient, "im.open", values, api) if err != nil { return false, false, "", err } @@ -94,7 +91,7 @@ func (api *Client) MarkIMChannelContext(ctx context.Context, channel, ts string) "ts": {ts}, } - _, err := imRequest(ctx, api.httpclient, "im.mark", values, api.debug) + _, err := imRequest(ctx, api.httpclient, "im.mark", values, api) return err } @@ -133,7 +130,7 @@ func (api *Client) GetIMHistoryContext(ctx context.Context, channel string, para } } - response, err := imRequest(ctx, api.httpclient, "im.history", values, api.debug) + response, err := imRequest(ctx, api.httpclient, "im.history", values, api) if err != nil { return nil, err } @@ -151,7 +148,7 @@ func (api *Client) GetIMChannelsContext(ctx context.Context) ([]IM, error) { "token": {api.token}, } - response, err := imRequest(ctx, api.httpclient, "im.list", values, api.debug) + response, err := imRequest(ctx, api.httpclient, "im.list", values, api) if err != nil { return nil, err } diff --git a/vendor/github.com/nlopes/slack/interactions.go b/vendor/github.com/nlopes/slack/interactions.go new file mode 100644 index 00000000..97e31a49 --- /dev/null +++ b/vendor/github.com/nlopes/slack/interactions.go @@ -0,0 +1,31 @@ +package slack + +// InteractionType type of interactions +type InteractionType string + +// Types of interactions that can be received. +const ( + InteractionTypeDialogSubmission = InteractionType("dialog_submission") + InteractionTypeDialogSuggestion = InteractionType("dialog_suggestion") + InteractionTypeInteractionMessage = InteractionType("interactive_message") + InteractionTypeMessageAction = InteractionType("message_action") +) + +// InteractionCallback is sent from slack when a user interactions with a button or dialog. +type InteractionCallback struct { + Type InteractionType `json:"type"` + Token string `json:"token"` + CallbackID string `json:"callback_id"` + ResponseURL string `json:"response_url"` + TriggerID string `json:"trigger_id"` + ActionTs string `json:"action_ts"` + Team Team `json:"team"` + Channel Channel `json:"channel"` + User User `json:"user"` + OriginalMessage Message `json:"original_message"` + Message Message `json:"message"` + Name string `json:"name"` + Value string `json:"value"` + ActionCallback + DialogSubmissionCallback +} diff --git a/vendor/github.com/nlopes/slack/logger.go b/vendor/github.com/nlopes/slack/logger.go index 501d1672..6a3533a9 100644 --- a/vendor/github.com/nlopes/slack/logger.go +++ b/vendor/github.com/nlopes/slack/logger.go @@ -2,52 +2,59 @@ package slack import ( "fmt" - "sync" ) -// SetLogger let's library users supply a logger, so that api debugging -// can be logged along with the application's debugging info. -func SetLogger(l logProvider) { - loggerMutex.Lock() - logger = ilogger{logProvider: l} - loggerMutex.Unlock() -} - -var ( - loggerMutex = new(sync.Mutex) - logger logInternal // A logger that can be set by consumers -) - -// logProvider is a logger interface compatible with both stdlib and some -// 3rd party loggers such as logrus. -type logProvider interface { +// logger is a logger interface compatible with both stdlib and some +// 3rd party loggers. +type logger interface { Output(int, string) error } -// logInternal represents the internal logging api we use. -type logInternal interface { +// ilogger represents the internal logging api we use. +type ilogger interface { + logger Print(...interface{}) Printf(string, ...interface{}) Println(...interface{}) - Output(int, string) error } -// ilogger implements the additional methods used by our internal logging. -type ilogger struct { - logProvider +type debug interface { + Debug() bool + + // Debugf print a formatted debug line. + Debugf(format string, v ...interface{}) + // Debugln print a debug line. + Debugln(v ...interface{}) +} + +// internalLog implements the additional methods used by our internal logging. +type internalLog struct { + logger } // Println replicates the behaviour of the standard logger. -func (t ilogger) Println(v ...interface{}) { +func (t internalLog) Println(v ...interface{}) { t.Output(2, fmt.Sprintln(v...)) } // Printf replicates the behaviour of the standard logger. -func (t ilogger) Printf(format string, v ...interface{}) { +func (t internalLog) Printf(format string, v ...interface{}) { t.Output(2, fmt.Sprintf(format, v...)) } // Print replicates the behaviour of the standard logger. -func (t ilogger) Print(v ...interface{}) { +func (t internalLog) Print(v ...interface{}) { t.Output(2, fmt.Sprint(v...)) } + +type discard struct{} + +func (t discard) Debug() bool { + return false +} + +// Debugf print a formatted debug line. +func (t discard) Debugf(format string, v ...interface{}) {} + +// Debugln print a debug line. +func (t discard) Debugln(v ...interface{}) {} diff --git a/vendor/github.com/nlopes/slack/messages.go b/vendor/github.com/nlopes/slack/messages.go index 6551dd4f..bde9a37e 100644 --- a/vendor/github.com/nlopes/slack/messages.go +++ b/vendor/github.com/nlopes/slack/messages.go @@ -4,11 +4,12 @@ package slack type OutgoingMessage struct { ID int `json:"id"` // channel ID - Channel string `json:"channel,omitempty"` - Text string `json:"text,omitempty"` - Type string `json:"type,omitempty"` - ThreadTimestamp string `json:"thread_ts,omitempty"` - ThreadBroadcast bool `json:"reply_broadcast,omitempty"` + Channel string `json:"channel,omitempty"` + Text string `json:"text,omitempty"` + Type string `json:"type,omitempty"` + ThreadTimestamp string `json:"thread_ts,omitempty"` + ThreadBroadcast bool `json:"reply_broadcast,omitempty"` + IDs []string `json:"ids,omitempty"` } // Message is an auxiliary type to allow us to have a message containing sub messages @@ -147,6 +148,15 @@ func (rtm *RTM) NewOutgoingMessage(text string, channelID string, options ...RTM return &msg } +// NewSubscribeUserPresence prepares an OutgoingMessage that the user can +// use to subscribe presence events for the specified users. +func (rtm *RTM) NewSubscribeUserPresence(ids []string) *OutgoingMessage { + return &OutgoingMessage{ + Type: "presence_sub", + IDs: ids, + } +} + // NewTypingMessage prepares an OutgoingMessage that the user can // use to send as a typing indicator. Use this function to properly set the // messageID. @@ -174,5 +184,4 @@ func RTMsgOptionBroadcast() RTMsgOption { return func(msg *OutgoingMessage) { msg.ThreadBroadcast = true } - } diff --git a/vendor/github.com/nlopes/slack/misc.go b/vendor/github.com/nlopes/slack/misc.go index 69103384..30ae4628 100644 --- a/vendor/github.com/nlopes/slack/misc.go +++ b/vendor/github.com/nlopes/slack/misc.go @@ -19,6 +19,7 @@ import ( "time" ) +// SlackResponse handles parsing out errors from the web api. type SlackResponse struct { Ok bool `json:"ok"` Error string `json:"error"` @@ -64,47 +65,31 @@ func (e *RateLimitedError) Error() string { return fmt.Sprintf("Slack rate limit exceeded, retry after %s", e.RetryAfter) } -func fileUploadReq(ctx context.Context, path, fieldname, filename string, values url.Values, r io.Reader) (*http.Request, error) { - body := &bytes.Buffer{} - wr := multipart.NewWriter(body) +func fileUploadReq(ctx context.Context, path string, values url.Values, r io.Reader) (*http.Request, error) { + req, err := http.NewRequest("POST", path, r) - ioWriter, err := wr.CreateFormFile(fieldname, filename) - if err != nil { - wr.Close() - return nil, err - } - _, err = io.Copy(ioWriter, r) - if err != nil { - wr.Close() - return nil, err - } - // Close the multipart writer or the footer won't be written - wr.Close() - req, err := http.NewRequest("POST", path, body) req = req.WithContext(ctx) if err != nil { return nil, err } - req.Header.Add("Content-Type", wr.FormDataContentType()) req.URL.RawQuery = (values).Encode() return req, nil } -func parseResponseBody(body io.ReadCloser, intf interface{}, debug bool) error { +func parseResponseBody(body io.ReadCloser, intf interface{}, d debug) error { response, err := ioutil.ReadAll(body) if err != nil { return err } - // FIXME: will be api.Debugf - if debug { - logger.Printf("parseResponseBody: %s\n", string(response)) + if d.Debug() { + d.Debugln("parseResponseBody", string(response)) } return json.Unmarshal(response, intf) } -func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error { +func postLocalWithMultipartResponse(ctx context.Context, client httpClient, path, fpath, fieldname string, values url.Values, intf interface{}, d debug) error { fullpath, err := filepath.Abs(fpath) if err != nil { return err @@ -114,14 +99,65 @@ func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, p return err } defer file.Close() - return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, debug) + return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, d) } -func postWithMultipartResponse(ctx context.Context, client HTTPRequester, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, debug bool) error { - req, err := fileUploadReq(ctx, SLACK_API+path, fieldname, name, values, r) +func postWithMultipartResponse(ctx context.Context, client httpClient, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, d debug) error { + pipeReader, pipeWriter := io.Pipe() + wr := multipart.NewWriter(pipeWriter) + errc := make(chan error) + go func() { + defer pipeWriter.Close() + ioWriter, err := wr.CreateFormFile(fieldname, name) + if err != nil { + errc <- err + return + } + _, err = io.Copy(ioWriter, r) + if err != nil { + errc <- err + return + } + if err = wr.Close(); err != nil { + errc <- err + return + } + }() + req, err := fileUploadReq(ctx, APIURL+path, values, pipeReader) if err != nil { return err } + req.Header.Add("Content-Type", wr.FormDataContentType()) + req = req.WithContext(ctx) + resp, err := client.Do(req) + + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusTooManyRequests { + retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64) + if err != nil { + return err + } + return &RateLimitedError{time.Duration(retry) * time.Second} + } + + // Slack seems to send an HTML body along with 5xx error codes. Don't parse it. + if resp.StatusCode != http.StatusOK { + logResponse(resp, d) + return statusCodeError{Code: resp.StatusCode, Status: resp.Status} + } + select { + case err = <-errc: + return err + default: + return parseResponseBody(resp.Body, intf, d) + } +} + +func doPost(ctx context.Context, client httpClient, req *http.Request, intf interface{}, d debug) error { req = req.WithContext(ctx) resp, err := client.Do(req) if err != nil { @@ -139,40 +175,15 @@ func postWithMultipartResponse(ctx context.Context, client HTTPRequester, path, // Slack seems to send an HTML body along with 5xx error codes. Don't parse it. if resp.StatusCode != http.StatusOK { - logResponse(resp, debug) + logResponse(resp, d) return statusCodeError{Code: resp.StatusCode, Status: resp.Status} } - return parseResponseBody(resp.Body, intf, debug) -} - -func doPost(ctx context.Context, client HTTPRequester, req *http.Request, intf interface{}, debug bool) error { - req = req.WithContext(ctx) - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode == http.StatusTooManyRequests { - retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64) - if err != nil { - return err - } - return &RateLimitedError{time.Duration(retry) * time.Second} - } - - // Slack seems to send an HTML body along with 5xx error codes. Don't parse it. - if resp.StatusCode != http.StatusOK { - logResponse(resp, debug) - return statusCodeError{Code: resp.StatusCode, Status: resp.Status} - } - - return parseResponseBody(resp.Body, intf, debug) + return parseResponseBody(resp.Body, intf, d) } // post JSON. -func postJSON(ctx context.Context, client HTTPRequester, endpoint, token string, json []byte, intf interface{}, debug bool) error { +func postJSON(ctx context.Context, client httpClient, endpoint, token string, json []byte, intf interface{}, d debug) error { reqBody := bytes.NewBuffer(json) req, err := http.NewRequest("POST", endpoint, reqBody) if err != nil { @@ -180,38 +191,53 @@ func postJSON(ctx context.Context, client HTTPRequester, endpoint, token string, } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - return doPost(ctx, client, req, intf, debug) + return doPost(ctx, client, req, intf, d) } // post a url encoded form. -func postForm(ctx context.Context, client HTTPRequester, endpoint string, values url.Values, intf interface{}, debug bool) error { +func postForm(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error { reqBody := strings.NewReader(values.Encode()) req, err := http.NewRequest("POST", endpoint, reqBody) if err != nil { return err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - return doPost(ctx, client, req, intf, debug) + return doPost(ctx, client, req, intf, d) } // post to a slack web method. -func postSlackMethod(ctx context.Context, client HTTPRequester, path string, values url.Values, intf interface{}, debug bool) error { - return postForm(ctx, client, SLACK_API+path, values, intf, debug) +func postSlackMethod(ctx context.Context, client httpClient, path string, values url.Values, intf interface{}, d debug) error { + return postForm(ctx, client, APIURL+path, values, intf, d) } -func parseAdminResponse(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, intf interface{}, debug bool) error { - endpoint := fmt.Sprintf(SLACK_WEB_API_FORMAT, teamName, method, time.Now().Unix()) - return postForm(ctx, client, endpoint, values, intf, debug) +// get a slack web method. +func getSlackMethod(ctx context.Context, client httpClient, path string, values url.Values, intf interface{}, d debug) error { + return getResource(ctx, client, APIURL+path, values, intf, d) } -func logResponse(resp *http.Response, debug bool) error { - if debug { +func getResource(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error { + req, err := http.NewRequest("GET", endpoint, nil) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.URL.RawQuery = values.Encode() + + return doPost(ctx, client, req, intf, d) +} + +func parseAdminResponse(ctx context.Context, client httpClient, method string, teamName string, values url.Values, intf interface{}, d debug) error { + endpoint := fmt.Sprintf(WEBAPIURLFormat, teamName, method, time.Now().Unix()) + return postForm(ctx, client, endpoint, values, intf, d) +} + +func logResponse(resp *http.Response, d debug) error { + if d.Debug() { text, err := httputil.DumpResponse(resp, true) if err != nil { return err } - - logger.Print(string(text)) + d.Debugln(string(text)) } return nil diff --git a/vendor/github.com/nlopes/slack/oauth.go b/vendor/github.com/nlopes/slack/oauth.go index 378af4a5..8a8194cb 100644 --- a/vendor/github.com/nlopes/slack/oauth.go +++ b/vendor/github.com/nlopes/slack/oauth.go @@ -2,10 +2,10 @@ package slack import ( "context" - "errors" "net/url" ) +// OAuthResponseIncomingWebhook ... type OAuthResponseIncomingWebhook struct { URL string `json:"url"` Channel string `json:"channel"` @@ -13,11 +13,13 @@ type OAuthResponseIncomingWebhook struct { 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"` @@ -30,24 +32,24 @@ type OAuthResponse struct { } // GetOAuthToken retrieves an AccessToken -func GetOAuthToken(clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) { - return GetOAuthTokenContext(context.Background(), clientID, clientSecret, code, redirectURI, debug) +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, clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) { - response, err := GetOAuthResponseContext(ctx, clientID, clientSecret, code, redirectURI, debug) +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(clientID, clientSecret, code, redirectURI string, debug bool) (resp *OAuthResponse, err error) { - return GetOAuthResponseContext(context.Background(), clientID, clientSecret, code, redirectURI, debug) +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, clientID, clientSecret, code, redirectURI string, debug bool) (resp *OAuthResponse, err error) { +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}, @@ -55,12 +57,8 @@ func GetOAuthResponseContext(ctx context.Context, clientID, clientSecret, code, "redirect_uri": {redirectURI}, } response := &OAuthResponse{} - err = postSlackMethod(ctx, customHTTPClient, "oauth.access", values, response, debug) - if err != nil { + if err = postSlackMethod(ctx, client, "oauth.access", values, response, discard{}); err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + return response, response.Err() } diff --git a/vendor/github.com/nlopes/slack/pins.go b/vendor/github.com/nlopes/slack/pins.go index 34863f17..c1d525df 100644 --- a/vendor/github.com/nlopes/slack/pins.go +++ b/vendor/github.com/nlopes/slack/pins.go @@ -34,7 +34,7 @@ func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemR } response := &SlackResponse{} - if err := postSlackMethod(ctx, api.httpclient, "pins.add", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "pins.add", values, response, api); err != nil { return err } @@ -63,7 +63,7 @@ func (api *Client) RemovePinContext(ctx context.Context, channel string, item It } response := &SlackResponse{} - if err := postSlackMethod(ctx, api.httpclient, "pins.remove", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "pins.remove", values, response, api); err != nil { return err } @@ -83,7 +83,7 @@ func (api *Client) ListPinsContext(ctx context.Context, channel string) ([]Item, } response := &listPinsResponseFull{} - err := postSlackMethod(ctx, api.httpclient, "pins.list", values, response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "pins.list", values, response, api) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/nlopes/slack/reactions.go b/vendor/github.com/nlopes/slack/reactions.go index 5eabde63..abe1e72a 100644 --- a/vendor/github.com/nlopes/slack/reactions.go +++ b/vendor/github.com/nlopes/slack/reactions.go @@ -155,7 +155,7 @@ func (api *Client) AddReactionContext(ctx context.Context, name string, item Ite } response := &SlackResponse{} - if err := postSlackMethod(ctx, api.httpclient, "reactions.add", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "reactions.add", values, response, api); err != nil { return err } @@ -189,7 +189,7 @@ func (api *Client) RemoveReactionContext(ctx context.Context, name string, item } response := &SlackResponse{} - if err := postSlackMethod(ctx, api.httpclient, "reactions.remove", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "reactions.remove", values, response, api); err != nil { return err } @@ -223,7 +223,7 @@ func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params } response := &getReactionsResponseFull{} - if err := postSlackMethod(ctx, api.httpclient, "reactions.get", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "reactions.get", values, response, api); err != nil { return nil, err } if !response.Ok { @@ -256,7 +256,7 @@ func (api *Client) ListReactionsContext(ctx context.Context, params ListReaction } response := &listReactionsResponseFull{} - err := postSlackMethod(ctx, api.httpclient, "reactions.list", values, response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "reactions.list", values, response, api) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/nlopes/slack/rtm.go b/vendor/github.com/nlopes/slack/rtm.go index 41a136eb..e7fa83f7 100644 --- a/vendor/github.com/nlopes/slack/rtm.go +++ b/vendor/github.com/nlopes/slack/rtm.go @@ -38,7 +38,7 @@ func (api *Client) StartRTM() (info *Info, websocketURL string, err error) { // To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it. func (api *Client) StartRTMContext(ctx context.Context) (info *Info, websocketURL string, err error) { response := &infoResponseFull{} - err = postSlackMethod(ctx, api.httpclient, "rtm.start", url.Values{"token": {api.token}}, response, api.debug) + err = postSlackMethod(ctx, api.httpclient, "rtm.start", url.Values{"token": {api.token}}, response, api) if err != nil { return nil, "", err } @@ -63,7 +63,7 @@ func (api *Client) ConnectRTM() (info *Info, websocketURL string, err error) { // To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it. func (api *Client) ConnectRTMContext(ctx context.Context) (info *Info, websocketURL string, err error) { response := &infoResponseFull{} - err = postSlackMethod(ctx, api.httpclient, "rtm.connect", url.Values{"token": {api.token}}, response, api.debug) + err = postSlackMethod(ctx, api.httpclient, "rtm.connect", url.Values{"token": {api.token}}, response, api) if err != nil { api.Debugf("Failed to connect to RTM: %s", err) return nil, "", err @@ -100,17 +100,24 @@ func RTMOptionPingInterval(d time.Duration) RTMOption { } } +// RTMOptionConnParams installs parameters to embed into the connection URL. +func RTMOptionConnParams(connParams url.Values) RTMOption { + return func(rtm *RTM) { + rtm.connParams = connParams + } +} + // NewRTM returns a RTM, which provides a fully managed connection to // Slack's websocket-based Real-Time Messaging protocol. func (api *Client) NewRTM(options ...RTMOption) *RTM { result := &RTM{ Client: *api, + wasIntentional: true, + isConnected: false, IncomingEvents: make(chan RTMEvent, 50), outgoingMessages: make(chan OutgoingMessage, 20), pingInterval: defaultPingInterval, pingDeadman: time.NewTimer(deadmanDuration(defaultPingInterval)), - isConnected: false, - wasIntentional: true, killChannel: make(chan bool), disconnected: make(chan struct{}, 1), forcePing: make(chan bool), @@ -125,14 +132,3 @@ func (api *Client) NewRTM(options ...RTMOption) *RTM { return result } - -// NewRTMWithOptions Deprecated just use NewRTM(RTMOptionsUseStart(true)) -// returns a RTM, which provides a fully managed connection to -// Slack's websocket-based Real-Time Messaging protocol. -// This also allows to configure various options available for RTM API. -func (api *Client) NewRTMWithOptions(options *RTMOptions) *RTM { - if options != nil { - return api.NewRTM(RTMOptionUseStart(options.UseRTMStart)) - } - return api.NewRTM() -} diff --git a/vendor/github.com/nlopes/slack/search.go b/vendor/github.com/nlopes/slack/search.go index e858952f..2d018fcc 100644 --- a/vendor/github.com/nlopes/slack/search.go +++ b/vendor/github.com/nlopes/slack/search.go @@ -2,7 +2,6 @@ package slack import ( "context" - "errors" "net/url" "strconv" ) @@ -104,14 +103,12 @@ func (api *Client) _search(ctx context.Context, path, query string, params Searc } response = &searchResponseFull{} - err := postSlackMethod(ctx, api.httpclient, path, values, response, api.debug) + err := postSlackMethod(ctx, api.httpclient, path, values, response, api) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + + return response, response.Err() } diff --git a/vendor/github.com/nlopes/slack/security.go b/vendor/github.com/nlopes/slack/security.go index 50201d99..6ab3c698 100644 --- a/vendor/github.com/nlopes/slack/security.go +++ b/vendor/github.com/nlopes/slack/security.go @@ -1,47 +1,99 @@ -package slack - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "hash" - "net/http" -) - -// SecretsVerifier contains the information needed to verify that the request comes from Slack -type SecretsVerifier struct { - slackSig string - timeStamp string - hmac hash.Hash -} - -// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret -func NewSecretsVerifier(header http.Header, signingSecret string) (SecretsVerifier, error) { - if header["X-Slack-Signature"][0] == "" || header["X-Slack-Request-Timestamp"][0] == "" { - return SecretsVerifier{}, errors.New("Headers are empty, cannot create SecretsVerifier") - } - - hash := hmac.New(sha256.New, []byte(signingSecret)) - hash.Write([]byte(fmt.Sprintf("v0:%s:", header["X-Slack-Request-Timestamp"][0]))) - return SecretsVerifier{ - slackSig: header["X-Slack-Signature"][0], - timeStamp: header["X-Slack-Request-Timestamp"][0], - hmac: hash, - }, nil -} - -func (v *SecretsVerifier) Write(body []byte) (n int, err error) { - return v.hmac.Write(body) -} - -// Ensure compares the signature sent from Slack with the actual computed hash to judge validity -func (v SecretsVerifier) Ensure() error { - computed := "v0=" + string(hex.EncodeToString(v.hmac.Sum(nil))) - if computed == v.slackSig { - return nil - } - - return fmt.Errorf("Expected signing signature: %s, but computed: %s", v.slackSig, computed) -} +package slack + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "hash" + "net/http" + "strconv" + "strings" + "time" +) + +// Signature headers +const ( + hSignature = "X-Slack-Signature" + hTimestamp = "X-Slack-Request-Timestamp" +) + +// SecretsVerifier contains the information needed to verify that the request comes from Slack +type SecretsVerifier struct { + signature []byte + hmac hash.Hash +} + +func unsafeSignatureVerifier(header http.Header, secret string) (_ SecretsVerifier, err error) { + var ( + bsignature []byte + ) + + signature := header.Get(hSignature) + stimestamp := header.Get(hTimestamp) + + if signature == "" || stimestamp == "" { + return SecretsVerifier{}, errors.New("missing headers") + } + + if bsignature, err = hex.DecodeString(strings.TrimPrefix(signature, "v0=")); err != nil { + return SecretsVerifier{}, err + } + + hash := hmac.New(sha256.New, []byte(secret)) + hash.Write([]byte(fmt.Sprintf("v0:%s:", stimestamp))) + + return SecretsVerifier{ + signature: bsignature, + hmac: hash, + }, nil +} + +// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret +func NewSecretsVerifier(header http.Header, secret string) (sv SecretsVerifier, err error) { + var ( + timestamp int64 + ) + + stimestamp := header.Get(hTimestamp) + + if sv, err = unsafeSignatureVerifier(header, secret); err != nil { + return SecretsVerifier{}, err + } + + if timestamp, err = strconv.ParseInt(stimestamp, 10, 64); err != nil { + return SecretsVerifier{}, err + } + + diff := absDuration(time.Now().Sub(time.Unix(timestamp, 0))) + if diff > 5*time.Minute { + return SecretsVerifier{}, fmt.Errorf("timestamp is too old") + } + + return sv, err +} + +func (v *SecretsVerifier) Write(body []byte) (n int, err error) { + return v.hmac.Write(body) +} + +// Ensure compares the signature sent from Slack with the actual computed hash to judge validity +func (v SecretsVerifier) Ensure() error { + computed := v.hmac.Sum(nil) + // use hmac.Equal prevent leaking timing information. + if hmac.Equal(computed, v.signature) { + return nil + } + + return fmt.Errorf("Expected signing signature: %s, but computed: %s", v.signature, computed) +} + +func abs64(n int64) int64 { + y := n >> 63 + return (n ^ y) - y +} + +func absDuration(n time.Duration) time.Duration { + return time.Duration(abs64(int64(n))) +} diff --git a/vendor/github.com/nlopes/slack/slack.go b/vendor/github.com/nlopes/slack/slack.go index 6d1e7de9..1e75142d 100644 --- a/vendor/github.com/nlopes/slack/slack.go +++ b/vendor/github.com/nlopes/slack/slack.go @@ -2,7 +2,6 @@ package slack import ( "context" - "errors" "fmt" "log" "net/http" @@ -10,31 +9,17 @@ import ( "os" ) -// Added as a var so that we can change this for testing purposes -var SLACK_API string = "https://slack.com/api/" -var SLACK_WEB_API_FORMAT string = "https://%s.slack.com/api/users.admin.%s?t=%s" +// APIURL a dded as a var so that we can change this for testing purposes +var APIURL = "https://slack.com/api/" -// HTTPClient sets a custom http.Client -// deprecated: in favor of SetHTTPClient() -var HTTPClient = &http.Client{} +// WEBAPIURLFormat ... +const WEBAPIURLFormat = "https://%s.slack.com/api/users.admin.%s?t=%d" -var customHTTPClient HTTPRequester = HTTPClient - -// HTTPRequester defines the minimal interface needed for an http.Client to be implemented. -// -// Use it in conjunction with the SetHTTPClient function to allow for other capabilities -// like a tracing http.Client -type HTTPRequester interface { +// httpClient defines the minimal interface needed for an http.Client to be implemented. +type httpClient interface { Do(*http.Request) (*http.Response, error) } -// SetHTTPClient allows you to specify a custom http.Client -// Use this instead of the package level HTTPClient variable if you want to use a custom client like the -// Stackdriver Trace HTTPClient https://godoc.org/cloud.google.com/go/trace#HTTPClient -func SetHTTPClient(client HTTPRequester) { - customHTTPClient = client -} - // ResponseMetadata holds pagination metadata type ResponseMetadata struct { Cursor string `json:"next_cursor"` @@ -48,6 +33,7 @@ func (t *ResponseMetadata) initialize() *ResponseMetadata { return &ResponseMetadata{} } +// AuthTestResponse ... type AuthTestResponse struct { URL string `json:"url"` Team string `json:"team"` @@ -61,20 +47,36 @@ type authTestResponseFull struct { AuthTestResponse } +// Client for the slack api. type Client struct { token string info Info debug bool - httpclient HTTPRequester + log ilogger + httpclient httpClient } // Option defines an option for a Client type Option func(*Client) // OptionHTTPClient - provide a custom http client to the slack client. -func OptionHTTPClient(c HTTPRequester) func(*Client) { - return func(s *Client) { - s.httpclient = c +func OptionHTTPClient(client httpClient) func(*Client) { + return func(c *Client) { + c.httpclient = client + } +} + +// OptionDebug enable debugging for the client +func OptionDebug(b bool) func(*Client) { + return func(c *Client) { + c.debug = b + } +} + +// OptionLog set logging for client. +func OptionLog(l logger) func(*Client) { + return func(c *Client) { + c.log = internalLog{logger: l} } } @@ -82,7 +84,8 @@ func OptionHTTPClient(c HTTPRequester) func(*Client) { func New(token string, options ...Option) *Client { s := &Client{ token: token, - httpclient: customHTTPClient, + httpclient: &http.Client{}, + log: log.New(os.Stderr, "nlopes/slack", log.LstdFlags|log.Lshortfile), } for _, opt := range options { @@ -98,43 +101,32 @@ func (api *Client) AuthTest() (response *AuthTestResponse, error error) { } // AuthTestContext tests if the user is able to do authenticated requests or not with a custom context -func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, error error) { +func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, err error) { api.Debugf("Challenging auth...") responseFull := &authTestResponseFull{} - err := postSlackMethod(ctx, api.httpclient, "auth.test", url.Values{"token": {api.token}}, responseFull, api.debug) + err = postSlackMethod(ctx, api.httpclient, "auth.test", url.Values{"token": {api.token}}, responseFull, api) if err != nil { - api.Debugf("failed to test for auth: %s", err) return nil, err } - if !responseFull.Ok { - api.Debugf("auth response was not Ok: %s", responseFull.Error) - return nil, errors.New(responseFull.Error) - } - api.Debugf("Auth challenge was successful with response %+v", responseFull.AuthTestResponse) - return &responseFull.AuthTestResponse, nil -} - -// SetDebug switches the api into debug mode -// When in debug mode, it logs various info about what its doing -// If you ever use this in production, don't call SetDebug(true) -func (api *Client) SetDebug(debug bool) { - api.debug = debug - if debug && logger == nil { - SetLogger(log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile)) - } + return &responseFull.AuthTestResponse, responseFull.Err() } // Debugf print a formatted debug line. func (api *Client) Debugf(format string, v ...interface{}) { if api.debug { - logger.Output(2, fmt.Sprintf(format, v...)) + api.log.Output(2, fmt.Sprintf(format, v...)) } } // Debugln print a debug line. func (api *Client) Debugln(v ...interface{}) { if api.debug { - logger.Output(2, fmt.Sprintln(v...)) + api.log.Output(2, fmt.Sprintln(v...)) } } + +// Debug returns if debug is enabled. +func (api *Client) Debug() bool { + return api.debug +} diff --git a/vendor/github.com/nlopes/slack/stars.go b/vendor/github.com/nlopes/slack/stars.go index c1e2f6cb..7e1e621d 100644 --- a/vendor/github.com/nlopes/slack/stars.go +++ b/vendor/github.com/nlopes/slack/stars.go @@ -58,7 +58,7 @@ func (api *Client) AddStarContext(ctx context.Context, channel string, item Item } response := &SlackResponse{} - if err := postSlackMethod(ctx, api.httpclient, "stars.add", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "stars.add", values, response, api); err != nil { return err } @@ -87,7 +87,7 @@ func (api *Client) RemoveStarContext(ctx context.Context, channel string, item I } response := &SlackResponse{} - if err := postSlackMethod(ctx, api.httpclient, "stars.remove", values, response, api.debug); err != nil { + if err := postSlackMethod(ctx, api.httpclient, "stars.remove", values, response, api); err != nil { return err } @@ -115,7 +115,7 @@ func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters) } response := &listResponseFull{} - err := postSlackMethod(ctx, api.httpclient, "stars.list", values, response, api.debug) + err := postSlackMethod(ctx, api.httpclient, "stars.list", values, response, api) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/nlopes/slack/team.go b/vendor/github.com/nlopes/slack/team.go index b6e341eb..1892cf5f 100644 --- a/vendor/github.com/nlopes/slack/team.go +++ b/vendor/github.com/nlopes/slack/team.go @@ -2,7 +2,6 @@ package slack import ( "context" - "errors" "net/url" "strconv" ) @@ -67,44 +66,33 @@ func NewAccessLogParameters() AccessLogParameters { } } -func teamRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*TeamResponse, error) { +func teamRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*TeamResponse, error) { response := &TeamResponse{} - err := postSlackMethod(ctx, client, path, values, response, debug) + err := postSlackMethod(ctx, client, path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - - return response, nil + return response, response.Err() } -func billableInfoRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (map[string]BillingActive, error) { +func billableInfoRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (map[string]BillingActive, error) { response := &BillableInfoResponse{} - err := postSlackMethod(ctx, client, path, values, response, debug) + err := postSlackMethod(ctx, client, path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - - return response.BillableInfo, nil + return response.BillableInfo, response.Err() } -func accessLogsRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*LoginResponse, error) { +func accessLogsRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*LoginResponse, error) { response := &LoginResponse{} - err := postSlackMethod(ctx, client, path, values, response, debug) + err := postSlackMethod(ctx, client, path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + return response, response.Err() } // GetTeamInfo gets the Team Information of the user @@ -118,7 +106,7 @@ func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) { "token": {api.token}, } - response, err := teamRequest(ctx, api.httpclient, "team.info", values, api.debug) + response, err := teamRequest(ctx, api.httpclient, "team.info", values, api) if err != nil { return nil, err } @@ -142,7 +130,7 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar values.Add("page", strconv.Itoa(params.Page)) } - response, err := accessLogsRequest(ctx, api.httpclient, "team.accessLogs", values, api.debug) + response, err := accessLogsRequest(ctx, api.httpclient, "team.accessLogs", values, api) if err != nil { return nil, nil, err } @@ -159,7 +147,7 @@ func (api *Client) GetBillableInfoContext(ctx context.Context, user string) (map "user": {user}, } - return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug) + return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api) } // GetBillableInfoForTeam returns the billing_active status of all users on the team. @@ -173,5 +161,5 @@ func (api *Client) GetBillableInfoForTeamContext(ctx context.Context) (map[strin "token": {api.token}, } - return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug) + return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api) } diff --git a/vendor/github.com/nlopes/slack/usergroups.go b/vendor/github.com/nlopes/slack/usergroups.go index cc9bc4ca..9e145272 100644 --- a/vendor/github.com/nlopes/slack/usergroups.go +++ b/vendor/github.com/nlopes/slack/usergroups.go @@ -2,7 +2,6 @@ package slack import ( "context" - "errors" "net/url" "strings" ) @@ -41,16 +40,14 @@ type userGroupResponseFull struct { SlackResponse } -func userGroupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userGroupResponseFull, error) { +func userGroupRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*userGroupResponseFull, error) { response := &userGroupResponseFull{} - err := postSlackMethod(ctx, client, path, values, response, debug) + err := postSlackMethod(ctx, client, path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + + return response, response.Err() } // CreateUserGroup creates a new user group @@ -77,7 +74,7 @@ func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGro values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")} } - response, err := userGroupRequest(ctx, api.httpclient, "usergroups.create", values, api.debug) + response, err := userGroupRequest(ctx, api.httpclient, "usergroups.create", values, api) if err != nil { return UserGroup{}, err } @@ -96,7 +93,7 @@ func (api *Client) DisableUserGroupContext(ctx context.Context, userGroup string "usergroup": {userGroup}, } - response, err := userGroupRequest(ctx, api.httpclient, "usergroups.disable", values, api.debug) + response, err := userGroupRequest(ctx, api.httpclient, "usergroups.disable", values, api) if err != nil { return UserGroup{}, err } @@ -115,7 +112,7 @@ func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string) "usergroup": {userGroup}, } - response, err := userGroupRequest(ctx, api.httpclient, "usergroups.enable", values, api.debug) + response, err := userGroupRequest(ctx, api.httpclient, "usergroups.enable", values, api) if err != nil { return UserGroup{}, err } @@ -179,7 +176,7 @@ func (api *Client) GetUserGroupsContext(ctx context.Context, options ...GetUserG values.Add("include_users", "true") } - response, err := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api.debug) + response, err := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api) if err != nil { return nil, err } @@ -210,7 +207,7 @@ func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGro values["description"] = []string{userGroup.Description} } - response, err := userGroupRequest(ctx, api.httpclient, "usergroups.update", values, api.debug) + response, err := userGroupRequest(ctx, api.httpclient, "usergroups.update", values, api) if err != nil { return UserGroup{}, err } @@ -229,7 +226,7 @@ func (api *Client) GetUserGroupMembersContext(ctx context.Context, userGroup str "usergroup": {userGroup}, } - response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.list", values, api.debug) + response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.list", values, api) if err != nil { return []string{}, err } @@ -249,7 +246,7 @@ func (api *Client) UpdateUserGroupMembersContext(ctx context.Context, userGroup "users": {members}, } - response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.update", values, api.debug) + response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.update", values, api) if err != nil { return UserGroup{}, err } diff --git a/vendor/github.com/nlopes/slack/users.go b/vendor/github.com/nlopes/slack/users.go index 0dd20db5..aa941a74 100644 --- a/vendor/github.com/nlopes/slack/users.go +++ b/vendor/github.com/nlopes/slack/users.go @@ -189,16 +189,14 @@ func NewUserSetPhotoParams() UserSetPhotoParams { } } -func userRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userResponseFull, error) { +func userRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*userResponseFull, error) { response := &userResponseFull{} - err := postForm(ctx, client, SLACK_API+path, values, response, debug) + err := postForm(ctx, client, APIURL+path, values, response, d) if err != nil { return nil, err } - if !response.Ok { - return nil, errors.New(response.Error) - } - return response, nil + + return response, response.Err() } // GetUserPresence will retrieve the current presence status of given user. @@ -213,7 +211,7 @@ func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*Us "user": {user}, } - response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api.debug) + response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api) if err != nil { return nil, err } @@ -232,7 +230,7 @@ func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, "user": {user}, } - response, err := userRequest(ctx, api.httpclient, "users.info", values, api.debug) + response, err := userRequest(ctx, api.httpclient, "users.info", values, api) if err != nil { return nil, err } @@ -310,7 +308,7 @@ func (t UserPagination) Next(ctx context.Context) (_ UserPagination, err error) "cursor": {t.previousResp.Cursor}, } - if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c.debug); err != nil { + if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c); err != nil { return t, err } @@ -355,7 +353,7 @@ func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*Us "token": {api.token}, "email": {email}, } - response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api.debug) + response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api) if err != nil { return nil, err } @@ -373,7 +371,7 @@ func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) { "token": {api.token}, } - _, err = userRequest(ctx, api.httpclient, "users.setActive", values, api.debug) + _, err = userRequest(ctx, api.httpclient, "users.setActive", values, api) return err } @@ -389,7 +387,7 @@ func (api *Client) SetUserPresenceContext(ctx context.Context, presence string) "presence": {presence}, } - _, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api.debug) + _, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api) return err } @@ -405,7 +403,7 @@ func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityRes } response := &UserIdentityResponse{} - err := postForm(ctx, api.httpclient, SLACK_API+"users.identity", values, response, api.debug) + err := postForm(ctx, api.httpclient, APIURL+"users.identity", values, response, api) if err != nil { return nil, err } @@ -436,7 +434,7 @@ func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params values.Add("crop_w", strconv.Itoa(params.CropW)) } - err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api.debug) + err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api) if err != nil { return err } @@ -456,7 +454,7 @@ func (api *Client) DeleteUserPhotoContext(ctx context.Context) error { "token": {api.token}, } - err := postForm(ctx, api.httpclient, SLACK_API+"users.deletePhoto", values, response, api.debug) + err := postForm(ctx, api.httpclient, APIURL+"users.deletePhoto", values, response, api) if err != nil { return err } @@ -506,7 +504,7 @@ func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, s } response := &userResponseFull{} - if err = postForm(ctx, api.httpclient, SLACK_API+"users.profile.set", values, response, api.debug); err != nil { + if err = postForm(ctx, api.httpclient, APIURL+"users.profile.set", values, response, api); err != nil { return err } @@ -547,7 +545,7 @@ func (api *Client) GetUserProfileContext(ctx context.Context, userID string, inc } resp := &getUserProfileResponse{} - err := postSlackMethod(ctx, api.httpclient, "users.profile.get", values, &resp, api.debug) + err := postSlackMethod(ctx, api.httpclient, "users.profile.get", values, &resp, api) if err != nil { return nil, err } diff --git a/vendor/github.com/nlopes/slack/websocket.go b/vendor/github.com/nlopes/slack/websocket.go index 242acf40..ec810a9b 100644 --- a/vendor/github.com/nlopes/slack/websocket.go +++ b/vendor/github.com/nlopes/slack/websocket.go @@ -3,6 +3,7 @@ package slack import ( "encoding/json" "errors" + "net/url" "sync" "time" @@ -20,6 +21,9 @@ const ( // // Create this element with Client's NewRTM() or NewRTMWithOptions(*RTMOptions) type RTM struct { + // Client is the main API, embedded + Client + idGen IDGenerator pingInterval time.Duration pingDeadman *time.Timer @@ -35,8 +39,6 @@ type RTM struct { wasIntentional bool isConnected bool - // Client is the main API, embedded - Client websocketURL string // UserDetails upon connection @@ -53,18 +55,9 @@ type RTM struct { // mu is mutex used to prevent RTM connection race conditions mu *sync.Mutex -} -// RTMOptions allows configuration of various options available for RTM messaging -// -// This structure will evolve in time so please make sure you are always using the -// named keys for every entry available as per Go 1 compatibility promise adding fields -// to this structure should not be considered a breaking change. -type RTMOptions struct { - // UseRTMStart set to true in order to use rtm.start or false to use rtm.connect - // As of 11th July 2017 you should prefer setting this to false, see: - // https://api.slack.com/changelog/2017-04-start-using-rtm-connect-and-stop-using-rtm-start - UseRTMStart bool + // connParams is a map of flags for connection parameters. + connParams url.Values } // Disconnect and wait, blocking until a successful disconnection. diff --git a/vendor/github.com/nlopes/slack/websocket_managed_conn.go b/vendor/github.com/nlopes/slack/websocket_managed_conn.go index e8ab65a1..62157910 100644 --- a/vendor/github.com/nlopes/slack/websocket_managed_conn.go +++ b/vendor/github.com/nlopes/slack/websocket_managed_conn.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/http" + stdurl "net/url" "reflect" "time" @@ -157,6 +158,14 @@ func (rtm *RTM) startRTMAndDial(useRTMStart bool) (info *Info, _ *websocket.Conn return nil, nil, err } + // install connection parameters + u, err := stdurl.Parse(url) + if err != nil { + return nil, nil, err + } + u.RawQuery = rtm.connParams.Encode() + url = u.String() + rtm.Debugf("Dialing to websocket on url %s", url) // Only use HTTPS for connections to prevent MITM attacks on the connection. upgradeHeader := http.Header{} @@ -274,7 +283,7 @@ func (rtm *RTM) sendWithDeadline(msg interface{}) error { // and instead lets a future failed 'PING' detect the failed connection. func (rtm *RTM) sendOutgoingMessage(msg OutgoingMessage) { rtm.Debugln("Sending message:", msg) - if len(msg.Text) > MaxMessageTextLength { + if len([]rune(msg.Text)) > MaxMessageTextLength { rtm.IncomingEvents <- RTMEvent{"outgoing_error", &MessageTooLongEvent{ Message: msg, MaxLength: MaxMessageTextLength, @@ -405,8 +414,7 @@ func (rtm *RTM) handlePong(event json.RawMessage) { rtm.resetDeadman() if err := json.Unmarshal(event, &p); err != nil { - logger.Println("RTM Error unmarshalling 'pong' event:", err) - rtm.Debugln(" -> Erroneous 'ping' event:", string(event)) + rtm.Client.log.Println("RTM Error unmarshalling 'pong' event:", err) return } @@ -423,8 +431,8 @@ func (rtm *RTM) handlePong(event json.RawMessage) { func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) { v, exists := EventMapping[typeStr] if !exists { - rtm.Debugf("RTM Error, received unmapped event %q: %s\n", typeStr, string(event)) - err := fmt.Errorf("RTM Error: Received unmapped event %q: %s\n", typeStr, string(event)) + rtm.Debugf("RTM Error - received unmapped event %q: %s\n", typeStr, string(event)) + err := fmt.Errorf("RTM Error: Received unmapped event %q: %s", typeStr, string(event)) rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}} return } @@ -433,7 +441,7 @@ func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) { err := json.Unmarshal(event, recvEvent) if err != nil { rtm.Debugf("RTM Error, could not unmarshall event %q: %s\n", typeStr, string(event)) - err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s\n", typeStr, string(event)) + err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s", typeStr, string(event)) rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}} return } diff --git a/vendor/github.com/nlopes/slack/websocket_misc.go b/vendor/github.com/nlopes/slack/websocket_misc.go index 16f48c74..bfcc805e 100644 --- a/vendor/github.com/nlopes/slack/websocket_misc.go +++ b/vendor/github.com/nlopes/slack/websocket_misc.go @@ -43,9 +43,10 @@ type HelloEvent struct{} // PresenceChangeEvent represents the presence change event type PresenceChangeEvent struct { - Type string `json:"type"` - Presence string `json:"presence"` - User string `json:"user"` + Type string `json:"type"` + Presence string `json:"presence"` + User string `json:"user"` + Users []string `json:"users"` } // UserTypingEvent represents the user typing event diff --git a/vendor/modules.txt b/vendor/modules.txt index 84e4822f..a665570f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -96,7 +96,7 @@ github.com/nicksnyder/go-i18n/i18n github.com/nicksnyder/go-i18n/i18n/bundle github.com/nicksnyder/go-i18n/i18n/language github.com/nicksnyder/go-i18n/i18n/translation -# github.com/nlopes/slack v0.4.0 +# github.com/nlopes/slack v0.4.1-0.20181111125009-5963eafd777b github.com/nlopes/slack github.com/nlopes/slack/slackutilsx # github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83