@@ -3,9 +3,6 @@ uid: Guides.Commands.PostExecution
title: Post-command Execution Handling
---
> [!WARNING]
> This page is still under construction!
# Preface
When developing a command system or modules, you may want to consider
@@ -40,49 +37,15 @@ may not always achieve the desired effect.
Enter [CommandExecuted], an event that was introduced in
Discord.Net 2.0. This event is raised when the command is
sucessfully executed **without any runtime exceptions** (more on this
later). This means this event can be used to streamline your
post-execution design, and the best thing about this event is that it
is not prone to `RunMode.Async`'s [ExecuteAsync] drawbacks.
sucessfully executed **without any runtime exceptions** or **without
any parsing or precondition failure**. This means this event can be
used to streamline your post-execution design, and the best thing
about this event is that it is not prone to `RunMode.Async`'s
[ExecuteAsync] drawbacks.
With that in mind, we can begin working on code such as:
```cs
public async Task SetupAsync()
{
await _command.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
// Hook the execution event
_command.CommandExecuted += OnCommandExecutedAsync;
// Hook the command handler
_client.MessageReceived += HandleCommandAsync;
}
public async Task OnCommandExecutedAsync(CommandInfo command, ICommandContext context, IResult result)
{
// We have access to the information of the command executed,
// the context of the command, and the result returned from the
// execution in this event.
// We can tell the user what went wrong
if (!string.IsNullOrEmpty(result?.ErrorReason))
{
await context.Channel.SendMessageAsync(result.ErrorReason);
}
// ...or even log the result (the method used should fit into
// your existing log handler)
await _log.LogAsync(new LogMessage(LogSeverity.Info, "CommandExecution", $"{command.Name} was executed at {DateTime.UtcNow}."));
}
public async Task HandleCommandAsync(SocketMessage msg)
{
// Notice how clean our new command handler has become.
var message = messageParam as SocketUserMessage;
if (message == null) return;
int argPos = 0;
if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return;
var context = new SocketCommandContext(_client, message);
await _commands.ExecuteAsync(context, argPos, _services);
}
```
[!code[CommandExecuted demo](samples/command_executed_demo.cs)]
So now we have a streamlined post-execution pipeline, great! What's
next? We can take this further by using [RuntimeResult].
@@ -104,15 +67,31 @@ be returned when the command has finished its logic.
The best way to make use of it is to create your own version of
`RuntimeResult`. You can achieve this by inheriting the `RuntimeResult`
class.
class. The following creates a bare-minimum for a sub-class
of `RuntimeResult`,
[!code[Base Use](samples/customresult_base.cs)]
The sky's the limit from here. You can add any additional information
you'd like regarding the execution result. For example, you may
want to add your own result type or other helpful information
regarding the execution, or something simple like static methods to
help you create return types easily.
[!code[Extended Use](samples/customresult_extended.cs)]
After you're done creating your own [RuntimeResult], you can
implement it in your command by marking its return type to
`Task<RuntimeResult>`.
> ![NOTE]
> You must mark the return type as `Task<RuntimeResult>` instead of
> `Task<MyCustomResult>`. Only the former will be picked up when
> building the module.
```cs
public class MyCustomResult : RuntimeResult
{
}
```
Here's an example of a command that utilizes such logic:
// todo: finish this section
[!code[Usage](samples/customresult_usage.cs)]
## CommandService.Log Event
@@ -126,21 +105,7 @@ sent to the Log event under the [LogMessage.Exception] property as a
[CommandException] type. The [CommandException] class allows us to
access the exception thrown, as well as the context of the command.
```cs
public async Task LogAsync(LogMessage logMessage)
{
// This casting type requries C#7
if (logMessage.Exception is CommandException cmdException)
{
// We can tell the user that something unexpected has happened
await cmdException.Context.Channel.SendMessageAsync("Something went catastrophically wrong!");
// We can also log this incident
Console.WriteLine($"{cmdException.Context.User} failed to execute '{cmdException.Command.Name}' in {cmdException.Context.Channel}.");
Console.WriteLine(cmdException.ToString());
}
}
```
[!code[Logger Sample](samples/command_exception_log.cs)]
[CommandException]: xref:Discord.Commands.CommandException
[LogMessage.Exception]: xref:Discord.LogMessage.Exception