From fb713ed91bfb48c0037e489f80be85c54e69953a Mon Sep 17 00:00:00 2001 From: Wim Date: Fri, 18 Jan 2019 18:35:31 +0100 Subject: Add initial support for getting ChannelMember info of all bridges (#678) * Add initial support for getting ChannelMember info of all bridges. Adds an EventGetChannelMembers event, which gets send every x time to all bridges. Bridges should respond on this event with a Message containing ChannelMembers in the EventGetChannelMembers key in the Extra field. handleEventGetChannelMembers will handle this Message and sets the contained ChannelMembers to the Bridge struct. * Add ChannelMembers support to the slack bridge --- bridge/slack/handlers.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ bridge/slack/helpers.go | 46 ++++++++++++++++++++++++++++++++++++++++++ bridge/slack/slack.go | 8 ++++++++ 3 files changed, 106 insertions(+) (limited to 'bridge/slack') diff --git a/bridge/slack/handlers.go b/bridge/slack/handlers.go index f46eb595..5e601eaa 100644 --- a/bridge/slack/handlers.go +++ b/bridge/slack/handlers.go @@ -309,6 +309,58 @@ func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File, retr return nil } +// handleGetChannelMembers handles messages containing the GetChannelMembers event +// Sends a message to the router containing *config.ChannelMembers +func (b *Bslack) handleGetChannelMembers(rmsg *config.Message) bool { + if rmsg.Event != config.EventGetChannelMembers { + return false + } + + cMembers := config.ChannelMembers{} + + b.channelMembersMutex.RLock() + + for channelID, members := range b.channelMembers { + for _, member := range members { + channelName := "" + userName := "" + userNick := "" + user := b.getUser(member) + if user != nil { + userName = user.Name + userNick = user.Profile.DisplayName + } + channel, _ := b.getChannelByID(channelID) + if channel != nil { + channelName = channel.Name + } + cMember := config.ChannelMember{ + Username: userName, + Nick: userNick, + UserID: member, + ChannelID: channelID, + ChannelName: channelName, + } + cMembers = append(cMembers, cMember) + } + } + + b.channelMembersMutex.RUnlock() + + extra := make(map[string][]interface{}) + extra[config.EventGetChannelMembers] = append(extra[config.EventGetChannelMembers], cMembers) + msg := config.Message{ + Extra: extra, + Event: config.EventGetChannelMembers, + Account: b.Account, + } + + b.Log.Debugf("sending msg to remote %#v", msg) + b.Remote <- msg + + return true +} + // fileCached implements Matterbridge's caching logic for files // shared via Slack. // diff --git a/bridge/slack/helpers.go b/bridge/slack/helpers.go index 4e6e5652..d84353f0 100644 --- a/bridge/slack/helpers.go +++ b/bridge/slack/helpers.go @@ -93,7 +93,9 @@ func (b *Bslack) populateUsers(wait bool) { return } for b.refreshInProgress { + b.refreshMutex.Unlock() time.Sleep(time.Second) + b.refreshMutex.Lock() } b.refreshInProgress = true b.refreshMutex.Unlock() @@ -139,13 +141,16 @@ func (b *Bslack) populateChannels(wait bool) { return } for b.refreshInProgress { + b.refreshMutex.Unlock() time.Sleep(time.Second) + b.refreshMutex.Lock() } b.refreshInProgress = true b.refreshMutex.Unlock() newChannelsByID := map[string]*slack.Channel{} newChannelsByName := map[string]*slack.Channel{} + newChannelMembers := make(map[string][]string) // We only retrieve public and private channels, not IMs // and MPIMs as those do not have a channel name. @@ -166,7 +171,18 @@ func (b *Bslack) populateChannels(wait bool) { for i := range channels { newChannelsByID[channels[i].ID] = &channels[i] newChannelsByName[channels[i].Name] = &channels[i] + // also find all the members in every channel + members, err := b.getUsersInConversation(channels[i].ID) + if err != nil { + if err = b.handleRateLimit(err); err != nil { + b.Log.Errorf("Could not retrieve channel members: %#v", err) + return + } + continue + } + newChannelMembers[channels[i].ID] = members } + if nextCursor == "" { break } @@ -178,6 +194,10 @@ func (b *Bslack) populateChannels(wait bool) { b.channelsByID = newChannelsByID b.channelsByName = newChannelsByName + b.channelMembersMutex.Lock() + defer b.channelMembersMutex.Unlock() + b.channelMembers = newChannelMembers + b.refreshMutex.Lock() defer b.refreshMutex.Unlock() b.earliestChannelRefresh = time.Now().Add(minimumRefreshInterval) @@ -367,3 +387,29 @@ func (b *Bslack) handleRateLimit(err error) error { time.Sleep(rateLimit.RetryAfter) return nil } + +// getUsersInConversation returns an array of userIDs that are members of channelID +func (b *Bslack) getUsersInConversation(channelID string) ([]string, error) { + channelMembers := []string{} + for { + queryParams := &slack.GetUsersInConversationParameters{ + ChannelID: channelID, + } + + members, nextCursor, err := b.sc.GetUsersInConversation(queryParams) + if err != nil { + if err = b.handleRateLimit(err); err != nil { + return channelMembers, fmt.Errorf("Could not retrieve users in channels: %#v", err) + } + continue + } + + channelMembers = append(channelMembers, members...) + + if nextCursor == "" { + break + } + queryParams.Cursor = nextCursor + } + return channelMembers, nil +} diff --git a/bridge/slack/slack.go b/bridge/slack/slack.go index b9430278..001b1268 100644 --- a/bridge/slack/slack.go +++ b/bridge/slack/slack.go @@ -37,6 +37,9 @@ type Bslack struct { channelsByName map[string]*slack.Channel channelsMutex sync.RWMutex + channelMembers map[string][]string + channelMembersMutex sync.RWMutex + refreshInProgress bool earliestChannelRefresh time.Time earliestUserRefresh time.Time @@ -267,6 +270,11 @@ func (b *Bslack) sendWebhook(msg config.Message) error { } func (b *Bslack) sendRTM(msg config.Message) (string, error) { + // Handle channelmember messages. + if handled := b.handleGetChannelMembers(&msg); handled { + return "", nil + } + channelInfo, err := b.getChannel(msg.Channel) if err != nil { return "", fmt.Errorf("could not send message: %v", err) -- cgit v1.2.3