From ba1c3861f4c79c911deb473c9281d4df9e02e9df Mon Sep 17 00:00:00 2001 From: Sean Stoves Date: Tue, 2 Jun 2020 22:07:09 -0400 Subject: [PATCH] Removing Scratch, Adding Exception Handling to all processes. --- .idea/.idea.ChaosBot/.idea/contentModel.xml | 2 +- ChaosBot/Discord/DiscordConnect.cs | 82 ++++++++------- ChaosBot/Discord/Modules/ExampleCommand.cs | 109 -------------------- ChaosBot/Discord/Modules/InfoCommands.cs | 50 +++++++++ ChaosBot/Discord/Services/CommandHandler.cs | 98 +++++++++--------- ChaosBot/Logging.cs | 4 +- ChaosBot/Program.cs | 2 +- 7 files changed, 150 insertions(+), 197 deletions(-) delete mode 100644 ChaosBot/Discord/Modules/ExampleCommand.cs create mode 100644 ChaosBot/Discord/Modules/InfoCommands.cs diff --git a/.idea/.idea.ChaosBot/.idea/contentModel.xml b/.idea/.idea.ChaosBot/.idea/contentModel.xml index 192f79c..dba5bbf 100644 --- a/.idea/.idea.ChaosBot/.idea/contentModel.xml +++ b/.idea/.idea.ChaosBot/.idea/contentModel.xml @@ -11,7 +11,7 @@ - + diff --git a/ChaosBot/Discord/DiscordConnect.cs b/ChaosBot/Discord/DiscordConnect.cs index 301a003..6632236 100644 --- a/ChaosBot/Discord/DiscordConnect.cs +++ b/ChaosBot/Discord/DiscordConnect.cs @@ -1,9 +1,7 @@ using NLog; using System; using Discord; -using Discord.Net; using Discord.Commands; -using System.Reflection; using Discord.WebSocket; using System.Threading.Tasks; using ChaosBot.Discord.Services; @@ -58,45 +56,59 @@ namespace ChaosBot.Discord private static ServiceProvider ConfigureServices() { - // this returns a ServiceProvider that is used later to call for those services - // we can add types we have access to here, hence adding the new using statement: - // using csharpi.Services; - // the config we build is also added, which comes in handy for setting the command prefix! - return new ServiceCollection() - .AddSingleton(Program.Cfg) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .BuildServiceProvider(); + ServiceProvider csInfo = null; + + try + { + csInfo = new ServiceCollection() + .AddSingleton(Program.Cfg) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .BuildServiceProvider(); + } + catch (Exception ex) + { + _logger.Error($"DiscordConnect.ConfigureServices: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + + return csInfo; } private static Task Log(LogMessage msg) { - switch (msg.Severity) + try { - case LogSeverity.Critical: - _logger.Fatal(msg.Message); - break; - case LogSeverity.Debug: - _logger.Debug(msg.Message); - break; - case LogSeverity.Error: - _logger.Error(msg.Message); - break; - case LogSeverity.Info: - _logger.Info(msg.Message); - break; - case LogSeverity.Warning: - _logger.Warn(msg.Message); - break; - case LogSeverity.Verbose: - _logger.Trace(msg.Message); - break; - default: - _logger.Trace(msg.Message); - break; + switch (msg.Severity) + { + case LogSeverity.Critical: + _logger.Fatal(msg.Message); + break; + case LogSeverity.Debug: + _logger.Debug(msg.Message); + break; + case LogSeverity.Error: + _logger.Error(msg.Message); + break; + case LogSeverity.Info: + _logger.Info(msg.Message); + break; + case LogSeverity.Warning: + _logger.Warn(msg.Message); + break; + case LogSeverity.Verbose: + _logger.Trace(msg.Message); + break; + default: + _logger.Trace(msg.Message); + break; + } } - + catch (Exception ex) + { + _logger.Error($"DiscordConnect.Log: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + return Task.CompletedTask; } } diff --git a/ChaosBot/Discord/Modules/ExampleCommand.cs b/ChaosBot/Discord/Modules/ExampleCommand.cs deleted file mode 100644 index af835f2..0000000 --- a/ChaosBot/Discord/Modules/ExampleCommand.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using Discord; -using System.Text; -using Discord.Commands; -using System.Threading.Tasks; -using System.Collections.Generic; - - -namespace ChaosBot.Discord.Modules -{ - // for commands to be available, and have the Context passed to them, we must inherit ModuleBase - public class ExampleCommands : ModuleBase - { - [Command("hello")] - public async Task HelloCommand() - { - // initialize empty string builder for reply - var sb = new StringBuilder(); - - // get user info from the Context - var user = Context.User; - - // build out the reply - sb.AppendLine($"You are -> [{user.Username}]"); - sb.AppendLine("I must now say, World!"); - - // send simple string reply - await ReplyAsync(sb.ToString()); - } - - [Command("8ball")] - [Alias("ask")] - [RequireUserPermission(GuildPermission.KickMembers)] - public async Task AskEightBall([Remainder]string args = null) - { - // I like using StringBuilder to build out the reply - var sb = new StringBuilder(); - // let's use an embed for this one! - var embed = new EmbedBuilder(); - - // now to create a list of possible replies - var replies = new List(); - - // add our possible replies - replies.Add("yes"); - replies.Add("no"); - replies.Add("maybe"); - replies.Add("hazzzzy...."); - - // time to add some options to the embed (like color and title) - embed.WithColor(new Color(0, 255,0)); - embed.Title = "Welcome to the 8-ball!"; - - // we can get lots of information from the Context that is passed into the commands - // here I'm setting up the preface with the user's name and a comma - sb.AppendLine($"{Context.User.Username},"); - sb.AppendLine(); - - // let's make sure the supplied question isn't null - if (args == null) - { - // if no question is asked (args are null), reply with the below text - sb.AppendLine("Sorry, can't answer a question you didn't ask!"); - } - else - { - // if we have a question, let's give an answer! - // get a random number to index our list with (arrays start at zero so we subtract 1 from the count) - var answer = replies[new Random().Next(replies.Count - 1)]; - - // build out our reply with the handy StringBuilder - sb.AppendLine($"You asked: [**{args}**]..."); - sb.AppendLine(); - sb.AppendLine($"...your answer is [**{answer}**]"); - - // bonus - let's switch out the reply and change the color based on it - switch (answer) - { - case "yes": - { - embed.WithColor(new Color(0, 255, 0)); - break; - } - case "no": - { - embed.WithColor(new Color(255, 0, 0)); - break; - } - case "maybe": - { - embed.WithColor(new Color(255,255,0)); - break; - } - case "hazzzzy....": - { - embed.WithColor(new Color(255,0,255)); - break; - } - } - } - - // now we can assign the description of the embed to the contents of the StringBuilder we created - embed.Description = sb.ToString(); - - // this will reply with the embed - await ReplyAsync(null, false, embed.Build()); - } - } -} \ No newline at end of file diff --git a/ChaosBot/Discord/Modules/InfoCommands.cs b/ChaosBot/Discord/Modules/InfoCommands.cs new file mode 100644 index 0000000..1eb1d80 --- /dev/null +++ b/ChaosBot/Discord/Modules/InfoCommands.cs @@ -0,0 +1,50 @@ +using System; +using Discord; +using System.Text; +using Discord.Commands; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using NLog; + + +namespace ChaosBot.Discord.Modules +{ + public class InfoCommands : ModuleBase + { + private static readonly Logger _logger = Program._logger; + + + [Command("info")] + [Alias("version")] + public async Task InfoCommand() + { + try + { + var sb = new StringBuilder(); + var embed = new EmbedBuilder(); + + embed.WithColor(new Color(255, 255,0)); + embed.Title = $"General Information"; + sb.AppendLine($"{Context.User.Mention} has requested information from {Program.Cfg.GetValue("Bot:Name")}."); + sb.AppendLine(); + sb.AppendLine($"Bot Version: {Program.Cfg.GetValue("Bot:Version")}"); + sb.AppendLine($"Bot Prefix: {Program.Cfg.GetValue("Discord:Prefix")}"); + + /* + * Add the string to the Embed + */ + embed.Description = sb.ToString(); + + /* + * Reply with the Embed created above + */ + await ReplyAsync(null, false, embed.Build()); + } + catch (Exception ex) + { + _logger.Error($"InfoCommands.InfoCommand: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + } + } +} \ No newline at end of file diff --git a/ChaosBot/Discord/Services/CommandHandler.cs b/ChaosBot/Discord/Services/CommandHandler.cs index 224a434..f08b05c 100644 --- a/ChaosBot/Discord/Services/CommandHandler.cs +++ b/ChaosBot/Discord/Services/CommandHandler.cs @@ -6,91 +6,91 @@ using Discord; using Discord.Commands; using Discord.WebSocket; using Microsoft.Extensions.Configuration; +using NLog; namespace ChaosBot.Discord.Services { public class CommandHandler { - // setup fields to be set later in the constructor private readonly IConfiguration _config; private readonly CommandService _commands; private readonly DiscordSocketClient _client; private readonly IServiceProvider _services; - + private readonly Logger _logger = Program._logger; + public CommandHandler(IServiceProvider services) { - // juice up the fields with these services - // since we passed the services in, we can use GetRequiredService to pass them into the fields set earlier - _config = services.GetRequiredService(); - _commands = services.GetRequiredService(); - _client = services.GetRequiredService(); - _services = services; + try + { + _config = services.GetRequiredService(); + _commands = services.GetRequiredService(); + _client = services.GetRequiredService(); + _services = services; - // take action when we execute a command - _commands.CommandExecuted += CommandExecutedAsync; + _commands.CommandExecuted += CommandExecutedAsync; - // take action when we receive a message (so we can process it, and see if it is a valid command) - _client.MessageReceived += MessageReceivedAsync; + _client.MessageReceived += MessageReceivedAsync; + } + catch (Exception ex) + { + _logger.Error($"CommandHandler.CommandHandler: Exception [{ex}] thrown, <[{ex.Message}]>."); + } } public async Task InitializeAsync() { - // register modules that are public and inherit ModuleBase. await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); } - // this class is where the magic starts, and takes actions upon receiving messages public async Task MessageReceivedAsync(SocketMessage rawMessage) { - // ensures we don't process system/other bot messages - if (!(rawMessage is SocketUserMessage message)) + try { - return; - } - - if (message.Source != MessageSource.User) - { - return; - } + if (!(rawMessage is SocketUserMessage message)) + return; - // sets the argument position away from the prefix we set - var argPos = 0; + if (message.Source != MessageSource.User) + return; - // get prefix from the configuration file - char prefix = Char.Parse(_config["Prefix"]); + var argPos = 0; - // determine if the message has a valid prefix, and adjust argPos based on prefix - if (!(message.HasMentionPrefix(_client.CurrentUser, ref argPos) || message.HasCharPrefix(prefix, ref argPos))) - { - return; - } + char prefix = Char.Parse(_config["Discord:Prefix"]); + + if (!(message.HasMentionPrefix(_client.CurrentUser, ref argPos) || message.HasCharPrefix(prefix, ref argPos))) + return; - var context = new SocketCommandContext(_client, message); + var context = new SocketCommandContext(_client, message); - // execute command if one is found that matches - await _commands.ExecuteAsync(context, argPos, _services); + await _commands.ExecuteAsync(context, argPos, _services); + } + catch (Exception ex) + { + _logger.Error($"CommandHandler.MessageReceivedAsync: Exception [{ex}] thrown, <[{ex.Message}]>."); + } } public async Task CommandExecutedAsync(Optional command, ICommandContext context, IResult result) { - // if a command isn't found, log that info to console and exit this method - if (!command.IsSpecified) + try { - System.Console.WriteLine($"Command failed to execute for [{context.User.Username}] <-> [{result.ErrorReason}]!"); - return; - } - + if (!command.IsSpecified) + { + System.Console.WriteLine($"Command failed to execute for [{context.User.Username}] <-> [{result.ErrorReason}]!"); + return; + } - // log success to the console and exit this method - if (result.IsSuccess) + if (result.IsSuccess) + { + System.Console.WriteLine($"Command [{command.Value.Name}] executed for -> [{context.User.Username}]"); + return; + } + + await context.Channel.SendMessageAsync($"Sorry, {context.User.Username}... something went wrong -> [{result}]!"); + } + catch (Exception ex) { - System.Console.WriteLine($"Command [{command.Value.Name}] executed for -> [{context.User.Username}]"); - return; + _logger.Error($"CommandHandler.CommandExecutedAsync: Exception [{ex}] thrown, <[{ex.Message}]>."); } - - - // failure scenario, let's let the user know - await context.Channel.SendMessageAsync($"Sorry, {context.User.Username}... something went wrong -> [{result}]!"); } } } \ No newline at end of file diff --git a/ChaosBot/Logging.cs b/ChaosBot/Logging.cs index 438049a..90a3c7c 100644 --- a/ChaosBot/Logging.cs +++ b/ChaosBot/Logging.cs @@ -14,9 +14,9 @@ namespace ChaosBot return LogManager.GetCurrentClassLogger(); } - catch (Exception e) + catch (Exception ex) { - Console.WriteLine(e); + Console.WriteLine($"Logging.Logger: Exception [{ex}] thrown, <[{ex.Message}]>."); throw; } } diff --git a/ChaosBot/Program.cs b/ChaosBot/Program.cs index 6c86185..b15ddf6 100644 --- a/ChaosBot/Program.cs +++ b/ChaosBot/Program.cs @@ -43,7 +43,7 @@ namespace ChaosBot } catch (Exception ex) { - _logger.Error(ex, $"Stopped program because of exception"); + _logger.Error(ex, $"Program.MainFunction: Exception [{ex}] thrown, <[{ex.Message}]>."); } } }