@@ -20,6 +20,7 @@ import (
2020 "github.com/mattermost/mattermost/server/public/model"
2121
2222 "github.com/mattermost/mattermost-plugin-jira/server/utils"
23+ "github.com/mattermost/mattermost-plugin-jira/server/utils/kvstore"
2324 "github.com/mattermost/mattermost-plugin-jira/server/utils/types"
2425)
2526
@@ -40,6 +41,7 @@ const (
4041 CommentVisibility = "commentVisibility"
4142 TeamFilter = "teamField"
4243 CommentVisibilityGroupType = "group"
44+ maxDMGMChannelMembers = 200
4345)
4446
4547type FieldFilter struct {
@@ -444,13 +446,66 @@ func (p *Plugin) removeChannelSubscription(instanceID types.ID, subscriptionID s
444446 })
445447}
446448
449+ func isDirectOrGroupChannel (channel * model.Channel ) bool {
450+ return channel .Type == model .ChannelTypeDirect || channel .Type == model .ChannelTypeGroup
451+ }
452+
453+ // channelHasConnectedMember reports whether any non-bot member of channel is still
454+ // connected to instanceID. Channel types other than DM/GM are always allowed, since
455+ // they are not tied to any single user's connection.
456+ func (p * Plugin ) channelHasConnectedMember (instanceID types.ID , channel * model.Channel ) (bool , error ) {
457+ if ! isDirectOrGroupChannel (channel ) {
458+ return true , nil
459+ }
460+
461+ botUserID := p .getConfig ().botUserID
462+ members , err := p .client .Channel .ListMembers (channel .Id , 0 , maxDMGMChannelMembers )
463+ if err != nil {
464+ return false , err
465+ }
466+
467+ for _ , member := range members {
468+ if member .UserId == botUserID {
469+ continue
470+ }
471+
472+ connection , err := p .userStore .LoadConnection (instanceID , types .ID (member .UserId ))
473+ if err != nil {
474+ if errors .Cause (err ) != kvstore .ErrNotFound {
475+ return false , err
476+ }
477+ continue
478+ }
479+
480+ // A missing connection can also read back as an empty one rather than an error.
481+ if connection .JiraAccountID () != "" {
482+ return true , nil
483+ }
484+ }
485+
486+ return false , nil
487+ }
488+
489+ // subscriptionIDsForChannel returns the IDs of subscriptions targeting channelID.
490+ // ByID is scanned directly because the IDByChannelID index can drift from it, e.g.
491+ // for data written before the index existed.
492+ func subscriptionIDsForChannel (subs * ChannelSubscriptions , channelID string ) []string {
493+ var subIDs []string
494+ for id , sub := range subs .ByID {
495+ if sub .ChannelID == channelID {
496+ subIDs = append (subIDs , id )
497+ }
498+ }
499+ return subIDs
500+ }
501+
447502func (p * Plugin ) removeSubscriptionsForChannel (instanceID types.ID , channelID string ) error {
448503 subs , err := p .getSubscriptions (instanceID )
449504 if err != nil {
450505 return err
451506 }
452507
453- if subs .Channel . IDByChannelID [ channelID ]. Len ( ) == 0 {
508+ if len ( subscriptionIDsForChannel ( subs .Channel , channelID ) ) == 0 {
454509 return nil
455510 }
456511
@@ -461,8 +516,7 @@ func (p *Plugin) removeSubscriptionsForChannel(instanceID types.ID, channelID st
461516 return nil , err
462517 }
463518
464- subIDs := subs .Channel .IDByChannelID [channelID ]
465- for _ , subID := range subIDs .Elems () {
519+ for _ , subID := range subscriptionIDsForChannel (subs .Channel , channelID ) {
466520 if sub , ok := subs .Channel .ByID [subID ]; ok {
467521 subs .Channel .remove (& sub )
468522 }
@@ -477,22 +531,62 @@ func (p *Plugin) removeSubscriptionsForChannel(instanceID types.ID, channelID st
477531 })
478532}
479533
534+ // cleanupDMSubscriptionsOnDisconnect removes channel subscriptions targeting any DM or
535+ // GM the disconnecting user belongs to, once no member of that channel remains connected
536+ // to instanceID. Must be called after the user's connection has been deleted.
480537func (p * Plugin ) cleanupDMSubscriptionsOnDisconnect (instanceID types.ID , mattermostUserID string ) {
481- conf := p .getConfig ()
482- dmChannel , err := p .client .Channel .GetDirect (mattermostUserID , conf .botUserID )
538+ subs , err := p .getSubscriptions (instanceID )
483539 if err != nil {
484- p .client .Log .Warn ("Failed to get DM channel for subscription cleanup on disconnect" ,
540+ p .client .Log .Warn ("Failed to load subscriptions for DM/GM cleanup on disconnect" ,
485541 "mattermostUserID" , mattermostUserID ,
486542 "instanceID" , string (instanceID ),
487543 "error" , err .Error ())
488544 return
489545 }
490546
491- if err := p .removeSubscriptionsForChannel (instanceID , dmChannel .Id ); err != nil {
492- p .client .Log .Warn ("Failed to clean up DM subscriptions on disconnect" ,
493- "mattermostUserID" , mattermostUserID ,
494- "instanceID" , string (instanceID ),
495- "error" , err .Error ())
547+ channelIDs := map [string ]bool {}
548+ for _ , sub := range subs .Channel .ByID {
549+ channelIDs [sub .ChannelID ] = true
550+ }
551+
552+ for channelID := range channelIDs {
553+ channel , err := p .client .Channel .Get (channelID )
554+ if err != nil {
555+ p .client .Log .Warn ("Failed to get channel for DM/GM subscription cleanup on disconnect" ,
556+ "channelID" , channelID ,
557+ "instanceID" , string (instanceID ),
558+ "error" , err .Error ())
559+ continue
560+ }
561+
562+ if ! isDirectOrGroupChannel (channel ) {
563+ continue
564+ }
565+
566+ if _ , err := p .client .Channel .GetMember (channelID , mattermostUserID ); err != nil {
567+ // The disconnecting user isn't a member of this DM/GM.
568+ continue
569+ }
570+
571+ hasConnectedMember , err := p .channelHasConnectedMember (instanceID , channel )
572+ if err != nil {
573+ p .client .Log .Warn ("Failed to check for connected members during DM/GM subscription cleanup" ,
574+ "channelID" , channelID ,
575+ "instanceID" , string (instanceID ),
576+ "error" , err .Error ())
577+ continue
578+ }
579+ if hasConnectedMember {
580+ continue
581+ }
582+
583+ if err := p .removeSubscriptionsForChannel (instanceID , channelID ); err != nil {
584+ p .client .Log .Warn ("Failed to clean up DM/GM subscriptions on disconnect" ,
585+ "mattermostUserID" , mattermostUserID ,
586+ "channelID" , channelID ,
587+ "instanceID" , string (instanceID ),
588+ "error" , err .Error ())
589+ }
496590 }
497591}
498592
0 commit comments