mirror of
https://github.com/42wim/matterbridge.git
synced 2024-11-28 21:52:08 -08:00
feat: use slack socket mode instead of rtm api
This commit is contained in:
parent
9abbdb5a96
commit
fe92a5d2c7
@ -9,6 +9,8 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
"github.com/slack-go/slack"
|
"github.com/slack-go/slack"
|
||||||
|
"github.com/slack-go/slack/slackevents"
|
||||||
|
"github.com/slack-go/slack/socketmode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrEventIgnored is for events that should be ignored
|
// ErrEventIgnored is for events that should be ignored
|
||||||
@ -48,65 +50,74 @@ func (b *Bslack) handleSlack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) handleSlackClient(messages chan *config.Message) {
|
func (b *Bslack) handleSlackClient(messages chan *config.Message) {
|
||||||
for msg := range b.rtm.IncomingEvents {
|
for msg := range b.smc.Events {
|
||||||
if msg.Type != sUserTyping && msg.Type != sHello && msg.Type != sLatencyReport {
|
switch msg.Type {
|
||||||
b.Log.Debugf("== Receiving event %#v", msg.Data)
|
case socketmode.EventTypeConnected:
|
||||||
|
if authTest, authErr := b.smc.AuthTest(); authErr == nil {
|
||||||
|
if botInfo, infoErr := b.smc.GetBotInfo(authTest.BotID); infoErr == nil {
|
||||||
|
b.si = botInfo
|
||||||
|
|
||||||
|
b.channels.populateChannels(true)
|
||||||
|
b.users.populateUsers(true)
|
||||||
|
} else {
|
||||||
|
b.Log.Fatalf("Unable to identify bot user")
|
||||||
}
|
}
|
||||||
switch ev := msg.Data.(type) {
|
} else {
|
||||||
case *slack.UserTypingEvent:
|
b.Log.Fatalf("Unable to identify bot user")
|
||||||
if !b.GetBool("ShowUserTyping") {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
rmsg, err := b.handleTypingEvent(ev)
|
case socketmode.EventTypeConnectionError:
|
||||||
if err == ErrEventIgnored {
|
ev, _ := msg.Data.(slack.ConnectionErrorEvent)
|
||||||
|
|
||||||
|
b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
|
||||||
|
case socketmode.EventTypeErrorWriteFailed:
|
||||||
|
ev, _ := msg.Data.(socketmode.ErrorWriteFailed)
|
||||||
|
|
||||||
|
b.Log.Debugf("%#v", ev.Cause.Error())
|
||||||
|
case socketmode.EventTypeInvalidAuth:
|
||||||
|
ev, _ := msg.Data.(slack.InvalidAuthEvent)
|
||||||
|
|
||||||
|
b.Log.Fatalf("Invalid Token %#v", ev)
|
||||||
|
case socketmode.EventTypeHello, socketmode.EventTypeConnecting:
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
|
||||||
b.Log.Errorf("%#v", err)
|
case socketmode.EventTypeEventsAPI:
|
||||||
|
b.smc.Ack(*msg.Request)
|
||||||
|
|
||||||
|
eventsAPIEvent, ok := msg.Data.(slackevents.EventsAPIEvent)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
b.Log.Debugf("Ignored %+v", eventsAPIEvent)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
messages <- rmsg
|
switch innerEventData := eventsAPIEvent.InnerEvent.Data.(type) {
|
||||||
case *slack.MessageEvent:
|
case *slackevents.MessageEvent:
|
||||||
if b.skipMessageEvent(ev) {
|
if b.skipMessageEvent(innerEventData) {
|
||||||
b.Log.Debugf("Skipped message: %#v", ev)
|
b.Log.Debugf("Skipped message: %#v", innerEventData)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rmsg, err := b.handleMessageEvent(ev)
|
rmsg, err := b.handleMessageEvent(innerEventData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Errorf("%#v", err)
|
b.Log.Errorf("%#v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
messages <- rmsg
|
messages <- rmsg
|
||||||
case *slack.FileDeletedEvent:
|
case *slackevents.MemberJoinedChannelEvent:
|
||||||
rmsg, err := b.handleFileDeletedEvent(ev)
|
if innerEventData.User == b.si.UserID {
|
||||||
|
channel, err := b.smc.GetConversationInfo(innerEventData.Channel, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Log.Errorf("%#v", err)
|
b.Log.Errorf("Unable to get conversation info for channel %s", innerEventData.Channel)
|
||||||
continue
|
}
|
||||||
|
|
||||||
|
b.channels.registerChannel(*channel)
|
||||||
|
} else {
|
||||||
|
b.users.populateUser(innerEventData.User)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
messages <- rmsg
|
|
||||||
case *slack.OutgoingErrorEvent:
|
|
||||||
b.Log.Debugf("%#v", ev.Error())
|
|
||||||
case *slack.ChannelJoinedEvent:
|
|
||||||
// When we join a channel we update the full list of users as
|
|
||||||
// well as the information for the channel that we joined as this
|
|
||||||
// should now tell that we are a member of it.
|
|
||||||
b.channels.registerChannel(ev.Channel)
|
|
||||||
case *slack.ConnectedEvent:
|
|
||||||
b.si = ev.Info
|
|
||||||
b.channels.populateChannels(true)
|
|
||||||
b.users.populateUsers(true)
|
|
||||||
case *slack.InvalidAuthEvent:
|
|
||||||
b.Log.Fatalf("Invalid Token %#v", ev)
|
|
||||||
case *slack.ConnectionErrorEvent:
|
|
||||||
b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
|
|
||||||
case *slack.MemberJoinedChannelEvent:
|
|
||||||
b.users.populateUser(ev.User)
|
|
||||||
case *slack.HelloEvent, *slack.LatencyReport, *slack.ConnectingEvent:
|
|
||||||
continue
|
|
||||||
case *slack.UserChangeEvent:
|
|
||||||
b.users.invalidateUser(ev.User.ID)
|
|
||||||
default:
|
default:
|
||||||
b.Log.Debugf("Unhandled incoming event: %T", ev)
|
b.Log.Debugf("== Receiving event %#v", msg.Data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +138,7 @@ func (b *Bslack) handleMatterHook(messages chan *config.Message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skipMessageEvent skips event that need to be skipped :-)
|
// skipMessageEvent skips event that need to be skipped :-)
|
||||||
func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
|
func (b *Bslack) skipMessageEvent(ev *slackevents.MessageEvent) bool {
|
||||||
switch ev.SubType {
|
switch ev.SubType {
|
||||||
case sChannelLeave, sChannelJoin:
|
case sChannelLeave, sChannelJoin:
|
||||||
return b.GetBool(noSendJoinConfig)
|
return b.GetBool(noSendJoinConfig)
|
||||||
@ -135,39 +146,36 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
|
|||||||
return true
|
return true
|
||||||
case sChannelTopic, sChannelPurpose:
|
case sChannelTopic, sChannelPurpose:
|
||||||
// Skip the event if our bot/user account changed the topic/purpose
|
// Skip the event if our bot/user account changed the topic/purpose
|
||||||
if ev.User == b.si.User.ID {
|
if ev.BotID == b.si.ID {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for our callback ID
|
// Check for our callback ID
|
||||||
hasOurCallbackID := false
|
hasOurCallbackID := false
|
||||||
if len(ev.Blocks.BlockSet) == 1 {
|
|
||||||
block, ok := ev.Blocks.BlockSet[0].(*slack.SectionBlock)
|
if len(ev.Attachments) == 1 {
|
||||||
|
block, ok := ev.Attachments[0].Blocks.BlockSet[0].(*slack.SectionBlock)
|
||||||
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
|
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
if ev.SubMessage != nil {
|
if ev.Message != nil {
|
||||||
// It seems ev.SubMessage.Edited == nil when slack unfurls.
|
// It seems ev.SubMessage.Edited == nil when slack unfurls.
|
||||||
// Do not forward these messages. See Github issue #266.
|
// Do not forward these messages. See Github issue #266.
|
||||||
if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
|
if ev.Message.ThreadTimeStamp != ev.Message.TimeStamp &&
|
||||||
ev.SubMessage.Edited == nil {
|
ev.Message.Edited == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// see hidden subtypes at https://api.slack.com/events/message
|
|
||||||
// these messages are sent when we add a message to a thread #709
|
if len(ev.Message.Attachments) == 1 {
|
||||||
if ev.SubType == "message_replied" && ev.Hidden {
|
block, ok := ev.Message.Attachments[0].Blocks.BlockSet[0].(*slack.SectionBlock)
|
||||||
return true
|
|
||||||
}
|
|
||||||
if len(ev.SubMessage.Blocks.BlockSet) == 1 {
|
|
||||||
block, ok := ev.SubMessage.Blocks.BlockSet[0].(*slack.SectionBlock)
|
|
||||||
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
|
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
|
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
|
||||||
if ev.Username == sSlackBotUser ||
|
if ev.Username == sSlackBotUser ||
|
||||||
(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
|
(b.smc != nil && ev.BotID == b.si.ID) || hasOurCallbackID {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +185,7 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) filesCached(files []slack.File) bool {
|
func (b *Bslack) filesCached(files []slackevents.File) bool {
|
||||||
for i := range files {
|
for i := range files {
|
||||||
if !b.fileCached(&files[i]) {
|
if !b.fileCached(&files[i]) {
|
||||||
return false
|
return false
|
||||||
@ -202,7 +210,7 @@ func (b *Bslack) filesCached(files []slack.File) bool {
|
|||||||
// 5. Handle any attachments of the received event.
|
// 5. Handle any attachments of the received event.
|
||||||
// 6. Check that the Matterbridge message that we end up with after at the end of the
|
// 6. Check that the Matterbridge message that we end up with after at the end of the
|
||||||
// pipeline is valid before sending it to the Matterbridge router.
|
// pipeline is valid before sending it to the Matterbridge router.
|
||||||
func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, error) {
|
func (b *Bslack) handleMessageEvent(ev *slackevents.MessageEvent) (*config.Message, error) {
|
||||||
rmsg, err := b.populateReceivedMessage(ev)
|
rmsg, err := b.populateReceivedMessage(ev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -222,14 +230,15 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er
|
|||||||
// This is probably a webhook we couldn't resolve.
|
// This is probably a webhook we couldn't resolve.
|
||||||
return nil, fmt.Errorf("message handling resulted in an empty bot message (probably an incoming webhook we couldn't resolve): %#v", ev)
|
return nil, fmt.Errorf("message handling resulted in an empty bot message (probably an incoming webhook we couldn't resolve): %#v", ev)
|
||||||
}
|
}
|
||||||
if ev.SubMessage != nil {
|
if ev.Message != nil {
|
||||||
return nil, fmt.Errorf("message handling resulted in an empty message: %#v with submessage %#v", ev, ev.SubMessage)
|
return nil, fmt.Errorf("message handling resulted in an empty message: %#v with submessage %#v", ev, ev.Message)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("message handling resulted in an empty message: %#v", ev)
|
return nil, fmt.Errorf("message handling resulted in an empty message: %#v", ev)
|
||||||
}
|
}
|
||||||
return rmsg, nil
|
return rmsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement file deletion handle when slack-go library will expose this event
|
||||||
func (b *Bslack) handleFileDeletedEvent(ev *slack.FileDeletedEvent) (*config.Message, error) {
|
func (b *Bslack) handleFileDeletedEvent(ev *slack.FileDeletedEvent) (*config.Message, error) {
|
||||||
if rawChannel, ok := b.cache.Get(cfileDownloadChannel + ev.FileID); ok {
|
if rawChannel, ok := b.cache.Get(cfileDownloadChannel + ev.FileID); ok {
|
||||||
channel, err := b.channels.getChannelByID(rawChannel.(string))
|
channel, err := b.channels.getChannelByID(rawChannel.(string))
|
||||||
@ -250,7 +259,7 @@ func (b *Bslack) handleFileDeletedEvent(ev *slack.FileDeletedEvent) (*config.Mes
|
|||||||
return nil, fmt.Errorf("channel ID for file ID %s not found", ev.FileID)
|
return nil, fmt.Errorf("channel ID for file ID %s not found", ev.FileID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) bool {
|
func (b *Bslack) handleStatusEvent(ev *slackevents.MessageEvent, rmsg *config.Message) bool {
|
||||||
switch ev.SubType {
|
switch ev.SubType {
|
||||||
case sChannelJoined, sMemberJoined:
|
case sChannelJoined, sMemberJoined:
|
||||||
// There's no further processing needed on channel events
|
// There's no further processing needed on channel events
|
||||||
@ -263,16 +272,16 @@ func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message)
|
|||||||
b.channels.populateChannels(false)
|
b.channels.populateChannels(false)
|
||||||
rmsg.Event = config.EventTopicChange
|
rmsg.Event = config.EventTopicChange
|
||||||
case sMessageChanged:
|
case sMessageChanged:
|
||||||
rmsg.Text = ev.SubMessage.Text
|
rmsg.Text = ev.Message.Text
|
||||||
// handle deleted thread starting messages
|
// handle deleted thread starting messages
|
||||||
if ev.SubMessage.Text == "This message was deleted." {
|
if ev.Message.Text == "This message was deleted." {
|
||||||
rmsg.Event = config.EventMsgDelete
|
rmsg.Event = config.EventMsgDelete
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case sMessageDeleted:
|
case sMessageDeleted:
|
||||||
rmsg.Text = config.EventMsgDelete
|
rmsg.Text = config.EventMsgDelete
|
||||||
rmsg.Event = config.EventMsgDelete
|
rmsg.Event = config.EventMsgDelete
|
||||||
rmsg.ID = ev.DeletedTimestamp
|
rmsg.ID = ev.PreviousMessage.TimeStamp
|
||||||
// If a message is being deleted we do not need to process
|
// If a message is being deleted we do not need to process
|
||||||
// the event any further so we return 'true'.
|
// the event any further so we return 'true'.
|
||||||
return true
|
return true
|
||||||
@ -282,7 +291,7 @@ func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message)
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) {
|
func (b *Bslack) handleAttachments(ev *slackevents.MessageEvent, rmsg *config.Message) {
|
||||||
// File comments are set by the system (because there is no username given).
|
// File comments are set by the system (because there is no username given).
|
||||||
if ev.SubType == sFileComment {
|
if ev.SubType == sFileComment {
|
||||||
rmsg.Username = sSystemUser
|
rmsg.Username = sSystemUser
|
||||||
@ -317,23 +326,8 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
|
|
||||||
if ev.User == b.si.User.ID {
|
|
||||||
return nil, ErrEventIgnored
|
|
||||||
}
|
|
||||||
channelInfo, err := b.channels.getChannelByID(ev.Channel)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &config.Message{
|
|
||||||
Channel: channelInfo.Name,
|
|
||||||
Account: b.Account,
|
|
||||||
Event: config.EventUserTyping,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleDownloadFile handles file download
|
// handleDownloadFile handles file download
|
||||||
func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File, retry bool) error {
|
func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slackevents.File, retry bool) error {
|
||||||
if b.fileCached(file) {
|
if b.fileCached(file) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -395,7 +389,7 @@ func (b *Bslack) handleGetChannelMembers(rmsg *config.Message) bool {
|
|||||||
// identically named file but with different content will be uploaded correctly
|
// identically named file but with different content will be uploaded correctly
|
||||||
// (the assumption is that such name collisions will not occur within the given
|
// (the assumption is that such name collisions will not occur within the given
|
||||||
// timeframes).
|
// timeframes).
|
||||||
func (b *Bslack) fileCached(file *slack.File) bool {
|
func (b *Bslack) fileCached(file *slackevents.File) bool {
|
||||||
if ts, ok := b.cache.Get("file" + file.ID); ok && time.Since(ts.(time.Time)) < time.Minute {
|
if ts, ok := b.cache.Get("file" + file.ID); ok && time.Since(ts.(time.Time)) < time.Minute {
|
||||||
return true
|
return true
|
||||||
} else if ts, ok = b.cache.Get("filename" + file.Name); ok && time.Since(ts.(time.Time)) < 10*time.Second {
|
} else if ts, ok = b.cache.Get("filename" + file.Name); ok && time.Since(ts.(time.Time)) < 10*time.Second {
|
||||||
|
@ -9,11 +9,12 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slack-go/slack"
|
"github.com/slack-go/slack"
|
||||||
|
"github.com/slack-go/slack/slackevents"
|
||||||
)
|
)
|
||||||
|
|
||||||
// populateReceivedMessage shapes the initial Matterbridge message that we will forward to the
|
// populateReceivedMessage shapes the initial Matterbridge message that we will forward to the
|
||||||
// router before we apply message-dependent modifications.
|
// router before we apply message-dependent modifications.
|
||||||
func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Message, error) {
|
func (b *Bslack) populateReceivedMessage(ev *slackevents.MessageEvent) (*config.Message, error) {
|
||||||
// Use our own func because rtm.GetChannelInfo doesn't work for private channels.
|
// Use our own func because rtm.GetChannelInfo doesn't work for private channels.
|
||||||
channel, err := b.channels.getChannelByID(ev.Channel)
|
channel, err := b.channels.getChannelByID(ev.Channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -24,9 +25,9 @@ func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Messag
|
|||||||
Text: ev.Text,
|
Text: ev.Text,
|
||||||
Channel: channel.Name,
|
Channel: channel.Name,
|
||||||
Account: b.Account,
|
Account: b.Account,
|
||||||
ID: ev.Timestamp,
|
ID: ev.TimeStamp,
|
||||||
Extra: make(map[string][]interface{}),
|
Extra: make(map[string][]interface{}),
|
||||||
ParentID: ev.ThreadTimestamp,
|
ParentID: ev.ThreadTimeStamp,
|
||||||
Protocol: b.Protocol,
|
Protocol: b.Protocol,
|
||||||
}
|
}
|
||||||
if b.useChannelID {
|
if b.useChannelID {
|
||||||
@ -34,19 +35,19 @@ func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Messag
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle 'edit' messages.
|
// Handle 'edit' messages.
|
||||||
if ev.SubMessage != nil && !b.GetBool(editDisableConfig) {
|
if ev.Message != nil && !b.GetBool(editDisableConfig) {
|
||||||
rmsg.ID = ev.SubMessage.Timestamp
|
rmsg.ID = ev.Message.TimeStamp
|
||||||
if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {
|
if ev.Message.ThreadTimeStamp != ev.Message.TimeStamp {
|
||||||
b.Log.Debugf("SubMessage %#v", ev.SubMessage)
|
b.Log.Debugf("SubMessage %#v", ev.Message)
|
||||||
rmsg.Text = ev.SubMessage.Text + b.GetString(editSuffixConfig)
|
rmsg.Text = ev.Message.Text + b.GetString(editSuffixConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For edits, only submessage has thread ts.
|
// For edits, only submessage has thread ts.
|
||||||
// Ensures edits to threaded messages maintain their prefix hint on the
|
// Ensures edits to threaded messages maintain their prefix hint on the
|
||||||
// unthreaded end.
|
// unthreaded end.
|
||||||
if ev.SubMessage != nil {
|
if ev.Message != nil {
|
||||||
rmsg.ParentID = ev.SubMessage.ThreadTimestamp
|
rmsg.ParentID = ev.Message.ThreadTimeStamp
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = b.populateMessageWithUserInfo(ev, rmsg); err != nil {
|
if err = b.populateMessageWithUserInfo(ev, rmsg); err != nil {
|
||||||
@ -55,7 +56,7 @@ func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Messag
|
|||||||
return rmsg, err
|
return rmsg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *config.Message) error {
|
func (b *Bslack) populateMessageWithUserInfo(ev *slackevents.MessageEvent, rmsg *config.Message) error {
|
||||||
if ev.SubType == sMessageDeleted || ev.SubType == sFileComment {
|
if ev.SubType == sMessageDeleted || ev.SubType == sFileComment {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -71,8 +72,8 @@ func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *confi
|
|||||||
switch {
|
switch {
|
||||||
case ev.User != "":
|
case ev.User != "":
|
||||||
userID = ev.User
|
userID = ev.User
|
||||||
case ev.SubMessage != nil && ev.SubMessage.User != "":
|
case ev.Message != nil && ev.Message.User != "":
|
||||||
userID = ev.SubMessage.User
|
userID = ev.Message.User
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -90,7 +91,7 @@ func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *confi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config.Message) error {
|
func (b *Bslack) populateMessageWithBotInfo(ev *slackevents.MessageEvent, rmsg *config.Message) error {
|
||||||
if ev.BotID == "" || b.GetString(outgoingWebhookConfig) != "" {
|
if ev.BotID == "" || b.GetString(outgoingWebhookConfig) != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -98,7 +99,7 @@ func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config
|
|||||||
var err error
|
var err error
|
||||||
var bot *slack.Bot
|
var bot *slack.Bot
|
||||||
for {
|
for {
|
||||||
bot, err = b.rtm.GetBotInfo(ev.BotID)
|
bot, err = b.smc.GetBotInfo(ev.BotID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
"github.com/slack-go/slack"
|
"github.com/slack-go/slack"
|
||||||
|
"github.com/slack-go/slack/socketmode"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bslack struct {
|
type Bslack struct {
|
||||||
@ -24,7 +25,8 @@ type Bslack struct {
|
|||||||
mh *matterhook.Client
|
mh *matterhook.Client
|
||||||
sc *slack.Client
|
sc *slack.Client
|
||||||
rtm *slack.RTM
|
rtm *slack.RTM
|
||||||
si *slack.Info
|
si *slack.Bot
|
||||||
|
smc *socketmode.Client
|
||||||
|
|
||||||
cache *lru.Cache
|
cache *lru.Cache
|
||||||
uuid string
|
uuid string
|
||||||
@ -36,7 +38,6 @@ type Bslack struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sHello = "hello"
|
|
||||||
sChannelJoin = "channel_join"
|
sChannelJoin = "channel_join"
|
||||||
sChannelLeave = "channel_leave"
|
sChannelLeave = "channel_leave"
|
||||||
sChannelJoined = "channel_joined"
|
sChannelJoined = "channel_joined"
|
||||||
@ -50,13 +51,12 @@ const (
|
|||||||
sChannelPurpose = "channel_purpose"
|
sChannelPurpose = "channel_purpose"
|
||||||
sFileComment = "file_comment"
|
sFileComment = "file_comment"
|
||||||
sMeMessage = "me_message"
|
sMeMessage = "me_message"
|
||||||
sUserTyping = "user_typing"
|
|
||||||
sLatencyReport = "latency_report"
|
|
||||||
sSystemUser = "system"
|
sSystemUser = "system"
|
||||||
sSlackBotUser = "slackbot"
|
sSlackBotUser = "slackbot"
|
||||||
cfileDownloadChannel = "file_download_channel"
|
cfileDownloadChannel = "file_download_channel"
|
||||||
|
|
||||||
tokenConfig = "Token"
|
tokenConfig = "Token"
|
||||||
|
appTokenConfig = "AppToken"
|
||||||
incomingWebhookConfig = "WebhookBindAddress"
|
incomingWebhookConfig = "WebhookBindAddress"
|
||||||
outgoingWebhookConfig = "WebhookURL"
|
outgoingWebhookConfig = "WebhookURL"
|
||||||
skipTLSConfig = "SkipTLSVerify"
|
skipTLSConfig = "SkipTLSVerify"
|
||||||
@ -71,12 +71,18 @@ const (
|
|||||||
func New(cfg *bridge.Config) bridge.Bridger {
|
func New(cfg *bridge.Config) bridge.Bridger {
|
||||||
// Print a deprecation warning for legacy non-bot tokens (#527).
|
// Print a deprecation warning for legacy non-bot tokens (#527).
|
||||||
token := cfg.GetString(tokenConfig)
|
token := cfg.GetString(tokenConfig)
|
||||||
|
appToken := cfg.GetString(appTokenConfig)
|
||||||
if token != "" && !strings.HasPrefix(token, "xoxb") {
|
if token != "" && !strings.HasPrefix(token, "xoxb") {
|
||||||
cfg.Log.Warn("Non-bot token detected. It is STRONGLY recommended to use a proper bot-token instead.")
|
cfg.Log.Warn("Non-bot token detected. It is STRONGLY recommended to use a proper bot-token instead.")
|
||||||
cfg.Log.Warn("Legacy tokens may be deprecated by Slack at short notice. See the Matterbridge GitHub wiki for a migration guide.")
|
cfg.Log.Warn("Legacy tokens may be deprecated by Slack at short notice. See the Matterbridge GitHub wiki for a migration guide.")
|
||||||
cfg.Log.Warn("See https://github.com/42wim/matterbridge/wiki/Slack-bot-setup")
|
cfg.Log.Warn("See https://github.com/42wim/matterbridge/wiki/Slack-bot-setup")
|
||||||
return NewLegacy(cfg)
|
return NewLegacy(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if appToken == "" || !strings.HasPrefix(appToken, "xapp-") {
|
||||||
|
cfg.Log.Fatalf("%s must have the prefix xapp-", appTokenConfig)
|
||||||
|
}
|
||||||
|
|
||||||
return newBridge(cfg)
|
return newBridge(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,18 +111,22 @@ func (b *Bslack) Connect() error {
|
|||||||
return errors.New("no connection method found: WebhookBindAddress, WebhookURL or Token need to be configured")
|
return errors.New("no connection method found: WebhookBindAddress, WebhookURL or Token need to be configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token := b.GetString(tokenConfig)
|
||||||
|
appToken := b.GetString(appTokenConfig)
|
||||||
|
|
||||||
// If we have a token we use the Slack websocket-based RTM for both sending and receiving.
|
// If we have a token we use the Slack websocket-based RTM for both sending and receiving.
|
||||||
if token := b.GetString(tokenConfig); token != "" {
|
if token != "" && appToken != "" {
|
||||||
b.Log.Info("Connecting using token")
|
b.Log.Info("Connecting using token")
|
||||||
|
|
||||||
b.sc = slack.New(token, slack.OptionDebug(b.GetBool("Debug")))
|
b.sc = slack.New(token, slack.OptionDebug(b.GetBool("Debug")), slack.OptionAppLevelToken(appToken))
|
||||||
|
b.smc = socketmode.New(b.sc, socketmode.OptionDebug(b.GetBool("Debug")))
|
||||||
|
|
||||||
b.channels = newChannelManager(b.Log, b.sc)
|
b.channels = newChannelManager(b.Log, b.sc)
|
||||||
b.users = newUserManager(b.Log, b.sc)
|
b.users = newUserManager(b.Log, b.sc)
|
||||||
|
|
||||||
b.rtm = b.sc.NewRTM()
|
|
||||||
go b.rtm.ManageConnection()
|
|
||||||
go b.handleSlack()
|
go b.handleSlack()
|
||||||
|
go b.smc.Run()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,10 +151,6 @@ func (b *Bslack) Connect() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bslack) Disconnect() error {
|
|
||||||
return b.rtm.Disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinChannel only acts as a verification method that checks whether Matterbridge's
|
// JoinChannel only acts as a verification method that checks whether Matterbridge's
|
||||||
// Slack integration is already member of the channel. This is because Slack does not
|
// Slack integration is already member of the channel. This is because Slack does not
|
||||||
// allow apps or bots to join channels themselves and they need to be invited
|
// allow apps or bots to join channels themselves and they need to be invited
|
||||||
@ -285,12 +291,12 @@ func (b *Bslack) sendRTM(msg config.Message) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("could not send message: %v", err)
|
return "", fmt.Errorf("could not send message: %v", err)
|
||||||
}
|
}
|
||||||
if msg.Event == config.EventUserTyping {
|
/*if msg.Event == config.EventUserTyping {
|
||||||
if b.GetBool("ShowUserTyping") {
|
if b.GetBool("ShowUserTyping") {
|
||||||
b.rtm.SendMessage(b.rtm.NewTypingMessage(channelInfo.ID))
|
b.rtm.SendMessage(b.rtm.NewTypingMessage(channelInfo.ID))
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}*/
|
||||||
|
|
||||||
var handled bool
|
var handled bool
|
||||||
|
|
||||||
@ -345,9 +351,9 @@ func (b *Bslack) updateTopicOrPurpose(msg *config.Message, channelInfo *slack.Ch
|
|||||||
incomingChangeType, text := b.extractTopicOrPurpose(msg.Text)
|
incomingChangeType, text := b.extractTopicOrPurpose(msg.Text)
|
||||||
switch incomingChangeType {
|
switch incomingChangeType {
|
||||||
case "topic":
|
case "topic":
|
||||||
updateFunc = b.rtm.SetTopicOfConversation
|
updateFunc = b.smc.SetTopicOfConversation
|
||||||
case "purpose":
|
case "purpose":
|
||||||
updateFunc = b.rtm.SetPurposeOfConversation
|
updateFunc = b.smc.SetPurposeOfConversation
|
||||||
default:
|
default:
|
||||||
b.Log.Errorf("Unhandled type received from extractTopicOrPurpose: %s", incomingChangeType)
|
b.Log.Errorf("Unhandled type received from extractTopicOrPurpose: %s", incomingChangeType)
|
||||||
return nil
|
return nil
|
||||||
@ -393,7 +399,7 @@ func (b *Bslack) deleteMessage(msg *config.Message, channelInfo *slack.Channel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, _, err := b.rtm.DeleteMessage(channelInfo.ID, msg.ID)
|
_, _, err := b.smc.DeleteMessage(channelInfo.ID, msg.ID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
@ -411,7 +417,7 @@ func (b *Bslack) editMessage(msg *config.Message, channelInfo *slack.Channel) (b
|
|||||||
}
|
}
|
||||||
messageOptions := b.prepareMessageOptions(msg)
|
messageOptions := b.prepareMessageOptions(msg)
|
||||||
for {
|
for {
|
||||||
_, _, _, err := b.rtm.UpdateMessage(channelInfo.ID, msg.ID, messageOptions...)
|
_, _, _, err := b.smc.UpdateMessage(channelInfo.ID, msg.ID, messageOptions...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
@ -430,7 +436,7 @@ func (b *Bslack) postMessage(msg *config.Message, channelInfo *slack.Channel) (s
|
|||||||
}
|
}
|
||||||
messageOptions := b.prepareMessageOptions(msg)
|
messageOptions := b.prepareMessageOptions(msg)
|
||||||
for {
|
for {
|
||||||
_, id, err := b.rtm.PostMessage(channelInfo.ID, messageOptions...)
|
_, id, err := b.smc.PostMessage(channelInfo.ID, messageOptions...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user