Wycademy/src/Wycademy/Commands/Services/MonsterInfoService.cs
/// <summary>
/// Gets the data of a certain category for a monster from all applicable games.
/// </summary>
/// <param name="category">What category of monster data should be used.</param>
/// <param name="name">The name of the monster. All lowercase, with words separated by hyphens.</param>
/// <param name="context">The database context that should be queried.</param>
/// <param name="include">The function used to join the table that will be used for building results.</param>
/// <returns>Dictionary of string, (string, int?). The key represents the game, and the value is a tuple, with the first item representing the table and the second representing the split point for multiple messages.</returns>
Gets the data of a certain category for a monster from all applicable games.
Gets the data of a certain category for a monster from all applicable games .
public async Task<Dictionary<string, (string, int?)>> GetMonsterInfo(MonsterDataCategory category, string name, MonsterContext context, Expression<Func<Monster, IEnumerable<IMonsterData>>> include)
        {
            var monster = await context.Monsters
                            .Include(include)
                            .SingleAsync(m => m.WebName == name);
            string[] columnTitles;
            IEnumerable<IMonsterData> data;
            switch (category)
            {
                case MonsterDataCategory.Hitzone:
                    data = monster.Hitzones;
                    columnTitles = HITZONE_COLUMN_NAMES;
                    break;
                case MonsterDataCategory.Status:
                    data = monster.Status;
                    columnTitles = STATUS_COLUMN_NAMES;
                    break;
                case MonsterDataCategory.Item:
                    data = monster.Items;
                    columnTitles = ITEMEFFECTS_COLUMN_NAMES;
                    break;
                case MonsterDataCategory.Stagger:
                    data = monster.Stagger;
                    columnTitles = STAGGER_COLUMN_NAMES;
                    break;
                default:
                    throw new ArgumentException($"Invalid Category: {category}");
            }
            var games = data.Select(d => d.Game).Distinct();
            Dictionary<string, (string, int?)> pages;
            if (games.Count() == 1)
            {
                pages = new Dictionary<string, (string, int?)>() { { games.First(), BuildTable(data, columnTitles) } };
            }
            else
            {
                var grouped = data.GroupBy(d => d.Game);
                pages = grouped.ToDictionary(g => g.Key, g => BuildTable(g.ToList(), columnTitles));
            }
            return pages;
            (string, int?) BuildTable(IEnumerable<IMonsterData> rows, string[] titles)
            {
                int columnTitleWidth = titles.Max(t => t.Length);
                int rowTitleWidth = rows.Max(d => d.Name.Length);
                var tableBuilder = new StringBuilder($"{rows.First().Game} {category} info for {monster.TitleName}:");
                tableBuilder.AppendLine("```");
                tableBuilder.Append(' ', rowTitleWidth);
                foreach (var title in titles)
                {
                    tableBuilder.Append("|" + PadCenter(title, columnTitleWidth));
                }
                tableBuilder.AppendLine();
                int? splitPoint = null;
                bool split = false;
                foreach (var row in rows)
                {
                    var rowBuilder = new StringBuilder();
                    rowBuilder.Append(row.Name.PadRight(rowTitleWidth));
                    foreach (var value in row.Values)
                    {
                        rowBuilder.Append("|" + PadCenter(value ?? "N/A", columnTitleWidth));
                    }
                    if (tableBuilder.Length + rowBuilder.Length >= 1990 && !split)
                    {
                        tableBuilder.AppendLine("```");
                        splitPoint = tableBuilder.Length;
                        split = true;
                        tableBuilder.AppendLine("```");
                    }
                    tableBuilder.AppendLine(rowBuilder.ToString());
                }
                tableBuilder.AppendLine("```");
                return (tableBuilder.ToString(), splitPoint);
            }
        }
public async Task < Dictionary < string , ( string , int ? ) > > GetMonsterInfo ( MonsterDataCategory category , string name , MonsterContext context , Expression < Func < Monster , IEnumerable < IMonsterData > > > include ) { var monster = await context . Monsters . Include ( include ) . SingleAsync ( m => m . WebName == name ) ; string [ ] columnTitles ; IEnumerable < IMonsterData > data ; switch ( category ) { case MonsterDataCategory . Hitzone : data = monster . Hitzones ; columnTitles = HITZONE_COLUMN_NAMES ; break ; case MonsterDataCategory . Status : data = monster . Status ; columnTitles = STATUS_COLUMN_NAMES ; break ; case MonsterDataCategory . Item : data = monster . Items ; columnTitles = ITEMEFFECTS_COLUMN_NAMES ; break ; case MonsterDataCategory . Stagger : data = monster . Stagger ; columnTitles = STAGGER_COLUMN_NAMES ; break ; default : throw new ArgumentException ( $" Invalid Category:  { category } " ) ; } var games = data . Select ( d => d . Game ) . Distinct ( ) ; Dictionary < string , ( string , int ? ) > pages ; if ( games . Count ( ) == 1 ) { pages = new Dictionary < string , ( string , int ? ) > ( ) { { games . First ( ) , BuildTable ( data , columnTitles ) } } ; } else { var grouped = data . GroupBy ( d => d . Game ) ; pages = grouped . ToDictionary ( g => g . Key , g => BuildTable ( g . ToList ( ) , columnTitles ) ) ; } return pages ; ( string , int ? ) BuildTable ( IEnumerable < IMonsterData > rows , string [ ] titles ) { int columnTitleWidth = titles . Max ( t => t . Length ) ; int rowTitleWidth = rows . Max ( d => d . Name . Length ) ; var tableBuilder = new StringBuilder ( $" { rows . First ( ) . Game } { category }  info for  { monster . TitleName } : " ) ; tableBuilder . AppendLine ( " " ) ; tableBuilder . Append ( ' ' , rowTitleWidth ) ; foreach ( var title in titles ) { tableBuilder . Append ( " " + PadCenter ( title , columnTitleWidth ) ) ; } tableBuilder . AppendLine ( ) ; int ? splitPoint = null ; bool split = false ; foreach ( var row in rows ) { var rowBuilder = new StringBuilder ( ) ; rowBuilder . Append ( row . Name . PadRight ( rowTitleWidth ) ) ; foreach ( var value in row . Values ) { rowBuilder . Append ( " " + PadCenter ( value ?? " " , columnTitleWidth ) ) ; } if ( tableBuilder . Length + rowBuilder . Length >= 1990 && ! split ) { tableBuilder . AppendLine ( " " ) ; splitPoint = tableBuilder . Length ; split = true ; tableBuilder . AppendLine ( " " ) ; } tableBuilder . AppendLine ( rowBuilder . ToString ( ) ) ; } tableBuilder . AppendLine ( " " ) ; return ( tableBuilder . ToString ( ) , splitPoint ) ; } }
Gets the data of a certain category for a monster from all applicable games.
Gets the data of a certain category for a monster from all applicable games .
// Get the monster matching the input name, joined with its hitzones/status/items/stagger data using the supplied function. // Get the data depending on the category requested. // Find how many different games are included in the result set. // Create the string builder with the table's title, and open the code block. // Put blank spaces in the upper left corner. // Add all the column titles. // Add the name, plus blank spaces to keep the columns aligned. // Add all the values. // If adding the row will bring the character count over 1990 characters (limit is 2000), mark the point where the table should be split into two messages. // Close the first code block. // Get the index of the split. // Open the second code block.
Dictionary of string, (string, int?). The key represents the game, and the value is a tuple, with the first item representing the table and the second representing the split point for multiple messages.
Dictionary of string ( string int? ) . The key represents the game and the value is a tuple with the first item representing the table and the second representing the split point for multiple messages .
category
MonsterDataCategory
None
name
string
The name of the monster. All lowercase, with words separated by hyphens.
The name of the monster . All lowercase with words separated by hyphens .
context
MonsterContext
The database context that should be queried.
The database context that should be queried .
include
Expression<Func<Monster, IEnumerable<IMonsterData>>>
The function used to join the table that will be used for building results.
The function used to join the table that will be used for building results .