Skip to content

Commit fe92a5d

Browse files
Ivan ZuevDavyJohnes
authored andcommitted
feat: use slack socket mode instead of rtm api
1 parent 9abbdb5 commit fe92a5d

3 files changed

Lines changed: 142 additions & 141 deletions

File tree

bridge/slack/handlers.go

Lines changed: 88 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"github.com/42wim/matterbridge/bridge/config"
1010
"github.com/42wim/matterbridge/bridge/helper"
1111
"github.com/slack-go/slack"
12+
"github.com/slack-go/slack/slackevents"
13+
"github.com/slack-go/slack/socketmode"
1214
)
1315

1416
// ErrEventIgnored is for events that should be ignored
@@ -48,65 +50,74 @@ func (b *Bslack) handleSlack() {
4850
}
4951

5052
func (b *Bslack) handleSlackClient(messages chan *config.Message) {
51-
for msg := range b.rtm.IncomingEvents {
52-
if msg.Type != sUserTyping && msg.Type != sHello && msg.Type != sLatencyReport {
53-
b.Log.Debugf("== Receiving event %#v", msg.Data)
54-
}
55-
switch ev := msg.Data.(type) {
56-
case *slack.UserTypingEvent:
57-
if !b.GetBool("ShowUserTyping") {
58-
continue
59-
}
60-
rmsg, err := b.handleTypingEvent(ev)
61-
if err == ErrEventIgnored {
62-
continue
63-
} else if err != nil {
64-
b.Log.Errorf("%#v", err)
65-
continue
53+
for msg := range b.smc.Events {
54+
switch msg.Type {
55+
case socketmode.EventTypeConnected:
56+
if authTest, authErr := b.smc.AuthTest(); authErr == nil {
57+
if botInfo, infoErr := b.smc.GetBotInfo(authTest.BotID); infoErr == nil {
58+
b.si = botInfo
59+
60+
b.channels.populateChannels(true)
61+
b.users.populateUsers(true)
62+
} else {
63+
b.Log.Fatalf("Unable to identify bot user")
64+
}
65+
} else {
66+
b.Log.Fatalf("Unable to identify bot user")
6667
}
68+
case socketmode.EventTypeConnectionError:
69+
ev, _ := msg.Data.(slack.ConnectionErrorEvent)
70+
71+
b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
72+
case socketmode.EventTypeErrorWriteFailed:
73+
ev, _ := msg.Data.(socketmode.ErrorWriteFailed)
74+
75+
b.Log.Debugf("%#v", ev.Cause.Error())
76+
case socketmode.EventTypeInvalidAuth:
77+
ev, _ := msg.Data.(slack.InvalidAuthEvent)
78+
79+
b.Log.Fatalf("Invalid Token %#v", ev)
80+
case socketmode.EventTypeHello, socketmode.EventTypeConnecting:
81+
continue
82+
83+
case socketmode.EventTypeEventsAPI:
84+
b.smc.Ack(*msg.Request)
85+
86+
eventsAPIEvent, ok := msg.Data.(slackevents.EventsAPIEvent)
87+
88+
if !ok {
89+
b.Log.Debugf("Ignored %+v", eventsAPIEvent)
6790

68-
messages <- rmsg
69-
case *slack.MessageEvent:
70-
if b.skipMessageEvent(ev) {
71-
b.Log.Debugf("Skipped message: %#v", ev)
72-
continue
73-
}
74-
rmsg, err := b.handleMessageEvent(ev)
75-
if err != nil {
76-
b.Log.Errorf("%#v", err)
7791
continue
7892
}
79-
messages <- rmsg
80-
case *slack.FileDeletedEvent:
81-
rmsg, err := b.handleFileDeletedEvent(ev)
82-
if err != nil {
83-
b.Log.Errorf("%#v", err)
84-
continue
93+
94+
switch innerEventData := eventsAPIEvent.InnerEvent.Data.(type) {
95+
case *slackevents.MessageEvent:
96+
if b.skipMessageEvent(innerEventData) {
97+
b.Log.Debugf("Skipped message: %#v", innerEventData)
98+
continue
99+
}
100+
rmsg, err := b.handleMessageEvent(innerEventData)
101+
if err != nil {
102+
b.Log.Errorf("%#v", err)
103+
continue
104+
}
105+
messages <- rmsg
106+
case *slackevents.MemberJoinedChannelEvent:
107+
if innerEventData.User == b.si.UserID {
108+
channel, err := b.smc.GetConversationInfo(innerEventData.Channel, false)
109+
110+
if err != nil {
111+
b.Log.Errorf("Unable to get conversation info for channel %s", innerEventData.Channel)
112+
}
113+
114+
b.channels.registerChannel(*channel)
115+
} else {
116+
b.users.populateUser(innerEventData.User)
117+
}
85118
}
86-
messages <- rmsg
87-
case *slack.OutgoingErrorEvent:
88-
b.Log.Debugf("%#v", ev.Error())
89-
case *slack.ChannelJoinedEvent:
90-
// When we join a channel we update the full list of users as
91-
// well as the information for the channel that we joined as this
92-
// should now tell that we are a member of it.
93-
b.channels.registerChannel(ev.Channel)
94-
case *slack.ConnectedEvent:
95-
b.si = ev.Info
96-
b.channels.populateChannels(true)
97-
b.users.populateUsers(true)
98-
case *slack.InvalidAuthEvent:
99-
b.Log.Fatalf("Invalid Token %#v", ev)
100-
case *slack.ConnectionErrorEvent:
101-
b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
102-
case *slack.MemberJoinedChannelEvent:
103-
b.users.populateUser(ev.User)
104-
case *slack.HelloEvent, *slack.LatencyReport, *slack.ConnectingEvent:
105-
continue
106-
case *slack.UserChangeEvent:
107-
b.users.invalidateUser(ev.User.ID)
108119
default:
109-
b.Log.Debugf("Unhandled incoming event: %T", ev)
120+
b.Log.Debugf("== Receiving event %#v", msg.Data)
110121
}
111122
}
112123
}
@@ -127,47 +138,44 @@ func (b *Bslack) handleMatterHook(messages chan *config.Message) {
127138
}
128139

129140
// skipMessageEvent skips event that need to be skipped :-)
130-
func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
141+
func (b *Bslack) skipMessageEvent(ev *slackevents.MessageEvent) bool {
131142
switch ev.SubType {
132143
case sChannelLeave, sChannelJoin:
133144
return b.GetBool(noSendJoinConfig)
134145
case sPinnedItem, sUnpinnedItem:
135146
return true
136147
case sChannelTopic, sChannelPurpose:
137148
// Skip the event if our bot/user account changed the topic/purpose
138-
if ev.User == b.si.User.ID {
149+
if ev.BotID == b.si.ID {
139150
return true
140151
}
141152
}
142153

143154
// Check for our callback ID
144155
hasOurCallbackID := false
145-
if len(ev.Blocks.BlockSet) == 1 {
146-
block, ok := ev.Blocks.BlockSet[0].(*slack.SectionBlock)
156+
157+
if len(ev.Attachments) == 1 {
158+
block, ok := ev.Attachments[0].Blocks.BlockSet[0].(*slack.SectionBlock)
147159
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
148160
}
149161

150-
if ev.SubMessage != nil {
162+
if ev.Message != nil {
151163
// It seems ev.SubMessage.Edited == nil when slack unfurls.
152164
// Do not forward these messages. See Github issue #266.
153-
if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
154-
ev.SubMessage.Edited == nil {
165+
if ev.Message.ThreadTimeStamp != ev.Message.TimeStamp &&
166+
ev.Message.Edited == nil {
155167
return true
156168
}
157-
// see hidden subtypes at https://api.slack.com/events/message
158-
// these messages are sent when we add a message to a thread #709
159-
if ev.SubType == "message_replied" && ev.Hidden {
160-
return true
161-
}
162-
if len(ev.SubMessage.Blocks.BlockSet) == 1 {
163-
block, ok := ev.SubMessage.Blocks.BlockSet[0].(*slack.SectionBlock)
169+
170+
if len(ev.Message.Attachments) == 1 {
171+
block, ok := ev.Message.Attachments[0].Blocks.BlockSet[0].(*slack.SectionBlock)
164172
hasOurCallbackID = ok && block.BlockID == "matterbridge_"+b.uuid
165173
}
166174
}
167175

168176
// Skip any messages that we made ourselves or from 'slackbot' (see #527).
169177
if ev.Username == sSlackBotUser ||
170-
(b.rtm != nil && ev.Username == b.si.User.Name) || hasOurCallbackID {
178+
(b.smc != nil && ev.BotID == b.si.ID) || hasOurCallbackID {
171179
return true
172180
}
173181

@@ -177,7 +185,7 @@ func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
177185
return false
178186
}
179187

180-
func (b *Bslack) filesCached(files []slack.File) bool {
188+
func (b *Bslack) filesCached(files []slackevents.File) bool {
181189
for i := range files {
182190
if !b.fileCached(&files[i]) {
183191
return false
@@ -202,7 +210,7 @@ func (b *Bslack) filesCached(files []slack.File) bool {
202210
// 5. Handle any attachments of the received event.
203211
// 6. Check that the Matterbridge message that we end up with after at the end of the
204212
// pipeline is valid before sending it to the Matterbridge router.
205-
func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, error) {
213+
func (b *Bslack) handleMessageEvent(ev *slackevents.MessageEvent) (*config.Message, error) {
206214
rmsg, err := b.populateReceivedMessage(ev)
207215
if err != nil {
208216
return nil, err
@@ -222,14 +230,15 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er
222230
// This is probably a webhook we couldn't resolve.
223231
return nil, fmt.Errorf("message handling resulted in an empty bot message (probably an incoming webhook we couldn't resolve): %#v", ev)
224232
}
225-
if ev.SubMessage != nil {
226-
return nil, fmt.Errorf("message handling resulted in an empty message: %#v with submessage %#v", ev, ev.SubMessage)
233+
if ev.Message != nil {
234+
return nil, fmt.Errorf("message handling resulted in an empty message: %#v with submessage %#v", ev, ev.Message)
227235
}
228236
return nil, fmt.Errorf("message handling resulted in an empty message: %#v", ev)
229237
}
230238
return rmsg, nil
231239
}
232240

241+
// TODO: implement file deletion handle when slack-go library will expose this event
233242
func (b *Bslack) handleFileDeletedEvent(ev *slack.FileDeletedEvent) (*config.Message, error) {
234243
if rawChannel, ok := b.cache.Get(cfileDownloadChannel + ev.FileID); ok {
235244
channel, err := b.channels.getChannelByID(rawChannel.(string))
@@ -250,7 +259,7 @@ func (b *Bslack) handleFileDeletedEvent(ev *slack.FileDeletedEvent) (*config.Mes
250259
return nil, fmt.Errorf("channel ID for file ID %s not found", ev.FileID)
251260
}
252261

253-
func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) bool {
262+
func (b *Bslack) handleStatusEvent(ev *slackevents.MessageEvent, rmsg *config.Message) bool {
254263
switch ev.SubType {
255264
case sChannelJoined, sMemberJoined:
256265
// There's no further processing needed on channel events
@@ -263,16 +272,16 @@ func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message)
263272
b.channels.populateChannels(false)
264273
rmsg.Event = config.EventTopicChange
265274
case sMessageChanged:
266-
rmsg.Text = ev.SubMessage.Text
275+
rmsg.Text = ev.Message.Text
267276
// handle deleted thread starting messages
268-
if ev.SubMessage.Text == "This message was deleted." {
277+
if ev.Message.Text == "This message was deleted." {
269278
rmsg.Event = config.EventMsgDelete
270279
return true
271280
}
272281
case sMessageDeleted:
273282
rmsg.Text = config.EventMsgDelete
274283
rmsg.Event = config.EventMsgDelete
275-
rmsg.ID = ev.DeletedTimestamp
284+
rmsg.ID = ev.PreviousMessage.TimeStamp
276285
// If a message is being deleted we do not need to process
277286
// the event any further so we return 'true'.
278287
return true
@@ -282,7 +291,7 @@ func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message)
282291
return false
283292
}
284293

285-
func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) {
294+
func (b *Bslack) handleAttachments(ev *slackevents.MessageEvent, rmsg *config.Message) {
286295
// File comments are set by the system (because there is no username given).
287296
if ev.SubType == sFileComment {
288297
rmsg.Username = sSystemUser
@@ -317,23 +326,8 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
317326
}
318327
}
319328

320-
func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
321-
if ev.User == b.si.User.ID {
322-
return nil, ErrEventIgnored
323-
}
324-
channelInfo, err := b.channels.getChannelByID(ev.Channel)
325-
if err != nil {
326-
return nil, err
327-
}
328-
return &config.Message{
329-
Channel: channelInfo.Name,
330-
Account: b.Account,
331-
Event: config.EventUserTyping,
332-
}, nil
333-
}
334-
335329
// handleDownloadFile handles file download
336-
func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File, retry bool) error {
330+
func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slackevents.File, retry bool) error {
337331
if b.fileCached(file) {
338332
return nil
339333
}
@@ -395,7 +389,7 @@ func (b *Bslack) handleGetChannelMembers(rmsg *config.Message) bool {
395389
// identically named file but with different content will be uploaded correctly
396390
// (the assumption is that such name collisions will not occur within the given
397391
// timeframes).
398-
func (b *Bslack) fileCached(file *slack.File) bool {
392+
func (b *Bslack) fileCached(file *slackevents.File) bool {
399393
if ts, ok := b.cache.Get("file" + file.ID); ok && time.Since(ts.(time.Time)) < time.Minute {
400394
return true
401395
} else if ts, ok = b.cache.Get("filename" + file.Name); ok && time.Since(ts.(time.Time)) < 10*time.Second {

bridge/slack/helpers.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import (
99
"github.com/42wim/matterbridge/bridge/config"
1010
"github.com/sirupsen/logrus"
1111
"github.com/slack-go/slack"
12+
"github.com/slack-go/slack/slackevents"
1213
)
1314

1415
// populateReceivedMessage shapes the initial Matterbridge message that we will forward to the
1516
// router before we apply message-dependent modifications.
16-
func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Message, error) {
17+
func (b *Bslack) populateReceivedMessage(ev *slackevents.MessageEvent) (*config.Message, error) {
1718
// Use our own func because rtm.GetChannelInfo doesn't work for private channels.
1819
channel, err := b.channels.getChannelByID(ev.Channel)
1920
if err != nil {
@@ -24,29 +25,29 @@ func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Messag
2425
Text: ev.Text,
2526
Channel: channel.Name,
2627
Account: b.Account,
27-
ID: ev.Timestamp,
28+
ID: ev.TimeStamp,
2829
Extra: make(map[string][]interface{}),
29-
ParentID: ev.ThreadTimestamp,
30+
ParentID: ev.ThreadTimeStamp,
3031
Protocol: b.Protocol,
3132
}
3233
if b.useChannelID {
3334
rmsg.Channel = "ID:" + channel.ID
3435
}
3536

3637
// Handle 'edit' messages.
37-
if ev.SubMessage != nil && !b.GetBool(editDisableConfig) {
38-
rmsg.ID = ev.SubMessage.Timestamp
39-
if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {
40-
b.Log.Debugf("SubMessage %#v", ev.SubMessage)
41-
rmsg.Text = ev.SubMessage.Text + b.GetString(editSuffixConfig)
38+
if ev.Message != nil && !b.GetBool(editDisableConfig) {
39+
rmsg.ID = ev.Message.TimeStamp
40+
if ev.Message.ThreadTimeStamp != ev.Message.TimeStamp {
41+
b.Log.Debugf("SubMessage %#v", ev.Message)
42+
rmsg.Text = ev.Message.Text + b.GetString(editSuffixConfig)
4243
}
4344
}
4445

4546
// For edits, only submessage has thread ts.
4647
// Ensures edits to threaded messages maintain their prefix hint on the
4748
// unthreaded end.
48-
if ev.SubMessage != nil {
49-
rmsg.ParentID = ev.SubMessage.ThreadTimestamp
49+
if ev.Message != nil {
50+
rmsg.ParentID = ev.Message.ThreadTimeStamp
5051
}
5152

5253
if err = b.populateMessageWithUserInfo(ev, rmsg); err != nil {
@@ -55,7 +56,7 @@ func (b *Bslack) populateReceivedMessage(ev *slack.MessageEvent) (*config.Messag
5556
return rmsg, err
5657
}
5758

58-
func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *config.Message) error {
59+
func (b *Bslack) populateMessageWithUserInfo(ev *slackevents.MessageEvent, rmsg *config.Message) error {
5960
if ev.SubType == sMessageDeleted || ev.SubType == sFileComment {
6061
return nil
6162
}
@@ -71,8 +72,8 @@ func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *confi
7172
switch {
7273
case ev.User != "":
7374
userID = ev.User
74-
case ev.SubMessage != nil && ev.SubMessage.User != "":
75-
userID = ev.SubMessage.User
75+
case ev.Message != nil && ev.Message.User != "":
76+
userID = ev.Message.User
7677
default:
7778
return nil
7879
}
@@ -90,15 +91,15 @@ func (b *Bslack) populateMessageWithUserInfo(ev *slack.MessageEvent, rmsg *confi
9091
return nil
9192
}
9293

93-
func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config.Message) error {
94+
func (b *Bslack) populateMessageWithBotInfo(ev *slackevents.MessageEvent, rmsg *config.Message) error {
9495
if ev.BotID == "" || b.GetString(outgoingWebhookConfig) != "" {
9596
return nil
9697
}
9798

9899
var err error
99100
var bot *slack.Bot
100101
for {
101-
bot, err = b.rtm.GetBotInfo(ev.BotID)
102+
bot, err = b.smc.GetBotInfo(ev.BotID)
102103
if err == nil {
103104
break
104105
}

0 commit comments

Comments
 (0)