github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/cmd/hkserver/commands/webhook.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  	"fmt"
     8  	"net/http"
     9  	"strings"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/spf13/cobra"
    13  
    14  	"github.com/masterhung0112/hk_server/v5/audit"
    15  	"github.com/masterhung0112/hk_server/v5/model"
    16  	"github.com/masterhung0112/hk_server/v5/store"
    17  )
    18  
    19  var WebhookCmd = &cobra.Command{
    20  	Use:   "webhook",
    21  	Short: "Management of webhooks",
    22  }
    23  
    24  var WebhookListCmd = &cobra.Command{
    25  	Use:     "list",
    26  	Short:   "List webhooks",
    27  	Long:    "list all webhooks",
    28  	Example: "  webhook list myteam",
    29  	RunE:    listWebhookCmdF,
    30  }
    31  
    32  var WebhookShowCmd = &cobra.Command{
    33  	Use:     "show [webhookId]",
    34  	Short:   "Show a webhook",
    35  	Long:    "Show the webhook specified by [webhookId]",
    36  	Args:    cobra.ExactArgs(1),
    37  	Example: "  webhook show w16zb5tu3n1zkqo18goqry1je",
    38  	RunE:    showWebhookCmdF,
    39  }
    40  
    41  var WebhookCreateIncomingCmd = &cobra.Command{
    42  	Use:     "create-incoming",
    43  	Short:   "Create incoming webhook",
    44  	Long:    "create incoming webhook which allows external posting of messages to specific channel",
    45  	Example: "  webhook create-incoming --channel [channelID] --user [userID] --display-name [displayName] --description [webhookDescription] --lock-to-channel --icon [iconURL]",
    46  	RunE:    createIncomingWebhookCmdF,
    47  }
    48  
    49  var WebhookModifyIncomingCmd = &cobra.Command{
    50  	Use:     "modify-incoming",
    51  	Short:   "Modify incoming webhook",
    52  	Long:    "Modify existing incoming webhook by changing its title, description, channel or icon url",
    53  	Example: "  webhook modify-incoming [webhookID] --channel [channelID] --display-name [displayName] --description [webhookDescription] --lock-to-channel --icon [iconURL]",
    54  	RunE:    modifyIncomingWebhookCmdF,
    55  }
    56  
    57  var WebhookCreateOutgoingCmd = &cobra.Command{
    58  	Use:   "create-outgoing",
    59  	Short: "Create outgoing webhook",
    60  	Long:  "create outgoing webhook which allows external posting of messages from a specific channel",
    61  	Example: `  webhook create-outgoing --team myteam --user myusername --display-name mywebhook --trigger-word "build" --trigger-word "test" --url http://localhost:8000/my-webhook-handler
    62  	webhook create-outgoing --team myteam --channel mychannel --user myusername --display-name mywebhook --description "My cool webhook" --trigger-when start --trigger-word build --trigger-word test --icon http://localhost:8000/my-slash-handler-bot-icon.png --url http://localhost:8000/my-webhook-handler --content-type "application/json"`,
    63  	RunE: createOutgoingWebhookCmdF,
    64  }
    65  
    66  var WebhookModifyOutgoingCmd = &cobra.Command{
    67  	Use:     "modify-outgoing",
    68  	Short:   "Modify outgoing webhook",
    69  	Long:    "Modify existing outgoing webhook by changing its title, description, channel, icon, url, content-type, and triggers",
    70  	Example: `  webhook modify-outgoing [webhookId] --channel [channelId] --display-name [displayName] --description "New webhook description" --icon http://localhost:8000/my-slash-handler-bot-icon.png --url http://localhost:8000/my-webhook-handler --content-type "application/json" --trigger-word test --trigger-when start`,
    71  	RunE:    modifyOutgoingWebhookCmdF,
    72  }
    73  
    74  var WebhookDeleteCmd = &cobra.Command{
    75  	Use:     "delete",
    76  	Short:   "Delete webhooks",
    77  	Long:    "Delete webhook with given id",
    78  	Example: "  webhook delete [webhookID]",
    79  	RunE:    deleteWebhookCmdF,
    80  }
    81  
    82  var WebhookMoveOutgoingCmd = &cobra.Command{
    83  	Use:     "move-outgoing",
    84  	Short:   "Move outgoing webhook",
    85  	Long:    "Move outgoing webhook with an id",
    86  	Example: "  webhook move-outgoing newteam oldteam:webhook-id --channel new-default-channel",
    87  	Args:    cobra.ExactArgs(2),
    88  	RunE:    moveOutgoingWebhookCmd,
    89  }
    90  
    91  func listWebhookCmdF(command *cobra.Command, args []string) error {
    92  	app, err := InitDBCommandContextCobra(command)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	defer app.Srv().Shutdown()
    97  
    98  	var teams []*model.Team
    99  	if len(args) < 1 {
   100  		var getErr *model.AppError
   101  		// If no team is specified, list all teams
   102  		teams, getErr = app.GetAllTeams()
   103  		if getErr != nil {
   104  			return getErr
   105  		}
   106  	} else {
   107  		teams = getTeamsFromTeamArgs(app, args)
   108  	}
   109  
   110  	for i, team := range teams {
   111  		if team == nil {
   112  			CommandPrintErrorln("Unable to find team '" + args[i] + "'")
   113  			continue
   114  		}
   115  
   116  		// Fetch all hooks with a very large limit so we get them all.
   117  		incomingResult := make(chan store.StoreResult, 1)
   118  		go func() {
   119  			incomingHooks, err := app.Srv().Store.Webhook().GetIncomingByTeam(team.Id, 0, 100000000)
   120  			incomingResult <- store.StoreResult{Data: incomingHooks, NErr: err}
   121  			close(incomingResult)
   122  		}()
   123  		outgoingResult := make(chan store.StoreResult, 1)
   124  		go func() {
   125  			outgoingHooks, err := app.Srv().Store.Webhook().GetOutgoingByTeam(team.Id, 0, 100000000)
   126  			outgoingResult <- store.StoreResult{Data: outgoingHooks, NErr: err}
   127  			close(outgoingResult)
   128  		}()
   129  
   130  		if result := <-incomingResult; result.NErr == nil {
   131  			CommandPrettyPrintln(fmt.Sprintf("Incoming webhooks for %s (%s):", team.DisplayName, team.Name))
   132  			hooks := result.Data.([]*model.IncomingWebhook)
   133  			for _, hook := range hooks {
   134  				CommandPrettyPrintln("\t" + hook.DisplayName + " (" + hook.Id + ")")
   135  			}
   136  		} else {
   137  			CommandPrintErrorln("Unable to list incoming webhooks for '" + args[i] + "'")
   138  		}
   139  
   140  		if result := <-outgoingResult; result.NErr == nil {
   141  			hooks := result.Data.([]*model.OutgoingWebhook)
   142  			CommandPrettyPrintln(fmt.Sprintf("Outgoing webhooks for %s (%s):", team.DisplayName, team.Name))
   143  			for _, hook := range hooks {
   144  				CommandPrettyPrintln("\t" + hook.DisplayName + " (" + hook.Id + ")")
   145  			}
   146  		} else {
   147  			CommandPrintErrorln("Unable to list outgoing webhooks for '" + args[i] + "'")
   148  		}
   149  	}
   150  	return nil
   151  }
   152  
   153  func createIncomingWebhookCmdF(command *cobra.Command, args []string) error {
   154  	app, err := InitDBCommandContextCobra(command)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	defer app.Srv().Shutdown()
   159  
   160  	channelArg, errChannel := command.Flags().GetString("channel")
   161  	if errChannel != nil || channelArg == "" {
   162  		return errors.New("Channel is required")
   163  	}
   164  	channel := getChannelFromChannelArg(app, channelArg)
   165  	if channel == nil {
   166  		return errors.New("Unable to find channel '" + channelArg + "'")
   167  	}
   168  
   169  	userArg, errUser := command.Flags().GetString("user")
   170  	if errUser != nil || userArg == "" {
   171  		return errors.New("User is required")
   172  	}
   173  	user := getUserFromUserArg(app, userArg)
   174  	if user == nil {
   175  		return errors.New("Unable to find user '" + userArg + "'")
   176  	}
   177  
   178  	displayName, _ := command.Flags().GetString("display-name")
   179  	description, _ := command.Flags().GetString("description")
   180  	iconURL, _ := command.Flags().GetString("icon")
   181  	channelLocked, _ := command.Flags().GetBool("lock-to-channel")
   182  
   183  	incomingWebhook := &model.IncomingWebhook{
   184  		ChannelId:     channel.Id,
   185  		DisplayName:   displayName,
   186  		Description:   description,
   187  		IconURL:       iconURL,
   188  		ChannelLocked: channelLocked,
   189  	}
   190  
   191  	createdIncoming, errIncomingWebhook := app.CreateIncomingWebhookForChannel(user.Id, channel, incomingWebhook)
   192  	if errIncomingWebhook != nil {
   193  		return errIncomingWebhook
   194  	}
   195  
   196  	CommandPrettyPrintln("Id: " + createdIncoming.Id)
   197  	CommandPrettyPrintln("Display Name: " + createdIncoming.DisplayName)
   198  
   199  	auditRec := app.MakeAuditRecord("createIncomingWebhook", audit.Success)
   200  	auditRec.AddMeta("user", user)
   201  	auditRec.AddMeta("channel", channel)
   202  	auditRec.AddMeta("hook", createdIncoming)
   203  	app.LogAuditRec(auditRec, nil)
   204  
   205  	return nil
   206  }
   207  
   208  func modifyIncomingWebhookCmdF(command *cobra.Command, args []string) (cmdError error) {
   209  	app, err := InitDBCommandContextCobra(command)
   210  	if err != nil {
   211  		return err
   212  	}
   213  	defer app.Srv().Shutdown()
   214  
   215  	if len(args) < 1 {
   216  		return errors.New("WebhookID is not specified")
   217  	}
   218  
   219  	webhookArg := args[0]
   220  	oldHook, getErr := app.GetIncomingWebhook(webhookArg)
   221  	if getErr != nil {
   222  		return errors.New("Unable to find webhook '" + webhookArg + "'")
   223  	}
   224  
   225  	updatedHook := oldHook
   226  
   227  	auditRec := app.MakeAuditRecord("createIncomingWebhook", audit.Fail)
   228  	defer func() { app.LogAuditRec(auditRec, cmdError) }()
   229  	auditRec.AddMeta("hook", oldHook)
   230  
   231  	channelArg, _ := command.Flags().GetString("channel")
   232  	if channelArg != "" {
   233  		channel := getChannelFromChannelArg(app, channelArg)
   234  		if channel == nil {
   235  			return errors.New("Unable to find channel '" + channelArg + "'")
   236  		}
   237  		updatedHook.ChannelId = channel.Id
   238  	}
   239  
   240  	displayName, _ := command.Flags().GetString("display-name")
   241  	if displayName != "" {
   242  		updatedHook.DisplayName = displayName
   243  	}
   244  	description, _ := command.Flags().GetString("description")
   245  	if description != "" {
   246  		updatedHook.Description = description
   247  	}
   248  	iconUrl, _ := command.Flags().GetString("icon")
   249  	if iconUrl != "" {
   250  		updatedHook.IconURL = iconUrl
   251  	}
   252  	channelLocked, _ := command.Flags().GetBool("lock-to-channel")
   253  	updatedHook.ChannelLocked = channelLocked
   254  
   255  	updatedIncomingHook, errUpdated := app.UpdateIncomingWebhook(oldHook, updatedHook)
   256  	if errUpdated != nil {
   257  		return errUpdated
   258  	}
   259  
   260  	auditRec.Success()
   261  	auditRec.AddMeta("update", updatedIncomingHook)
   262  
   263  	return nil
   264  }
   265  
   266  func createOutgoingWebhookCmdF(command *cobra.Command, args []string) error {
   267  	app, err := InitDBCommandContextCobra(command)
   268  	if err != nil {
   269  		return err
   270  	}
   271  	defer app.Srv().Shutdown()
   272  
   273  	teamArg, errTeam := command.Flags().GetString("team")
   274  	if errTeam != nil || teamArg == "" {
   275  		return errors.New("Team is required")
   276  	}
   277  	team := getTeamFromTeamArg(app, teamArg)
   278  	if team == nil {
   279  		return errors.New("Unable to find team: " + teamArg)
   280  	}
   281  
   282  	userArg, errUser := command.Flags().GetString("user")
   283  	if errUser != nil || userArg == "" {
   284  		return errors.New("User is required")
   285  	}
   286  	user := getUserFromUserArg(app, userArg)
   287  	if user == nil {
   288  		return errors.New("Unable to find user: " + userArg)
   289  	}
   290  
   291  	displayName, errName := command.Flags().GetString("display-name")
   292  	if errName != nil || displayName == "" {
   293  		return errors.New("Display name is required")
   294  	}
   295  
   296  	triggerWords, errWords := command.Flags().GetStringArray("trigger-word")
   297  	if errWords != nil || len(triggerWords) == 0 {
   298  		return errors.New("Trigger word or words required")
   299  	}
   300  
   301  	callbackURLs, errURL := command.Flags().GetStringArray("url")
   302  	if errURL != nil || len(callbackURLs) == 0 {
   303  		return errors.New("Callback URL or URLs required")
   304  	}
   305  
   306  	triggerWhenString, _ := command.Flags().GetString("trigger-when")
   307  	var triggerWhen int
   308  	if triggerWhenString == "exact" {
   309  		triggerWhen = 0
   310  	} else if triggerWhenString == "start" {
   311  		triggerWhen = 1
   312  	} else {
   313  		return errors.New("Invalid trigger when parameter")
   314  	}
   315  	description, _ := command.Flags().GetString("description")
   316  	contentType, _ := command.Flags().GetString("content-type")
   317  	iconURL, _ := command.Flags().GetString("icon")
   318  
   319  	outgoingWebhook := &model.OutgoingWebhook{
   320  		CreatorId:    user.Id,
   321  		Username:     user.Username,
   322  		TeamId:       team.Id,
   323  		TriggerWords: triggerWords,
   324  		TriggerWhen:  triggerWhen,
   325  		CallbackURLs: callbackURLs,
   326  		DisplayName:  displayName,
   327  		Description:  description,
   328  		ContentType:  contentType,
   329  		IconURL:      iconURL,
   330  	}
   331  
   332  	var channel *model.Channel
   333  	channelArg, _ := command.Flags().GetString("channel")
   334  	if channelArg != "" {
   335  		channel = getChannelFromChannelArg(app, channelArg)
   336  		if channel != nil {
   337  			outgoingWebhook.ChannelId = channel.Id
   338  		}
   339  	}
   340  
   341  	createdOutgoing, errOutgoing := app.CreateOutgoingWebhook(outgoingWebhook)
   342  	if errOutgoing != nil {
   343  		return errOutgoing
   344  	}
   345  
   346  	CommandPrettyPrintln("Id: " + createdOutgoing.Id)
   347  	CommandPrettyPrintln("Display Name: " + createdOutgoing.DisplayName)
   348  
   349  	auditRec := app.MakeAuditRecord("createOutgoingWebhook", audit.Success)
   350  	auditRec.AddMeta("user", user)
   351  	auditRec.AddMeta("hook", createdOutgoing)
   352  	if channel != nil {
   353  		auditRec.AddMeta("channel", channel)
   354  	}
   355  	app.LogAuditRec(auditRec, nil)
   356  
   357  	return nil
   358  }
   359  
   360  func modifyOutgoingWebhookCmdF(command *cobra.Command, args []string) error {
   361  	app, err := InitDBCommandContextCobra(command)
   362  	if err != nil {
   363  		return err
   364  	}
   365  	defer app.Srv().Shutdown()
   366  
   367  	if len(args) < 1 {
   368  		return errors.New("WebhookID is not specified")
   369  	}
   370  
   371  	webhookArg := args[0]
   372  	oldHook, appErr := app.GetOutgoingWebhook(webhookArg)
   373  	if appErr != nil {
   374  		return fmt.Errorf("unable to find webhook '%s'", webhookArg)
   375  	}
   376  
   377  	updatedHook := model.OutgoingWebhookFromJson(strings.NewReader(oldHook.ToJson()))
   378  
   379  	channelArg, _ := command.Flags().GetString("channel")
   380  	if channelArg != "" {
   381  		channel := getChannelFromChannelArg(app, channelArg)
   382  		if channel == nil {
   383  			return fmt.Errorf("unable to find channel '%s'", channelArg)
   384  		}
   385  		updatedHook.ChannelId = channel.Id
   386  	}
   387  
   388  	displayName, _ := command.Flags().GetString("display-name")
   389  	if displayName != "" {
   390  		updatedHook.DisplayName = displayName
   391  	}
   392  
   393  	description, _ := command.Flags().GetString("description")
   394  	if description != "" {
   395  		updatedHook.Description = description
   396  	}
   397  
   398  	triggerWords, err := command.Flags().GetStringArray("trigger-word")
   399  	if err != nil {
   400  		return errors.Wrap(err, "invalid trigger-word parameter")
   401  	}
   402  	if len(triggerWords) > 0 {
   403  		updatedHook.TriggerWords = triggerWords
   404  	}
   405  
   406  	triggerWhenString, _ := command.Flags().GetString("trigger-when")
   407  	if triggerWhenString != "" {
   408  		var triggerWhen int
   409  		if triggerWhenString == "exact" {
   410  			triggerWhen = 0
   411  		} else if triggerWhenString == "start" {
   412  			triggerWhen = 1
   413  		} else {
   414  			return errors.New("invalid trigger-when parameter")
   415  		}
   416  		updatedHook.TriggerWhen = triggerWhen
   417  	}
   418  
   419  	iconURL, _ := command.Flags().GetString("icon")
   420  	if iconURL != "" {
   421  		updatedHook.IconURL = iconURL
   422  	}
   423  
   424  	contentType, _ := command.Flags().GetString("content-type")
   425  	if contentType != "" {
   426  		updatedHook.ContentType = contentType
   427  	}
   428  
   429  	callbackURLs, err := command.Flags().GetStringArray("url")
   430  	if err != nil {
   431  		return errors.Wrap(err, "invalid URL parameter")
   432  	}
   433  	if len(callbackURLs) > 0 {
   434  		updatedHook.CallbackURLs = callbackURLs
   435  	}
   436  
   437  	updatedWebhook, appErr := app.UpdateOutgoingWebhook(oldHook, updatedHook)
   438  	if appErr != nil {
   439  		return appErr
   440  	}
   441  
   442  	auditRec := app.MakeAuditRecord("modifyOutgoingWebhook", audit.Success)
   443  	auditRec.AddMeta("hook", oldHook)
   444  	auditRec.AddMeta("update", updatedWebhook)
   445  	app.LogAuditRec(auditRec, nil)
   446  
   447  	return nil
   448  }
   449  
   450  func deleteWebhookCmdF(command *cobra.Command, args []string) error {
   451  	app, err := InitDBCommandContextCobra(command)
   452  	if err != nil {
   453  		return err
   454  	}
   455  	defer app.Srv().Shutdown()
   456  
   457  	if len(args) < 1 {
   458  		return errors.New("WebhookID is not specified")
   459  	}
   460  
   461  	webhookId := args[0]
   462  	errIncomingWebhook := app.DeleteIncomingWebhook(webhookId)
   463  	errOutgoingWebhook := app.DeleteOutgoingWebhook(webhookId)
   464  
   465  	if errIncomingWebhook != nil && errOutgoingWebhook != nil {
   466  		return errors.New("Unable to delete webhook '" + webhookId + "'")
   467  	}
   468  
   469  	auditRec := app.MakeAuditRecord("deleteWebhook", audit.Success)
   470  	auditRec.AddMeta("hook_id", webhookId)
   471  	app.LogAuditRec(auditRec, nil)
   472  
   473  	return nil
   474  }
   475  
   476  func showWebhookCmdF(command *cobra.Command, args []string) error {
   477  	app, err := InitDBCommandContextCobra(command)
   478  	if err != nil {
   479  		return err
   480  	}
   481  	defer app.Srv().Shutdown()
   482  
   483  	webhookId := args[0]
   484  	if incomingWebhook, err := app.GetIncomingWebhook(webhookId); err == nil {
   485  		fmt.Printf("%s", prettyPrintStruct(*incomingWebhook))
   486  		return nil
   487  	}
   488  	if outgoingWebhook, err := app.GetOutgoingWebhook(webhookId); err == nil {
   489  		fmt.Printf("%s", prettyPrintStruct(*outgoingWebhook))
   490  		return nil
   491  	}
   492  
   493  	return errors.New("Webhook with id " + webhookId + " not found")
   494  }
   495  
   496  func moveOutgoingWebhookCmd(command *cobra.Command, args []string) (cmdError error) {
   497  	app, err := InitDBCommandContextCobra(command)
   498  	if err != nil {
   499  		return err
   500  	}
   501  	defer app.Srv().Shutdown()
   502  
   503  	newTeamId := args[0]
   504  	_, teamError := app.GetTeam(newTeamId)
   505  	if teamError != nil {
   506  		return teamError
   507  	}
   508  
   509  	webhookInformation := strings.Split(args[1], ":")
   510  	sourceTeam := webhookInformation[0]
   511  	_, teamErr := app.GetTeam(sourceTeam)
   512  	if teamErr != nil {
   513  		return teamErr
   514  	}
   515  
   516  	webhookId := webhookInformation[1]
   517  	webhook, appError := app.GetOutgoingWebhook(webhookId)
   518  	if appError != nil {
   519  		return appError
   520  	}
   521  
   522  	auditRec := app.MakeAuditRecord("moveOutgoingWebhook", audit.Fail)
   523  	defer func() { app.LogAuditRec(auditRec, cmdError) }()
   524  	auditRec.AddMeta("hook", webhook)
   525  
   526  	channelName, channelErr := command.Flags().GetString("channel")
   527  	if channelErr != nil {
   528  		return channelErr
   529  	}
   530  	channel, getChannelErr := app.GetChannelByName(channelName, newTeamId, false)
   531  
   532  	if webhook.ChannelId != "" {
   533  		if getChannelErr != nil {
   534  			return getChannelErr
   535  		}
   536  		webhook.ChannelId = channel.Id
   537  	} else if channelName != "" {
   538  		webhook.ChannelId = channel.Id
   539  	}
   540  
   541  	deleteErr := app.DeleteOutgoingWebhook(webhook.Id)
   542  	if deleteErr != nil {
   543  		return deleteErr
   544  	}
   545  
   546  	webhook.Id = ""
   547  	webhook.TeamId = newTeamId
   548  
   549  	updatedWebHook, createErr := app.CreateOutgoingWebhook(webhook)
   550  	if createErr != nil {
   551  		return model.NewAppError("moveOutgoingWebhookCmd", "cli.outgoing_webhook.inconsistent_state.app_error", nil, createErr.Error(), http.StatusInternalServerError)
   552  	}
   553  
   554  	auditRec.Success()
   555  	auditRec.AddMeta("update", updatedWebHook)
   556  
   557  	return nil
   558  }
   559  
   560  func init() {
   561  	WebhookCreateIncomingCmd.Flags().String("channel", "", "Channel ID (required)")
   562  	WebhookCreateIncomingCmd.Flags().String("user", "", "User ID (required)")
   563  	WebhookCreateIncomingCmd.Flags().String("display-name", "", "Incoming webhook display name")
   564  	WebhookCreateIncomingCmd.Flags().String("description", "", "Incoming webhook description")
   565  	WebhookCreateIncomingCmd.Flags().String("icon", "", "Icon URL")
   566  	WebhookCreateIncomingCmd.Flags().Bool("lock-to-channel", false, "Lock to channel")
   567  
   568  	WebhookModifyIncomingCmd.Flags().String("channel", "", "Channel ID")
   569  	WebhookModifyIncomingCmd.Flags().String("display-name", "", "Incoming webhook display name")
   570  	WebhookModifyIncomingCmd.Flags().String("description", "", "Incoming webhook description")
   571  	WebhookModifyIncomingCmd.Flags().String("icon", "", "Icon URL")
   572  	WebhookModifyIncomingCmd.Flags().Bool("lock-to-channel", false, "Lock to channel")
   573  
   574  	WebhookCreateOutgoingCmd.Flags().String("team", "", "Team name or ID (required)")
   575  	WebhookCreateOutgoingCmd.Flags().String("channel", "", "Channel name or ID")
   576  	WebhookCreateOutgoingCmd.Flags().String("user", "", "User username, email, or ID (required)")
   577  	WebhookCreateOutgoingCmd.Flags().String("display-name", "", "Outgoing webhook display name (required)")
   578  	WebhookCreateOutgoingCmd.Flags().String("description", "", "Outgoing webhook description")
   579  	WebhookCreateOutgoingCmd.Flags().StringArray("trigger-word", []string{}, "Word to trigger webhook (required)")
   580  	WebhookCreateOutgoingCmd.Flags().String("trigger-when", "exact", "When to trigger webhook (exact: for first word matches a trigger word exactly, start: for first word starts with a trigger word)")
   581  	WebhookCreateOutgoingCmd.Flags().String("icon", "", "Icon URL")
   582  	WebhookCreateOutgoingCmd.Flags().StringArray("url", []string{}, "Callback URL (required)")
   583  	WebhookCreateOutgoingCmd.Flags().String("content-type", "", "Content-type")
   584  
   585  	WebhookModifyOutgoingCmd.Flags().String("channel", "", "Channel name or ID")
   586  	WebhookModifyOutgoingCmd.Flags().String("display-name", "", "Outgoing webhook display name")
   587  	WebhookModifyOutgoingCmd.Flags().String("description", "", "Outgoing webhook description")
   588  	WebhookModifyOutgoingCmd.Flags().StringArray("trigger-word", []string{}, "Word to trigger webhook")
   589  	WebhookModifyOutgoingCmd.Flags().String("trigger-when", "", "When to trigger webhook (exact: for first word matches a trigger word exactly, start: for first word starts with a trigger word)")
   590  	WebhookModifyOutgoingCmd.Flags().String("icon", "", "Icon URL")
   591  	WebhookModifyOutgoingCmd.Flags().StringArray("url", []string{}, "Callback URL")
   592  	WebhookModifyOutgoingCmd.Flags().String("content-type", "", "Content-type")
   593  
   594  	WebhookMoveOutgoingCmd.Flags().String("channel", "", "Channel name or ID")
   595  
   596  	WebhookCmd.AddCommand(
   597  		WebhookListCmd,
   598  		WebhookCreateIncomingCmd,
   599  		WebhookModifyIncomingCmd,
   600  		WebhookCreateOutgoingCmd,
   601  		WebhookModifyOutgoingCmd,
   602  		WebhookDeleteCmd,
   603  		WebhookShowCmd,
   604  		WebhookMoveOutgoingCmd,
   605  	)
   606  
   607  	RootCmd.AddCommand(WebhookCmd)
   608  }