Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MM-56501 Enable/disable automuting when a user connects/disconnects #479

Merged
merged 15 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ linters:
enable:
- bodyclose
- errcheck
- gocritic
- gofmt
- goimports
- gosec
Expand All @@ -42,6 +41,12 @@ issues:
- path: server/configuration.go
linters:
- unused

- linters:
- revive
text: "var-naming|error-naming|exported|increment-decrement|error-strings|if-return|unused-parameter|blank-imports|empty-block"
# We need to fix the unused parameter issues and remove the exception.

- path: _test\.go
linters:
- bodyclose
Expand Down
2 changes: 2 additions & 0 deletions server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ func (a *API) oauthRedirectHandler(w http.ResponseWriter, r *http.Request) {
return
}

_, _ = a.p.updateAutomutingOnUserConnect(mmUserID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we log LogWarn any error that's returned here to avoid burying it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is actually a bit misleading since we do log any errors inside updateAutomutingOnUserConnect. I wanted the method to log its own errors to keep the automute logic more self-contained, but I still wanted the method to return errors because that makes testing easier


bundlePath, err := a.p.API.GetBundlePath()
if err != nil {
a.p.API.LogWarn("Failed to get bundle path.", "error", err.Error())
Expand Down
45 changes: 20 additions & 25 deletions server/automute.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ func (p *Plugin) setAutomuteIsEnabledForUser(userID string, channelsAutomuted bo
return nil
}

// func (p *Plugin) isUsersPrimaryPlatformTeams(userID string) bool {
// pref, appErr := p.API.GetPreferenceForUser(userID, PreferenceCategoryPlugin, PreferenceNamePlatform)
// if appErr != nil {
// // GetPreferenceForUser returns an error when a preference is unset, so we default to MM being primary platform
// return false
// }
func (p *Plugin) isUsersPrimaryPlatformTeams(userID string) bool {
pref, appErr := p.API.GetPreferenceForUser(userID, PreferenceCategoryPlugin, PreferenceNamePlatform)
if appErr != nil {
// GetPreferenceForUser returns an error when a preference is unset, so we default to MM being primary platform
hmhealey marked this conversation as resolved.
Show resolved Hide resolved
return false
}

// return pref.Value == PreferenceValuePlatformMSTeams
// }
return pref.Value == PreferenceValuePlatformMSTeams
}

func (p *Plugin) isUserConnected(userID string) (bool, error) {
token, err := p.store.GetTokenForMattermostUser(userID)
Expand All @@ -145,33 +145,28 @@ func (p *Plugin) isUserConnected(userID string) (bool, error) {
return token != nil, nil
}

// // canAutomuteChannelID returns true if the channel with the given ID is either explicitly linked to a channel in
// // MS Teams or if it's a DM/GM channel that is implicitly linked to MS Teams.
// func (p *Plugin) canAutomuteChannelID(channelID string) (bool, error) {
// channel, appErr := p.API.GetChannel(channelID)
// if appErr != nil {
// return false, errors.Wrap(appErr, fmt.Sprintf("Unable to get channel %s to check if it's a DM/GM channel", channelID))
// }
// canAutomuteChannelID returns true if the channel is either explicitly linked to a channel in MS Teams or if it's a
// DM/GM channel that is implicitly linked to MS Teams.
func (p *Plugin) canAutomuteChannelID(channelID string) (bool, error) {
channel, err := p.API.GetChannel(channelID)
if err != nil {
return false, errors.Wrap(err, fmt.Sprintf("Unable to get channel %s to determine if it can be automuted", channelID))
}

// return p.canAutomuteChannel(channel)
// }
return p.canAutomuteChannel(channel)
}

// canAutomuteChannel returns true if the channel is either explicitly linked to a channel in MS Teams or if it's a
// DM/GM channel that is implicitly linked to MS Teams.
func (p *Plugin) canAutomuteChannel(channel *model.Channel) (bool, error) {
// Automute all DM/GM channels
if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
if channel.IsGroupOrDirect() {
return true, nil
}

return p.isChannelLinked(channel.Id)
}

// isChannelLinked returns true if the channel is explicitly linked to a channel in MS Teams.
func (p *Plugin) isChannelLinked(channelID string) (bool, error) {
link, err := p.store.GetLinkByChannelID(channelID)
link, err := p.store.GetLinkByChannelID(channel.Id)
if err != nil && err != sql.ErrNoRows {
return false, errors.Wrap(err, fmt.Sprintf("Unable to determine if channel %s is linked to MS Teams", channelID))
return false, errors.Wrap(err, fmt.Sprintf("Unable to determine if channel %s is linked to MS Teams", channel.Id))
}

// The channel is linked as long as a ChannelLink exists
Expand Down
94 changes: 94 additions & 0 deletions server/automute_channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"fmt"

"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"

"github.com/pkg/errors"
)

func (p *Plugin) UserHasJoinedChannel(c *plugin.Context, channelMember *model.ChannelMember, actor *model.User) {
_, _ = p.updateAutomutingOnUserJoinedChannel(c, channelMember.UserId, channelMember.ChannelId)
}

func (p *Plugin) updateAutomutingOnUserJoinedChannel(c *plugin.Context, userID string, channelID string) (bool, error) {
if automuteEnabled := p.getAutomuteIsEnabledForUser(userID); !automuteEnabled {
return false, nil
}

if canAutomute, err := p.canAutomuteChannelID(channelID); err != nil {
p.API.LogError(
"Unable to check if channel is linked to update automuting when a user has joined the channel",
"user_id", userID,
"channel_id", channelID,
"error", err.Error(),
)
return false, errors.Wrap(err, "Unable to update automuting when a user has joined a channel")
} else if !canAutomute {
// Only automute channels that are linked
return false, nil
}

err := p.setChannelMembersAutomuted([]*model.ChannelMemberIdentifier{{UserId: userID, ChannelId: channelID}}, true)
return err == nil, err
}

func (p *Plugin) ChannelHasBeenCreated(c *plugin.Context, channel *model.Channel) {
_ = p.updateAutomutingOnChannelCreated(channel)
}

func (p *Plugin) updateAutomutingOnChannelCreated(channel *model.Channel) error {
if !channel.IsGroupOrDirect() {
// Assume that newly created channels can never be linked by the time this is called
return nil
}

return p.updateAutomutingForChannelMembers(channel.Id, true)
}

func (p *Plugin) updateAutomutingOnChannelLinked(channelID string) error {
// This simply mutes the channel for all users with automuting enabled, regardless of their settings before. It
// doesn't pay attention to if the user manually muted the channel beforehand.
return p.updateAutomutingForChannelMembers(channelID, true)
}

func (p *Plugin) updateAutomutingOnChannelUnlinked(channelID string) error {
// This simply unmutes the channel for all users with automuting enabled, regardless of their settings before. It
// doesn't pay attention to if the user manually muted the channel beforehand to keep it muted.
return p.updateAutomutingForChannelMembers(channelID, false)
}

func (p *Plugin) updateAutomutingForChannelMembers(channelID string, enableAutomute bool) error {
var membersToUpdate []*model.ChannelMemberIdentifier

page := 0
perPage := 200
for {
members, appErr := p.API.GetChannelMembers(channelID, page, perPage)
if appErr != nil {
return errors.Wrap(appErr, fmt.Sprintf("Unable to get all members of channel %s to update automuting", channelID))
}

for _, member := range members {
if p.getAutomuteIsEnabledForUser(member.UserId) {
membersToUpdate = append(membersToUpdate, &model.ChannelMemberIdentifier{ChannelId: channelID, UserId: member.UserId})
}
}

if len(members) < perPage {
break
}

page += 1
}

if len(membersToUpdate) > 0 {
if err := p.setChannelMembersAutomuted(membersToUpdate, enableAutomute); err != nil {
return err
}
}

return nil
}
Loading
Loading