github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/cmd/mattermost/commands/sampledata.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 "encoding/json" 8 "errors" 9 "fmt" 10 "io/ioutil" 11 "math/rand" 12 "os" 13 "path" 14 "sort" 15 "strings" 16 "time" 17 18 "github.com/icrowley/fake" 19 "github.com/spf13/cobra" 20 21 "github.com/mattermost/mattermost-server/v5/app" 22 "github.com/mattermost/mattermost-server/v5/audit" 23 "github.com/mattermost/mattermost-server/v5/model" 24 "github.com/mattermost/mattermost-server/v5/utils" 25 ) 26 27 const ( 28 DeactivatedUser = "deactivated" 29 GuestUser = "guest" 30 ) 31 32 var SampleDataCmd = &cobra.Command{ 33 Use: "sampledata", 34 Short: "Generate sample data", 35 RunE: sampleDataCmdF, 36 } 37 38 func init() { 39 SampleDataCmd.Flags().Int64P("seed", "s", 1, "Seed used for generating the random data (Different seeds generate different data).") 40 SampleDataCmd.Flags().IntP("teams", "t", 2, "The number of sample teams.") 41 SampleDataCmd.Flags().Int("channels-per-team", 10, "The number of sample channels per team.") 42 SampleDataCmd.Flags().IntP("users", "u", 15, "The number of sample users.") 43 SampleDataCmd.Flags().IntP("guests", "g", 1, "The number of sample guests.") 44 SampleDataCmd.Flags().Int("deactivated-users", 0, "The number of deactivated users.") 45 SampleDataCmd.Flags().Int("team-memberships", 2, "The number of sample team memberships per user.") 46 SampleDataCmd.Flags().Int("channel-memberships", 5, "The number of sample channel memberships per user in a team.") 47 SampleDataCmd.Flags().Int("posts-per-channel", 100, "The number of sample post per channel.") 48 SampleDataCmd.Flags().Int("direct-channels", 30, "The number of sample direct message channels.") 49 SampleDataCmd.Flags().Int("posts-per-direct-channel", 15, "The number of sample posts per direct message channel.") 50 SampleDataCmd.Flags().Int("group-channels", 15, "The number of sample group message channels.") 51 SampleDataCmd.Flags().Int("posts-per-group-channel", 30, "The number of sample posts per group message channel.") 52 SampleDataCmd.Flags().IntP("workers", "w", 2, "How many workers to run during the import.") 53 SampleDataCmd.Flags().String("profile-images", "", "Optional. Path to folder with images to randomly pick as user profile image.") 54 SampleDataCmd.Flags().StringP("bulk", "b", "", "Optional. Path to write a JSONL bulk file instead of loading into the database.") 55 RootCmd.AddCommand(SampleDataCmd) 56 } 57 58 func randomPastTime(seconds int) int64 { 59 now := time.Now() 60 today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.FixedZone("UTC", 0)) 61 return (today.Unix() * 1000) - int64(rand.Intn(seconds*1000)) 62 } 63 64 func sortedRandomDates(size int) []int64 { 65 dates := make([]int64, size) 66 for i := 0; i < size; i++ { 67 dates[i] = randomPastTime(50000) 68 } 69 sort.Slice(dates, func(a, b int) bool { return dates[a] < dates[b] }) 70 return dates 71 } 72 73 func randomEmoji() string { 74 emojis := []string{"+1", "-1", "heart", "blush"} 75 return emojis[rand.Intn(len(emojis))] 76 } 77 78 func randomReaction(users []string, parentCreateAt int64) app.ReactionImportData { 79 user := users[rand.Intn(len(users))] 80 emoji := randomEmoji() 81 date := parentCreateAt + int64(rand.Intn(100000)) 82 return app.ReactionImportData{ 83 User: &user, 84 EmojiName: &emoji, 85 CreateAt: &date, 86 } 87 } 88 89 func randomReply(users []string, parentCreateAt int64) app.ReplyImportData { 90 user := users[rand.Intn(len(users))] 91 message := randomMessage(users) 92 date := parentCreateAt + int64(rand.Intn(100000)) 93 return app.ReplyImportData{ 94 User: &user, 95 Message: &message, 96 CreateAt: &date, 97 } 98 } 99 100 func randomMessage(users []string) string { 101 var message string 102 switch rand.Intn(30) { 103 case 0: 104 mention := users[rand.Intn(len(users))] 105 message = "@" + mention + " " + fake.Sentence() 106 case 1: 107 switch rand.Intn(2) { 108 case 0: 109 mattermostVideos := []string{"Q4MgnxbpZas", "BFo7E9-Kc_E", "LsMLR-BHsKg", "MRmGDhlMhNA", "mUOPxT7VgWc"} 110 message = "https://www.youtube.com/watch?v=" + mattermostVideos[rand.Intn(len(mattermostVideos))] 111 case 1: 112 mattermostTweets := []string{"943119062334353408", "949370809528832005", "948539688171819009", "939122439115681792", "938061722027425797"} 113 message = "https://twitter.com/mattermosthq/status/" + mattermostTweets[rand.Intn(len(mattermostTweets))] 114 } 115 case 2: 116 message = "" 117 if rand.Intn(2) == 0 { 118 message += fake.Sentence() 119 } 120 for i := 0; i < rand.Intn(4)+1; i++ { 121 message += "\n * " + fake.Word() 122 } 123 default: 124 if rand.Intn(2) == 0 { 125 message = fake.Sentence() 126 } else { 127 message = fake.Paragraph() 128 } 129 if rand.Intn(3) == 0 { 130 message += "\n" + fake.Sentence() 131 } 132 if rand.Intn(3) == 0 { 133 message += "\n" + fake.Sentence() 134 } 135 if rand.Intn(3) == 0 { 136 message += "\n" + fake.Sentence() 137 } 138 } 139 return message 140 } 141 142 func sampleDataCmdF(command *cobra.Command, args []string) error { 143 a, err := InitDBCommandContextCobra(command) 144 if err != nil { 145 return err 146 } 147 defer a.Srv().Shutdown() 148 149 seed, err := command.Flags().GetInt64("seed") 150 if err != nil { 151 return errors.New("Invalid seed parameter") 152 } 153 bulk, err := command.Flags().GetString("bulk") 154 if err != nil { 155 return errors.New("Invalid bulk parameter") 156 } 157 teams, err := command.Flags().GetInt("teams") 158 if err != nil || teams < 0 { 159 return errors.New("Invalid teams parameter") 160 } 161 channelsPerTeam, err := command.Flags().GetInt("channels-per-team") 162 if err != nil || channelsPerTeam < 0 { 163 return errors.New("Invalid channels-per-team parameter") 164 } 165 users, err := command.Flags().GetInt("users") 166 if err != nil || users < 0 { 167 return errors.New("Invalid users parameter") 168 } 169 deactivatedUsers, err := command.Flags().GetInt("deactivated-users") 170 if err != nil || deactivatedUsers < 0 { 171 return errors.New("Invalid deactivated-users parameter") 172 } 173 guests, err := command.Flags().GetInt("guests") 174 if err != nil || guests < 0 { 175 return errors.New("Invalid guests parameter") 176 } 177 teamMemberships, err := command.Flags().GetInt("team-memberships") 178 if err != nil || teamMemberships < 0 { 179 return errors.New("Invalid team-memberships parameter") 180 } 181 channelMemberships, err := command.Flags().GetInt("channel-memberships") 182 if err != nil || channelMemberships < 0 { 183 return errors.New("Invalid channel-memberships parameter") 184 } 185 postsPerChannel, err := command.Flags().GetInt("posts-per-channel") 186 if err != nil || postsPerChannel < 0 { 187 return errors.New("Invalid posts-per-channel parameter") 188 } 189 directChannels, err := command.Flags().GetInt("direct-channels") 190 if err != nil || directChannels < 0 { 191 return errors.New("Invalid direct-channels parameter") 192 } 193 postsPerDirectChannel, err := command.Flags().GetInt("posts-per-direct-channel") 194 if err != nil || postsPerDirectChannel < 0 { 195 return errors.New("Invalid posts-per-direct-channel parameter") 196 } 197 groupChannels, err := command.Flags().GetInt("group-channels") 198 if err != nil || groupChannels < 0 { 199 return errors.New("Invalid group-channels parameter") 200 } 201 postsPerGroupChannel, err := command.Flags().GetInt("posts-per-group-channel") 202 if err != nil || postsPerGroupChannel < 0 { 203 return errors.New("Invalid posts-per-group-channel parameter") 204 } 205 workers, err := command.Flags().GetInt("workers") 206 if err != nil { 207 return errors.New("Invalid workers parameter") 208 } 209 profileImagesPath, err := command.Flags().GetString("profile-images") 210 if err != nil { 211 return errors.New("Invalid profile-images parameter") 212 } 213 profileImages := []string{} 214 if profileImagesPath != "" { 215 var profileImagesStat os.FileInfo 216 profileImagesStat, err = os.Stat(profileImagesPath) 217 if os.IsNotExist(err) { 218 return errors.New("Profile images folder doesn't exists.") 219 } 220 if !profileImagesStat.IsDir() { 221 return errors.New("profile-images parameters must be a folder path.") 222 } 223 var profileImagesFiles []os.FileInfo 224 profileImagesFiles, err = ioutil.ReadDir(profileImagesPath) 225 if err != nil { 226 return errors.New("Invalid profile-images parameter") 227 } 228 for _, profileImage := range profileImagesFiles { 229 profileImages = append(profileImages, path.Join(profileImagesPath, profileImage.Name())) 230 } 231 sort.Strings(profileImages) 232 } 233 234 if workers < 1 { 235 return errors.New("You must have at least one worker.") 236 } 237 if teamMemberships > teams { 238 return errors.New("You can't have more team memberships than teams.") 239 } 240 if channelMemberships > channelsPerTeam { 241 return errors.New("You can't have more channel memberships than channels per team.") 242 } 243 244 if users < 6 && groupChannels > 0 { 245 return errors.New("You can't have group channels generation with less than 6 users. Use --group-channels 0 or increase the number of users.") 246 } 247 248 var bulkFile *os.File 249 switch bulk { 250 case "": 251 bulkFile, err = ioutil.TempFile("", ".mattermost-sample-data-") 252 defer os.Remove(bulkFile.Name()) 253 if err != nil { 254 return errors.New("Unable to open temporary file.") 255 } 256 case "-": 257 bulkFile = os.Stdout 258 default: 259 bulkFile, err = os.OpenFile(bulk, os.O_RDWR|os.O_CREATE, 0755) 260 if err != nil { 261 return errors.New("Unable to write into the \"" + bulk + "\" file.") 262 } 263 } 264 265 encoder := json.NewEncoder(bulkFile) 266 version := 1 267 encoder.Encode(app.LineImportData{Type: "version", Version: &version}) 268 269 fake.Seed(seed) 270 rand.Seed(seed) 271 272 teamsAndChannels := make(map[string][]string) 273 for i := 0; i < teams; i++ { 274 teamLine := createTeam(i) 275 teamsAndChannels[*teamLine.Team.Name] = []string{} 276 encoder.Encode(teamLine) 277 } 278 279 teamsList := []string{} 280 for teamName := range teamsAndChannels { 281 teamsList = append(teamsList, teamName) 282 } 283 sort.Strings(teamsList) 284 285 for _, teamName := range teamsList { 286 for i := 0; i < channelsPerTeam; i++ { 287 channelLine := createChannel(i, teamName) 288 teamsAndChannels[teamName] = append(teamsAndChannels[teamName], *channelLine.Channel.Name) 289 encoder.Encode(channelLine) 290 } 291 } 292 293 allUsers := []string{} 294 for i := 0; i < users; i++ { 295 userLine := createUser(i, teamMemberships, channelMemberships, teamsAndChannels, profileImages, "") 296 encoder.Encode(userLine) 297 allUsers = append(allUsers, *userLine.User.Username) 298 } 299 for i := 0; i < guests; i++ { 300 userLine := createUser(i, teamMemberships, channelMemberships, teamsAndChannels, profileImages, GuestUser) 301 encoder.Encode(userLine) 302 allUsers = append(allUsers, *userLine.User.Username) 303 } 304 for i := 0; i < deactivatedUsers; i++ { 305 userLine := createUser(i, teamMemberships, channelMemberships, teamsAndChannels, profileImages, DeactivatedUser) 306 encoder.Encode(userLine) 307 allUsers = append(allUsers, *userLine.User.Username) 308 } 309 310 for team, channels := range teamsAndChannels { 311 for _, channel := range channels { 312 dates := sortedRandomDates(postsPerChannel) 313 314 for i := 0; i < postsPerChannel; i++ { 315 postLine := createPost(team, channel, allUsers, dates[i]) 316 encoder.Encode(postLine) 317 } 318 } 319 } 320 321 for i := 0; i < directChannels; i++ { 322 user1 := allUsers[rand.Intn(len(allUsers))] 323 user2 := allUsers[rand.Intn(len(allUsers))] 324 channelLine := createDirectChannel([]string{user1, user2}) 325 encoder.Encode(channelLine) 326 } 327 328 for i := 0; i < directChannels; i++ { 329 user1 := allUsers[rand.Intn(len(allUsers))] 330 user2 := allUsers[rand.Intn(len(allUsers))] 331 332 dates := sortedRandomDates(postsPerDirectChannel) 333 for j := 0; j < postsPerDirectChannel; j++ { 334 postLine := createDirectPost([]string{user1, user2}, dates[j]) 335 encoder.Encode(postLine) 336 } 337 } 338 339 for i := 0; i < groupChannels; i++ { 340 users := []string{} 341 totalUsers := 3 + rand.Intn(3) 342 for len(users) < totalUsers { 343 user := allUsers[rand.Intn(len(allUsers))] 344 if !utils.StringInSlice(user, users) { 345 users = append(users, user) 346 } 347 } 348 channelLine := createDirectChannel(users) 349 encoder.Encode(channelLine) 350 } 351 352 for i := 0; i < groupChannels; i++ { 353 users := []string{} 354 totalUsers := 3 + rand.Intn(3) 355 for len(users) < totalUsers { 356 user := allUsers[rand.Intn(len(allUsers))] 357 if !utils.StringInSlice(user, users) { 358 users = append(users, user) 359 } 360 } 361 362 dates := sortedRandomDates(postsPerGroupChannel) 363 for j := 0; j < postsPerGroupChannel; j++ { 364 postLine := createDirectPost(users, dates[j]) 365 encoder.Encode(postLine) 366 } 367 } 368 369 if bulk == "" { 370 _, err := bulkFile.Seek(0, 0) 371 if err != nil { 372 return errors.New("Unable to read correctly the temporary file.") 373 } 374 375 var importErr *model.AppError 376 importErr, lineNumber := a.BulkImport(bulkFile, false, workers) 377 if importErr != nil { 378 return fmt.Errorf("%s: %s, %s (line: %d)", importErr.Where, importErr.Message, importErr.DetailedError, lineNumber) 379 } 380 auditRec := a.MakeAuditRecord("sampleData", audit.Success) 381 auditRec.AddMeta("file", bulkFile.Name()) 382 a.LogAuditRec(auditRec, nil) 383 } else if bulk != "-" { 384 err := bulkFile.Close() 385 if err != nil { 386 return errors.New("Unable to close correctly the output file") 387 } 388 } 389 390 return nil 391 } 392 393 func createUser(idx int, teamMemberships int, channelMemberships int, teamsAndChannels map[string][]string, profileImages []string, userType string) app.LineImportData { 394 firstName := fake.FirstName() 395 lastName := fake.LastName() 396 position := fake.JobTitle() 397 398 username := fmt.Sprintf("%s.%s", strings.ToLower(firstName), strings.ToLower(lastName)) 399 roles := "system_user" 400 401 var password string 402 var email string 403 404 switch userType { 405 case GuestUser: 406 password = fmt.Sprintf("SampleGu@st-%d", idx) 407 email = fmt.Sprintf("guest-%d@sample.mattermost.com", idx) 408 roles = "system_guest" 409 if idx == 0 { 410 username = "guest" 411 password = "SampleGu@st1" 412 email = "guest@sample.mattermost.com" 413 } 414 case DeactivatedUser: 415 password = fmt.Sprintf("SampleDe@ctivated-%d", idx) 416 email = fmt.Sprintf("deactivated-%d@sample.mattermost.com", idx) 417 default: 418 password = fmt.Sprintf("SampleUs@r-%d", idx) 419 email = fmt.Sprintf("user-%d@sample.mattermost.com", idx) 420 if idx == 0 { 421 username = "sysadmin" 422 password = "Sys@dmin-sample1" 423 email = "sysadmin@sample.mattermost.com" 424 } else if idx == 1 { 425 username = "user-1" 426 } 427 428 if idx%5 == 0 { 429 roles = "system_admin system_user" 430 } 431 } 432 433 // The 75% of the users have custom profile image 434 var profileImage *string = nil 435 if rand.Intn(4) != 0 { 436 profileImageSelector := rand.Int() 437 if len(profileImages) > 0 { 438 profileImage = &profileImages[profileImageSelector%len(profileImages)] 439 } 440 } 441 442 useMilitaryTime := "false" 443 if idx != 0 && rand.Intn(2) == 0 { 444 useMilitaryTime = "true" 445 } 446 447 collapsePreviews := "false" 448 if idx != 0 && rand.Intn(2) == 0 { 449 collapsePreviews = "true" 450 } 451 452 messageDisplay := "clean" 453 if idx != 0 && rand.Intn(2) == 0 { 454 messageDisplay = "compact" 455 } 456 457 channelDisplayMode := "full" 458 if idx != 0 && rand.Intn(2) == 0 { 459 channelDisplayMode = "centered" 460 } 461 462 // Some users has nickname 463 nickname := "" 464 if rand.Intn(5) == 0 { 465 nickname = fake.Company() 466 } 467 468 // sysadmin, user-1 and user-2 users skip tutorial steps 469 // Other half of users also skip tutorial steps 470 tutorialStep := "999" 471 if idx > 2 { 472 switch rand.Intn(6) { 473 case 1: 474 tutorialStep = "1" 475 case 2: 476 tutorialStep = "2" 477 case 3: 478 tutorialStep = "3" 479 } 480 } 481 482 teams := []app.UserTeamImportData{} 483 possibleTeams := []string{} 484 for teamName := range teamsAndChannels { 485 possibleTeams = append(possibleTeams, teamName) 486 } 487 sort.Strings(possibleTeams) 488 for x := 0; x < teamMemberships; x++ { 489 if len(possibleTeams) == 0 { 490 break 491 } 492 position := rand.Intn(len(possibleTeams)) 493 team := possibleTeams[position] 494 possibleTeams = append(possibleTeams[:position], possibleTeams[position+1:]...) 495 if teamChannels, err := teamsAndChannels[team]; err { 496 teams = append(teams, createTeamMembership(channelMemberships, teamChannels, &team, userType == GuestUser)) 497 } 498 } 499 500 var deleteAt int64 501 if userType == DeactivatedUser { 502 deleteAt = model.GetMillis() 503 } 504 505 user := app.UserImportData{ 506 ProfileImage: profileImage, 507 Username: &username, 508 Email: &email, 509 Password: &password, 510 Nickname: &nickname, 511 FirstName: &firstName, 512 LastName: &lastName, 513 Position: &position, 514 Roles: &roles, 515 Teams: &teams, 516 UseMilitaryTime: &useMilitaryTime, 517 CollapsePreviews: &collapsePreviews, 518 MessageDisplay: &messageDisplay, 519 ChannelDisplayMode: &channelDisplayMode, 520 TutorialStep: &tutorialStep, 521 DeleteAt: &deleteAt, 522 } 523 return app.LineImportData{ 524 Type: "user", 525 User: &user, 526 } 527 } 528 529 func createTeamMembership(numOfchannels int, teamChannels []string, teamName *string, guest bool) app.UserTeamImportData { 530 roles := "team_user" 531 if guest { 532 roles = "team_guest" 533 } else if rand.Intn(5) == 0 { 534 roles = "team_user team_admin" 535 } 536 channels := []app.UserChannelImportData{} 537 teamChannelsCopy := append([]string(nil), teamChannels...) 538 for x := 0; x < numOfchannels; x++ { 539 if len(teamChannelsCopy) == 0 { 540 break 541 } 542 position := rand.Intn(len(teamChannelsCopy)) 543 channelName := teamChannelsCopy[position] 544 teamChannelsCopy = append(teamChannelsCopy[:position], teamChannelsCopy[position+1:]...) 545 channels = append(channels, createChannelMembership(channelName, guest)) 546 } 547 548 return app.UserTeamImportData{ 549 Name: teamName, 550 Roles: &roles, 551 Channels: &channels, 552 } 553 } 554 555 func createChannelMembership(channelName string, guest bool) app.UserChannelImportData { 556 roles := "channel_user" 557 if guest { 558 roles = "channel_guest" 559 } else if rand.Intn(5) == 0 { 560 roles = "channel_user channel_admin" 561 } 562 favorite := rand.Intn(5) == 0 563 564 return app.UserChannelImportData{ 565 Name: &channelName, 566 Roles: &roles, 567 Favorite: &favorite, 568 } 569 } 570 571 func getSampleTeamName(idx int) string { 572 for { 573 name := fmt.Sprintf("%s-%d", fake.Word(), idx) 574 if !model.IsReservedTeamName(name) { 575 return name 576 } 577 } 578 } 579 580 func createTeam(idx int) app.LineImportData { 581 displayName := fake.Word() 582 name := getSampleTeamName(idx) 583 allowOpenInvite := rand.Intn(2) == 0 584 585 description := fake.Paragraph() 586 if len(description) > 255 { 587 description = description[0:255] 588 } 589 590 teamType := "O" 591 if rand.Intn(2) == 0 { 592 teamType = "I" 593 } 594 595 team := app.TeamImportData{ 596 DisplayName: &displayName, 597 Name: &name, 598 AllowOpenInvite: &allowOpenInvite, 599 Description: &description, 600 Type: &teamType, 601 } 602 return app.LineImportData{ 603 Type: "team", 604 Team: &team, 605 } 606 } 607 608 func createChannel(idx int, teamName string) app.LineImportData { 609 displayName := fake.Word() 610 name := fmt.Sprintf("%s-%d", fake.Word(), idx) 611 header := fake.Paragraph() 612 purpose := fake.Paragraph() 613 614 if len(purpose) > 250 { 615 purpose = purpose[0:250] 616 } 617 618 channelType := "P" 619 if rand.Intn(2) == 0 { 620 channelType = "O" 621 } 622 623 channel := app.ChannelImportData{ 624 Team: &teamName, 625 Name: &name, 626 DisplayName: &displayName, 627 Type: &channelType, 628 Header: &header, 629 Purpose: &purpose, 630 } 631 return app.LineImportData{ 632 Type: "channel", 633 Channel: &channel, 634 } 635 } 636 637 func createPost(team string, channel string, allUsers []string, createAt int64) app.LineImportData { 638 message := randomMessage(allUsers) 639 create_at := createAt 640 user := allUsers[rand.Intn(len(allUsers))] 641 642 // Some messages are flagged by an user 643 flagged_by := []string{} 644 if rand.Intn(10) == 0 { 645 flagged_by = append(flagged_by, allUsers[rand.Intn(len(allUsers))]) 646 } 647 648 reactions := []app.ReactionImportData{} 649 if rand.Intn(10) == 0 { 650 for { 651 reactions = append(reactions, randomReaction(allUsers, create_at)) 652 if rand.Intn(3) == 0 { 653 break 654 } 655 } 656 } 657 658 replies := []app.ReplyImportData{} 659 if rand.Intn(10) == 0 { 660 for { 661 replies = append(replies, randomReply(allUsers, create_at)) 662 if rand.Intn(4) == 0 { 663 break 664 } 665 } 666 } 667 668 post := app.PostImportData{ 669 Team: &team, 670 Channel: &channel, 671 User: &user, 672 Message: &message, 673 CreateAt: &create_at, 674 FlaggedBy: &flagged_by, 675 Reactions: &reactions, 676 Replies: &replies, 677 } 678 return app.LineImportData{ 679 Type: "post", 680 Post: &post, 681 } 682 } 683 684 func createDirectChannel(members []string) app.LineImportData { 685 header := fake.Sentence() 686 687 channel := app.DirectChannelImportData{ 688 Members: &members, 689 Header: &header, 690 } 691 return app.LineImportData{ 692 Type: "direct_channel", 693 DirectChannel: &channel, 694 } 695 } 696 697 func createDirectPost(members []string, createAt int64) app.LineImportData { 698 message := randomMessage(members) 699 create_at := createAt 700 user := members[rand.Intn(len(members))] 701 702 // Some messages are flagged by an user 703 flagged_by := []string{} 704 if rand.Intn(10) == 0 { 705 flagged_by = append(flagged_by, members[rand.Intn(len(members))]) 706 } 707 708 reactions := []app.ReactionImportData{} 709 if rand.Intn(10) == 0 { 710 for { 711 reactions = append(reactions, randomReaction(members, create_at)) 712 if rand.Intn(3) == 0 { 713 break 714 } 715 } 716 } 717 718 replies := []app.ReplyImportData{} 719 if rand.Intn(10) == 0 { 720 for { 721 replies = append(replies, randomReply(members, create_at)) 722 if rand.Intn(4) == 0 { 723 break 724 } 725 } 726 } 727 728 post := app.DirectPostImportData{ 729 ChannelMembers: &members, 730 User: &user, 731 Message: &message, 732 CreateAt: &create_at, 733 FlaggedBy: &flagged_by, 734 Reactions: &reactions, 735 Replies: &replies, 736 } 737 return app.LineImportData{ 738 Type: "direct_post", 739 DirectPost: &post, 740 } 741 }