Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

commit 24d652c0ede4efc5987aa8595aa4b2a620c1cf8d Author: Duco van Amstel <duco.vanamstel@gmail.com> Date: Tue Oct 16 19:34:09 2018 +0000 Clean up user and channel information management (slack) (#521) diff --git a/bridge/slack/handlers.go b/bridge/slack/handlers.go index 56fd702..b6400a8 100644 --- a/bridge/slack/handlers.go +++ b/bridge/slack/handlers.go @@ -6125 +6111 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {   case *slack.OutgoingErrorEvent:   b.Log.Debugf("%#v", ev.Error())   case *slack.ChannelJoinedEvent: - var err error - b.users, err = b.sc.GetUsers() - if err != nil { - b.Log.Errorf("Could not reload users: %#v", err) - } + b.populateUsers()   case *slack.ConnectedEvent: - var err error - b.channels, _, err = b.sc.GetConversations(&slack.GetConversationsParameters{ - Limit: 1000, - Types: []string{"public_channel,private_channel,mpim,im"}, - }) - if err != nil { - b.Log.Errorf("Channel list failed: %#v", err) - }   b.si = ev.Info - b.users, err = b.sc.GetUsers() - if err != nil { - b.Log.Errorf("Could not reload users: %#v", err) - } + b.populateChannels() + b.populateUsers()   case *slack.InvalidAuthEvent:   b.Log.Fatalf("Invalid Token %#v", ev)   case *slack.ConnectionErrorEvent: @@ -1639 +1497 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er     // update the userlist on a channel_join   if ev.SubType == sChannelJoin { - if b.users, err = b.sc.GetUsers(); err != nil { - return nil, err - } + b.populateUsers()   }     // Edit message diff --git a/bridge/slack/helpers.go b/bridge/slack/helpers.go index 9af5dfa..18dded3 100644 --- a/bridge/slack/helpers.go +++ b/bridge/slack/helpers.go @@ -3827 +3872 @@ func (b *Bslack) getChannel(channel string) (*slack.Channel, error) {  }    func (b *Bslack) getChannelByName(name string) (*slack.Channel, error) { - if b.channels == nil { - return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, name) - } - for _, channel := range b.channels { - if channel.Name == name { - return &channel, nil - } + b.channelsMutex.RLock() + defer b.channelsMutex.RUnlock() + + if channel, ok := b.channelsByName[name]; ok { + return channel, nil   }   return nil, fmt.Errorf("%s: channel %s not found", b.Account, name)  }    func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) { - if b.channels == nil { - return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, ID) + b.channelsMutex.RLock() + defer b.channelsMutex.RUnlock() + + if channel, ok := b.channelsByID[ID]; ok { + return channel, nil + } + return nil, fmt.Errorf("%s: channel %s not found", b.Account, ID) +} + +func (b *Bslack) populateUsers() { + users, err := b.sc.GetUsers() + if err != nil { + b.Log.Errorf("Could not reload users: %#v", err) + return + } + + newUsers := map[string]*slack.User{} + for _, user := range users { + newUsers[user.ID] = &user   } - for _, channel := range b.channels { - if channel.ID == ID { - return &channel, nil + + b.usersMutex.Lock() + defer b.usersMutex.Unlock() + b.users = newUsers +} + +func (b *Bslack) populateChannels() { + newChannelsByID := map[string]*slack.Channel{} + newChannelsByName := map[string]*slack.Channel{} + + // We only retrieve public and private channels, not IMs + // and MPIMs as those do not have a channel name. + queryParams := &slack.GetConversationsParameters{ + ExcludeArchived: "true", + Types: []string{"public_channel,private_channel"}, + } + for { + channels, nextCursor, err := b.sc.GetConversations(queryParams) + if err != nil { + b.Log.Errorf("Could not reload channels: %#v", err) + return   } + for i := 0; i < len(channels); i++ { + newChannelsByID[channels[i].ID] = &channels[i] + newChannelsByName[channels[i].Name] = &channels[i] + } + if nextCursor == "" { + break + } + queryParams.Cursor = nextCursor   } - return nil, fmt.Errorf("%s: channel %s not found", b.Account, ID) + + b.channelsMutex.Lock() + defer b.channelsMutex.Unlock() + b.channelsByID = newChannelsByID + b.channelsByName = newChannelsByName  }    var ( diff --git a/bridge/slack/slack.go b/bridge/slack/slack.go index 6524f97..6585c9f 100644 --- a/bridge/slack/slack.go +++ b/bridge/slack/slack.go @@ -1617 +1624 @@ import (  )    type Bslack struct { - mh *matterhook.Client - sc *slack.Client - rtm *slack.RTM - users []slack.User - si *slack.Info - channels []slack.Channel + sync.RWMutex + *bridge.Config + + mh *matterhook.Client + sc *slack.Client + rtm *slack.RTM + si *slack.Info +   cache *lru.Cache - useChannelID bool   uuid string - *bridge.Config - sync.RWMutex + useChannelID bool + + users map[string]*slack.User + usersMutex sync.RWMutex + + channelsByID map[string]*slack.Channel + channelsByName map[string]*slack.Channel + channelsMutex sync.RWMutex  }    const ( @@ -619 +6812 @@ func New(cfg *bridge.Config) bridge.Bridger {   cfg.Log.Fatalf("Could not create LRU cache for Slack bridge: %v", err)   }   b := &Bslack{ - Config: cfg, - uuid: xid.New().String(), - cache: newCache, + Config: cfg, + uuid: xid.New().String(), + cache: newCache, + users: map[string]*slack.User{}, + channelsByID: map[string]*slack.Channel{}, + channelsByName: map[string]*slack.Channel{},   }   return b  } @@ -13237 +14225 @@ func (b *Bslack) Disconnect() error {   return b.rtm.Disconnect()  }   +// 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 +// allow apps or bots to join channels themselves and they need to be invited +// manually by a user.  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.populateChannels() + + channelInfo, err := b.getChannel(channel.Name) + if err != nil { + return fmt.Errorf("could not join channel: %#v", err) + } + + if strings.HasPrefix(channel.Name, "ID:") {   b.useChannelID = true - ch, err := b.sc.GetChannelInfo(idcheck[1]) - if err != nil { - return err - } - channel.Name = ch.Name - if err != nil { - return err - } + channel.Name = channelInfo.Name   }   - // we can only join channels using the API - if b.sc != nil { - if strings.HasPrefix(b.GetString(tokenConfig), "xoxb") { - // TODO check if bot has already joined channel - return nil - } - _, err := b.sc.JoinChannel(channel.Name) - if err != nil { - switch err.Error() { - case "name_taken", "restricted_action": - case "default": - { - return err - } - } - } + if !channelInfo.IsMember { + return fmt.Errorf("slack integration that matterbridge is using is not member of channel '%s', please add it manually", channelInfo.Name)   }   return nil  }