From 6dcfeabfbdb199c40f52e7fc73a8b678170232a7 Mon Sep 17 00:00:00 2001 From: Runebaas Date: Sat, 30 Sep 2017 01:38:10 +0200 Subject: [PATCH] UserRepository, Handler now have own class, uptime in !info --- Geekbot.net/Handlers.cs | 100 ++++++++++++++++++++++++++++++ Geekbot.net/Lib/UserRepository.cs | 84 +++++++++++++++++++++++++ Geekbot.net/Modules/AdminCmd.cs | 72 ++++++++++++++++----- Geekbot.net/Modules/Info.cs | 41 +++++++----- Geekbot.net/Modules/UserInfo.cs | 67 +++++++++++--------- Geekbot.net/Program.cs | 75 +++++----------------- 6 files changed, 322 insertions(+), 117 deletions(-) create mode 100644 Geekbot.net/Handlers.cs create mode 100644 Geekbot.net/Lib/UserRepository.cs diff --git a/Geekbot.net/Handlers.cs b/Geekbot.net/Handlers.cs new file mode 100644 index 0000000..6c25104 --- /dev/null +++ b/Geekbot.net/Handlers.cs @@ -0,0 +1,100 @@ +using System; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using Geekbot.net.Lib; +using Serilog; +using StackExchange.Redis; + +namespace Geekbot.net +{ + public class Handlers + { + private readonly IDiscordClient _client; + private readonly ILogger _logger; + private readonly IDatabase _redis; + private readonly IServiceProvider _servicesProvider; + private readonly CommandService _commands; + private readonly IUserRepository _userRepository; + + public Handlers(IDiscordClient client, ILogger logger, IDatabase redis, IServiceProvider servicesProvider, CommandService commands, IUserRepository userRepository) + { + _client = client; + _logger = logger; + _redis = redis; + _servicesProvider = servicesProvider; + _commands = commands; + _userRepository = userRepository; + } + + // + // Incoming Messages + // + + public Task RunCommand(SocketMessage messageParam) + { + var message = messageParam as SocketUserMessage; + if (message == null) return Task.CompletedTask; + if (message.Author.IsBot) return Task.CompletedTask; + var argPos = 0; + var lowCaseMsg = message.ToString().ToLower(); + if (lowCaseMsg.StartsWith("ping")) + { + message.Channel.SendMessageAsync("pong"); + return Task.CompletedTask; + } + if (lowCaseMsg.StartsWith("hui")) + { + message.Channel.SendMessageAsync("hui!!!"); + return Task.CompletedTask; + } + if (!(message.HasCharPrefix('!', ref argPos) || + message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return Task.CompletedTask; + var context = new CommandContext(_client, message); + var commandExec = _commands.ExecuteAsync(context, argPos, _servicesProvider); + return Task.CompletedTask; + } + + public Task UpdateStats(SocketMessage messsageParam) + { + var message = messsageParam; + if (message == null) return Task.CompletedTask; + + var channel = (SocketGuildChannel) message.Channel; + + _redis.HashIncrementAsync($"{channel.Guild.Id}:Messages", message.Author.Id.ToString()); + _redis.HashIncrementAsync($"{channel.Guild.Id}:Messages", 0.ToString()); + + if (message.Author.IsBot) return Task.CompletedTask; + _logger.Information($"[Message] {channel.Guild.Name} - {message.Channel} - {message.Author.Username} - {message.Content}"); + return Task.CompletedTask; + } + + // + // User Stuff + // + + public Task UserJoined(SocketGuildUser user) + { + if (!user.IsBot) + { + var message = _redis.HashGet($"{user.Guild.Id}:Settings", "WelcomeMsg"); + if (!message.IsNullOrEmpty) + { + message = message.ToString().Replace("$user", user.Mention); + user.Guild.DefaultChannel.SendMessageAsync(message); + } + } + _userRepository.Update(user); + _logger.Information($"[Geekbot] {user.Id} ({user.Username}) joined {user.Guild.Id} ({user.Guild.Name})"); + return Task.CompletedTask; + } + + public Task UserUpdated(SocketUser oldUser, SocketUser newUser) + { + _userRepository.Update(newUser); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/UserRepository.cs b/Geekbot.net/Lib/UserRepository.cs new file mode 100644 index 0000000..71faba3 --- /dev/null +++ b/Geekbot.net/Lib/UserRepository.cs @@ -0,0 +1,84 @@ +using System; +using System.Threading.Tasks; +using Discord.WebSocket; +using Serilog; +using StackExchange.Redis; + +namespace Geekbot.net.Lib +{ + public class UserRepository : IUserRepository + { + private readonly IDatabase _redis; + private readonly ILogger _logger; + public UserRepository(IDatabase redis, ILogger logger) + { + _redis = redis; + _logger = logger; + } + + public Task Update(SocketUser user) + { + try + { + _redis.HashSetAsync($"Users:{user.Id}", new HashEntry[] + { + new HashEntry("Id", user.Id.ToString()), + new HashEntry("Username", user.Username), + new HashEntry("Discriminator", user.Discriminator), + new HashEntry("AvatarUrl", user.GetAvatarUrl() ?? "0"), + new HashEntry("IsBot", user.IsBot), + }); + _logger.Information($"[UserRepository] Updated User {user.Id}"); + return Task.FromResult(true); + } + catch (Exception e) + { + _logger.Warning(e, $"[UserRepository] Failed to update {user.Id}"); + return Task.FromResult(false); + } + } + + public UserRepositoryUser Get(ulong userId) + { + var user = _redis.HashGetAll($"Users:{userId}").ToDictionary(); + var dto = new UserRepositoryUser(); + foreach (var a in user) + { + switch (a.Key) + { + case "Id": + dto.Id = ulong.Parse(a.Value); + break; + case "Username": + dto.Username = a.Value.ToString(); + break; + case "Discriminator": + dto.Discriminator = a.Value.ToString(); + break; + case "AvatarUrl": + dto.AvatarUrl = (a.Value != "0") ? a.Value.ToString() : null; + break; + case "IsBot": + dto.IsBot = a.Value == 1; + break; + } + } + return dto; + } + } + + public class UserRepositoryUser + { + public ulong Id { get; set; } + public string Username { get; set; } + public string Discriminator { get; set; } + public string AvatarUrl { get; set; } + public bool IsBot { get; set; } + } + + public interface IUserRepository + { + Task Update(SocketUser user); + UserRepositoryUser Get(ulong userId); + } +} \ No newline at end of file diff --git a/Geekbot.net/Modules/AdminCmd.cs b/Geekbot.net/Modules/AdminCmd.cs index 2229469..ea2bc05 100644 --- a/Geekbot.net/Modules/AdminCmd.cs +++ b/Geekbot.net/Modules/AdminCmd.cs @@ -1,7 +1,10 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Discord; using Discord.Commands; using Discord.WebSocket; +using Geekbot.net.Lib; +using Google.Apis.YouTube.v3.Data; using Serilog; using StackExchange.Redis; @@ -10,15 +13,19 @@ namespace Geekbot.net.Modules [Group("admin")] public class AdminCmd : ModuleBase { - private readonly IDatabase redis; - private readonly DiscordSocketClient client; - private readonly ILogger logger; + private readonly IDatabase _redis; + private readonly DiscordSocketClient _client; + private readonly ILogger _logger; + private readonly IUserRepository _userRepository; + private readonly IErrorHandler _errorHandler; - public AdminCmd(IDatabase redis, DiscordSocketClient client, ILogger logger) + public AdminCmd(IDatabase redis, DiscordSocketClient client, ILogger logger, IUserRepository userRepositry, IErrorHandler errorHandler) { - this.redis = redis; - this.client = client; - this.logger = logger; + _redis = redis; + _client = client; + _logger = logger; + _userRepository = userRepositry; + _errorHandler = errorHandler; } [RequireUserPermission(GuildPermission.Administrator)] @@ -26,7 +33,7 @@ namespace Geekbot.net.Modules [Summary("Set a Welcome Message (use '$user' to mention the new joined user).")] public async Task SetWelcomeMessage([Remainder] [Summary("message")] string welcomeMessage) { - redis.HashSet($"{Context.Guild.Id}:Settings", new HashEntry[] { new HashEntry("WelcomeMsg", welcomeMessage) }); + _redis.HashSet($"{Context.Guild.Id}:Settings", new HashEntry[] { new HashEntry("WelcomeMsg", welcomeMessage) }); var formatedMessage = welcomeMessage.Replace("$user", Context.User.Mention); await ReplyAsync("Welcome message has been changed\r\nHere is an example of how it would look:\r\n" + formatedMessage); @@ -36,14 +43,14 @@ namespace Geekbot.net.Modules [Summary("Set the youtube api key")] public async Task SetYoutubeKey([Summary("API Key")] string key) { - var botOwner = Context.Guild.GetUserAsync(ulong.Parse(redis.StringGet("botOwner"))).Result; + var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result; if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString())) { await ReplyAsync($"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})"); return; } - redis.StringSet("youtubeKey", key); + _redis.StringSet("youtubeKey", key); await ReplyAsync("Apikey has been set"); } @@ -51,17 +58,52 @@ namespace Geekbot.net.Modules [Summary("Set the game that the bot is playing")] public async Task SetGame([Remainder] [Summary("Game")] string key) { - var botOwner = Context.Guild.GetUserAsync(ulong.Parse(redis.StringGet("botOwner"))).Result; + var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result; if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString())) { await ReplyAsync($"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})"); return; } - redis.StringSet("Game", key); - await client.SetGameAsync(key); - logger.Information($"[Geekbot] Changed game to {key}"); + _redis.StringSet("Game", key); + await _client.SetGameAsync(key); + _logger.Information($"[Geekbot] Changed game to {key}"); await ReplyAsync($"Now Playing {key}"); } + + [Command("popuserrepo", RunMode = RunMode.Async)] + [Summary("Set the game that the bot is playing")] + public async Task popUserRepoCommand() + { + var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result; + if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString())) + { + await ReplyAsync($"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})"); + return; + } + var success = 0; + var failed = 0; + try + { + _logger.Warning("[UserRepository] Populating User Repositry"); + await ReplyAsync("Starting Population of User Repository"); + foreach (var guild in _client.Guilds) + { + _logger.Information($"[UserRepository] Populating users from {guild.Name}"); + foreach (var user in guild.Users) + { + var succeded = await _userRepository.Update(user); + var inc = succeded ? success++ : failed++; + } + } + _logger.Warning("[UserRepository] Finished Updating User Repositry"); + await ReplyAsync($"Successfully Populated User Repository with {success} Users in {_client.Guilds.Count} Guilds (Failed: {failed})"); + } + catch (Exception e) + { + await ReplyAsync("Couldn't complete User Repository, see console for more info"); + _errorHandler.HandleCommandException(e, Context); + } + } } } \ No newline at end of file diff --git a/Geekbot.net/Modules/Info.cs b/Geekbot.net/Modules/Info.cs index 749594a..bf02862 100644 --- a/Geekbot.net/Modules/Info.cs +++ b/Geekbot.net/Modules/Info.cs @@ -1,36 +1,49 @@ -using System.Threading.Tasks; +using System; +using System.Diagnostics; +using System.Threading.Tasks; using Discord; using Discord.Commands; +using Geekbot.net.Lib; using StackExchange.Redis; namespace Geekbot.net.Modules { public class Info : ModuleBase { - private readonly IDatabase redis; + private readonly IDatabase _redis; + private readonly IErrorHandler _errorHandler; - public Info(IDatabase redis) + public Info(IDatabase redis, IErrorHandler errorHandler) { - this.redis = redis; + _redis = redis; + _errorHandler = errorHandler; } [Command("info", RunMode = RunMode.Async)] [Summary("Get Information about the bot")] public async Task BotInfo() { - var eb = new EmbedBuilder(); + try + { + var eb = new EmbedBuilder(); - eb.WithTitle("Geekbot V3.2"); + eb.WithTitle("Geekbot V3.3"); - var botOwner = Context.Guild.GetUserAsync(ulong.Parse(redis.StringGet("botOwner"))).Result; + var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result; + var uptime = (DateTime.Now.Subtract(Process.GetCurrentProcess().StartTime)); + + eb.AddInlineField("Bot Name", Context.Client.CurrentUser.Username) + .AddInlineField("Servers", Context.Client.GetGuildsAsync().Result.Count) + .AddInlineField("Uptime", $"{uptime.Days}D {uptime.Hours}H {uptime.Minutes}M {uptime.Seconds}S") + .AddInlineField("Bot Owner", $"{botOwner.Username}#{botOwner.Discriminator}") + .AddInlineField("Website", "https://geekbot.pizzaandcoffee.rocks/"); - eb.AddInlineField("Status", Context.Client.ConnectionState.ToString()) - .AddInlineField("Bot Name", Context.Client.CurrentUser.Username) - .AddInlineField("Bot Owner", $"{botOwner.Username}#{botOwner.Discriminator}"); - - eb.AddInlineField("Servers", Context.Client.GetGuildsAsync().Result.Count); - - await ReplyAsync("", false, eb.Build()); + await ReplyAsync("", false, eb.Build()); + } + catch (Exception e) + { + _errorHandler.HandleCommandException(e, Context); + } } } } \ No newline at end of file diff --git a/Geekbot.net/Modules/UserInfo.cs b/Geekbot.net/Modules/UserInfo.cs index 83411a3..cb7725d 100644 --- a/Geekbot.net/Modules/UserInfo.cs +++ b/Geekbot.net/Modules/UserInfo.cs @@ -16,49 +16,60 @@ namespace Geekbot.net.Modules private readonly IDatabase redis; private readonly IErrorHandler errorHandler; private readonly ILogger logger; + private readonly IUserRepository userRepository; - public UserInfo(IDatabase redis, IErrorHandler errorHandler, ILogger logger) + public UserInfo(IDatabase redis, IErrorHandler errorHandler, ILogger logger, IUserRepository userRepository) { this.redis = redis; this.errorHandler = errorHandler; this.logger = logger; + this.userRepository = userRepository; } [Command("stats", RunMode = RunMode.Async)] [Summary("Get information about this user")] public async Task User([Summary("@someone")] IUser user = null) { - var userInfo = user ?? Context.Message.Author; + try + { + var userInfo = user ?? Context.Message.Author; + var userGuildInfo = (IGuildUser) userInfo; + var createdAt = userInfo.CreatedAt; + var joinedAt = userGuildInfo.JoinedAt.Value; + var age = Math.Floor((DateTime.Now - createdAt).TotalDays); + var joinedDayAgo = Math.Floor((DateTime.Now - joinedAt).TotalDays); - var age = Math.Floor((DateTime.Now - userInfo.CreatedAt).TotalDays); + var messages = (int) redis.HashGet($"{Context.Guild.Id}:Messages", userInfo.Id.ToString()); + var guildMessages = (int) redis.HashGet($"{Context.Guild.Id}:Messages", 0.ToString()); + var level = LevelCalc.GetLevelAtExperience(messages); - var messages = (int) redis.HashGet($"{Context.Guild.Id}:Messages", userInfo.Id.ToString()); - var guildMessages = (int) redis.HashGet($"{Context.Guild.Id}:Messages", 0.ToString()); - var level = LevelCalc.GetLevelAtExperience(messages); + var percent = Math.Round((double) (100 * messages) / guildMessages, 2); - var percent = Math.Round((double) (100 * messages) / guildMessages, 2); + var eb = new EmbedBuilder(); + eb.WithAuthor(new EmbedAuthorBuilder() + .WithIconUrl(userInfo.GetAvatarUrl()) + .WithName(userInfo.Username)); + eb.WithColor(new Color(221, 255, 119)); + + var karma = redis.HashGet($"{Context.Guild.Id}:Karma", userInfo.Id); + var correctRolls = redis.HashGet($"{Context.Guild.Id}:Rolls", userInfo.Id.ToString()); - var eb = new EmbedBuilder(); - eb.WithAuthor(new EmbedAuthorBuilder() - .WithIconUrl(userInfo.GetAvatarUrl()) - .WithName(userInfo.Username)); - eb.WithColor(new Color(221, 255, 119)); + eb.AddInlineField("Discordian Since", $"{createdAt.Day}.{createdAt.Month}.{createdAt.Year} ({age} days)") + .AddInlineField("Joined Server", $"{joinedAt.Day}.{joinedAt.Month}.{joinedAt.Year} ({joinedDayAgo} days)") + .AddInlineField("Karma", karma.ToString() ?? "0") + .AddInlineField("Level", level) + .AddInlineField("Messages Sent", messages) + .AddInlineField("Server Total", $"{percent}%"); - eb.AddField("Discordian Since", - $"{userInfo.CreatedAt.Day}/{userInfo.CreatedAt.Month}/{userInfo.CreatedAt.Year} ({age} days)"); - eb.AddInlineField("Level", level) - .AddInlineField("Messages Sent", messages) - .AddInlineField("Server Total", $"{percent}%"); + if (!correctRolls.IsNullOrEmpty) + eb.AddInlineField("Guessed Rolls", correctRolls); - var karma = redis.HashGet($"{Context.Guild.Id}:Karma", userInfo.Id.ToString()); - if (!karma.IsNullOrEmpty) - eb.AddInlineField("Karma", karma); - - var correctRolls = redis.HashGet($"{Context.Guild.Id}:Rolls", userInfo.Id.ToString()); - if (!correctRolls.IsNullOrEmpty) - eb.AddInlineField("Guessed Rolls", correctRolls); - - await ReplyAsync("", false, eb.Build()); + await ReplyAsync("", false, eb.Build()); + } + catch (Exception e) + { + errorHandler.HandleCommandException(e, Context); + } } [Command("rank", RunMode = RunMode.Async)] @@ -80,8 +91,8 @@ namespace Geekbot.net.Modules if (listLimiter > 10) break; try { - var guildUser = Context.Guild.GetUserAsync((ulong) user.Name).Result; - if (guildUser != null) + var guildUser = userRepository.Get((ulong)user.Name); + if (guildUser.Username != null) { highscoreUsers.Add(new RankUserPolyfill() { diff --git a/Geekbot.net/Program.cs b/Geekbot.net/Program.cs index 5cb003f..12bdb73 100755 --- a/Geekbot.net/Program.cs +++ b/Geekbot.net/Program.cs @@ -26,6 +26,7 @@ namespace Geekbot.net private IServiceProvider servicesProvider; private RedisValue token; private ILogger logger; + private IUserRepository userRepository; private string[] args; private bool firstStart = false; @@ -99,16 +100,19 @@ namespace Geekbot.net } services = new ServiceCollection(); + + userRepository = new UserRepository(redis, logger); + var errorHandler = new ErrorHandler(logger); var RandomClient = new Random(); var fortunes = new FortunesProvider(RandomClient, logger); var checkEmImages = new CheckEmImageProvider(RandomClient, logger); var pandaImages = new PandaProvider(RandomClient, logger); - var errorHandler = new ErrorHandler(logger); services.AddSingleton(errorHandler); services.AddSingleton(redis); - services.AddSingleton(RandomClient); services.AddSingleton(logger); + services.AddSingleton(userRepository); + services.AddSingleton(RandomClient); services.AddSingleton(fortunes); services.AddSingleton(checkEmImages); services.AddSingleton(pandaImages); @@ -133,14 +137,18 @@ namespace Geekbot.net logger.Information($"[Geekbot] Now Connected to {client.Guilds.Count} Servers"); logger.Information("[Geekbot] Registering Stuff"); - - client.MessageReceived += HandleCommand; - client.MessageReceived += HandleMessageReceived; - client.UserJoined += HandleUserJoined; + await commands.AddModulesAsync(Assembly.GetEntryAssembly()); services.AddSingleton(commands); services.AddSingleton(client); servicesProvider = services.BuildServiceProvider(); + + var handlers = new Handlers(client, logger, redis, servicesProvider, commands, userRepository); + + client.MessageReceived += handlers.RunCommand; + client.MessageReceived += handlers.UpdateStats; + client.UserJoined += handlers.UserJoined; + client.UserUpdated += handlers.UserUpdated; if (firstStart || (args.Length != 0 && args.Contains("--reset"))) { @@ -166,59 +174,6 @@ namespace Geekbot.net return true; } - private Task HandleCommand(SocketMessage messageParam) - { - var message = messageParam as SocketUserMessage; - if (message == null) return Task.CompletedTask; - if (message.Author.IsBot) return Task.CompletedTask; - var argPos = 0; - var lowCaseMsg = message.ToString().ToLower(); - if (lowCaseMsg.StartsWith("ping")) - { - message.Channel.SendMessageAsync("pong"); - return Task.CompletedTask; - } - if (lowCaseMsg.StartsWith("hui")) - { - message.Channel.SendMessageAsync("hui!!!"); - return Task.CompletedTask; - } - if (!(message.HasCharPrefix('!', ref argPos) || - message.HasMentionPrefix(client.CurrentUser, ref argPos))) return Task.CompletedTask; - var context = new CommandContext(client, message); - var commandExec = commands.ExecuteAsync(context, argPos, servicesProvider); - return Task.CompletedTask; - } - - private Task HandleMessageReceived(SocketMessage messsageParam) - { - var message = messsageParam; - if (message == null) return Task.CompletedTask; - - var channel = (SocketGuildChannel) message.Channel; - - redis.HashIncrementAsync($"{channel.Guild.Id}:Messages", message.Author.Id.ToString()); - redis.HashIncrementAsync($"{channel.Guild.Id}:Messages", 0.ToString()); - - if (message.Author.IsBot) return Task.CompletedTask; - logger.Information($"[Message] {channel.Guild.Name} - {message.Channel} - {message.Author.Username} - {message.Content}"); - return Task.CompletedTask; - } - - private Task HandleUserJoined(SocketGuildUser user) - { - if (!user.IsBot) - { - var message = redis.HashGet($"{user.Guild.Id}:Settings", "WelcomeMsg"); - if (!message.IsNullOrEmpty) - { - message = message.ToString().Replace("$user", user.Mention); - user.Guild.DefaultChannel.SendMessageAsync(message); - } - } - return Task.CompletedTask; - } - private async Task FinishSetup() { var appInfo = await client.GetApplicationInfoAsync(); @@ -252,7 +207,7 @@ namespace Geekbot.net logger.Error(message.Exception, logMessage); break; default: - logger.Information($"{logMessage} --- {message.Severity.ToString()}"); + logger.Information($"{logMessage} --- {message.Severity}"); break; } return Task.CompletedTask;