github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/bot.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "errors" 8 "fmt" 9 "io" 10 "mime/multipart" 11 "net/http" 12 13 "github.com/mattermost/mattermost-server/v5/mlog" 14 "github.com/mattermost/mattermost-server/v5/model" 15 "github.com/mattermost/mattermost-server/v5/store" 16 "github.com/mattermost/mattermost-server/v5/utils" 17 ) 18 19 // CreateBot creates the given bot and corresponding user. 20 func (a *App) CreateBot(bot *model.Bot) (*model.Bot, *model.AppError) { 21 user, err := a.Srv().Store.User().Save(model.UserFromBot(bot)) 22 if err != nil { 23 return nil, err 24 } 25 bot.UserId = user.Id 26 27 savedBot, nErr := a.Srv().Store.Bot().Save(bot) 28 if nErr != nil { 29 a.Srv().Store.User().PermanentDelete(bot.UserId) 30 var appErr *model.AppError 31 switch { 32 case errors.As(nErr, &appErr): // in case we haven't converted to plain error. 33 return nil, appErr 34 default: // last fallback in case it doesn't map to an existing app error. 35 return nil, model.NewAppError("CreateBot", "app.bot.createbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError) 36 } 37 } 38 39 // Get the owner of the bot, if one exists. If not, don't send a message 40 ownerUser, err := a.Srv().Store.User().Get(bot.OwnerId) 41 if err != nil && err.Id != store.MISSING_ACCOUNT_ERROR { 42 mlog.Error(err.Error()) 43 return nil, err 44 } else if ownerUser != nil { 45 // Send a message to the bot's creator to inform them that the bot needs to be added 46 // to a team and channel after it's created 47 channel, err := a.GetOrCreateDirectChannel(savedBot.UserId, bot.OwnerId) 48 if err != nil { 49 return nil, err 50 } 51 52 T := utils.GetUserTranslations(ownerUser.Locale) 53 botAddPost := &model.Post{ 54 Type: model.POST_ADD_BOT_TEAMS_CHANNELS, 55 UserId: savedBot.UserId, 56 ChannelId: channel.Id, 57 Message: T("api.bot.teams_channels.add_message_mobile"), 58 } 59 60 if _, err := a.CreatePostAsUser(botAddPost, a.Session().Id, true); err != nil { 61 return nil, err 62 } 63 } 64 65 return savedBot, nil 66 } 67 68 func (a *App) getOrCreateWarnMetricsBot(botDef *model.Bot) (*model.Bot, *model.AppError) { 69 botUser, appErr := a.GetUserByUsername(botDef.Username) 70 if appErr != nil { 71 if appErr.StatusCode != http.StatusNotFound { 72 mlog.Error(appErr.Error()) 73 return nil, appErr 74 } 75 76 // cannot find this bot user, save the user 77 user, err := a.Srv().Store.User().Save(model.UserFromBot(botDef)) 78 if err != nil { 79 mlog.Error(err.Error()) 80 return nil, err 81 } 82 botDef.UserId = user.Id 83 84 //save the bot 85 savedBot, nErr := a.Srv().Store.Bot().Save(botDef) 86 if nErr != nil { 87 a.Srv().Store.User().PermanentDelete(savedBot.UserId) 88 var nAppErr *model.AppError 89 switch { 90 case errors.As(nErr, &nAppErr): // in case we haven't converted to plain error. 91 return nil, nAppErr 92 default: // last fallback in case it doesn't map to an existing app error. 93 return nil, model.NewAppError("getOrCreateWarnMetricsBot", "app.bot.createbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError) 94 } 95 } 96 return savedBot, nil 97 } 98 99 if botUser == nil { 100 return nil, model.NewAppError("getOrCreateWarnMetricsBot", "app.bot.createbot.internal_error", nil, "", http.StatusInternalServerError) 101 } 102 103 //return the bot for this user 104 savedBot, appErr := a.GetBot(botUser.Id, false) 105 if appErr != nil { 106 mlog.Error(appErr.Error()) 107 return nil, appErr 108 } 109 110 return savedBot, nil 111 } 112 113 // PatchBot applies the given patch to the bot and corresponding user. 114 func (a *App) PatchBot(botUserId string, botPatch *model.BotPatch) (*model.Bot, *model.AppError) { 115 bot, err := a.GetBot(botUserId, true) 116 if err != nil { 117 return nil, err 118 } 119 120 bot.Patch(botPatch) 121 122 user, err := a.Srv().Store.User().Get(botUserId) 123 if err != nil { 124 return nil, err 125 } 126 127 patchedUser := model.UserFromBot(bot) 128 user.Id = patchedUser.Id 129 user.Username = patchedUser.Username 130 user.Email = patchedUser.Email 131 user.FirstName = patchedUser.FirstName 132 133 userUpdate, err := a.Srv().Store.User().Update(user, true) 134 if err != nil { 135 return nil, err 136 } 137 a.InvalidateCacheForUser(user.Id) 138 139 ruser := userUpdate.New 140 a.sendUpdatedUserEvent(*ruser) 141 142 bot, nErr := a.Srv().Store.Bot().Update(bot) 143 if nErr != nil { 144 var nfErr *store.ErrNotFound 145 var appErr *model.AppError 146 switch { 147 case errors.As(nErr, &nfErr): 148 return nil, model.MakeBotNotFoundError(nfErr.Id) 149 case errors.As(nErr, &appErr): // in case we haven't converted to plain error. 150 return nil, appErr 151 default: // last fallback in case it doesn't map to an existing app error. 152 return nil, model.NewAppError("PatchBot", "app.bot.patchbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError) 153 } 154 } 155 return bot, nil 156 } 157 158 // GetBot returns the given bot. 159 func (a *App) GetBot(botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) { 160 bot, err := a.Srv().Store.Bot().Get(botUserId, includeDeleted) 161 if err != nil { 162 var nfErr *store.ErrNotFound 163 switch { 164 case errors.As(err, &nfErr): 165 return nil, model.MakeBotNotFoundError(nfErr.Id) 166 default: // last fallback in case it doesn't map to an existing app error. 167 return nil, model.NewAppError("GetBot", "app.bot.getbot.internal_error", nil, err.Error(), http.StatusInternalServerError) 168 } 169 } 170 return bot, nil 171 } 172 173 // GetBots returns the requested page of bots. 174 func (a *App) GetBots(options *model.BotGetOptions) (model.BotList, *model.AppError) { 175 bots, err := a.Srv().Store.Bot().GetAll(options) 176 if err != nil { 177 return nil, model.NewAppError("GetBots", "app.bot.getbots.internal_error", nil, err.Error(), http.StatusInternalServerError) 178 } 179 return bots, nil 180 } 181 182 // UpdateBotActive marks a bot as active or inactive, along with its corresponding user. 183 func (a *App) UpdateBotActive(botUserId string, active bool) (*model.Bot, *model.AppError) { 184 user, err := a.Srv().Store.User().Get(botUserId) 185 if err != nil { 186 return nil, err 187 } 188 189 if _, err = a.UpdateActive(user, active); err != nil { 190 return nil, err 191 } 192 193 bot, nErr := a.Srv().Store.Bot().Get(botUserId, true) 194 if nErr != nil { 195 var nfErr *store.ErrNotFound 196 switch { 197 case errors.As(nErr, &nfErr): 198 return nil, model.MakeBotNotFoundError(nfErr.Id) 199 default: // last fallback in case it doesn't map to an existing app error. 200 return nil, model.NewAppError("UpdateBotActive", "app.bot.getbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError) 201 } 202 } 203 204 changed := true 205 if active && bot.DeleteAt != 0 { 206 bot.DeleteAt = 0 207 } else if !active && bot.DeleteAt == 0 { 208 bot.DeleteAt = model.GetMillis() 209 } else { 210 changed = false 211 } 212 213 if changed { 214 bot, nErr = a.Srv().Store.Bot().Update(bot) 215 if nErr != nil { 216 var nfErr *store.ErrNotFound 217 var appErr *model.AppError 218 switch { 219 case errors.As(nErr, &nfErr): 220 return nil, model.MakeBotNotFoundError(nfErr.Id) 221 case errors.As(nErr, &appErr): // in case we haven't converted to plain error. 222 return nil, appErr 223 default: // last fallback in case it doesn't map to an existing app error. 224 return nil, model.NewAppError("PatchBot", "app.bot.patchbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError) 225 } 226 } 227 } 228 229 return bot, nil 230 } 231 232 // PermanentDeleteBot permanently deletes a bot and its corresponding user. 233 func (a *App) PermanentDeleteBot(botUserId string) *model.AppError { 234 if err := a.Srv().Store.Bot().PermanentDelete(botUserId); err != nil { 235 var invErr *store.ErrInvalidInput 236 switch { 237 case errors.As(err, &invErr): 238 return model.NewAppError("PermanentDeleteBot", "app.bot.permenent_delete.bad_id", map[string]interface{}{"user_id": invErr.Value}, invErr.Error(), http.StatusBadRequest) 239 default: // last fallback in case it doesn't map to an existing app error. 240 return model.NewAppError("PatchBot", "app.bot.permanent_delete.internal_error", nil, err.Error(), http.StatusInternalServerError) 241 } 242 } 243 244 if err := a.Srv().Store.User().PermanentDelete(botUserId); err != nil { 245 return err 246 } 247 248 return nil 249 } 250 251 // UpdateBotOwner changes a bot's owner to the given value. 252 func (a *App) UpdateBotOwner(botUserId, newOwnerId string) (*model.Bot, *model.AppError) { 253 bot, err := a.Srv().Store.Bot().Get(botUserId, true) 254 if err != nil { 255 var nfErr *store.ErrNotFound 256 switch { 257 case errors.As(err, &nfErr): 258 return nil, model.MakeBotNotFoundError(nfErr.Id) 259 default: // last fallback in case it doesn't map to an existing app error. 260 return nil, model.NewAppError("UpdateBotOwner", "app.bot.getbot.internal_error", nil, err.Error(), http.StatusInternalServerError) 261 } 262 } 263 264 bot.OwnerId = newOwnerId 265 266 bot, err = a.Srv().Store.Bot().Update(bot) 267 if err != nil { 268 var nfErr *store.ErrNotFound 269 var appErr *model.AppError 270 switch { 271 case errors.As(err, &nfErr): 272 return nil, model.MakeBotNotFoundError(nfErr.Id) 273 case errors.As(err, &appErr): // in case we haven't converted to plain error. 274 return nil, appErr 275 default: // last fallback in case it doesn't map to an existing app error. 276 return nil, model.NewAppError("PatchBot", "app.bot.patchbot.internal_error", nil, err.Error(), http.StatusInternalServerError) 277 } 278 } 279 280 return bot, nil 281 } 282 283 // disableUserBots disables all bots owned by the given user. 284 func (a *App) disableUserBots(userId string) *model.AppError { 285 perPage := 20 286 for { 287 options := &model.BotGetOptions{ 288 OwnerId: userId, 289 IncludeDeleted: false, 290 OnlyOrphaned: false, 291 Page: 0, 292 PerPage: perPage, 293 } 294 userBots, err := a.GetBots(options) 295 if err != nil { 296 return err 297 } 298 299 for _, bot := range userBots { 300 _, err := a.UpdateBotActive(bot.UserId, false) 301 if err != nil { 302 mlog.Error("Unable to deactivate bot.", mlog.String("bot_user_id", bot.UserId), mlog.Err(err)) 303 } 304 } 305 306 // Get next set of bots if we got the max number of bots 307 if len(userBots) == perPage { 308 options.Page += 1 309 continue 310 } 311 break 312 } 313 314 return nil 315 } 316 317 func (a *App) notifySysadminsBotOwnerDeactivated(userId string) *model.AppError { 318 perPage := 25 319 botOptions := &model.BotGetOptions{ 320 OwnerId: userId, 321 IncludeDeleted: false, 322 OnlyOrphaned: false, 323 Page: 0, 324 PerPage: perPage, 325 } 326 // get owner bots 327 var userBots []*model.Bot 328 for { 329 bots, err := a.GetBots(botOptions) 330 if err != nil { 331 return err 332 } 333 334 userBots = append(userBots, bots...) 335 336 if len(bots) < perPage { 337 break 338 } 339 340 botOptions.Page += 1 341 } 342 343 // user does not own bots 344 if len(userBots) == 0 { 345 return nil 346 } 347 348 userOptions := &model.UserGetOptions{ 349 Page: 0, 350 PerPage: perPage, 351 Role: model.SYSTEM_ADMIN_ROLE_ID, 352 Inactive: false, 353 } 354 // get sysadmins 355 var sysAdmins []*model.User 356 for { 357 sysAdminsList, err := a.GetUsers(userOptions) 358 if err != nil { 359 return err 360 } 361 362 sysAdmins = append(sysAdmins, sysAdminsList...) 363 364 if len(sysAdminsList) < perPage { 365 break 366 } 367 368 userOptions.Page += 1 369 } 370 371 // user being disabled 372 user, err := a.GetUser(userId) 373 if err != nil { 374 return err 375 } 376 377 // for each sysadmin, notify user that owns bots was disabled 378 for _, sysAdmin := range sysAdmins { 379 channel, appErr := a.GetOrCreateDirectChannel(sysAdmin.Id, sysAdmin.Id) 380 if appErr != nil { 381 return appErr 382 } 383 384 post := &model.Post{ 385 UserId: sysAdmin.Id, 386 ChannelId: channel.Id, 387 Message: a.getDisableBotSysadminMessage(user, userBots), 388 Type: model.POST_SYSTEM_GENERIC, 389 } 390 391 _, appErr = a.CreatePost(post, channel, false, true) 392 if appErr != nil { 393 return appErr 394 } 395 } 396 return nil 397 } 398 399 func (a *App) getDisableBotSysadminMessage(user *model.User, userBots model.BotList) string { 400 disableBotsSetting := *a.Config().ServiceSettings.DisableBotsWhenOwnerIsDeactivated 401 402 var printAllBots = true 403 numBotsToPrint := len(userBots) 404 405 if numBotsToPrint > 10 { 406 numBotsToPrint = 10 407 printAllBots = false 408 } 409 410 var message, botList string 411 for _, bot := range userBots[:numBotsToPrint] { 412 botList += fmt.Sprintf("* %v\n", bot.Username) 413 } 414 415 T := utils.GetUserTranslations(user.Locale) 416 message = T("app.bot.get_disable_bot_sysadmin_message", 417 map[string]interface{}{ 418 "UserName": user.Username, 419 "NumBots": len(userBots), 420 "BotNames": botList, 421 "disableBotsSetting": disableBotsSetting, 422 "printAllBots": printAllBots, 423 }) 424 425 return message 426 } 427 428 // ConvertUserToBot converts a user to bot. 429 func (a *App) ConvertUserToBot(user *model.User) (*model.Bot, *model.AppError) { 430 bot, err := a.Srv().Store.Bot().Save(model.BotFromUser(user)) 431 if err != nil { 432 var appErr *model.AppError 433 switch { 434 case errors.As(err, &appErr): // in case we haven't converted to plain error. 435 return nil, appErr 436 default: // last fallback in case it doesn't map to an existing app error. 437 return nil, model.NewAppError("CreateBot", "app.bot.createbot.internal_error", nil, err.Error(), http.StatusInternalServerError) 438 } 439 } 440 return bot, nil 441 } 442 443 // SetBotIconImageFromMultiPartFile sets LHS icon for a bot. 444 func (a *App) SetBotIconImageFromMultiPartFile(botUserId string, imageData *multipart.FileHeader) *model.AppError { 445 file, err := imageData.Open() 446 if err != nil { 447 return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.open.app_error", nil, err.Error(), http.StatusBadRequest) 448 } 449 defer file.Close() 450 451 file.Seek(0, 0) 452 return a.SetBotIconImage(botUserId, file) 453 } 454 455 // SetBotIconImage sets LHS icon for a bot. 456 func (a *App) SetBotIconImage(botUserId string, file io.ReadSeeker) *model.AppError { 457 bot, err := a.GetBot(botUserId, true) 458 if err != nil { 459 return err 460 } 461 462 if _, err := parseSVG(file); err != nil { 463 return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.parse.app_error", nil, err.Error(), http.StatusBadRequest) 464 } 465 466 // Set icon 467 file.Seek(0, 0) 468 if _, err = a.WriteFile(file, getBotIconPath(botUserId)); err != nil { 469 return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.app_error", nil, err.Error(), http.StatusInternalServerError) 470 } 471 472 bot.LastIconUpdate = model.GetMillis() 473 if _, err := a.Srv().Store.Bot().Update(bot); err != nil { 474 var nfErr *store.ErrNotFound 475 var appErr *model.AppError 476 switch { 477 case errors.As(err, &nfErr): 478 return model.MakeBotNotFoundError(nfErr.Id) 479 case errors.As(err, &appErr): // in case we haven't converted to plain error. 480 return appErr 481 default: // last fallback in case it doesn't map to an existing app error. 482 return model.NewAppError("SetBotIconImage", "app.bot.patchbot.internal_error", nil, err.Error(), http.StatusInternalServerError) 483 } 484 } 485 a.invalidateUserCacheAndPublish(botUserId) 486 487 return nil 488 } 489 490 // DeleteBotIconImage deletes LHS icon for a bot. 491 func (a *App) DeleteBotIconImage(botUserId string) *model.AppError { 492 bot, err := a.GetBot(botUserId, true) 493 if err != nil { 494 return err 495 } 496 497 // Delete icon 498 if err = a.RemoveFile(getBotIconPath(botUserId)); err != nil { 499 return model.NewAppError("DeleteBotIconImage", "api.bot.delete_bot_icon_image.app_error", nil, err.Error(), http.StatusInternalServerError) 500 } 501 502 if err = a.Srv().Store.User().UpdateLastPictureUpdate(botUserId); err != nil { 503 mlog.Error(err.Error()) 504 } 505 506 bot.LastIconUpdate = int64(0) 507 if _, err := a.Srv().Store.Bot().Update(bot); err != nil { 508 var nfErr *store.ErrNotFound 509 var appErr *model.AppError 510 switch { 511 case errors.As(err, &nfErr): 512 return model.MakeBotNotFoundError(nfErr.Id) 513 case errors.As(err, &appErr): // in case we haven't converted to plain error. 514 return appErr 515 default: // last fallback in case it doesn't map to an existing app error. 516 return model.NewAppError("DeleteBotIconImage", "app.bot.patchbot.internal_error", nil, err.Error(), http.StatusInternalServerError) 517 } 518 } 519 520 a.invalidateUserCacheAndPublish(botUserId) 521 522 return nil 523 } 524 525 // GetBotIconImage retrieves LHS icon for a bot. 526 func (a *App) GetBotIconImage(botUserId string) ([]byte, *model.AppError) { 527 if _, err := a.GetBot(botUserId, true); err != nil { 528 return nil, err 529 } 530 531 data, err := a.ReadFile(getBotIconPath(botUserId)) 532 if err != nil { 533 return nil, model.NewAppError("GetBotIconImage", "api.bot.get_bot_icon_image.read.app_error", nil, err.Error(), http.StatusNotFound) 534 } 535 536 return data, nil 537 } 538 539 func getBotIconPath(botUserId string) string { 540 return fmt.Sprintf("bots/%v/icon.svg", botUserId) 541 }