using Microsoft.AspNetCore.Identity; using NTDLS.Helpers; using System.Security.Claims; using System.Text; using ZelWiki.Engine.Implementation; using ZelWiki.Engine.Library.Interfaces; using ZelWiki.Library; using ZelWiki.Models; using ZelWiki.Models.DataModels; using ZelWiki.Repository; namespace DummyPageGenerator { internal class PageGenerator { private readonly object _lockObject = new(); private List _pagePool; private readonly Random _random; private readonly List _namespaces; private readonly List _tags; private readonly List _fileNames; private List _recentPageNames = new(); private readonly UserManager _userManager; private readonly List _users; public List Users => _users; public Random Random => _random; public PageGenerator(UserManager userManager) { _userManager = userManager; _random = new Random(); _namespaces = PageRepository.GetAllNamespaces(); _tags = WordsRepository.GetRandomWords(250); _fileNames = WordsRepository.GetRandomWords(50); _pagePool = PageRepository.GetAllPages(); if (_namespaces.Count < 250) { _namespaces.AddRange(WordsRepository.GetRandomWords(250)); } _users = UsersRepository.GetAllUsers(); if (_users.Count < 1124) { for (int i = 0; i < 1124 - _users.Count; i++) { string emailAddress = WordsRepository.GetRandomWords(1).First() + "@" + WordsRepository.GetRandomWords(1).First() + ".com"; CreateUserAndProfile(emailAddress); } _users = UsersRepository.GetAllUsers(); } } public static string GetRandomUnusedAccountName() { while (true) { var randomAccountName = string.Join(" ", WordsRepository.GetRandomWords(2)); if (UsersRepository.DoesProfileAccountExist(Navigation.Clean(randomAccountName)) == false) { return randomAccountName; } } } /// /// Creates a user and the associated profile with claims and such. /// /// /// public void CreateUserAndProfile(string emailAddress) { var user = new IdentityUser() { UserName = emailAddress, Email = emailAddress }; var result = _userManager.CreateAsync(user, WordsRepository.GetRandomWords(1).First() + Guid.NewGuid().ToString()).Result; if (!result.Succeeded) { throw new Exception(string.Join("\r\n", result.Errors.Select(o => o.Description))); } var userId = _userManager.GetUserIdAsync(user).Result; var membershipConfig = ConfigurationRepository.GetConfigurationEntryValuesByGroupName("Membership"); UsersRepository.CreateProfile(Guid.Parse(userId), GetRandomUnusedAccountName()); var claimsToAdd = new List { new (ClaimTypes.Role, membershipConfig.Value("Default Signup Role").EnsureNotNull()), new ("timezone", membershipConfig.Value("Default TimeZone").EnsureNotNull()), new (ClaimTypes.Country, membershipConfig.Value("Default Country").EnsureNotNull()), new ("language", membershipConfig.Value("Default Language").EnsureNotNull()), }; SecurityRepository.UpsertUserClaims(_userManager, user, claimsToAdd); } /// /// Creates a paragraph/sentence structure. /// /// /// private string GetParagraph(int words) { using var client = new HttpClient(); var response = client.GetAsync($"https://textsauce.com/api/Paragraph/English/{words}").Result; response.EnsureSuccessStatusCode(); return response.Content.ReadAsStringAsync().Result; } /// /// Creates a paragraph/sentence structure with links and wiki markup. /// /// /// private string GenerateWikiParagraph(int wordCount) { var paragraph = GetParagraph(wordCount); var tokens = paragraph.Split(' '); int replacementCount = _random.Next(2, 10); for (int r = 0; r < replacementCount; r++) { var token = tokens[_random.Next(0, tokens.Length)]; switch (_random.Next(0, 7)) { case 2: //Dead link. paragraph = paragraph.Replace(token, $"[[{token}]]"); break; case 4: //Wiki markup. paragraph = paragraph.Replace(token, AddWikiMarkup(token)); break; case 6: //Good link. var recentPage = GetRandomRecentPageName(); if (recentPage != null) { paragraph = paragraph.Replace(token, $"[[{recentPage}]]"); } break; } } return paragraph; } private string? GetRandomRecentPageName() { lock (_pagePool) { if (_recentPageNames.Count == 0) { return null; } if (_recentPageNames.Count > 200) //Shuffle and limit the recent page names. { _recentPageNames = ShuffleList(_recentPageNames).Take(100).ToList(); } return _recentPageNames[_random.Next(0, _recentPageNames.Count)]; } } private List GetRandomRecentPageNames(int count) { lock (_pagePool) { if (_recentPageNames.Count > 200) //Shuffle and limit the recent page names. { _recentPageNames = ShuffleList(_recentPageNames).Take(100).ToList(); } var pageNames = new List(); for (int i = 0; i < count; i++) { pageNames.Add(_recentPageNames[_random.Next(0, _recentPageNames.Count)]); } return pageNames; } } private void AddRecentPageName(string pageName) { lock (_pagePool) { _recentPageNames.Add(pageName); } } /// /// Creates a random page on the wiki. /// /// public void GeneratePage(ITightEngine engine, Guid userId) { try { Console.WriteLine($"{userId} is creating a page."); var ns = _namespaces[_random.Next(_namespaces.Count)]; var pageName = ns + " :: " + string.Join(" ", WordsRepository.GetRandomWords(3)); AddRecentPageName(pageName); var body = new StringBuilder(); body.AppendLine($"##title ##Tag(" + string.Join(' ', ShuffleList(_tags).Take(_random.Next(1, 4))) + ")"); body.AppendLine($"##toc"); body.AppendLine($"==Overview"); body.AppendLine(GenerateWikiParagraph(_random.Next(50, 100))); body.AppendLine("\r\n"); body.AppendLine($"==Revision Section"); body.AppendLine($"This is here for the workload generator to easily modify the page."); body.AppendLine($"PLACEHOLDER_FOR_REVISION_TEXT_BEGIN\r\nPLACEHOLDER_FOR_REVISION_TEXT_END\r\n"); var textWithLinks = WordsRepository.GetRandomWords(_random.Next(5, 10)); textWithLinks.AddRange(GetRandomRecentPageNames(_random.Next(1, 2)).Select(o => $"[[{o}]]")); if (_random.Next(100) >= 95) { //Add dead links (missing pages). textWithLinks.AddRange(WordsRepository.GetRandomWords(_random.Next(1, 2)).Select(o => $"[[{o}]]")); } body.AppendLine($"==See Also"); body.AppendLine(string.Join(' ', ShuffleList(textWithLinks))); body.AppendLine("\r\n"); body.AppendLine($"==Related"); body.AppendLine($"##related"); body.AppendLine("\r\n"); var page = new Page() { Name = pageName, Body = body.ToString(), CreatedByUserId = userId, ModifiedByUserId = userId, CreatedDate = DateTime.UtcNow, ModifiedDate = DateTime.UtcNow, Description = string.Join(' ', WordsRepository.GetRandomWords(_random.Next(3, 5))), }; int newPageId = Helpers.UpsertPage(engine, page); if (_random.Next(100) >= 70) { var fileName = _fileNames[_random.Next(_fileNames.Count)] + ".txt"; ; var fileData = Encoding.UTF8.GetBytes(page.Body); AttachFile(newPageId, userId, fileName, fileData); } InsertPagePool(page); } catch (Exception ex) { Console.WriteLine(ex.Message); } } private Page GetRandomPage() { lock (_pagePool) { return _pagePool[_random.Next(0, _pagePool.Count)]; } } private void InsertPagePool(Page page) { lock (_pagePool) { _pagePool.Add(page); } } /// /// Modifies a random page on the wiki. /// /// public void ModifyRandomPages(ITightEngine engine, Guid userId) { var pageToModify = GetRandomPage(); Console.WriteLine($"{userId} is modifying page: {pageToModify.Id}."); AddRecentPageName(pageToModify.Name); string beginTag = "PLACEHOLDER_FOR_REVISION_TEXT_BEGIN"; string endTag = "PLACEHOLDER_FOR_REVISION_TEXT_END"; int beginIndex = pageToModify.Body.IndexOf(beginTag); int endIndex = pageToModify.Body.IndexOf(endTag); if (beginIndex > 0 && endIndex > beginIndex) { string topText = pageToModify.Body.Substring(0, beginIndex + beginTag.Length); string bottomText = pageToModify.Body.Substring(endIndex); pageToModify.Body = topText.Trim() + "\r\n" + GenerateWikiParagraph(_random.Next(10, 20)) + "\r\n" + bottomText.Trim(); pageToModify.ModifiedByUserId = userId; pageToModify.ModifiedByUserId = userId; Helpers.UpsertPage(engine, pageToModify); if (_random.Next(100) >= 90) { var fileName = _fileNames[_random.Next(_fileNames.Count)] + ".txt"; var fileData = Encoding.UTF8.GetBytes(pageToModify.Body); AttachFile(pageToModify.Id, userId, fileName, fileData); } } } /// /// Attaches a file to a wiki page. /// /// /// /// /// private void AttachFile(int pageId, Guid userId, string fileName, byte[] fileData) { if (fileData.Length > GlobalConfiguration.MaxAttachmentFileSize) { throw new Exception("Could not save the attached file, too large"); } PageFileRepository.UpsertPageFile(new PageFileAttachment() { Data = fileData, CreatedDate = DateTime.UtcNow, PageId = pageId, Name = fileName, FileNavigation = Navigation.Clean(fileName), Size = fileData.Length, ContentType = Utility.GetMimeType(fileName) }, userId); } /// /// Returns a shuffled version of the input list. /// /// /// /// private List ShuffleList(List list) { var newList = new List(list); var rand = new Random(); int n = newList.Count; while (n > 1) { n--; int k = _random.Next(n + 1); T value = newList[k]; newList[k] = newList[n]; newList[n] = value; } return newList; } /// /// Adds some random wiki text to a word. /// /// /// private string AddWikiMarkup(string text) { switch (_random.Next(0, 5)) { case 1: return $"//{text}//"; case 2: return $"~~{text}~~"; case 3: return $"__{text}__"; case 4: return $"!!{text}!!"; default: return text; } } } }