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