Skip to content

Commit

Permalink
add a few things to the checks article
Browse files Browse the repository at this point in the history
  • Loading branch information
akiraveliara authored and OoLunar committed Mar 31, 2024
1 parent eb38e61 commit 1153d7d
Showing 1 changed file with 24 additions and 6 deletions.
30 changes: 24 additions & 6 deletions docs/articles/commands/custom_context_checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ A context check contains two important pieces:
- The attribute that will be applied to the command. This contains parameters that will be passed to the executing check.
- The check itself. This is the method that determines if the command can be executed.

## Implementing a Context Check Attribute
To create a context check, you will need to create a new attribute that inherits from `CheckBaseAttribute`. This attribute will be applied to the command method and will contain the parameters that will be passed to the check method.
## Implementing a context check attribute
To create a context check, you will need to create a new attribute that inherits from `ContextCheckAttribute`. This attribute will be applied to the command method and may contain metadata for the check method to use.

```cs
public class DirectMessageUsageAttribute : ContextCheckAttribute
Expand All @@ -23,8 +23,11 @@ public class DirectMessageUsageAttribute : ContextCheckAttribute

This is the attribute that you will apply to the command method.

## Implementing the Check Method
Now we're going to implement the logic which checks if the command is allowed to be executed. The `IContextCheck<T>` interface is used to define the check method. The `T` is the attribute/parameters that's associated with the command. In this case, it's the `DirectMessageUsageAttribute`.
## Implementing the context check
Now we're going to implement the logic which checks if the command is allowed to be executed. The interface `IContextCheck<T>` is used to define the check method. The `T` is the attribute that was applied to the command. In this case, it's the `DirectMessageUsageAttribute`, but it can be any check attribute - there can be multiple checks for one attribute.

If the check was successful, the method should return `null`. If it was unsuccessful, the method should return a string that will then be provided
to `CommandsExtension.CommandErrored`.

```cs
public class DirectMessageUsageCheck : IContextCheck<DirectMessageUsageAttribute>
Expand Down Expand Up @@ -59,7 +62,8 @@ public class DirectMessageUsageCheck : IContextCheck<DirectMessageUsageAttribute
}
```

As seen here, we return `null` when the command is allowed to be executed. If the command is not allowed to be executed, we return an error string which can be retrieved from the `CommandsExtension.CommandErrored` event.
> [!WARNING]
> Your check may inspect the command context to get more information, but it should not make any API calls, especially none that may alter state such as `RespondAsync`. This is an easy source of bugs, and depending on what you call you may stall an interaction over the three-second limit, which will break your interactions.
Now, for the most important part, we need to register the check:

Expand All @@ -73,4 +77,18 @@ Then we use the check like such:
[Command("dm")]
[DirectMessageUsage(DirectMessageUsage.RequireDMs)]
public async ValueTask RequireDMs(CommandContext commandContext) => await commandContext.RespondAsync("This command was executed in a DM!");
```
```

## Advanced Features

The classes you use to implement checks participate in dependency injection, and you can request any type you previously supplied to the service provider in a public constructor. Useful applications include, but are not limited to, logging or tracking how often a command executes.

A single check class can also implement multiple checks, like so:

```cs
public class Check : IContextCheck<FirstAttribute>, IContextCheck<SecondAttribute>;
```

This means that all other code in that class can be shared between the two check methods, but this should be used with caution - it means that both checks are registered at once and cannot be removed independently of each other, and it means the same construction ceremony will run for both checks.

There is no limit on how many different checks can reference the same attribute, they will all be supplied with that attribute. If you need a check that always runs, you can target `UnconditionalCheckAttribute`.

0 comments on commit 1153d7d

Please sign in to comment.