@@ -6,6 +6,7 @@ package plugin
66import (
77 "context"
88 "fmt"
9+ "net/http"
910 "strings"
1011 "unicode"
1112
@@ -41,6 +42,8 @@ const (
4142 PerPageValue = 50
4243)
4344
45+ var ErrNotFound = errors .New ("github user not found" )
46+
4447const DefaultRepoKey string = "%s_%s-default-repo"
4548
4649var validFeatures = map [string ]bool {
@@ -187,6 +190,36 @@ func contains(s []string, e string) bool {
187190 return false
188191}
189192
193+ func (p * Plugin ) isValidGitHubUsername (username string , userInfo * GitHubUserInfo ) (bool , error ) {
194+ githubClient := p .githubConnectUser (context .Background (), userInfo )
195+
196+ if cErr := p .useGitHubClient (userInfo , func (userInfo * GitHubUserInfo , token * oauth2.Token ) error {
197+ ghUser , _ , err := githubClient .Users .Get (context .Background (), username )
198+ if err != nil {
199+ if gErr , ok := err .(* github.ErrorResponse ); ok && gErr .Response .StatusCode == http .StatusNotFound {
200+ return ErrNotFound
201+ }
202+
203+ return err
204+ }
205+
206+ if ghUser == nil {
207+ return ErrNotFound
208+ }
209+
210+ return nil
211+ }); cErr != nil {
212+ if errors .Is (cErr , ErrNotFound ) {
213+ return false , nil
214+ }
215+
216+ p .client .Log .Warn ("Failed to fetch user" , "error" , cErr .Error ())
217+ return false , errors .New ("Failed to fetch user" )
218+ }
219+
220+ return true , nil
221+ }
222+
190223func (p * Plugin ) handleMuteAdd (_ * model.CommandArgs , username string , userInfo * GitHubUserInfo ) string {
191224 mutedUsernames , err := p .getMutedUsernames (userInfo )
192225 if err != nil {
@@ -198,7 +231,12 @@ func (p *Plugin) handleMuteAdd(_ *model.CommandArgs, username string, userInfo *
198231 return username + " is already muted"
199232 }
200233
201- if strings .Contains (username , "," ) {
234+ isValidUsername , err := p .isValidGitHubUsername (username , userInfo )
235+ if err != nil {
236+ return "Error occurred validating username"
237+ }
238+
239+ if strings .Contains (username , "," ) || ! isValidUsername {
202240 return "Invalid username provided"
203241 }
204242
@@ -226,7 +264,10 @@ func (p *Plugin) handleUnmute(_ *model.CommandArgs, username string, userInfo *G
226264 }
227265
228266 userToMute := []string {username }
229- newMutedList := arrayDifference (mutedUsernames , userToMute )
267+ newMutedList , removed := arrayDifference (mutedUsernames , userToMute )
268+ if ! removed {
269+ return username + " is not muted"
270+ }
230271
231272 _ , err = p .store .Set (userInfo .UserID + "-muted-users" , []byte (strings .Join (newMutedList , "," )))
232273 if err != nil {
@@ -237,7 +278,17 @@ func (p *Plugin) handleUnmute(_ *model.CommandArgs, username string, userInfo *G
237278}
238279
239280func (p * Plugin ) handleUnmuteAll (_ * model.CommandArgs , userInfo * GitHubUserInfo ) string {
240- _ , err := p .store .Set (userInfo .UserID + "-muted-users" , []byte ("" ))
281+ mutedUsernames , err := p .getMutedUsernames (userInfo )
282+ if err != nil {
283+ p .client .Log .Error ("error occurred getting muted users." , "UserID" , userInfo .UserID , "Error" , err )
284+ return "An error occurred getting muted users. Please try again later"
285+ }
286+
287+ if len (mutedUsernames ) == 0 {
288+ return "You have no muted users"
289+ }
290+
291+ _ , err = p .store .Set (userInfo .UserID + "-muted-users" , []byte ("" ))
241292 if err != nil {
242293 return "Error occurred unmuting users"
243294 }
@@ -273,18 +324,22 @@ func (p *Plugin) handleMuteCommand(_ *plugin.Context, args *model.CommandArgs, p
273324}
274325
275326// Returns the elements in a, that are not in b
276- func arrayDifference (a , b []string ) []string {
327+ func arrayDifference (a , b []string ) ( []string , bool ) {
277328 mb := make (map [string ]struct {}, len (b ))
278329 for _ , x := range b {
279330 mb [x ] = struct {}{}
280331 }
332+
281333 var diff []string
334+ removed := false
282335 for _ , x := range a {
283336 if _ , found := mb [x ]; ! found {
284337 diff = append (diff , x )
338+ } else {
339+ removed = true
285340 }
286341 }
287- return diff
342+ return diff , removed
288343}
289344
290345func (p * Plugin ) handleSubscribe (c * plugin.Context , args * model.CommandArgs , parameters []string , userInfo * GitHubUserInfo ) string {
@@ -599,8 +654,12 @@ func (p *Plugin) handleUnsubscribe(_ *plugin.Context, args *model.CommandArgs, p
599654
600655 owner = strings .ToLower (owner )
601656 repo = strings .ToLower (repo )
602- if err := p .Unsubscribe (args .ChannelId , repo , owner ); err != nil {
603- p .client .Log .Warn ("Failed to unsubscribe" , "repo" , repo , "error" , err .Error ())
657+ if sErr := p .Unsubscribe (args .ChannelId , repo , owner ); sErr != nil {
658+ if sErr .Code == SubscriptionNotFound {
659+ return sErr .Error .Error ()
660+ }
661+
662+ p .client .Log .Warn ("Failed to unsubscribe" , "repo" , repo , "error" , sErr .Error .Error ())
604663 return "Encountered an error trying to unsubscribe. Please try again."
605664 }
606665
@@ -798,8 +857,8 @@ func (p *Plugin) handleSetDefaultRepo(args *model.CommandArgs, parameters []stri
798857 owner = strings .ToLower (owner )
799858 repo = strings .ToLower (repo )
800859
801- if config .GitHubOrg != "" && strings . ToLower (config .GitHubOrg ) != owner {
802- return fmt .Sprintf ("Repository is not part of the locked Github organization. Locked Github organization : %s" , config .GitHubOrg )
860+ if config .GitHubOrg != "" && ! p . isOrgInLockedOrgs (config .GitHubOrg , owner ) {
861+ return fmt .Sprintf ("Repository is not part of the locked Github organization. Locked Github organizations : %s" , config .GitHubOrg )
803862 }
804863
805864 ctx := context .Background ()
@@ -866,6 +925,21 @@ func (p *Plugin) handleUnSetDefaultRepo(args *model.CommandArgs, userInfo *GitHu
866925 return "The default repository has been unset successfully"
867926}
868927
928+ func (p * Plugin ) isOrgInLockedOrgs (configuredOrgs , owner string ) bool {
929+ if configuredOrgs == "" {
930+ return true
931+ }
932+
933+ orgs := strings .Split (configuredOrgs , "," )
934+ for _ , org := range orgs {
935+ if strings .EqualFold (strings .TrimSpace (org ), strings .TrimSpace (owner )) {
936+ return true
937+ }
938+ }
939+
940+ return false
941+ }
942+
869943func (p * Plugin ) handleSetup (_ * plugin.Context , args * model.CommandArgs , parameters []string ) string {
870944 userID := args .UserId
871945 isSysAdmin , err := p .isAuthorizedSysAdmin (userID )
@@ -944,6 +1018,14 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo
9441018 return & model.CommandResponse {}, nil
9451019 }
9461020
1021+ if action == "help" {
1022+ message := p .handleHelp (c , args , parameters , nil )
1023+ if message != "" {
1024+ p .postCommandResponse (args , message )
1025+ }
1026+ return & model.CommandResponse {}, nil
1027+ }
1028+
9471029 config := p .getConfiguration ()
9481030
9491031 if validationErr := config .IsValid (); validationErr != nil {
@@ -1032,6 +1114,9 @@ func getAutocompleteData(config *Configuration) *model.AutocompleteData {
10321114 about := command .BuildInfoAutocomplete ("about" )
10331115 github .AddCommand (about )
10341116
1117+ help := model .NewAutocompleteData ("help" , "" , "Display Slash Command help text" )
1118+ github .AddCommand (help )
1119+
10351120 return github
10361121 }
10371122
0 commit comments