summaryrefslogtreecommitdiffstats
path: root/bridge/slack
diff options
context:
space:
mode:
authorPatrick Connolly <patrick.c.connolly@gmail.com>2018-11-26 17:47:04 +0800
committerDuco van Amstel <duco.vanamstel@gmail.com>2018-11-26 09:47:04 +0000
commitf5659d455d2c28a6f2fe4c40f4dc344419ff523a (patch)
tree76447e104bfafeea83f3df6cf7f540d7c0bf048c /bridge/slack
parent5ed7abdbeb8a81599b3516583e15df75651fb9bc (diff)
downloadmatterbridge-msglm-f5659d455d2c28a6f2fe4c40f4dc344419ff523a.tar.gz
matterbridge-msglm-f5659d455d2c28a6f2fe4c40f4dc344419ff523a.tar.bz2
matterbridge-msglm-f5659d455d2c28a6f2fe4c40f4dc344419ff523a.zip
Sync channel topics between Slack bridges (#585)
Added logic to allow for configurable synchronisation of topics and purposes of channels between Slack bridges.
Diffstat (limited to 'bridge/slack')
-rw-r--r--bridge/slack/handlers.go7
-rw-r--r--bridge/slack/helpers.go24
-rw-r--r--bridge/slack/helpers_test.go36
-rw-r--r--bridge/slack/slack.go51
4 files changed, 112 insertions, 6 deletions
diff --git a/bridge/slack/handlers.go b/bridge/slack/handlers.go
index 035c5af5..89c800da 100644
--- a/bridge/slack/handlers.go
+++ b/bridge/slack/handlers.go
@@ -116,6 +116,11 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
return b.GetBool(noSendJoinConfig)
case sPinnedItem, sUnpinnedItem:
return true
+ case sChannelTopic, sChannelPurpose:
+ // Skip the event if our bot/user account changed the topic/purpose
+ if ev.User == b.si.User.ID {
+ return true
+ }
}
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
@@ -136,7 +141,6 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
if len(ev.Files) > 0 {
return b.filesCached(ev.Files)
}
-
return false
}
@@ -201,6 +205,7 @@ func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message)
rmsg.Username = sSystemUser
rmsg.Event = config.EventJoinLeave
case sChannelTopic, sChannelPurpose:
+ b.populateChannels()
rmsg.Event = config.EventTopicChange
case sMessageChanged:
rmsg.Text = ev.SubMessage.Text
diff --git a/bridge/slack/helpers.go b/bridge/slack/helpers.go
index b0fdaba1..39fbcea7 100644
--- a/bridge/slack/helpers.go
+++ b/bridge/slack/helpers.go
@@ -262,12 +262,28 @@ func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config
}
var (
- mentionRE = regexp.MustCompile(`<@([a-zA-Z0-9]+)>`)
- channelRE = regexp.MustCompile(`<#[a-zA-Z0-9]+\|(.+?)>`)
- variableRE = regexp.MustCompile(`<!((?:subteam\^)?[a-zA-Z0-9]+)(?:\|@?(.+?))?>`)
- urlRE = regexp.MustCompile(`<(.*?)(\|.*?)?>`)
+ mentionRE = regexp.MustCompile(`<@([a-zA-Z0-9]+)>`)
+ channelRE = regexp.MustCompile(`<#[a-zA-Z0-9]+\|(.+?)>`)
+ variableRE = regexp.MustCompile(`<!((?:subteam\^)?[a-zA-Z0-9]+)(?:\|@?(.+?))?>`)
+ urlRE = regexp.MustCompile(`<(.*?)(\|.*?)?>`)
+ topicOrPurposeRE = regexp.MustCompile(`(?s)(@.+) (cleared|set)(?: the)? channel (topic|purpose)(?:: (.*))?`)
)
+func (b *Bslack) extractTopicOrPurpose(text string) (string, string) {
+ r := topicOrPurposeRE.FindStringSubmatch(text)
+ if len(r) == 5 {
+ action, updateType, extracted := r[2], r[3], r[4]
+ switch action {
+ case "set":
+ return updateType, extracted
+ case "cleared":
+ return updateType, ""
+ }
+ }
+ b.Log.Warnf("Encountered channel topic or purpose change message with unexpected format: %s", text)
+ return "unknown", ""
+}
+
// @see https://api.slack.com/docs/message-formatting#linking_to_channels_and_users
func (b *Bslack) replaceMention(text string) string {
replaceFunc := func(match string) string {
diff --git a/bridge/slack/helpers_test.go b/bridge/slack/helpers_test.go
new file mode 100644
index 00000000..c9ff647d
--- /dev/null
+++ b/bridge/slack/helpers_test.go
@@ -0,0 +1,36 @@
+package bslack
+
+import (
+ "io/ioutil"
+ "testing"
+
+ "github.com/42wim/matterbridge/bridge"
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestExtractTopicOrPurpose(t *testing.T) {
+ testcases := map[string]struct {
+ input string
+ wantChangeType string
+ wantOutput string
+ }{
+ "success - topic type": {"@someone set channel topic: foo bar", "topic", "foo bar"},
+ "success - purpose type": {"@someone set channel purpose: foo bar", "purpose", "foo bar"},
+ "success - one line": {"@someone set channel topic: foo bar", "topic", "foo bar"},
+ "success - multi-line": {"@someone set channel topic: foo\nbar", "topic", "foo\nbar"},
+ "success - cleared": {"@someone cleared channel topic", "topic", ""},
+ "error - unhandled": {"some unmatched message", "unknown", ""},
+ }
+
+ logger := logrus.New()
+ logger.SetOutput(ioutil.Discard)
+ cfg := &bridge.Config{Log: logger.WithFields(nil)}
+ b := newBridge(cfg)
+ for name, tc := range testcases {
+ gotChangeType, gotOutput := b.extractTopicOrPurpose(tc.input)
+
+ assert.Equalf(t, tc.wantChangeType, gotChangeType, "This testcase failed: %s", name)
+ assert.Equalf(t, tc.wantOutput, gotOutput, "This testcase failed: %s", name)
+ }
+}
diff --git a/bridge/slack/slack.go b/bridge/slack/slack.go
index d054ae81..a38bbb53 100644
--- a/bridge/slack/slack.go
+++ b/bridge/slack/slack.go
@@ -281,8 +281,14 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) {
return "", nil
}
- // Handle message deletions.
var handled bool
+
+ // Handle topic/purpose updates.
+ if handled, err = b.handleTopicOrPurpose(&msg, channelInfo); handled {
+ return "", err
+ }
+
+ // Handle message deletions.
if handled, err = b.deleteMessage(&msg, channelInfo); handled {
return msg.ID, err
}
@@ -315,6 +321,49 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) {
return b.postMessage(&msg, messageParameters, channelInfo)
}
+func (b *Bslack) updateTopicOrPurpose(msg *config.Message, channelInfo *slack.Channel) (bool, error) {
+ var updateFunc func(channelID string, value string) (*slack.Channel, error)
+
+ incomingChangeType, text := b.extractTopicOrPurpose(msg.Text)
+ switch incomingChangeType {
+ case "topic":
+ updateFunc = b.rtm.SetTopicOfConversation
+ case "purpose":
+ updateFunc = b.rtm.SetPurposeOfConversation
+ default:
+ b.Log.Errorf("Unhandled type received from extractTopicOrPurpose: %s", incomingChangeType)
+ return true, nil
+ }
+ for {
+ _, err := updateFunc(channelInfo.ID, text)
+ if err == nil {
+ return true, nil
+ }
+ if err = b.handleRateLimit(err); err != nil {
+ return true, err
+ }
+ }
+}
+
+// handles updating topic/purpose and determining whether to further propagate update messages.
+func (b *Bslack) handleTopicOrPurpose(msg *config.Message, channelInfo *slack.Channel) (bool, error) {
+ if msg.Event != config.EventTopicChange {
+ return false, nil
+ }
+
+ if b.GetBool("SyncTopic") {
+ return b.updateTopicOrPurpose(msg, channelInfo)
+ }
+
+ // Pass along to normal message handlers.
+ if b.GetBool("ShowTopicChange") {
+ return false, nil
+ }
+
+ // Swallow message as handled no-op.
+ return true, nil
+}
+
func (b *Bslack) deleteMessage(msg *config.Message, channelInfo *slack.Channel) (bool, error) {
if msg.Event != config.EventMsgDelete {
return false, nil