diff options
Diffstat (limited to 'vendor/github.com/nlopes/slack/chat.go')
-rw-r--r-- | vendor/github.com/nlopes/slack/chat.go | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/vendor/github.com/nlopes/slack/chat.go b/vendor/github.com/nlopes/slack/chat.go new file mode 100644 index 00000000..0eb042df --- /dev/null +++ b/vendor/github.com/nlopes/slack/chat.go @@ -0,0 +1,376 @@ +package slack + +import ( + "context" + "encoding/json" + "errors" + "net/url" + "strings" +) + +const ( + DEFAULT_MESSAGE_USERNAME = "" + DEFAULT_MESSAGE_THREAD_TIMESTAMP = "" + DEFAULT_MESSAGE_REPLY_BROADCAST = false + DEFAULT_MESSAGE_ASUSER = false + DEFAULT_MESSAGE_PARSE = "" + DEFAULT_MESSAGE_LINK_NAMES = 0 + DEFAULT_MESSAGE_UNFURL_LINKS = false + DEFAULT_MESSAGE_UNFURL_MEDIA = true + DEFAULT_MESSAGE_ICON_URL = "" + DEFAULT_MESSAGE_ICON_EMOJI = "" + DEFAULT_MESSAGE_MARKDOWN = true + DEFAULT_MESSAGE_ESCAPE_TEXT = true +) + +type chatResponseFull struct { + Channel string `json:"channel"` + Timestamp string `json:"ts"` + Text string `json:"text"` + SlackResponse +} + +// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request +type PostMessageParameters struct { + Text string `json:"text"` + Username string `json:"user_name"` + AsUser bool `json:"as_user"` + Parse string `json:"parse"` + ThreadTimestamp string `json:"thread_ts"` + ReplyBroadcast bool `json:"reply_broadcast"` + LinkNames int `json:"link_names"` + Attachments []Attachment `json:"attachments"` + UnfurlLinks bool `json:"unfurl_links"` + UnfurlMedia bool `json:"unfurl_media"` + IconURL string `json:"icon_url"` + IconEmoji string `json:"icon_emoji"` + Markdown bool `json:"mrkdwn,omitempty"` + EscapeText bool `json:"escape_text"` + + // chat.postEphemeral support + Channel string `json:"channel"` + User string `json:"user"` +} + +// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set +func NewPostMessageParameters() PostMessageParameters { + return PostMessageParameters{ + Username: DEFAULT_MESSAGE_USERNAME, + User: DEFAULT_MESSAGE_USERNAME, + AsUser: DEFAULT_MESSAGE_ASUSER, + Parse: DEFAULT_MESSAGE_PARSE, + LinkNames: DEFAULT_MESSAGE_LINK_NAMES, + Attachments: nil, + UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS, + UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA, + IconURL: DEFAULT_MESSAGE_ICON_URL, + IconEmoji: DEFAULT_MESSAGE_ICON_EMOJI, + Markdown: DEFAULT_MESSAGE_MARKDOWN, + EscapeText: DEFAULT_MESSAGE_ESCAPE_TEXT, + } +} + +// DeleteMessage deletes a message in a channel +func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) { + respChannel, respTimestamp, _, err := api.SendMessageContext(context.Background(), channel, MsgOptionDelete(messageTimestamp)) + return respChannel, respTimestamp, err +} + +// DeleteMessageContext deletes a message in a channel with a custom context +func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTimestamp string) (string, string, error) { + respChannel, respTimestamp, _, err := api.SendMessageContext(ctx, channel, MsgOptionDelete(messageTimestamp)) + return respChannel, respTimestamp, err +} + +// 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) { + respChannel, respTimestamp, _, err := api.SendMessageContext( + context.Background(), + channel, + MsgOptionText(text, params.EscapeText), + MsgOptionAttachments(params.Attachments...), + MsgOptionPostMessageParameters(params), + ) + 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) { + respChannel, respTimestamp, _, err := api.SendMessageContext( + ctx, + channel, + MsgOptionText(text, params.EscapeText), + MsgOptionAttachments(params.Attachments...), + MsgOptionPostMessageParameters(params), + ) + return respChannel, respTimestamp, err +} + +// PostEphemeral sends an ephemeral message to a user in 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) PostEphemeral(channel, userID string, options ...MsgOption) (string, error) { + options = append(options, MsgOptionPostEphemeral()) + return api.PostEphemeralContext( + context.Background(), + channel, + userID, + options..., + ) +} + +// 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, channel, userID string, options ...MsgOption) (string, error) { + path, values, err := ApplyMsgOptions(api.config.token, channel, options...) + if err != nil { + return "", err + } + + values.Add("user", userID) + + response, err := chatRequest(ctx, path, values, api.debug) + if err != nil { + return "", err + } + + return response.Timestamp, nil +} + +// UpdateMessage updates a message in a channel +func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) { + return api.UpdateMessageContext(context.Background(), channel, timestamp, text) +} + +// UpdateMessage updates a message in a channel +func (api *Client) UpdateMessageContext(ctx context.Context, channel, timestamp, text string) (string, string, string, error) { + return api.SendMessageContext(ctx, channel, MsgOptionUpdate(timestamp), MsgOptionText(text, true)) +} + +// SendMessage more flexible method for configuring messages. +func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) { + return api.SendMessageContext(context.Background(), channel, options...) +} + +// SendMessageContext more flexible method for configuring messages with a custom context. +func (api *Client) SendMessageContext(ctx context.Context, channel string, options ...MsgOption) (string, string, string, error) { + channel, values, err := ApplyMsgOptions(api.config.token, channel, options...) + if err != nil { + return "", "", "", err + } + + response, err := chatRequest(ctx, channel, values, api.debug) + if err != nil { + return "", "", "", err + } + + return response.Channel, response.Timestamp, response.Text, nil +} + +// ApplyMsgOptions utility function for debugging/testing chat requests. +func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) { + config := sendConfig{ + mode: chatPostMessage, + values: url.Values{ + "token": {token}, + "channel": {channel}, + }, + } + + for _, opt := range options { + if err := opt(&config); err != nil { + return string(config.mode), config.values, err + } + } + + return string(config.mode), config.values, nil +} + +func escapeMessage(message string) string { + replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">") + return replacer.Replace(message) +} + +func chatRequest(ctx context.Context, path string, values url.Values, debug bool) (*chatResponseFull, error) { + response := &chatResponseFull{} + err := post(ctx, path, values, response, debug) + if err != nil { + return nil, err + } + if !response.Ok { + return nil, errors.New(response.Error) + } + return response, nil +} + +type sendMode string + +const ( + chatUpdate sendMode = "chat.update" + chatPostMessage sendMode = "chat.postMessage" + chatDelete sendMode = "chat.delete" + chatPostEphemeral sendMode = "chat.postEphemeral" +) + +type sendConfig struct { + mode sendMode + values url.Values +} + +// MsgOption option provided when sending a message. +type MsgOption func(*sendConfig) error + +// MsgOptionPost posts a messages, this is the default. +func MsgOptionPost() MsgOption { + return func(config *sendConfig) error { + config.mode = chatPostMessage + config.values.Del("ts") + return nil + } +} + +// MsgOptionPostEphemeral posts an ephemeral message +func MsgOptionPostEphemeral() MsgOption { + return func(config *sendConfig) error { + config.mode = chatPostEphemeral + config.values.Del("ts") + return nil + } +} + +// MsgOptionUpdate updates a message based on the timestamp. +func MsgOptionUpdate(timestamp string) MsgOption { + return func(config *sendConfig) error { + config.mode = chatUpdate + config.values.Add("ts", timestamp) + return nil + } +} + +// MsgOptionDelete deletes a message based on the timestamp. +func MsgOptionDelete(timestamp string) MsgOption { + return func(config *sendConfig) error { + config.mode = chatDelete + config.values.Add("ts", timestamp) + return nil + } +} + +// MsgOptionAsUser whether or not to send the message as the user. +func MsgOptionAsUser(b bool) MsgOption { + return func(config *sendConfig) error { + if b != DEFAULT_MESSAGE_ASUSER { + config.values.Set("as_user", "true") + } + return nil + } +} + +// MsgOptionText provide the text for the message, optionally escape the provided +// text. +func MsgOptionText(text string, escape bool) MsgOption { + return func(config *sendConfig) error { + if escape { + text = escapeMessage(text) + } + config.values.Add("text", text) + return nil + } +} + +// MsgOptionAttachments provide attachments for the message. +func MsgOptionAttachments(attachments ...Attachment) MsgOption { + return func(config *sendConfig) error { + if attachments == nil { + return nil + } + + attachments, err := json.Marshal(attachments) + if err == nil { + config.values.Set("attachments", string(attachments)) + } + return err + } +} + +// MsgOptionEnableLinkUnfurl enables link unfurling +func MsgOptionEnableLinkUnfurl() MsgOption { + return func(config *sendConfig) error { + config.values.Set("unfurl_links", "true") + return nil + } +} + +// MsgOptionDisableMediaUnfurl disables media unfurling. +func MsgOptionDisableMediaUnfurl() MsgOption { + return func(config *sendConfig) error { + config.values.Set("unfurl_media", "false") + return nil + } +} + +// MsgOptionDisableMarkdown disables markdown. +func MsgOptionDisableMarkdown() MsgOption { + return func(config *sendConfig) error { + config.values.Set("mrkdwn", "false") + return nil + } +} + +// MsgOptionPostMessageParameters maintain backwards compatibility. +func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption { + return func(config *sendConfig) error { + if params.Username != DEFAULT_MESSAGE_USERNAME { + config.values.Set("username", string(params.Username)) + } + + // chat.postEphemeral support + if params.User != DEFAULT_MESSAGE_USERNAME { + config.values.Set("user", params.User) + } + + // never generates an error. + MsgOptionAsUser(params.AsUser)(config) + + if params.Parse != DEFAULT_MESSAGE_PARSE { + config.values.Set("parse", string(params.Parse)) + } + if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES { + config.values.Set("link_names", "1") + } + + if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS { + config.values.Set("unfurl_links", "true") + } + + // I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request. + // Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side. + if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS { + config.values.Set("unfurl_links", "false") + } + if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA { + config.values.Set("unfurl_media", "false") + } + if params.IconURL != DEFAULT_MESSAGE_ICON_URL { + config.values.Set("icon_url", params.IconURL) + } + if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI { + config.values.Set("icon_emoji", params.IconEmoji) + } + if params.Markdown != DEFAULT_MESSAGE_MARKDOWN { + config.values.Set("mrkdwn", "false") + } + + if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP { + config.values.Set("thread_ts", params.ThreadTimestamp) + } + if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST { + config.values.Set("reply_broadcast", "true") + } + + return nil + } +} |