Refactor !rank, add HighscoreManager and add /v1/highscore to the api
This commit is contained in:
parent
a5c70859a4
commit
99245b9ead
15 changed files with 261 additions and 109 deletions
|
@ -5,31 +5,30 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.net.Database;
|
||||
using Geekbot.net.Lib.AlmostRedis;
|
||||
using Geekbot.net.Lib.Converters;
|
||||
using Geekbot.net.Lib.ErrorHandling;
|
||||
using Geekbot.net.Lib.Extensions;
|
||||
using Geekbot.net.Lib.Highscores;
|
||||
using Geekbot.net.Lib.UserRepository;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace Geekbot.net.Commands.User.Ranking
|
||||
{
|
||||
public class Rank : ModuleBase
|
||||
{
|
||||
private readonly IEmojiConverter _emojiConverter;
|
||||
private readonly IHighscoreManager _highscoreManager;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IAlmostRedis _redis;
|
||||
|
||||
public Rank(DatabaseContext database, IErrorHandler errorHandler, IUserRepository userRepository,
|
||||
IEmojiConverter emojiConverter, IAlmostRedis redis)
|
||||
IEmojiConverter emojiConverter, IHighscoreManager highscoreManager)
|
||||
{
|
||||
_database = database;
|
||||
_errorHandler = errorHandler;
|
||||
_userRepository = userRepository;
|
||||
_emojiConverter = emojiConverter;
|
||||
_redis = redis;
|
||||
_highscoreManager = highscoreManager;
|
||||
}
|
||||
|
||||
[Command("rank", RunMode = RunMode.Async)]
|
||||
|
@ -38,10 +37,10 @@ namespace Geekbot.net.Commands.User.Ranking
|
|||
{
|
||||
try
|
||||
{
|
||||
RankType type;
|
||||
HighscoreTypes type;
|
||||
try
|
||||
{
|
||||
type = Enum.Parse<RankType>(typeUnformated.ToLower());
|
||||
type = Enum.Parse<HighscoreTypes>(typeUnformated.ToLower());
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -49,7 +48,6 @@ namespace Geekbot.net.Commands.User.Ranking
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
var replyBuilder = new StringBuilder();
|
||||
if (amount > 20)
|
||||
{
|
||||
|
@ -57,69 +55,28 @@ namespace Geekbot.net.Commands.User.Ranking
|
|||
amount = 20;
|
||||
}
|
||||
|
||||
Dictionary<ulong, int> list;
|
||||
|
||||
switch (type)
|
||||
var guildId = Context.Guild.Id;
|
||||
Dictionary<HighscoreUserDto, int> highscoreUsers;
|
||||
try
|
||||
{
|
||||
case RankType.messages:
|
||||
list = GetMessageList(amount);
|
||||
break;
|
||||
case RankType.karma:
|
||||
list = GetKarmaList(amount);
|
||||
break;
|
||||
case RankType.rolls:
|
||||
list = GetRollsList(amount);
|
||||
break;
|
||||
default:
|
||||
await ReplyAsync("Valid types are '`messages`' '`karma`', '`rolls`'");
|
||||
return;
|
||||
highscoreUsers = _highscoreManager.GetHighscoresWithUserData(type, guildId, amount);
|
||||
}
|
||||
|
||||
if (!list.Any())
|
||||
catch (HighscoreListEmptyException)
|
||||
{
|
||||
await ReplyAsync($"No {type} found on this server");
|
||||
return;
|
||||
}
|
||||
|
||||
int guildMessages = 0;
|
||||
if (type == RankType.messages)
|
||||
if (type == HighscoreTypes.messages)
|
||||
{
|
||||
// guildMessages = _database.Messages
|
||||
// .Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong()))
|
||||
// .Select(e => e.MessageCount)
|
||||
// .Sum();
|
||||
guildMessages = (int) _redis.Db.HashGet($"{Context.Guild.Id}:Messages", 0.ToString());
|
||||
guildMessages = _database.Messages
|
||||
.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong()))
|
||||
.Select(e => e.MessageCount)
|
||||
.Sum();
|
||||
}
|
||||
|
||||
var highscoreUsers = new Dictionary<RankUserDto, int>();
|
||||
var failedToRetrieveUser = false;
|
||||
foreach (var user in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
var guildUser = _userRepository.Get(user.Key);
|
||||
if (guildUser?.Username != null)
|
||||
{
|
||||
highscoreUsers.Add(new RankUserDto
|
||||
{
|
||||
Username = guildUser.Username,
|
||||
Discriminator = guildUser.Discriminator
|
||||
}, user.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
highscoreUsers.Add(new RankUserDto
|
||||
{
|
||||
Id = user.Key.ToString()
|
||||
}, user.Value);
|
||||
failedToRetrieveUser = true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
var failedToRetrieveUser = highscoreUsers.Any(e => string.IsNullOrEmpty(e.Key.Username));
|
||||
|
||||
if (failedToRetrieveUser) replyBuilder.AppendLine(":warning: I couldn't find all usernames. Maybe they left the server?\n");
|
||||
replyBuilder.AppendLine($":bar_chart: **{type.ToString().CapitalizeFirst()} Highscore for {Context.Guild.Name}**");
|
||||
|
@ -134,7 +91,7 @@ namespace Geekbot.net.Commands.User.Ranking
|
|||
? $"**{user.Key.Username}#{user.Key.Discriminator}**"
|
||||
: $"**{user.Key.Id}**");
|
||||
|
||||
replyBuilder.Append(type == RankType.messages
|
||||
replyBuilder.Append(type == HighscoreTypes.messages
|
||||
? $" - {user.Value} {type} - {Math.Round((double) (100 * user.Value) / guildMessages, digits: 2)}%\n"
|
||||
: $" - {user.Value} {type}\n");
|
||||
|
||||
|
@ -148,38 +105,5 @@ namespace Geekbot.net.Commands.User.Ranking
|
|||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<ulong, int> GetMessageList(int amount)
|
||||
{
|
||||
return _database.Messages
|
||||
.Where(k => k.GuildId.Equals(Context.Guild.Id.AsLong()))
|
||||
.OrderByDescending(o => o.MessageCount)
|
||||
.Take(amount)
|
||||
.ToDictionary(key => key.UserId.AsUlong(), key => key.MessageCount);
|
||||
// return _redis.Db
|
||||
// .HashGetAll($"{Context.Guild.Id}:Messages")
|
||||
// .Where(user => !user.Name.Equals(0))
|
||||
// .OrderByDescending(s => s.Value)
|
||||
// .Take(amount)
|
||||
// .ToDictionary(user => ulong.Parse(user.Name), user => int.Parse(user.Value));
|
||||
}
|
||||
|
||||
private Dictionary<ulong, int> GetKarmaList(int amount)
|
||||
{
|
||||
return _database.Karma
|
||||
.Where(k => k.GuildId.Equals(Context.Guild.Id.AsLong()))
|
||||
.OrderByDescending(o => o.Karma)
|
||||
.Take(amount)
|
||||
.ToDictionary(key => key.UserId.AsUlong(), key => key.Karma);
|
||||
}
|
||||
|
||||
private Dictionary<ulong, int> GetRollsList(int amount)
|
||||
{
|
||||
return _database.Rolls
|
||||
.Where(k => k.GuildId.Equals(Context.Guild.Id.AsLong()))
|
||||
.OrderByDescending(o => o.Rolls)
|
||||
.Take(amount)
|
||||
.ToDictionary(key => key.UserId.AsUlong(), key => key.Rolls);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
namespace Geekbot.net.Commands.User.Ranking
|
||||
{
|
||||
public enum RankType
|
||||
{
|
||||
messages,
|
||||
karma,
|
||||
rolls
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ namespace Geekbot.net.Database
|
|||
Console.WriteLine(g.Name);
|
||||
allGuilds.Add(g);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
|
13
Geekbot.net/Lib/Highscores/HighscoreListEmptyException.cs
Normal file
13
Geekbot.net/Lib/Highscores/HighscoreListEmptyException.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Geekbot.net.Lib.Highscores
|
||||
{
|
||||
public class HighscoreListEmptyException : Exception
|
||||
{
|
||||
public HighscoreListEmptyException() {}
|
||||
|
||||
public HighscoreListEmptyException(string message) : base(message) {}
|
||||
|
||||
public HighscoreListEmptyException(string message, Exception inner) : base(message, inner) {}
|
||||
}
|
||||
}
|
106
Geekbot.net/Lib/Highscores/HighscoreManager.cs
Normal file
106
Geekbot.net/Lib/Highscores/HighscoreManager.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Geekbot.net.Database;
|
||||
using Geekbot.net.Lib.Extensions;
|
||||
using Geekbot.net.Lib.Logger;
|
||||
using Geekbot.net.Lib.UserRepository;
|
||||
|
||||
namespace Geekbot.net.Lib.Highscores
|
||||
{
|
||||
public class HighscoreManager : IHighscoreManager
|
||||
{
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public HighscoreManager(DatabaseContext databaseContext, IUserRepository userRepository)
|
||||
{
|
||||
_database = databaseContext;
|
||||
_userRepository = userRepository;
|
||||
|
||||
}
|
||||
|
||||
public Dictionary<HighscoreUserDto, int> GetHighscoresWithUserData(HighscoreTypes type, ulong guildId, int amount)
|
||||
{
|
||||
Dictionary<ulong, int> list;
|
||||
switch (type)
|
||||
{
|
||||
case HighscoreTypes.messages:
|
||||
list = GetMessageList(guildId, amount);
|
||||
break;
|
||||
case HighscoreTypes.karma:
|
||||
list = GetKarmaList(guildId, amount);
|
||||
break;
|
||||
case HighscoreTypes.rolls:
|
||||
list = GetRollsList(guildId, amount);
|
||||
break;
|
||||
default:
|
||||
list = new Dictionary<ulong, int>();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!list.Any())
|
||||
{
|
||||
throw new HighscoreListEmptyException($"No {type} found for guild {guildId}");
|
||||
}
|
||||
|
||||
var highscoreUsers = new Dictionary<HighscoreUserDto, int>();
|
||||
foreach (var user in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
var guildUser = _userRepository.Get(user.Key);
|
||||
if (guildUser?.Username != null)
|
||||
{
|
||||
highscoreUsers.Add(new HighscoreUserDto
|
||||
{
|
||||
Username = guildUser.Username,
|
||||
Discriminator = guildUser.Discriminator,
|
||||
Avatar = guildUser.AvatarUrl
|
||||
}, user.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
highscoreUsers.Add(new HighscoreUserDto
|
||||
{
|
||||
Id = user.Key.ToString()
|
||||
}, user.Value);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return highscoreUsers;
|
||||
}
|
||||
|
||||
public Dictionary<ulong, int> GetMessageList(ulong guildId, int amount)
|
||||
{
|
||||
return _database.Messages
|
||||
.Where(k => k.GuildId.Equals(guildId.AsLong()))
|
||||
.OrderByDescending(o => o.MessageCount)
|
||||
.Take(amount)
|
||||
.ToDictionary(key => key.UserId.AsUlong(), key => key.MessageCount);
|
||||
}
|
||||
|
||||
public Dictionary<ulong, int> GetKarmaList(ulong guildId, int amount)
|
||||
{
|
||||
return _database.Karma
|
||||
.Where(k => k.GuildId.Equals(guildId.AsLong()))
|
||||
.OrderByDescending(o => o.Karma)
|
||||
.Take(amount)
|
||||
.ToDictionary(key => key.UserId.AsUlong(), key => key.Karma);
|
||||
}
|
||||
|
||||
public Dictionary<ulong, int> GetRollsList(ulong guildId, int amount)
|
||||
{
|
||||
return _database.Rolls
|
||||
.Where(k => k.GuildId.Equals(guildId.AsLong()))
|
||||
.OrderByDescending(o => o.Rolls)
|
||||
.Take(amount)
|
||||
.ToDictionary(key => key.UserId.AsUlong(), key => key.Rolls);
|
||||
}
|
||||
}
|
||||
}
|
9
Geekbot.net/Lib/Highscores/HighscoreTypes.cs
Normal file
9
Geekbot.net/Lib/Highscores/HighscoreTypes.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Geekbot.net.Lib.Highscores
|
||||
{
|
||||
public enum HighscoreTypes
|
||||
{
|
||||
messages,
|
||||
karma,
|
||||
rolls
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
namespace Geekbot.net.Commands.User.Ranking
|
||||
namespace Geekbot.net.Lib.Highscores
|
||||
{
|
||||
internal class RankUserDto
|
||||
public class HighscoreUserDto
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public string Discriminator { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
12
Geekbot.net/Lib/Highscores/IHighscoreManager.cs
Normal file
12
Geekbot.net/Lib/Highscores/IHighscoreManager.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Geekbot.net.Lib.Highscores
|
||||
{
|
||||
public interface IHighscoreManager
|
||||
{
|
||||
Dictionary<HighscoreUserDto, int> GetHighscoresWithUserData(HighscoreTypes type, ulong guildId, int amount);
|
||||
Dictionary<ulong, int> GetMessageList(ulong guildId, int amount);
|
||||
Dictionary<ulong, int> GetKarmaList(ulong guildId, int amount);
|
||||
Dictionary<ulong, int> GetRollsList(ulong guildId, int amount);
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ namespace Geekbot.net.Lib.Logger
|
|||
Command,
|
||||
Api,
|
||||
Migration,
|
||||
HighscoreManager,
|
||||
Other
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ using Discord;
|
|||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.net.Database;
|
||||
using Geekbot.net.Database.LoggingAdapter;
|
||||
using Geekbot.net.Lib;
|
||||
using Geekbot.net.Lib.AlmostRedis;
|
||||
using Geekbot.net.Lib.Audio;
|
||||
|
@ -15,6 +14,7 @@ using Geekbot.net.Lib.Clients;
|
|||
using Geekbot.net.Lib.Converters;
|
||||
using Geekbot.net.Lib.ErrorHandling;
|
||||
using Geekbot.net.Lib.GlobalSettings;
|
||||
using Geekbot.net.Lib.Highscores;
|
||||
using Geekbot.net.Lib.Levels;
|
||||
using Geekbot.net.Lib.Localization;
|
||||
using Geekbot.net.Lib.Logger;
|
||||
|
@ -40,6 +40,7 @@ namespace Geekbot.net
|
|||
private IUserRepository _userRepository;
|
||||
private RunParameters _runParameters;
|
||||
private IAlmostRedis _redis;
|
||||
private IHighscoreManager _highscoreManager;
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
|
@ -123,6 +124,7 @@ namespace Geekbot.net
|
|||
var mtgManaConverter = new MtgManaConverter();
|
||||
var wikipediaClient = new WikipediaClient();
|
||||
var audioUtils = new AudioUtils();
|
||||
_highscoreManager = new HighscoreManager(_databaseInitializer.Initialize(), _userRepository);
|
||||
|
||||
_services.AddSingleton<IAlmostRedis>(_redis);
|
||||
_services.AddSingleton<IUserRepository>(_userRepository);
|
||||
|
@ -135,6 +137,7 @@ namespace Geekbot.net
|
|||
_services.AddSingleton<IMtgManaConverter>(mtgManaConverter);
|
||||
_services.AddSingleton<IWikipediaClient>(wikipediaClient);
|
||||
_services.AddSingleton<IAudioUtils>(audioUtils);
|
||||
_services.AddSingleton<IHighscoreManager>(_highscoreManager);
|
||||
_services.AddSingleton<IGlobalSettings>(_globalSettings);
|
||||
_services.AddTransient<DatabaseContext>((e) => _databaseInitializer.Initialize());
|
||||
|
||||
|
@ -203,7 +206,7 @@ namespace Geekbot.net
|
|||
private Task StartWebApi()
|
||||
{
|
||||
_logger.Information(LogSource.Api, "Starting Webserver");
|
||||
WebApi.WebApiStartup.StartWebApi(_logger, _runParameters, _commands, _databaseInitializer.Initialize(), _client, _globalSettings);
|
||||
WebApi.WebApiStartup.StartWebApi(_logger, _runParameters, _commands, _databaseInitializer.Initialize(), _client, _globalSettings, _highscoreManager);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
7
Geekbot.net/WebApi/ApiError.cs
Normal file
7
Geekbot.net/WebApi/ApiError.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.net.WebApi
|
||||
{
|
||||
public class ApiError
|
||||
{
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using System.Collections.Generic;
|
||||
using Geekbot.net.Lib.Highscores;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Geekbot.net.WebApi.Controllers.Highscores
|
||||
{
|
||||
[EnableCors("AllowSpecificOrigin")]
|
||||
public class HighscoreController : Controller
|
||||
{
|
||||
private readonly IHighscoreManager _highscoreManager;
|
||||
|
||||
public HighscoreController(IHighscoreManager highscoreManager)
|
||||
{
|
||||
_highscoreManager = highscoreManager;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("/v1/highscore")]
|
||||
public IActionResult GetHighscores([FromBody] HighscoreControllerPostBodyDto body)
|
||||
{
|
||||
if (!ModelState.IsValid || body == null)
|
||||
{
|
||||
var error = new SerializableError(ModelState);
|
||||
return BadRequest(error);
|
||||
}
|
||||
|
||||
Dictionary<HighscoreUserDto, int> list;
|
||||
try
|
||||
{
|
||||
list = _highscoreManager.GetHighscoresWithUserData(body.Type, body.GuildId, body.Amount);
|
||||
}
|
||||
catch (HighscoreListEmptyException)
|
||||
{
|
||||
return NotFound(new ApiError
|
||||
{
|
||||
Message = $"No {body.Type} found on this server"
|
||||
});
|
||||
}
|
||||
|
||||
var response = new List<HighscoreControllerReponseBody>();
|
||||
var counter = 1;
|
||||
foreach (var item in list)
|
||||
{
|
||||
response.Add(new HighscoreControllerReponseBody
|
||||
{
|
||||
count = item.Value,
|
||||
rank = counter,
|
||||
user = item.Key
|
||||
});
|
||||
counter++;
|
||||
}
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Geekbot.net.Lib.Highscores;
|
||||
|
||||
namespace Geekbot.net.WebApi.Controllers.Highscores
|
||||
{
|
||||
public class HighscoreControllerPostBodyDto
|
||||
{
|
||||
[Required]
|
||||
public ulong GuildId { get; set; }
|
||||
|
||||
public HighscoreTypes Type { get; set; } = HighscoreTypes.messages;
|
||||
|
||||
[Range(1, 150)]
|
||||
public int Amount { get; set; } = 50;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using Geekbot.net.Lib.Highscores;
|
||||
|
||||
namespace Geekbot.net.WebApi.Controllers.Highscores
|
||||
{
|
||||
public class HighscoreControllerReponseBody
|
||||
{
|
||||
public int rank { get; set; }
|
||||
public HighscoreUserDto user { get; set; }
|
||||
public int count { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using Discord.WebSocket;
|
|||
using Geekbot.net.Database;
|
||||
using Geekbot.net.Lib;
|
||||
using Geekbot.net.Lib.GlobalSettings;
|
||||
using Geekbot.net.Lib.Highscores;
|
||||
using Geekbot.net.Lib.Logger;
|
||||
using Geekbot.net.WebApi.Logging;
|
||||
using Microsoft.AspNetCore;
|
||||
|
@ -18,7 +19,7 @@ namespace Geekbot.net.WebApi
|
|||
public static class WebApiStartup
|
||||
{
|
||||
public static void StartWebApi(IGeekbotLogger logger, RunParameters runParameters, CommandService commandService,
|
||||
DatabaseContext databaseContext, DiscordSocketClient client, IGlobalSettings globalSettings)
|
||||
DatabaseContext databaseContext, DiscordSocketClient client, IGlobalSettings globalSettings, IHighscoreManager highscoreManager)
|
||||
{
|
||||
WebHost.CreateDefaultBuilder()
|
||||
.UseKestrel(options =>
|
||||
|
@ -32,6 +33,7 @@ namespace Geekbot.net.WebApi
|
|||
services.AddSingleton<DatabaseContext>(databaseContext);
|
||||
services.AddSingleton<DiscordSocketClient>(client);
|
||||
services.AddSingleton<IGlobalSettings>(globalSettings);
|
||||
services.AddSingleton<IHighscoreManager>(highscoreManager);
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowSpecificOrigin",
|
||||
|
|
Loading…
Reference in a new issue