Skip to content

Commit

Permalink
Merge branch 'dev' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
BrammyS committed Aug 10, 2021
2 parents 6398de0 + 6754dc7 commit becb50b
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 163 deletions.
1 change: 1 addition & 0 deletions samples/Pong/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public void ConfigureServices(IServiceCollection services)
{
slashOptions.EnableAutoSync = true;
slashOptions.EnableAutoGetGuild = true;
slashOptions.SendDefaultErrorMessage = true;
}
};

Expand Down
7 changes: 4 additions & 3 deletions samples/RoleManager/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ public void ConfigureServices(IServiceCollection services)
{
SlashCommandConfigs = slashOptions =>
{
slashOptions.EnableAutoSync = true; // <---
slashOptions.EnableAutoGetGuild = true; // <---
slashOptions.EnableAutoSync = true;
slashOptions.EnableAutoGetGuild = true;
slashOptions.SendDefaultErrorMessage = true;
}
};
};

// Replace the arguments with the data of your bot.
// Note: It is not recommended to hardcode them in, loading them from an environment variable or from a json file is better.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Color_Chan.Discord.Commands.Configurations;
using System.Collections.Generic;
using Color_Chan.Discord.Commands.Configurations;
using Color_Chan.Discord.Core.Common.Models;
using Color_Chan.Discord.Core.Common.Models.Guild;
using Color_Chan.Discord.Core.Common.Models.Interaction;
Expand Down Expand Up @@ -84,6 +85,11 @@ public interface ISlashCommandContext
/// <remarks>
/// Used to create a unique string for rate limiting.
/// </remarks>
string MethodName { get; set; }
string? MethodName { get; set; }

/// <summary>
/// The full slash command name.
/// </summary>
IEnumerable<string> SlashCommandName { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Color_Chan.Discord.Core.Common.Models;
using System.Collections.Generic;
using Color_Chan.Discord.Core.Common.Models;
using Color_Chan.Discord.Core.Common.Models.Guild;
using Color_Chan.Discord.Core.Common.Models.Interaction;
using Color_Chan.Discord.Core.Common.Models.Message;
Expand Down Expand Up @@ -42,6 +43,9 @@ public class SlashCommandContext : ISlashCommandContext
public ulong InteractionId { get; init; }

/// <inheritdoc />
public string MethodName { get; set; } = null!;
public string? MethodName { get; set; }

/// <inheritdoc />
public IEnumerable<string> SlashCommandName { get; set; } = null!;
}
}
44 changes: 1 addition & 43 deletions src/Color-Chan.Discord.Commands/Services/ISlashCommandService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(ISlashCommand
/// Execute a specific command with their dependencies.
/// The command will be searched for with its <see cref="SlashCommandAttribute.Name" />.
/// </summary>
/// <param name="name">The name of the command.</param>
/// <param name="context">The current <see cref="ISlashCommandContext" /> that will be passed to the command module.</param>
/// <param name="options">The options used with the command.</param>
/// <param name="serviceProvider">
Expand All @@ -99,50 +98,9 @@ Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(ISlashCommand
/// </returns>
/// <seealso cref="Result" />
/// <seealso cref="SlashCommandAttribute" />
Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(string name, ISlashCommandContext context, IEnumerable<IDiscordInteractionCommandOption>? options = null,
Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(ISlashCommandContext context, IEnumerable<IDiscordInteractionCommandOption>? options = null,
IServiceProvider? serviceProvider = null);

/// <summary>
/// Execute a specific command with their dependencies.
/// The command will be searched for with its <see cref="SlashCommandAttribute.Name" />.
/// </summary>
/// <param name="commandGroupName">The name of the command group.</param>
/// <param name="commandName">The name of the command.</param>
/// <param name="context">The current <see cref="ISlashCommandContext" /> that will be passed to the command module.</param>
/// <param name="options">The options used with the command.</param>
/// <param name="serviceProvider">
/// The <see cref="IServiceProvider" /> containing the necessary dependencies for the the
/// module of the command.
/// </param>
/// <returns>
/// The <see cref="Result" /> containing the result of the command execution.
/// </returns>
/// <seealso cref="Result" />
/// <seealso cref="SlashCommandAttribute" />
Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(string commandGroupName, string commandName, ISlashCommandContext context,
IEnumerable<IDiscordInteractionCommandOption>? options = null, IServiceProvider? serviceProvider = null);

/// <summary>
/// Execute a specific command with their dependencies.
/// The command will be searched for with its <see cref="SlashCommandAttribute.Name" />.
/// </summary>
/// <param name="commandGroupName">The name of the command group.</param>
/// <param name="subCommandGroupName">The name of the sub command group.</param>
/// <param name="commandName">The name of the command.</param>
/// <param name="context">The current <see cref="ISlashCommandContext" /> that will be passed to the command module.</param>
/// <param name="options">The options used with the command.</param>
/// <param name="serviceProvider">
/// The <see cref="IServiceProvider" /> containing the necessary dependencies for the the
/// module of the command.
/// </param>
/// <returns>
/// The <see cref="Result" /> containing the result of the command execution.
/// </returns>
/// <seealso cref="Result" />
/// <seealso cref="SlashCommandAttribute" />
Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(string commandGroupName, string subCommandGroupName, string commandName, ISlashCommandContext context,
IEnumerable<IDiscordInteractionCommandOption>? options = null, IServiceProvider? serviceProvider = null);

/// <summary>
/// Search for a command by its <see cref="SlashCommandAttribute.Name" />.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,30 @@ public async Task<IDiscordInteractionResponse> HandleSlashCommandAsync(IDiscordI
Channel = channel,
Guild = guild
};

// Local method to execute the command.
async Task<Result<IDiscordInteractionResponse>> Handler()

// Get the command name.
if (interaction.Data.Options is null)
{
context.SlashCommandName = new []{interaction.Data.Name};
}
else
{
if (interaction.Data.Options is null)
{
return await _slashCommandService.ExecuteSlashCommandAsync(interaction.Data.Name, context, interaction.Data.Options?.ToList(), _serviceProvider).ConfigureAwait(false);
}

// Check if any of the used options was a sub command (group) and execute it.
foreach (var option in interaction.Data.Options)
{
return option.Type switch
context.SlashCommandName = option.Type switch
{
DiscordApplicationCommandOptionType.SubCommand => await ExecuteSubCommandAsync(interaction.Data.Name, option, context).ConfigureAwait(false),
DiscordApplicationCommandOptionType.SubCommandGroup => await ExecuteSubCommandGroupAsync(interaction.Data.Name, option, context).ConfigureAwait(false),
DiscordApplicationCommandOptionType.SubCommand => context.SlashCommandName = new []{interaction.Data.Name, option.Name},
DiscordApplicationCommandOptionType.SubCommandGroup => new []{interaction.Data.Name, option.Name, GetSubGroupCommandName(option)},
_ => throw new InvalidSlashCommandGroupException("Failed to find sub command")
};
}

throw new InvalidSlashCommandGroupException("Failed to find top level/sub command");
}

// Local method to execute the command.
async Task<Result<IDiscordInteractionResponse>> Handler()
{
return await _slashCommandService.ExecuteSlashCommandAsync(context, interaction.Data.Options?.ToList(), _serviceProvider).ConfigureAwait(false);
}

// Execute the pipelines and the command.
Expand All @@ -120,7 +123,7 @@ async Task<Result<IDiscordInteractionResponse>> Handler()

throw new SlashCommandResultException($"Command request {interaction.Id} returned unsuccessfully, {result.ErrorResult?.ErrorMessage}");
}

/// <summary>
/// Get a default error message response.
/// </summary>
Expand All @@ -142,33 +145,8 @@ private IDiscordInteractionResponse DefaultErrorMessage()
// Return the response to Discord.
return errorResponse;
}

/// <summary>
/// Executes a sub slash command.
/// </summary>
/// <param name="commandGroupName">The command group the sub command belongs to.</param>
/// <param name="option">The options used with the command request.</param>
/// <param name="context">The current context of the command request.</param>
/// <returns>
/// A <see cref="IDiscordInteractionResponse" /> with the the slash command response.
/// </returns>
private Task<Result<IDiscordInteractionResponse>> ExecuteSubCommandAsync(string commandGroupName, IDiscordInteractionCommandOption option, ISlashCommandContext context)
{
return _slashCommandService.ExecuteSlashCommandAsync(commandGroupName, option.Name, context, option.SubOptions?.ToList(), _serviceProvider);
}

/// <summary>
/// Executes a sub slash command in a sub command group.
/// </summary>
/// <param name="commandGroupName">The command group the sub command group belongs to.</param>
/// <param name="option">The options used with the command request.</param>
/// <param name="context">The current context of the command request.</param>
/// <returns>
/// A <see cref="IDiscordInteractionResponse" /> with the the slash command response.
/// </returns>
/// <exception cref="NullReferenceException">Thrown when the sub options are null.</exception>
/// <exception cref="InvalidSlashCommandGroupException">Thrown when no sub command has been found.</exception>
private Task<Result<IDiscordInteractionResponse>> ExecuteSubCommandGroupAsync(string commandGroupName, IDiscordInteractionCommandOption option, ISlashCommandContext context)

private string GetSubGroupCommandName(IDiscordInteractionCommandOption option)
{
if (option.SubOptions is null)
{
Expand All @@ -180,13 +158,13 @@ private Task<Result<IDiscordInteractionResponse>> ExecuteSubCommandGroupAsync(st
{
if (subOption.Type == DiscordApplicationCommandOptionType.SubCommand)
{
return _slashCommandService.ExecuteSlashCommandAsync(commandGroupName, option.Name, subOption.Name, context, subOption.SubOptions?.ToList(), _serviceProvider);
return subOption.Name;
}
}

// The command group had no sub command.
var exception = new InvalidSlashCommandGroupException($"Command group {commandGroupName} had no sub command");
_logger.LogError(exception, "Command group {Name} had no sub command", commandGroupName);
var exception = new InvalidSlashCommandGroupException($"Command group {option.Name} had no sub command");
_logger.LogError(exception, "Command group {Name} had no sub command", option.Name);
throw exception;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,54 +169,41 @@ public async Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(
}

/// <inheritdoc />
public async Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(string name, ISlashCommandContext context, IEnumerable<IDiscordInteractionCommandOption>? options = null,
IServiceProvider? serviceProvider = null)
{
var commandInfo = SearchSlashCommand(name);

if (commandInfo is null)
{
_logger.LogWarning("Failed to find slash command {Name}", name);
return Result<IDiscordInteractionResponse>.FromError(default, new ErrorResult($"Failed to find command {name}"));
}

if (commandInfo.CommandMethod is null)
{
_logger.LogWarning("Failed to executed {Name} since it was a command group or a sub command group", commandInfo.CommandName);
return Result<IDiscordInteractionResponse>.FromError(default, new ErrorResult("Can not execute command group or sub command group"));
}

return await ExecuteSlashCommandAsync(commandInfo, context, options?.ToList(), serviceProvider).ConfigureAwait(false);
}

/// <inheritdoc />
public async Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(string commandGroupName, string commandName, ISlashCommandContext context,
IEnumerable<IDiscordInteractionCommandOption>? options = null, IServiceProvider? serviceProvider = null)
public async Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(ISlashCommandContext context, IEnumerable<IDiscordInteractionCommandOption>? options = null, IServiceProvider? serviceProvider = null)
{
var searchResult = SearchSlashCommand(commandGroupName, commandName);

if (searchResult is null)
var arr = context.SlashCommandName.ToArray();
var count = arr.Length;

switch (count)
{
_logger.LogWarning("Command {GroupName} {CommandName} is not registered", commandGroupName, commandName);
return Result<IDiscordInteractionResponse>.FromError(default, new ErrorResult($"Failed to find command {commandGroupName} {commandName}"));
case 1:
{
var command = SearchSlashCommand(arr[0]);
if (command is null) return NoCommandFoundResponse();
return await ExecuteSlashCommandAsync(command, context, options?.ToList(), serviceProvider).ConfigureAwait(false);
}
case 2:
{
var subCommand = SearchSlashCommand(arr[0], arr[1]);
if (subCommand is null) return NoCommandFoundResponse();
return await ExecuteSlashCommandAsync(subCommand, context, options?.ToList(), serviceProvider).ConfigureAwait(false);
}
case 3:
{
var subCommand = SearchSlashCommand(arr[0], arr[1], arr[2]);
if (subCommand is null) return NoCommandFoundResponse();
return await ExecuteSlashCommandAsync(subCommand, context, options?.ToList(), serviceProvider).ConfigureAwait(false);
}
}

return await ExecuteSlashCommandAsync(searchResult, context, options?.ToList(), serviceProvider).ConfigureAwait(false);
}

/// <inheritdoc />
public async Task<Result<IDiscordInteractionResponse>> ExecuteSlashCommandAsync(string commandGroupName, string subCommandGroupName, string commandName, ISlashCommandContext context,
IEnumerable<IDiscordInteractionCommandOption>? options = null, IServiceProvider? serviceProvider = null)
{
var searchResult = SearchSlashCommand(commandGroupName, subCommandGroupName, commandName);

if (searchResult is null)
Result<IDiscordInteractionResponse> NoCommandFoundResponse()
{
_logger.LogWarning("Command {GroupName} {SubCommandGroupName} {CommandName} is not registered", commandGroupName, subCommandGroupName, commandName);
return Result<IDiscordInteractionResponse>.FromError(default, new ErrorResult($"Failed to find command {commandGroupName} {subCommandGroupName} {commandName}"));
var readableCommandName = string.Join(" ", context.SlashCommandName);
_logger.LogWarning("Command {Name} is not registered", context.SlashCommandName);
return Result<IDiscordInteractionResponse>.FromError(default, new ErrorResult($"Failed to find command {readableCommandName}"));
}

return await ExecuteSlashCommandAsync(searchResult, context, options?.ToList(), serviceProvider).ConfigureAwait(false);
throw new ArgumentOutOfRangeException(nameof(context.SlashCommandName), "A command name needs to be between 1 one 3 words");
}

/// <inheritdoc />
Expand Down
Loading

0 comments on commit becb50b

Please sign in to comment.