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

@ -5,6 +5,7 @@ using NLog.Targets;
using ServerManagerTool.Common; using ServerManagerTool.Common;
using ServerManagerTool.Common.Utils; using ServerManagerTool.Common.Utils;
using ServerManagerTool.DiscordBot; using ServerManagerTool.DiscordBot;
using ServerManagerTool.DiscordBot.Models;
using ServerManagerTool.Enums; using ServerManagerTool.Enums;
using ServerManagerTool.Lib; using ServerManagerTool.Lib;
using ServerManagerTool.Plugin.Common; using ServerManagerTool.Plugin.Common;
@ -606,13 +607,20 @@ namespace ServerManagerTool
Task discordTask = Task.Run(async () => Task discordTask = Task.Run(async () =>
{ {
var discordWhiteList = new List<string>(); var config = new DiscordBotConfig
{
LogLevel = Config.Default.DiscordBotLogLevel,
DiscordToken = Config.Default.DiscordBotToken,
CommandPrefix = Config.Default.DiscordBotPrefix,
DataDirectory = Config.Default.DataDir,
AllowAllBots = Config.Default.DiscordBotAllowAllBots,
};
if (Config.Default.DiscordBotWhitelist != null) if (Config.Default.DiscordBotWhitelist != null)
{ {
discordWhiteList.AddRange(Config.Default.DiscordBotWhitelist.Cast<string>()); config.DiscordBotWhitelists = Config.Default.DiscordBotWhitelist.Cast<string>();
} }
await ServerManagerBotFactory.GetServerManagerBot()?.StartAsync(Config.Default.DiscordBotLogLevel, Config.Default.DiscordBotToken, Config.Default.DiscordBotPrefix, Config.Default.DataDir, Config.Default.DiscordBotAllowAllBots, discordWhiteList, DiscordBotHelper.HandleDiscordCommand, DiscordBotHelper.HandleTranslation, _tokenSourceDiscordBot.Token); await ServerManagerBotFactory.GetServerManagerBot().RunAsync(config, DiscordBotHelper.HandleDiscordCommand, DiscordBotHelper.HandleTranslation, _tokenSourceDiscordBot.Token);
if (_tokenSourceDiscordBot != null) if (_tokenSourceDiscordBot != null)
{ {

View file

@ -634,6 +634,8 @@
<sys:String x:Key="GlobalSettings_DiscordBotLogLevelLabel">Nível de registro:</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotLogLevelLabel">Nível de registro:</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotApplyButtonLabel">Obter Token...</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotApplyButtonLabel">Obter Token...</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotHelpButtonLabel">Ajuda...</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotHelpButtonLabel">Ajuda...</sys:String>
<sys:String x:Key="ServerSettings_DiscordBotAllowAllBotsLabel">Permitir todos os bots</sys:String>
<sys:String x:Key="ServerSettings_DiscordBotAllowAllBotsTooltip">Se ativado, o bot do gerenciador do servidor responderá a todos os outros bots, caso contrário, eles serão ignorados, a menos que estejam na lista de permissões.</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotWhitelistLabel">Bot Whitelist</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotWhitelistLabel">Bot Whitelist</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdLabel">Bot ID</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdLabel">Bot ID</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdTooltip">O id do bot a ser colocado na whitelist.</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdTooltip">O id do bot a ser colocado na whitelist.</sys:String>
@ -790,12 +792,18 @@
<sys:String x:Key="MainWindow_AutoUpdateTaskEnableLabel">Habilitar</sys:String> <sys:String x:Key="MainWindow_AutoUpdateTaskEnableLabel">Habilitar</sys:String>
<sys:String x:Key="MainWindow_AutoUpdateTaskEnableTooltip">Ativar a tarefa agendada de atualização automática</sys:String> <sys:String x:Key="MainWindow_AutoUpdateTaskEnableTooltip">Ativar a tarefa agendada de atualização automática</sys:String>
<sys:String x:Key="MainWindow_TaskRunTimeLabel">Próximo tempo de execução:</sys:String> <sys:String x:Key="MainWindow_TaskRunTimeLabel">Próximo tempo de execução:</sys:String>
<sys:String x:Key="MainWindow_DiscordBotStatusLabel">Discord Bot:</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStartLabel">Iniciar</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStartTooltip">Inicia o discord bot</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStopLabel">Parar</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStopTooltip">Para o discord bot</sys:String>
<sys:String x:Key="MainWindow_TaskStateUnknownLabel">Desconhecido</sys:String> <sys:String x:Key="MainWindow_TaskStateUnknownLabel">Desconhecido</sys:String>
<sys:String x:Key="MainWindow_TaskStateDisabledLabel">Desativado</sys:String> <sys:String x:Key="MainWindow_TaskStateDisabledLabel">Desativado</sys:String>
<sys:String x:Key="MainWindow_TaskStateQueuedLabel">Na fila</sys:String> <sys:String x:Key="MainWindow_TaskStateQueuedLabel">Na fila</sys:String>
<sys:String x:Key="MainWindow_TaskStateReadyLabel">Pronto</sys:String> <sys:String x:Key="MainWindow_TaskStateReadyLabel">Pronto</sys:String>
<sys:String x:Key="MainWindow_TaskStateRunningLabel">Running</sys:String> <sys:String x:Key="MainWindow_TaskStateRunningLabel">Running</sys:String>
<sys:String x:Key="MainWindow_TaskStateStoppedLabel">Parado</sys:String>
<sys:String x:Key="MainWindow_ProfileLoad_FailedTitle">O perfil não foi carregado</sys:String> <sys:String x:Key="MainWindow_ProfileLoad_FailedTitle">O perfil não foi carregado</sys:String>
<sys:String x:Key="MainWindow_ProfileLoad_FailedLabel">O perfil em {0} falhou ao carregar. O erro foi: {1}\r\n{2}</sys:String> <sys:String x:Key="MainWindow_ProfileLoad_FailedLabel">O perfil em {0} falhou ao carregar. O erro foi: {1}\r\n{2}</sys:String>
@ -5612,6 +5620,7 @@
<!--#region Discord Bot --> <!--#region Discord Bot -->
<sys:String x:Key="DiscordBot_ErrorTitle">Discord Bot Error</sys:String> <sys:String x:Key="DiscordBot_ErrorTitle">Discord Bot Error</sys:String>
<sys:String x:Key="DiscordBot_MissingTokenError">O discord bot requer um token válido para que possa se conectar ao servidor discord\r\nIsso pode ser definido nas configurações globais.</sys:String> <sys:String x:Key="DiscordBot_MissingTokenError">O discord bot requer um token válido para que possa se conectar ao servidor discord\r\nIsso pode ser definido nas configurações globais.</sys:String>
<sys:String x:Key="DiscordBot_MissingPrefixError">O discord bot requer um prefixo válido.\r\nIsso pode ser definido nas configurações globais.</sys:String>
<sys:String x:Key="DiscordBot_InvalidPrefixError">O prefixo discord bot contém caracteres inválidos. Somente letras e números são permitidos.</sys:String> <sys:String x:Key="DiscordBot_InvalidPrefixError">O prefixo discord bot contém caracteres inválidos. Somente letras e números são permitidos.</sys:String>
<sys:String x:Key="DiscordBot_CommandNotEnabled">O comando '{0}' não foi habilitado.</sys:String> <sys:String x:Key="DiscordBot_CommandNotEnabled">O comando '{0}' não foi habilitado.</sys:String>

View file

@ -9,7 +9,7 @@
<!--#region Generic --> <!--#region Generic -->
<sys:String x:Key="Generic_TranslatedByLabel">Последнее обновление:</sys:String> <sys:String x:Key="Generic_TranslatedByLabel">Последнее обновление:</sys:String>
<sys:String x:Key="Generic_TranslatedBy">16.12.2021 от Varlonec, Эдван</sys:String> <sys:String x:Key="Generic_TranslatedBy">17.12.2021 от Varlonec, Эдван</sys:String>
<sys:String x:Key="Generic_ErrorLabel">Ошибка</sys:String> <sys:String x:Key="Generic_ErrorLabel">Ошибка</sys:String>
<!--#endregion--> <!--#endregion-->
@ -634,6 +634,8 @@
<sys:String x:Key="GlobalSettings_DiscordBotLogLevelLabel">Лог уровня:</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotLogLevelLabel">Лог уровня:</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotApplyButtonLabel">Получить токен ...</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotApplyButtonLabel">Получить токен ...</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotHelpButtonLabel">Помощь...</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotHelpButtonLabel">Помощь...</sys:String>
<sys:String x:Key="ServerSettings_DiscordBotAllowAllBotsLabel">Разрешить всем ботам</sys:String>
<sys:String x:Key="ServerSettings_DiscordBotAllowAllBotsTooltip">Если включено, менеджер сервера будет отвечать всем остальным ботам, в противном случае они будут игнорироваться, если только они не находятся в белом списке.</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotWhitelistLabel">Белый лист бота</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotWhitelistLabel">Белый лист бота</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdLabel">Бот ID</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdLabel">Бот ID</sys:String>
<sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdTooltip">ID бота для внесения в белый список.</sys:String> <sys:String x:Key="GlobalSettings_DiscordBotWhitelistIdTooltip">ID бота для внесения в белый список.</sys:String>
@ -790,13 +792,19 @@
<sys:String x:Key="MainWindow_AutoUpdateTaskEnableLabel">Включить</sys:String> <sys:String x:Key="MainWindow_AutoUpdateTaskEnableLabel">Включить</sys:String>
<sys:String x:Key="MainWindow_AutoUpdateTaskEnableTooltip">Включить Авто-Обновленя по заданному времени</sys:String> <sys:String x:Key="MainWindow_AutoUpdateTaskEnableTooltip">Включить Авто-Обновленя по заданному времени</sys:String>
<sys:String x:Key="MainWindow_TaskRunTimeLabel">Следующее время выполнения:</sys:String> <sys:String x:Key="MainWindow_TaskRunTimeLabel">Следующее время выполнения:</sys:String>
<sys:String x:Key="MainWindow_DiscordBotStatusLabel">Discord бот:</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStartLabel">Старт</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStartTooltip">Запуск бота Discord</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStopLabel">Стоп</sys:String>
<sys:String x:Key="MainWindow_DiscordBotTaskStopTooltip">Остановить бот Discord</sys:String>
<sys:String x:Key="MainWindow_TaskStateUnknownLabel">Отключено</sys:String> <sys:String x:Key="MainWindow_TaskStateUnknownLabel">Отключено</sys:String>
<sys:String x:Key="MainWindow_TaskStateDisabledLabel">Выключено</sys:String> <sys:String x:Key="MainWindow_TaskStateDisabledLabel">Выключено</sys:String>
<sys:String x:Key="MainWindow_TaskStateQueuedLabel">В очереди</sys:String> <sys:String x:Key="MainWindow_TaskStateQueuedLabel">В очереди</sys:String>
<sys:String x:Key="MainWindow_TaskStateReadyLabel">Активно</sys:String> <sys:String x:Key="MainWindow_TaskStateReadyLabel">Активно</sys:String>
<sys:String x:Key="MainWindow_TaskStateRunningLabel">Запущено</sys:String> <sys:String x:Key="MainWindow_TaskStateRunningLabel">Запущено</sys:String>
<sys:String x:Key="MainWindow_TaskStateStoppedLabel">Остановлено</sys:String>
<sys:String x:Key="MainWindow_ProfileLoad_FailedTitle">Профиль не загружен</sys:String> <sys:String x:Key="MainWindow_ProfileLoad_FailedTitle">Профиль не загружен</sys:String>
<sys:String x:Key="MainWindow_ProfileLoad_FailedLabel">Не удалось загрузить профиль в {0}. Ошибка: {1}\r\n{2}</sys:String> <sys:String x:Key="MainWindow_ProfileLoad_FailedLabel">Не удалось загрузить профиль в {0}. Ошибка: {1}\r\n{2}</sys:String>
<sys:String x:Key="MainWindow_ProfileDelete_Title">Удалить Профиль {0}?</sys:String> <sys:String x:Key="MainWindow_ProfileDelete_Title">Удалить Профиль {0}?</sys:String>
@ -5689,6 +5697,7 @@
<!--#region Discord Bot --> <!--#region Discord Bot -->
<sys:String x:Key="DiscordBot_ErrorTitle">Ошибка бота Discord</sys:String> <sys:String x:Key="DiscordBot_ErrorTitle">Ошибка бота Discord</sys:String>
<sys:String x:Key="DiscordBot_MissingTokenError">Бот Discord требует действующий токен, чтобы он мог войти на сервер Discord\r\nЭто можно установить в глобальных настройках.</sys:String> <sys:String x:Key="DiscordBot_MissingTokenError">Бот Discord требует действующий токен, чтобы он мог войти на сервер Discord\r\nЭто можно установить в глобальных настройках.</sys:String>
<sys:String x:Key="DiscordBot_MissingPrefixError">Для бота discord требуется действительный префикс.\r\nЭто можно установить в глобальных настройках.</sys:String>
<sys:String x:Key="DiscordBot_InvalidPrefixError">Префикс бота Discord содержит недопустимые символы. Разрешены только буквы и цифры.</sys:String> <sys:String x:Key="DiscordBot_InvalidPrefixError">Префикс бота Discord содержит недопустимые символы. Разрешены только буквы и цифры.</sys:String>
<sys:String x:Key="DiscordBot_CommandNotEnabled">Команда '{0}' не активирована.</sys:String> <sys:String x:Key="DiscordBot_CommandNotEnabled">Команда '{0}' не активирована.</sys:String>

View file

@ -5,14 +5,14 @@
<title>Ark Server Manager Version Feed</title> <title>Ark Server Manager Version Feed</title>
<subtitle>This is the Ark Server Manager release version feed.</subtitle> <subtitle>This is the Ark Server Manager release version feed.</subtitle>
<link href="http://arkservermanager.freeforums.net/" /> <link href="http://arkservermanager.freeforums.net/" />
<updated>2021-12-17T00:00:00Z</updated> <updated>2021-12-18T00:00:00Z</updated>
<entry> <entry>
<id>urn:uuid:3E33DCB2-ECFE-4489-B1A4-56F5D386F9DC</id> <id>urn:uuid:3E33DCB2-ECFE-4489-B1A4-56F5D386F9DC</id>
<title>1.1.413 (1.1.413.8)</title> <title>1.1.413 (1.1.413.9)</title>
<summary>1.1.413.8</summary> <summary>1.1.413.9</summary>
<link href="" /> <link href="" />
<updated>2021-12-17T00:00:00Z</updated> <updated>2021-12-18T00:00:00Z</updated>
<content type="xhtml"> <content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;"> <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
<p> <p>

View file

@ -7,6 +7,31 @@
<link href="http://arkservermanager.freeforums.net/" /> <link href="http://arkservermanager.freeforums.net/" />
<updated>2021-12-16T00:00:00Z</updated> <updated>2021-12-16T00:00:00Z</updated>
<entry>
<id>urn:uuid:1B7448FB-E4E0-4E0F-BF87-AE8661AA7D93</id>
<title>1.1.413 (1.1.413.9)</title>
<summary>1.1.413.9</summary>
<link href="" />
<updated>2021-12-18T00:00:00Z</updated>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
<p>
<u style="font-size: .9em;">CHANGE</u>
<br/>
<ul>
<li>Discord Bot - added some additional logging and some code cleanup.</li>
<li>pt-BR Translation file updated.</li>
<li>ru-RU Translation file updated.</li>
</ul>
</p>
</div>
</content>
<author>
<name>bletch</name>
<email>bletch1971@hotmail.com</email>
</author>
</entry>
<entry> <entry>
<id>urn:uuid:8EE5659C-18E6-47D3-941D-C32B129D2E06</id> <id>urn:uuid:8EE5659C-18E6-47D3-941D-C32B129D2E06</id>
<title>1.1.413 (1.1.413.8)</title> <title>1.1.413 (1.1.413.8)</title>

View file

@ -4,6 +4,7 @@ using NLog.Targets;
using ServerManagerTool.Common; using ServerManagerTool.Common;
using ServerManagerTool.Common.Utils; using ServerManagerTool.Common.Utils;
using ServerManagerTool.DiscordBot; using ServerManagerTool.DiscordBot;
using ServerManagerTool.DiscordBot.Models;
using ServerManagerTool.Enums; using ServerManagerTool.Enums;
using ServerManagerTool.Lib; using ServerManagerTool.Lib;
using ServerManagerTool.Plugin.Common; using ServerManagerTool.Plugin.Common;
@ -587,13 +588,20 @@ namespace ServerManagerTool
Task discordTask = Task.Run(async () => Task discordTask = Task.Run(async () =>
{ {
var discordWhiteList = new List<string>(); var config = new DiscordBotConfig
{
LogLevel = Config.Default.DiscordBotLogLevel,
DiscordToken = Config.Default.DiscordBotToken,
CommandPrefix = Config.Default.DiscordBotPrefix,
DataDirectory = Config.Default.DataPath,
AllowAllBots = Config.Default.DiscordBotAllowAllBots,
};
if (Config.Default.DiscordBotWhitelist != null) if (Config.Default.DiscordBotWhitelist != null)
{ {
discordWhiteList.AddRange(Config.Default.DiscordBotWhitelist.Cast<string>()); config.DiscordBotWhitelists = Config.Default.DiscordBotWhitelist.Cast<string>();
} }
await ServerManagerBotFactory.GetServerManagerBot()?.StartAsync(Config.Default.DiscordBotLogLevel, Config.Default.DiscordBotToken, Config.Default.DiscordBotPrefix, Config.Default.DataPath, Config.Default.DiscordBotAllowAllBots, discordWhiteList, DiscordBotHelper.HandleDiscordCommand, DiscordBotHelper.HandleTranslation, _tokenSourceDiscordBot.Token); await ServerManagerBotFactory.GetServerManagerBot().RunAsync(config, DiscordBotHelper.HandleDiscordCommand, DiscordBotHelper.HandleTranslation, _tokenSourceDiscordBot.Token);
if (_tokenSourceDiscordBot != null) if (_tokenSourceDiscordBot != null)
{ {

View file

@ -5,14 +5,14 @@
<title>Conan Server Manager Version Feed</title> <title>Conan Server Manager Version Feed</title>
<subtitle>This is the Conan Server Manager release version feed.</subtitle> <subtitle>This is the Conan Server Manager release version feed.</subtitle>
<link href="http://servermanagers.freeforums.net/" /> <link href="http://servermanagers.freeforums.net/" />
<updated>2021-12-16T00:00:00Z</updated> <updated>2021-12-18T00:00:00Z</updated>
<entry> <entry>
<id>urn:uuid:19B09A66-43F2-4D5F-AF33-5C77D7EA9A6B</id> <id>urn:uuid:19B09A66-43F2-4D5F-AF33-5C77D7EA9A6B</id>
<title>1.1.58 (1.1.58.8)</title> <title>1.1.58 (1.1.58.9)</title>
<summary>1.1.58.8</summary> <summary>1.1.58.9</summary>
<link href="" /> <link href="" />
<updated>2021-12-16T00:00:00Z</updated> <updated>2021-12-18T00:00:00Z</updated>
<content type="xhtml"> <content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;"> <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
<p> <p>

View file

@ -7,6 +7,29 @@
<link href="http://servermanagers.freeforums.net/" /> <link href="http://servermanagers.freeforums.net/" />
<updated>2021-12-17T00:00:00Z</updated> <updated>2021-12-17T00:00:00Z</updated>
<entry>
<id>urn:uuid:9A427D82-9904-44F5-8C1E-7C943049869A</id>
<title>1.1.58 (1.1.58.9)</title>
<summary>1.1.58.9</summary>
<link href="" />
<updated>2021-12-18T00:00:00Z</updated>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
<p>
<u style="font-size: .9em;">CHANGE</u>
<br/>
<ul>
<li>Discord Bot - added some additional logging and some code cleanup.</li>
</ul>
</p>
</div>
</content>
<author>
<name>bletch</name>
<email>bletch1971@hotmail.com</email>
</author>
</entry>
<entry> <entry>
<id>urn:uuid:9A427D82-9904-44F5-8C1E-7C943049869A</id> <id>urn:uuid:9A427D82-9904-44F5-8C1E-7C943049869A</id>
<title>1.1.58 (1.1.58.8)</title> <title>1.1.58 (1.1.58.8)</title>

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,6 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using ServerManagerTool.DiscordBot.Enums; using ServerManagerTool.DiscordBot.Enums;
using ServerManagerTool.DiscordBot.Models; using ServerManagerTool.DiscordBot.Models;
using System; using System;
@ -12,27 +11,51 @@ namespace ServerManagerTool.DiscordBot.Services
{ {
public class CommandHandlerService public class CommandHandlerService
{ {
private readonly DiscordSocketClient _discord; private readonly DiscordSocketClient _client;
private readonly CommandService _commands; private readonly CommandService _commands;
private readonly LoggingService _logger; private readonly LoggingService _logger;
private readonly IConfigurationRoot _config; private readonly IServiceProvider _services;
private readonly IServiceProvider _provider;
private readonly DiscordBotConfig _botConfig; 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; _commands = commands;
_logger = logger; _logger = logger;
_config = config; _services = services;
_provider = provider;
_botConfig = botConfig ?? new DiscordBotConfig(); _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) 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}")); 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 // Ensure the message is a valid user socket message
@ -40,9 +63,9 @@ namespace ServerManagerTool.DiscordBot.Services
return; return;
// Ignore self // 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.")); await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from this bot, message will be ignored."));
return; return;
@ -50,43 +73,39 @@ namespace ServerManagerTool.DiscordBot.Services
// check if the author is a bot // check if the author is a bot
if (msg.Author.IsBot) 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.")); await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message has come from another bot, allow all bots enabled."));
} }
else 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.")); 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.")); 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; return;
} }
} }
}
// Check if the message has a valid command prefix // Check if the message has a valid command prefix
var argPos = 0; 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.")); await _logger?.OnLogAsync(new LogMessage(LogSeverity.Debug, MessageSource.System.ToString(), $"Message prefix matched, message will be processed."));
// Create the command context // Create the command context
var context = new SocketCommandContext(_discord, msg); var context = new SocketCommandContext(_client, msg);
// Execute the command // Execute the command
var result = await _commands.ExecuteAsync(context, argPos, _provider); await _commands.ExecuteAsync(context, argPos, _services);
if (!result.IsSuccess)
{
// If not successful, reply with the error.
await context.Channel.SendMessageAsync(result.ToString());
}
} }
} }
} }

View file

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

View file

@ -5,17 +5,17 @@ namespace ServerManagerTool.DiscordBot.Services
{ {
public class ShutdownService 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() public async Task StopAsync()
{ {
await _discord.StopAsync(); await _client.StopAsync();
await _discord.LogoutAsync(); await _client.LogoutAsync();
} }
} }
} }

View file

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