github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/cmd/mattermost/commands/command.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package commands 5 6 import ( 7 "errors" 8 "strings" 9 10 "fmt" 11 12 "github.com/mattermost/mattermost-server/v5/app" 13 "github.com/mattermost/mattermost-server/v5/audit" 14 "github.com/mattermost/mattermost-server/v5/model" 15 "github.com/spf13/cobra" 16 ) 17 18 var CommandCmd = &cobra.Command{ 19 Use: "command", 20 Short: "Management of slash commands", 21 } 22 23 var CommandCreateCmd = &cobra.Command{ 24 Use: "create [team]", 25 Short: "Create a custom slash command", 26 Long: `Create a custom slash command for the specified team.`, 27 Args: cobra.MinimumNArgs(1), 28 Example: ` command create myteam --title MyCommand --description "My Command Description" --trigger-word mycommand --url http://localhost:8000/my-slash-handler --creator myusername --response-username my-bot-username --icon http://localhost:8000/my-slash-handler-bot-icon.png --autocomplete --post`, 29 RunE: createCommandCmdF, 30 } 31 32 var CommandShowCmd = &cobra.Command{ 33 Use: "show", 34 Short: "Show a custom slash command", 35 Long: `Show a custom slash command. Commands can be specified by command ID.`, 36 Args: cobra.ExactArgs(1), 37 Example: ` command show commandID`, 38 RunE: showCommandCmdF, 39 } 40 41 var CommandMoveCmd = &cobra.Command{ 42 Use: "move", 43 Short: "Move a slash command to a different team", 44 Long: `Move a slash command to a different team. Commands can be specified by [team]:[command-trigger-word]. ie. myteam:trigger or by command ID.`, 45 Example: ` command move newteam oldteam:command`, 46 RunE: moveCommandCmdF, 47 } 48 49 var CommandListCmd = &cobra.Command{ 50 Use: "list", 51 Short: "List all commands on specified teams.", 52 Long: `List all commands on specified teams.`, 53 Example: ` command list myteam`, 54 RunE: listCommandCmdF, 55 } 56 57 var CommandDeleteCmd = &cobra.Command{ 58 Use: "delete", 59 Short: "Delete a slash command", 60 Long: `Delete a slash command. Commands can be specified by command ID.`, 61 Example: ` command delete commandID`, 62 Args: cobra.ExactArgs(1), 63 RunE: deleteCommandCmdF, 64 } 65 66 var CommandModifyCmd = &cobra.Command{ 67 Use: "modify", 68 Short: "Modify a slash command", 69 Long: `Modify a slash command. Commands can be specified by command ID.`, 70 Example: ` command modify commandID`, 71 Args: cobra.MinimumNArgs(1), 72 RunE: modifyCommandCmdF, 73 } 74 75 func init() { 76 CommandCreateCmd.Flags().String("title", "", "Command Title") 77 CommandCreateCmd.Flags().String("description", "", "Command Description") 78 CommandCreateCmd.Flags().String("trigger-word", "", "Command Trigger Word (required)") 79 CommandCreateCmd.MarkFlagRequired("trigger-word") 80 CommandCreateCmd.Flags().String("url", "", "Command Callback URL (required)") 81 CommandCreateCmd.MarkFlagRequired("url") 82 CommandCreateCmd.Flags().String("creator", "", "Command Creator's Username (required)") 83 CommandCreateCmd.MarkFlagRequired("creator") 84 CommandCreateCmd.Flags().String("response-username", "", "Command Response Username") 85 CommandCreateCmd.Flags().String("icon", "", "Command Icon URL") 86 CommandCreateCmd.Flags().Bool("autocomplete", false, "Show Command in autocomplete list") 87 CommandCreateCmd.Flags().String("autocompleteDesc", "", "Short Command Description for autocomplete list") 88 CommandCreateCmd.Flags().String("autocompleteHint", "", "Command Arguments displayed as help in autocomplete list") 89 CommandCreateCmd.Flags().Bool("post", false, "Use POST method for Callback URL") 90 91 CommandModifyCmd.Flags().String("title", "", "Command Title") 92 CommandModifyCmd.Flags().String("description", "", "Command Description") 93 CommandModifyCmd.Flags().String("trigger-word", "", "Command Trigger Word") 94 CommandModifyCmd.Flags().String("url", "", "Command Callback URL") 95 CommandModifyCmd.Flags().String("creator", "", "Command Creator's Username") 96 CommandModifyCmd.Flags().String("response-username", "", "Command Response Username") 97 CommandModifyCmd.Flags().String("icon", "", "Command Icon URL") 98 CommandModifyCmd.Flags().Bool("autocomplete", false, "Show Command in autocomplete list") 99 CommandModifyCmd.Flags().String("autocompleteDesc", "", "Short Command Description for autocomplete list") 100 CommandModifyCmd.Flags().String("autocompleteHint", "", "Command Arguments displayed as help in autocomplete list") 101 CommandModifyCmd.Flags().Bool("post", false, "Use POST method for Callback URL") 102 103 CommandCmd.AddCommand( 104 CommandCreateCmd, 105 CommandShowCmd, 106 CommandMoveCmd, 107 CommandListCmd, 108 CommandDeleteCmd, 109 CommandModifyCmd, 110 ) 111 RootCmd.AddCommand(CommandCmd) 112 } 113 114 func createCommandCmdF(command *cobra.Command, args []string) error { 115 a, err := InitDBCommandContextCobra(command) 116 if err != nil { 117 return err 118 } 119 defer a.Srv().Shutdown() 120 121 team := getTeamFromTeamArg(a, args[0]) 122 if team == nil { 123 return errors.New("unable to find team '" + args[0] + "'") 124 } 125 126 // get the creator 127 creator, _ := command.Flags().GetString("creator") 128 user := getUserFromUserArg(a, creator) 129 if user == nil { 130 return errors.New("unable to find user '" + creator + "'") 131 } 132 133 // check if creator has permission to create slash commands 134 if !a.HasPermissionToTeam(user.Id, team.Id, model.PERMISSION_MANAGE_SLASH_COMMANDS) { 135 return errors.New("the creator must be a user who has permissions to manage slash commands") 136 } 137 138 title, _ := command.Flags().GetString("title") 139 description, _ := command.Flags().GetString("description") 140 trigger, _ := command.Flags().GetString("trigger-word") 141 142 if strings.HasPrefix(trigger, "/") { 143 return errors.New("a trigger word cannot begin with a /") 144 } 145 if strings.Contains(trigger, " ") { 146 return errors.New("a trigger word must not contain spaces") 147 } 148 149 url, _ := command.Flags().GetString("url") 150 responseUsername, _ := command.Flags().GetString("response-username") 151 icon, _ := command.Flags().GetString("icon") 152 autocomplete, _ := command.Flags().GetBool("autocomplete") 153 autocompleteDesc, _ := command.Flags().GetString("autocompleteDesc") 154 autocompleteHint, _ := command.Flags().GetString("autocompleteHint") 155 post, errp := command.Flags().GetBool("post") 156 method := "P" 157 if errp != nil || !post { 158 method = "G" 159 } 160 161 newCommand := &model.Command{ 162 CreatorId: user.Id, 163 TeamId: team.Id, 164 Trigger: trigger, 165 Method: method, 166 Username: responseUsername, 167 IconURL: icon, 168 AutoComplete: autocomplete, 169 AutoCompleteDesc: autocompleteDesc, 170 AutoCompleteHint: autocompleteHint, 171 DisplayName: title, 172 Description: description, 173 URL: url, 174 } 175 176 createdCommand, errCreate := a.CreateCommand(newCommand) 177 if errCreate != nil { 178 return errors.New("unable to create command '" + newCommand.DisplayName + "'. " + errCreate.Error()) 179 } 180 CommandPrettyPrintln("created command '" + newCommand.DisplayName + "'") 181 182 auditRec := a.MakeAuditRecord("createCommand", audit.Success) 183 auditRec.AddMeta("user", user) 184 auditRec.AddMeta("command", createdCommand) 185 a.LogAuditRec(auditRec, nil) 186 return nil 187 } 188 189 func showCommandCmdF(command *cobra.Command, args []string) error { 190 a, err := InitDBCommandContextCobra(command) 191 if err != nil { 192 return err 193 } 194 defer a.Srv().Shutdown() 195 196 slashCommand := getCommandFromCommandArg(a, args[0]) 197 if slashCommand == nil { 198 command.SilenceUsage = true 199 return errors.New("Unable to find command '" + args[0] + "'") 200 } 201 // pretty print 202 fmt.Printf("%s", prettyPrintStruct(*slashCommand)) 203 204 return nil 205 } 206 207 func moveCommandCmdF(command *cobra.Command, args []string) error { 208 a, err := InitDBCommandContextCobra(command) 209 if err != nil { 210 return err 211 } 212 defer a.Srv().Shutdown() 213 214 if len(args) < 2 { 215 return errors.New("Enter the destination team and at least one command to move.") 216 } 217 218 team := getTeamFromTeamArg(a, args[0]) 219 if team == nil { 220 return errors.New("Unable to find destination team '" + args[0] + "'") 221 } 222 223 commands := getCommandsFromCommandArgs(a, args[1:]) 224 for i, command := range commands { 225 if command == nil { 226 CommandPrintErrorln("Unable to find command '" + args[i+1] + "'") 227 continue 228 } 229 if err := moveCommand(a, team, command); err != nil { 230 CommandPrintErrorln("Unable to move command '" + command.DisplayName + "' error: " + err.Error()) 231 } else { 232 CommandPrettyPrintln("Moved command '" + command.DisplayName + "'") 233 234 auditRec := a.MakeAuditRecord("moveCommand", audit.Success) 235 auditRec.AddMeta("team", team) 236 auditRec.AddMeta("command", command) 237 a.LogAuditRec(auditRec, nil) 238 } 239 } 240 return nil 241 } 242 243 func moveCommand(a *app.App, team *model.Team, command *model.Command) *model.AppError { 244 return a.MoveCommand(team, command) 245 } 246 247 func listCommandCmdF(command *cobra.Command, args []string) error { 248 a, err := InitDBCommandContextCobra(command) 249 if err != nil { 250 return err 251 } 252 defer a.Srv().Shutdown() 253 254 var teams []*model.Team 255 if len(args) < 1 { 256 teamList, err := a.GetAllTeams() 257 if err != nil { 258 return err 259 } 260 teams = teamList 261 } else { 262 teams = getTeamsFromTeamArgs(a, args) 263 } 264 265 for i, team := range teams { 266 if team == nil { 267 CommandPrintErrorln("Unable to find team '" + args[i] + "'") 268 continue 269 } 270 commands, err := a.Srv().Store.Command().GetByTeam(team.Id) 271 if err != nil { 272 CommandPrintErrorln("Unable to list commands for '" + args[i] + "'") 273 continue 274 } 275 for _, command := range commands { 276 commandListItem := fmt.Sprintf("%s: %s (team: %s)", command.Id, command.DisplayName, team.Name) 277 CommandPrettyPrintln(commandListItem) 278 } 279 } 280 return nil 281 } 282 283 func deleteCommandCmdF(command *cobra.Command, args []string) error { 284 a, err := InitDBCommandContextCobra(command) 285 if err != nil { 286 return err 287 } 288 defer a.Srv().Shutdown() 289 290 slashCommand := getCommandFromCommandArg(a, args[0]) 291 if slashCommand == nil { 292 command.SilenceUsage = true 293 return errors.New("Unable to find command '" + args[0] + "'") 294 } 295 296 if err := a.DeleteCommand(slashCommand.Id); err != nil { 297 command.SilenceUsage = true 298 return errors.New("Unable to delete command '" + slashCommand.Id + "' error: " + err.Error()) 299 } 300 CommandPrettyPrintln("Deleted command '" + slashCommand.Id + "' (" + slashCommand.DisplayName + ")") 301 302 auditRec := a.MakeAuditRecord("deleteCommand", audit.Success) 303 auditRec.AddMeta("command", slashCommand) 304 a.LogAuditRec(auditRec, nil) 305 return nil 306 } 307 308 func modifyCommandCmdF(command *cobra.Command, args []string) (cmdError error) { 309 a, err := InitDBCommandContextCobra(command) 310 if err != nil { 311 return err 312 } 313 defer a.Srv().Shutdown() 314 315 oldCommand := getCommandFromCommandArg(a, args[0]) 316 if oldCommand == nil { 317 command.SilenceUsage = true 318 return errors.New("Unable to find command '" + args[0] + "'") 319 } 320 modifiedCommand := oldCommand 321 322 auditRec := a.MakeAuditRecord("modifyCommand", audit.Fail) 323 defer func() { a.LogAuditRec(auditRec, cmdError) }() 324 auditRec.AddMeta("command", oldCommand) 325 326 // get creator user 327 creator, _ := command.Flags().GetString("creator") 328 if creator != "" { 329 user := getUserFromUserArg(a, creator) 330 if user == nil { 331 return errors.New("unable to find user '" + creator + "'") 332 } 333 334 // check if creator has permission to create slash commands 335 if !a.HasPermissionToTeam(user.Id, modifiedCommand.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { 336 return errors.New("the creator must be a user who has permissions to manage slash commands") 337 } 338 339 modifiedCommand.CreatorId = user.Id 340 } 341 342 title, _ := command.Flags().GetString("title") 343 if title != "" { 344 modifiedCommand.DisplayName = title 345 } 346 347 description, _ := command.Flags().GetString("description") 348 if description != "" { 349 modifiedCommand.Description = description 350 } 351 352 trigger, _ := command.Flags().GetString("trigger-word") 353 if trigger != "" { 354 if strings.HasPrefix(trigger, "/") { 355 return errors.New("a trigger word cannot begin with a /") 356 } 357 if strings.Contains(trigger, " ") { 358 return errors.New("a trigger word must not contain spaces") 359 } 360 modifiedCommand.Trigger = trigger 361 } 362 363 url, _ := command.Flags().GetString("url") 364 if url != "" { 365 modifiedCommand.URL = url 366 } 367 368 responseUsername, _ := command.Flags().GetString("response-username") 369 if responseUsername != "" { 370 modifiedCommand.Username = responseUsername 371 } 372 373 icon, _ := command.Flags().GetString("icon") 374 if icon != "" { 375 modifiedCommand.IconURL = icon 376 } 377 378 autocomplete, _ := command.Flags().GetBool("autocomplete") 379 modifiedCommand.AutoComplete = autocomplete 380 381 autocompleteDesc, _ := command.Flags().GetString("autocompleteDesc") 382 if autocompleteDesc != "" { 383 modifiedCommand.AutoCompleteDesc = autocompleteDesc 384 } 385 386 autocompleteHint, _ := command.Flags().GetString("autocompleteHint") 387 if autocompleteHint != "" { 388 modifiedCommand.AutoCompleteHint = autocompleteHint 389 } 390 391 post, err := command.Flags().GetBool("post") 392 method := "P" 393 if err != nil || !post { 394 method = "G" 395 } 396 modifiedCommand.Method = method 397 398 updatedCommand, errUpdated := a.UpdateCommand(oldCommand, modifiedCommand) 399 if errUpdated != nil { 400 return errors.New("unable to modify command '" + modifiedCommand.DisplayName + "'. " + errUpdated.Error()) 401 } 402 CommandPrettyPrintln("modified command '" + modifiedCommand.DisplayName + "'") 403 404 auditRec.Success() 405 auditRec.AddMeta("update", updatedCommand) 406 407 return nil 408 }