github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/model/bot.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package model 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io" 10 "net/http" 11 "strings" 12 "unicode/utf8" 13 ) 14 15 const ( 16 BOT_DISPLAY_NAME_MAX_RUNES = USER_FIRST_NAME_MAX_RUNES 17 BOT_DESCRIPTION_MAX_RUNES = 1024 18 BOT_CREATOR_ID_MAX_RUNES = KEY_VALUE_PLUGIN_ID_MAX_RUNES // UserId or PluginId 19 BOT_WARN_METRIC_BOT_USERNAME = "mattermost-advisor" 20 ) 21 22 // Bot is a special type of User meant for programmatic interactions. 23 // Note that the primary key of a bot is the UserId, and matches the primary key of the 24 // corresponding user. 25 type Bot struct { 26 UserId string `json:"user_id"` 27 Username string `json:"username"` 28 DisplayName string `json:"display_name,omitempty"` 29 Description string `json:"description,omitempty"` 30 OwnerId string `json:"owner_id"` 31 LastIconUpdate int64 `json:"last_icon_update,omitempty"` 32 CreateAt int64 `json:"create_at"` 33 UpdateAt int64 `json:"update_at"` 34 DeleteAt int64 `json:"delete_at"` 35 } 36 37 // BotPatch is a description of what fields to update on an existing bot. 38 type BotPatch struct { 39 Username *string `json:"username"` 40 DisplayName *string `json:"display_name"` 41 Description *string `json:"description"` 42 } 43 44 // BotGetOptions acts as a filter on bulk bot fetching queries. 45 type BotGetOptions struct { 46 OwnerId string 47 IncludeDeleted bool 48 OnlyOrphaned bool 49 Page int 50 PerPage int 51 } 52 53 // BotList is a list of bots. 54 type BotList []*Bot 55 56 // Trace describes the minimum information required to identify a bot for the purpose of logging. 57 func (b *Bot) Trace() map[string]interface{} { 58 return map[string]interface{}{"user_id": b.UserId} 59 } 60 61 // Clone returns a shallow copy of the bot. 62 func (b *Bot) Clone() *Bot { 63 copy := *b 64 return © 65 } 66 67 // IsValid validates the bot and returns an error if it isn't configured correctly. 68 func (b *Bot) IsValid() *AppError { 69 if !IsValidId(b.UserId) { 70 return NewAppError("Bot.IsValid", "model.bot.is_valid.user_id.app_error", b.Trace(), "", http.StatusBadRequest) 71 } 72 73 if !IsValidUsername(b.Username) { 74 return NewAppError("Bot.IsValid", "model.bot.is_valid.username.app_error", b.Trace(), "", http.StatusBadRequest) 75 } 76 77 if utf8.RuneCountInString(b.DisplayName) > BOT_DISPLAY_NAME_MAX_RUNES { 78 return NewAppError("Bot.IsValid", "model.bot.is_valid.user_id.app_error", b.Trace(), "", http.StatusBadRequest) 79 } 80 81 if utf8.RuneCountInString(b.Description) > BOT_DESCRIPTION_MAX_RUNES { 82 return NewAppError("Bot.IsValid", "model.bot.is_valid.description.app_error", b.Trace(), "", http.StatusBadRequest) 83 } 84 85 if len(b.OwnerId) == 0 || utf8.RuneCountInString(b.OwnerId) > BOT_CREATOR_ID_MAX_RUNES { 86 return NewAppError("Bot.IsValid", "model.bot.is_valid.creator_id.app_error", b.Trace(), "", http.StatusBadRequest) 87 } 88 89 if b.CreateAt == 0 { 90 return NewAppError("Bot.IsValid", "model.bot.is_valid.create_at.app_error", b.Trace(), "", http.StatusBadRequest) 91 } 92 93 if b.UpdateAt == 0 { 94 return NewAppError("Bot.IsValid", "model.bot.is_valid.update_at.app_error", b.Trace(), "", http.StatusBadRequest) 95 } 96 97 return nil 98 } 99 100 // PreSave should be run before saving a new bot to the database. 101 func (b *Bot) PreSave() { 102 b.CreateAt = GetMillis() 103 b.UpdateAt = b.CreateAt 104 b.DeleteAt = 0 105 } 106 107 // PreUpdate should be run before saving an updated bot to the database. 108 func (b *Bot) PreUpdate() { 109 b.UpdateAt = GetMillis() 110 } 111 112 // Etag generates an etag for caching. 113 func (b *Bot) Etag() string { 114 return Etag(b.UserId, b.UpdateAt) 115 } 116 117 // ToJson serializes the bot to json. 118 func (b *Bot) ToJson() []byte { 119 data, _ := json.Marshal(b) 120 return data 121 } 122 123 // BotFromJson deserializes a bot from json. 124 func BotFromJson(data io.Reader) *Bot { 125 var bot *Bot 126 json.NewDecoder(data).Decode(&bot) 127 return bot 128 } 129 130 // Patch modifies an existing bot with optional fields from the given patch. 131 func (b *Bot) Patch(patch *BotPatch) { 132 if patch.Username != nil { 133 b.Username = *patch.Username 134 } 135 136 if patch.DisplayName != nil { 137 b.DisplayName = *patch.DisplayName 138 } 139 140 if patch.Description != nil { 141 b.Description = *patch.Description 142 } 143 } 144 145 // ToJson serializes the bot patch to json. 146 func (b *BotPatch) ToJson() []byte { 147 data, err := json.Marshal(b) 148 if err != nil { 149 return nil 150 } 151 152 return data 153 } 154 155 // BotPatchFromJson deserializes a bot patch from json. 156 func BotPatchFromJson(data io.Reader) *BotPatch { 157 decoder := json.NewDecoder(data) 158 var botPatch BotPatch 159 err := decoder.Decode(&botPatch) 160 if err != nil { 161 return nil 162 } 163 164 return &botPatch 165 } 166 167 // UserFromBot returns a user model describing the bot fields stored in the User store. 168 func UserFromBot(b *Bot) *User { 169 return &User{ 170 Id: b.UserId, 171 Username: b.Username, 172 Email: NormalizeEmail(fmt.Sprintf("%s@localhost", b.Username)), 173 FirstName: b.DisplayName, 174 Roles: SYSTEM_USER_ROLE_ID, 175 } 176 } 177 178 // BotFromUser returns a bot model given a user model 179 func BotFromUser(u *User) *Bot { 180 return &Bot{ 181 OwnerId: u.Id, 182 UserId: u.Id, 183 Username: u.Username, 184 DisplayName: u.GetDisplayName(SHOW_USERNAME), 185 } 186 } 187 188 // BotListFromJson deserializes a list of bots from json. 189 func BotListFromJson(data io.Reader) BotList { 190 var bots BotList 191 json.NewDecoder(data).Decode(&bots) 192 return bots 193 } 194 195 // ToJson serializes a list of bots to json. 196 func (l *BotList) ToJson() []byte { 197 b, _ := json.Marshal(l) 198 return b 199 } 200 201 // Etag computes the etag for a list of bots. 202 func (l *BotList) Etag() string { 203 id := "0" 204 var t int64 = 0 205 var delta int64 = 0 206 207 for _, v := range *l { 208 if v.UpdateAt > t { 209 t = v.UpdateAt 210 id = v.UserId 211 } 212 213 } 214 215 return Etag(id, t, delta, len(*l)) 216 } 217 218 // MakeBotNotFoundError creates the error returned when a bot does not exist, or when the user isn't allowed to query the bot. 219 // The errors must the same in both cases to avoid leaking that a user is a bot. 220 func MakeBotNotFoundError(userId string) *AppError { 221 return NewAppError("SqlBotStore.Get", "store.sql_bot.get.missing.app_error", map[string]interface{}{"user_id": userId}, "", http.StatusNotFound) 222 } 223 224 func IsBotDMChannel(channel *Channel, botUserID string) bool { 225 if channel.Type != CHANNEL_DIRECT { 226 return false 227 } 228 229 if !strings.HasPrefix(channel.Name, botUserID+"__") && !strings.HasSuffix(channel.Name, "__"+botUserID) { 230 return false 231 } 232 233 return true 234 }