public async Task ReplyLeaderboardAsync(ILeaderboard leaderboard) { List <string> lines = new List <string>(leaderboard.Select(item => { int rankWidth = leaderboard.Count().ToString().Length; int scoreWidth = leaderboard.Max(i => i.Score).ToString().Length; return(string.Format("**`{0}.`**{1}`{2}` {3}", item.Rank.ToString("0".PadRight(rankWidth, '0')), item.Icon, item.Score.ToString("0".PadRight(scoreWidth, '0')), string.Format(item.Rank <= 3 ? "**{0}**" : "{0}", string.IsNullOrEmpty(item.Name) ? "Results" : item.Name.ToTitle()) )); })); IEnumerable <Discord.Messaging.IEmbed> pages = EmbedUtilities.CreateEmbedPages(string.Empty, lines, itemsPerPage: 20, columnsPerPage: 1, options: EmbedPaginationOptions.AddPageNumbers); string title = leaderboard.Title; if (string.IsNullOrWhiteSpace(title)) { title = "Leaderboard"; } title = $"🏆 {title.ToTitle()} ({lines.Count()})"; foreach (Discord.Messaging.IEmbed page in pages) { page.Title = title; } await ReplyAsync(new PaginatedMessage(pages)); }
public async Task <IResponsiveMessageResponse> GetResponseAsync(ICommandContext context, Messaging.IMessage message, bool allowCancellation = true) { ResponsiveMessageInfo info = await SendMessageAndReturnInfoAsync(context, message, allowCancellation); IResponsiveMessageResponse response = new ResponseMessageResponse(null, true); if (info != null) { // Block until we get a response. info.Waiter = new ManualResetEvent(false); info.Waiter.WaitOne(); response = info.Response; if (response is null) { response = new ResponseMessageResponse(null, true); } if (response.Canceled) { await context.Channel.SendMessageAsync(embed : EmbedUtilities.BuildInfoEmbed("The command has been canceled.").ToDiscordEmbed()); } } return(response); }
public async Task Roles(string arg0) { // Possible cases: // 1. <role> // 2. <species> // If a role with this name exists, that's what we'll prioritize (users can use the genus + species overload if they need to). // If no such role exists, check for a species with this name instead. Common.Roles.IRole role = await Db.GetRoleAsync(arg0); if (role.IsValid()) { // The role is valid. // List all extant species with this role. IEnumerable <ISpecies> species = (await Db.GetSpeciesAsync(role)) .Where(s => !s.IsExtinct()) .OrderBy(s => s.GetShortName()); IEnumerable <Discord.Messaging.IEmbed> pages = EmbedUtilities.CreateEmbedPages($"Extant species with this role ({species.Count()}):", species, options: EmbedPaginationOptions.AddPageNumbers); foreach (Discord.Messaging.IEmbed page in pages) { page.Title = $"Role: {role.GetName()}"; page.Description = role.GetDescriptionOrDefault(); } await ReplyAsync(new Discord.Messaging.PaginatedMessage(pages)); } else { // The role is not valid. IEnumerable <ISpecies> matchingSpecies = await Db.GetSpeciesAsync(string.Empty, arg0); if (matchingSpecies.Count() > 0) { ISpecies species = await ReplyValidateSpeciesAsync(matchingSpecies); if (species.IsValid()) { await ReplyRolesAsync(matchingSpecies.First()); } } else { // There were no matching species, so just say that the role is invalid. await this.ReplyValidateRoleAsync(role); } } }
public EmbedBuilder CreateResponseEmbed(ResponseFormat format = ResponseFormat.Default, string title = "", string description = "", bool hasDefaultFooter = true) { var builder = EmbedUtilities.CreateResponseEmbed(format, title, description); var defaultFooter = new EmbedFooterBuilder() .WithIconUrl(Context.User.GetAvatarUrl()) .WithText($"{(Context.User as IGuildUser).Nickname}#{Context.User.Discriminator}"); if (hasDefaultFooter) { builder.WithFooter(defaultFooter); } return(builder); }
private async Task ReadyAsync() { foreach (IGuild guild in Client.Guilds) { await OnLogAsync(LogSeverity.Info, Name, $"Joined {guild.Name} ({guild.Id})"); } if (restartChannel != null && restartMessage != null) { ulong restartMessageId = restartMessage.Id; ulong restartChannelId = restartChannel.Id; // Attempt to get the channel the bot was restarted from. restartChannel = Client.GetChannel(restartChannelId) as IMessageChannel; if (restartChannel is null) { restartChannel = await Client.GetDMChannelAsync(restartChannelId) as IMessageChannel; } // Attempt to get the confirmation message. if (restartChannel != null) { restartMessage = await restartChannel.GetMessageAsync(restartMessageId) as IUserMessage; } else { restartMessage = null; } if (restartMessage != null) { // Modify the confirmation message. await restartMessage.ModifyAsync(async m => { m.Embed = EmbedUtilities.BuildSuccessEmbed($"Restarting {Name.ToBold()}... and we're back!").ToDiscordEmbed(); await Task.CompletedTask; }); } } restartChannel = null; restartMessage = null; }
public async Task Prey([Remainder] string speciesName) { speciesName = StringUtilities.StripOuterQuotes(speciesName); ISpecies species = await GetSpeciesOrReplyAsync(speciesName); if (species.IsValid()) { // Get the preyed-upon species. IEnumerable <IPredationInfo> preySpecies = (await Db.GetPreyAsync(species)) .OrderBy(info => info.Species.GetShortName()); if (preySpecies.Count() > 0) { List <string> lines = new List <string>(); foreach (IPredationInfo preyInfo in preySpecies) { string line = TaxonFormatter.GetString(preyInfo.Species); if (!string.IsNullOrEmpty(preyInfo.Notes)) { line += (string.Format(" ({0})", preyInfo.Notes.ToLowerInvariant())); } lines.Add(line); } string title = string.Format("Species preyed upon by {0} ({1})", TaxonFormatter.GetString(species, false), preySpecies.Count()); IEnumerable <Discord.Messaging.IEmbed> pages = EmbedUtilities.CreateEmbedPages(string.Empty, lines, columnsPerPage: 2, options: EmbedPaginationOptions.AddPageNumbers); Discord.Messaging.IPaginatedMessage message = new Discord.Messaging.PaginatedMessage(pages); message.SetTitle(title); await ReplyAsync(message); } else { await ReplyInfoAsync(string.Format("**{0}** does not prey upon any other species.", species.GetShortName())); } } }
// Private members private async Task ReplySpeciesAddedByAsync(ICreator creator, string thumbnailUrl, IEnumerable <ISpecies> species) { if (species.Count() <= 0) { await ReplyInfoAsync($"**{creator}** has not submitted any species yet."); } else { IEnumerable <Discord.Messaging.IEmbed> pages = EmbedUtilities.CreateEmbedPages($"Species owned by {creator} ({species.Count()})", species, options: EmbedPaginationOptions.AddPageNumbers); foreach (Discord.Messaging.IEmbed page in pages) { page.ThumbnailUrl = thumbnailUrl; } await ReplyAsync(new Discord.Messaging.PaginatedMessage(pages)); } }
public static async Task <IEnumerable <Discord.Messaging.IEmbed> > ZonesToEmbedPagesAsync(int existingLength, IEnumerable <IZone> zones, SQLiteDatabase database, bool showIcon = true) { List <string> lines = new List <string>(); int zones_per_page = 20; int maxLineLength = Math.Min(showIcon ? 78 : 80, (DiscordUtils.MaxEmbedLength - existingLength) / zones_per_page); foreach (IZone zone in zones) { IZoneType type = await database.GetZoneTypeAsync(zone.TypeId); StringBuilder lineBuilder = new StringBuilder(); if (showIcon) { lineBuilder.Append((type ?? new ZoneType()).Icon); lineBuilder.Append(" "); } lineBuilder.Append(zone.GetFullName().ToBold()); if (!string.IsNullOrWhiteSpace(zone.Description)) { lineBuilder.Append("\t—\t"); lineBuilder.Append(zone.Description.GetFirstSentence()); } string line = lineBuilder.ToString(); if (line.Length > maxLineLength) { line = line.Substring(0, maxLineLength - 3) + "..."; } lines.Add(line); } return(EmbedUtilities.CreateEmbedPages(string.Empty, lines, itemsPerPage: 20, columnsPerPage: 1, options: EmbedPaginationOptions.AddPageNumbers)); }
public async Task ReplyTaxonAsync(TaxonRankType rank) { // List all taxa of the given rank. IEnumerable <ITaxon> taxa = (await Db.GetTaxaAsync(rank)).OrderBy(t => t.GetName()); List <string> lines = new List <string>(); foreach (ITaxon taxon in taxa) { // Count the number of items under this taxon. int subtaxaCount = (await Db.GetSubtaxaAsync(taxon)).Count(); if (subtaxaCount > 0) { lines.Add($"{taxon.GetName().ToTitle()} ({subtaxaCount})"); } } if (lines.Count() <= 0) { await ReplyInfoAsync($"No {rank.GetName(true)} have been added yet."); } else { string title = $"All {rank.GetName(true)} ({lines.Count()})"; IEnumerable <Discord.Messaging.IEmbed> pages = EmbedUtilities.CreateEmbedPages(title.ToTitle(), lines, options: EmbedPaginationOptions.AddPageNumbers); foreach (Discord.Messaging.IEmbed page in pages) { page.Footer += $" — Empty {rank.GetName(true)} are not listed."; } await ReplyAsync(new PaginatedMessage(pages)); } }
public static async Task <IPaginatedMessage> BuildRecentEventsMessageAsync(this OfcModuleBase moduleBase, DateTimeOffset start, DateTimeOffset end) { IEnumerable <ISpecies> newSpecies = (await moduleBase.Db.GetSpeciesAsync(start, end)).OrderBy(species => moduleBase.TaxonFormatter.GetString(species, false)); IEnumerable <ISpecies> extinctSpecies = (await moduleBase.Db.GetExtinctSpeciesAsync(start, end)).OrderBy(species => moduleBase.TaxonFormatter.GetString(species, false)); List <IEmbed> pages = new List <IEmbed>(); if (newSpecies.Count() > 0) { EmbedUtilities.AppendEmbedPages(pages, EmbedUtilities.CreateEmbedPages($"New species ({newSpecies.Count()})", newSpecies.Select(species => moduleBase.TaxonFormatter.GetString(species)))); } if (extinctSpecies.Count() > 0) { EmbedUtilities.AppendEmbedPages(pages, EmbedUtilities.CreateEmbedPages($"Extinctions ({extinctSpecies.Count()})", extinctSpecies.Select(species => moduleBase.TaxonFormatter.GetString(species, false)))); } EmbedUtilities.AddPageNumbers(pages); if (pages.Count() <= 0) { pages.Add(new Embed() { Description = "No events" }); } foreach (IEmbed page in pages) { page.Title = $"Recent events ({DateUtilities.GetTimeSpanString(end - start)})"; } IPaginatedMessage message = new PaginatedMessage(pages); return(message); }
private async Task ShowZoneAsync(IZone zone) { if (await this.ReplyValidateZoneAsync(zone)) { // Get all species living in this zone. List <ISpecies> speciesList = new List <ISpecies>((await Db.GetSpeciesAsync(zone)).Where(species => !species.IsExtinct())); speciesList.Sort((lhs, rhs) => TaxonFormatter.GetString(lhs, false).CompareTo(TaxonFormatter.GetString(rhs, false))); // Starting building a paginated message. // The message will have a paginated species list, and a toggle button to display the species sorted by role. string description = zone.GetDescriptionOrDefault(); if (!speciesList.Any()) { description += "\n\nThis zone does not contain any species."; } List <IEmbed> embedPages = new List <IEmbed>(); if (zone.Fields.Any()) { embedPages.Add(new Embed()); foreach (IZoneField field in zone.Fields) { if (!string.IsNullOrWhiteSpace(field.GetName()) && !string.IsNullOrWhiteSpace(field.GetValue())) { embedPages.Last().AddField(field.GetName(), field.GetValue(), true); } } embedPages.Last().Description = description; } embedPages.AddRange(EmbedUtilities.CreateEmbedPages(string.Format("Extant species in this zone ({0}):", speciesList.Count()), speciesList, formatter: TaxonFormatter)); // Add title, decription, etc., to all pages. if (!embedPages.Any()) { embedPages.Add(new Embed()); } IZoneType type = await Db.GetZoneTypeAsync(zone.TypeId) ?? new ZoneType(); string aliases = zone.Aliases.Any() ? string.Format("({0})", string.Join(", ", zone.Aliases.Select(alias => alias.ToTitle()))) : string.Empty; string title = string.Format("{0} {1} {2}", type.Icon, zone.GetFullName(), aliases).Trim(); System.Drawing.Color color = type.Color; foreach (IEmbed page in embedPages) { page.Title = title; page.ThumbnailUrl = zone.Pictures.FirstOrDefault()?.Url; page.Color = color; // Add the zone description to all pages if the zone doesn't have any fields (because the info page will be missing). if (!zone.Fields.Any()) { page.Description = description; } } IPaginatedMessage message = new PaginatedMessage(embedPages); message.AddPageNumbers(); // This page will have species organized by role. // Only bother with the role page if species actually exist in this zone. if (speciesList.Count() > 0) { IEmbed rolesPage = new Embed { Title = title, ThumbnailUrl = zone.GetPictureUrl(), Color = color }; Dictionary <string, List <ISpecies> > rolesMap = new Dictionary <string, List <ISpecies> >(); foreach (ISpecies species in speciesList) { IEnumerable <Common.Roles.IRole> roles_list = await Db.GetRolesAsync(species); if (roles_list.Count() <= 0) { if (!rolesMap.ContainsKey("no role")) { rolesMap["no role"] = new List <ISpecies>(); } rolesMap["no role"].Add(species); continue; } foreach (Common.Roles.IRole role in roles_list) { if (!rolesMap.ContainsKey(role.GetName())) { rolesMap[role.GetName()] = new List <ISpecies>(); } rolesMap[role.GetName()].Add(species); } } // Sort the list of species belonging to each role. foreach (List <ISpecies> i in rolesMap.Values) { i.Sort((lhs, rhs) => TaxonFormatter.GetString(lhs, false).CompareTo(TaxonFormatter.GetString(rhs, false))); } // Create a sorted list of keys so that the roles are in order. List <string> sorted_keys = new List <string>(rolesMap.Keys); sorted_keys.Sort(); foreach (string i in sorted_keys) { StringBuilder lines = new StringBuilder(); foreach (ISpecies j in rolesMap[i]) { lines.AppendLine(TaxonFormatter.GetString(j)); } rolesPage.AddField(string.Format("{0}s ({1})", StringUtilities.ToTitleCase(i), rolesMap[i].Count()), lines.ToString(), inline: true); } // Add the page to the builder. message.AddReaction("🇷", async(args) => { if (args.Emoji != "🇷") { return; } args.Message.PaginationEnabled = !args.ReactionAdded; if (args.ReactionAdded) { args.Message.CurrentPage = new Message() { Embed = rolesPage } } ; else { args.Message.CurrentPage = null; } await Task.CompletedTask; }); } await ReplyAsync(message); } }
public static void AddLines(this IPaginatedMessage message, string listTitle, IEnumerable <string> listItems, int itemsPerPage = EmbedUtilities.DefaultItemsPerPage, int columnsPerPage = EmbedUtilities.DefaultColumnsPerPage, EmbedPaginationOptions options = EmbedPaginationOptions.None) { message.AddPages(EmbedUtilities.CreateEmbedPages(listTitle, listItems, itemsPerPage, columnsPerPage, options)); }
public async Task Help([Remainder] string commandName) { ICommandHelpInfo helpInfo = await HelpService.GetCommandHelpInfoAsync(commandName.Trim()); await ReplyAsync(embed : EmbedUtilities.BuildCommandHelpInfoEmbed(helpInfo, Config).ToDiscordEmbed()); }
public async Task Help() { IEnumerable <ICommandHelpInfo> helpInfos = await HelpService.GetCommandHelpInfoAsync(Context); await ReplyAsync(embed : EmbedUtilities.BuildCommandHelpInfoEmbed(helpInfos, Config).ToDiscordEmbed()); }
public async Task EvaluateAsync([Remainder, Summary("Code to evaluate.")] string input) { var codeblockRegex = new Regex(@"((?<=^```[a-z]*?)\n[.\s\S]*(?=(\n)?```$))|((?<=^```)(.*)(?=```$))", RegexOptions.Compiled | RegexOptions.IgnoreCase); var matches = codeblockRegex.Matches(input); if (matches.Count is 0) { throw new ArgumentException("You need to wrap the code into a code block.", nameof(input)); } var code = matches.First().Value; var msg = await EmbedUtilities.EmbedResponseAsync(Context.Message, ResponseFormat.Default, "Evaluating..."); var globals = new EvaluationUtilities.EvaluationEnvironment() { Client = Context.Client, Context = Context, Message = Context.Message }; var scriptOptions = EvaluationUtilities.ScriptOptions; var compilationStopwatch = Stopwatch.StartNew(); var script = CSharpScript.Create(code, scriptOptions, typeof(EvaluationUtilities.EvaluationEnvironment)); var compiled = script.Compile(); compilationStopwatch.Stop(); if (compiled.Any(d => d.Severity == DiagnosticSeverity.Error)) { var errorCompilationTime = compilationStopwatch.ElapsedMilliseconds.ToString("#,##0"); var errorCompilationErrors = compiled.Length.ToString("#,##0"); var errorDescription = $"Compilation failed after {Format.Code($"{errorCompilationTime}ms")} " + $"with {errorCompilationErrors} error{(errorCompilationErrors != "1" ? "s" : "")}"; var errorEmbed = EmbedUtilities.CreateResponseEmbed(ResponseFormat.Error, description: errorDescription); foreach (var error in compiled.Take(3)) { var ls = error.Location.GetLineSpan(); var errorLine = ls.StartLinePosition.Line.ToString("#,##0"); var errorChar = ls.StartLinePosition.Character.ToString("#,##0"); errorEmbed.AddField( $"Error at {Format.Code($"{errorLine}, {errorChar}")}", Format.Code(error.GetMessage(), "ini"), false ); } if (compiled.Length > 3) { var errorsOmitted = (compiled.Length - 3).ToString("#,##0"); errorEmbed.AddField("Some errors omitted", $"{errorsOmitted} more errors not displayed.", false); } await msg.ModifyAsync(props => { props.Embed = errorEmbed.Build(); }); return; } Exception exception = null; ScriptState <object> state = null; var executionStopwatch = Stopwatch.StartNew(); try { state = await script.RunAsync(globals); exception = state.Exception; } catch (Exception ex) { exception = ex; } executionStopwatch.Stop(); if (exception != null) { var executionErrorTime = executionStopwatch.ElapsedMilliseconds.ToString("#,##0"); var description = $"Execution failed after {Format.Code($"{executionErrorTime}ms")}" + $"{exception.GetType()}: {Format.Code(exception.Message, "ini")}"; var embed = CreateResponseEmbed(ResponseFormat.Error, description: description); await Context.AnswerAsync(embed : embed.Build()); return; } // Execution Succeeded var compilationTime = compilationStopwatch.ElapsedMilliseconds.ToString("#,##0"); var executionTime = executionStopwatch.ElapsedMilliseconds.ToString("#,##0"); var result = state.ReturnValue != null?state.ReturnValue.ToString() : "No value returned."; var successEmbed = CreateResponseEmbed(ResponseFormat.Success) .WithDescription($"Took {Format.Code($"{compilationTime}ms")} to compile, " + $"{Format.Code($"{executionTime}ms")} to execute.") .AddField("Result", Format.Code(result, "json"), false) .WithFooter(new EmbedFooterBuilder().WithText($"· react with ❌ to delete.")); if (state.ReturnValue != null) { successEmbed.AddField("Return Type", Format.Code(state.ReturnValue.GetType().ToString()), true); } await msg.ModifyAsync(props => { props.Embed = successEmbed.Build(); }); _ = Interactivity.NextReactionAsync(actions: (r, _) => { if (r.Emote.Name is "❌") { return(msg.DeleteAsync()); } else { return(Task.CompletedTask); } }); }
public static void AddPageNumbers(this IPaginatedMessage message) { EmbedUtilities.AddPageNumbers(message.Select(page => page.Embed).Where(embed => embed != null)); }
public async Task HandleCommandResult(ShadeContext context, IResult result) { if (result.IsSuccess) { return; } _logger.Error(result.ErrorReason); switch (result.Error) { case CommandError.UnknownCommand: { // Fail silently. } break; case CommandError.ParseFailed: { await EmbedUtilities.EmbedResponseAsync(context.Message, ResponseFormat.Error, description : result.ErrorReason); } break; case CommandError.BadArgCount: { await EmbedUtilities.EmbedResponseAsync(context.Message, ResponseFormat.Error, description : result.ErrorReason); } break; case CommandError.ObjectNotFound: { await EmbedUtilities.EmbedResponseAsync(context.Message, ResponseFormat.Error, description : result.ErrorReason); } break; case CommandError.MultipleMatches: { await EmbedUtilities.EmbedResponseAsync(context.Message, ResponseFormat.Error, description : result.ErrorReason); } break; case CommandError.UnmetPrecondition: { await EmbedUtilities.EmbedResponseAsync(context.Message, ResponseFormat.Error, description : result.ErrorReason); } break; case CommandError.Exception: { await EmbedUtilities.EmbedResponseAsync(context.Message, ResponseFormat.Error, description : result.ErrorReason); } break; default: case CommandError.Unsuccessful: { await EmbedUtilities.EmbedResponseAsync(context.Message, ResponseFormat.Error, description : result.ErrorReason); } break; } }
public async Task <IPaginatedMessage> BuildSpeciesMessageAsync(ISpecies species) { if (!species.IsValid()) { return(null); } Discord.Messaging.Embed embed = new Discord.Messaging.Embed { Title = species.GetFullName() }; if (species.CommonNames.Count() > 0) { embed.Title += string.Format(" ({0})", string.Join(", ", species.CommonNames.Select(name => name.ToTitle()))); } if (Config.GenerationsEnabled) { // Add a field for the generation. IGeneration gen = await Db.GetGenerationByDateAsync(species.CreationDate); embed.AddField("Gen", gen is null ? "???" : gen.Number.ToString(), inline: true); } // Add a field for the species owner. embed.AddField("Owner", await GetCreatorAsync(species.Creator), inline: true); // Add a field for the species' zones. IEnumerable <ISpeciesZoneInfo> speciesZoneList = (await Db.GetZonesAsync(species)) .Where(info => !info.Zone.Flags.HasFlag(ZoneFlags.Retired)); string zonesFieldValue = speciesZoneList.ToString(ZoneListToStringOptions.Default, DiscordUtilities.MaxFieldLength); embed.AddField("Zone(s)", string.IsNullOrEmpty(zonesFieldValue) ? "None" : zonesFieldValue, inline: true); // Add the species' description. StringBuilder descriptionBuilder = new StringBuilder(); if (species.IsExtinct()) { embed.Title = "[EXTINCT] " + embed.Title; if (!string.IsNullOrEmpty(species.Status.Reason)) { descriptionBuilder.AppendLine(string.Format("**Extinct ({0}):** _{1}_\n", await BotUtils.TimestampToDateStringAsync(DateUtilities.GetTimestampFromDate((DateTimeOffset)species.Status.Date), BotContext), species.Status.Reason)); } } descriptionBuilder.Append(species.GetDescriptionOrDefault()); embed.Description = descriptionBuilder.ToString(); // Add the species' picture. embed.ThumbnailUrl = species.GetPictureUrl(); if (!string.IsNullOrEmpty(Config.WikiUrlFormat)) { // Discord automatically encodes certain characters in URIs, which doesn't allow us to update the config via Discord when we have "{0}" in the URL. // Replace this with the proper string before attempting to call string.Format. string format = Config.WikiUrlFormat.Replace("%7B0%7D", "{0}"); embed.Url = string.Format(format, Uri.EscapeUriString(WikiUtilities.GetWikiPageTitle(species))); } // Create embed pages. IEnumerable <Discord.Messaging.IEmbed> embedPages = EmbedUtilities.CreateEmbedPages(embed, EmbedPaginationOptions.AddPageNumbers | EmbedPaginationOptions.CopyFields); IPaginatedMessage paginatedMessage = new PaginatedMessage(embedPages); if (speciesZoneList.Count() > 0) { paginatedMessage.SetColor((await Db.GetZoneTypeAsync(speciesZoneList.GroupBy(x => x.Zone.TypeId).OrderBy(x => x.Count()).Last().Key)).Color); } if (species.IsExtinct()) { paginatedMessage.SetColor(Color.Red); } return(paginatedMessage); }
protected async Task <IUserMessage> ReplyNewEmbed(string text, Color color) { var embed = EmbedUtilities.CreateEmbed(text, color); return(await ReplyAsync(embed : embed)); }
public async Task <IPaginatedMessage> BuildTaxonMessageAsync(ITaxon taxon) { if (!taxon.IsValid()) { return(null); } List <string> subItems = new List <string>(); if (taxon.Rank.Type == TaxonRankType.Species) { ISpecies species = await Db.GetSpeciesAsync(taxon.Id); return(await BuildSpeciesMessageAsync(species)); } else if (taxon.Rank.Type == TaxonRankType.Genus) { // For genera, get all species underneath it. // This will let us check if the species is extinct, and cross it out if that's the case. List <ISpecies> speciesList = new List <ISpecies>(); foreach (ITaxon subtaxon in await Db.GetSubtaxaAsync(taxon)) { speciesList.Add(await Db.GetSpeciesAsync(subtaxon.Id)); } speciesList.Sort((lhs, rhs) => TaxonFormatter.GetString(lhs, false).CompareTo(TaxonFormatter.GetString(rhs, false))); foreach (ISpecies species in speciesList.Where(s => s.IsValid())) { subItems.Add(TaxonFormatter.GetString(species)); } } else { // Get all subtaxa under this taxon. IEnumerable <ITaxon> subtaxa = await Db.GetSubtaxaAsync(taxon); // Add all subtaxa to the list. foreach (ITaxon subtaxon in subtaxa) { if (subtaxon.Rank.Type == TaxonRankType.Species) { // Do not attempt to count sub-taxa for species. subItems.Add(subtaxon.GetName()); } else { // Count the number of species under this taxon. // Taxa with no species under them will not be displayed. long speciesCount = await Db.GetSpeciesCountAsync(subtaxon); if (speciesCount > 0) { // Count the sub-taxa under this taxon. long subtaxaCount = (await Db.GetSubtaxaAsync(subtaxon)).Count(); // Add the taxon to the list. if (subtaxaCount > 0) { subItems.Add(string.Format("{0} ({1})", subtaxon.GetName(), subtaxaCount)); } } } } } // Generate embed pages. string title = taxon.CommonNames.Count() <= 0 ? taxon.GetName() : string.Format("{0} ({1})", taxon.GetName(), taxon.GetCommonName()); string fieldTitle = string.Format("{0} in this {1} ({2}):", taxon.GetChildRank().GetName(true).ToTitle(), taxon.GetRank().GetName().ToLowerInvariant(), subItems.Count()); string thumbnailUrl = taxon.GetPictureUrl(); StringBuilder description = new StringBuilder(); description.AppendLine(taxon.GetDescriptionOrDefault()); if (subItems.Count() <= 0) { description.AppendLine(); description.AppendLine(string.Format("This {0} contains no {1}.", taxon.GetRank().GetName(), taxon.GetChildRank().GetName(true))); } List <Discord.Messaging.IEmbed> pages = new List <Discord.Messaging.IEmbed>(EmbedUtilities.CreateEmbedPages(fieldTitle, subItems, options: EmbedPaginationOptions.AddPageNumbers)); if (!pages.Any()) { pages.Add(new Discord.Messaging.Embed()); } IPaginatedMessage paginatedMessage = new PaginatedMessage(pages); foreach (Discord.Messaging.IEmbed page in paginatedMessage.Select(m => m.Embed)) { page.Title = title; page.ThumbnailUrl = thumbnailUrl; page.Description = description.ToString(); if (subItems.Count() > 0 && taxon.GetRank() != TaxonRankType.Genus) { page.Footer += string.Format(" — Empty {0} are not listed.", taxon.GetChildRank().GetName(true)); } } return(paginatedMessage); }