func GetSlackLink(name string, url string, args ...string) slack.AttachmentAction {
	style := "default"
	if len(args) > 0 {
		style = args[0]
	}
	action := slack.AttachmentAction{}
	action.Style = style
	action.Type = "button"
	action.Text = name
	action.URL = url
	return action
}
GetSlackLink generates a "link button" as a slack.AttachmentAction which will
open the given URL in the Slack client (when pressed)
package client
import (
	"fmt"
	"strings"
	"sync"
	"github.com/innogames/slack-bot/v2/bot/config"
	"github.com/innogames/slack-bot/v2/bot/msg"
	"github.com/innogames/slack-bot/v2/bot/util"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/slack-go/slack"
	"github.com/slack-go/slack/socketmode"
)
var InternalMessages = make(chan msg.Message, 50)
func HandleMessage(message msg.Message) {
	message.Text = strings.TrimSpace(message.Text)
	if message.Text == "" {
		return
	}
	InternalMessages <- message
}
func HandleMessageWithDoneHandler(message msg.Message) *sync.WaitGroup {
	done := message.AddDoneHandler()
	if message.Text != "" {
		HandleMessage(message)
	} else {
		done.Done()
	}
	return done
}
var AuthResponse slack.AuthTestResponse
var Users config.UserMap
var Channels map[string]string
func GetSlackClient(cfg config.Slack) (*Slack, error) {
	if !strings.HasPrefix(cfg.Token, "xoxb-") {
		return nil, fmt.Errorf("config slack.token needs to start with 'xoxb-'")
	}
	options := []slack.Option{
		slack.OptionHTTPClient(GetHTTPClient()),
	}
	if cfg.TestEndpointURL != "" {
		options = append(options, slack.OptionAPIURL(cfg.TestEndpointURL))
	}
	if cfg.Debug {
		options = append(options, slack.OptionDebug(true))
	}
	if cfg.SocketToken != "" {
		if !strings.HasPrefix(cfg.SocketToken, "xapp-") {
			return nil, fmt.Errorf("config slack.socket_token needs to start to 'xapp-'")
		}
		options = append(options, slack.OptionAppLevelToken(cfg.SocketToken))
	}
	rawClient := slack.New(cfg.Token, options...)
	var rtm *slack.RTM
	var socket *socketmode.Client
	if cfg.SocketToken != "" {
		socket = socketmode.New(
			rawClient,
		)
	} else {
		rtm = rawClient.NewRTM()
	}
	return &Slack{Client: rawClient, RTM: rtm, Socket: socket, config: cfg}, nil
}
type SlackClient interface {
	ReplyError(ref msg.Ref, err error)
	SendMessage(ref msg.Ref, text string, options ...slack.MsgOption) string
	SendEphemeralMessage(ref msg.Ref, text string, options ...slack.MsgOption)
	SendBlockMessage(ref msg.Ref, blocks []slack.Block, options ...slack.MsgOption) string
	SendToUser(user string, text string)
	RemoveReaction(reaction util.Reaction, ref msg.Ref)
	AddReaction(reaction util.Reaction, ref msg.Ref)
	GetReactions(item slack.ItemRef, params slack.GetReactionsParameters) ([]slack.ItemReaction, error)
	GetConversationHistory(*slack.GetConversationHistoryParameters) (*slack.GetConversationHistoryResponse, error)
	CanHandleInteractions() bool
}
type Slack struct {
	*slack.Client
	RTM    *slack.RTM
	Socket *socketmode.Client
	config config.Slack
}
func (s *Slack) AddReaction(reaction util.Reaction, ref msg.Ref) {
	err := s.Client.AddReaction(reaction.ToSlackReaction(), slack.NewRefToMessage(ref.GetChannel(), ref.GetTimestamp()))
	if err != nil {
		log.WithError(err).Warnf("Error while adding reaction %s", reaction)
	}
}
func (s *Slack) RemoveReaction(reaction util.Reaction, ref msg.Ref) {
	err := s.Client.RemoveReaction(reaction.ToSlackReaction(), slack.NewRefToMessage(ref.GetChannel(), ref.GetTimestamp()))
	if err != nil {
		log.WithError(err).Warnf("Error while removing reaction %s", reaction)
	}
}
func (s *Slack) SendEphemeralMessage(ref msg.Ref, text string, options ...slack.MsgOption) {
	_, err := s.Client.PostEphemeral(
		ref.GetChannel(),
		ref.GetUser(),
		slack.MsgOptionAsUser(true),
		slack.MsgOptionTS(ref.GetThread()),  
		slack.MsgOptionText(text, false),
	)
	if err != nil {
		log.Warn(errors.Wrapf(err, "Error while sending Ephemeral message %s", err))
	}
}
func (s *Slack) SendMessage(ref msg.Ref, text string, options ...slack.MsgOption) string {
	if ref.GetChannel() == "" {
		log.Warnf("no channel given: %s", ref)
		return ""
	}
	if len(options) == 0 && text == "" {
		return ""
	}
	defaultOptions := []slack.MsgOption{
		slack.MsgOptionTS(ref.GetThread()),  
		slack.MsgOptionAsUser(true),
		slack.MsgOptionText(text, false),
		slack.MsgOptionDisableLinkUnfurl(),
	}
	options = append(defaultOptions, options...)
	_, msgTimestamp, err := s.PostMessage(
		ref.GetChannel(),
		options...,
	)
	if err != nil {
		log.
			WithField("user", ref.GetUser()).
			Error(err.Error())
	}
	return msgTimestamp
}
func (s *Slack) ReplyError(ref msg.Ref, err error) {
	log.WithError(err).Warnf("Error while sending reply")
	s.SendMessage(ref, err.Error())
	if s.config.ErrorChannel != "" {
		text := fmt.Sprintf(
			"<@%s> Error in command: %s",
			ref.GetUser(),
			err.Error(),
		)
		message := msg.Message{}
		message.Channel, _ = GetChannelIDAndName(s.config.ErrorChannel)
		s.SendMessage(message, text)
	}
}
func (s *Slack) SendToUser(user string, text string) {
	userID, _ := GetUserIDAndName(user)
	if userID == "" {
		log.Errorf("Invalid user: %s", user)
		return
	}
	options := &slack.OpenConversationParameters{
		Users: []string{userID},
	}
	channel, _, _, err := s.Client.OpenConversation(options)
	if err != nil {
		log.WithError(err).Errorf("Cannot open channel")
		return
	}
	message := msg.Message{}
	message.Channel = channel.ID
	s.SendMessage(message, text)
}
func (s *Slack) CanHandleInteractions() bool {
	return s.config.CanHandleInteractions()
}
func (s *Slack) SendBlockMessage(ref msg.Ref, blocks []slack.Block, options ...slack.MsgOption) string {
	allOptions := []slack.MsgOption{
		slack.MsgOptionBlocks(blocks...),
	}
	return s.SendMessage(ref, "", append(allOptions, options...)...)
}
func GetUserIDAndName(identifier string) (id string, name string) {
	identifier = strings.TrimPrefix(identifier, "@")
	if name, ok := Users[identifier]; ok {
		return identifier, name
	}
	identifier = strings.ToLower(identifier)
	for id, name := range Users {
		if strings.EqualFold(name, identifier) {
			return id, name
		}
	}
	return "", ""
}
func GetChannelIDAndName(identifier string) (id string, name string) {
	identifier = strings.TrimPrefix(identifier, "#")
	if name, ok := Channels[identifier]; ok {
		return identifier, name
	}
	identifier = strings.ToLower(identifier)
	for id, name := range Channels {
		if strings.EqualFold(name, identifier) {
			return id, name
		}
	}
	return "", ""
}