Files
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

286 lines
8.1 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"context"
"fmt"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var BotCmd = &cobra.Command{
Use: "bot",
Short: "Management of bots",
}
var CreateBotCmd = &cobra.Command{
Use: "create [username]",
Short: "Create bot",
Long: "Create bot.",
Example: ` bot create testbot`,
PreRun: disableLocalPrecheck,
RunE: withClient(botCreateCmdF),
Args: cobra.ExactArgs(1),
}
var UpdateBotCmd = &cobra.Command{
Use: "update [username]",
Short: "Update bot",
Long: "Update bot information.",
Example: ` bot update testbot --username newbotusername`,
RunE: withClient(botUpdateCmdF),
Args: cobra.ExactArgs(1),
}
var ListBotCmd = &cobra.Command{
Use: "list",
Short: "List bots",
Long: "List the bots users.",
Example: ` bot list`,
RunE: withClient(botListCmdF),
Args: cobra.NoArgs,
}
var DisableBotCmd = &cobra.Command{
Use: "disable [username]",
Short: "Disable bot",
Long: "Disable an enabled bot",
Example: ` bot disable testbot`,
RunE: withClient(botDisableCmdF),
Args: cobra.MinimumNArgs(1),
}
var EnableBotCmd = &cobra.Command{
Use: "enable [username]",
Short: "Enable bot",
Long: "Enable a disabled bot",
Example: ` bot enable testbot`,
RunE: withClient(botEnableCmdF),
Args: cobra.MinimumNArgs(1),
}
var AssignBotCmd = &cobra.Command{
Use: "assign [bot-username] [new-owner-username]",
Short: "Assign bot",
Long: "Assign the ownership of a bot to another user",
Example: ` bot assign testbot user2`,
RunE: withClient(botAssignCmdF),
Args: cobra.ExactArgs(2),
}
func init() {
CreateBotCmd.Flags().String("display-name", "", "Optional. The display name for the new bot.")
CreateBotCmd.Flags().String("description", "", "Optional. The description text for the new bot.")
CreateBotCmd.Flags().Bool("with-token", false, "Optional. Auto genreate access token for the bot.")
ListBotCmd.Flags().Bool("orphaned", false, "Optional. Only show orphaned bots.")
ListBotCmd.Flags().Bool("all", false, "Optional. Show all bots (including deleleted and orphaned).")
UpdateBotCmd.Flags().String("username", "", "Optional. The new username for the bot.")
UpdateBotCmd.Flags().String("display-name", "", "Optional. The new display name for the bot.")
UpdateBotCmd.Flags().String("description", "", "Optional. The new description text for the bot.")
BotCmd.AddCommand(
CreateBotCmd,
UpdateBotCmd,
ListBotCmd,
EnableBotCmd,
DisableBotCmd,
AssignBotCmd,
)
RootCmd.AddCommand(BotCmd)
}
func botCreateCmdF(c client.Client, cmd *cobra.Command, args []string) error {
username := args[0]
displayName, _ := cmd.Flags().GetString("display-name")
description, _ := cmd.Flags().GetString("description")
bot, _, err := c.CreateBot(context.TODO(), &model.Bot{
Username: username,
DisplayName: displayName,
Description: description,
})
if err != nil {
return errors.Errorf("could not create bot: %s", err)
}
printer.PrintT("Created bot {{.UserId}}", bot)
if withToken, _ := cmd.Flags().GetBool("with-token"); withToken {
return generateTokenForAUserCmdF(c, cmd, []string{args[0], "autogenerated"})
}
return nil
}
func botUpdateCmdF(c client.Client, cmd *cobra.Command, args []string) error {
if !cmd.Flags().Changed("username") && !cmd.Flags().Changed("display-name") && !cmd.Flags().Changed("description") {
return errors.New("at least one of --username, --display-name or --description must be set")
}
user := getUserFromUserArg(c, args[0])
if user == nil {
return errors.New("unable to find user '" + args[0] + "'")
}
patch := model.BotPatch{}
username, err := cmd.Flags().GetString("username")
if err == nil && cmd.Flags().Changed("username") {
patch.Username = &username
}
displayName, err := cmd.Flags().GetString("display-name")
if err == nil && cmd.Flags().Changed("display-name") {
patch.DisplayName = &displayName
}
description, err := cmd.Flags().GetString("description")
if err == nil && cmd.Flags().Changed("description") {
patch.Description = &description
}
bot, _, err := c.PatchBot(context.TODO(), user.Id, &patch)
if err != nil {
return errors.Errorf("could not update bot: %s", err)
}
printer.PrintT("Updated bot {{.UserId}} ({{.Username}})", bot)
return nil
}
func botListCmdF(c client.Client, cmd *cobra.Command, args []string) error {
orphaned, _ := cmd.Flags().GetBool("orphaned")
all, _ := cmd.Flags().GetBool("all")
page := 0
perPage := DefaultPageSize
tpl := `{{.UserId}}: {{.Username}}`
for {
var bots []*model.Bot
var err error
if all { //nolint:gocritic
bots, _, err = c.GetBotsIncludeDeleted(context.TODO(), page, perPage, "")
} else if orphaned {
bots, _, err = c.GetBotsOrphaned(context.TODO(), page, perPage, "")
} else {
bots, _, err = c.GetBots(context.TODO(), page, perPage, "")
}
if err != nil {
return errors.Wrap(err, "Failed to fetch bots")
}
userIds := []string{}
for _, bot := range bots {
userIds = append(userIds, bot.OwnerId)
}
users, _, err := c.GetUsersByIds(context.TODO(), userIds)
if err != nil {
return errors.Wrap(err, "Failed to fetch bots")
}
usersByID := map[string]*model.User{}
for _, user := range users {
usersByID[user.Id] = user
}
var ownerName string
var ownerDeleteAt int64
for _, bot := range bots {
if owner, ok := usersByID[bot.OwnerId]; ok {
ownerName = owner.Username
ownerDeleteAt = owner.DeleteAt
} else {
// not all bots have a userId in their ownerId field.
ownerName = bot.OwnerId
ownerDeleteAt = 0
}
tplExtraText := fmt.Sprintf("(Owned by %s, {{if ne .DeleteAt 0}}Disabled{{else}}Enabled{{end}}{{if ne %d 0}}, Orphaned{{end}})", ownerName, ownerDeleteAt)
printer.PrintT(tpl+tplExtraText, bot)
}
if len(bots) < perPage {
break
}
page++
}
return nil
}
func botEnableCmdF(c client.Client, cmd *cobra.Command, args []string) error {
users := getUsersFromUserArgs(c, args)
var result *multierror.Error
for i, user := range users {
if user == nil {
printer.PrintError(fmt.Sprintf("can't find user '%v'", args[i]))
result = multierror.Append(result, fmt.Errorf("can't find user %q", args[i]))
continue
}
bot, _, err := c.EnableBot(context.TODO(), user.Id)
if err != nil {
printer.PrintError(fmt.Sprintf("could not enable bot '%v'", args[i]))
result = multierror.Append(result, fmt.Errorf("could not enable bot %q: %w", args[i], err))
continue
}
printer.PrintT("Enabled bot {{.UserId}} ({{.Username}})", bot)
}
return result.ErrorOrNil()
}
func botDisableCmdF(c client.Client, cmd *cobra.Command, args []string) error {
users := getUsersFromUserArgs(c, args)
var result *multierror.Error
for i, user := range users {
if user == nil {
printer.PrintError(fmt.Sprintf("can't find user '%v'", args[i]))
result = multierror.Append(result, fmt.Errorf("can't find user %q", args[i]))
continue
}
bot, _, err := c.DisableBot(context.TODO(), user.Id)
if err != nil {
printer.PrintError(fmt.Sprintf("could not disable bot '%v'", args[i]))
result = multierror.Append(result, fmt.Errorf("could not disable bot %q: %w", args[i], err))
continue
}
printer.PrintT("Disabled bot {{.UserId}} ({{.Username}})", bot)
}
return result.ErrorOrNil()
}
func botAssignCmdF(c client.Client, cmd *cobra.Command, args []string) error {
botUser := getUserFromUserArg(c, args[0])
if botUser == nil {
return errors.New("unable to find user '" + args[0] + "'")
}
newOwnerUser := getUserFromUserArg(c, args[1])
if newOwnerUser == nil {
return errors.New("unable to find user '" + args[1] + "'")
}
newBot, _, err := c.AssignBot(context.TODO(), botUser.Id, newOwnerUser.Id)
if err != nil {
return errors.Errorf("can not assign bot '%s' to user '%s'", args[0], args[1])
}
printer.PrintT("The bot {{.UserId}} ({{.Username}}) now belongs to the user "+newOwnerUser.Username, newBot)
return nil
}