summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/slack-go/slack/chat.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/slack-go/slack/chat.go')
-rw-r--r--vendor/github.com/slack-go/slack/chat.go627
1 files changed, 627 insertions, 0 deletions
diff --git a/vendor/github.com/slack-go/slack/chat.go b/vendor/github.com/slack-go/slack/chat.go
new file mode 100644
index 00000000..b54f8d6d
--- /dev/null
+++ b/vendor/github.com/slack-go/slack/chat.go
@@ -0,0 +1,627 @@
+package slack
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "net/url"
+
+ "github.com/slack-go/slack/slackutilsx"
+)
+
+const (
+ DEFAULT_MESSAGE_USERNAME = ""
+ DEFAULT_MESSAGE_REPLY_BROADCAST = false
+ DEFAULT_MESSAGE_ASUSER = false
+ DEFAULT_MESSAGE_PARSE = ""
+ DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
+ 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"` //Regular message timestamp
+ MessageTimeStamp string `json:"message_ts"` //Ephemeral message timestamp
+ Text string `json:"text"`
+ SlackResponse
+}
+
+// getMessageTimestamp will inspect the `chatResponseFull` to ruturn a timestamp value
+// in `chat.postMessage` its under `ts`
+// in `chat.postEphemeral` its under `message_ts`
+func (c chatResponseFull) getMessageTimestamp() string {
+ if len(c.Timestamp) > 0 {
+ return c.Timestamp
+ }
+ return c.MessageTimeStamp
+}
+
+// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
+type PostMessageParameters struct {
+ Username string `json:"username"`
+ AsUser bool `json:"as_user"`
+ Parse string `json:"parse"`
+ ThreadTimestamp string `json:"thread_ts"`
+ ReplyBroadcast bool `json:"reply_broadcast"`
+ LinkNames int `json:"link_names"`
+ 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,
+ ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP,
+ LinkNames: DEFAULT_MESSAGE_LINK_NAMES,
+ 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(channelID string, options ...MsgOption) (string, string, error) {
+ respChannel, respTimestamp, _, err := api.SendMessageContext(
+ context.Background(),
+ 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, channelID string, options ...MsgOption) (string, string, error) {
+ respChannel, respTimestamp, _, err := api.SendMessageContext(
+ ctx,
+ channelID,
+ MsgOptionPost(),
+ MsgOptionCompose(options...),
+ )
+ 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(channelID, userID string, options ...MsgOption) (string, error) {
+ return api.PostEphemeralContext(
+ context.Background(),
+ channelID,
+ 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, channelID, userID string, options ...MsgOption) (timestamp string, err error) {
+ _, timestamp, _, err = api.SendMessageContext(ctx, channelID, MsgOptionPostEphemeral(userID), MsgOptionCompose(options...))
+ return timestamp, err
+}
+
+// UpdateMessage updates a message in a channel
+func (api *Client) UpdateMessage(channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
+ return api.SendMessageContext(context.Background(), channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
+}
+
+// UpdateMessageContext updates a message in a channel
+func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
+ return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
+}
+
+// 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.
+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, channelID string, options ...MsgOption) (_channel string, _timestamp string, _text string, err error) {
+ var (
+ req *http.Request
+ parser func(*chatResponseFull) responseParser
+ response chatResponseFull
+ )
+
+ if req, parser, err = buildSender(api.endpoint, options...).BuildRequest(api.token, channelID); err != nil {
+ return "", "", "", err
+ }
+
+ if err = doPost(ctx, api.httpclient, req, parser(&response), api); err != nil {
+ return "", "", "", err
+ }
+
+ return response.Channel, response.getMessageTimestamp(), response.Text, response.Err()
+}
+
+// UnsafeApplyMsgOptions utility function for debugging/testing chat requests.
+// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this function
+// will be supported by the library.
+func UnsafeApplyMsgOptions(token, channel, apiurl string, options ...MsgOption) (string, url.Values, error) {
+ config, err := applyMsgOptions(token, channel, apiurl, options...)
+ return config.endpoint, config.values, err
+}
+
+func applyMsgOptions(token, channel, apiurl string, options ...MsgOption) (sendConfig, error) {
+ config := sendConfig{
+ apiurl: apiurl,
+ endpoint: apiurl + string(chatPostMessage),
+ values: url.Values{
+ "token": {token},
+ "channel": {channel},
+ },
+ }
+
+ for _, opt := range options {
+ if err := opt(&config); err != nil {
+ return config, err
+ }
+ }
+
+ return config, nil
+}
+
+func buildSender(apiurl string, options ...MsgOption) sendConfig {
+ return sendConfig{
+ apiurl: apiurl,
+ options: options,
+ }
+}
+
+type sendMode string
+
+const (
+ chatUpdate sendMode = "chat.update"
+ chatPostMessage sendMode = "chat.postMessage"
+ chatDelete sendMode = "chat.delete"
+ chatPostEphemeral sendMode = "chat.postEphemeral"
+ chatResponse sendMode = "chat.responseURL"
+ chatMeMessage sendMode = "chat.meMessage"
+ chatUnfurl sendMode = "chat.unfurl"
+)
+
+type sendConfig struct {
+ apiurl string
+ options []MsgOption
+ mode sendMode
+ endpoint string
+ values url.Values
+ attachments []Attachment
+ blocks Blocks
+ responseType string
+}
+
+func (t sendConfig) BuildRequest(token, channelID string) (req *http.Request, _ func(*chatResponseFull) responseParser, err error) {
+ if t, err = applyMsgOptions(token, channelID, t.apiurl, t.options...); err != nil {
+ return nil, nil, err
+ }
+
+ switch t.mode {
+ case chatResponse:
+ return responseURLSender{
+ endpoint: t.endpoint,
+ values: t.values,
+ attachments: t.attachments,
+ blocks: t.blocks,
+ responseType: t.responseType,
+ }.BuildRequest()
+ default:
+ return formSender{endpoint: t.endpoint, values: t.values}.BuildRequest()
+ }
+}
+
+type formSender struct {
+ endpoint string
+ values url.Values
+}
+
+func (t formSender) BuildRequest() (*http.Request, func(*chatResponseFull) responseParser, error) {
+ req, err := formReq(t.endpoint, t.values)
+ return req, func(resp *chatResponseFull) responseParser {
+ return newJSONParser(resp)
+ }, err
+}
+
+type responseURLSender struct {
+ endpoint string
+ values url.Values
+ attachments []Attachment
+ blocks Blocks
+ responseType string
+}
+
+func (t responseURLSender) BuildRequest() (*http.Request, func(*chatResponseFull) responseParser, error) {
+ req, err := jsonReq(t.endpoint, Msg{
+ Text: t.values.Get("text"),
+ Timestamp: t.values.Get("ts"),
+ Attachments: t.attachments,
+ Blocks: t.blocks,
+ ResponseType: t.responseType,
+ })
+ return req, func(resp *chatResponseFull) responseParser {
+ return newContentTypeParser(resp)
+ }, err
+}
+
+// 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.endpoint = config.apiurl + string(chatPostMessage)
+ config.values.Del("ts")
+ return nil
+ }
+}
+
+// MsgOptionPostEphemeral - posts an ephemeral message to the provided user.
+func MsgOptionPostEphemeral(userID string) MsgOption {
+ return func(config *sendConfig) error {
+ config.endpoint = config.apiurl + string(chatPostEphemeral)
+ MsgOptionUser(userID)(config)
+ config.values.Del("ts")
+
+ return nil
+ }
+}
+
+// MsgOptionMeMessage posts a "me message" type from the calling user
+func MsgOptionMeMessage() MsgOption {
+ return func(config *sendConfig) error {
+ config.endpoint = config.apiurl + string(chatMeMessage)
+ return nil
+ }
+}
+
+// MsgOptionUpdate updates a message based on the timestamp.
+func MsgOptionUpdate(timestamp string) MsgOption {
+ return func(config *sendConfig) error {
+ config.endpoint = config.apiurl + string(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.endpoint = config.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 = config.apiurl + string(chatUnfurl)
+ config.values.Add("ts", timestamp)
+ unfurlsStr, err := json.Marshal(unfurls)
+ if err == nil {
+ config.values.Add("unfurls", string(unfurlsStr))
+ }
+ return err
+ }
+}
+
+// MsgOptionResponseURL supplies a url to use as the endpoint.
+func MsgOptionResponseURL(url string, responseType string) MsgOption {
+ return func(config *sendConfig) error {
+ config.mode = chatResponse
+ config.endpoint = url
+ config.responseType = responseType
+ config.values.Del("ts")
+ 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
+ }
+}
+
+// MsgOptionUser set the user for the message.
+func MsgOptionUser(userID string) MsgOption {
+ return func(config *sendConfig) error {
+ config.values.Set("user", userID)
+ return nil
+ }
+}
+
+// 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 {
+ return func(config *sendConfig) error {
+ if escape {
+ text = slackutilsx.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
+ }
+
+ config.attachments = attachments
+
+ // FIXME: We are setting the attachments on the message twice: above for
+ // the json version, and below for the html version. The marshalled bytes
+ // we put into config.values below don't work directly in the Msg version.
+
+ attachmentBytes, err := json.Marshal(attachments)
+ if err == nil {
+ config.values.Set("attachments", string(attachmentBytes))
+ }
+
+ return err
+ }
+}
+
+// MsgOptionBlocks sets blocks for the message
+func MsgOptionBlocks(blocks ...Block) MsgOption {
+ return func(config *sendConfig) error {
+ if blocks == nil {
+ return nil
+ }
+
+ config.blocks.BlockSet = append(config.blocks.BlockSet, blocks...)
+
+ blocks, err := json.Marshal(blocks)
+ if err == nil {
+ config.values.Set("blocks", string(blocks))
+ }
+ return err
+ }
+}
+
+// MsgOptionEnableLinkUnfurl enables link unfurling
+func MsgOptionEnableLinkUnfurl() MsgOption {
+ return func(config *sendConfig) error {
+ config.values.Set("unfurl_links", "true")
+ return nil
+ }
+}
+
+// MsgOptionDisableLinkUnfurl disables link unfurling
+func MsgOptionDisableLinkUnfurl() MsgOption {
+ return func(config *sendConfig) error {
+ config.values.Set("unfurl_links", "false")
+ 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
+ }
+}
+
+// MsgOptionTS sets the thread TS of the message to enable creating or replying to a thread
+func MsgOptionTS(ts string) MsgOption {
+ return func(config *sendConfig) error {
+ config.values.Set("thread_ts", ts)
+ return nil
+ }
+}
+
+// MsgOptionBroadcast sets reply_broadcast to true
+func MsgOptionBroadcast() MsgOption {
+ return func(config *sendConfig) error {
+ config.values.Set("reply_broadcast", "true")
+ return nil
+ }
+}
+
+// MsgOptionCompose combines multiple options into a single option.
+func MsgOptionCompose(options ...MsgOption) MsgOption {
+ return func(c *sendConfig) error {
+ for _, opt := range options {
+ if err := opt(c); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+}
+
+// MsgOptionParse set parse option.
+func MsgOptionParse(b bool) MsgOption {
+ return func(c *sendConfig) error {
+ var v string
+ if b {
+ v = "full"
+ } else {
+ v = "none"
+ }
+ c.values.Set("parse", v)
+ return nil
+ }
+}
+
+// MsgOptionIconURL sets an icon URL
+func MsgOptionIconURL(iconURL string) MsgOption {
+ return func(c *sendConfig) error {
+ c.values.Set("icon_url", iconURL)
+ return nil
+ }
+}
+
+// MsgOptionIconEmoji sets an icon emoji
+func MsgOptionIconEmoji(iconEmoji string) MsgOption {
+ return func(c *sendConfig) error {
+ c.values.Set("icon_emoji", iconEmoji)
+ return nil
+ }
+}
+
+// UnsafeMsgOptionEndpoint deliver the message to the specified endpoint.
+// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this Option
+// will be supported by the library, it is subject to change without notice that
+// may result in compilation errors or runtime behaviour changes.
+func UnsafeMsgOptionEndpoint(endpoint string, update func(url.Values)) MsgOption {
+ return func(config *sendConfig) error {
+ config.endpoint = endpoint
+ update(config.values)
+ 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", 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", 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
+ }
+}
+
+// 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 := api.getMethod(ctx, "chat.getPermalink", values, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.Permalink, response.Err()
+}