matterbridge/vendor/github.com/nlopes/slack/chat.go

449 lines
14 KiB
Go
Raw Normal View History

2016-09-05 16:34:37 +02:00
package slack
import (
2017-07-16 14:29:46 +02:00
"context"
2016-09-05 16:34:37 +02:00
"encoding/json"
"net/url"
"strings"
)
const (
2017-03-25 20:45:10 +01:00
DEFAULT_MESSAGE_USERNAME = ""
2018-01-08 22:41:38 +01:00
DEFAULT_MESSAGE_REPLY_BROADCAST = false
2017-03-25 20:45:10 +01:00
DEFAULT_MESSAGE_ASUSER = false
DEFAULT_MESSAGE_PARSE = ""
2018-08-10 00:38:19 +02:00
DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
2017-03-25 20:45:10 +01:00
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
2016-09-05 16:34:37 +02:00
)
type chatResponseFull struct {
2018-08-10 00:38:19 +02:00
Channel string `json:"channel"`
Timestamp string `json:"ts"` //Regualr message timestamp
MessageTimeStamp string `json:"message_ts"` //Ephemeral message timestamp
Text string `json:"text"`
2016-09-05 16:34:37 +02:00
SlackResponse
}
2018-08-10 00:38:19 +02:00
// 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
}
2016-09-05 16:34:37 +02:00
// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
2018-08-10 00:38:19 +02:00
Username string `json:"username"`
2017-03-25 20:45:10 +01:00
AsUser bool `json:"as_user"`
Parse string `json:"parse"`
ThreadTimestamp string `json:"thread_ts"`
2018-01-08 22:41:38 +01:00
ReplyBroadcast bool `json:"reply_broadcast"`
2017-03-25 20:45:10 +01:00
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"`
2018-01-08 22:41:38 +01:00
// chat.postEphemeral support
Channel string `json:"channel"`
User string `json:"user"`
2016-09-05 16:34:37 +02:00
}
// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
return PostMessageParameters{
2018-08-10 00:38:19 +02:00
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,
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,
2016-09-05 16:34:37 +02:00
}
}
2017-07-16 14:29:46 +02:00
// 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
}
2018-01-08 22:41:38 +01:00
// 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.
2018-08-10 00:38:19 +02:00
func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption) (string, error) {
2018-01-08 22:41:38 +01:00
return api.PostEphemeralContext(
context.Background(),
2018-08-10 00:38:19 +02:00
channelID,
2018-01-08 22:41:38 +01:00
userID,
options...,
)
}
// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
// For more details, see PostEphemeral documentation
2018-08-10 00:38:19 +02:00
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))...)
return timestamp, err
2018-01-08 22:41:38 +01:00
}
2017-07-16 14:29:46 +02:00
// UpdateMessage updates a message in a channel
2018-08-10 00:38:19 +02:00
func (api *Client) UpdateMessage(channelID, timestamp, text string) (string, string, string, error) {
return api.UpdateMessageContext(context.Background(), channelID, timestamp, text)
2017-07-16 14:29:46 +02:00
}
2018-08-10 00:38:19 +02:00
// 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))
2017-07-16 14:29:46 +02:00
}
// 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.
2018-08-10 00:38:19 +02:00
func (api *Client) SendMessageContext(ctx context.Context, channelID string, options ...MsgOption) (channel string, timestamp string, text string, err error) {
var (
config sendConfig
response chatResponseFull
)
if config, err = applyMsgOptions(api.token, channelID, options...); err != nil {
2017-07-16 14:29:46 +02:00
return "", "", "", err
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
2018-08-10 00:38:19 +02:00
if err = postSlackMethod(ctx, api.httpclient, string(config.mode), config.values, &response, api.debug); err != nil {
2017-07-16 14:29:46 +02:00
return "", "", "", err
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
2018-08-10 00:38:19 +02:00
return response.Channel, response.getMessageTimestamp(), response.Text, response.Err()
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
// ApplyMsgOptions utility function for debugging/testing chat requests.
func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
2018-08-10 00:38:19 +02:00
config, err := applyMsgOptions(token, channel, options...)
return string(config.mode), config.values, err
}
func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) {
2017-07-16 14:29:46 +02:00
config := sendConfig{
mode: chatPostMessage,
values: url.Values{
"token": {token},
"channel": {channel},
},
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
for _, opt := range options {
if err := opt(&config); err != nil {
2018-08-10 00:38:19 +02:00
return config, err
2017-07-16 14:29:46 +02:00
}
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
2018-08-10 00:38:19 +02:00
return config, nil
2016-09-05 16:34:37 +02:00
}
func escapeMessage(message string) string {
replacer := strings.NewReplacer("&", "&amp;", "<", "&lt;", ">", "&gt;")
return replacer.Replace(message)
}
2017-07-16 14:29:46 +02:00
type sendMode string
const (
2018-01-08 22:41:38 +01:00
chatUpdate sendMode = "chat.update"
chatPostMessage sendMode = "chat.postMessage"
chatDelete sendMode = "chat.delete"
chatPostEphemeral sendMode = "chat.postEphemeral"
2018-08-10 00:38:19 +02:00
chatMeMessage sendMode = "chat.meMessage"
2017-07-16 14:29:46 +02:00
)
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
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
}
2018-08-10 00:38:19 +02:00
// MsgOptionPostEphemeral - DEPRECATED: use MsgOptionPostEphemeral2
// posts an ephemeral message.
2018-01-08 22:41:38 +01:00
func MsgOptionPostEphemeral() MsgOption {
return func(config *sendConfig) error {
config.mode = chatPostEphemeral
config.values.Del("ts")
return nil
}
}
2018-08-10 00:38:19 +02:00
// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user.
func MsgOptionPostEphemeral2(userID string) MsgOption {
return func(config *sendConfig) error {
config.mode = 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.mode = chatMeMessage
return nil
}
}
2017-07-16 14:29:46 +02:00
// 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
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
}
// 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
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
}
// 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")
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
return nil
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
}
2018-08-10 00:38:19 +02:00
// MsgOptionUser set the user for the message.
func MsgOptionUser(userID string) MsgOption {
return func(config *sendConfig) error {
config.values.Set("user", userID)
return nil
}
}
2017-07-16 14:29:46 +02:00
// 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
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
}
// 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
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
}
// MsgOptionEnableLinkUnfurl enables link unfurling
func MsgOptionEnableLinkUnfurl() MsgOption {
return func(config *sendConfig) error {
config.values.Set("unfurl_links", "true")
return nil
2017-03-25 20:45:10 +01:00
}
2017-07-16 14:29:46 +02:00
}
2016-09-05 16:34:37 +02:00
2018-08-10 00:38:19 +02:00
// MsgOptionDisableLinkUnfurl disables link unfurling
func MsgOptionDisableLinkUnfurl() MsgOption {
return func(config *sendConfig) error {
config.values.Set("unfurl_links", "false")
return nil
}
}
2017-07-16 14:29:46 +02:00
// MsgOptionDisableMediaUnfurl disables media unfurling.
func MsgOptionDisableMediaUnfurl() MsgOption {
return func(config *sendConfig) error {
config.values.Set("unfurl_media", "false")
return nil
2016-09-05 16:34:37 +02:00
}
}
2017-07-16 14:29:46 +02:00
// MsgOptionDisableMarkdown disables markdown.
func MsgOptionDisableMarkdown() MsgOption {
return func(config *sendConfig) error {
config.values.Set("mrkdwn", "false")
return nil
2016-09-05 16:34:37 +02:00
}
2017-07-16 14:29:46 +02:00
}
2018-08-10 00:38:19 +02:00
// 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
}
}
// this function 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
}
}
func MsgOptionParse(b bool) MsgOption {
return func(c *sendConfig) error {
var v string
if b {
v = "1"
} else {
v = "0"
}
c.values.Set("parse", v)
return nil
}
}
2017-07-16 14:29:46 +02:00
// MsgOptionPostMessageParameters maintain backwards compatibility.
func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
return func(config *sendConfig) error {
if params.Username != DEFAULT_MESSAGE_USERNAME {
2018-08-10 00:38:19 +02:00
config.values.Set("username", params.Username)
2017-07-16 14:29:46 +02:00
}
2018-01-08 22:41:38 +01:00
// chat.postEphemeral support
if params.User != DEFAULT_MESSAGE_USERNAME {
config.values.Set("user", params.User)
}
2017-07-16 14:29:46 +02:00
// never generates an error.
MsgOptionAsUser(params.AsUser)(config)
if params.Parse != DEFAULT_MESSAGE_PARSE {
2018-08-10 00:38:19 +02:00
config.values.Set("parse", params.Parse)
2017-07-16 14:29:46 +02:00
}
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)
}
2018-01-08 22:41:38 +01:00
if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST {
config.values.Set("reply_broadcast", "true")
}
2017-07-16 14:29:46 +02:00
return nil
2016-09-05 16:34:37 +02:00
}
}