Bot cleanup

Language file updates
This commit is contained in:
Brett Hewitson 2021-12-18 10:10:43 +10:00
parent e72f5fb28f
commit 40b85340ae
21 changed files with 258 additions and 129 deletions

View file

@ -0,0 +1,41 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
namespace ServerManagerTool.DiscordBot.Attributes
{
public class RequireRoleAttribute : PreconditionAttribute
{
private readonly string _name;
public RequireRoleAttribute(string name)
{
_name = name;
}
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
// Check if this user is a Guild User, which is the only context where roles exist
if (context.User is SocketGuildUser guildUser)
{
// If this command was executed by a user with the appropriate role, return a success
if (guildUser.Roles.Any(r => r.Name == _name))
{
// Since no async work is done, the result has to be wrapped with `Task.FromResult` to avoid compiler errors
return Task.FromResult(PreconditionResult.FromSuccess());
}
else
{
// Since it wasn't, fail
return Task.FromResult(PreconditionResult.FromError($"You must have a role named '{_name}' to run this command."));
}
}
else
{
return Task.FromResult(PreconditionResult.FromError("You must be in a guild to run this command."));
}
}
}
}

View file

@ -21,5 +21,10 @@ namespace ServerManagerTool.DiscordBot.Enums
return logSeverity;
return LogSeverity.Info;
}
public static bool CheckLogLevel(LogLevel LogLevelToCheck, LogLevel configLogLevel)
{
return (int)configLogLevel >= (int)LogLevelToCheck;
}
}
}

View file

@ -1,6 +1,5 @@
using ServerManagerTool.DiscordBot.Delegates;
using ServerManagerTool.DiscordBot.Enums;
using System.Collections.Generic;
using ServerManagerTool.DiscordBot.Models;
using System.Threading;
using System.Threading.Tasks;
@ -10,6 +9,6 @@ namespace ServerManagerTool.DiscordBot.Interfaces
{
CancellationToken Token { get; }
Task StartAsync(LogLevel logLevel, string discordToken, string commandPrefix, string dataDirectory, bool allowAllBots, IEnumerable<string> botWhitelist, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token);
Task RunAsync(DiscordBotConfig discordBotConfig, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token);
}
}

View file

@ -1,11 +1,20 @@
using System.Collections.Generic;
using ServerManagerTool.DiscordBot.Enums;
using System.Collections.Generic;
namespace ServerManagerTool.DiscordBot.Models
{
public class DiscordBotConfig
{
public LogLevel LogLevel { get; set; } = LogLevel.Info;
public string DiscordToken { get; set; } = string.Empty;
public string CommandPrefix { get; set; } = string.Empty;
public string DataDirectory { get; set; } = string.Empty;
public bool AllowAllBots { get; set; } = false;
public List<DiscordBotWhitelist> DiscordBotWhitelists { get; set; } = new List<DiscordBotWhitelist>();
public IEnumerable<string> DiscordBotWhitelists { get; set; } = new List<string>();
}
}

View file

@ -1,7 +0,0 @@
namespace ServerManagerTool.DiscordBot.Models
{
public class DiscordBotWhitelist
{
public string BotId { get; set; } = string.Empty;
}
}

View file

@ -1,6 +1,6 @@
using Discord;
using Discord.Commands;
using Microsoft.Extensions.Configuration;
using ServerManagerTool.DiscordBot.Models;
using System;
using System.Collections.Generic;
using System.Linq;
@ -13,15 +13,15 @@ namespace ServerManagerTool.DiscordBot.Modules
{
private const int MAX_VALUE_LENGTH = 1024;
private readonly CommandService _service;
private readonly IConfigurationRoot _config;
private readonly CommandService _commands;
private readonly DiscordBotConfig _botConfig;
private readonly IServiceProvider _services;
public HelpModule(CommandService service, IConfigurationRoot config, IServiceProvider services)
public HelpModule(CommandService commands, IServiceProvider services, DiscordBotConfig botConfig)
{
_service = service;
_config = config;
_commands = commands;
_services = services;
_botConfig = botConfig;
}
[Command("help")]
@ -29,7 +29,7 @@ namespace ServerManagerTool.DiscordBot.Modules
[RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)]
public async Task HelpAsync()
{
var prefix = _config["DiscordSettings:Prefix"];
var prefix = _botConfig.CommandPrefix;
var builder = new EmbedBuilder()
{
@ -37,7 +37,7 @@ namespace ServerManagerTool.DiscordBot.Modules
Description = "These are the commands you can use"
};
foreach (var module in _service.Modules)
foreach (var module in _commands.Modules)
{
var moduleName = module.Name;
@ -106,7 +106,7 @@ namespace ServerManagerTool.DiscordBot.Modules
[RequireBotPermission(ChannelPermission.ViewChannel | ChannelPermission.SendMessages)]
public async Task HelpAsync(string command)
{
var searchResults = _service.Search(Context, command);
var searchResults = _commands.Search(Context, command);
if (!searchResults.IsSuccess)
{
@ -114,7 +114,7 @@ namespace ServerManagerTool.DiscordBot.Modules
return;
}
var prefix = _config["DiscordSettings:Prefix"];
var prefix = _botConfig.CommandPrefix;
var builder = new EmbedBuilder()
{

View file

@ -6,7 +6,6 @@ using ServerManagerTool.DiscordBot.Delegates;
using ServerManagerTool.DiscordBot.Enums;
using ServerManagerTool.DiscordBot.Interfaces;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ServerManagerTool.DiscordBot.Modules
@ -15,16 +14,14 @@ namespace ServerManagerTool.DiscordBot.Modules
public sealed class ServerCommandModule : InteractiveBase
{
private readonly IServerManagerBot _serverManagerBot;
private readonly CommandService _service;
private readonly CommandService _commands;
private readonly HandleCommandDelegate _handleCommandCallback;
private readonly IConfigurationRoot _config;
public ServerCommandModule(IServerManagerBot serverManagerBot, CommandService service, HandleCommandDelegate handleCommandCallback, IConfigurationRoot config)
public ServerCommandModule(IServerManagerBot serverManagerBot, CommandService commands, HandleCommandDelegate handleCommandCallback)
{
_serverManagerBot = serverManagerBot;
_service = service;
_serverManagerBot = serverManagerBot;
_commands = commands;
_handleCommandCallback = handleCommandCallback;
_config = config;
}
[Command("backup", RunMode = RunMode.Async)]

View file

@ -1,12 +1,10 @@
using Discord;
using Discord.Addons.Interactive;
using Discord.Commands;
using Microsoft.Extensions.Configuration;
using ServerManagerTool.DiscordBot.Delegates;
using ServerManagerTool.DiscordBot.Enums;
using ServerManagerTool.DiscordBot.Interfaces;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ServerManagerTool.DiscordBot.Modules
@ -15,16 +13,14 @@ namespace ServerManagerTool.DiscordBot.Modules
public sealed class ServerQueryModule : InteractiveBase
{
private readonly IServerManagerBot _serverManagerBot;
private readonly CommandService _service;
private readonly CommandService _commands;
private readonly HandleCommandDelegate _handleCommandCallback;
private readonly IConfigurationRoot _config;
public ServerQueryModule(IServerManagerBot serverManagerBot, CommandService service, HandleCommandDelegate handleCommandCallback, IConfigurationRoot config)
public ServerQueryModule(IServerManagerBot serverManagerBot, CommandService commands, HandleCommandDelegate handleCommandCallback)
{
_serverManagerBot = serverManagerBot;
_service = service;
_commands = commands;
_handleCommandCallback = handleCommandCallback;
_config = config;
}
[Command("info", RunMode = RunMode.Async)]

View file

@ -10,9 +10,7 @@ using ServerManagerTool.DiscordBot.Interfaces;
using ServerManagerTool.DiscordBot.Models;
using ServerManagerTool.DiscordBot.Services;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -28,19 +26,24 @@ namespace ServerManagerTool.DiscordBot
public CancellationToken Token { get; private set; }
public bool Started { get; private set; }
public async Task StartAsync(LogLevel logLevel, string discordToken, string commandPrefix, string dataDirectory, bool allowAllBots, IEnumerable<string> botWhitelist, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token)
public async Task RunAsync(DiscordBotConfig discordBotConfig, HandleCommandDelegate handleCommandCallback, HandleTranslationDelegate handleTranslationCallback, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(discordToken))
if (discordBotConfig is null || handleTranslationCallback is null || handleCommandCallback is null)
{
return;
}
if (string.IsNullOrWhiteSpace(discordBotConfig.DiscordToken))
{
throw new Exception("#DiscordBot_MissingTokenError");
}
if (string.IsNullOrWhiteSpace(commandPrefix))
if (string.IsNullOrWhiteSpace(discordBotConfig.CommandPrefix))
{
throw new Exception("#DiscordBot_MissingPrefixError");
}
if (Started || handleTranslationCallback is null || handleCommandCallback is null)
if (Started)
{
return;
}
@ -48,22 +51,13 @@ namespace ServerManagerTool.DiscordBot
Started = true;
Token = token;
var settings = new Dictionary<string, string>
{
{ "DiscordSettings:Token", discordToken },
{ "DiscordSettings:Prefix", commandPrefix },
{ "DiscordSettings:LogLevel", logLevel.ToString() },
{ "ServerManager:DataDirectory", dataDirectory },
};
// Begin building the configuration file
var config = new ConfigurationBuilder()
.AddInMemoryCollection(settings)
.Build();
var socketConfig = new DiscordSocketConfig
{
LogLevel = LogLevelHelper.GetLogSeverity(logLevel),
LogLevel = LogLevelHelper.GetLogSeverity(discordBotConfig.LogLevel),
MessageCacheSize = 1000,
};
if (Environment.OSVersion.Version < new Version(6, 2))
@ -76,16 +70,10 @@ namespace ServerManagerTool.DiscordBot
{
// Force all commands to run async
DefaultRunMode = RunMode.Async,
LogLevel = LogLevelHelper.GetLogSeverity(logLevel),
LogLevel = LogLevelHelper.GetLogSeverity(discordBotConfig.LogLevel),
CaseSensitiveCommands = false,
};
var discordBotConfig = new DiscordBotConfig
{
AllowAllBots = allowAllBots,
DiscordBotWhitelists = new List<DiscordBotWhitelist> ( botWhitelist.Select(i => new DiscordBotWhitelist { BotId = i }) ),
};
// Build the service provider
var services = new ServiceCollection()
// Add the discord client to the service provider

View file

@ -1,7 +1,6 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using ServerManagerTool.DiscordBot.Enums;
using ServerManagerTool.DiscordBot.Models;
using System;
@ -12,27 +11,51 @@ namespace ServerManagerTool.DiscordBot.Services
{
public class CommandHandlerService
{
private readonly DiscordSocketClient _discord;
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
private readonly LoggingService _logger;
private readonly IConfigurationRoot _config;
private readonly IServiceProvider _provider;
private readonly IServiceProvider _services;
private readonly DiscordBotConfig _botConfig;
public CommandHandlerService(DiscordSocketClient discord, CommandService commands, LoggingService logger, IConfigurationRoot config, IServiceProvider provider, DiscordBotConfig botConfig)
public CommandHandlerService(DiscordSocketClient client, CommandService commands, LoggingService logger, IServiceProvider services, DiscordBotConfig botConfig)
{
_discord = discord;
_client = client;
_commands = commands;
_logger = logger;
_config = config;
_provider = provider;
_services = services;
_botConfig = botConfig ?? new DiscordBotConfig();
_discord.MessageReceived += OnMessageReceivedAsync;
_commands.CommandExecuted += OnCommandExecutedAsync;
_client.MessageReceived += OnMessageReceivedAsync;
}
public async Task OnCommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
{
var commandName = command.IsSpecified ? command.Value.Name : "A command";
// We can tell the user what went wrong
if (!string.IsNullOrWhiteSpace(result?.ErrorReason))
{
switch (result?.Error)
{
case CommandError.BadArgCount:
await context.Channel.SendMessageAsync("Parameter count does not match any command.");
break;
default:
await context.Channel.SendMessageAsync(result.ErrorReason);
break;
}
}
if (LogLevelHelper.CheckLogLevel(LogLevel.Info, _botConfig.LogLevel))
{
await _logger?.OnLogAsync(new LogMessage(LogSeverity.Info, "CommandExecution", $"{commandName} was executed at {DateTime.Now}."));
}
}
private async Task OnMessageReceivedAsync(SocketMessage s)
{
if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"]))
if (LogLevelHelper.CheckLogLevel(LogLevel.Debug, _botConfig.LogLevel))
await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Intercepted the following message from {s.Author.Username} ({s.Author.Id}) - {s.Content}"));
// Ensure the message is a valid user socket message
@ -40,9 +63,9 @@ namespace ServerManagerTool.DiscordBot.Services
return;
// Ignore self
if (msg.Author.Id == _discord.CurrentUser.Id)
if (msg.Author.Id == _client.CurrentUser.Id)
{
if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"]))
if (LogLevelHelper.CheckLogLevel(LogLevel.Debug, _botConfig.LogLevel))
await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from this bot, message will be ignored."));
return;
@ -50,43 +73,39 @@ namespace ServerManagerTool.DiscordBot.Services
// check if the author is a bot
if (msg.Author.IsBot)
if (_botConfig.AllowAllBots)
{
if (_botConfig.AllowAllBots)
{
if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"]))
if (LogLevelHelper.CheckLogLevel(LogLevel.Debug, _botConfig.LogLevel))
await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from another bot, allow all bots enabled."));
}
else
{
if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"]))
if (LogLevelHelper.CheckLogLevel(LogLevel.Debug, _botConfig.LogLevel))
await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from another bot, checking if bot is in the whitelist."));
if (!_botConfig.DiscordBotWhitelists.Any(b => b.BotId.Equals(msg.Author.Id.ToString())))
if (!_botConfig.DiscordBotWhitelists.Any(botId => botId.Equals(msg.Author.Id.ToString())))
{
if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"]))
if (LogLevelHelper.CheckLogLevel(LogLevel.Debug, _botConfig.LogLevel))
await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from another bot, bot is not in the whitelist, message will be ignored."));
return;
}
}
}
}
// Check if the message has a valid command prefix
var argPos = 0;
if (msg.HasStringPrefix(_config["DiscordSettings:Prefix"], ref argPos, StringComparison.OrdinalIgnoreCase) || msg.HasMentionPrefix(_discord.CurrentUser, ref argPos))
if (msg.HasStringPrefix(_botConfig.CommandPrefix, ref argPos, StringComparison.OrdinalIgnoreCase) || msg.HasMentionPrefix(_client.CurrentUser, ref argPos))
{
if (LogLevel.Debug.ToString().Equals(_config["DiscordSettings:LogLevel"]))
if (LogLevelHelper.CheckLogLevel(LogLevel.Debug, _botConfig.LogLevel))
await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message prefix matched, message will be processed."));
// Create the command context
var context = new SocketCommandContext(_discord, msg);
var context = new SocketCommandContext(_client, msg);
// Execute the command
var result = await _commands.ExecuteAsync(context, argPos, _provider);
if (!result.IsSuccess)
{
// If not successful, reply with the error.
await context.Channel.SendMessageAsync(result.ToString());
}
await _commands.ExecuteAsync(context, argPos, _services);
}
}
}

View file

@ -1,7 +1,7 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using ServerManagerTool.DiscordBot.Models;
using System;
using System.IO;
using System.Threading.Tasks;
@ -10,24 +10,24 @@ namespace ServerManagerTool.DiscordBot.Services
{
public class LoggingService
{
private readonly DiscordSocketClient _discord;
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
private readonly IConfigurationRoot _config;
private readonly DiscordBotConfig _botConfig;
private string LogDirectory { get; }
private string LogFile => Path.Combine(LogDirectory, $"ServerManager_DiscordBot.{DateTime.Now:yyyyMMdd}.log");
public LoggingService(DiscordSocketClient discord, CommandService commands, IConfigurationRoot config)
public LoggingService(DiscordSocketClient client, CommandService commands, DiscordBotConfig botConfig)
{
_discord = discord;
_client = client;
_commands = commands;
_config = config;
_botConfig = botConfig;
// Get the data directory from the config file
var rootDirectory = _config["ServerManager:DataDirectory"] ?? AppContext.BaseDirectory;
var rootDirectory = _botConfig.DataDirectory ?? AppContext.BaseDirectory;
LogDirectory = Path.Combine(rootDirectory, "logs");
_discord.Log += OnLogAsync;
_client.Log += OnLogAsync;
_commands.Log += OnLogAsync;
}

View file

@ -5,17 +5,17 @@ namespace ServerManagerTool.DiscordBot.Services
{
public class ShutdownService
{
private readonly DiscordSocketClient _discord;
private readonly DiscordSocketClient _client;
public ShutdownService(DiscordSocketClient discord)
public ShutdownService(DiscordSocketClient client)
{
_discord = discord;
_client = client;
}
public async Task StopAsync()
{
await _discord.StopAsync();
await _discord.LogoutAsync();
await _client.StopAsync();
await _client.LogoutAsync();
}
}
}

View file

@ -1,7 +1,7 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using ServerManagerTool.DiscordBot.Models;
using System;
using System.Reflection;
using System.Threading.Tasks;
@ -10,23 +10,23 @@ namespace ServerManagerTool.DiscordBot.Services
{
public class StartupService
{
private readonly DiscordSocketClient _discord;
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
private readonly IConfigurationRoot _config;
private readonly IServiceProvider _provider;
private readonly IServiceProvider _services;
private readonly DiscordBotConfig _botConfig;
public StartupService(DiscordSocketClient discord, CommandService commands, IConfigurationRoot config, IServiceProvider provider)
public StartupService(DiscordSocketClient client, CommandService commands, IServiceProvider services, DiscordBotConfig botConfig)
{
_discord = discord;
_client = client;
_commands = commands;
_config = config;
_provider = provider;
_services = services;
_botConfig = botConfig;
}
public async Task StartAsync()
{
// Get the discord token from the config file
var discordToken = _config["DiscordSettings:Token"];
var discordToken = _botConfig?.DiscordToken;
if (string.IsNullOrWhiteSpace(discordToken))
{
@ -34,12 +34,12 @@ namespace ServerManagerTool.DiscordBot.Services
}
// Login to discord
await _discord.LoginAsync(TokenType.Bot, discordToken);
await _client.LoginAsync(TokenType.Bot, discordToken);
// Connect to the websocket
await _discord.StartAsync();
await _client.StartAsync();
// Load commands and modules into the command service
await _commands.AddModulesAsync(Assembly.GetExecutingAssembly(), _provider);
await _commands.AddModulesAsync(Assembly.GetExecutingAssembly(), _services);
}
}
}