Removing Scratch, Adding Exception Handling to all processes.

This commit is contained in:
Sean "Solao Bajiuik" Stoves 2020-06-02 22:07:09 -04:00
parent 64b6f39033
commit ba1c3861f4
7 changed files with 150 additions and 197 deletions

View File

@ -11,7 +11,7 @@
<e p="Discord" t="Include"> <e p="Discord" t="Include">
<e p="DiscordConnect.cs" t="Include" /> <e p="DiscordConnect.cs" t="Include" />
<e p="Modules" t="Include"> <e p="Modules" t="Include">
<e p="ExampleCommand.cs" t="Include" /> <e p="InfoCommands.cs" t="Include" />
</e> </e>
<e p="Services" t="Include"> <e p="Services" t="Include">
<e p="CommandHandler.cs" t="Include" /> <e p="CommandHandler.cs" t="Include" />

View File

@ -1,9 +1,7 @@
using NLog; using NLog;
using System; using System;
using Discord; using Discord;
using Discord.Net;
using Discord.Commands; using Discord.Commands;
using System.Reflection;
using Discord.WebSocket; using Discord.WebSocket;
using System.Threading.Tasks; using System.Threading.Tasks;
using ChaosBot.Discord.Services; using ChaosBot.Discord.Services;
@ -58,43 +56,57 @@ namespace ChaosBot.Discord
private static ServiceProvider ConfigureServices() private static ServiceProvider ConfigureServices()
{ {
// this returns a ServiceProvider that is used later to call for those services ServiceProvider csInfo = null;
// we can add types we have access to here, hence adding the new using statement:
// using csharpi.Services; try
// the config we build is also added, which comes in handy for setting the command prefix! {
return new ServiceCollection() csInfo = new ServiceCollection()
.AddSingleton(Program.Cfg) .AddSingleton(Program.Cfg)
.AddSingleton<DiscordSocketClient>() .AddSingleton<DiscordSocketClient>()
.AddSingleton<CommandService>() .AddSingleton<CommandService>()
.AddSingleton<CommandHandler>() .AddSingleton<CommandHandler>()
.BuildServiceProvider(); .BuildServiceProvider();
}
catch (Exception ex)
{
_logger.Error($"DiscordConnect.ConfigureServices: Exception [{ex}] thrown, <[{ex.Message}]>.");
}
return csInfo;
} }
private static Task Log(LogMessage msg) private static Task Log(LogMessage msg)
{ {
switch (msg.Severity) try
{ {
case LogSeverity.Critical: switch (msg.Severity)
_logger.Fatal(msg.Message); {
break; case LogSeverity.Critical:
case LogSeverity.Debug: _logger.Fatal(msg.Message);
_logger.Debug(msg.Message); break;
break; case LogSeverity.Debug:
case LogSeverity.Error: _logger.Debug(msg.Message);
_logger.Error(msg.Message); break;
break; case LogSeverity.Error:
case LogSeverity.Info: _logger.Error(msg.Message);
_logger.Info(msg.Message); break;
break; case LogSeverity.Info:
case LogSeverity.Warning: _logger.Info(msg.Message);
_logger.Warn(msg.Message); break;
break; case LogSeverity.Warning:
case LogSeverity.Verbose: _logger.Warn(msg.Message);
_logger.Trace(msg.Message); break;
break; case LogSeverity.Verbose:
default: _logger.Trace(msg.Message);
_logger.Trace(msg.Message); break;
break; default:
_logger.Trace(msg.Message);
break;
}
}
catch (Exception ex)
{
_logger.Error($"DiscordConnect.Log: Exception [{ex}] thrown, <[{ex.Message}]>.");
} }
return Task.CompletedTask; return Task.CompletedTask;

View File

@ -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<string>();
// 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());
}
}
}

View File

@ -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<string>("Bot:Name")}.");
sb.AppendLine();
sb.AppendLine($"Bot Version: {Program.Cfg.GetValue<string>("Bot:Version")}");
sb.AppendLine($"Bot Prefix: {Program.Cfg.GetValue<string>("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}]>.");
}
}
}
}

View File

@ -6,91 +6,91 @@ using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using NLog;
namespace ChaosBot.Discord.Services namespace ChaosBot.Discord.Services
{ {
public class CommandHandler public class CommandHandler
{ {
// setup fields to be set later in the constructor
private readonly IConfiguration _config; private readonly IConfiguration _config;
private readonly CommandService _commands; private readonly CommandService _commands;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IServiceProvider _services; private readonly IServiceProvider _services;
private readonly Logger _logger = Program._logger;
public CommandHandler(IServiceProvider services) public CommandHandler(IServiceProvider services)
{ {
// juice up the fields with these services try
// since we passed the services in, we can use GetRequiredService to pass them into the fields set earlier {
_config = services.GetRequiredService<IConfiguration>(); _config = services.GetRequiredService<IConfiguration>();
_commands = services.GetRequiredService<CommandService>(); _commands = services.GetRequiredService<CommandService>();
_client = services.GetRequiredService<DiscordSocketClient>(); _client = services.GetRequiredService<DiscordSocketClient>();
_services = services; _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() public async Task InitializeAsync()
{ {
// register modules that are public and inherit ModuleBase<T>.
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); 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) public async Task MessageReceivedAsync(SocketMessage rawMessage)
{ {
// ensures we don't process system/other bot messages try
if (!(rawMessage is SocketUserMessage message))
{ {
return; if (!(rawMessage is SocketUserMessage message))
} return;
if (message.Source != MessageSource.User) if (message.Source != MessageSource.User)
return;
var argPos = 0;
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);
await _commands.ExecuteAsync(context, argPos, _services);
}
catch (Exception ex)
{ {
return; _logger.Error($"CommandHandler.MessageReceivedAsync: Exception [{ex}] thrown, <[{ex.Message}]>.");
} }
// sets the argument position away from the prefix we set
var argPos = 0;
// get prefix from the configuration file
char prefix = Char.Parse(_config["Prefix"]);
// 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;
}
var context = new SocketCommandContext(_client, message);
// execute command if one is found that matches
await _commands.ExecuteAsync(context, argPos, _services);
} }
public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result) public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
{ {
// if a command isn't found, log that info to console and exit this method try
if (!command.IsSpecified)
{ {
System.Console.WriteLine($"Command failed to execute for [{context.User.Username}] <-> [{result.ErrorReason}]!"); if (!command.IsSpecified)
return; {
System.Console.WriteLine($"Command failed to execute for [{context.User.Username}] <-> [{result.ErrorReason}]!");
return;
}
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)
// log success to the console and exit this method
if (result.IsSuccess)
{ {
System.Console.WriteLine($"Command [{command.Value.Name}] executed for -> [{context.User.Username}]"); _logger.Error($"CommandHandler.CommandExecutedAsync: Exception [{ex}] thrown, <[{ex.Message}]>.");
return;
} }
// failure scenario, let's let the user know
await context.Channel.SendMessageAsync($"Sorry, {context.User.Username}... something went wrong -> [{result}]!");
} }
} }
} }

View File

@ -14,9 +14,9 @@ namespace ChaosBot
return LogManager.GetCurrentClassLogger(); return LogManager.GetCurrentClassLogger();
} }
catch (Exception e) catch (Exception ex)
{ {
Console.WriteLine(e); Console.WriteLine($"Logging.Logger: Exception [{ex}] thrown, <[{ex.Message}]>.");
throw; throw;
} }
} }

View File

@ -43,7 +43,7 @@ namespace ChaosBot
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, $"Stopped program because of exception"); _logger.Error(ex, $"Program.MainFunction: Exception [{ex}] thrown, <[{ex.Message}]>.");
} }
} }
} }