Chat Command-related Questions
In the following section, you will find commonly asked questions and answered regarding general command usage when using Discord.Commands.
How can I restrict some of my commands so only specific users can execute them?
You can use the built-in RequireUserPermission
precondition, which allows you to
restrict the command based on the user's current permissions in the
guild or channel (e.g., GuildPermission.Administrator
,
ChannelPermission.ManageMessages
).
Note
There are many more preconditions to use, including being able to make some yourself. Precondition documentation is covered here
Why am I getting an error about Assembly.GetEntryAssembly
?
You may be confusing AddModulesAsync with AddModuleAsync. The former is used to add modules via the assembly, while the latter is used to add a single module.
What does [Remainder] do in the command signature?
The RemainderAttribute leaves the string unparsed, meaning you do not have to add quotes around the text for the text to be recognized as a single object. Please note that if your method has multiple parameters, the remainder attribute can only be applied to the last parameter.
// Input:
// !echo Coffee Cake
// Output:
// Coffee Cake
[Command("echo")]
public Task EchoRemainderAsync([Remainder]string text) => ReplyAsync(text);
// Output:
// CommandError.BadArgCount
[Command("echo-hassle")]
public Task EchoAsync(string text) => ReplyAsync(text);
// The message would be seen as having multiple parameters,
// while the method only accepts one.
// Wrapping the message in quotes solves this.
// This way, the system knows the entire message is to be parsed as a
// single String.
// e.g.,
// !echo "Coffee Cake"
Discord.Net keeps saying that a MessageReceived
handler is blocking the gateway, what should I do?
By default, the library warns the user about any long-running event handler that persists for more than 3 seconds. Any event handlers that are run on the same thread as the gateway task, the task in charge of keeping the connection alive, may block the processing of heartbeat, and thus terminating the connection.
In this case, the library detects that a MessageReceived
event handler is blocking the gateway thread. This warning is
typically associated with the command handler as it listens for that
particular event. If the command handler is blocking the thread, then
this might mean that you have a long-running command.
Note
In rare cases, runtime errors can also cause blockage, usually associated with Mono, which is not supported by this library.
To prevent a long-running command from blocking the gateway thread, a flag called RunMode is explicitly designed to resolve this issue.
There are 2 main RunMode
s.
RunMode.Sync
RunMode.Async
Sync
is the default behavior and makes the command to be run on the
same thread as the gateway one. Async
will spin the task off to a
different thread from the gateway one.
Important
While specifying RunMode.Async
allows the command to be spun off
to a different thread, keep in mind that by doing so, there will be
potentially unwanted consequences. Before applying this flag,
please consider whether it is necessary to do so.
Further details regarding RunMode.Async
can be found below.
You can set the RunMode
either by specifying it individually via
the CommandAttribute
or by setting the global default with
the DefaultRunMode flag under CommandServiceConfig
.
[Command("process", RunMode = RunMode.Async)]
public async Task ProcessAsync(string input)
{
// Does heavy calculation here.
await Task.Delay(TimeSpan.FromMinute(1));
await ReplyAsync(input);
}
How does RunMode.Async
work, and why is Discord.Net not using it by default?
RunMode.Async
works by spawning a new Task
with an unawaited
Task.Run, essentially making the task that is used to invoke the
command task to be finished on a different thread. This design means
that ExecuteAsync will be forced to return a successful
ExecuteResult regardless of the actual execution result.
The following are the known caveats with RunMode.Async
,
- You can potentially introduce a race condition.
- Unnecessary overhead caused by the async state machine.
- ExecuteAsync will immediately return ExecuteResult instead of other result types (this is particularly important for those who wish to utilize RuntimeResult in 2.0).
- Exceptions are swallowed in the
ExecuteAsync
result.
However, there are ways to remedy some of these.
For #3, in Discord.Net 2.0, the library introduces a new event called
CommandService.CommandExecuted, which is raised whenever the command is executed.
This event will be raised regardless of
the RunMode
type and will return the appropriate execution result
and the associated CommandInfo if applicable.
For #4, exceptions are caught in CommandService.Log event under LogMessage.Exception as CommandException and in the CommandService.CommandExecuted event under the IResult as ExecuteResult.Exception.