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  }