diff options
Diffstat (limited to 'bridge/slack/slack.go')
-rw-r--r-- | bridge/slack/slack.go | 214 |
1 files changed, 136 insertions, 78 deletions
diff --git a/bridge/slack/slack.go b/bridge/slack/slack.go index 6aa89375..6524f974 100644 --- a/bridge/slack/slack.go +++ b/bridge/slack/slack.go @@ -2,6 +2,7 @@ package bslack import ( "errors" + "fmt" "strings" "sync" @@ -18,22 +19,52 @@ type Bslack struct { mh *matterhook.Client sc *slack.Client rtm *slack.RTM - Users []slack.User - Usergroups []slack.UserGroup + users []slack.User si *slack.Info channels []slack.Channel cache *lru.Cache - UseChannelID bool + useChannelID bool uuid string *bridge.Config sync.RWMutex } -const messageDeleted = "message_deleted" +const ( + sChannelJoin = "channel_join" + sChannelLeave = "channel_leave" + sMessageDeleted = "message_deleted" + sSlackAttachment = "slack_attachment" + sPinnedItem = "pinned_item" + sUnpinnedItem = "unpinned_item" + sChannelTopic = "channel_topic" + sChannelPurpose = "channel_purpose" + sFileComment = "file_comment" + sMeMessage = "me_message" + sUserTyping = "user_typing" + sLatencyReport = "latency_report" + sSystemUser = "system" + + tokenConfig = "Token" + incomingWebhookConfig = "WebhookBindAddress" + outgoingWebhookConfig = "WebhookURL" + skipTLSConfig = "SkipTLSVerify" + useNickPrefixConfig = "PrefixMessagesWithNick" + editDisableConfig = "EditDisable" + editSuffixConfig = "EditSuffix" + iconURLConfig = "iconurl" + noSendJoinConfig = "nosendjoinpart" +) func New(cfg *bridge.Config) bridge.Bridger { - b := &Bslack{Config: cfg, uuid: xid.New().String()} - b.cache, _ = lru.New(5000) + newCache, err := lru.New(5000) + if err != nil { + cfg.Log.Fatalf("Could not create LRU cache for Slack bridge: %v", err) + } + b := &Bslack{ + Config: cfg, + uuid: xid.New().String(), + cache: newCache, + } return b } @@ -44,50 +75,54 @@ func (b *Bslack) Command(cmd string) string { func (b *Bslack) Connect() error { b.RLock() defer b.RUnlock() - if b.GetString("WebhookBindAddress") != "" { - if b.GetString("WebhookURL") != "" { + if b.GetString(incomingWebhookConfig) != "" { + if b.GetString(outgoingWebhookConfig) != "" { b.Log.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)") - b.mh = matterhook.New(b.GetString("WebhookURL"), - matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), - BindAddress: b.GetString("WebhookBindAddress")}) - } else if b.GetString("Token") != "" { + b.mh = matterhook.New(b.GetString(outgoingWebhookConfig), matterhook.Config{ + InsecureSkipVerify: b.GetBool(skipTLSConfig), + BindAddress: b.GetString(incomingWebhookConfig), + }) + } else if b.GetString(tokenConfig) != "" { b.Log.Info("Connecting using token (sending)") - b.sc = slack.New(b.GetString("Token")) + b.sc = slack.New(b.GetString(tokenConfig)) b.rtm = b.sc.NewRTM() go b.rtm.ManageConnection() b.Log.Info("Connecting using webhookbindaddress (receiving)") - b.mh = matterhook.New(b.GetString("WebhookURL"), - matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), - BindAddress: b.GetString("WebhookBindAddress")}) + b.mh = matterhook.New(b.GetString(outgoingWebhookConfig), matterhook.Config{ + InsecureSkipVerify: b.GetBool(skipTLSConfig), + BindAddress: b.GetString(incomingWebhookConfig), + }) } else { b.Log.Info("Connecting using webhookbindaddress (receiving)") - b.mh = matterhook.New(b.GetString("WebhookURL"), - matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), - BindAddress: b.GetString("WebhookBindAddress")}) + b.mh = matterhook.New(b.GetString(outgoingWebhookConfig), matterhook.Config{ + InsecureSkipVerify: b.GetBool(skipTLSConfig), + BindAddress: b.GetString(incomingWebhookConfig), + }) } go b.handleSlack() return nil } - if b.GetString("WebhookURL") != "" { + if b.GetString(outgoingWebhookConfig) != "" { b.Log.Info("Connecting using webhookurl (sending)") - b.mh = matterhook.New(b.GetString("WebhookURL"), - matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), - DisableServer: true}) - if b.GetString("Token") != "" { + b.mh = matterhook.New(b.GetString(outgoingWebhookConfig), matterhook.Config{ + InsecureSkipVerify: b.GetBool(skipTLSConfig), + DisableServer: true, + }) + if b.GetString(tokenConfig) != "" { b.Log.Info("Connecting using token (receiving)") - b.sc = slack.New(b.GetString("Token")) + b.sc = slack.New(b.GetString(tokenConfig)) b.rtm = b.sc.NewRTM() go b.rtm.ManageConnection() go b.handleSlack() } - } else if b.GetString("Token") != "" { + } else if b.GetString(tokenConfig) != "" { b.Log.Info("Connecting using token (sending and receiving)") - b.sc = slack.New(b.GetString("Token")) + b.sc = slack.New(b.GetString(tokenConfig)) b.rtm = b.sc.NewRTM() go b.rtm.ManageConnection() go b.handleSlack() } - if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") == "" && b.GetString("Token") == "" { + if b.GetString(incomingWebhookConfig) == "" && b.GetString(outgoingWebhookConfig) == "" && b.GetString(tokenConfig) == "" { return errors.New("no connection method found. See that you have WebhookBindAddress, WebhookURL or Token configured") } return nil @@ -101,7 +136,7 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error { // use ID:channelid and resolve it to the actual name idcheck := strings.Split(channel.Name, "ID:") if len(idcheck) > 1 { - b.UseChannelID = true + b.useChannelID = true ch, err := b.sc.GetChannelInfo(idcheck[1]) if err != nil { return err @@ -114,7 +149,7 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error { // we can only join channels using the API if b.sc != nil { - if strings.HasPrefix(b.GetString("Token"), "xoxb") { + if strings.HasPrefix(b.GetString(tokenConfig), "xoxb") { // TODO check if bot has already joined channel return nil } @@ -141,11 +176,14 @@ func (b *Bslack) Send(msg config.Message) (string, error) { } // Use webhook to send the message - if b.GetString("WebhookURL") != "" { + if b.GetString(outgoingWebhookConfig) != "" { return b.sendWebhook(msg) } - channelID := b.getChannelID(msg.Channel) + channelInfo, err := b.getChannel(msg.Channel) + if err != nil { + return "", fmt.Errorf("could not send message: %v", err) + } // Delete message if msg.Event == config.EVENT_MSG_DELETE { @@ -155,7 +193,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) { } // we get a "slack <ID>", split it ts := strings.Fields(msg.ID) - _, _, err := b.sc.DeleteMessage(channelID, ts[1]) + _, _, err = b.sc.DeleteMessage(channelInfo.ID, ts[1]) if err != nil { return msg.ID, err } @@ -163,14 +201,14 @@ func (b *Bslack) Send(msg config.Message) (string, error) { } // Prepend nick if configured - if b.GetBool("PrefixMessagesWithNick") { + if b.GetBool(useNickPrefixConfig) { msg.Text = msg.Username + msg.Text } // Edit message if we have an ID if msg.ID != "" { ts := strings.Fields(msg.ID) - _, _, _, err := b.sc.UpdateMessage(channelID, ts[1], msg.Text) + _, _, _, err = b.sc.UpdateMessage(channelInfo.ID, ts[1], msg.Text) if err != nil { return msg.ID, err } @@ -179,12 +217,12 @@ func (b *Bslack) Send(msg config.Message) (string, error) { // create slack new post parameters np := slack.NewPostMessageParameters() - if b.GetBool("PrefixMessagesWithNick") { + if b.GetBool(useNickPrefixConfig) { np.AsUser = true } np.Username = msg.Username np.LinkNames = 1 // replace mentions - np.IconURL = config.GetIconURL(&msg, b.GetString("iconurl")) + np.IconURL = config.GetIconURL(&msg, b.GetString(iconURLConfig)) if msg.Avatar != "" { np.IconURL = msg.Avatar } @@ -193,8 +231,8 @@ func (b *Bslack) Send(msg config.Message) (string, error) { // add file attachments np.Attachments = append(np.Attachments, b.createAttach(msg.Extra)...) // add slack attachments (from another slack bridge) - if len(msg.Extra["slack_attachment"]) > 0 { - for _, attach := range msg.Extra["slack_attachment"] { + if msg.Extra != nil { + for _, attach := range msg.Extra[sSlackAttachment] { np.Attachments = append(np.Attachments, attach.([]slack.Attachment)...) } } @@ -202,16 +240,17 @@ func (b *Bslack) Send(msg config.Message) (string, error) { // Upload a file if it exists if msg.Extra != nil { for _, rmsg := range helper.HandleExtra(&msg, b.General) { - b.sc.PostMessage(channelID, rmsg.Username+rmsg.Text, np) - } - // check if we have files to upload (from slack, telegram or mattermost) - if len(msg.Extra["file"]) > 0 { - b.handleUploadFile(&msg, channelID) + _, _, err = b.sc.PostMessage(channelInfo.ID, rmsg.Username+rmsg.Text, np) + if err != nil { + b.Log.Error(err) + } } + // Upload files if necessary (from Slack, Telegram or Mattermost). + b.handleUploadFile(&msg, channelInfo.ID) } // Post normal message - _, id, err := b.sc.PostMessage(channelID, msg.Text, np) + _, id, err := b.sc.PostMessage(channelInfo.ID, msg.Text, np) if err != nil { return "", err } @@ -223,26 +262,36 @@ func (b *Bslack) Reload(cfg *bridge.Config) (string, error) { } func (b *Bslack) createAttach(extra map[string][]interface{}) []slack.Attachment { - var attachs []slack.Attachment + var attachements []slack.Attachment for _, v := range extra["attachments"] { entry := v.(map[string]interface{}) - s := slack.Attachment{} - s.Fallback = entry["fallback"].(string) - s.Color = entry["color"].(string) - s.Pretext = entry["pretext"].(string) - s.AuthorName = entry["author_name"].(string) - s.AuthorLink = entry["author_link"].(string) - s.AuthorIcon = entry["author_icon"].(string) - s.Title = entry["title"].(string) - s.TitleLink = entry["title_link"].(string) - s.Text = entry["text"].(string) - s.ImageURL = entry["image_url"].(string) - s.ThumbURL = entry["thumb_url"].(string) - s.Footer = entry["footer"].(string) - s.FooterIcon = entry["footer_icon"].(string) - attachs = append(attachs, s) + s := slack.Attachment{ + Fallback: extractStringField(entry, "fallback"), + Color: extractStringField(entry, "color"), + Pretext: extractStringField(entry, "pretext"), + AuthorName: extractStringField(entry, "author_name"), + AuthorLink: extractStringField(entry, "author_link"), + AuthorIcon: extractStringField(entry, "author_icon"), + Title: extractStringField(entry, "title"), + TitleLink: extractStringField(entry, "title_link"), + Text: extractStringField(entry, "text"), + ImageURL: extractStringField(entry, "image_url"), + ThumbURL: extractStringField(entry, "thumb_url"), + Footer: extractStringField(entry, "footer"), + FooterIcon: extractStringField(entry, "footer_icon"), + } + attachements = append(attachements, s) + } + return attachements +} + +func extractStringField(data map[string]interface{}, field string) string { + if rawValue, found := data[field]; found { + if value, ok := rawValue.(string); ok { + return value + } } - return attachs + return "" } // sendWebhook uses the configured WebhookURL to send the message @@ -252,39 +301,48 @@ func (b *Bslack) sendWebhook(msg config.Message) (string, error) { return "", nil } - if b.GetBool("PrefixMessagesWithNick") { + if b.GetBool(useNickPrefixConfig) { msg.Text = msg.Username + msg.Text } if msg.Extra != nil { // this sends a message only if we received a config.EVENT_FILE_FAILURE_SIZE for _, rmsg := range helper.HandleExtra(&msg, b.General) { - iconURL := config.GetIconURL(&rmsg, b.GetString("iconurl")) - matterMessage := matterhook.OMessage{IconURL: iconURL, Channel: msg.Channel, UserName: rmsg.Username, Text: rmsg.Text} - b.mh.Send(matterMessage) + iconURL := config.GetIconURL(&rmsg, b.GetString(iconURLConfig)) + matterMessage := matterhook.OMessage{ + IconURL: iconURL, + Channel: msg.Channel, + UserName: rmsg.Username, + Text: rmsg.Text, + } + if err := b.mh.Send(matterMessage); err != nil { + b.Log.Errorf("Failed to send message: %v", err) + } } // webhook doesn't support file uploads, so we add the url manually - if len(msg.Extra["file"]) > 0 { - for _, f := range msg.Extra["file"] { - fi := f.(config.FileInfo) - if fi.URL != "" { - msg.Text += " " + fi.URL - } + for _, f := range msg.Extra["file"] { + fi := f.(config.FileInfo) + if fi.URL != "" { + msg.Text += " " + fi.URL } } } // if we have native slack_attachments add them var attachs []slack.Attachment - if len(msg.Extra["slack_attachment"]) > 0 { - for _, attach := range msg.Extra["slack_attachment"] { - attachs = append(attachs, attach.([]slack.Attachment)...) - } + for _, attach := range msg.Extra[sSlackAttachment] { + attachs = append(attachs, attach.([]slack.Attachment)...) } - iconURL := config.GetIconURL(&msg, b.GetString("iconurl")) - matterMessage := matterhook.OMessage{IconURL: iconURL, Attachments: attachs, Channel: msg.Channel, UserName: msg.Username, Text: msg.Text} + iconURL := config.GetIconURL(&msg, b.GetString(iconURLConfig)) + matterMessage := matterhook.OMessage{ + IconURL: iconURL, + Attachments: attachs, + Channel: msg.Channel, + UserName: msg.Username, + Text: msg.Text, + } if msg.Avatar != "" { matterMessage.IconURL = msg.Avatar } |