Skip to content

Commit

Permalink
Add support new version of Slack applocation
Browse files Browse the repository at this point in the history
- Added support of Event API in socketMode
- Fix files upload for new applications
  • Loading branch information
kolsys committed Jul 1, 2024
1 parent d16645c commit 5b86262
Show file tree
Hide file tree
Showing 17 changed files with 2,575 additions and 22 deletions.
1 change: 1 addition & 0 deletions bridge/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ type Protocol struct {
TeamID string // msteams
TenantID string // msteams
Token string // gitter, slack, discord, api, matrix
AppToken string // slack
Topic string // zulip
URL string // mattermost, slack // DEPRECATED
UseAPI bool // mattermost, slack
Expand Down
135 changes: 135 additions & 0 deletions bridge/slack/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/slack-go/slack/slackevents"
"encoding/json"
)

// ErrEventIgnored is for events that should be ignored
Expand All @@ -19,6 +22,9 @@ func (b *Bslack) handleSlack() {
if b.GetString(incomingWebhookConfig) != "" && b.GetString(tokenConfig) == "" {
b.Log.Debugf("Choosing webhooks based receiving")
go b.handleMatterHook(messages)
} else if b.GetString(appTokenConfig) != "" && b.GetString(tokenConfig) != "" {
b.Log.Debugf("Choosing socket mode based receiving")
go b.handleSlackClientSocketMode(messages)
} else {
b.Log.Debugf("Choosing token based receiving")
go b.handleSlackClient(messages)
Expand Down Expand Up @@ -47,6 +53,135 @@ func (b *Bslack) handleSlack() {
}
}

func (b *Bslack) handleSlackClientSocketMode(messages chan *config.Message) {

for evt := range b.smc.Events {
switch evt.Type {
case socketmode.EventTypeConnecting:
b.Log.Debug("Connecting to Slack with Socket Mode...")
case socketmode.EventTypeConnectionError:
b.Log.Debug("Connection failed. Retrying later...")
case socketmode.EventTypeConnected:
b.Log.Debug("Connected to Slack with Socket Mode.")
if info, err := b.rtm.AuthTest(); err == nil {
b.si = &slack.Info {
User: &slack.UserDetails{
ID: info.UserID,
Name: info.User,
},
Team: &slack.Team{
ID: info.TeamID,
Name: info.Team,
},
}
b.channels.populateChannels(true)
b.users.populateUsers(true)
} else {
b.Log.Fatalf("Get user info error %+v", err)
}
case socketmode.EventTypeEventsAPI:
eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent)
if !ok {
b.Log.Printf("Ignored %+v\n", evt)
continue
}

b.smc.Ack(*evt.Request)

switch eventsAPIEvent.Type {
case slackevents.CallbackEvent:
innerEvent := eventsAPIEvent.InnerEvent
// b.Log.Debugf("Event received %+v", innerEvent)
switch ev := innerEvent.Data.(type) {
case *slackevents.MessageEvent:
// Workaround for handler compability
evString, _ := json.Marshal(ev)
b.Log.Debugf("Message event: %s", evString)
slackEvent := &slack.MessageEvent{}
if err := json.Unmarshal(evString, &slackEvent); err != nil {
b.Log.Errorf("Skipped message: %#v", err)
continue
}

if b.skipMessageEvent(slackEvent) {
b.Log.Debugf("Skipped message: %#v", slackEvent)
continue
}
rmsg, err := b.handleMessageEvent(slackEvent)
if err != nil {
b.Log.Errorf("%#v", err)
continue
}
messages <- rmsg
case *slackevents.FileDeletedEvent:
slackEvent := &slack.FileDeletedEvent{
Type: ev.Type,
EventTimestamp: ev.EventTimestamp,
FileID: ev.FileID,
}
rmsg, err := b.handleFileDeletedEvent(slackEvent)
if err != nil {
b.Log.Printf("%#v", err)
continue
}
messages <- rmsg
case *slackevents.MemberJoinedChannelEvent:
b.users.populateUser(ev.User)
case *slackevents.UserProfileChangedEvent:
b.users.invalidateUser(ev.User.ID)

// TODO not implemented
// 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 *slackevents.AppMentionEvent:
default:
b.Log.Debugf("Unhandled incoming event: %T", ev)
}
default:
b.Log.Printf("Unsupported Events API event received: %+v", eventsAPIEvent.Type)
}
case socketmode.EventTypeInteractive:
callback, ok := evt.Data.(slack.InteractionCallback)
if !ok {
b.Log.Printf("Ignored %+v\n", evt)
continue
}

b.Log.Debugf("Interaction skipped: %+v\n", callback)

var payload interface{}

switch callback.Type {
case slack.InteractionTypeBlockActions:
case slack.InteractionTypeShortcut:
case slack.InteractionTypeViewSubmission:
case slack.InteractionTypeDialogSubmission:
default:

}

b.smc.Ack(*evt.Request, payload)
case socketmode.EventTypeSlashCommand:
cmd, ok := evt.Data.(slack.SlashCommand)
if !ok {
b.Log.Printf("Ignored %+v\n", evt)
} else {
b.Log.Debugf("Slash command skipped: %+v", cmd)
}
var payload interface{}
b.smc.Ack(*evt.Request, payload)
case "hello":
continue
default:
b.Log.Errorf("Unexpected event type received: %s\n", evt.Type)
}
}
}

func (b *Bslack) handleSlackClient(messages chan *config.Message) {
for msg := range b.rtm.IncomingEvents {
if msg.Type != sUserTyping && msg.Type != sHello && msg.Type != sLatencyReport {
Expand Down
84 changes: 62 additions & 22 deletions bridge/slack/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/rs/xid"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
)

type Bslack struct {
Expand All @@ -24,6 +25,7 @@ type Bslack struct {
mh *matterhook.Client
sc *slack.Client
rtm *slack.RTM
smc *socketmode.Client
si *slack.Info

cache *lru.Cache
Expand Down Expand Up @@ -57,6 +59,7 @@ const (
cfileDownloadChannel = "file_download_channel"

tokenConfig = "Token"
appTokenConfig = "AppToken"
incomingWebhookConfig = "WebhookBindAddress"
outgoingWebhookConfig = "WebhookURL"
skipTLSConfig = "SkipTLSVerify"
Expand Down Expand Up @@ -109,14 +112,26 @@ func (b *Bslack) Connect() error {
if token := b.GetString(tokenConfig); token != "" {
b.Log.Info("Connecting using token")

b.sc = slack.New(token, slack.OptionDebug(b.GetBool("Debug")))
appToken := b.GetString(appTokenConfig)
b.sc = slack.New(token, slack.OptionDebug(b.GetBool("Debug")), slack.OptionAppLevelToken(appToken))

b.channels = newChannelManager(b.Log, b.sc)
b.users = newUserManager(b.Log, b.sc)

b.rtm = b.sc.NewRTM()
go b.rtm.ManageConnection()

if appToken != "" {
b.smc = socketmode.New(
b.sc,
socketmode.OptionDebug(b.GetBool("Debug")),
)
} else {
go b.rtm.ManageConnection()
}
go b.handleSlack()
if b.smc != nil {
go b.smc.Run()
}
return nil
}

Expand Down Expand Up @@ -457,35 +472,60 @@ func (b *Bslack) uploadFile(msg *config.Message, channelID string) (string, erro
// Because the result of the UploadFile is slower than the MessageEvent from slack
// we can't match on the file ID yet, so we have to match on the filename too.
ts := time.Now()
b.Log.Debugf("Adding file %s to cache at %s with timestamp", fi.Name, ts.String())
fSize := int(fi.Size)
if fSize == 0 {
fSize = len(*fi.Data)
}
b.Log.Debugf("Adding file %s to cache at %s with timestamp, size %d", fi.Name, ts.String(), fSize)
b.cache.Add("filename"+fi.Name, ts)
initialComment := fmt.Sprintf("File from %s", msg.Username)
if fi.Comment != "" {
initialComment += fmt.Sprintf(" with comment: %s", fi.Comment)
}
res, err := b.sc.UploadFile(slack.FileUploadParameters{
Reader: bytes.NewReader(*fi.Data),
Filename: fi.Name,
Channels: []string{channelID},
InitialComment: initialComment,
ThreadTimestamp: msg.ParentID,
})
if err != nil {
b.Log.Errorf("uploadfile %#v", err)
return "", err
}
if res.ID != "" {
b.Log.Debugf("Adding file ID %s to cache with timestamp %s", res.ID, ts.String())
b.cache.Add("file"+res.ID, ts)

// search for message id by uploaded file in private/public channels, get thread timestamp from uploaded file
if v, ok := res.Shares.Private[channelID]; ok && len(v) > 0 {
messageID = v[0].Ts
if b.smc != nil {
res, err := b.sc.UploadFileV2(slack.UploadFileV2Parameters{
Reader: bytes.NewReader(*fi.Data),
Filename: fi.Name,
FileSize: fSize,
Channel: channelID,
InitialComment: initialComment,
ThreadTimestamp: msg.ParentID,
})
if err != nil {
b.Log.Errorf("uploadfile %#v", err)
return "", err
}
if res.ID != "" {
b.Log.Debugf("Adding file ID %s to cache with timestamp %s", res.ID, ts.String())
b.cache.Add("file"+res.ID, ts)
messageID = res.ID // TODO
}
if v, ok := res.Shares.Public[channelID]; ok && len(v) > 0 {
messageID = v[0].Ts
} else { // Deprecated version
res, err := b.sc.UploadFile(slack.FileUploadParameters{
Reader: bytes.NewReader(*fi.Data),
Filename: fi.Name,
Channels: []string{channelID},
InitialComment: initialComment,
ThreadTimestamp: msg.ParentID,
})
if err != nil {
b.Log.Errorf("uploadfile %#v", err)
return "", err
}
if res.ID != "" {
b.Log.Debugf("Adding file ID %s to cache with timestamp %s", res.ID, ts.String())
b.cache.Add("file"+res.ID, ts)
// search for message id by uploaded file in private/public channels, get thread timestamp from uploaded file
if v, ok := res.Shares.Private[channelID]; ok && len(v) > 0 {
messageID = v[0].Ts
}
if v, ok := res.Shares.Public[channelID]; ok && len(v) > 0 {
messageID = v[0].Ts
}
}
}

}
return messageID, nil
}
Expand Down
5 changes: 5 additions & 0 deletions matterbridge.toml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,11 @@ PreserveThreading=false
#Use https://api.slack.com/custom-integrations/legacy-tokens
#REQUIRED (when not using webhooks)
Token="yourslacktoken"
#Socket mode token to use modern application API (Events API + socket mode)
#See https://github.com/42wim/matterbridge/issues/2159
#REQUIRED (when not using webhooks or legacy application)
#AppToken="yourslackapptoken"


#Extra slack specific debug info, warning this generates a lot of output.
#OPTIONAL (default false)
Expand Down
36 changes: 36 additions & 0 deletions vendor/github.com/slack-go/slack/slackevents/action_events.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5b86262

Please sign in to comment.